Files
ax/service/node_service.go

89 lines
2.6 KiB
Go
Raw Normal View History

2026-03-29 18:58:34 +02:00
package service
import (
"axolotl/models"
"axolotl/store"
"fmt"
)
2026-03-29 18:58:34 +02:00
// NodeService is the single entry point for all node operations.
// All data-model integrity rules are enforced here; callers cannot produce
// invalid state by interacting with this interface alone.
//
// Every NodeService instance is bound to a specific user (see User()).
// GetNodeService returns an error when no user is configured.
2026-03-29 18:58:34 +02:00
type NodeService interface {
// User returns the name/ID of the user this service instance acts on behalf of.
User() string
// Query
2026-03-29 18:58:34 +02:00
GetByID(id string) (*models.Node, error)
List(filter ListFilter) ([]*models.Node, error)
// Lifecycle
Add(input AddInput) (*models.Node, error)
Update(id string, input UpdateInput) (*models.Node, error)
Delete(id string) error
// User management
AddUser(name string) (*models.Node, error)
ListUsers() ([]*models.Node, error)
}
// AddInput describes a new node to create.
// Tags may include special property tags (_type::, _status::, _prio::); the
// service applies defaults (type=issue, status=open for issues) and validates.
// Rels may include assignee, in_namespace, blocks, subtask, related, etc.;
// user and namespace targets are auto-created as needed.
type AddInput struct {
Title string
Content string
DueDate string
Tags []string
Rels []RelInput
}
// UpdateInput describes changes to apply to an existing node.
// Nil pointer fields mean "no change".
// Setting _status::done in AddTags is rejected when the node has open blockers.
// Adding assignee or in_namespace rels replaces the previous single target.
type UpdateInput struct {
Title *string
Content *string
DueDate *string // nil = no change; pointer to "" = clear due date
AddTags []string
RemoveTags []string
AddRels []RelInput
RemoveRels []RelInput
}
// ListFilter specifies which nodes to return. Empty slices are ignored.
// Tags are matched as exact tag values or prefixes (e.g. "_status::open").
// Rels are resolved to node IDs; a missing target returns no results.
type ListFilter struct {
Tags []string
Rels []RelInput
}
// RelInput is a typed, directed edge with a target that may be a name or node ID.
type RelInput struct {
Type models.RelType
Target string // name or node ID; the service resolves names
2026-03-29 18:58:34 +02:00
}
func InitNodeService(path string) error {
return store.InitSQLiteStore(path)
}
func GetNodeService(cfg Config) (NodeService, error) {
user := cfg.GetUser()
if user == "" {
return nil, fmt.Errorf("no user configured: run 'ax user set <username>' first")
}
st, err := store.FindAndOpenSQLiteStore()
if err != nil {
return nil, err
}
return &nodeServiceImpl{store: st, userID: user}, nil
}