Documentation
¶
Overview ¶
Package confluence is a small client for the Confluence Cloud REST API, covering exactly the calls cflio needs. It is hand-rolled rather than built on a third-party library because the page body has to survive as an untouched string end to end — a model layer that re-encodes it would break the lossless round-trip that is the whole point of this tool.
Capabilities are split across API versions as Atlassian publishes them: pages, children and comments come from v2, while CQL search and the credential check only exist in v1.
Index ¶
- type APIError
- type Attachment
- type Child
- type Client
- func (c *Client) ChildPages(ctx context.Context, id string, limit int) ([]Child, bool, error)
- func (c *Client) CommentReplies(ctx context.Context, kind CommentKind, commentID string, limit int) ([]Comment, bool, error)
- func (c *Client) CreateFooterComment(ctx context.Context, pageID, body string) (*Comment, error)
- func (c *Client) CurrentUser(ctx context.Context) (User, error)
- func (c *Client) DownloadAttachment(ctx context.Context, downloadLink string, w io.Writer) (int64, error)
- func (c *Client) GetPage(ctx context.Context, id string, withBody bool) (*Page, error)
- func (c *Client) PageAttachments(ctx context.Context, pageID string, limit int) ([]Attachment, bool, error)
- func (c *Client) PageComments(ctx context.Context, kind CommentKind, pageID string, limit int) ([]Comment, bool, error)
- func (c *Client) PagesByTitle(ctx context.Context, spaceKey string, titles []string) ([]PageMatch, error)
- func (c *Client) Search(ctx context.Context, cql string, limit int) ([]SearchResult, int, error)
- func (c *Client) SiteURL() string
- func (c *Client) UpdatePage(ctx context.Context, req UpdatePageRequest) (*Page, error)
- func (c *Client) Users(ctx context.Context, accountIDs []string) ([]User, error)
- type Comment
- type CommentKind
- type Option
- type Page
- type PageMatch
- type SearchResult
- type UpdatePageRequest
- type User
- type Version
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIError ¶
APIError is a non-2xx response. It keeps the status code accessible because the API documents no status for a stale-version conflict on page update, so that case can only be surfaced, never anticipated.
type Attachment ¶
type Attachment struct {
Title string `json:"title"`
MediaType string `json:"mediaType"`
FileSize int64 `json:"fileSize"`
DownloadLink string `json:"downloadLink"`
}
Attachment is one file attached to a page. Title is the filename, which is what `attachments download` matches its glob against. DownloadLink is relative to the site base URL, in the form /rest/api/content/{pageID}/child/attachment/{attachmentID}/download.
The API's own attachment id is deliberately not modelled: DownloadLink already carries it, and it is nothing a caller could use — downloads select by filename, and Confluence keeps same-named attachments as versions of one attachment rather than as separate ones, so no two listing entries share a name for an id to tell apart.
type Child ¶
type Child struct {
ID string `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
Type string `json:"type"`
}
Child is one entry of a page's direct children. Type distinguishes pages from whiteboards, folders, databases and embeds. The API returns no space for a child, so callers that need one read it off the parent page.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to one Confluence Cloud site with one set of credentials.
func New ¶
New creates a Client for the given site base URL (including the /wiki path, e.g. "https://example.atlassian.net/wiki"), authenticating with Atlassian Basic auth (account email + API token).
func (*Client) ChildPages ¶
ChildPages lists a page's direct child pages, oldest sibling first.
It reads /pages/{id}/direct-children — which returns every hierarchical content type — and filters to pages, rather than the /pages/{id}/children endpoint that would do that server-side: the latter is the only deprecated GET in the v2 API. Filtering inside the pagination loop (as opposed to in the caller) is what keeps limit counting child *pages* rather than children of any type.
func (*Client) CommentReplies ¶
func (c *Client) CommentReplies(ctx context.Context, kind CommentKind, commentID string, limit int) ([]Comment, bool, error)
CommentReplies lists the direct replies to one comment.
func (*Client) CreateFooterComment ¶
CreateFooterComment posts body as a new footer comment on a page and returns the comment the server created.
The body travels as the storage representation, unchanged. The API also accepts "wiki" and "atlas_doc_format", neither of which cflio sends: they are conversions, and a body that is converted on the way in is no longer the one the caller wrote.
func (*Client) CurrentUser ¶
CurrentUser identifies the authenticated account. It is the cheapest authenticated call available, so `auth login` uses it to validate credentials before writing anything to disk.
func (*Client) DownloadAttachment ¶
func (c *Client) DownloadAttachment(ctx context.Context, downloadLink string, w io.Writer) (int64, error)
DownloadAttachment streams the bytes behind downloadLink to w and returns how many it wrote. The body is copied through untouched: an attachment is the only response cflio takes that is not JSON, and re-encoding it would leave an image that no longer opens.
It cannot go through do, which asks for JSON and decodes the response as JSON. Nothing else about the request differs, so the auth and the error handling are the same.
The download redirects once, to Atlassian's media service on another host. Go's http.Client follows it and drops the Authorization header on the way, because the target is neither the initial host nor a subdomain of it (see shouldCopyHeaderOnRedirect in net/http). That is what the media service needs: the redirect target is a signed URL, and it rejects a request that carries the site's credentials as well.
func (*Client) GetPage ¶
GetPage fetches a page. withBody requests the storage representation; callers that only need the version (the optimistic-lock pre-flight in `update`) pass false to avoid pulling the whole body back.
func (*Client) PageAttachments ¶
func (c *Client) PageAttachments(ctx context.Context, pageID string, limit int) ([]Attachment, bool, error)
PageAttachments lists the files attached to a page.
func (*Client) PageComments ¶
func (c *Client) PageComments(ctx context.Context, kind CommentKind, pageID string, limit int) ([]Comment, bool, error)
PageComments lists a page's root comments of the given kind, oldest first, with bodies in the storage representation. The comment endpoints do not accept body-format=view, so rendering for humans happens locally.
func (*Client) PagesByTitle ¶
func (c *Client) PagesByTitle(ctx context.Context, spaceKey string, titles []string) ([]PageMatch, error)
PagesByTitle resolves exact page titles within one space. Titles are unique per space, so one query answers the whole batch; a title that matched nothing is simply missing from the result.
One query per space rather than one OR-ing (space, title) pairs across spaces: every hit then belongs to the space that was asked for, so the caller can attribute it by title alone without the response having to carry the space back.
func (*Client) Search ¶
Search runs a CQL query, returning at most limit results plus the total number of matches the server reports, which is what lets the caller say how many results were left out.
CQL search only exists in v1, and it pages by offset rather than cursor.
func (*Client) SiteURL ¶
SiteURL returns the site base URL the client was built for, for callers that need to turn a relative link into an absolute one.
func (*Client) UpdatePage ¶
UpdatePage writes a new version of a page and returns the updated page, whose version number is the authoritative new value to record locally.
type Comment ¶
type Comment struct {
ID string `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
ParentCommentID string `json:"parentCommentId"`
Version Version `json:"version"`
ResolutionStatus string `json:"resolutionStatus"`
Body struct {
Storage struct {
Value string `json:"value"`
} `json:"storage"`
} `json:"body"`
Properties struct {
InlineOriginalSelection string `json:"inlineOriginalSelection"`
} `json:"properties"`
Links links `json:"_links"`
}
Comment is one footer or inline comment. ResolutionStatus and Properties are only populated for inline comments.
type CommentKind ¶
type CommentKind string
CommentKind selects between the two comment families a page can carry.
const ( FooterComments CommentKind = "footer-comments" // InlineComments are anchored to a highlighted span of the body. InlineComments CommentKind = "inline-comments" )
type Option ¶
type Option func(*Client)
Option configures a Client constructed by New.
func WithHTTPClient ¶
WithHTTPClient overrides the HTTP client. Tests use this to point at an httptest server; production callers should leave it unset.
type Page ¶
type Page struct {
ID string `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
// Subtype is "live" for a live doc and absent for a classic page. It is
// what tells the two apart offline, once `read` has recorded it in the
// sidecar: a live doc's editor rewrites the storage body behind cflio's
// back, which some edits have to refuse rather than silently lose.
Subtype string `json:"subtype"`
Version Version `json:"version"`
Body struct {
Storage struct {
Representation string `json:"representation"`
Value string `json:"value"`
} `json:"storage"`
} `json:"body"`
Links links `json:"_links"`
}
Page is the subset of v2's PageSingle cflio needs. Body holds the raw storage representation exactly as the API returned it.
type PageMatch ¶
PageMatch is one page a title lookup resolved to. Title is what the search API returned, which is HTML-escaped and may carry highlight markers, so comparing it to a title read out of a page body means normalising it first — format.StripHighlightMarkers does both halves.
type SearchResult ¶
type SearchResult struct {
Content *struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Title string `json:"title"`
} `json:"content"`
Title string `json:"title"`
Excerpt string `json:"excerpt"`
URL string `json:"url"`
EntityType string `json:"entityType"`
LastModified string `json:"lastModified"`
}
SearchResult is one CQL hit. Content is absent for results that are not content (spaces, users), so callers fall back to the top-level fields.
type UpdatePageRequest ¶
type UpdatePageRequest struct {
ID string `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
Body struct {
Representation string `json:"representation"`
Value string `json:"value"`
} `json:"body"`
Version struct {
Number int `json:"number"`
Message string `json:"message"`
} `json:"version"`
}
UpdatePageRequest is the v2 page-update payload. Every field is required by the API; cflio fills them from the sidecar written at read time.
func NewUpdatePageRequest ¶
func NewUpdatePageRequest(id, status, title, body string, version int, message string) UpdatePageRequest
NewUpdatePageRequest builds an update payload for a storage-format body.