- Add workitem package with provider interface for workspace item management - Update plane client to support workspace item operations - Add tests for workitem provider - Update roadmap current.md and plane-task-pipeline-design milestone
141 lines
3.7 KiB
Go
141 lines
3.7 KiB
Go
package workitem
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
var ErrInvalidRef = errors.New("invalid workitem ref")
|
|
|
|
type ProviderID string
|
|
|
|
type Capability string
|
|
|
|
const (
|
|
CapabilityLookup Capability = "lookup"
|
|
CapabilityComment Capability = "comment"
|
|
CapabilityStatusProjection Capability = "status_projection"
|
|
CapabilityLabelProjection Capability = "label_projection"
|
|
)
|
|
|
|
type Capabilities map[Capability]bool
|
|
|
|
func (c Capabilities) Supports(capability Capability) bool {
|
|
return c[capability]
|
|
}
|
|
|
|
type Provider interface {
|
|
ProviderID() ProviderID
|
|
Capabilities() Capabilities
|
|
}
|
|
|
|
type Reader interface {
|
|
FetchWorkItem(ctx context.Context, ref Ref) (WorkItem, error)
|
|
}
|
|
|
|
type Commenter interface {
|
|
AppendComment(ctx context.Context, input CommentInput) error
|
|
}
|
|
|
|
type StatusProjector interface {
|
|
ProjectStatus(ctx context.Context, input StatusProjection) error
|
|
}
|
|
|
|
type LabelProjector interface {
|
|
ProjectLabels(ctx context.Context, input LabelProjection) error
|
|
}
|
|
|
|
type Ref struct {
|
|
Provider ProviderID `json:"provider"`
|
|
Tenant string `json:"tenant"`
|
|
Project string `json:"project"`
|
|
ID string `json:"id"`
|
|
URL string `json:"url,omitempty"`
|
|
Metadata json.RawMessage `json:"metadata,omitempty"`
|
|
}
|
|
|
|
type WorkItem struct {
|
|
Ref Ref `json:"ref"`
|
|
Title string `json:"title"`
|
|
DescriptionText string `json:"description_text,omitempty"`
|
|
DescriptionHTML string `json:"description_html,omitempty"`
|
|
State string `json:"state,omitempty"`
|
|
AssigneeIDs []string `json:"assignee_ids,omitempty"`
|
|
Labels []string `json:"labels,omitempty"`
|
|
Metadata json.RawMessage `json:"metadata,omitempty"`
|
|
}
|
|
|
|
type CommentFormat string
|
|
|
|
const (
|
|
CommentFormatText CommentFormat = "text"
|
|
CommentFormatMarkdown CommentFormat = "markdown"
|
|
CommentFormatHTML CommentFormat = "html"
|
|
)
|
|
|
|
type CommentInput struct {
|
|
Ref Ref `json:"ref"`
|
|
Body string `json:"body"`
|
|
Format CommentFormat `json:"format"`
|
|
ExternalSource string `json:"external_source,omitempty"`
|
|
ExternalID string `json:"external_id,omitempty"`
|
|
Visibility string `json:"visibility,omitempty"`
|
|
}
|
|
|
|
type StatusProjection struct {
|
|
Ref Ref `json:"ref"`
|
|
CanonicalState string `json:"canonical_state"`
|
|
ProviderState string `json:"provider_state"`
|
|
}
|
|
|
|
type LabelTarget struct {
|
|
Canonical string `json:"canonical"`
|
|
Provider string `json:"provider"`
|
|
}
|
|
|
|
type LabelProjection struct {
|
|
Ref Ref `json:"ref"`
|
|
Add []LabelTarget `json:"add,omitempty"`
|
|
Remove []LabelTarget `json:"remove,omitempty"`
|
|
}
|
|
|
|
type Mapping struct {
|
|
States map[string]string `json:"states,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
}
|
|
|
|
func (m Mapping) StateTarget(canonical string) (string, bool) {
|
|
target := strings.TrimSpace(m.States[strings.TrimSpace(canonical)])
|
|
return target, target != ""
|
|
}
|
|
|
|
func (m Mapping) LabelTarget(canonical string) (string, bool) {
|
|
target := strings.TrimSpace(m.Labels[strings.TrimSpace(canonical)])
|
|
return target, target != ""
|
|
}
|
|
|
|
func NormalizeRef(ref Ref) (Ref, error) {
|
|
provider := ProviderID(strings.TrimSpace(string(ref.Provider)))
|
|
id := strings.TrimSpace(ref.ID)
|
|
if provider == "" || id == "" {
|
|
return Ref{}, ErrInvalidRef
|
|
}
|
|
|
|
meta := ref.Metadata
|
|
if len(meta) == 0 {
|
|
meta = json.RawMessage("{}")
|
|
} else if !json.Valid(meta) {
|
|
return Ref{}, ErrInvalidRef
|
|
}
|
|
|
|
return Ref{
|
|
Provider: provider,
|
|
Tenant: strings.TrimSpace(ref.Tenant),
|
|
Project: strings.TrimSpace(ref.Project),
|
|
ID: id,
|
|
URL: strings.TrimSpace(ref.URL),
|
|
Metadata: meta,
|
|
}, nil
|
|
}
|