2026-03-29 18:58:34 +02:00
|
|
|
package service
|
|
|
|
|
|
2026-03-31 15:38:06 +02:00
|
|
|
import (
|
|
|
|
|
"axolotl/models"
|
|
|
|
|
"axolotl/store"
|
|
|
|
|
)
|
2026-03-29 18:58:34 +02:00
|
|
|
|
2026-03-31 15:55:47 +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.
|
2026-03-29 18:58:34 +02:00
|
|
|
type NodeService interface {
|
2026-03-31 15:55:47 +02:00
|
|
|
// Query
|
2026-03-29 18:58:34 +02:00
|
|
|
GetByID(id string) (*models.Node, error)
|
2026-03-31 15:55:47 +02:00
|
|
|
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.
|
2026-03-31 23:10:56 +02:00
|
|
|
// 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.
|
2026-03-31 15:55:47 +02:00
|
|
|
type AddInput struct {
|
2026-03-31 23:10:56 +02:00
|
|
|
Title string
|
|
|
|
|
Content string
|
|
|
|
|
DueDate string
|
|
|
|
|
Tags []string
|
|
|
|
|
Rels []RelInput
|
2026-03-31 15:55:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpdateInput describes changes to apply to an existing node.
|
|
|
|
|
// Nil pointer fields mean "no change".
|
2026-03-31 23:10:56 +02:00
|
|
|
// Setting _status::done in AddTags is rejected when the node has open blockers.
|
|
|
|
|
// Adding assignee or in_namespace rels replaces the previous single target.
|
2026-03-31 15:55:47 +02:00
|
|
|
type UpdateInput struct {
|
2026-03-31 23:10:56 +02:00
|
|
|
Title *string
|
|
|
|
|
Content *string
|
|
|
|
|
DueDate *string // nil = no change; pointer to "" = clear due date
|
2026-03-31 15:55:47 +02:00
|
|
|
AddTags []string
|
|
|
|
|
RemoveTags []string
|
|
|
|
|
AddRels []RelInput
|
|
|
|
|
RemoveRels []RelInput
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 23:10:56 +02:00
|
|
|
// 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.
|
2026-03-31 15:55:47 +02:00
|
|
|
type ListFilter struct {
|
2026-03-31 23:10:56 +02:00
|
|
|
Tags []string
|
2026-03-31 15:55:47 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2026-03-29 21:24:09 +02:00
|
|
|
func InitNodeService(path string) error {
|
2026-03-31 15:38:06 +02:00
|
|
|
return store.InitSQLiteStore(path)
|
2026-03-29 21:24:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetNodeService(cfg Config) (NodeService, error) {
|
2026-03-31 15:38:06 +02:00
|
|
|
st, err := store.FindAndOpenSQLiteStore()
|
2026-03-29 21:24:09 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2026-03-31 15:38:06 +02:00
|
|
|
return &nodeServiceImpl{store: st, userID: cfg.GetUser()}, nil
|
2026-03-29 21:24:09 +02:00
|
|
|
}
|