Documentation
¶
Overview ¶
Package service is the CRUD facade over `<BaseDir>/workflows/`. All file IO goes through Service so callers (engine, MCP, canvas, UI) can swap implementations (in-memory test fakes, audited wrappers).
Index ¶
- Variables
- func WriteAtomic(path string, data []byte) error
- type DBService
- func (s *DBService) Create(id string, w workflow.Workflow) error
- func (s *DBService) Delete(id string) error
- func (s *DBService) DeleteTest(id, name string) error
- func (s *DBService) DiscardDraft(id string) error
- func (s *DBService) FindByName(name, exceptID string) (string, error)
- func (s *DBService) GetTest(id, name string) ([]byte, error)
- func (s *DBService) HasDraft(id string) bool
- func (s *DBService) List() ([]string, error)
- func (s *DBService) ListTests(id string) ([]string, error)
- func (s *DBService) Load(id string) (workflow.Workflow, error)
- func (s *DBService) LoadDraft(id string) (workflow.Workflow, error)
- func (s *DBService) Publish(id string) (workflow.Workflow, error)
- func (s *DBService) SaveDraft(id string, w workflow.Workflow) error
- func (s *DBService) SaveTest(id, name string, body []byte) error
- func (s *DBService) Toggle(id string, enabled bool) error
- func (s *DBService) Update(id string, w workflow.Workflow) error
- type FileService
- func (s *FileService) BaseDir() string
- func (s *FileService) Create(id string, w workflow.Workflow) error
- func (s *FileService) Delete(id string) error
- func (s *FileService) DeleteTest(id, name string) error
- func (s *FileService) DiscardDraft(id string) error
- func (s *FileService) FindByName(name, exceptID string) (string, error)
- func (s *FileService) GetTest(id, name string) ([]byte, error)
- func (s *FileService) HasDraft(id string) bool
- func (s *FileService) List() ([]string, error)
- func (s *FileService) ListTests(id string) ([]string, error)
- func (s *FileService) Load(id string) (workflow.Workflow, error)
- func (s *FileService) LoadDraft(id string) (workflow.Workflow, error)
- func (s *FileService) LoadEnvValues(id string) (map[string]string, error)
- func (s *FileService) LoadState(id string) (workflow.WorkflowState, error)
- func (s *FileService) Publish(id string) (workflow.Workflow, error)
- func (s *FileService) SaveDraft(id string, w workflow.Workflow) error
- func (s *FileService) SaveEnvValues(id string, values map[string]string) error
- func (s *FileService) SaveState(id string, st workflow.WorkflowState) error
- func (s *FileService) SaveTest(id, name string, body []byte) error
- func (s *FileService) Toggle(id string, enabled bool) error
- func (s *FileService) Update(id string, w workflow.Workflow) error
- type Service
Constants ¶
This section is empty.
Variables ¶
var ErrLocked = errors.New("workflow is locked — unlock first")
ErrLocked is returned by SaveDraft when the persisted workflow has `_canvas.locked = true` and the incoming body would mutate non-lock fields. Callers can unlock by writing a new body with `_canvas.locked = false`.
var ErrNameTaken = errors.New("workflow name already taken")
ErrNameTaken is returned when Create / Update would land on a Name that another workflow already uses. Display name uniqueness keeps list + canvas pickers unambiguous; the underlying id stays the stable file identifier.
var ErrNotFound = errors.New("workflow not found")
ErrNotFound is returned when an id is missing.
Functions ¶
func WriteAtomic ¶
WriteAtomic does tmp+rename so a crash leaves the old file intact. Exported because the engine + canvas use it too.
Types ¶
type DBService ¶ added in v0.14.20
type DBService struct {
*FileService
// contains filtered or unexported fields
}
DBService is the database-primary implementation of Service. The workflow definition (body + draft + version history + supporting files + test fixtures) lives in SQL via repository.Repo; runtime concerns the engine needs from the filesystem (state.json, env.yaml, runs/<id>/) still go through the embedded FileService.
Composition over inheritance: FileService is embedded so methods we don't override (LoadState/SaveState, LoadEnvValues/SaveEnvValues, BaseDir) keep their on-disk semantics. The runtime side of wick continues to write state + events as JSON/JSONL files so the engine stays cheap and crash-friendly.
func NewDB ¶ added in v0.14.20
func NewDB(layout config.Layout, repo *repository.Repo) *DBService
NewDB constructs a DBService with the shared Repo and Layout. The Layout is still required because the embedded FileService owns the state.json + env.yaml + runs/<id>/ paths.
func (*DBService) Create ¶ added in v0.14.20
Create inserts the workflow row + the initial draft body. The first SaveDraft snapshot anchors the version history.
func (*DBService) Delete ¶ added in v0.14.20
Delete drops the workflow row + every cascading table (versions, files, test cases) in one transaction. The on-disk runs/ folder is removed too so old run logs don't linger.
func (*DBService) DeleteTest ¶ added in v0.14.20
DeleteTest drops one test case by name.
func (*DBService) DiscardDraft ¶ added in v0.14.20
DiscardDraft wipes the draft slot and HasDraft flag.
func (*DBService) FindByName ¶ added in v0.14.20
FindByName resolves a display-name conflict by querying every row in the workflows table. Case-insensitive trim match — same contract the file-store advertised.
func (*DBService) HasDraft ¶ added in v0.14.20
HasDraft reports whether a draft body is currently persisted for the workflow. Falls through to false on lookup error so callers can proceed without a 500.
func (*DBService) List ¶ added in v0.14.20
List returns every workflow id stored in the DB, ordered by id so the SPA list stays deterministic across reloads. The DB-backed list does NOT scan disk — folders left over from the file era are invisible to the SPA once the boot importer has either migrated them or skipped them as unreadable.
func (*DBService) ListTests ¶ added in v0.14.20
ListTests returns every test case name registered under the workflow.
func (*DBService) Load ¶ added in v0.14.20
Load returns the published workflow. Falls back to the draft when nothing has been published yet — matches the file-based Service.Load semantics so the engine keeps booting from whichever copy exists.
func (*DBService) LoadDraft ¶ added in v0.14.20
LoadDraft returns the draft if one exists, otherwise the published copy. ErrNotFound when no row exists at all.
func (*DBService) Publish ¶ added in v0.14.20
Publish promotes the draft to the published slot, appends a published-kind snapshot, and validates before committing so a broken draft can't go live.
func (*DBService) SaveDraft ¶ added in v0.14.20
SaveDraft persists the canvas state. Appends a new draft snapshot to workflow_versions; retention is enforced by Repo.SaveDraft. Rejects writes when the persisted draft is locked unless the incoming body also flips `_canvas.locked = false` (explicit unlock).
func (*DBService) Toggle ¶ added in v0.14.20
Toggle flips the Enabled flag on the published copy and clones the change into the draft (when a draft exists) so the SPA editor's header chip stays in sync with the live router.
type FileService ¶
FileService is the on-disk implementation.
func (*FileService) BaseDir ¶
func (s *FileService) BaseDir() string
BaseDir returns the workflows root for diagnostics + MCP exposure.
func (*FileService) Create ¶
func (s *FileService) Create(id string, w workflow.Workflow) error
Create scaffolds a new folder.
func (*FileService) Delete ¶
func (s *FileService) Delete(id string) error
Delete removes the folder.
func (*FileService) DeleteTest ¶ added in v0.14.20
func (s *FileService) DeleteTest(id, name string) error
DeleteTest drops one test case file.
func (*FileService) DiscardDraft ¶
func (s *FileService) DiscardDraft(id string) error
DiscardDraft removes the draft file. No-op if no draft.
func (*FileService) FindByName ¶
func (s *FileService) FindByName(name, exceptID string) (string, error)
FindByName scans every workflow on disk and returns the id of the first one whose Name (case-insensitive, trimmed) matches the target. `exceptID` lets Update skip the current workflow when re-checking its own name. Returns "" + nil error when no collision exists.
O(N) scan acceptable — workflow counts stay small (tens, not thousands) and this only fires on create/update/UI validation, not on hot paths.
func (*FileService) GetTest ¶ added in v0.14.20
func (s *FileService) GetTest(id, name string) ([]byte, error)
GetTest returns one test case body by name.
func (*FileService) HasDraft ¶
func (s *FileService) HasDraft(id string) bool
HasDraft reports whether a workflow.draft.yaml file exists.
func (*FileService) List ¶
func (s *FileService) List() ([]string, error)
List returns every workflow id, sorted.
func (*FileService) ListTests ¶ added in v0.14.20
func (s *FileService) ListTests(id string) ([]string, error)
ListTests returns every test case name registered under the workflow. FileService stores cases on disk under `__tests__/*.json` — name is the basename without extension.
func (*FileService) Load ¶
func (s *FileService) Load(id string) (workflow.Workflow, error)
Load reads + parses a workflow.yaml.
func (*FileService) LoadDraft ¶
func (s *FileService) LoadDraft(id string) (workflow.Workflow, error)
LoadDraft loads the draft file if present, otherwise falls back to the published workflow. Editor always opens this so the user sees their in-progress edits across refreshes.
func (*FileService) LoadEnvValues ¶
func (s *FileService) LoadEnvValues(id string) (map[string]string, error)
LoadEnvValues reads `<id>/env.yaml`.
func (*FileService) LoadState ¶
func (s *FileService) LoadState(id string) (workflow.WorkflowState, error)
LoadState reads `<id>/state.json`. Missing file returns zero value.
func (*FileService) Publish ¶
func (s *FileService) Publish(id string) (workflow.Workflow, error)
Publish promotes the draft to workflow.yaml and removes the draft. Returns the published workflow. No-op (returns current published) when no draft exists.
func (*FileService) SaveDraft ¶
func (s *FileService) SaveDraft(id string, w workflow.Workflow) error
SaveDraft writes the workflow body to the draft slot. Never touches the published copy — Publish is the only path that promotes a draft. Rejects writes when the persisted draft is locked AND the incoming body is also locked — that catches every mutation except an explicit unlock (writing `_canvas.locked = false`).
func (*FileService) SaveEnvValues ¶
func (s *FileService) SaveEnvValues(id string, values map[string]string) error
SaveEnvValues writes `<id>/env.yaml` atomically.
func (*FileService) SaveState ¶
func (s *FileService) SaveState(id string, st workflow.WorkflowState) error
SaveState writes `<id>/state.json` atomically.
func (*FileService) SaveTest ¶ added in v0.14.20
func (s *FileService) SaveTest(id, name string, body []byte) error
SaveTest writes one test case body atomically.
type Service ¶
type Service interface {
List() ([]string, error)
Load(id string) (workflow.Workflow, error)
Create(id string, w workflow.Workflow) error
Update(id string, w workflow.Workflow) error
Delete(id string) error
Toggle(id string, enabled bool) error
// FindByName returns the id of an existing workflow whose Name
// matches the given name (case-insensitive, trimmed). Excludes the
// optional `exceptID` so Update can call this without flagging
// itself. Empty id + nil error means no collision. UI form
// pre-validation + Create/Update guards both use this.
FindByName(name, exceptID string) (string, error)
// Draft/Publish lifecycle. SaveDraft persists in-progress edits,
// Publish promotes the draft to the live slot, DiscardDraft drops
// the draft and reverts to the published body.
LoadDraft(id string) (workflow.Workflow, error)
HasDraft(id string) bool
SaveDraft(id string, w workflow.Workflow) error
Publish(id string) (workflow.Workflow, error)
DiscardDraft(id string) error
// Test fixtures live under the workflow as named cases. Name is the
// slug-safe identifier ([a-z0-9_-]); the body is the raw JSON the
// runner consumes. Implementations route to disk (legacy) or to the
// workflow_test_cases table (DB-primary).
ListTests(id string) ([]string, error)
GetTest(id, name string) ([]byte, error)
SaveTest(id, name string, body []byte) error
DeleteTest(id, name string) error
LoadState(id string) (workflow.WorkflowState, error)
SaveState(id string, st workflow.WorkflowState) error
LoadEnvValues(id string) (map[string]string, error)
SaveEnvValues(id string, values map[string]string) error
BaseDir() string
}
Service is the CRUD contract.