Documentation
¶
Overview ¶
Package app resolves the selected project and names into ids and computes minimal writes for the curated commands. It talks to Taiga only through the narrow API interface.
Index ¶
- func FieldPath(kind string) (string, error)
- func ID(v any) int64
- func MergeIDs(current, add, remove []int64) []int64
- func MergeNames(current, add, remove []string) []string
- func Names(v any) ([]string, error)
- func OrderStatuses(current []string, specs []StatusSpec) ([]string, error)
- func ParseFieldValue(typ, raw string) (any, error)
- func PreviewProject(plan ProjectPlan, statuses []Object, projectID any) ([]WritePlan, []Action, error)
- func Snapshot(o Object) (map[string]json.RawMessage, error)
- func SortStatuses(statuses []Object)
- func SystemComment(o Object) bool
- func TagName(s string) string
- func UnsetFieldValue(typ, name string) error
- func Unsupported(cause, recovery string) error
- func Usage(cause string) error
- func ValidateField(name, typ string) error
- func ValuePath(kind string, id int64) (string, error)
- func WriteApplied(method, path string, status int, readErr error) error
- type API
- type Action
- type ApplyResult
- type FieldSpec
- type Object
- func AssignmentPatch(before Object, p Patch) (Object, error)
- func BuildPatch(before Object, p Patch) (Object, error)
- func Decode(b []byte) (Object, error)
- func FieldsView(values Object, defs []Object) []Object
- func MergeValues(current Object, updates Object) Object
- func Read(ctx context.Context, api API, path string, q url.Values) (Object, error)
- func Resolve(items []Object, selector, nameKey string) (Object, error)
- type Patch
- type ProjectPlan
- type ProjectSpec
- type Service
- func (s *Service) ApplyProject(ctx context.Context, spec ProjectSpec, dry bool) (ApplyResult, error)
- func (s *Service) Catalog(ctx context.Context, path string) ([]Object, error)
- func (s *Service) CloseStory(ctx context.Context, ref, selector string, dry, force bool) (any, error)
- func (s *Service) Comment(ctx context.Context, ref, body string, dry bool) (any, error)
- func (s *Service) Comments(ctx context.Context, ref string, includeSystem bool) ([]Object, error)
- func (s *Service) CreateField(ctx context.Context, kind, name, typ string, description *string, dry bool) (any, error)
- func (s *Service) CreateStory(ctx context.Context, body Object, dry bool) (any, error)
- func (s *Service) Epic(ctx context.Context, ref string) (Object, error)
- func (s *Service) FieldValues(ctx context.Context, kind string, id int64) (Object, error)
- func (s *Service) Fields(ctx context.Context, kind string) ([]Object, error)
- func (s *Service) Member(ctx context.Context, selector string) (Object, error)
- func (s *Service) ProjectPlan(ctx context.Context, spec ProjectSpec) (ProjectPlan, error)
- func (s *Service) SetFieldValues(ctx context.Context, kind string, id int64, entries, unsets []string, ...) (any, error)
- func (s *Service) Stories(ctx context.Context, filters url.Values) ([]Object, error)
- func (s *Service) Story(ctx context.Context, ref string, id int64) (Object, error)
- func (s *Service) StoryView(o Object) (Object, error)
- func (s *Service) UpdateStory(ctx context.Context, ref string, p Patch, dry, force bool) (any, error)
- func (s *Service) User(ctx context.Context, selector string) (Object, error)
- func (s *Service) Write(ctx context.Context, path string, before, patch Object, dry, force bool) (any, error)
- type StatusSpec
- type StatusWriter
- type WritePlan
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MergeIDs ¶
MergeIDs returns current ∪ add − remove, keeping order and dropping duplicates. It never returns nil, so removing the last id sends [].
func MergeNames ¶
MergeNames returns current ∪ add − remove, keeping order and dropping duplicates.
func OrderStatuses ¶
func OrderStatuses(current []string, specs []StatusSpec) ([]string, error)
OrderStatuses returns the desired order of every status: current ones (in their order) plus the declared ones that do not exist yet, appended in file order, each declaration with After moved right behind its predecessor. Statuses after the same predecessor keep the file order. An unknown predecessor or a cycle is an error.
func ParseFieldValue ¶
ParseFieldValue converts raw to the JSON value of a field of type typ. Taiga does not validate values (docs/api-notes.md), so the CLI does. An assignment never unsets a field: "null" is text, and an empty date is refused; unsetting takes UnsetFieldValue.
func PreviewProject ¶
func PreviewProject(plan ProjectPlan, statuses []Object, projectID any) ([]WritePlan, []Action, error)
PreviewProject materializes the requests of plan without sending anything. statuses is the read that produced plan. New statuses get an order after the current ones, in file order.
func Snapshot ¶
func Snapshot(o Object) (map[string]json.RawMessage, error)
Snapshot converts an object read from Taiga back to raw JSON, for WriteVersionedFrom.
func SortStatuses ¶
func SortStatuses(statuses []Object)
SortStatuses orders statuses as Taiga does (order, then name), numerically.
func SystemComment ¶
SystemComment reports a history entry written by Taiga's own GitLab integration for a push hook: the inactive gitlab-<hash> user and one of the hook's comment templates (taiga/hooks/event_hooks.py). Both must match. A service account, a human quoting the template, a diff without text or an unknown author are never system comments: agents publish human comments with integration accounts (docs/api-notes.md).
func UnsetFieldValue ¶
UnsetFieldValue checks that a field of type typ can be cleared.
func Unsupported ¶
Unsupported reports a flag whose Taiga contract the CLI cannot honour safely.
func ValidateField ¶
ValidateField checks a definition before any request. Taiga limits names to 64 characters; "=" is refused because Name=value assignments end the name at the first "=".
Types ¶
type API ¶
type API interface {
Do(context.Context, taiga.Request) (*taiga.Response, error)
GetAll(context.Context, string, url.Values) ([]json.RawMessage, error)
WriteVersioned(context.Context, string, string, map[string]any, bool) (*taiga.Response, error)
WriteVersionedFrom(context.Context, string, string, map[string]any, map[string]json.RawMessage, bool) (*taiga.Response, error)
BaseURL() string
}
API is the part of *taiga.Client the services use.
type Action ¶
type Action struct {
Kind string `json:"kind"`
Name string `json:"name"`
Body Object `json:"body"`
}
Action is one semantic step of a project plan; new statuses have no id or version yet.
type ApplyResult ¶
type ApplyResult struct {
Plan ProjectPlan `json:"plan"`
Applied []Action `json:"applied"`
Remaining []Action `json:"remaining"`
Complete bool `json:"complete"`
Requests []WritePlan `json:"requests"`
Deferred []Action `json:"deferred"`
}
ApplyResult is what project apply did. Applied and Remaining describe a partial run; Complete is set only after a new read finds nothing left to do. Requests is the dry-run preview.
func Apply ¶
func Apply(ctx context.Context, writer StatusWriter, spec ProjectSpec, dry bool) (ApplyResult, error)
Apply validates everything (permission, drift, reorder support) before the first write, then runs the plan in order. A failure stops it: nothing is undone and nothing is repeated, and the result says what was applied and what remains. Running it again re-reads and re-plans.
type FieldSpec ¶
type FieldSpec struct {
Name string `toml:"name"`
Type string `toml:"type"`
Description string `toml:"description"`
}
FieldSpec declares a story custom field; an absent description means "".
type Object ¶
Object is a decoded Taiga object; numbers stay json.Number and unknown keys are kept.
func AssignmentPatch ¶
AssignmentPatch returns the assignee and block fields that differ from before.
Taiga 6.7 answers assigned_users as the stored list plus assigned_to (docs/api-notes.md), so: removing the owner without changing assigned_to is refused, because Taiga would keep showing them; and any change of assigned_to also sends the full list, because the previous owner may not be in the stored list and would silently disappear.
func BuildPatch ¶
BuildPatch returns only the fields that differ from before.
func FieldsView ¶
FieldsView binds the values to their definitions: each definition gets the stored value under "value" when there is one. Ids without a definition stay in attributes_values.
func MergeValues ¶
MergeValues returns current with updates applied; keys not in updates are kept as read.
type Patch ¶
type Patch struct {
Set Object
AddTags, RemoveTags []string
Append *string
AddAssignees, RemoveAssignees []int64
Owner *int64
ClearOwner bool
Block *string
Unblock bool
}
Patch records which changes were asked for; absent fields stay untouched.
type ProjectPlan ¶
type ProjectPlan struct {
Actions []Action `json:"actions"`
Unmanaged []Object `json:"unmanaged"`
Drift []Object `json:"drift"`
}
ProjectPlan is what apply would do. Unmanaged lists what exists and the file does not declare (kept as is); Drift lists declared definitions that exist with other values, which apply refuses instead of updating.
func BuildProjectPlan ¶
func BuildProjectPlan(spec ProjectSpec, statuses, fields []Object) (ProjectPlan, error)
BuildProjectPlan compares spec with the statuses (already sorted by SortStatuses) and story field definitions read from Taiga. It never plans an update or a removal.
type ProjectSpec ¶
type ProjectSpec struct {
StoryStatus []StatusSpec `toml:"story_status"`
StoryField []FieldSpec `toml:"story_field"`
}
ProjectSpec is the project TOML: only what it declares is managed, nothing is removed.
func ParseProjectSpec ¶
func ParseProjectSpec(r io.Reader) (ProjectSpec, error)
ParseProjectSpec decodes the project TOML strictly (unknown keys are errors) and validates every declaration before anything is read from Taiga.
type Service ¶
Service is created per command run; catalogs are cached only for that run.
func (*Service) ApplyProject ¶
func (s *Service) ApplyProject(ctx context.Context, spec ProjectSpec, dry bool) (ApplyResult, error)
ApplyProject runs Apply against Taiga.
func (*Service) Catalog ¶
Catalog lists path for the project once per run. Entries that declare another project are dropped: a query parameter the server ignores must not widen the scope.
func (*Service) CloseStory ¶
func (s *Service) CloseStory(ctx context.Context, ref, selector string, dry, force bool) (any, error)
CloseStory moves the story to a closed status: the given one, or the project's only closed status. A story already closed is left alone unless another closed status is named.
func (*Service) Comment ¶
Comment publishes body on a story with PATCH {comment, version} and returns the story read again. Taiga's OCC never refuses a comment (it is not a field of the story, so an old version is accepted; docs/api-notes.md), so the version protects nothing and the PATCH is sent once, never repeated. When its outcome is unknown (network error after the connection opened, or 5xx), the CLI compares our comments with this exact text in the history before and after: a new one means published; otherwise the outcome stays unknown (comment_unconfirmed, exit 1).
func (*Service) Comments ¶
Comments lists the comments of a story, newest first as Taiga sends them. Comments of the GitLab integration (SystemComment) are left out unless includeSystem; every entry is marked with is_system and its story. Edited and deleted comments stay, with Taiga's edit_comment_date and delete_comment_date.
func (*Service) CreateField ¶
func (s *Service) CreateField(ctx context.Context, kind, name, typ string, description *string, dry bool) (any, error)
CreateField creates a definition unless one with the same name (case-sensitive, as Taiga compares) already exists: a compatible one is returned unchanged, a different one is an error. Existing definitions are never modified. A nil description is not compared.
func (*Service) CreateStory ¶
CreateStory posts body to the project. There is no previous version: none is sent.
func (*Service) FieldValues ¶
FieldValues reads the values of the story or task id, after checking that it belongs to the selected project. A read error is returned as is, never as empty values.
func (*Service) Member ¶
Member resolves a project member by exact username, id or "me": the id must appear in the project's memberships.
func (*Service) ProjectPlan ¶
func (s *Service) ProjectPlan(ctx context.Context, spec ProjectSpec) (ProjectPlan, error)
ProjectPlan reads the project's statuses and story fields and compares them with spec. It only reads: no permission is needed beyond seeing the project.
func (*Service) SetFieldValues ¶
func (s *Service) SetFieldValues(ctx context.Context, kind string, id int64, entries, unsets []string, dry, force bool) (any, error)
SetFieldValues merges the Name=value entries into the values of the story or task id, clears the fields named in unsets, and writes the whole dictionary with the version of the values resource. A cleared field keeps its key with null: Taiga refuses an empty dictionary, so the key cannot be dropped. Clearing a field without a stored value adds nothing, because an absent key and null both mean no value. Every entry is resolved and parsed before anything is read or written; an unchanged dictionary writes nothing.
func (*Service) Stories ¶
Stories lists every story of the project matching filters (keys: ref, status, closed, search, assignee, epic, tag; ids already resolved). Proven filters go to Taiga to shorten the list; every filter is then checked locally, so an ignored parameter never widens it.
func (*Service) Story ¶
Story reads a story of the selected project by ref (userstories/by_ref) or by id. Tags stay as Taiga sent them, because the object is the baseline of a versioned write.
func (*Service) StoryView ¶
StoryView is the output form: tag names instead of pairs, plus the web URL.
func (*Service) UpdateStory ¶
type StatusSpec ¶
type StatusSpec struct {
Name string `toml:"name"`
Color string `toml:"color"`
Closed bool `toml:"closed"`
After string `toml:"after"`
}
StatusSpec declares a story status. Closed defaults to false; After names the status it follows (existing or declared in the same file).
type StatusWriter ¶
type StatusWriter interface {
CheckPermission(context.Context) error
ProjectID() any
Load(context.Context) (statuses, fields []Object, err error)
CheckReorderSupport(context.Context) error
CreateStatus(context.Context, Object) error
CreateField(context.Context, Object) error
Reorder(context.Context, []string) error
}
StatusWriter is the Taiga side of apply; its contracts are in docs/api-notes.md.