jira

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: 19 Imported by: 0

Documentation

Overview

Package jira is the Atlassian Cloud REST client: read paths plus user-initiated writes.

The token lives only in the Authorization header. It is never put in an error, a log line or a URL (constitution article 8), which is why transport reports the method and path but never the request itself.

Index

Constants

View Source
const Layout = "2006-01-02T15:04:05.000-0700"

Layout is how Jira stamps timestamps: ISO-8601 with a numeric offset and no colon in it, which is why time.RFC3339 does not parse them.

Variables

View Source
var DefaultBackoff = httppolicy.DefaultBackoff

DefaultBackoff is the first wait New applies, doubling per attempt and capped at httppolicy.MaxWait. Tests may assign 0 and restore it with t.Cleanup.

View Source
var DefaultCreatesVersionsByName = false

DefaultCreatesVersionsByName is copied onto Client at New. Production is false (Cloud Jira). Tests that drive the issuetap mint-by-name path through a Connected httptest client set this and restore with t.Cleanup (same seam as DefaultRetries). origin.transportJira then enables it for real issuetap clients regardless of this default.

View Source
var DefaultRetries = httppolicy.DefaultRetries

DefaultRetries is the production retry budget New applies (total attempts). Tests may assign a smaller value and restore it with t.Cleanup. The value is httppolicy.DefaultRetries; this var exists so tests can override it.

View Source
var ErrAuth = atlhttp.Auth("jira")

ErrAuth is the Jira-named rejected credential. It unwraps to atlhttp.ErrAuth so Watch detects it without a per-source branch. Error() keeps the "jira:" prefix so last_error names the source. Callers keep using errors.Is(err, jira.ErrAuth).

Functions

func Doc

func Doc(text string, mentions map[string]string) json.RawMessage

Doc builds the ADF document a comment body has to be. The composer sends plain text with `@Display Name` typed into it plus the account ids it resolved, so this is where those become real mention nodes — a mention that stays plain text notifies nobody, which is the whole point of typing it.

mentions maps display name to account id.

func DocWithMedia

func DocWithMedia(text string, mentions map[string]string, media []Media) json.RawMessage

DocWithMedia is Doc plus inline images, appended after the text — where a screenshot belongs in a comment that describes it.

func FormatTransition added in v0.17.0

func FormatTransition(t Transition) string

func ISOTime

func ISOTime(s string) string

ISOTime normalizes a Jira timestamp to the ISO-8601 UTC form every stored column uses (data-model.md, "Conventions"), so string comparison sorts chronologically. An unparseable value passes through untouched.

func IsBotAccountType added in v0.17.0

func IsBotAccountType(t string) bool

IsBotAccountType is the one bot judgement (GDK-590): "agent" (standalone actor accounts) and "app" (Cloud Connect accounts) are bots, everything else — "atlassian", "customer", "" — is a human or unknown. Web, CLI and MCP all call this; nothing re-derives it from display names.

func JoinTransitions added in v0.17.0

func JoinTransitions(list []Transition) string

func PickTransition added in v0.17.0

func PickTransition(key, want string, list []Transition) (string, error)

PickTransition resolves want against the issue's available transitions. Order: transition id, target status id, transition name / target status name, then category tokens (new|inprogress|done). Two landings in the same category refuse rather than picking the first. A token that is one transition's id and a different transition's to.id is also refused.

func PlainText

func PlainText(raw json.RawMessage) string

PlainText flattens an ADF document to plain text: it is what FTS indexes and what makes a repro-steps custom field searchable. A field that holds a bare string (the older wiki-markup shape) passes through unchanged.

func ReachableCategories added in v0.17.0

func ReachableCategories(list []Transition) []string

func StatusCategoryToken added in v0.17.0

func StatusCategoryToken(s string) (string, bool)

StatusCategoryToken accepts only the three values data-model.md documents. Category and jql.mapStatusCategory both fold aliases (todo, indeterminate) onto those values; applying either to the user token would reopen the localization trap this command is closing. Apply's category no-op uses this same function so a token PickTransition would refuse cannot no-op.

Types

type APIError

type APIError struct {
	Status   int
	Messages []string
	Errors   map[string]string
	Body     string
}

APIError is a non-2xx answer with its body parsed. Errors is Jira's per-field rejection map, which the server passes to the client as `jira_errors` so the message lands on the input that caused it. Neither the request nor the credential is ever recorded here (constitution article 8).

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Message

func (e *APIError) Message() string

Message is the first thing worth showing a person: Jira's own message when it sent one, the field errors when it only rejected fields, the raw body last.

type Attachment

type Attachment struct {
	ID       string `json:"id"`
	Filename string `json:"filename"`
	MimeType string `json:"mimeType"`
	Size     int64  `json:"size"`
	Author   User   `json:"author"`
	Created  string `json:"created"`
}

type Changelog

type Changelog struct {
	Total      int       `json:"total"`
	MaxResults int       `json:"maxResults"`
	Histories  []History `json:"histories"`
}

Changelog as returned inline by expand=changelog. Total above len(Histories) means it was truncated and the dedicated endpoint has to be paged.

type ClaimResult added in v0.17.0

type ClaimResult struct {
	Key       string `json:"key"`
	Assignee  User   `json:"assignee"`
	Status    Status `json:"status"`
	ClaimedAt string `json:"claimedAt"`
}

ClaimResult is the answer from POST /issue/{key}/claim: who holds the issue now, in which status, since when.

type Client

type Client struct {
	HTTP *http.Client
	// Retries is the total number of attempts per request; Backoff is the first
	// wait, doubling per attempt and capped at 30 s.
	Retries int
	Backoff time.Duration
	// contains filtered or unexported fields
}

Client talks to one Atlassian Cloud site over REST. The credential is held only as an Authorization header value; it is never copied into an error, a log line, or a URL. Retries and Backoff apply to reads; writes use a narrower policy (see write).

func New

func New(site, email, token string) *Client

New builds a Client for site using Basic auth (email:token). The HTTP client times out at httppolicy.DefaultTimeout; Retries is DefaultRetries; the first Backoff is DefaultBackoff.

origin.Client is the only production construction path for this workspace's Jira client. origin.Connected is the candidate-credential sibling; both live in internal/origin and call New. Tests may call New to stand up httptest servers. internal/origin/direct_new_gate_test.go fails if a new production call site appears outside that package.

func (*Client) AddComment

func (c *Client) AddComment(ctx context.Context, key string, adf json.RawMessage, visibility *CommentVisibility, internal bool) (Comment, error)

AddComment posts an ADF body (not plain text). Mentions must already be mention nodes — a leftover "@Name" string notifies nobody. visibility is sent as Jira's visibility object when non-nil. internal adds the JSM sd.public.comment property. Neither key is present when the corresponding argument is unset, so a flagless CLI comment matches the previous POST body.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL is the site origin, used to build deep links.

func (*Client) Changelog

func (c *Client) Changelog(ctx context.Context, key string) ([]History, error)

Changelog pages the full history of one issue, for the issues whose inline expand=changelog came back truncated.

func (*Client) Claim added in v0.17.0

func (c *Client) Claim(ctx context.Context, key, transitionID string, takeOver bool) (ClaimResult, error)

Claim is POST /issue/{key}/claim — issuetap's claim extension (GDK-591): assignee plus the in-progress transition as one mutation, so of two agents claiming concurrently exactly one wins. An empty transitionID lets the origin pick the first destination whose category is in-progress; takeOver replaces another assignee that already holds the issue in progress. Atlassian Cloud has no claim route and answers 404 — internal/claim falls back to the two calls this route fuses there. The acting account is the credential (or X-Issuetap-Actor on an issuetap origin), never the body.

func (*Client) Comments

func (c *Client) Comments(ctx context.Context, key string) ([]Comment, error)

Comments pages every comment on one issue, for the issues with more than the inline limit.

func (*Client) Count

func (c *Client) Count(ctx context.Context, jql string) (int, error)

Count returns Jira's approximate issue count for a JQL. It exists only to give progress output a denominator, so a failure is not the caller's problem: callers treat any error as "unknown" and keep going.

func (*Client) CreateFields added in v0.17.0

func (c *Client) CreateFields(ctx context.Context, projectIDOrKey, issueTypeID string) ([]CreateFieldMeta, error)

CreateFields pages GET /issue/createmeta/{project}/issuetypes/{type}. The expand=projects.issuetypes.fields form is the discarded path; this is the current Cloud list (fields[], startAt/maxResults/total).

func (*Client) CreateIssue

func (c *Client) CreateIssue(ctx context.Context, fields map[string]any) (string, error)

CreateIssue returns the new issue's key.

func (*Client) CreateMeta

func (c *Client) CreateMeta(ctx context.Context, projects []string) ([]CreateMetaProject, error)

CreateMeta lists what can be created. Restricted to the configured projects: the site-wide answer is large and most of it is unreachable from this UI.

func (*Client) CreatesVersionsByName added in v0.17.0

func (c *Client) CreatesVersionsByName() bool

CreatesVersionsByName is true when a fixVersions add {"name": token} mints the version on this origin (issuetap). Cloud Jira is false: unknown names 400, and creating a version is a separate project-admin permission (GDK-678).

func (*Client) DevStatusPRCount added in v0.17.0

func (c *Client) DevStatusPRCount(ctx context.Context, issueID string) (int, error)

DevStatusPRCount reads the summary's pullrequest count — one cheap call to decide whether the detail call is worth making. issueID is the origin's issue id verbatim (numeric on Cloud), never the key.

func (*Client) DevStatusPRs added in v0.17.0

func (c *Client) DevStatusPRs(ctx context.Context, issueID string) ([]DevPR, error)

DevStatusPRs reads the pull requests the panel holds for one issue. applicationType=GitHub is what Cloud's own frontend sends for the GitHub app (and issuetap accepts any value); the response is parsed defensively — unknown fields are ignored, missing ones stay zero.

func (*Client) EditIssue added in v0.14.1

func (c *Client) EditIssue(ctx context.Context, key string, fields, update map[string]any) error

EditIssue PUTs /issue/{key} with fields and/or update. Either map may be empty; empty maps are omitted so a labels-only edit is {"update":…} only.

func (*Client) EditMeta

func (c *Client) EditMeta(ctx context.Context, key string) (map[string]FieldMeta, error)

EditMeta returns the fields this user may edit on this issue, keyed by field id.

func (*Client) EnableNameCreatedVersions added in v0.17.0

func (c *Client) EnableNameCreatedVersions()

EnableNameCreatedVersions marks this client as talking to an origin that mints a project version from {"name": token} on a fixVersions add. origin.transportJira is the production caller (issuetap in-process, routed serve, and paired home).

func (*Client) Fields

func (c *Client) Fields(ctx context.Context) ([]FieldInfo, error)

Fields returns every system and custom field the site exposes to this user.

func (*Client) IssueLinkTypes added in v0.17.0

func (c *Client) IssueLinkTypes(ctx context.Context) ([]IssueLinkType, error)

IssueLinkTypes is GET /rest/api/3/issueLinkType — the site catalog.

func (*Client) IssueStatus added in v0.17.0

func (c *Client) IssueStatus(ctx context.Context, key string) (Status, *User, error)

IssueStatus is GET /issue/{key}?fields=status,assignee — the two facts a claim must judge locally on an origin with no atomic claim route (Cloud): is the issue in progress, and who holds it. Nothing else is fetched. The answer nests under "fields" like every GET /issue/{key} does.

func (*Client) LinkDevBuild added in v0.17.0

func (c *Client) LinkDevBuild(ctx context.Context, issueID string, state DevBuildState, number, url string) (DevBuild, error)

LinkDevBuild posts one build record (GDK-592). state is the closed three-bucket vocabulary; url and number are optional keys (the origin requires one) and are omitted from the body when empty.

func (*Client) LinkDevDeployment added in v0.17.0

func (c *Client) LinkDevDeployment(ctx context.Context, issueID, environment, state, url string) (DevDeployment, error)

LinkDevDeployment posts one deployment record (GDK-592). environment and state are required by the origin; url is optional — omitted from the body when empty so the origin keys the row by its environment. The actor is never sent — issuetap stamps it from the request identity.

func (*Client) LinkDevPR added in v0.17.0

func (c *Client) LinkDevPR(ctx context.Context, issueID, prURL, name, author, branch string, status DevPRStatus) (DevPR, error)

LinkDevPR posts one pull-request link to the origin's dev-status store. Only issuetap (standalone) implements the endpoint — Jira Cloud's panel is written by its marketplace apps, so a connected origin answers 404 and the caller says so instead of pretending. author (the PR author's login) and branch (the head ref) are optional (GDK-589): empty ones are omitted so a re-link without them keeps what the origin already holds, and an older origin ignores both keys. The actor is never sent — issuetap stamps it from the request identity.

func (*Client) LinkIssues added in v0.17.0

func (c *Client) LinkIssues(ctx context.Context, typeID, outwardKey, inwardKey string) error

LinkIssues is POST /rest/api/3/issueLink. outwardIssue is the subject of the type's outward description (MKY-1 duplicates HSP-1 when outward is MKY-1). 201/200 with an empty body is success.

func (*Client) MediaRef

func (c *Client) MediaRef(ctx context.Context, attachmentID string) (mediaID, filename string, err error)

MediaRef resolves an attachment id to both the media UUID Jira needs in an ADF node and the filename our own renderer matches on (`alt`), which is what makes an inline image resolve without persisting the UUID anywhere.

There is no documented endpoint for this. Requesting the attachment's content answers 3xx to a pre-signed media URL that carries the UUID, so the redirect is read rather than followed — following it would download the whole file for a string, and the credential must not travel to the media host.

func (*Client) MyFilters added in v0.13.0

func (c *Client) MyFilters(ctx context.Context) ([]SavedFilter, error)

MyFilters returns filters the user owns, plus visible favourites when includeFavourites is honoured (Cloud's GET /filter/my).

func (*Client) Myself

func (c *Client) Myself(ctx context.Context) (User, error)

Myself verifies a credential and identifies its owner. It is the only call `PUT credential/` makes before storing a token.

func (*Client) Priorities

func (c *Client) Priorities(ctx context.Context) ([]string, error)

Priorities returns the site's priority names, most urgent first, which is the order priority_rank counts from.

func (*Client) PriorityCatalog

func (c *Client) PriorityCatalog(ctx context.Context) ([]NamedID, error)

PriorityCatalog is the site's priority list, most urgent first. Names are in the account language; writes should send the id.

func (*Client) ProjectVersions added in v0.17.0

func (c *Client) ProjectVersions(ctx context.Context, projectKey string) ([]Version, error)

ProjectVersions is GET /rest/api/3/project/{key}/versions — the project's version catalog. Names can be renamed; writes should send the id.

func (*Client) Projects

func (c *Client) Projects(ctx context.Context, limit int) (list []Project, truncated bool, err error)

Projects lists the projects this credential can browse, newest API first (`/project/search` is the only paged, permission-filtered listing).

It stops after limit projects and reports truncated=true, because a very large site would otherwise turn a first-run picker into hundreds of requests.

func (*Client) Raw

func (c *Client) Raw(ctx context.Context, method, path string, body []byte, mutating bool) (status int, out []byte, err error)

Raw sends a request and returns the HTTP status and response body without JSON decoding. Path must be site-relative (leading "/"); absolute URLs and scheme-relative paths are rejected so the Authorization header never leaves the configured site. mutating selects the write retry policy (429/503 only).

A completed HTTP response always returns err == nil with the status and body (including non-2xx). err is reserved for transport failures and bad paths.

func (*Client) Resolutions added in v0.17.0

func (c *Client) Resolutions(ctx context.Context) ([]NamedID, error)

Resolutions is GET /rest/api/3/resolution — the site catalog. Names are in the account language; writes should send the id.

func (*Client) Search

func (c *Client) Search(ctx context.Context, jql string, fields []string, withChangelog bool, fn func([]Issue) error) error

Search pages a JQL query and calls fn once per page, which is what lets sync commit page by page. Pagination is by nextPageToken: the legacy startAt search is deprecated and drifts under concurrent writes.

func (*Client) SearchUsers

func (c *Client) SearchUsers(ctx context.Context, query string) ([]User, error)

SearchUsers backs the assignee picker. Jira's own endpoint decides what matches; there is no local user table to search.

func (*Client) SetAssignee

func (c *Client) SetAssignee(ctx context.Context, key, accountID string) error

SetAssignee assigns or, with an empty id, unassigns. Jira distinguishes "no assignee" (null) from "default assignee" (-1); the UI only ever asks for the former.

func (*Client) Statuses

func (c *Client) Statuses(ctx context.Context) (map[string]string, error)

Statuses maps every status id on the site to its category. This is the input the derived-field rules need, because a changelog entry carries ids only.

func (*Client) TakeUsage

func (c *Client) TakeUsage() Usage

TakeUsage returns the current counters and zeroes the numeric fields so a flusher can accumulate into daily totals without double-counting.

LastThrottledAt is a timestamp, not a counter: it is included in the snapshot but is NOT cleared. The in-process "last 429" stays visible until the process exits or a later 429 overwrites it.

func (*Client) Transition

func (c *Client) Transition(ctx context.Context, key, transitionID string, fields map[string]any, comment json.RawMessage) error

Transition performs the transition id on key. fields and comment are omitted from the body when empty — a screen that does not list a field rejects it with 400, so an empty map is not sent. comment is ADF (callers use Doc). Only 429 and 503 are retried, because a 500 may mean Jira already acted.

func (*Client) Transitions

func (c *Client) Transitions(ctx context.Context, key string) ([]Transition, error)

Transitions lists the status changes Jira will currently accept on key. Each entry's To carries StatusCategory so callers can key on the stable category; names are localized per account.

func (*Client) UpdateFields

func (c *Client) UpdateFields(ctx context.Context, key string, fields map[string]any) error

UpdateFields sets raw field values. The caller is responsible for the shape each field id expects, which EditMeta describes.

func (*Client) Upload

func (c *Client) Upload(ctx context.Context, key, filename string, file io.Reader) ([]Attachment, error)

Upload attaches one file. Jira requires the nosniff header on this endpoint and answers with the created attachments.

ponytail: buffers the whole file in memory. Fine for the screenshots this is for; stream with io.Pipe if someone starts attaching video.

func (*Client) Usage

func (c *Client) Usage() Usage

Usage returns the current counters without resetting them.

type Comment

type Comment struct {
	ID         string             `json:"id"`
	Author     User               `json:"author"`
	Body       json.RawMessage    `json:"body"`
	Created    string             `json:"created"`
	Updated    string             `json:"updated"`
	Visibility *CommentVisibility `json:"visibility"`
	JsdPublic  *bool              `json:"jsdPublic"` // nil = key absent; false ≠ absent
}

type CommentPage

type CommentPage struct {
	Comments   []Comment `json:"comments"`
	Total      int       `json:"total"`
	MaxResults int       `json:"maxResults"`
	StartAt    int       `json:"startAt"`
}

type CommentVisibility added in v0.17.0

type CommentVisibility struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

CommentVisibility is Jira's comment restriction. Type is "role" or "group"; Value is the role or group name. Unrestricted comments omit the JSON key (nil here). gadak mirrors the fields; it does not compute who may read the comment.

type CreateFieldMeta added in v0.17.0

type CreateFieldMeta struct {
	FieldID         string `json:"fieldId"`
	Name            string `json:"name"`
	Required        bool   `json:"required"`
	HasDefaultValue bool   `json:"hasDefaultValue"`
	Schema          struct {
		Type   string `json:"type"`
		Items  string `json:"items"`
		Custom string `json:"custom"`
	} `json:"schema"`
	AllowedValues []struct {
		ID    string `json:"id"`
		Value string `json:"value"`
		Name  string `json:"name"`
	} `json:"allowedValues"`
}

CreateFieldMeta is one field Jira lists at create time. Distinct from FieldMeta: createmeta fields are a list with fieldId on each object and carry hasDefaultValue; editmeta is a map keyed by field id with no fieldId on the value (GDK-254). Schema matches FieldMeta's anonymous shape.

type CreateMetaIssueType added in v0.17.0

type CreateMetaIssueType struct {
	ID               string `json:"id"`
	Name             string `json:"name"`
	UntranslatedName string `json:"untranslatedName,omitempty"`
	Subtask          bool   `json:"subtask,omitempty"`
	HierarchyLevel   int    `json:"hierarchyLevel,omitempty"`
}

CreateMetaIssueType is one creatable issue type. Distinct from NamedID: priorities, resolutions, components, and versions share {id,name} but have no hierarchy, and putting subtask/hierarchyLevel/untranslatedName on NamedID would leak meaningless fields onto those catalogs. JSON names match Jira's createmeta object (id, name, untranslatedName, subtask, hierarchyLevel). omitempty keeps a standard type (false, 0, empty untranslatedName) looking the way it did before these fields existed.

func (CreateMetaIssueType) NamedID added in v0.17.0

func (t CreateMetaIssueType) NamedID() NamedID

NamedID is the id/name pair FormatTypes, NeedTypeError, and Priority matching use. Hierarchy is dropped on purpose: those catalogs share {id,name} and have no rank. create.Type matches CreateMetaIssueType directly so it can see subtask, hierarchyLevel, and untranslatedName.

type CreateMetaProject

type CreateMetaProject struct {
	Key        string                `json:"key"`
	Name       string                `json:"name"`
	IssueTypes []CreateMetaIssueType `json:"issuetypes"`
}

CreateMetaProject is one project a person may file into, with its issue types.

func (CreateMetaProject) NamedTypes added in v0.17.0

func (p CreateMetaProject) NamedTypes() []NamedID

NamedTypes is the id/name catalog FormatTypes and NeedTypeError consume.

type DevBuild added in v0.17.0

type DevBuild struct {
	ID     string     `json:"id"`
	URL    string     `json:"url"`
	Number string     `json:"number"`
	State  string     `json:"state"`
	Actor  DevPRActor `json:"actor"`
}

DevBuild is one build record the link POST answers (GDK-592). Number is the build number as a string; a url-less build's ID is build:<number>.

type DevBuildState added in v0.17.0

type DevBuildState string

DevBuildState is the build-record state vocabulary (GDK-592): the three buckets the dev-status summary counts. Deployment states are free-form — only "successful" is load-bearing there — so they stay plain strings.

const (
	DevBuildSuccessful DevBuildState = "successful"
	DevBuildFailed     DevBuildState = "failed"
	DevBuildUnknown    DevBuildState = "unknown"
)

func ParseDevBuildState added in v0.17.0

func ParseDevBuildState(s string) (DevBuildState, bool)

ParseDevBuildState accepts the CLI/origin tokens (any case); anything outside the three buckets is rejected — an in-progress build is not a state this vocabulary has.

type DevDeployment added in v0.17.0

type DevDeployment struct {
	ID          string     `json:"id"`
	URL         string     `json:"url"`
	Environment string     `json:"environment"`
	State       string     `json:"state"`
	Actor       DevPRActor `json:"actor"`
}

DevDeployment is one deployment record the link POST answers (GDK-592). issuetap's own vocabulary — Cloud's detail rows for deployments were never captured, so no read path produces this type; it exists for the write's 201 echo. A url-less deployment's ID is environment:<name>.

type DevPR added in v0.17.0

type DevPR struct {
	ID     string      `json:"id"`
	URL    string      `json:"url"`
	Name   string      `json:"name"`
	Status DevPRStatus `json:"status"`
	// Author is the pull request's author. Actor is who WROTE the link —
	// issuetap stamps it from the request identity and serves it back;
	// Cloud has no such field and it stays empty. Different axes — a bot
	// links a human's PR — never merged (GDK-589). Source is the head ref.
	Author DevPRAuthor `json:"author"`
	Source DevPRSource `json:"source"`
	Actor  DevPRActor  `json:"actor"`
}

DevPR is one pull request from the dev-status detail payload.

type DevPRActor added in v0.17.0

type DevPRActor struct {
	AccountID   string `json:"accountId"`
	DisplayName string `json:"displayName"`
}

DevPRActor is issuetap's extension naming who wrote the link: accountId is the X-Issuetap-Actor slug, displayName its human form. Cloud's dev-status has no such block — both fields stay empty there.

type DevPRAuthor added in v0.17.0

type DevPRAuthor struct {
	Name string `json:"name"`
}

DevPRAuthor is the PR author block (Cloud vocabulary: author.name is the GitHub login).

type DevPRSource added in v0.17.0

type DevPRSource struct {
	Branch string `json:"branch"`
}

DevPRSource is where the PR heads from (Cloud vocabulary: source.branch is the head ref).

type DevPRStatus added in v0.17.0

type DevPRStatus string

DevPRStatus is the origin's development-panel pull-request state (Jira Cloud / issuetap). These three tokens are the whole vocabulary.

const (
	DevPROpen     DevPRStatus = "OPEN"
	DevPRMerged   DevPRStatus = "MERGED"
	DevPRDeclined DevPRStatus = "DECLINED"
)

func DevPRStatusFromGitHub added in v0.17.0

func DevPRStatusFromGitHub(ghState string) DevPRStatus

DevPRStatusFromGitHub maps `gh pr list --json state` onto the origin vocabulary. CLOSED is DECLINED; anything else (including OPEN) is OPEN.

func ParseDevPRStatus added in v0.17.0

func ParseDevPRStatus(s string) (DevPRStatus, bool)

ParseDevPRStatus accepts the origin/CLI tokens (any case). Unknown input is rejected — callers that need a default (gh scan) use DevPRStatusFromGitHub.

func (DevPRStatus) Stored added in v0.17.0

func (s DevPRStatus) Stored() string

Stored is the mirror-column form (lowercase). store.DevLink.Status stores this so list/detail JSON stays "open"|"merged"|"declined".

type FieldInfo

type FieldInfo struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Custom bool   `json:"custom"`
	Schema struct {
		Type   string `json:"type"`
		Custom string `json:"custom"`
		Items  string `json:"items"`
	} `json:"schema"`
}

FieldInfo is one row from GET /rest/api/3/field — the site-wide field catalog. Distinct from FieldMeta (editmeta for one issue); do not reuse that type here.

type FieldMeta

type FieldMeta struct {
	Required   bool     `json:"required"`
	Operations []string `json:"operations"`
	Schema     struct {
		Type   string `json:"type"`
		Items  string `json:"items"`
		Custom string `json:"custom"`
	} `json:"schema"`
	AllowedValues []struct {
		ID    string `json:"id"`
		Value string `json:"value"`
		Name  string `json:"name"`
	} `json:"allowedValues"`
}

FieldMeta is one editable field as Jira describes it: what it accepts and, for a closed set, every value it accepts.

type Fields

type Fields struct {
	Summary     string          `json:"summary"`
	Description json.RawMessage `json:"description"`
	Environment json.RawMessage `json:"environment"`
	IssueType   NamedID         `json:"issuetype"`
	Status      Status          `json:"status"`
	Priority    *NamedID        `json:"priority"`
	Assignee    *User           `json:"assignee"`
	Reporter    *User           `json:"reporter"`
	Creator     *User           `json:"creator"`
	Project     struct {
		Key string `json:"key"`
	} `json:"project"`
	Parent *struct {
		Key string `json:"key"`
	} `json:"parent"`
	Labels      []string  `json:"labels"`
	Components  []NamedID `json:"components"`
	FixVersions []NamedID `json:"fixVersions"`
	Versions    []NamedID `json:"versions"` // affects versions
	Duedate     string    `json:"duedate"`
	Resolution  *NamedID  `json:"resolution"`
	// Security is the issue security level (id+name). Nil when the
	// payload omits the key (unrestricted). NamedID is the existing
	// {id,name} shape; do not invent a second struct.
	Security   *NamedID     `json:"security"`
	Created    string       `json:"created"`
	Updated    string       `json:"updated"`
	Comment    CommentPage  `json:"comment"`
	Attachment []Attachment `json:"attachment"`
	IssueLinks []IssueLink  `json:"issuelinks"`
}

type History

type History struct {
	ID      string        `json:"id"`
	Created string        `json:"created"`
	Author  User          `json:"author"`
	Items   []HistoryItem `json:"items"`
}

type HistoryItem

type HistoryItem struct {
	Field      string `json:"field"`
	FieldID    string `json:"fieldId"`
	From       string `json:"from"`
	FromString string `json:"fromString"`
	To         string `json:"to"`
	ToString   string `json:"toString"`
}

HistoryItem's FieldID is the stable identifier ("status", "assignee"); Field is the display name and is localized.

type Issue

type Issue struct {
	ID        string
	Key       string
	Fields    Fields
	Extra     map[string]json.RawMessage
	Raw       json.RawMessage
	Changelog *Changelog
}

Issue keeps the fields object three ways: typed for the mapping, verbatim per field id so configured custom fields need no code, and whole as Raw.

func (*Issue) UnmarshalJSON

func (i *Issue) UnmarshalJSON(b []byte) error
type IssueLink struct {
	Type struct {
		Name string `json:"name"`
	} `json:"type"`
	InwardIssue *struct {
		Key string `json:"key"`
	} `json:"inwardIssue"`
	OutwardIssue *struct {
		Key string `json:"key"`
	} `json:"outwardIssue"`
}

type IssueLinkType added in v0.17.0

type IssueLinkType struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Inward  string `json:"inward"`
	Outward string `json:"outward"`
}

IssueLinkType is one row of GET /rest/api/3/issueLinkType. Names and inward/outward descriptions can be renamed and localized; writes send the id.

type Media

type Media struct {
	ID       string
	Filename string
}

Media is one inline image in a comment: the Jira media UUID (not the attachment id — see Client.MediaRef) plus the filename, which is carried as `alt` so our own renderer can match the node to the attachment without persisting the UUID anywhere (web/src/lib/adf.ts, findAttachment).

type NamedID

type NamedID struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Value string `json:"value,omitempty"`
}

type Project

type Project struct {
	Key     string `json:"key"`
	Name    string `json:"name"`
	TypeKey string `json:"projectTypeKey"`
}

Project is one row of the site's project list, as the onboarding picker needs it: the key sync will use, a name to recognise it by, and Jira's own type slug.

type SavedFilter added in v0.13.0

type SavedFilter struct {
	ID        string
	Name      string
	JQL       string
	Favourite bool
	Owner     string
}

SavedFilter is a Jira filter the account owns or has starred.

type Status

type Status struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	StatusCategory struct {
		Key string `json:"key"`
	} `json:"statusCategory"`
}

Status carries the category because every piece of logic keys on it: names come back in the account's display language (contracts/sync.md, "Localization hazard").

type Transition

type Transition struct {
	ID     string                     `json:"id"`
	Name   string                     `json:"name"`
	To     Status                     `json:"to"`
	Fields map[string]TransitionField `json:"fields"`
}

Transition is one available status change, with the target's category so the UI can colour it without knowing the site's status names. Fields is the screen Jira returned for expand=transitions.fields (often empty).

type TransitionField added in v0.17.0

type TransitionField struct {
	Required bool   `json:"required"`
	Name     string `json:"name"`
	Schema   struct {
		Type string `json:"type"`
	} `json:"schema"`
	AllowedValues []NamedID `json:"allowedValues"`
}

TransitionField is the subset of a transition screen field the write path uses: required, a display name, the schema type, and closed-set values. Names in AllowedValues are localized per account; writes send the id.

type Usage

type Usage = atlhttp.Usage

Usage is a point-in-time snapshot of this client's outbound Jira traffic. Counters are process-local until a caller persists them (see store.api_usage).

Requests counts every HTTP attempt, including retries: that is the unit that draws from Jira's rate budget. This is our own call volume, not Jira's remaining point pool — the site does not expose that.

type User

type User struct {
	AccountID   string `json:"accountId"`
	DisplayName string `json:"displayName"`
	Email       string `json:"emailAddress"`
	// AccountType is the bot axis (GDK-590): standalone issuetap mints
	// "agent" for the accounts behind X-Issuetap-Actor, Cloud sends "app"
	// for Connect accounts and "atlassian"/"customer" for humans. Judge it
	// only through IsBotAccountType.
	AccountType string `json:"accountType"`
	// The two below are only ever filled by the user search the assignee picker
	// calls; the mirror stores neither.
	AvatarURLs map[string]string `json:"avatarUrls"`
	Active     bool              `json:"active"`
}

func (User) Avatar

func (u User) Avatar() string

Avatar is the 48px avatar, or empty when Jira sent none.

type Version added in v0.17.0

type Version struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Released    bool   `json:"released"`
	Archived    bool   `json:"archived"`
	ReleaseDate string `json:"releaseDate"`
}

Version is one row of GET /rest/api/3/project/{key}/versions. Writes send the id: names can be renamed on the site.

Jump to

Keyboard shortcuts

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