Documentation
¶
Overview ¶
Package contentsource reads a service's content — its description, its keywords, its picture — from a system outside Atlas, and decides whether any of it may reach the shop (ADR-0491).
Nothing here is engine state. A source is design-time configuration, a fetch is a catalogue-service request made before any command and outside the processor, and what it yields is ordinary catalogue data written through the run loop. No record is replayed, so nothing here is fetched during replay, and I2, I4 and I6 are untouched (§ 7 of the record).
The package does not know the catalogue. It answers three questions for it — what a page says, whether a page may be shown, and whether the account Atlas reads with is what it must be — and the catalogue decides what to do with the answers. That keeps the security rules a pure function of the facts an adapter returns (rules.go), which is what lets them be tested row by row.
Index ¶
- Constants
- Variables
- func CheckPage(p Page, ancestors []Page, st Standing) []string
- func CheckStanding(st Standing) []string
- func Keywords(src Source, labels ...[]string) []string
- func LooksLikeCredential(ref string) bool
- func PictureNames(p Page, field string) []string
- func Recheck(ctx context.Context, ad Adapter, checks []Check) ([]string, []string, error)
- func ReleasedFor(st Standing, space string) bool
- func RichText(blocks []Rich) string
- func SafeRef(ref string) string
- type Adapter
- type Block
- type BlockKind
- type Bound
- type Change
- type Changes
- type Check
- type Content
- type Converted
- type Dropped
- type Fact
- type Hit
- type Language
- type Mark
- type MarkKind
- type Page
- type Picture
- type Refused
- type Registry
- type Result
- type Rich
- type Runner
- func (r *Runner) Kick(source string)
- func (r *Runner) RecheckItems(ctx context.Context, source string, items []string) error
- func (r *Runner) Reload()
- func (r *Runner) Run(quit <-chan struct{})
- func (r *Runner) Sweep(ctx context.Context, source string) error
- func (r *Runner) Watched(source string) []string
- type Service
- func (s *Service) HandleCheck(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)
- type SlotProblem
- type Source
- type SpaceGrant
- type Standing
- type Store
- type Taken
- type Target
- type WikiWikiBangBang
- func (w *WikiWikiBangBang) Ancestors(ctx context.Context, p Page) ([]Page, error)
- func (w *WikiWikiBangBang) Attachment(ctx context.Context, pageID, file string, max int64) ([]byte, string, error)
- func (w *WikiWikiBangBang) Changes(ctx context.Context, after int64, wait time.Duration) (Changes, error)
- func (w *WikiWikiBangBang) Children(ctx context.Context, p Page) ([]string, error)
- func (w *WikiWikiBangBang) Head(ctx context.Context) (int64, error)
- func (w *WikiWikiBangBang) Page(ctx context.Context, id string) (Page, error)
- func (w *WikiWikiBangBang) Resolve(ctx context.Context, input string) (string, error)
- func (w *WikiWikiBangBang) Search(ctx context.Context, q string) ([]Hit, error)
- func (w *WikiWikiBangBang) Standing(ctx context.Context) (Standing, error)
- func (w *WikiWikiBangBang) Unwatch(ctx context.Context, id string) error
- func (w *WikiWikiBangBang) Watch(ctx context.Context, id string) error
Constants ¶
const KindWikiWikiBangBang = "wikiwikibangbang"
KindWikiWikiBangBang is the first source (§ 3): the typed item model whose pages a product owner keeps one per language.
const MaxWait = 30 * time.Second
MaxWait is the longest the source holds a long poll (its ChangesWait budget).
const Placeholder = '▒'
Placeholder is the character a source veils a passage with. wikiwikibangbang writes eight of them for every passage a reader may not read (its SecretPlaceholder); one is enough to know that something was veiled.
const RecheckInterval = 15 * time.Minute
RecheckInterval is the sweep's period, and the age a passed recheck may reach before publishing refuses (§ 9).
const RoleViewer = "viewer"
RoleViewer is the one role the account may hold in a space (§ 4). Any other — editor, space administrator — reads protected passages or bypasses restrictions, which is exactly what the shop must not be built from.
const ScopeRead = "read"
ScopeRead is the one token scope that passes (its ADR-0027): a token that can change no content.
Variables ¶
var ErrNoSource = errors.New("no such content source")
ErrNoSource is a binding naming a source that is not configured.
var ErrUnreadable = errors.New("the page cannot be read")
ErrUnreadable is a page the account cannot read, or that is gone. The source does not say which, and must not have to: an unreadable item is non-existent to whoever cannot read it (its D10), and either way the page is refused.
var ImageID = regexp.MustCompile(`^[0-9a-f]{32}$`)
ImageID is the shape of an image's id in Atlas's store.
var Kinds = []string{KindWikiWikiBangBang}
Kinds are the source kinds this build has an adapter for. A kind is admitted only once its adapter proves the facts of § 10, so the list is closed and a configuration naming anything else is refused rather than stored for a later build to interpret.
Functions ¶
func CheckPage ¶
CheckPage refuses a page for any protection of § 8: a read restriction on it or on any ancestor, an open hold, a page not live, a space not released for the shop. ancestors is the whole parent chain; an ancestor that could not be read never reaches here, because reading the chain fails first.
An edit restriction alone is no reason: reading stays open, and the facts do not carry it at all.
func CheckStanding ¶
CheckStanding refuses an account that is anything but a read-only viewer (§ 4): a token not of scope read, any system role, any space role other than viewer. A wrong standing stops every fetch and quarantines every bound item of the source.
func Keywords ¶
Keywords are the labels of every language page, minus the excluded labels (§ 7), lower-cased, sorted and once each.
func LooksLikeCredential ¶
LooksLikeCredential reports whether a credentialsRef is a credential rather than the name of one. The field names a vault secret; a token typed into it is stored in clear beside the configuration, shown on the console, written to the audit trail and quoted in error messages product managers read. So such a value is refused when a source is saved, and never shown or logged when one that predates the refusal is read.
func PictureNames ¶
PictureNames are the attachment names a page's picture field names, in order. A string is a list separated by commas or new lines; a list of strings is one name each. Anything else names nothing.
func Recheck ¶
Recheck asks whether the content copied from these pages may still be shown (§ 9). It reads the account's standing and every page's protection — never its content — and returns every reason it may not, and the ids it read, for the change feed to watch: the pages and every ancestor, because a restriction set on an ancestor protects the page without changing it.
A passage protected after the copy was taken refuses as well. Its text was plain when it was copied, so Atlas holds it; § 8 removes a passage *before* converting, and this is the same rule applied to a copy that already exists.
func ReleasedFor ¶
ReleasedFor reports whether a space is released for the shop: the account holds viewer there, and nothing else (§ 4). A space it holds no grant in is not released, whatever the account happens to read through an inherited default.
func RichText ¶
RichText is a rich description as plain text, for a reader that keeps only text: headings and paragraphs stand apart, list items are lines, a table is its rows with cells separated by " | ", a child page or an embedded item is its title followed by its own text. An image is its caption, where it has one.
Types ¶
type Adapter ¶
type Adapter interface {
// Standing reads the account's own standing.
Standing(ctx context.Context) (Standing, error)
// Search lists pages matching a query.
Search(ctx context.Context, q string) ([]Hit, error)
// Resolve turns what a product manager pasted — an address, a key or an id
// — into the page's stable id. An address of another host than the source's
// is refused.
Resolve(ctx context.Context, input string) (string, error)
// Page reads one page by id, with its security facts.
Page(ctx context.Context, id string) (Page, error)
// Ancestors reads the whole parent chain of a page, nearest first. An
// ancestor the account cannot read is [ErrUnreadable].
Ancestors(ctx context.Context, p Page) ([]Page, error)
// Children lists a page's readable child pages, by id, in sibling order.
Children(ctx context.Context, p Page) ([]string, error)
// Attachment reads the file of a page named name — or whose id is name, as
// an image block names it — at most max bytes, with the media type the
// source gives it.
Attachment(ctx context.Context, pageID, name string, max int64) ([]byte, string, error)
// Watch asks the source to report changes to a page in its feed.
Watch(ctx context.Context, id string) error
// Unwatch ends the watch of a page Atlas no longer holds anything of, by
// id — which ends it even when the page can no longer be read.
Unwatch(ctx context.Context, id string) error
// Head is the feed's current position, from which no change is yet known.
Head(ctx context.Context) (int64, error)
// Changes waits up to wait for changes after the cursor.
Changes(ctx context.Context, after int64, wait time.Duration) (Changes, error)
}
Adapter is one source kind (§ 10). Every method talks to the source and none may be called on the run loop.
A new adapter is admitted only when it proves the facts the rules read, and where its source has no equivalent of a protection it reports the fact as Unknown — which refuses — rather than as absent.
type Block ¶
type Block struct {
Kind BlockKind
// Source is the source's own name for the block's kind, for the preview's
// account of what was dropped.
Source string
Text string
Marks []Mark
// Ref names what an image (an attachment of the page) or an embed (another
// item) shows.
Ref string
// Level is a heading's level, 1 to 3; Tone a panel's tone; Checked whether a
// task is done; Align and Width how an image or a paragraph is laid out.
Level int
Tone string
Checked bool
Align string
Width int
}
Block is one block of a body.
type BlockKind ¶
type BlockKind string
BlockKind is what a block of a page's body is, in the vocabulary the conversion understands. An adapter maps its source's kinds onto these, and everything it cannot map is BlockOther, which is never taken.
const ( // BlockParagraph, BlockHeading and the list items are the text § 7 takes. BlockParagraph BlockKind = "paragraph" BlockHeading BlockKind = "heading" BlockBullet BlockKind = "bullet" BlockOrdered BlockKind = "ordered" // The blocks the rich description takes besides the text (amendment of // 2026-10-28): a quote, code, a panel with its tone, a table whose text holds // its rows (one per line) and cells (separated by tabs), a divider, an image // attachment of the page (Ref names it) and another item embedded (Ref names // it). The plain description still takes only the text kinds above. BlockQuote BlockKind = "quote" BlockCode BlockKind = "code" BlockPanel BlockKind = "panel" BlockTable BlockKind = "table" BlockDivider BlockKind = "divider" BlockImage BlockKind = "image" BlockEmbed BlockKind = "embed" // BlockOther is everything else — videos, models, computed lists, a kind // added to the source later. Dropped and reported, never converted. BlockOther BlockKind = "other" )
type Bound ¶
type Bound interface {
// Referenced names every source a binding names, configured or not: a
// binding to a source that was removed is rechecked too, and fails.
Referenced() ([]string, error)
// Targets lists the products bound to a source.
Targets(source string) ([]Target, error)
// Record writes what a recheck found.
Record(source string, results []Result) error
}
Bound is the catalogue, as the recheck sees it. Every method is called off the run loop and makes its own visit to it.
type Change ¶
type Change struct {
Seq int64
// Item is the changed page's id, or empty for "a watched page you can no
// longer read changed" — which names nothing, so everything is rechecked.
Item string
}
Change is one entry of the source's change feed (§ 11).
type Changes ¶
Changes is one delivery of the change feed: what changed after the cursor, and the cursor to ask from next.
type Check ¶
Check is one page a recheck asks about: its id, and the passages the copy of it was taken without.
type Content ¶
type Content struct {
Languages map[string]Language
Keywords []string
Picture *Picture
// Images are the images the rich descriptions show, by the id their blocks
// name: copied out of the source, so the shop never asks it for anything.
Images map[string]*Picture
}
Content is everything a binding supplies: a description per language, the keywords, and the picture.
func Gather ¶
func Gather(ctx context.Context, ad Adapter, src Source, slots map[string]string, primary string, maxPicture int64) (Content, error)
Gather reads every language page of a binding, and the picture of the primary one, and refuses the whole of it if any part fails (§ 7: per item, all or nothing). slots maps a language tag to what to read for it.
func GatherExcept ¶
func GatherExcept(ctx context.Context, ad Adapter, src Source, slots map[string]string, primary string, maxPicture int64, others map[string]string) (Content, error)
GatherExcept is Gather for a source some of whose pages already describe other products: others maps such a page's id to its product, and a child page among them is left out of every language page's description with everything below it, as PreviewExcept leaves it out.
type Converted ¶
type Converted struct {
// Text is the page's body as plain text. Never markup: it is rendered as
// text wherever it is shown.
Text string
// Passages are the protected passages removed, by the id the source gives
// them. They are kept so a recheck can tell a passage protected after the
// copy was taken — whose text Atlas then holds — from one it removed.
Passages []string
// Dropped names the blocks not taken, by the source's kind, in order.
Dropped []string
// Problems refuse the page: something the conversion could not make safe.
Problems []string
}
Converted is one page's body as text, and what the conversion left out.
func Convert ¶
Convert turns a page's body into text (§ 7, § 8).
The order is the rule: every protected passage is removed by its mark *before* anything is converted, whether or not the account could read it, so the text of a passage never enters Atlas. Mentions go the same way. A link keeps its label and loses its target, because only the text is taken. Paragraphs, headings and list items are text; every other block is dropped and named.
Then the guard: a placeholder character left anywhere in the result means the source veiled something Atlas did not recognise as a passage, and the page is refused rather than shown with a hole whose edges might be wrong.
type Dropped ¶
type Dropped struct {
// Page names the page it was on, by key where it has one.
Page string `json:"page"`
// What is "page" for a child page dropped with everything below it,
// "passage" for a protected passage removed, or the source's kind of a block
// not taken.
What string `json:"what"`
Reasons []string `json:"reasons,omitempty"`
}
Dropped is something a description does not carry, and why.
type Fact ¶
type Fact int
Fact is one security fact about a page, as far as its adapter could establish it.
It is three-valued on purpose, and the zero value is the third. § 10 says "a fact an adapter cannot establish counts as present", and a boolean cannot say that: false would read as "established absent" for every field an adapter forgot to fill. With Unknown as the zero value, a forgotten field refuses the page — the failure is a refusal somebody reads, not content in the shop.
type Hit ¶
type Hit struct {
ID string `json:"id"`
Key string `json:"key"`
Title string `json:"title"`
Space string `json:"space"`
State string `json:"state,omitempty"`
URL string `json:"url,omitempty"`
}
Hit is one search result (§ 5).
type Language ¶
type Language struct {
// Input is what was asked for — an address, a key or an id.
Input string `json:"input"`
// Page is the language page itself.
Page Taken `json:"page"`
Space string `json:"space,omitempty"`
State string `json:"state,omitempty"`
// Labels are the page's own labels, as the source answered them.
Labels []string `json:"labels,omitempty"`
// Children are the child pages taken, depth first in sibling order.
Children []Taken `json:"children,omitempty"`
// Text is the description: the page's body followed by its children's.
Text string `json:"text"`
// Rich is the description as the source lays it out: the page's blocks,
// then each child page with its own, depth first, every embedded item in
// place (amendment of 2026-10-28).
Rich []Rich `json:"rich,omitempty"`
// Embeds are the items embedded anywhere in the description and taken.
Embeds []Taken `json:"embeds,omitempty"`
// Dropped is everything the rules or the conversion left out.
Dropped []Dropped `json:"dropped,omitempty"`
// Refusals refuse the page. Empty is the only pass.
Refusals []string `json:"refusals,omitempty"`
// contains filtered or unexported fields
}
Language is one language page as a fetch read it (§ 5, § 7).
func Preview ¶
Preview reads one language page as a fetch would take it, with everything the rules would drop and every reason they refuse (§ 5). It never fails on a rule: a refusal is part of the answer. It fails on a source it cannot talk to.
func PreviewExcept ¶
func PreviewExcept(ctx context.Context, ad Adapter, st Standing, input string, others map[string]string) (Language, error)
PreviewExcept is Preview for a source some of whose pages already describe other products. others maps such a page's id to the product it describes: a child page found there is that product's description and not this one's, so it is dropped with everything below it, naming the product. The page asked for is never skipped — whether it may be bound is the caller's question.
type Mark ¶
type Mark struct {
Kind MarkKind
Start int
End int
// Ref names the passage a MarkPassage protects.
Ref string
}
Mark is one range over a block's text, in Unicode code points (runes) — the unit wikiwikibangbang counts its marks in.
type MarkKind ¶
type MarkKind string
MarkKind is what a mark over a block's text is. Only the two that remove text matter to the conversion; every other mark (bold, a link) keeps its text and loses its formatting or target.
const ( // MarkPassage is a protected passage (its ADR-0088). Its text is removed // whatever the account could read. MarkPassage MarkKind = "passage" // MarkMention is a mention of a person or an item. Its text is removed: a // mention of somebody is not a description of a service. MarkMention MarkKind = "mention" // MarkOther keeps its text. MarkOther MarkKind = "other" )
type Page ¶
type Page struct {
ID string
Key string
Title string
Space string
Parent string
URL string
Version int
State string
Labels []string
// Properties are the page's typed properties, as the source answered them.
Properties map[string]any
Body []Block
// ReadRestricted is a read restriction on the page itself, whoever it
// names — including the account (§ 8).
ReadRestricted Fact
// Held is an open data protection hold.
Held Fact
// Withdrawn is a page not live: trashed, purged or redacted.
Withdrawn Fact
// Presence is the source's own word for where the page stands, for the
// refusal's sentence.
Presence string
}
Page is one page as its adapter read it, with the security facts of § 8.
func WalkAncestors ¶
func WalkAncestors(ctx context.Context, read func(context.Context, string) (Page, error), p Page) ([]Page, error)
WalkAncestors reads a page's parent chain, nearest first, one page at a time. It is what an adapter's Ancestors is made of, and what a recheck uses through its cache. A cycle or a chain past [maxAncestors] cannot be established, and is an error rather than a shorter chain.
type Picture ¶
Picture is the first picture the primary page names, copied out of the source so the shop never asks the source for anything (ADR-0391).
type Refused ¶
type Refused struct {
// Problems name the slot (a language tag, or "" for the whole binding) and
// why.
Problems []SlotProblem
}
Refused is a fetch that may not be written, with every reason at once: a product manager who fixes one refusal per attempt eventually works around the gate.
type Registry ¶
type Registry struct {
// Transport is the HTTP transport adapters use; nil is the process-wide one.
// Tests set a test server's.
Transport http.RoundTripper
// Dial builds the adapter for a source. Nil dials by kind; tests replace it.
Dial func(src Source, token string, transport http.RoundTripper) (Adapter, error)
// contains filtered or unexported fields
}
Registry is how the rest of Atlas reaches a configured source: the record from the store, the token from the secret store, and the adapter for its kind.
The first two are the run loop's (I3 — the vault and the stores are written there); dialling and every request after it are not, and must not be. So every method here makes its own visit to the loop and none may be called from inside one: Do is a rendezvous, and a nested one deadlocks.
func NewRegistry ¶
NewRegistry builds the registry over a store. secret runs on the loop.
func (*Registry) Configured ¶
Configured reports whether any source is configured — the switch the whole feature hangs on (§ 3: off until an administrator configures one). Off the loop only.
func (*Registry) OnChange ¶
func (r *Registry) OnChange(fn func())
OnChange registers what to tell when a source is written or removed.
type Rich ¶
type Rich struct {
// Kind is heading, paragraph, bullet, ordered, quote, code, panel, table,
// divider, image, page (a child page: Title and Blocks) or embed (an
// embedded item: Title and Blocks).
Kind string `json:"kind"`
// Level is a heading's level, 1 to 3.
Level int `json:"level,omitempty"`
// Text is the block's text with every protected passage and mention
// removed; an image's caption.
Text string `json:"text,omitempty"`
// Checked is a task's state, for a bullet that came from one.
Checked bool `json:"checked,omitempty"`
// Tone is a panel's tone, as the source names it.
Tone string `json:"tone,omitempty"`
// Rows are a table's cells, the first row its header.
Rows [][]string `json:"rows,omitempty"`
// Image is an image's id in Atlas's own store: the shop never asks the
// source for anything. Empty in a preview, which copies nothing.
Image string `json:"image,omitempty"`
// Width is an image's width in percent of the column, 0 for its own size;
// Align its alignment, as the source names it.
Width int `json:"width,omitempty"`
Align string `json:"align,omitempty"`
// Title and Blocks are a child page's or an embedded item's.
Title string `json:"title,omitempty"`
Blocks []Rich `json:"blocks,omitempty"`
// contains filtered or unexported fields
}
Rich is one block of a rich description.
type Runner ¶
type Runner struct {
// Interval is the sweep's period; Wait is how long one long poll of the feed
// is held; Backoff is the pause after the feed failed. Settable for tests.
Interval time.Duration
Wait time.Duration
Backoff time.Duration
// contains filtered or unexported fields
}
Runner owns the recheck goroutines.
func (*Runner) Kick ¶
Kick rechecks a source now, in the background — after an unlink, so the watches nothing needs any more end without waiting for the next sweep.
func (*Runner) RecheckItems ¶
RecheckItems rechecks the named products of a source.
func (*Runner) Reload ¶
func (r *Runner) Reload()
Reload asks the runner to look at the configured sources again now.
func (*Runner) Run ¶
func (r *Runner) Run(quit <-chan struct{})
Run keeps one goroutine per source until quit closes, then stops them all and waits for them.
type Service ¶
type Service struct {
// InUse names the products bound to a source, so removing one that still
// supplies something is refused rather than leaving bindings nothing can
// recheck. Off the loop. Nil answers none.
InUse func(id string) ([]string, error)
// contains filtered or unexported fields
}
Service is the content-source configuration API.
func NewService ¶
NewService builds the configuration API over a registry.
func (*Service) HandleCheck ¶
func (s *Service) HandleCheck(w http.ResponseWriter, r *http.Request)
HandleCheck asks the source for the account's standing, so an administrator sees before any product is bound whether the token and the roles are right. It answers 200 either way: the result is the answer, not the request's failure.
func (*Service) HandleCreate ¶
func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)
HandleCreate configures a new source. An id already taken is a conflict: a create that silently replaced a source would rebind every product bound to it.
func (*Service) HandleDelete ¶
func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
HandleDelete removes a source no product is bound to.
func (*Service) HandleGet ¶
func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
HandleGet reads one.
func (*Service) HandleList ¶
func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
HandleList lists the configured sources.
func (*Service) HandleUpdate ¶
func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)
HandleUpdate replaces a source's configuration. The id is the path's.
type SlotProblem ¶
type SlotProblem struct {
Lang string `json:"lang,omitempty"`
Page string `json:"page,omitempty"`
Message string `json:"message"`
}
SlotProblem is one reason a binding cannot be written.
type Source ¶
type Source struct {
// ID names the source in a binding. It is chosen, not minted, because a
// binding names it for as long as the item exists.
ID string `json:"id"`
// Name is what a product manager reads when choosing a source.
Name string `json:"name"`
// Kind is the adapter, one of [Kinds].
Kind string `json:"kind"`
// BaseURL is where the source answers, https only. A page address of any
// other host is refused when it is pasted (§ 5), and the adapter follows no
// redirect away from it.
BaseURL string `json:"baseURL"`
// CredentialsRef names the vault secret (or ATLAS_CONNECTOR_<REF>_TOKEN)
// holding the technical account's read token.
CredentialsRef string `json:"credentialsRef"`
// CredentialsRefRedacted is set only on a source as an API answer shows it
// (see [Source.Shown]): the stored credentialsRef looked like a credential
// itself, so it is not shown, and the administrator must name a secret.
CredentialsRefRedacted bool `json:"credentialsRefRedacted,omitempty"`
// PictureField is the property of the primary page that names the
// attachments to use as pictures. Empty means the source supplies none.
PictureField string `json:"pictureField,omitempty"`
// ExcludedLabels are labels that organise the wiki and are not keywords.
ExcludedLabels []string `json:"excludedLabels,omitempty"`
// LinkInShop says whether the shop may show "more in the wiki" under a
// description. Off by default: the address of a page is itself something
// the product owner may not have meant every orderer to see.
LinkInShop bool `json:"linkInShop,omitempty"`
// CreatedAt and UpdatedAt are the server's Unix nanoseconds.
CreatedAt int64 `json:"createdAt,omitempty"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
}
Source is one configured content source (§ 3).
It is configuration an administrator writes and nothing else does. The credential is a reference into the secret store (ADR-0041), never the token itself: a source record is what a backup carries and what an API answer shows, and a token in either is a token leaving the building.
func (Source) Normalized ¶
Normalized is the source as it is stored: trimmed, the base URL without a trailing slash, the excluded labels lower-cased and sorted once each.
func (Source) Problem ¶
Problem returns why a source cannot be stored, or "" when it can.
It is a statement about the record alone. Whether the credential resolves and whether the account's standing is right are questions for the source, asked by the check route and before every fetch — a configuration refused because the wiki was down at the moment somebody saved it would be a configuration nobody can write during an outage.
type SpaceGrant ¶
SpaceGrant is one role the account holds in one space, directly or through a group.
type Standing ¶
type Standing struct {
// Scope is the token's scope; only "read" passes.
Scope string
SystemRoles []string
SystemRolesKnown bool
Spaces []SpaceGrant
SpacesKnown bool
}
Standing is the account's own standing at the source (§ 4).
The two Known flags are the three-valued rule for a list: an empty list the source answered and a list it never answered are different statements, and only the first one is "none".
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds the configured sources, one JSON file each. Like every design-time store it is the run loop's to write (I3).
type Taken ¶
type Taken struct {
ID string `json:"ref"`
Key string `json:"key,omitempty"`
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
Version int `json:"version"`
Passages []string `json:"passages,omitempty"`
}
Taken is one page whose text a description holds.
type WikiWikiBangBang ¶
type WikiWikiBangBang struct {
// contains filtered or unexported fields
}
WikiWikiBangBang is one configured wikiwikibangbang source.
func NewWikiWikiBangBang ¶
func NewWikiWikiBangBang(baseURL, token string, transport http.RoundTripper) (*WikiWikiBangBang, error)
NewWikiWikiBangBang builds the adapter for a source's base URL and token. transport is nil in production — the process-wide transport, which carries the outbound proxy (internal/sysproxy) — and a test server's in tests.
func (*WikiWikiBangBang) Ancestors ¶
Ancestors walks the parent chain with one GET per ancestor. The source answers a page's own restriction and not its ancestors', so the chain is the only way to learn what the page inherits.
func (*WikiWikiBangBang) Attachment ¶
func (w *WikiWikiBangBang) Attachment(ctx context.Context, pageID, file string, max int64) ([]byte, string, error)
Attachment reads the newest file of a page by its name: the listing, then the download (GET /api/items/{id}/attachments/{attachment}).
func (*WikiWikiBangBang) Changes ¶
func (w *WikiWikiBangBang) Changes(ctx context.Context, after int64, wait time.Duration) (Changes, error)
Changes long-polls the feed for changes after the cursor.
func (*WikiWikiBangBang) Children ¶
Children lists a page's readable children in sibling order, following the tree's cursor (GET /api/spaces/{space}/tree?parent=).
func (*WikiWikiBangBang) Head ¶
func (w *WikiWikiBangBang) Head(ctx context.Context) (int64, error)
Head reads the feed's position: GET /api/changes without a cursor answers no changes and the head.
func (*WikiWikiBangBang) Resolve ¶
Resolve turns an address (https://host/#/item/SVC-12), a key or an id into the page's id. The id is what a binding stores: a key changes when an item moves between spaces, an id never does.
func (*WikiWikiBangBang) Standing ¶
func (w *WikiWikiBangBang) Standing(ctx context.Context) (Standing, error)
Standing reads the account's standing from two routes: GET /api/me for the token's scope and the two system roles it reports as flags, and GET /api/me/roles for every grant, direct and through groups (§ 11). A source that does not answer the second has not proved the account is a viewer, and the fields stay unknown.
func (*WikiWikiBangBang) Unwatch ¶
func (w *WikiWikiBangBang) Unwatch(ctx context.Context, id string) error
Unwatch ends the account's watch of a page by its id. By id it ends even the watch of a page the account can no longer read (its ADR-0120); by key it would not, which is why a binding keeps ids.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package wikifake is a wikiwikibangbang that lives in a test: the routes Atlas's adapter reads, answering from pages a test writes, under an https test server.
|
Package wikifake is a wikiwikibangbang that lives in a test: the routes Atlas's adapter reads, answering from pages a test writes, under an https test server. |