service

package
v0.14.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 13 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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`.

View Source
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.

View Source
var ErrNotFound = errors.New("workflow not found")

ErrNotFound is returned when an id is missing.

Functions

func WriteAtomic

func WriteAtomic(path string, data []byte) error

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

func (s *DBService) Create(id string, w workflow.Workflow) error

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

func (s *DBService) Delete(id string) error

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

func (s *DBService) DeleteTest(id, name string) error

DeleteTest drops one test case by name.

func (*DBService) DiscardDraft added in v0.14.20

func (s *DBService) DiscardDraft(id string) error

DiscardDraft wipes the draft slot and HasDraft flag.

func (*DBService) FindByName added in v0.14.20

func (s *DBService) FindByName(name, exceptID string) (string, error)

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) GetTest added in v0.14.20

func (s *DBService) GetTest(id, name string) ([]byte, error)

GetTest returns one test case body by name.

func (*DBService) HasDraft added in v0.14.20

func (s *DBService) HasDraft(id string) bool

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

func (s *DBService) List() ([]string, error)

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

func (s *DBService) ListTests(id string) ([]string, error)

ListTests returns every test case name registered under the workflow.

func (*DBService) Load added in v0.14.20

func (s *DBService) Load(id string) (workflow.Workflow, error)

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

func (s *DBService) LoadDraft(id string) (workflow.Workflow, error)

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

func (s *DBService) Publish(id string) (workflow.Workflow, error)

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

func (s *DBService) SaveDraft(id string, w workflow.Workflow) error

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) SaveTest added in v0.14.20

func (s *DBService) SaveTest(id, name string, body []byte) error

SaveTest upserts one test case body.

func (*DBService) Toggle added in v0.14.20

func (s *DBService) Toggle(id string, enabled bool) error

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.

func (*DBService) Update added in v0.14.20

func (s *DBService) Update(id string, w workflow.Workflow) error

Update replaces the workflow body in-place. Used by rename / metadata-only paths where the canvas isn't driving the change. Internally writes through the published slot (Repo.SaveDraft + Publish would create a phantom draft).

type FileService

type FileService struct {
	Layout config.Layout
}

FileService is the on-disk implementation.

func New

func New(layout config.Layout) *FileService

New constructs a FileService.

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.

func (*FileService) Toggle

func (s *FileService) Toggle(id string, enabled bool) error

Toggle flips enabled flag atomically.

func (*FileService) Update

func (s *FileService) Update(id string, w workflow.Workflow) error

Update overwrites the workflow body in place.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL