Documentation
¶
Overview ¶
Package origin is the single owner of "this workspace's origin clients".
Production code constructs a *jira.Client only through Client (the workspace's origin) or Connected (a candidate site/email/token, used when verifying a credential the user just typed). The wiki client is Wiki — the same split (standalone in-process handler vs connected site). jira.New stays exported for tests that stand up httptest servers; a gate test fails if production files grow a new direct call.
Index ¶
- Constants
- Variables
- func Client(cfg *config.Config) (*jira.Client, error)
- func Close() error
- func CloseStandalone(cfg *config.Config) error
- func Connected(site, email, token string) *jira.Client
- func CreatesVersionsByName(w Writer) bool
- func DefaultConfluenceConfig() *config.ConfluenceConfig
- func Describe(cfg *config.Config) (kind, origin string)
- func FoldPairedError(cfg *config.Config, err error) error
- func ForgetLive()
- func FormatLinkType(t jira.IssueLinkType) string
- func FormatLinkTypes(list []jira.IssueLinkType) string
- func InProcessAuthB64() string
- func IsInProcess(cfg *config.Config) bool
- func IsPairingFailure(err error) bool
- func LegacyYAMLPath(dir string) string
- func Linear(cfg *config.Config) (*linear.Client, error)
- func OpenHolder(persist string) int
- func OpenHolderString(persist string) string
- func OwnerStatus(cfg *config.Config) string
- func PairedStatus(cfg *config.Config) (*pairing.Remote, error)
- func PersistPath(dir string) string
- func ResetInProcess()
- func ResolveCreateSource(ctx context.Context, cfg *config.Config, db *store.DB, project string) (string, error)
- func ResolveLinkType(token string, catalog []jira.IssueLinkType) (lt jira.IssueLinkType, reverse bool, err error)
- func SessionsConstructed() uint64
- func SetInProcess(cfg *config.Config, v bool)
- func StandaloneHandler(cfg *config.Config) (http.Handler, error)
- func TransportIsEmbedded(rt http.RoundTripper) bool
- func TransportIsServe(rt http.RoundTripper) bool
- func VerifyPaired(ctx context.Context, endpoint, token string) (jira.User, error)
- func Wiki(cfg *config.Config) (*confluence.Client, error)
- type Attachment
- type Comment
- type CommentVisibility
- type CreateFieldCatalog
- type CreateFieldMeta
- type CreateMetaIssueType
- type CreateMetaProject
- type FieldMeta
- type GadakProbe
- type IssueLinkType
- type IssueLinker
- type LinkTypeHit
- type MediaRef
- type NamedID
- type PairingError
- type Transition
- type User
- type Version
- type VersionCatalog
- type Writer
Constants ¶
const DefaultProjectKey = "STD"
DefaultProjectKey is the project seeded into a new standalone origin so create/createmeta have somewhere to file. Issuetap also creates a project on first write if a caller names another key.
const DefaultSpaceKey = "LOC"
DefaultSpaceKey is the wiki space seeded into a new standalone origin so page create has somewhere to file. Short and obviously local — not a display name, and not a site-specific key.
const LegacyYAMLRel = "origin/issuetap.yaml"
LegacyYAMLRel is the pre-SQLite persist path. When PersistRel is absent and this file exists, NewEmbedded seeds from it as FixturePath once. The YAML is left in place as a rollback asset.
const PersistRel = "origin/issuetap.db"
PersistRel is the workspace-relative path of the issuetap write-through SQLite state file (WAL). The file is the origin; gadak.db remains a disposable cache filled by sync.
const ProbePath = "/api/v1/issues/sync/progress/"
ProbePath and probeTimeout are how port_fallback and views open decide whether a loopback port is a gadak UI serve (X-Gadak / X-Gadak-Profile). They are not used to route origin writes. A leftover serve-origin.json from a previous version is ignored (GDK-936).
const RESTPrefix = "/api/v1/origin"
RESTPrefix is the serve passthrough root. A client request to /rest/api/3/issue is sent to <serve> + RESTPrefix + /rest/api/3/issue. Paired remote clients reach this machine's origin through this prefix.
Variables ¶
var ( ErrUnsupported = errors.New("this origin does not support that") ErrNoVersionCatalog = unsupported("linear: project versions are not supported on this origin") ErrNoIssueLinks = unsupported("linear: issue links are not supported on this origin") ErrNoCreateFields = unsupported("linear: create-time field metadata is not supported on this origin") ErrNoMediaRef = unsupported("linear: inline comment media is not supported; the file is attached to the issue") )
These strings were the linearWriter stubs' Error() values. Callers that type-assert a missing face must return the same text (GDK-641).
ErrUnsupported is the cause of that whole class: a permanent "this origin cannot do that", distinct from a transport failure. failJira branches on it. The four ErrNo* wrap it so errors.Is(err, ErrUnsupported) is true while errors.Is(err, ErrNoIssueLinks) (and the other three) keep matching and Error() stays the original sentence (GDK-685).
var ErrWorkspaceBusy = errors.New("origin: another process is using this workspace (persist is locked); write through its serve, or close it and retry")
ErrWorkspaceBusy is the historical persist-lock refusal (GDK-343). The lock is gone (GDK-936): standalone persist is WAL SQLite, and a second process embeds the same file. The sentinel remains so the REST mapper still answers workspace_busy rather than credential_required (GDK-345) if a caller passes this error in.
var ErrWorkspaceFrozen = errors.New("this workspace is frozen — no requests leave for the origin; unfreeze with `gadak config set frozen false`")
ErrWorkspaceFrozen: frozen means no request leaves for the origin — pulls and writes alike (GDK-507 decision). The gate lives here, on the client mint, so every surface (CLI verbs, REST writes, per-issue resync, the api escape hatch, page writes) refuses in one place instead of each carrying its own check. A scrubbed demo fixture with a live credential must neither re-pollute its mirror nor create real issues on the origin.
var LinearEndpoint string
LinearEndpoint, when non-empty, is the GraphQL URL Linear() installs on the client. Tests point it at httptest; production leaves it empty so New keeps linear.Endpoint. This is not a config.json field — an install URL must not become a persisted setting.
Functions ¶
func Client ¶
Client is the single owner of "this workspace's Jira client". A connected workspace gets the same jira.New(site, email, token) as before. A standalone workspace embeds issuetap over the persist SQLite file (WAL). A paired remote workspace talks to the home serve's RESTPrefix passthrough. BaseURL stays empty so stored browse links are /browse/KEY rather than a fake https origin a person might click.
func Close ¶
func Close() error
Close checkpoints every live standalone origin (WAL) and drops the sessions. Safe to call more than once. The process owner (cmd/gadak main) calls this on the way out. Writes commit before ACK; Close is a checkpoint, not a debounce flush.
In-flight constructors are waited on (they publish, then this snapshots) so Close never closes a half-built Embedded. There is no permanent closed flag: Client after Close must open a new session (persist is the origin; the process is allowed to come back).
func CloseStandalone ¶ added in v0.18.0
CloseStandalone checkpoints and drops the live session for cfg's persist. Waits an in-flight constructor for the same key. No-op when nothing is live. Callers that marked SetInProcess unmark it themselves — this only owns the session.
func Connected ¶
Connected builds a client for an explicit site/email/token — verifying a credential the user just typed, not "this workspace's origin".
func CreatesVersionsByName ¶ added in v0.17.0
CreatesVersionsByName reports whether w mints a project version from a fixVersions add {"name": token}. True for issuetap (standalone in-process, routed serve, paired home). False for Cloud Jira and Linear (GDK-678).
func DefaultConfluenceConfig ¶
func DefaultConfluenceConfig() *config.ConfluenceConfig
DefaultConfluenceConfig is what initStandalone writes so the wiki sync pass is on and scoped to the seeded space. Presence of the block is the on switch (internal/sync/confluence.go).
func Describe ¶
Describe answers doctor: which kind of workspace, and where the origin is. Connected reports "jira" — "jira+linear" when the Linear source is on (no hostname or key either way; doctor is safe to paste). Standalone reports the persist path.
func FoldPairedError ¶ added in v0.17.0
FoldPairedError is the single owner of the remote-device pairing sentence. On a paired workspace, 401 and dial failures become a PairingError whose Error() is the first line a person should see. Other workspaces pass err through.
func ForgetLive ¶
func ForgetLive()
ForgetLive drops cached embedded sessions without closing them. Tests use this to simulate a second process: the serve handler still holds the graph, but Client no longer finds it in this process.
func FormatLinkType ¶ added in v0.17.0
func FormatLinkType(t jira.IssueLinkType) string
FormatLinkType names one type the way both the CLI listing and the HTTP error body name it.
func FormatLinkTypes ¶ added in v0.17.0
func FormatLinkTypes(list []jira.IssueLinkType) string
FormatLinkTypes renders a catalog for an error message or a listing.
func InProcessAuthB64 ¶ added in v0.17.0
func InProcessAuthB64() string
InProcessAuthB64 is the base64 payload of the in-process Basic credential the local CLI presents to the origin passthrough. Exported so the server's pairing gate can rewrite a *validated* Bearer into the exact Authorization shape the embedded issuetap graph has always seen: the gate authenticates the caller, then speaks to the origin as the in-process user it always did (GDK-433).
func IsInProcess ¶ added in v0.18.0
IsInProcess reports whether this process owns cfg's persist.
func IsPairingFailure ¶ added in v0.17.0
IsPairingFailure reports a pairing/dial/auth failure that create must not relabel as a missing --project flag.
func LegacyYAMLPath ¶ added in v0.17.1
LegacyYAMLPath is the absolute pre-SQLite persist path inside a profile directory. Empty dir yields empty path.
func Linear ¶ added in v0.16.1
Linear is the single owner of "this workspace's Linear client" — the same role Wiki plays for Confluence (GDK-258: a third source beside the Jira client, never a facade behind its Transport). There is no standalone variant: issuetap has no Linear surface, and the block carries its own credential rather than the Atlassian one.
func OpenHolder ¶ added in v0.18.1
OpenHolder reports the PID of another live process holding this persist, or 0. This process's own mark is not a holder — the caller is asking whether it must stand aside for someone else.
A marker naming a dead PID is removed here rather than reported: the alternative is a workspace that refuses conversion forever after one crash, with nothing on screen explaining why.
func OpenHolderString ¶ added in v0.18.1
OpenHolderString is OpenHolder rendered for an error message, or "".
func OwnerStatus ¶
OwnerStatus is the doctor line for a standalone workspace. There is no exclusive persist owner after GDK-936 (WAL); leftover serve-origin.json is ignored. Empty when the workspace is not standalone.
func PairedStatus ¶ added in v0.17.0
PairedStatus is the single owner of "is this workspace paired with a remote gadak serve?". status, doctor, profiles, and pairing list read this instead of opening remote-origin.json themselves. Standalone is excluded: the same file on the home machine only carries the local pairing-gate token (`_home`), not a remote origin.
func PersistPath ¶
PersistPath is the absolute issuetap SQLite state path inside a profile directory.
func ResetInProcess ¶ added in v0.18.0
func ResetInProcess()
ResetInProcess clears every ownership mark. Tests only — production unmarks per workspace (Runtime.Close / closeEntry).
func ResolveCreateSource ¶ added in v0.17.2
func ResolveCreateSource(ctx context.Context, cfg *config.Config, db *store.DB, project string) (string, error)
ResolveCreateSource picks the origin a create files to — the create-side sibling of WriterFor's per-key routing. A project the mirror already knows as Linear routes there (same idea as store.KeySource; create has no issue key yet, so the project key is the only routing fact); a Linear-only workspace (no Atlassian credential) always routes to Linear, even before the first team is mirrored. CLI withCreateSession and REST createWriter both call this — one routing rule, not two copies that can drift (GDK-820).
func ResolveLinkType ¶ added in v0.17.0
func ResolveLinkType(token string, catalog []jira.IssueLinkType) (lt jira.IssueLinkType, reverse bool, err error)
ResolveLinkType matches token against the catalog. An all-digit token is a type id and keeps the outward convention. Otherwise the type name and its outward description keep A as the outward side; a match on the inward description swaps A and B. reverse reports that swap.
func SessionsConstructed ¶
func SessionsConstructed() uint64
SessionsConstructed is how many times constructStandalone ran. Tests use a delta to prove a live session was reused.
func SetInProcess ¶
SetInProcess marks/unmarks this process as the persist owner of cfg's workspace. Keyed by persist path — the same key as live — so owning one workspace's persist does not disable routing for any other (STD-3).
func StandaloneHandler ¶
StandaloneHandler is the in-process issuetap HTTP surface for this workspace. The serve RESTPrefix passthrough uses it so a paired remote client lands on the same origin the UI already holds. Always embeds.
func TransportIsEmbedded ¶
func TransportIsEmbedded(rt http.RoundTripper) bool
TransportIsEmbedded reports whether c talks to an in-process issuetap handler. Tests use this instead of naming the unexported type.
func TransportIsServe ¶
func TransportIsServe(rt http.RoundTripper) bool
TransportIsServe reports whether c talks to a live serve passthrough.
func VerifyPaired ¶ added in v0.17.0
VerifyPaired proves a pairing offer before anything is saved (GDK-433 verify-before-save): one GET /rest/api/3/myself over the exact transport the paired workspace would use. A serve that answers 401 surfaces as jira.ErrAuth; an unreachable or broken endpoint surfaces as a transport error — the caller tells those apart without retrying. The offer string itself never enters an error. Deliberately no actor header (GDK-586): verification must not provision an agent identity on an origin the workspace does not exist on yet.
Types ¶
type Attachment ¶ added in v0.17.0
type Attachment = jira.Attachment
type CommentVisibility ¶ added in v0.17.0
type CommentVisibility = jira.CommentVisibility
type CreateFieldCatalog ¶ added in v0.17.0
type CreateFieldCatalog interface {
CreateFields(ctx context.Context, projectIDOrKey, issueTypeID string) ([]CreateFieldMeta, error)
}
CreateFieldCatalog is what this project+type requires and accepts at create time — the create-side sibling of EditMeta (GDK-254). An origin that cannot answer is missing the face; callers degrade, they do not block. Linear: AsCreateFieldCatalog returns ErrNoCreateFields.
func AsCreateFieldCatalog ¶ added in v0.17.0
func AsCreateFieldCatalog(w Writer) (CreateFieldCatalog, error)
AsCreateFieldCatalog returns w as CreateFieldCatalog, or ErrNoCreateFields.
type CreateFieldMeta ¶ added in v0.17.2
type CreateFieldMeta = jira.CreateFieldMeta
type CreateMetaIssueType ¶ added in v0.17.0
type CreateMetaIssueType = jira.CreateMetaIssueType
type CreateMetaProject ¶ added in v0.17.0
type CreateMetaProject = jira.CreateMetaProject
type GadakProbe ¶ added in v0.16.1
GadakProbe classifies a loopback GET to the progress endpoint. Exported so cmd/gadak's port fallback and views open use this single copy (GDK-423). Guards: 700ms context, no Origin header, X-Gadak required, profile from X-Gadak-Profile.
func ProbeGadakOnPort ¶ added in v0.16.1
func ProbeGadakOnPort(port string, timeout time.Duration) GadakProbe
type IssueLinkType ¶ added in v0.17.2
type IssueLinkType = jira.IssueLinkType
type IssueLinker ¶ added in v0.17.0
type IssueLinker interface {
IssueLinkTypes(ctx context.Context) ([]IssueLinkType, error)
LinkIssues(ctx context.Context, typeID, outwardKey, inwardKey string) error
}
IssueLinker is GET /rest/api/3/issueLinkType plus POST /rest/api/3/issueLink. Names and inward/outward descriptions can be renamed; writes send the id. Linear has relations but no counterpart in this adapter yet (GDK-19): AsIssueLinker returns ErrNoIssueLinks.
func AsIssueLinker ¶ added in v0.17.0
func AsIssueLinker(w Writer) (IssueLinker, error)
AsIssueLinker returns w as IssueLinker, or ErrNoIssueLinks.
type LinkTypeHit ¶ added in v0.17.0
type LinkTypeHit struct {
Type jira.IssueLinkType
Reverse bool
}
LinkTypeHit is one catalog entry that matched a token, with the direction the match implies.
type MediaRef ¶ added in v0.17.0
type MediaRef interface {
MediaRef(ctx context.Context, attachmentID string) (mediaID, filename string, err error)
}
MediaRef resolves an attachment id to the media UUID Jira needs in an ADF comment node plus the filename. Linear has no counterpart; AsMediaRef returns ErrNoMediaRef and the comment path already degrades on that error.
func AsMediaRef ¶ added in v0.17.0
AsMediaRef returns w as MediaRef, or ErrNoMediaRef.
type PairingError ¶ added in v0.17.0
type PairingError struct {
// contains filtered or unexported fields
}
PairingError is the remote-device first line for a failed home-serve round trip: cause and next action, no REST method/path.
func (*PairingError) Error ¶ added in v0.17.0
func (e *PairingError) Error() string
type Transition ¶ added in v0.17.0
type Transition = jira.Transition
type VersionCatalog ¶ added in v0.17.0
type VersionCatalog interface {
ProjectVersions(ctx context.Context, projectKey string) ([]Version, error)
CreatesVersionsByName() bool
}
VersionCatalog is GET /rest/api/3/project/{key}/versions. Linear has no counterpart (GDK-516): AsVersionCatalog returns ErrNoVersionCatalog.
CreatesVersionsByName is the issuetap mint-by-name capability (GDK-678): a fixVersions add {"name": token} creates the version when it is missing from the catalog. Cloud Jira is false — unknown names 400, and creating a version is a separate project-admin permission. Same shape as sync.OSNotifier.Supported: a boolean on the face that already owns the verb, not a workspace-kind string.
func AsVersionCatalog ¶ added in v0.17.0
func AsVersionCatalog(w Writer) (VersionCatalog, error)
AsVersionCatalog returns w as VersionCatalog, or ErrNoVersionCatalog.
type Writer ¶ added in v0.16.1
type Writer interface {
CreateMeta(ctx context.Context, projects []string) ([]CreateMetaProject, error)
CreateIssue(ctx context.Context, fields map[string]any) (string, error)
EditMeta(ctx context.Context, key string) (map[string]FieldMeta, error)
UpdateFields(ctx context.Context, key string, fields map[string]any) error
EditIssue(ctx context.Context, key string, fields, update map[string]any) error
Transitions(ctx context.Context, key string) ([]Transition, error)
Transition(ctx context.Context, key, transitionID string, fields map[string]any, comment json.RawMessage) error
AddComment(ctx context.Context, key string, adf json.RawMessage, visibility *CommentVisibility, internal bool) (Comment, error)
SetAssignee(ctx context.Context, key, accountID string) error
SearchUsers(ctx context.Context, query string) ([]User, error)
PriorityCatalog(ctx context.Context) ([]NamedID, error)
Upload(ctx context.Context, key, filename string, file io.Reader) ([]Attachment, error)
}
Writer is the write surface of an origin: the verbs every origin implements and the server and CLI call when writing through. Jira (connected) and standalone (issuetap speaks the Jira API) satisfy it via jiraWriter wrapping *jira.Client; a Linear adapter implements the same verbs over GraphQL (GDK-358). The method vocabulary is Jira-shaped (EditMeta, Transitions) so existing callers keep working; the types are origin DTOs (GDK-665), not internal/jira names.
Capability negotiation stays EditMeta/CreateMeta: an origin that cannot edit a field omits it there, and the existing UI already turns fields on and off from that answer. An unsupported verb returns an honest error, never a silent no-op.
Optional faces (VersionCatalog, IssueLinker, CreateFieldCatalog, MediaRef) are not part of Writer. Callers type-assert via As*; a missing face returns the matching ErrNo* string, never a silent no-op (GDK-641).
func WriterFor ¶ added in v0.16.1
WriterFor picks the write path for one issue's source — the caller reads it from the mirror (store.KeySource), because a key's shape cannot tell a Linear "MID-5" from a Jira "MID-5". Jira and standalone rows share the Jira client (wrapped); "linear" routes to the GraphQL adapter (GDK-361). An empty source (a key the mirror does not know yet, or a create) routes to the default origin.