package service import ( "axolotl/models" "axolotl/store" "net/http" ) // 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()). type NodeService interface { // User returns the name/ID of the user this service instance acts on behalf of. User() string // Query 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. // Rels may contain tag rels (Target == ""), property rels (Target == "", // Type is "prefix::value"), and edge rels (Target is a node name or ID). // The service applies defaults (type=issue, status=open for issues) and validates. type AddInput struct { Title string Content string DueDate string Namespace string // namespace name or ID; defaults to the user's personal namespace Rels []RelInput } // UpdateInput describes changes to apply to an existing node. // AddRels and RemoveRels accept both tag rels (Target == "") and edge rels. // Setting _status::done in AddRels is rejected when the node has open blockers. // Adding an assignee rel replaces the previous single target. // Setting Namespace transfers ownership from the current namespace to the new one. type UpdateInput struct { Title *string Content *string DueDate *string // nil = no change; pointer to "" = clear due date Namespace *string // nil = no change; namespace name or ID to move node into AddRels []RelInput RemoveRels []RelInput RegenerateAccessToken bool // when true, regenerates the access token for agent nodes } // ListFilter specifies which nodes to return. Empty slices are ignored. // Tag filters (Target == "") match by rel_name prefix. // Edge filters (Target != "") are resolved to node IDs. type ListFilter struct { Namespace string // when non-empty, only return nodes owned by this namespace Rels []RelInput HasDueDate bool // when true, only return nodes that have a due date set DueWithin *int // when non-nil, only return nodes due within this many days (includes overdue) } // RelInput is a typed, directed rel with a target that may be a name or node ID. // Target == "" means this is a tag or property rel (no target node). type RelInput struct { Type models.RelType Target string // name or node ID; the service resolves names. Empty = tag rel. } func NewLocalNodeService(st store.GraphStore, user string) NodeService { return &nodeServiceImpl{store: st, userID: user} } func NewRemoteNodeService(base, user string) NodeService { return &apiClient{base: base, user: user, http: &http.Client{}} }