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 AdvertisePath(dir string) string
- func Client(cfg *config.Config) (*jira.Client, error)
- func Close() error
- func Connected(site, email, token string) *jira.Client
- func DefaultConfluenceConfig() *config.ConfluenceConfig
- func Describe(cfg *config.Config) (kind, origin string)
- func ForgetLive()
- func Linear(cfg *config.Config) (*linear.Client, error)
- func OwnerStatus(cfg *config.Config) string
- func PersistPath(dir string) string
- func RemoveAdvertise(dir string) error
- func SessionInFlight() int64
- func SessionsConstructed() uint64
- func SessionsDiscarded() uint64
- func SetInProcess(v bool)
- func StandaloneHandler(cfg *config.Config) (http.Handler, error)
- func TransportIsEmbedded(rt http.RoundTripper) bool
- func TransportIsServe(rt http.RoundTripper) bool
- func Wiki(cfg *config.Config) (*confluence.Client, error)
- func WriteAdvertise(dir, addr string) error
- type Advertise
- type GadakProbe
- type Writer
Constants ¶
const AdvertiseRel = "serve-origin.json"
AdvertiseRel is the profile-relative runtime file a standalone serve writes so other processes can find the persist owner. Connected workspaces never write it.
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 PersistRel = "origin/issuetap.yaml"
PersistRel is the workspace-relative path of the issuetap write-through snapshot. The file is the origin; the SQLite mirror remains a disposable cache filled by sync.
const ProbePath = "/api/v1/issues/sync/progress/"
ProbePath and probeTimeout match cmd/gadak/port_fallback.go. Origin cannot import cmd, so the values and the header contract (X-Gadak, X-Gadak-Profile) are copied here including the 700ms bound — a longer timeout would stall every CLI write when no serve is up. Exported so the desktop app's origin-only listener (GDK-340) can serve exactly the path the probe hits.
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.
Variables ¶
var ErrWorkspaceBusy = errors.New("origin: another process is using this workspace (persist is locked); write through its serve, or close it and retry")
ErrWorkspaceBusy means another process holds the persist lock for this workspace and did not advertise a routable origin. Embedding anyway would open a second graph over the same file (GDK-343: last Close wins, silent loss), so the caller gets this instead.
Functions ¶
func AdvertisePath ¶
AdvertisePath is the absolute runtime-file path inside a profile directory.
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 gets a client whose Transport is the in-process issuetap handler, unless a live `gadak serve` for this profile advertised itself — then Transport is that serve's origin passthrough so persist has one owner. 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 flushes every live standalone origin and drops the sessions. Safe to call more than once. The process owner (cmd/gadak main) calls this on the way out so the last PersistDebounce window is not lost.
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 Connected ¶
Connected builds a client for an explicit site/email/token — verifying a credential the user just typed, not "this workspace's origin".
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 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 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 OwnerStatus ¶
OwnerStatus is the doctor line: "serve pid=… addr=…" or "embedded (no live serve)". Empty when the workspace is not standalone.
func PersistPath ¶
PersistPath is the absolute issuetap snapshot path inside a profile directory.
func RemoveAdvertise ¶
RemoveAdvertise deletes the runtime file. Missing is not an error — crash leftover is the other path, and the next probe treats a dead file as "no live serve".
func SessionInFlight ¶
func SessionInFlight() int64
SessionInFlight is how many standaloneSession constructions are running outside mu. Same shape as store.WriteBusyRetries: a cheap accessor, no logs.
func SessionsConstructed ¶
func SessionsConstructed() uint64
SessionsConstructed is how many times constructStandalone ran. Tests use a delta to prove a routed Client did not open a second persist graph.
func SessionsDiscarded ¶
func SessionsDiscarded() uint64
SessionsDiscarded is how many constructed sessions lost the publish race and were closed. Same shape as store.WriteBusyRetries.
func SetInProcess ¶
func SetInProcess(v bool)
SetInProcess marks this process as the persist owner (`gadak serve` on a standalone workspace). Client and Wiki then always use the embedded session and never proxy to the advertise file — that would loop back onto this same listener.
func StandaloneHandler ¶
StandaloneHandler is the in-process issuetap HTTP surface for this workspace. Serve's origin passthrough uses it so CLI writes land on the same graph the UI already holds. Always embeds — never routes — so the serve process cannot proxy to itself through this entry.
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 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.
func WriteAdvertise ¶
WriteAdvertise publishes the final listen address of a standalone serve. Atomic write (temp + rename) at 0600, same as config.Save.
Types ¶
type Advertise ¶
type Advertise struct {
Addr string `json:"addr"`
PID int `json:"pid"`
StartedAt string `json:"startedAt"`
}
Advertise is the JSON document in AdvertiseRel.
type GadakProbe ¶ added in v0.16.1
GadakProbe classifies a loopback GET to the progress endpoint. Exported so cmd/gadak's port fallback uses 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 Writer ¶ added in v0.16.1
type Writer interface {
CreateMeta(ctx context.Context, projects []string) ([]jira.CreateMetaProject, error)
CreateIssue(ctx context.Context, fields map[string]any) (string, error)
EditMeta(ctx context.Context, key string) (map[string]jira.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) ([]jira.Transition, error)
Transition(ctx context.Context, key, transitionID string) error
AddComment(ctx context.Context, key string, adf json.RawMessage) (jira.Comment, error)
SetAssignee(ctx context.Context, key, accountID string) error
SearchUsers(ctx context.Context, query string) ([]jira.User, error)
PriorityCatalog(ctx context.Context) ([]jira.NamedID, error)
Upload(ctx context.Context, key, filename string, file io.Reader) ([]jira.Attachment, error)
MediaRef(ctx context.Context, attachmentID string) (mediaID, filename string, err error)
}
Writer is the write surface of an origin: exactly the verbs the server and CLI call when writing through. Jira (connected) and standalone (issuetap speaks the Jira API) both satisfy it via *jira.Client; a Linear adapter implements the same verbs over GraphQL (GDK-358).
The vocabulary is deliberately Jira-shaped — including the types — because two of the three origins already speak it natively. 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.
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; "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.