jira

package
v0.10.6 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package jira — Board service for /rest/agile/1.0.

Wire shapes and field semantics live in the boards HTTP contract and data-model.md. This file holds the Board struct, BoardScope helper, BoardService interface, and the drain/resolve implementations.

Typed errors for the client's transport-layer mutation guards. Both fire in Client.Do before any bytes reach the wire, so every command inherits them without per-command boilerplate. They are TYPED (not synthetic *APIError values) so the error mapper can recognize them with errors.As and emit their own stable codes instead of the validation_failed catch-all.

Index

Constants

View Source
const (
	ErrorTypeAuth       = errtax.TypeAuth
	ErrorTypeNotFound   = errtax.TypeNotFound
	ErrorTypeValidation = errtax.TypeValidation
	ErrorTypeRateLimit  = errtax.TypeRateLimit
	ErrorTypeServer     = errtax.TypeServer
)

The taxonomy types, re-exported under jira's historical names.

View Source
const DefaultWorkdaySeconds = 28800

Variables

View Source
var ErrBoardNotFound = errors.New("board not found in cache")

ErrBoardNotFound is returned by ResolveOne when the cache contains no board matching the supplied name (case-insensitive exact match). CLI maps to exit 2 (not_found).

View Source
var ErrUserNotFound = errors.New("user not found")

ErrUserNotFound is returned by ResolveUser when /user/search yields zero matches for the supplied query. The CLI surface maps this to exit 2 per the constitution error contract (resource-not-found).

View Source
var IssueListFields = append([]string(nil), defaultIssueListFields...)

Functions

func Bool

func Bool(v bool) *bool

func CodeForStatus added in v0.10.0

func CodeForStatus(status int) errtax.Code

CodeForStatus derives the taxonomy code for an HTTP status. It moves in lockstep with ClassifyStatus and the errtax registry: a status added here MUST carry a registry row (the taxonomy guard enumerates the pairing) — a jira_* code with no hint is the exact gap that guard exists to catch. An unmapped status falls back to its classified type's catch-all code.

func DefaultBoardMissingMessage

func DefaultBoardMissingMessage(profile, name string) string

DefaultBoardMissingMessage returns the pinned wording the CLI emits when `default_board` references a name that does not resolve in the boards cache. Both arguments are interpolated verbatim — `profile` is the active profile name (e.g. "default" / "work"), `name` is the configured default_board value. Wording is pinned so the contract test can assert on it; any change here is a spec change too.

func DefaultIssueListFields

func DefaultIssueListFields() []string

func DrainSearch

func DrainSearch(ctx context.Context, svc SearchService, req *SearchRequest, opts DrainOptions) ([]*Issue, DrainInfo, error)

DrainSearch pulls every page from a SearchService.JQL call, respecting the configured bounds. Unbounded=true bypasses the bounds entirely; this is what the CLI's --unbounded flag wires.

func Int

func Int(v int) *int

func ListIssuesPage added in v0.6.0

func ListIssuesPage(ctx context.Context, svc IssuePageLister, opts *IssueListOptions, cur PageCursor) ([]*Issue, PageCursor, error)

ListIssuesPage runs one page of an issue search and returns the cursor for the next call. Pass the zero PageCursor for the first page; a returned cursor with More() == false means the result set is complete.

func MaxAttachmentUploadBytes

func MaxAttachmentUploadBytes() int64

func NormalizeBoardName

func NormalizeBoardName(name string) string

NormalizeBoardName strips leading/trailing whitespace from board names while preserving internal whitespace and Unicode verbatim. Called by the cache prime path before persisting each board, so the cache file holds clean names while the wire shape is left untouched.

func ParseDuration

func ParseDuration(input string, workdaySeconds int) (int, error)

func ParseStarted added in v0.10.0

func ParseStarted(input string, now time.Time, loc *time.Location) (string, error)

ParseStarted parses a worklog start timestamp and normalizes it to the strict form Jira requires. Accepted forms: ISO-8601 with an explicit offset (kept as given), a naive date/time without one (interpreted in loc), and the relative keywords `now`, `yesterday`, and `<duration> ago` (e.g. `2h ago`), all resolved against now. Anything else is an error, so an unparseable value fails local validation instead of a Jira submit.

func RESTPath

func RESTPath(parts ...string) string
func SortIssueLinks(links []IssueLinkView)

func StatusCounts

func StatusCounts(issues []*Issue) map[string]int

func String

func String(v string) *string

Types

type APIError

type APIError struct {
	StatusCode int
	Type       ErrorType
	Message    string
	// ErrorMessages mirrors the Jira ErrorCollection.errorMessages array
	// (general, non-field-scoped human messages). Nil when the body
	// carried none. Never used as a branch target — wording is not
	// contractual.
	ErrorMessages []string
	// FieldErrors mirrors the Jira ErrorCollection.errors map
	// (field-name -> single human message). Nil when the body carried
	// none.
	FieldErrors map[string]string
	// UpstreamStatus is the integer ErrorCollection.status field when the
	// body carried one. Zero when absent.
	UpstreamStatus int
	// RetryAfterSeconds is the Retry-After header value on a 429
	// response. Zero when the header was absent — callers fall back to
	// exponential backoff with jitter.
	RetryAfterSeconds int
	// RateLimitRemaining is the X-RateLimit-Remaining header value when
	// Jira sends it. Zero can mean either absent or exhausted; callers use
	// it as diagnostic context only.
	RateLimitRemaining int
	// UpstreamRequestID is Jira's own trace id for the failed exchange,
	// read from the Atl-Traceid (preferred) or X-ARequestId response
	// header. It is the value Atlassian support can correlate a failure
	// against; the envelope's request_id is a local value with no
	// server-side meaning. Empty when neither header was present.
	UpstreamRequestID string
	// Cause preserves transport/body/JSON failures for errors.Is/As while
	// keeping the public APIError message stable.
	Cause error
}

APIError is the typed error returned for any non-2xx Jira REST response or transport failure. Beyond the HTTP status and a display message it preserves the schema-backed fields of Jira Cloud's ErrorCollection body (errorMessages[], errors map, status) and the rate-limit Retry-After header, all as optional metadata. Jira exposes no stable machine error code, so APIError carries none — callers derive a normalized code from the HTTP status.

func (*APIError) Code added in v0.10.0

func (e *APIError) Code() errtax.Code

Code classifies the API failure by its HTTP status.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

type AmbiguousBoardError

type AmbiguousBoardError struct {
	Query      string
	Candidates []Board
}

AmbiguousBoardError is returned by ResolveOne when 2+ boards match the supplied name (e.g. same name in different projects). The cmd layer renders Candidates in the disambiguation envelope. CLI maps to exit 3 (validation).

func (*AmbiguousBoardError) Error

func (e *AmbiguousBoardError) Error() string

type AmbiguousUserError

type AmbiguousUserError struct {
	Query      string
	Candidates []*User
}

AmbiguousUserError is returned by ResolveUser when /user/search yields 2+ matches and we refuse to pick a winner. Candidates carries every returned User so the CLI can render the disambiguation envelope.

func (*AmbiguousUserError) CandidateRows added in v0.10.0

func (e *AmbiguousUserError) CandidateRows() []map[string]any

CandidateRows flattens the /user/search candidates into envelope candidate rows so an agent can pick a winner without re-querying. The method is CandidateRows (not Candidates) because the struct carries a Candidates field. Nil candidates and absent fields are skipped; the returned slice is never nil.

func (*AmbiguousUserError) Code added in v0.10.0

func (e *AmbiguousUserError) Code() errtax.Code

Code classifies the failure under the taxonomy's user_ambiguous code.

func (*AmbiguousUserError) Error

func (e *AmbiguousUserError) Error() string

type Attachment

type Attachment struct {
	ID       *string `json:"id,omitempty"`
	Self     *string `json:"self,omitempty"`
	Filename *string `json:"filename,omitempty"`
	MimeType *string `json:"mimeType,omitempty"`
	Size     *int64  `json:"size,omitempty"`
	Author   *User   `json:"author,omitempty"`
	Created  *string `json:"created,omitempty"`
	// Content is Atlassian's signed download URL. The CLI doesn't expose
	// this in the --json envelope (the download command goes through
	// /attachment/content/{id} instead), but it's preserved for callers
	// that want to embed the URL directly.
	Content *string `json:"content,omitempty"`
}

Attachment is a file attached to a Jira issue. Pointer-typed nullable fields follow the rest of pkg/jira (Issue, Comment, …) so JSON round-tripping preserves "field absent" vs "field present and empty".

Wire shape: GET /rest/api/3/issue/{key}?fields=attachment returns `fields.attachment[]` populated with this shape; POST .../attachments returns an array of these directly.

type AttachmentService

type AttachmentService interface {
	List(ctx context.Context, key string) ([]Attachment, *Response, error)
	Add(ctx context.Context, key string, files []FileSource) ([]Attachment, *Response, error)
	Delete(ctx context.Context, attachmentID string) (*Response, error)
	Download(ctx context.Context, attachmentID string) (io.ReadCloser, *Response, error)
}

AttachmentService exposes Atlassian's attachment endpoints. Mutation methods follow the same pointer-based response convention as IssueService so callers can distinguish "no field" from "empty field".

Design notes:

  • Add uses multipart/form-data with X-Atlassian-Token: no-check and form field name "file" — both required by Atlassian.
  • Delete is global by attachment id (Atlassian's endpoint is /attachment/{id}, not nested under the issue).
  • Download streams via io.Copy in the caller; this method returns the raw response body.

func NewAttachmentService

func NewAttachmentService(client *Client) AttachmentService

NewAttachmentService binds an AttachmentService to the given client.

type AvatarURLs

type AvatarURLs struct {
	Size16 string `json:"16x16,omitempty"`
	Size24 string `json:"24x24,omitempty"`
	Size32 string `json:"32x32,omitempty"`
	Size48 string `json:"48x48,omitempty"`
}

AvatarURLs maps Jira's standard avatar size keys to URLs.

type Board

type Board struct {
	ID          *int     `json:"id,omitempty"`
	Self        *string  `json:"self,omitempty"`
	Name        *string  `json:"name,omitempty"`
	Type        *string  `json:"type,omitempty"`
	ProjectKeys []string `json:"project_keys"`
}

Board is a Jira agile board fetched via /rest/agile/1.0/board and (for the project list) /rest/agile/1.0/board/{id}/project. Pointer-typed nullable fields mirror Issue/Comment/Attachment: distinguishes "field absent" from "present and zero".

func DecodeBoardsCache

func DecodeBoardsCache(data []byte) ([]Board, error)

DecodeBoardsCache parses the on-disk boards-cache payload. The earlier resolver tests stored the boards as a bare JSON array; the 003 prime path persists the BoardsCache envelope. This helper accepts both so the migration is invisible to callers.

type BoardDrainOptions

type BoardDrainOptions struct {
	PageSize   int
	MaxPages   int
	MaxResults int
	Unbounded  bool
}

BoardDrainOptions configures BoardService.ListAll. Defaults: PageSize 50, MaxPages 100, MaxResults 10 000. Set Unbounded to disable both bounds.

type BoardDrainResult

type BoardDrainResult struct {
	Boards          []*Board
	LastResp        *Response
	PagesFetched    int
	RateLimitHit    *APIError
	Truncated       bool
	TruncatedReason string
}

BoardDrainResult carries the outcome of ListAll: every board fetched, the rate-limit APIError that aborted the walk if one fired (partial-success), and the truncation reason if a bound stopped the walk short of isLast.

type BoardScope

type BoardScope struct {
	Board      Board
	Precedence string
}

BoardScope is what BoardService.ResolveOne returns: the resolved board plus the precedence path that selected it (one of "flag", "default_board", "none").

func (BoardScope) JQLClause

func (s BoardScope) JQLClause() (string, bool)

JQLClause emits `project in (P1, P2, ...)` from the board's cached project keys. The bool result is the canonical "did this scope produce a clause?" signal — every consumer that needs to branch on emission (envelope's `applied` flag, JQL prepender) reads it from here so no caller has to re-derive `len(ProjectKeys) > 0`.

type BoardService

type BoardService interface {
	List(ctx context.Context, opts *ListOptions) ([]Board, *Response, error)
	ListAll(ctx context.Context, opts BoardDrainOptions) (BoardDrainResult, error)
	ProjectsForBoard(ctx context.Context, boardID int) ([]string, *Response, error)
	ResolveOne(ctx context.Context, profile, name string) (BoardScope, error)
}

BoardService groups the agile board endpoints used by the boards cache primer, the `boards list` command, and the --board resolver.

func NewBoardService

func NewBoardService(client *Client) BoardService

NewBoardService binds a BoardService to the supplied client.

type BoardsCacheFile

type BoardsCacheFile struct {
	FetchedAt         string  `json:"fetched_at"`
	TTLSeconds        int     `json:"ttl_seconds"`
	Truncated         bool    `json:"truncated"`
	TruncatedReason   string  `json:"truncated_reason"`
	PagesFetched      int     `json:"pages_fetched,omitempty"`
	RetryAfterSeconds int     `json:"retry_after_seconds,omitempty"`
	Items             []Board `json:"items"`
}

BoardsCacheFile is the on-disk envelope persisted under `<cache-dir>/jira-cli/<profile>/boards.json` (the OS-native cache dir: `${XDG_CACHE_HOME:-~/.cache}` on Unix, `%LocalAppData%\cache` on Windows) per data-model.md > BoardsCache. Carried inside the generic cache.Entry's Data field so the existing internal/cache machinery (atomic write, 0600 perms, TTL freshness) round-trips it unchanged.

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(opts ...Option) *Client

func NewClientE

func NewClientE(opts ...Option) (*Client, error)

func (*Client) BaseURL

func (c *Client) BaseURL() *url.URL

BaseURL exposes the resolved base URL so services that need to build fully-qualified URLs without going through NewRequest (multipart, streaming download) can ResolveReference against the same root the rest of the package uses.

func (*Client) Do

func (c *Client) Do(req *http.Request, out any) (*Response, error)

func (*Client) NewRawRequest

func (c *Client) NewRawRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error)

func (*Client) NewRequest

func (c *Client) NewRequest(ctx context.Context, method, path string, body any) (*http.Request, error)

func (*Client) SignRequest

func (c *Client) SignRequest(req *http.Request)

SignRequest applies the active profile's Jira Cloud auth to req: HTTP Basic with the account email and API token. Services that build requests outside NewRequest (multipart upload, raw-body posts, streaming download) MUST call this rather than re-implementing the basic-auth selection inline so the auth surface stays consistent.

type Comment

type Comment struct {
	ID           *string       `json:"id,omitempty"`
	Self         *string       `json:"self,omitempty"`
	Body         *adf.Document `json:"body,omitempty"`
	Author       *User         `json:"author,omitempty"`
	UpdateAuthor *User         `json:"updateAuthor,omitempty"`
	Created      *string       `json:"created,omitempty"`
	Updated      *string       `json:"updated,omitempty"`
	Visibility   *Visibility   `json:"visibility,omitempty"`
}

type CommentAddRequest

type CommentAddRequest struct {
	Body   adf.Document `json:"body"`
	DryRun bool         `json:"-"`
}

type CommentBody

type CommentBody struct {
	ADF *adf.Document
	// DryRun short-circuits the HTTP call. The service returns a synthetic
	// {ID: "DRY-RUN"} Comment so callers can keep their envelope flow.
	DryRun bool
}

CommentBody carries the comment payload. Exactly one of ADF or Markdown must be supplied; the cmd-layer binding converts Markdown → ADF before constructing the body.

type CommentDrainOptions

type CommentDrainOptions struct {
	PageSize   int
	MaxPages   int
	MaxResults int
	Unbounded  bool
}

CommentDrainOptions configures CommentService.ListAll. Mirrors DrainOptions on the search side so consumer-side opt-in walks have consistent runaway protection. Defaults: PageSize 50, MaxPages 100, MaxResults 10_000. Set Unbounded to disable both bounds (the CLI requires --unbounded; the TUI never sets it).

type CommentDrainResult

type CommentDrainResult struct {
	Comments        []*Comment
	LastResp        *Response
	PagesFetched    int
	RateLimitHit    *APIError
	Truncated       bool
	TruncatedReason string
}

CommentDrainResult carries the outcome of ListAll: every comment fetched, the final page's response (for envelope pagination metadata), and the rate-limit APIError that aborted the walk if one fired (partial-success semantics — exit 0 with a structured warning). Truncated reports whether one of the configured bounds stopped the walk short of isLast.

type CommentPage

type CommentPage struct {
	Comments []*Comment `json:"comments,omitempty"`
}

type CommentService

type CommentService interface {
	List(ctx context.Context, key string, opts *ListCommentsOptions) ([]*Comment, *Response, error)
	ListAll(ctx context.Context, key string, opts CommentDrainOptions) (CommentDrainResult, error)
	Add(ctx context.Context, key string, body *CommentBody) (*Comment, *Response, error)
	AddWithVisibility(ctx context.Context, key string, body *CommentBody, vis VisibilityChange) (*Comment, *Response, error)
	Edit(ctx context.Context, key, commentID string, body *CommentBody, vis VisibilityChange) (*Comment, *Response, error)
	Delete(ctx context.Context, key, commentID string) (*Response, error)
}

CommentService groups the Jira-issue comment endpoints. Split out of IssueService per plan.md "Service Architecture" — comments deserve a service of their own now that the lifecycle covers list / add / edit / delete instead of just add.

All methods use the underlying *Client's auth, debug, retry, and rate-limit hooks. Pagination on List follows the same ListOptions shape the rest of pkg/jira already exposes.

func NewCommentService

func NewCommentService(client *Client) CommentService

NewCommentService constructs a CommentService bound to the given client.

type Component

type Component struct {
	Name *string `json:"name,omitempty"`
}

type CurrentUser

type CurrentUser struct {
	Self         string     `json:"self,omitempty"`
	AccountID    string     `json:"accountId,omitempty"`
	AccountType  string     `json:"accountType,omitempty"`
	EmailAddress string     `json:"emailAddress,omitempty"`
	DisplayName  string     `json:"displayName,omitempty"`
	Active       bool       `json:"active,omitempty"`
	TimeZone     string     `json:"timeZone,omitempty"`
	Locale       string     `json:"locale,omitempty"`
	AvatarURLs   AvatarURLs `json:"avatarUrls,omitempty"`
}

User identity returned by /rest/api/3/myself. Subset of the full response — only the fields we use for assign-to-me, profile bootstrap and `whoami`.

type DrainInfo

type DrainInfo struct {
	PagesFetched    int
	Truncated       bool
	TruncatedReason string
	// NextPageToken resumes the walk after a truncation that fell on a
	// page boundary. Empty when the walk completed, or when the cut fell
	// mid-page (a max_results stop inside a page) — resuming from the
	// page token there would silently skip the tail of the cut page.
	NextPageToken string
}

DrainInfo describes how DrainSearch terminated. Truncated reports whether a bound stopped consumption short of isLast.

type DrainOptions

type DrainOptions struct {
	MaxPages   int
	MaxResults int
	Unbounded  bool
}

DrainOptions configures DrainSearch — the bounded `--all` consumer.

Defaults: max-pages 100, max-results-total 10,000. Override either with explicit positive values; set Unbounded to disable both (CLI requires --unbounded; the TUI never sets it).

type DryRunBlockedError added in v0.10.0

type DryRunBlockedError struct {
	Method string
	Path   string
}

DryRunBlockedError reports a mutation that reached the transport under --dry-run. Dry-run commands stop before submitting; a request getting this far is caught by the transport guard, not user error.

func (*DryRunBlockedError) Code added in v0.10.0

func (e *DryRunBlockedError) Code() errtax.Code

Code classifies a dry-run transport block under dry_run_blocked.

func (*DryRunBlockedError) Error added in v0.10.0

func (e *DryRunBlockedError) Error() string

type EditMeta added in v0.10.0

type EditMeta struct {
	Fields map[string]EditMetaField `json:"fields,omitempty"`
}

EditMeta is the issue edit-screen metadata Jira returns under expand=editmeta on GET issue: a map keyed by field id of what the edit screen accepts. The struct keeps Jira's wire shape (camelCase keys) but trims each field to the agent-useful subset — unknown keys are dropped on unmarshal rather than carried opaquely.

type EditMetaAllowedValue added in v0.10.0

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

EditMetaAllowedValue is one allowed value of an edit-screen field. Jira mixes shapes per field type — options carry `value`, versions/components/ priorities carry `name` — so both are kept and the blank one omitted.

type EditMetaField added in v0.10.0

type EditMetaField struct {
	Name     string `json:"name,omitempty"`
	Key      string `json:"key,omitempty"`
	Required bool   `json:"required"`
	// Operations lists the verbs the field accepts in an update block
	// (set, add, remove, edit, copy).
	Operations []string             `json:"operations,omitempty"`
	Schema     *EditMetaFieldSchema `json:"schema,omitempty"`
	// AllowedValues enumerates the values Jira accepts for the field,
	// trimmed to their identifying keys. Absent when the field is
	// free-form.
	AllowedValues []EditMetaAllowedValue `json:"allowedValues,omitempty"`
}

EditMetaField describes one editable field on an issue's edit screen.

type EditMetaFieldSchema added in v0.10.0

type EditMetaFieldSchema struct {
	Type string `json:"type,omitempty"`
	// Items is the element type when Type is "array".
	Items string `json:"items,omitempty"`
	// Custom is the fully-qualified custom-field type identifier; empty
	// for system fields.
	Custom string `json:"custom,omitempty"`
}

EditMetaFieldSchema is the type identity of an editable field.

type Epic

type Epic struct {
	Key     *string `json:"key,omitempty"`
	Summary *string `json:"summary,omitempty"`
	Status  *string `json:"status,omitempty"`
}

type EpicService

type EpicService interface {
	List(context.Context, *ListOptions) ([]*Issue, *Response, error)
	Get(context.Context, string, *IssueGetOptions) (*Issue, *Response, error)
	AddIssue(context.Context, string, string) (*Response, error)
	RemoveIssue(context.Context, string) (*Response, error)
	IssuesInEpic(context.Context, string) ([]*Issue, *Response, error)
}

func NewEpicService

func NewEpicService(client *Client) EpicService

type ErrorType

type ErrorType = errtax.Type

ErrorType aliases the shared taxonomy type so jira's status classification and the envelope mapper speak one enum.

func ClassifyStatus added in v0.10.0

func ClassifyStatus(code int) ErrorType

ClassifyStatus reports the error type for an HTTP status. It stays an independent status→type switch (never derived from CodeForStatus plus the registry) so the taxonomy lockstep test compares two genuine sources.

type FieldSchema

type FieldSchema struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Required bool   `json:"required"`
	Type     string `json:"type"`
	// Custom is the schema.custom token Jira reports for a custom
	// field — the trailing identifier of
	// com.atlassian.jira.plugin.system.customfieldtypes:* (e.g.
	// "select", "datepicker", "float"). Empty for system fields and for
	// custom fields whose type Jira does not expose. It is the branch
	// key the mutation pipeline uses to encode a custom-field value.
	Custom string `json:"custom,omitempty"`
}

type FileSource

type FileSource struct {
	Name   string
	Size   int64
	Reader io.Reader
}

FileSource is what AttachmentService.Add accepts per file: a name (the filename Jira sees) plus a Reader carrying the bytes. Using io.Reader lets callers stream from *os.File without slurping into memory; the service implementation routes large payloads through io.Pipe .

type Issue

type Issue struct {
	ID           *string      `json:"id,omitempty"`
	Key          *string      `json:"key,omitempty"`
	Self         *string      `json:"self,omitempty"`
	Fields       *IssueFields `json:"fields,omitempty"`
	Comments     []*Comment   `json:"comments,omitempty"`
	Worklogs     []*Worklog   `json:"worklogs,omitempty"`
	LinkedIssues []*Issue     `json:"linked_issues,omitempty"`
	Subtasks     []*Issue     `json:"subtasks,omitempty"`
	// Transitions carries the workflow transitions Jira returns under
	// expand=transitions — the moves valid from the issue's current status.
	// Populated by issue view so a read answers "what can I transition to"
	// without a second call.
	Transitions []*Transition `json:"transitions,omitempty"`
	// EditMeta carries the edit-screen metadata Jira returns under
	// expand=editmeta — which fields are editable, whether they are
	// required, and their allowed values. Populated by issue view so an
	// edit can be planned from the same read.
	EditMeta *EditMeta `json:"editmeta,omitempty"`
}

func (*Issue) UnmarshalJSON

func (i *Issue) UnmarshalJSON(data []byte) error

type IssueCloneRequest

type IssueCloneRequest struct {
	Fields map[string]any `json:"fields,omitempty"`
	DryRun bool           `json:"-"`
}

type IssueCreateRequest

type IssueCreateRequest struct {
	Project   string         `json:"project,omitempty"`
	IssueType string         `json:"issuetype,omitempty"`
	Summary   string         `json:"summary,omitempty"`
	Fields    map[string]any `json:"fields,omitempty"`
	DryRun    bool           `json:"-"`
}

type IssueDeleteOptions

type IssueDeleteOptions struct {
	DeleteSubtasks bool
}

IssueDeleteOptions controls deletion behavior. DeleteSubtasks=true removes the issue and all its subtasks atomically (Jira refuses the delete otherwise when subtasks exist).

type IssueFields

type IssueFields struct {
	Summary      *string                    `json:"summary,omitempty"`
	IssueType    *IssueType                 `json:"issuetype,omitempty"`
	Description  *adf.Document              `json:"description,omitempty"`
	Status       *Status                    `json:"status,omitempty"`
	Assignee     *User                      `json:"assignee,omitempty"`
	Reporter     *User                      `json:"reporter,omitempty"`
	Priority     *Priority                  `json:"priority,omitempty"`
	Labels       []string                   `json:"labels,omitempty"`
	Components   []Component                `json:"components,omitempty"`
	Parent       *Issue                     `json:"parent,omitempty"`
	FixVersions  []Version                  `json:"fixVersions,omitempty"`
	Versions     []Version                  `json:"versions,omitempty"`
	Updated      *string                    `json:"updated,omitempty"`
	Comment      *CommentPage               `json:"comment,omitempty"`
	Worklog      *WorklogPage               `json:"worklog,omitempty"`
	IssueLinks   []IssueLink                `json:"issuelinks,omitempty"`
	Subtasks     []*Issue                   `json:"subtasks,omitempty"`
	CustomFields map[string]json.RawMessage `json:"-"`
}

func (IssueFields) MarshalJSON

func (f IssueFields) MarshalJSON() ([]byte, error)

func (*IssueFields) UnmarshalJSON

func (f *IssueFields) UnmarshalJSON(data []byte) error

type IssueGetOptions

type IssueGetOptions struct {
	Expand []string
}
type IssueLink struct {
	InwardIssue  *Issue `json:"inwardIssue,omitempty"`
	OutwardIssue *Issue `json:"outwardIssue,omitempty"`
}

type IssueLinkRequest

type IssueLinkRequest struct {
	Type string
	// TypeID addresses the link type by id instead of name; when both
	// are set the id wins, matching Jira's own precedence.
	TypeID       string
	InwardIssue  string
	OutwardIssue string
	// Comment, when non-empty, is the native REST comment block posted
	// with the link. The CLI validates its ADF body before it gets
	// here; the wire forwards it verbatim.
	Comment map[string]any
}

IssueLinkRequest creates an issue link via POST /rest/api/3/issueLink. Type is the link-type name (e.g., "Blocks", "Relates", "Cloners"). InwardIssue and OutwardIssue are issue keys; semantics depend on the type ("Blocks" → outwardIssue is blocked by inwardIssue).

type IssueLinkService

type IssueLinkService interface {
	List(context.Context, string) ([]IssueLinkView, *Response, error)
	Create(context.Context, *IssueLinkRequest) (*Response, error)
	Delete(context.Context, string) (*Response, error)
}

func NewIssueLinkService

func NewIssueLinkService(client *Client) IssueLinkService

type IssueLinkType

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

type IssueLinkTypeService

type IssueLinkTypeService interface {
	List(context.Context) ([]IssueLinkType, *Response, error)
}

func NewIssueLinkTypeService

func NewIssueLinkTypeService(client *Client) IssueLinkTypeService

type IssueLinkView

type IssueLinkView struct {
	ID         string        `json:"id"`
	Self       string        `json:"self,omitempty"`
	Type       IssueLinkType `json:"type"`
	Direction  string        `json:"direction"`
	OtherIssue IssueRef      `json:"other_issue"`
}

type IssueListOptions

type IssueListOptions struct {
	ListOptions
	JQL    string
	Fields []string
	Expand []string
}

type IssueMoveRequest

type IssueMoveRequest struct {
	Fields map[string]any `json:"fields,omitempty"`
	DryRun bool           `json:"-"`
}

type IssuePageLister added in v0.6.0

type IssuePageLister interface {
	List(ctx context.Context, opts *IssueListOptions) ([]*Issue, *Response, error)
}

IssuePageLister is the one-method surface ListIssuesPage needs.

type IssueRef

type IssueRef struct {
	Key     string `json:"key"`
	Summary string `json:"summary,omitempty"`
	Status  string `json:"status,omitempty"`
}

type IssueService

type IssueService interface {
	List(context.Context, *IssueListOptions) ([]*Issue, *Response, error)
	Get(context.Context, string, *IssueGetOptions) (*Issue, *Response, error)
	Create(context.Context, *IssueCreateRequest) (*Issue, *Response, error)
	Update(context.Context, string, *IssueUpdateRequest) (*Issue, *Response, error)
	Delete(context.Context, string, *IssueDeleteOptions) (*Response, error)
	Clone(context.Context, string, *IssueCloneRequest) (*Issue, *Response, error)
	Move(context.Context, string, *IssueMoveRequest) (*Issue, *Response, error)
	Transitions(context.Context, string) ([]*Transition, *Response, error)
	Transition(context.Context, string, *TransitionRequest) (*Response, error)
	AddComment(context.Context, string, *CommentAddRequest) (*Comment, *Response, error)
	// Link creates an issue link between two issues (Blocks, Relates,
	// etc.). Jira's bulk-edit endpoint rejects issuelinks updates;
	// links require this dedicated endpoint.
	Link(context.Context, *IssueLinkRequest) (*Response, error)
	// AddRemoteLink attaches a web link (URL + title) to an issue —
	// the Jira "Web links" feature. Different endpoint from Link.
	AddRemoteLink(context.Context, string, *RemoteLinkRequest) (*Response, error)
}

func NewIssueService

func NewIssueService(client *Client) IssueService

type IssueType added in v0.6.0

type IssueType struct {
	Name    *string `json:"name,omitempty"`
	IconURL *string `json:"iconUrl,omitempty"`
	Subtask *bool   `json:"subtask,omitempty"`
}

IssueType is the issue's type (Epic, Story, Task, Bug, Sub-task, ...).

type IssueTypeUnknownError added in v0.10.6

type IssueTypeUnknownError struct {
	IssueType  string
	ProjectKey string
	Available  []string // creatable issue-type names, for suggestions
}

IssueTypeUnknownError reports a --type value that matches no issue type on a project's create screen. The name is resolved against the fetched issuetypes list in-code, so a miss is a bad input value, NOT a Jira 404 — the CLI maps this to a validation error (exit 3) and surfaces Available as the envelope's "did you mean" suggestions. Do not give it a synthetic HTTP status: a hand-built 404 would drag a local lookup miss into the not-found bucket (see .claude/rules/output.md).

func (*IssueTypeUnknownError) Error added in v0.10.6

func (e *IssueTypeUnknownError) Error() string

type IssueUpdateRequest

type IssueUpdateRequest struct {
	Fields map[string]any `json:"fields"`
	// Update, when non-empty, is the native REST update block of
	// add/set/remove operations, sent as a sibling of fields. Jira
	// validates the operation verbs server-side.
	Update map[string]any `json:"update,omitempty"`
	DryRun bool           `json:"-"`
}

type JQLField added in v0.3.3

type JQLField struct {
	Value         string
	DisplayName   string
	CustomFieldID string
	Operators     []string
	Auto          bool
}

JQLField is one queryable field. CustomFieldID is set when the field is a custom field, and holds Jira's JQL custom-field token (e.g. cf[10010], the same form as Value) — not the customfield_NNNNN REST selector. Its real use is as a discriminator: present means custom, absent means a system field. Operators are the JQL operators the field supports; Auto reports that the field's values can be fetched from the suggestions endpoint.

type JQLFunction added in v0.3.3

type JQLFunction struct {
	Value       string
	DisplayName string
}

JQLFunction is one JQL function (e.g. currentUser()).

type JQLReference added in v0.3.3

type JQLReference struct {
	Fields        []JQLField
	Functions     []JQLFunction
	ReservedWords []string
}

JQLReference is the instance's JQL metadata from /jql/autocompletedata: the fields and functions queryable on this Jira (including custom fields) plus the JQL reserved words.

type JQLService added in v0.3.3

type JQLService interface {
	Parse(context.Context, []string, string) ([]ParsedQuery, *Response, error)
	AutocompleteData(context.Context) (JQLReference, *Response, error)
	AutocompleteSuggestions(ctx context.Context, fieldName, fieldValue string) ([]JQLSuggestion, *Response, error)
}

JQLService wraps Jira Cloud's /jql/* endpoints (parse/validate and autocomplete reference data).

func NewJQLService added in v0.3.3

func NewJQLService(client *Client) JQLService

type JQLSuggestion added in v0.6.0

type JQLSuggestion struct {
	Value       string `json:"value"`
	DisplayName string `json:"displayName"`
}

JQLSuggestion is one autocomplete value for a field (e.g. a status name for `status =`). Value is what belongs in the query; DisplayName may carry markup from Jira and is for display only.

type LabelService

type LabelService interface {
	List(context.Context, *ListOptions) ([]string, *Response, error)
}

LabelService surfaces Jira's global label list. Labels in Jira are not scoped to a project — they're a global string pool — so callers generally fetch once per profile and reuse via the local cache.

func NewLabelService

func NewLabelService(client *Client) LabelService

NewLabelService constructs a LabelService bound to the given client.

type ListCommentsOptions

type ListCommentsOptions struct {
	ListOptions
	// OrderBy lets callers override the default ordering. Empty string =
	// "created" (oldest-first), matching Atlassian's native default.
	OrderBy string
}

ListCommentsOptions controls pagination and ordering for comment list. Atlassian's `/issue/{key}/comment` endpoint returns oldest-first by default when `orderBy=created` is supplied; we send it explicitly so the contract is stable across instances.

type ListOptions

type ListOptions struct {
	StartAt       int
	MaxResults    int
	NextPageToken string
}

func (ListOptions) QueryValues

func (o ListOptions) QueryValues() url.Values

type LossyCommentWarning

type LossyCommentWarning struct {
	Type            string   `json:"type"`
	CommentID       string   `json:"comment_id"`
	LossyConstructs []string `json:"lossy_constructs"`
}

LossyCommentWarning is the structured warning shape emitted under envelope.warnings[] when a comment's ADF body lost fidelity during the Markdown render path. CommentID lets the consumer cross-reference the affected comment in data.comments[]; LossyConstructs lists the dropped node/mark types in sorted-unique order.

func CollectLossyCommentWarnings

func CollectLossyCommentWarnings(comments []*Comment) []LossyCommentWarning

CollectLossyCommentWarnings walks comments[] and builds one warning per comment whose ADF body included nodes/marks the Markdown renderer couldn't fully express. Comments without a body are skipped silently — they're not lossy, just empty.

type Option

type Option func(*Client)

func WithBaseURL

func WithBaseURL(raw string) Option

func WithBasicAuth

func WithBasicAuth(email, token string) Option

func WithDebug

func WithDebug(debug bool) Option

func WithDryRun

func WithDryRun(dryRun bool) Option

WithDryRun causes Do to refuse any state-changing HTTP method (POST / PUT / PATCH / DELETE) so a --dry-run invocation cannot mutate Jira. Reads still pass through, so the same client can serve a dry-run preview that renders live data. This is the service-level safety net behind the command-layer dry-run branches: even a command path that forgets to gate a submission is stopped here.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

func WithHTTPTimeout

func WithHTTPTimeout(timeout time.Duration) Option

func WithMaxRetryWait added in v0.5.0

func WithMaxRetryWait(d time.Duration) Option

WithMaxRetryWait sets the rate-limit retry budget: the longest a single request will spend sleeping out 429 / Retry-After responses before it gives up and returns the typed error. Zero disables auto-retry. The CLI clamps the effective wait to the remaining --timeout deadline.

func WithRateObserver added in v0.5.1

func WithRateObserver(fn RateObserver) Option

WithRateObserver registers a callback invoked with the rate-limit state of every successful response. The CLI uses it to raise a near-limit warning; it is a no-op observability seam, so a nil observer (the default) changes nothing.

func WithReadOnly

func WithReadOnly(readOnly bool) Option

WithReadOnly causes Do to refuse any non-safe HTTP method (POST / PUT / PATCH / DELETE) with a validation-typed APIError. Single point of control — the CLI sets this once based on env / per-profile config and every mutation in every command path is automatically gated.

type PageCursor added in v0.6.0

type PageCursor struct {
	// contains filtered or unexported fields
}

PageCursor is an opaque continuation handle for page-at-a-time consumers (the TUI's scroll-to-fetch). Pagination mechanics live in this package per the pagination guardrail: callers only test More() and hand the cursor back to ListIssuesPage — they never see the raw token.

func CursorForTest added in v0.6.0

func CursorForTest(token string) PageCursor

CursorForTest fabricates a continuation cursor so consumers outside this package can exercise fetch-more paths in tests without touching raw tokens. An empty token yields the done cursor.

func (PageCursor) More added in v0.6.0

func (c PageCursor) More() bool

More reports whether the server has another page beyond this cursor.

type ParsedQuery added in v0.3.3

type ParsedQuery struct {
	Query     string          `json:"query"`
	Errors    []string        `json:"errors,omitempty"`
	Warnings  []string        `json:"warnings,omitempty"`
	Structure json.RawMessage `json:"structure,omitempty"`
}

ParsedQuery is the per-query result of a parse/validate call: the query as Jira read it, any parse errors, any warnings, and the parsed structure (kept raw — callers that don't need it ignore it).

type PermissionEntry

type PermissionEntry struct {
	ID             string `json:"id,omitempty"`
	Key            string `json:"key,omitempty"`
	Name           string `json:"name,omitempty"`
	Type           string `json:"type,omitempty"`
	Description    string `json:"description,omitempty"`
	HavePermission bool   `json:"havePermission"`
}

PermissionEntry is a single result of /rest/api/3/mypermissions.

type PermissionsResponse

type PermissionsResponse struct {
	Permissions map[string]PermissionEntry `json:"permissions"`
}

PermissionsResponse wraps the /mypermissions envelope.

type Priority

type Priority struct {
	Name *string `json:"name,omitempty"`
}

type ProjectFieldSchema

type ProjectFieldSchema struct {
	ProjectKey string        `json:"project_key"`
	IssueType  string        `json:"issue_type"`
	Fields     []FieldSchema `json:"fields,omitempty"`
}

type ProjectSchemaCache

type ProjectSchemaCache struct {
	// contains filtered or unexported fields
}

func NewProjectSchemaCache

func NewProjectSchemaCache(ttl time.Duration) *ProjectSchemaCache

func (*ProjectSchemaCache) Get

func (c *ProjectSchemaCache) Get(profile, projectKey, issueType string) (*ProjectFieldSchema, bool)

func (*ProjectSchemaCache) InvalidateAll

func (c *ProjectSchemaCache) InvalidateAll()

func (*ProjectSchemaCache) InvalidateProfile

func (c *ProjectSchemaCache) InvalidateProfile(profile string)

func (*ProjectSchemaCache) Set

func (c *ProjectSchemaCache) Set(profile, projectKey, issueType string, schema *ProjectFieldSchema)

type ProjectService

type ProjectService interface {
	GetFieldSchema(context.Context, string, string) (*ProjectFieldSchema, *Response, error)
	GetFieldSchemaForProfile(context.Context, string, string, string) (*ProjectFieldSchema, *Response, error)
	// GetEditSchemaForProfile resolves the edit-screen field schema for
	// one issue via GET /rest/api/3/issue/{idOrKey}/editmeta. The
	// edit/move flows validate against this; createmeta covers
	// create/clone. Cached per profile + issue key.
	GetEditSchemaForProfile(context.Context, string, string) (*ProjectFieldSchema, *Response, error)
	List(context.Context, *ListOptions) ([]ProjectSummary, *Response, error)
}

func NewProjectService

func NewProjectService(client *Client, ttl time.Duration) ProjectService

type ProjectSummary

type ProjectSummary struct {
	ID          string `json:"id"`
	Key         string `json:"key"`
	Name        string `json:"name"`
	ProjectType string `json:"project_type,omitempty"`
	Lead        string `json:"lead,omitempty"`
}

ProjectSummary is the discovery shape for `jira cache projects` (and shell completion). Subset of /rest/api/3/project/search; only the keys agents and humans need.

type QueryOptions

type QueryOptions struct {
	ListOptions
	JQL string
}

type Rate

type Rate struct {
	Remaining         int
	RetryAfterSeconds int
	Reset             time.Time
	// Reason is Jira's RateLimit-Reason header (e.g. jira-burst-based,
	// jira-per-issue-on-write). Empty when absent. Carried for diagnostics;
	// the retry decision keys off the HTTP status and Retry-After, not this.
	Reason string
	// NearLimit reflects Jira's X-RateLimit-NearLimit header: the server's
	// own "you are approaching the limit" signal on an otherwise-successful
	// response. Surfaced as a non-fatal warning so a caller can ease off
	// before it turns into a 429.
	NearLimit bool
}

type RateObserver added in v0.5.1

type RateObserver func(context.Context, Rate)

RateObserver receives the rate-limit state of a successful response. It runs on the request's goroutine — possibly one of several under -p fan-out — so an implementation must be safe for concurrent calls.

type ReadOnlyError added in v0.10.0

type ReadOnlyError struct {
	Method string
	Path   string
}

ReadOnlyError reports a mutation refused because read-only mode is active (the JIRA_READ_ONLY env var or the profile's read_only=true).

func (*ReadOnlyError) Code added in v0.10.0

func (e *ReadOnlyError) Code() errtax.Code

Code classifies a read-only refusal under the taxonomy's read_only code.

func (*ReadOnlyError) Error added in v0.10.0

func (e *ReadOnlyError) Error() string

type RemoteLinkRequest

type RemoteLinkRequest struct {
	URL   string
	Title string
}

RemoteLinkRequest creates a "web link" via POST /rest/api/3/issue/{key}/remotelink. Title is what the user sees; URL is where the link points.

type Response

type Response struct {
	Response   *http.Response
	StartAt    int
	MaxResults int
	Total      int
	// TotalKnown reports that Total was decoded from the endpoint's own
	// total field. Token-paged endpoints (enhanced search) never report
	// one, and a zero Total from them is fabrication, not fact — the
	// envelope only publishes a total when this is true.
	TotalKnown    bool
	IsLast        bool
	NextPageToken string
	TokenPage     bool
	Rate          Rate
	RawBody       json.RawMessage
}

func (Response) NextCursor

func (r Response) NextCursor() string

func (Response) UpstreamRequestID added in v0.10.0

func (r Response) UpstreamRequestID() string

UpstreamRequestID returns Jira's own trace id for the exchange (the Atl-Traceid / X-ARequestId response header) so envelopes can carry a value Atlassian support can correlate. Empty when the response carried neither header.

type SearchRequest

type SearchRequest struct {
	JQL           string `json:"jql,omitempty"`
	MaxResults    int    `json:"maxResults,omitempty"`
	NextPageToken string `json:"nextPageToken,omitempty"`
	Fields        []string
	Expand        []string
	ListOptions   `json:"-"`
}

type SearchResult

type SearchResult struct {
	Issues        []*Issue `json:"issues,omitempty"`
	IsLast        bool     `json:"isLast,omitempty"`
	NextPageToken string   `json:"nextPageToken,omitempty"`
}

type SearchService

type SearchService interface {
	JQL(context.Context, *SearchRequest) ([]*Issue, *Response, error)
	ApproximateCount(context.Context, string) (int, *Response, error)
}

func NewSearchService

func NewSearchService(client *Client) SearchService

type Status

type Status struct {
	Name           *string         `json:"name,omitempty"`
	StatusCategory *StatusCategory `json:"statusCategory,omitempty"`
}

type StatusCategory added in v0.3.0

type StatusCategory struct {
	Key       *string `json:"key,omitempty"`
	Name      *string `json:"name,omitempty"`
	ColorName *string `json:"colorName,omitempty"`
}

StatusCategory is the workflow bucket a status belongs to. Key is one of "new", "indeterminate", or "done" — stable across projects and used to color statuses by category in human output. ColorName is Jira's own color designation for the category ("blue-gray", "yellow", "green", "medium-gray"), preferred over Key when coloring so the badge matches the Jira UI.

type Transition

type Transition struct {
	ID   *string `json:"id,omitempty"`
	Name *string `json:"name,omitempty"`
	// HasScreen reports whether the transition presents a screen. Jira
	// silently discards fields and update blocks sent to a screenless
	// transition, so payload-carrying transitions must check it.
	HasScreen *bool `json:"hasScreen,omitempty"`
}

type TransitionRequest

type TransitionRequest struct {
	ID     string
	Fields map[string]any
	// Comment, when non-nil, is posted atomically with the status change
	// via the transition body's update block.
	Comment *adf.Document
	// Update, when non-empty, is a native REST update block forwarded
	// verbatim into the transition body. The CLI refuses a payload that
	// sets both Update's comment operations and Comment, so the two
	// never collide here.
	Update map[string]any
	DryRun bool
}

type User

type User struct {
	AccountID    *string `json:"accountId,omitempty"`
	AccountType  *string `json:"accountType,omitempty"`
	DisplayName  *string `json:"displayName,omitempty"`
	EmailAddress *string `json:"emailAddress,omitempty"`
	Active       *bool   `json:"active,omitempty"`
	Deleted      *bool   `json:"deleted,omitempty"`
}

type UserService

type UserService interface {
	Myself(context.Context) (*CurrentUser, *Response, error)
	MyPermissions(ctx context.Context, projectKey string, keys []string) (*PermissionsResponse, *Response, error)
	Search(ctx context.Context, query string) ([]*User, *Response, error)
	ResolveAccountID(ctx context.Context, accountID string) (string, error)
	ResolveUser(ctx context.Context, query string) (string, error)
}

UserService exposes user-identity endpoints.

func NewUserService

func NewUserService(client *Client) UserService

NewUserService constructs a UserService bound to the given client.

type Version added in v0.10.0

type Version struct {
	Name *string `json:"name,omitempty"`
}

Version is a project version reference as it appears in an issue's fixVersions / versions arrays. Only the name is modeled — it is the identity the CLI submits and diffs by.

type Visibility

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

Visibility restricts a comment to a Jira role or a Jira group. Atlassian's data model treats Type/Value as mutually exclusive across role and group; the CLI's --visibility-role and --visibility-group flags enforce that exclusivity locally before any HTTP call ().

type VisibilityChange

type VisibilityChange struct {
	Mode  VisibilityMode
	Type  string // "role" or "group"; only meaningful when Mode == VisibilityReplace
	Value string // role/group name; only meaningful when Mode == VisibilityReplace
}

VisibilityChange is the typed, validated input to CommentService.Edit. Construct via ParseVisibilityChange so the mutex-flag rules are enforced before the wire body is built.

func ParseVisibilityChange

func ParseVisibilityChange(flags VisibilityFlags) (VisibilityChange, error)

ParseVisibilityChange validates the flag combination and produces a VisibilityChange. Returns an error (mapped to exit 3 by the cmd layer) when:

  • --visibility-role and --visibility-group are both supplied
  • --clear-visibility is combined with either visibility-role or visibility-group
  • --visibility-role is supplied with an empty value
  • --visibility-group is supplied with an empty value

All three flags omitted → VisibilityKeep (preserve-when-omitted).

type VisibilityFlags

type VisibilityFlags struct {
	RoleSet  bool
	Role     string
	GroupSet bool
	Group    string
	Clear    bool
}

VisibilityFlags is the cmd-layer's view of the three CLI flags. The *Set fields distinguish "flag was supplied" from "flag was supplied with empty value" — cobra's IsChanged() drives the booleans.

type VisibilityMode

type VisibilityMode int

VisibilityMode is the discriminator on a VisibilityChange.

const (
	// VisibilityKeep leaves the existing visibility untouched. The wire
	// body MUST omit the visibility key entirely so Atlassian preserves
	// whatever was previously set.
	VisibilityKeep VisibilityMode = iota
	// VisibilityReplace sets a new restriction (role or group). The wire
	// body sends `"visibility": {"type": ..., "value": ...}`.
	VisibilityReplace
	// VisibilityClear removes the existing restriction. The wire body
	// sends `"visibility": null` explicitly so Atlassian clears it.
	VisibilityClear
)

type WatcherService

type WatcherService interface {
	List(ctx context.Context, issueKey string) (*WatchersResponse, *Response, error)
	Add(ctx context.Context, issueKey, accountID string) (*Response, error)
	Remove(ctx context.Context, issueKey, accountID string) (*Response, error)
}

WatcherService manages watchers on an issue.

func NewWatcherService

func NewWatcherService(client *Client) WatcherService

NewWatcherService constructs a WatcherService bound to the given client.

type WatchersResponse

type WatchersResponse struct {
	IsWatching bool    `json:"isWatching"`
	WatchCount int     `json:"watchCount"`
	Watchers   []*User `json:"watchers"`
}

WatchersResponse is the shape Atlassian returns from GET /rest/api/3/issue/{key}/watchers — the watcher list plus the caller-perspective `IsWatching` flag and instance-level `WatchCount`.

type Worklog

type Worklog struct {
	ID               *string       `json:"id,omitempty"`
	TimeSpentSeconds *int          `json:"timeSpentSeconds,omitempty"`
	Started          *string       `json:"started,omitempty"`
	Comment          *adf.Document `json:"comment,omitempty"`
}

type WorklogAddRequest

type WorklogAddRequest struct {
	TimeSpentSeconds int           `json:"timeSpentSeconds"`
	Started          string        `json:"started,omitempty"`
	Comment          *adf.Document `json:"comment,omitempty"`
	DryRun           bool          `json:"-"`
}

type WorklogPage

type WorklogPage struct {
	Worklogs []*Worklog `json:"worklogs,omitempty"`
}

type WorklogService

type WorklogService interface {
	List(context.Context, string, *ListOptions) ([]*Worklog, *Response, error)
	Add(context.Context, string, *WorklogAddRequest) (*Worklog, *Response, error)
	Delete(context.Context, string, string) (*Response, error)
}

func NewWorklogService

func NewWorklogService(client *Client) WorklogService

Directories

Path Synopsis
Package customfield is the Jira custom-field type registry.
Package customfield is the Jira custom-field type registry.

Jump to

Keyboard shortcuts

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