Documentation
¶
Overview ¶
Package schemas talks to the public tabstack-schemas repository on GitHub and manages a local store of pulled schemas. It is deliberately separate from internal/client: that client is bound to the authenticated Tabstack API, whereas this fetches unauthenticated raw files from GitHub over a different host. Keeping them apart avoids leaking the API bearer token to GitHub and keeps each transport's concerns isolated.
Index ¶
- Constants
- func CanonicalSHA(data []byte) string
- func Equal(a, b []byte) bool
- func FindLocal(dir, selector string) (string, error)
- func ListLocal(dir string) ([]string, error)
- func LocalPath(dir, schemaPath string) string
- func Read(dir, schemaPath string) ([]byte, bool, error)
- func SafePath(dir, schemaPath string) (string, error)
- func Write(dir, schemaPath string, data []byte) error
- type Entry
- type FetchError
- type Fetcher
- type Index
- type Manifest
- type ManifestEntry
- type Option
Constants ¶
const DefaultRawBase = "https://raw.githubusercontent.com/Mozilla-Ocho/tabstack-schemas/main"
DefaultRawBase is the raw.githubusercontent.com root for the schema repo's main branch. Every schema and the index hang off this.
const IndexCacheName = ".index-cache.json"
IndexCacheName is the per-store cache of the library manifest, used to avoid refetching index.json on every list/pull/status.
const ManifestName = ".manifest.json"
ManifestName is the bookkeeping file recording what was pulled and from which remote content, so `schema status` can tell local edits from upstream drift.
Variables ¶
This section is empty.
Functions ¶
func CanonicalSHA ¶
CanonicalSHA returns the hex SHA-256 of the canonical JSON form of data, so formatting-only differences do not change the hash. Non-JSON input is hashed as raw bytes.
func Equal ¶
Equal reports whether two schema documents are semantically equal, ignoring formatting differences (whitespace, key order). This keeps a re-pull from flagging a conflict when only the on-disk formatting differs from the remote. If either side is not valid JSON we fall back to a raw byte comparison.
func FindLocal ¶
FindLocal resolves a selector to a stored schema's repo-relative path by scanning dir. It is the offline counterpart to Index.Resolve: it works against already-pulled files without touching the network. A selector may be a repo-relative path (trailing .json optional) or a bare schema name; a bare name that matches more than one stored file returns an ambiguity error.
func ListLocal ¶
ListLocal returns the repo-relative paths of every schema stored under dir, sorted. Internal files (manifest, index cache) are skipped, and a missing store directory yields an empty list rather than an error.
func LocalPath ¶
LocalPath joins a storage directory with a repo-relative schema path. The repo layout (category/name.json) is mirrored on disk so a pulled schema keeps a stable identity for later comparison. It does not guard against traversal; callers that touch the filesystem with caller- or remote-supplied paths use SafePath instead.
func Read ¶
Read returns the bytes of a locally stored schema. The bool reports whether the file exists; a missing file is not an error.
func SafePath ¶
SafePath is LocalPath plus a containment check: it rejects any schema path that would escape dir. Both user selectors and remote index.json entries flow through here before any read/write, so a hostile manifest cannot read or clobber files outside the store.
Two layers: a lexical check rejects absolute paths and ".." traversal, then a symlink-resolved check rejects paths that escape via a symlinked component inside the store (e.g. <store>/jobs -> /etc, where jobs/x.json is lexically contained but writes to /etc/x.json). The returned path is the original lexical join (not the resolved one) so the on-disk layout stays stable.
Types ¶
type Entry ¶
type Entry struct {
Category string `json:"category"`
Title string `json:"title"`
Description string `json:"description"`
Path string `json:"path"`
}
Entry is one schema's manifest record from index.json.
type FetchError ¶
FetchError is a non-2xx response from GitHub. We surface the URL and status so the command layer can produce a clear, actionable message.
func (*FetchError) Error ¶
func (e *FetchError) Error() string
type Fetcher ¶
type Fetcher struct {
// contains filtered or unexported fields
}
Fetcher retrieves the index and individual schemas from the repo. The http.Client and base URL are injectable so tests can point at a mock, mirroring client.WithHTTPClient.
func NewFetcher ¶
NewFetcher constructs a Fetcher pointed at the public schema repo.
type Index ¶
type Index struct {
Name string `json:"name"`
Description string `json:"description"`
Count int `json:"count"`
Schemas []Entry `json:"schemas"`
}
Index is the decoded index.json: a manifest of every available schema.
func CachedIndex ¶
func CachedIndex(ctx context.Context, f *Fetcher, dir string, ttl time.Duration, refresh bool) (Index, error)
CachedIndex returns the library index, using a per-store cache file so we do not refetch index.json within ttl. refresh forces a fetch. If the network fetch fails but a (possibly stale) cache exists, the cache is returned so the commands keep working offline.
func (Index) Resolve ¶
Resolve maps a user selector onto manifest entries. A selector may be:
- a repo-relative path ("jobs/job-posting.json", trailing .json optional)
- a category ("jobs") -> every schema in that category
- a bare schema name ("job-posting") -> the matching schema
Bare names that match more than one schema, or that collide with a category, return an ambiguity error so the user can disambiguate with a full path.
type Manifest ¶
type Manifest struct {
Schemas map[string]ManifestEntry `json:"schemas"`
}
Manifest is the decoded .manifest.json, keyed by repo-relative schema path.
func LoadManifest ¶
LoadManifest reads the store manifest; a missing manifest yields an empty one.
func (Manifest) Save ¶
Save writes the manifest. json.MarshalIndent emits map keys in sorted order, so the file stays diff-friendly across pulls.
The write is atomic (temp file + rename) so a crash mid-write cannot leave a truncated, unparseable manifest behind. It is NOT locked, however: two concurrent `schema pull` runs against the same store still race, and the last writer wins, dropping the other's just-recorded entries (they then read as untracked until re-pulled). Running one pull at a time per store avoids this; a documented CLI limitation, not a guarantee.
type ManifestEntry ¶
ManifestEntry records the canonical content hash of a schema as it was last pulled, plus when. Comparing this against the current local file detects local edits; comparing it against the current remote detects upstream changes.
type Option ¶
type Option func(*Fetcher)
Option configures a Fetcher.
func WithHTTPClient ¶
WithHTTPClient swaps the underlying http.Client. Mostly useful for tests.
func WithRawBase ¶
WithRawBase overrides the raw content base URL. Mostly useful for tests.