origin

package
v0.18.1 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

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

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

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

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

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

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

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

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

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

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

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

func Client(cfg *config.Config) (*jira.Client, error)

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

func CloseStandalone(cfg *config.Config) error

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

func Connected(site, email, token string) *jira.Client

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

func CreatesVersionsByName(w Writer) bool

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

func Describe(cfg *config.Config) (kind, origin string)

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

func FoldPairedError(cfg *config.Config, err error) error

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

func IsInProcess(cfg *config.Config) bool

IsInProcess reports whether this process owns cfg's persist.

func IsPairingFailure added in v0.17.0

func IsPairingFailure(err error) bool

IsPairingFailure reports a pairing/dial/auth failure that create must not relabel as a missing --project flag.

func LegacyYAMLPath added in v0.17.1

func LegacyYAMLPath(dir string) string

LegacyYAMLPath is the absolute pre-SQLite persist path inside a profile directory. Empty dir yields empty path.

func Linear added in v0.16.1

func Linear(cfg *config.Config) (*linear.Client, error)

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

func OpenHolder(persist string) int

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

func OpenHolderString(persist string) string

OpenHolderString is OpenHolder rendered for an error message, or "".

func OwnerStatus

func OwnerStatus(cfg *config.Config) string

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

func PairedStatus(cfg *config.Config) (*pairing.Remote, error)

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

func PersistPath(dir string) string

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

func SetInProcess(cfg *config.Config, v bool)

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

func StandaloneHandler(cfg *config.Config) (http.Handler, error)

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

func VerifyPaired(ctx context.Context, endpoint, token string) (jira.User, error)

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.

func Wiki

func Wiki(cfg *config.Config) (*confluence.Client, error)

Wiki is the single owner of "this workspace's Confluence client". A connected workspace gets confluence.New(site, email, token). A standalone workspace shares the in-process issuetap handler with Client.

Types

type Attachment added in v0.17.0

type Attachment = jira.Attachment

type Comment added in v0.17.0

type Comment = jira.Comment

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 FieldMeta added in v0.17.0

type FieldMeta = jira.FieldMeta

type GadakProbe added in v0.16.1

type GadakProbe struct {
	IsGadak bool
	Profile string
}

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

func AsMediaRef(w Writer) (MediaRef, error)

AsMediaRef returns w as MediaRef, or ErrNoMediaRef.

type NamedID added in v0.17.0

type NamedID = jira.NamedID

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 User added in v0.17.0

type User = jira.User

type Version added in v0.17.2

type Version = jira.Version

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

func WriterFor(cfg *config.Config, source string) (Writer, error)

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.

Jump to

Keyboard shortcuts

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