zotero

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package zotero is zotgo's client for a running Zotero 7+ desktop app.

It speaks only Zotero's own HTTP contracts — today the Local API (/api/*), which is read-only — and never opens zotero.sqlite. The package depends on the standard library alone.

This is an internal package, not a published SDK: it cannot be imported from outside this module, and its API is free to change with zotgo's needs.

Index

Constants

View Source
const (
	LibraryKindUser  = "user"
	LibraryKindGroup = "group"
)
View Source
const AuthorizeTimeout = 2 * time.Minute

AuthorizeTimeout bounds how long Authorize waits for the user to answer Zotero's approval modal. It is far longer than a normal request because a person has to click Allow or Deny.

View Source
const DefaultBaseURL = "http://localhost:23119"

DefaultBaseURL is Zotero's default local HTTP server address.

View Source
const DefaultTimeout = 5 * time.Second

DefaultTimeout bounds a single Local API round-trip, body included.

View Source
const DefaultWebBaseURL = "https://api.zotero.org"

DefaultWebBaseURL is the hosted Web API's address.

View Source
const DefaultWebTimeout = 30 * time.Second

DefaultWebTimeout bounds a single Web API round-trip. It is looser than the local default because the request crosses the public internet rather than a loopback socket.

View Source
const LocalWriteUnsupportedReason = "this Zotero build has no local write API (added upstream in zotero/zotero#5015; update Zotero once a release ships it)"

LocalWriteUnsupportedReason explains a local endpoint whose Zotero build predates the write API. It is shared by doctor's capability report and the write commands' pre-flight so the two never drift.

View Source
const (
	// MaxAttachmentFileSize is zotgo's explicit, conservative limit while
	// Zotero's Local API receiver buffers an upload before staging it to disk.
	MaxAttachmentFileSize int64 = 128 * 1024 * 1024
)
View Source
const MaxDeleteObjects = 50

MaxDeleteObjects is the most keys accepted in one multi-delete (upstream MAX_DELETE_OBJECTS).

View Source
const MaxWriteObjects = 50

MaxWriteObjects is the most objects the local write API accepts in one batch (MAX_WRITE_OBJECTS upstream).

Variables

View Source
var (
	// ErrZoteroDown means the endpoint refused a connection: nothing is
	// listening, so Zotero is not running.
	ErrZoteroDown = errors.New("zotero is not running")
	// ErrTransport means the request failed after a connection was established.
	// Zotero may well be running; the exchange itself broke.
	ErrTransport = errors.New("zotero request failed")
	// ErrBadPagination means Zotero advertised a rel="next" cursor that cannot
	// be followed: no start offset, an unparseable one, or one that does not
	// advance. Following it would loop forever.
	ErrBadPagination = errors.New("zotero returned an unusable pagination cursor")
	// ErrUnsupportedFormat means the requested export format is not one of the
	// Zotero translators zotgo knows how to reassemble across pages.
	ErrUnsupportedFormat = errors.New("unsupported export format")
	// ErrUnmergeableExport means the result spans several pages in a format
	// whose pages cannot be joined into one valid document.
	ErrUnmergeableExport = errors.New("export spans pages that cannot be merged")
	// ErrLocalAPIDisabled means Zotero is running, but the Local API pref is off.
	ErrLocalAPIDisabled = errors.New("zotero local api is disabled")
	// ErrNotFound means Zotero returned 404 for a requested object.
	ErrNotFound = errors.New("zotero object not found")
	// ErrLibraryNotFound means a library selector did not match My Library or a group.
	ErrLibraryNotFound = errors.New("zotero library not found")
	// ErrAmbiguousLibrary means a library selector matched more than one group name.
	ErrAmbiguousLibrary = errors.New("zotero library selector is ambiguous")
	// ErrInvalidAPIKey means the Web API rejected the API key, or it resolved to
	// no user: it is missing, revoked, or wrong.
	ErrInvalidAPIKey = errors.New("zotero web api key is invalid")
	// ErrWriteUnsupported means the local Zotero build has no write API, so a
	// write cannot succeed. Its message is the actionable reason.
	ErrWriteUnsupported = errors.New(LocalWriteUnsupportedReason)
	// ErrAuthorizeDenied means the user declined the local write authorization
	// prompt in Zotero.
	ErrAuthorizeDenied = errors.New("zotero write authorization denied")
	// ErrWriteNotAuthorized means no WriteAuthorizer permitted the write: none was
	// installed (deny-by-default) or the installed one refused without a more
	// specific reason. It is distinct from ErrWriteUnauthorized, which is Zotero's
	// own 401 for a bad key.
	ErrWriteNotAuthorized = errors.New("write not authorized")
	// ErrWriteUnauthorized means a local write was rejected for a missing or
	// consumed local API key (HTTP 401): re-authorize.
	ErrWriteUnauthorized = errors.New("zotero local write not authorized")
	// ErrPreconditionRequired means a required write precondition header was
	// absent (HTTP 428): the Zotero-Server-ID or If-Unmodified-Since-Version.
	ErrPreconditionRequired = errors.New("zotero write precondition required")
	// ErrPreconditionFailed means a write precondition did not hold (HTTP 412):
	// the Zotero-Server-ID no longer matches, or the library changed since the
	// version the write was based on.
	ErrPreconditionFailed = errors.New("zotero write precondition failed")
	// ErrWriteOutcomeUnknown means Zotero accepted a mutating request but its
	// successful response could not be decoded, so the created object's key is
	// unavailable and retrying might duplicate data.
	ErrWriteOutcomeUnknown = errors.New("zotero write outcome is unknown")
)

Functions

func ExportFormats added in v0.3.0

func ExportFormats() []string

ExportFormats lists the server-side translator names Export accepts, sorted.

func ManagedAttachmentLinkMode added in v0.9.0

func ManagedAttachmentLinkMode(linkMode string) (bool, error)

ManagedAttachmentLinkMode reports whether linkMode identifies storage owned by Zotero. Unknown modes fail closed so callers do not treat future managed modes as ordinary linked attachments.

func RequireItemType added in v0.9.0

func RequireItemType(raw json.RawMessage, want string) error

RequireItemType checks one raw item's family without decoding any other fields.

Types

type Annotation added in v0.9.0

type Annotation struct {
	Key           string
	AttachmentKey string
	Type          string
	PageLabel     string
	Color         string
	SortIndex     string
	HasText       bool
	HasComment    bool
}

Annotation is compact metadata for one attachment annotation. Text, comments, position data, and image bytes remain outside this listing model.

func DecodeAnnotation added in v0.9.0

func DecodeAnnotation(raw json.RawMessage) (Annotation, error)

DecodeAnnotation validates and decodes compact annotation metadata from one complete Zotero item envelope.

func DecodeAnnotations added in v0.9.0

func DecodeAnnotations(rawItems []json.RawMessage, attachmentKey string) ([]Annotation, error)

DecodeAnnotations validates one attachment's annotations and returns them in document order: sort index first, key as a deterministic tie-breaker, and missing sort indexes last.

type Attachment added in v0.9.0

type Attachment struct {
	Key          string
	ParentKey    string
	Title        string
	LinkMode     string
	ContentType  string
	Charset      string
	Filename     string
	URL          string
	AccessDate   string
	DateAdded    string
	DateModified string
	Tags         []Tag
	MD5          *string
	MTime        *int64
	Enclosure    *AttachmentEnclosure
}

Attachment is the metadata Zotero exposes for one attachment item. It contains no filesystem-derived state.

func DecodeAttachment added in v0.9.0

func DecodeAttachment(raw json.RawMessage) (Attachment, error)

DecodeAttachment decodes the bounded attachment fields from a raw item envelope. Unrelated link objects remain outside the typed contract.

type AttachmentEnclosure added in v0.9.0

type AttachmentEnclosure struct {
	Href   string `json:"href"`
	Type   string `json:"type"`
	Title  string `json:"title"`
	Length *int64 `json:"length"`
}

AttachmentEnclosure is the location and optional size metadata Zotero advertises for a stored attachment.

type AttachmentUploadAuthorization added in v0.9.0

type AttachmentUploadAuthorization struct {
	Exists    bool
	URL       string
	UploadKey string
}

AttachmentUploadAuthorization is Zotero's response to the file authorize phase.

type AttachmentUploadMetadata added in v0.9.0

type AttachmentUploadMetadata struct {
	MD5         string
	Filename    string
	Size        int64
	MTime       int64
	ContentType string
}

AttachmentUploadMetadata describes the immutable file staged for upload.

type Authenticator added in v0.5.0

type Authenticator interface {
	// contains filtered or unexported methods
}

An Authenticator applies an endpoint's credentials to an outbound request.

It is a Client collaborator rather than part of the Profile so that the credential never rides along with the endpoint's printable identity. The local endpoint needs no credential; the Web API needs an API key.

The method is unexported: the two endpoints zotgo speaks to are the only authentication strategies there are, so there is nothing for an outside implementation to add.

type Capability added in v0.3.0

type Capability string

Capability is something an endpoint can do. It describes the *endpoint*, not what zotgo has implemented against it: doctor answers "what does this Zotero allow?", and a capability may be available before a command uses it.

const (
	// CapabilityRead is reading items, collections, and tags.
	CapabilityRead Capability = "read"
	// CapabilityWrite is creating, updating, or deleting objects through the
	// official API write contract.
	CapabilityWrite Capability = "write"
	// CapabilityConnectorIngest is app-mediated ingestion over /connector/*:
	// PDF recognition, file import, snapshots.
	CapabilityConnectorIngest Capability = "connector-ingest"
	// CapabilityLocalFileAccess is resolving an attachment to a local file path.
	CapabilityLocalFileAccess Capability = "local-file-access"
)

type CapabilityStatus added in v0.3.0

type CapabilityStatus struct {
	Name      Capability
	Supported bool
	// Reason explains an unsupported capability, in terms the user can act on
	// or at least understand. Empty when Supported.
	Reason string
}

CapabilityStatus reports whether one capability is available, and why not when it is missing. A missing capability without a reason is a bug: the user is left with nothing to act on.

type ChildItemsOptions added in v0.9.0

type ChildItemsOptions struct {
	ItemType string
	Limit    int
	Start    int
}

ChildItemsOptions controls requests for the children of an item.

type Client

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

Client talks to a running Zotero over its Local API and Connector API, or to the hosted Web API — which of the two is fixed by its Profile at construction.

A Client is safe for concurrent use. It performs no I/O until a method is called; construction never fails.

func New

func New(baseURL string, opts ...Option) *Client

New returns a Client for a running local Zotero at baseURL, or DefaultBaseURL when baseURL is empty. The local endpoint needs no credential. Options are applied in order.

func NewWeb added in v0.5.0

func NewWeb(baseURL, apiKey string, opts ...Option) *Client

NewWeb returns a Client for the hosted Web API at baseURL, or DefaultWebBaseURL when baseURL is empty, authenticated with apiKey. Options are applied in order.

func (*Client) AllCollections

func (c *Client) AllCollections(ctx context.Context, library LibraryRef, opts CollectionsOptions) ([]Envelope, error)

AllCollections follows Link rel="next" and returns every collection.

func (*Client) AllItems

func (c *Client) AllItems(ctx context.Context, library LibraryRef, opts ItemsOptions) ([]Envelope, error)

AllItems follows Link rel="next" and returns the full result set.

func (*Client) AllRawAnnotations added in v0.9.0

func (c *Client) AllRawAnnotations(ctx context.Context, library LibraryRef, attachmentKey string) ([]json.RawMessage, error)

AllRawAnnotations reads every direct annotation child of one attachment. It validates that the parent is an attachment and honors the parent response's backoff before requesting children, then returns complete annotation envelopes in server order — or no partial result if any page fails. It encapsulates the parent-fetch + validate + backoff + child-fetch dance behind one call, matching how Note/Attachment/RawItemWithChildren expose their reads.

func (*Client) AllRawChildItems added in v0.9.0

func (c *Client) AllRawChildItems(ctx context.Context, library LibraryRef, parentKey string, opts ChildItemsOptions) ([]json.RawMessage, error)

AllRawChildItems follows Link rel="next" and returns every complete child item envelope in server order. It returns no partial result if any page fails.

func (*Client) Attachment added in v0.9.0

func (c *Client) Attachment(ctx context.Context, library LibraryRef, key string) (Attachment, error)

Attachment reads and decodes one attachment's bounded metadata.

func (*Client) Authorize added in v0.6.0

func (c *Client) Authorize(ctx context.Context, appName string) (remember bool, err error)

Authorize obtains a local API key for writes by asking Zotero to prompt the user; appName is shown in the modal. On approval it stores the key for subsequent writes and reports whether Zotero will remember it (persistent) or granted single-use access. Only the local endpoint supports it.

It returns ErrAuthorizeDenied if the user declines. The key is never logged.

func (*Client) AuthorizeAttachmentUpload added in v0.9.0

func (c *Client) AuthorizeAttachmentUpload(ctx context.Context, library LibraryRef, attachmentKey string, metadata AttachmentUploadMetadata) (AttachmentUploadAuthorization, error)

AuthorizeAttachmentUpload requests a new full-file upload or learns that the attachment already has matching bytes.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL reports the address the client targets.

func (*Client) CheckHealth

func (c *Client) CheckHealth(ctx context.Context) Health

CheckHealth probes the active endpoint and returns a Health snapshot. It never returns an error: an unreachable or misconfigured endpoint is a normal, renderable result, not a failure of the probe.

func (*Client) Collection added in v0.6.0

func (c *Client) Collection(ctx context.Context, library LibraryRef, key string) (Envelope, error)

Collection reads one collection by key.

func (*Client) Collections

func (c *Client) Collections(ctx context.Context, library LibraryRef, opts CollectionsOptions) ([]Envelope, Page, error)

Collections reads a single page of collections.

func (*Client) CreateCollections added in v0.6.0

func (c *Client) CreateCollections(ctx context.Context, op Operation, lib LibraryRef, cols []json.RawMessage) (WriteResult, error)

CreateCollections creates collections ({name, parentCollection}) in one batch.

func (*Client) CreateItems added in v0.6.0

func (c *Client) CreateItems(ctx context.Context, op Operation, lib LibraryRef, items []json.RawMessage) (WriteResult, error)

CreateItems creates items from their JSON representations (itemType plus fields) in one batch write.

func (*Client) CreateItemsReturningKeys added in v0.9.0

func (c *Client) CreateItemsReturningKeys(ctx context.Context, op Operation, lib LibraryRef, items []json.RawMessage) (KeyWriteResult, error)

CreateItemsReturningKeys creates items while decoding only generated keys.

func (*Client) DeleteCollections added in v0.6.0

func (c *Client) DeleteCollections(ctx context.Context, op Operation, lib LibraryRef, keys []string, ifUnmodifiedSince int) error

DeleteCollections removes collections by key (collectionKey=…). Deleting a collection does not delete its items.

func (*Client) DeleteItems added in v0.6.0

func (c *Client) DeleteItems(ctx context.Context, op Operation, lib LibraryRef, keys []string, ifUnmodifiedSince int) error

DeleteItems removes items by key in one request (itemKey=…).

func (*Client) DeleteTags added in v0.6.0

func (c *Client) DeleteTags(ctx context.Context, op Operation, lib LibraryRef, names []string, ifUnmodifiedSince int) error

DeleteTags removes tags from the whole library by name (tag=a||b||…), stripping each from every item. Names are joined with "||" as the API expects.

func (*Client) Export added in v0.3.0

func (c *Client) Export(ctx context.Context, library LibraryRef, opts ItemsOptions, format string) ([]byte, error)

Export returns the items matching opts rendered by one of Zotero's own translators (format=…), reassembled across pages.

Zotero does the formatting; zotgo only rejoins what pagination split. A format whose pages cannot be rejoined into a valid document yields ErrUnmergeableExport rather than corrupt output.

func (*Client) Groups

func (c *Client) Groups(ctx context.Context) ([]Group, error)

Groups lists the groups the endpoint's own user can reach. On the Web API the route carries the key owner's real id; locally it is the "0" sentinel.

func (*Client) HasLocalKey added in v0.6.0

func (c *Client) HasLocalKey() bool

HasLocalKey reports whether the client holds a local API key for writes.

func (*Client) Item

func (c *Client) Item(ctx context.Context, library LibraryRef, key string) (Envelope, error)

Item reads one item by key.

func (*Client) ItemChildren

func (c *Client) ItemChildren(ctx context.Context, library LibraryRef, key string) ([]Envelope, Page, error)

ItemChildren reads one page of attachments and notes under a parent item.

func (*Client) ItemTemplate added in v0.6.0

func (c *Client) ItemTemplate(ctx context.Context, itemType string) (json.RawMessage, error)

ItemTemplate returns a blank item of itemType, assembled from the Local API's itemTypeFields and itemTypeCreatorTypes endpoints — the Local API has no /items/new template route. Fields appear in Zotero's order with itemType first, plus one empty creator of the primary type and empty tags/collections/ relations, ready to fill in and pass to CreateItems.

Special item types (note, attachment) carry their content outside these fields, so their template will be sparse. An unknown type yields a clear error.

func (*Client) Items

func (c *Client) Items(ctx context.Context, library LibraryRef, opts ItemsOptions) ([]Envelope, Page, error)

Items reads a single page of items.

func (*Client) IterItems added in v0.8.0

func (c *Client) IterItems(ctx context.Context, library LibraryRef, opts ItemsOptions, yield func([]Envelope) error) error

IterItems fetches items one page at a time and passes each page to yield in order, so a caller can process a very large library without ever holding all of it in memory. Pagination, the start cursor, and Web API backoff are handled exactly as AllItems. If yield returns an error, iteration stops and that error is returned unchanged; a nil error runs to the last page.

func (*Client) LibraryFileAccess added in v0.9.0

func (c *Client) LibraryFileAccess(ctx context.Context) ([]LibraryFiles, error)

LibraryFileAccess lists, per library the running Zotero can currently file into, whether that library accepts attachment files. It is pure diagnosis: one POST /connector/getSelectedCollection — the same Connector surface doctor already pings for liveness — with no ingestion semantics, so it does not touch the "connector is not a write backend" boundary. Local endpoint only.

func (*Client) LibraryVersion added in v0.6.0

func (c *Client) LibraryVersion(ctx context.Context, lib LibraryRef) (int, error)

LibraryVersion returns the library's current clientVersion, read from the Last-Modified-Version header of a cheap list request. Bulk writes and deletes use it as If-Unmodified-Since-Version. Single-object PATCH requests must use that object's version instead.

func (*Client) LocalKey added in v0.6.0

func (c *Client) LocalKey() string

LocalKey returns the client's local API key, for a caller that wants to persist a freshly authorized one. Empty when none is held.

func (*Client) Note added in v0.9.0

func (c *Client) Note(ctx context.Context, library LibraryRef, key string) (Note, error)

Note reads and decodes one note's bounded metadata and rich-text HTML.

func (*Client) PatchCollection added in v0.6.0

func (c *Client) PatchCollection(ctx context.Context, op Operation, lib LibraryRef, key string, patch json.RawMessage, ifUnmodifiedSince int) error

PatchCollection applies a partial update to one collection. It backs collection.rename ({"name":…}) and collection.move ({"parentCollection":…}), so the caller names the operation.

func (*Client) PatchItem added in v0.6.0

func (c *Client) PatchItem(ctx context.Context, op Operation, lib LibraryRef, key string, patch json.RawMessage, ifUnmodifiedSince int) error

PatchItem applies a partial update (a JSON object of fields to change) to one item. It backs item.patch as well as tag.add/tag.remove, which edit the item's tags array — so the caller names the operation.

func (*Client) Profile added in v0.3.0

func (c *Client) Profile() Profile

Profile reports the endpoint this Client targets.

func (*Client) RawChildItems added in v0.9.0

func (c *Client) RawChildItems(ctx context.Context, library LibraryRef, parentKey string, opts ChildItemsOptions) ([]json.RawMessage, Page, error)

RawChildItems reads one page of complete child item envelopes. It validates only each child's identity, leaving all other Zotero-owned fields untouched.

func (*Client) RawItem added in v0.9.0

func (c *Client) RawItem(ctx context.Context, library LibraryRef, key string) (json.RawMessage, error)

RawItem reads one complete item envelope, validating only the response shape and requested identity while leaving every other Zotero-owned field untouched.

func (*Client) RawItemWithChildren added in v0.9.0

func (c *Client) RawItemWithChildren(ctx context.Context, library LibraryRef, key string) (json.RawMessage, []json.RawMessage, error)

RawItemWithChildren reads one item and every direct child, honoring backoff between the parent response and the first child request.

func (*Client) RegisterAttachmentUpload added in v0.9.0

func (c *Client) RegisterAttachmentUpload(ctx context.Context, library LibraryRef, attachmentKey, uploadKey string) error

RegisterAttachmentUpload makes staged bytes the attachment's managed file.

func (*Client) ReplaceItem added in v0.8.0

func (c *Client) ReplaceItem(ctx context.Context, op Operation, lib LibraryRef, key string, full json.RawMessage, ifUnmodifiedSince int) error

ReplaceItem overwrites one item with a complete JSON representation (PUT). Fields absent from full are reset to their defaults — unlike PatchItem, which leaves them — so the caller must send the whole object. One field is verified to behave otherwise against the local API: an omitted "tags" is preserved rather than cleared, so a caller that means to strip tags must send "tags": []. (Omitted "collections" is cleared, like ordinary fields.)

func (*Client) RequireWriteCapability added in v0.9.0

func (c *Client) RequireWriteCapability(ctx context.Context) error

RequireWriteCapability confirms this endpoint can accept writes before the CLI prompts, authorizes, or issues one. On the local endpoint the write API announces itself with a Zotero-Server-ID header on every response (zotero/zotero#5015); a single GET bootstraps it, and its absence means the build predates writes. It returns ErrWriteUnsupported in that case, so a write fails early with an actionable reason instead of a late, opaque 404. The web endpoint has no such probe; its write grants are reported by doctor.

func (*Client) ResolveLibrary

func (c *Client) ResolveLibrary(ctx context.Context, selector string) (LibraryRef, error)

ResolveLibrary maps a CLI-facing selector to a library route on this endpoint.

"", "me", "my", and "user" select the endpoint's own user library. Group selectors may be a numeric group id, "group/<id>", "groups/<id>", or an exact group name. Identity is resolved once (a no-op locally, one /keys/current call on the Web endpoint) and reused for any group lookup.

func (*Client) ServerID added in v0.6.0

func (c *Client) ServerID() string

ServerID reports the most recently seen Zotero-Server-ID, or "" if none.

func (*Client) SetLocalKey added in v0.6.0

func (c *Client) SetLocalKey(key string)

SetLocalKey installs a previously obtained (remembered) local API key, so a caller that persisted one need not re-prompt.

func (*Client) SetWriteAuthorizer added in v0.9.0

func (c *Client) SetWriteAuthorizer(a WriteAuthorizer)

SetWriteAuthorizer installs the authority policy consulted before every write. Installing nil restores deny-by-default: with no authorizer, every write is refused with ErrWriteNotAuthorized, so a write path that never established one cannot reach Zotero.

func (*Client) Stats

func (c *Client) Stats(ctx context.Context, library LibraryRef) (Stats, error)

Stats returns library-wide counts. It issues one cheap limit=1 request per count and reads the Total-Results header, never paging the actual rows.

func (*Client) UploadAuthorizedAttachment added in v0.9.0

func (c *Client) UploadAuthorizedAttachment(ctx context.Context, authorization AttachmentUploadAuthorization, body io.Reader, size int64) error

UploadAuthorizedAttachment streams staged bytes to Zotero's key-scoped local receiver.

func (*Client) WebKey added in v0.5.0

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

WebKey fetches /keys/current, describing the key this Client authenticates with. It is only valid on the Web endpoint.

A 403 or 404 means the key is missing, revoked, or wrong; both surface as ErrInvalidAPIKey rather than the transport-level status, so the CLI can print one clear message.

type CollectionData

type CollectionData struct {
	Key              string          `json:"key"`
	Name             string          `json:"name"`
	ParentCollection json.RawMessage `json:"parentCollection"`
}

CollectionData contains the stable collection fields used for tree rendering.

func (CollectionData) ParentKey

func (d CollectionData) ParentKey() string

ParentKey returns data.parentCollection as a collection key, or "" for top-level collections.

type CollectionPath added in v0.9.0

type CollectionPath struct {
	Key       string
	Name      string
	ParentKey string
	NumItems  int
	Segments  []CollectionPathSegment
}

CollectionPath is one requested collection and its complete ancestry.

func ResolveCollectionPaths added in v0.9.0

func ResolveCollectionPaths(envelopes []Envelope, keys []string) ([]CollectionPath, error)

ResolveCollectionPaths resolves requested keys from a complete collection listing. Results preserve request order and duplicates.

type CollectionPathSegment added in v0.9.0

type CollectionPathSegment struct {
	Key  string
	Name string
}

CollectionPathSegment is one collection in a root-to-leaf path.

type CollectionsOptions

type CollectionsOptions struct {
	Top   bool
	Start int
}

CollectionsOptions controls collection-list requests.

type Creator

type Creator struct {
	CreatorType string `json:"creatorType"`
	FirstName   string `json:"firstName"`
	LastName    string `json:"lastName"`
	Name        string `json:"name"`
}

type EndpointKind added in v0.3.0

type EndpointKind string

EndpointKind distinguishes the endpoints zotgo can be pointed at.

The durable axis is local vs remote, not read vs write: Zotero's Web API already offers remote CRUD, and its Local API is read-only only for now. Each endpoint is its own version and concurrency domain, so an operation is never silently moved between them.

const (
	// EndpointLocal is a Zotero 7+ desktop app's HTTP server on this machine.
	EndpointLocal EndpointKind = "local"
	// EndpointWeb is the hosted Web API at api.zotero.org, reached with an API
	// key. Its transport is implemented; the CLI wiring lands with v0.5.
	EndpointWeb EndpointKind = "web"
)

type Envelope

type Envelope struct {
	Key     string                     `json:"key"`
	Version int                        `json:"version"`
	Library Library                    `json:"library"`
	Links   map[string]Link            `json:"links"`
	Meta    map[string]json.RawMessage `json:"meta"`
	Data    json.RawMessage            `json:"data"`
}

Envelope is the common Local API wrapper for items and collections.

Data is intentionally raw: Zotero item fields vary by itemType, and preserving unknown fields is safer than flattening them away.

func (Envelope) AttachmentData added in v0.9.0

func (e Envelope) AttachmentData() (Attachment, error)

AttachmentData decodes and validates one attachment's data object.

func (Envelope) CollectionData

func (e Envelope) CollectionData() (CollectionData, error)

CollectionData decodes e.Data as Zotero collection JSON.

func (Envelope) CreatorSummary

func (e Envelope) CreatorSummary() string

CreatorSummary returns meta.creatorSummary.

func (Envelope) ItemData

func (e Envelope) ItemData() (ItemData, error)

ItemData decodes e.Data as Zotero item JSON.

func (Envelope) ItemType

func (e Envelope) ItemType() string

ItemType returns data.itemType when this envelope wraps an item.

func (Envelope) NoteData added in v0.9.0

func (e Envelope) NoteData() (Note, error)

NoteData decodes and validates one note item's data object.

func (Envelope) NumChildren

func (e Envelope) NumChildren() int

NumChildren returns meta.numChildren. Missing/non-numeric values return 0.

func (Envelope) ParsedDate

func (e Envelope) ParsedDate() string

ParsedDate returns meta.parsedDate.

func (Envelope) Title

func (e Envelope) Title() string

Title is a convenience accessor for item titles and collection names.

func (*Envelope) UnmarshalJSON added in v0.7.0

func (e *Envelope) UnmarshalJSON(data []byte) error

type Group

type Group struct {
	ID      int64     `json:"id"`
	Version int       `json:"version"`
	Meta    GroupMeta `json:"meta"`
	Data    GroupData `json:"data"`
}

Group is the subset of /api/users/0/groups needed for library selection.

type GroupData

type GroupData struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

type GroupMeta

type GroupMeta struct {
	NumItems int `json:"numItems"`
}

type Health

type Health struct {
	// Endpoint identifies what was probed: its kind and address.
	Endpoint Profile
	// Reachable is the liveness signal both endpoints share: the local Connector
	// accepted a connection, or the Web API answered at all (even to reject a key).
	Reachable bool

	// Local endpoint only.
	//
	// ZoteroRunning is true when the Connector HTTP server accepted a connection
	// (Zotero 7+ is running). ZoteroVersion is its reported version.
	// LocalAPIEnabled is true when the Local API serves protected routes rather
	// than 403 "Local API is not enabled".
	ZoteroRunning   bool
	ZoteroVersion   string
	LocalAPIEnabled bool
	// ServerID is the Zotero-Server-ID header the Local API returns on every
	// response once it has the local write API (zotero/zotero#5015). Its presence
	// is the probe signal that this Zotero build supports writes; empty on builds
	// that predate the write endpoints.
	ServerID string

	// Web endpoint only.
	//
	// KeyValid is true when /keys/current accepted the API key and it resolves to
	// a user. WebUserID is that user's id.
	KeyValid  bool
	WebUserID int64

	// SchemaVersion and APIVersion are reported by either endpoint on a
	// successful protected read.
	SchemaVersion string
	APIVersion    string
	// contains filtered or unexported fields
}

Health is a snapshot of whether zotgo can reach the active endpoint and what it may do there. Some fields describe only one endpoint kind; which apply is fixed by Endpoint.Kind.

func (Health) Capabilities added in v0.3.0

func (h Health) Capabilities() []CapabilityStatus

Capabilities reports what the probed endpoint supports, in a stable order.

Capabilities are derived from the probe rather than discovered by exercising them: zotgo will not issue a speculative write to find out whether writes are allowed. The Web endpoint can report writes honestly because the key's own grants say so; the local endpoint cannot, and says why.

func (Health) Ready

func (h Health) Ready() bool

Ready reports whether the active endpoint is usable for reads: locally, that Zotero is running and its Local API is enabled; on the Web API, that the key is valid and grants some library read access.

func (Health) Supports added in v0.3.0

func (h Health) Supports(c Capability) bool

Supports reports whether the endpoint offers a capability.

type ItemData

type ItemData struct {
	Key          string `json:"key"`
	Version      int    `json:"version"`
	ItemType     string `json:"itemType"`
	Title        string `json:"title"`
	Date         string `json:"date"`
	DateAdded    string `json:"dateAdded"`
	DateModified string `json:"dateModified"`
	// ParentItem is the parent's key on a child item (attachment/note/annotation);
	// empty on a top-level item.
	ParentItem  string    `json:"parentItem"`
	Creators    []Creator `json:"creators"`
	Tags        []Tag     `json:"tags"`
	Collections []string  `json:"collections"`
}

ItemData contains the stable item fields zotgo needs for display and tests.

func (*ItemData) UnmarshalJSON added in v0.7.0

func (d *ItemData) UnmarshalJSON(data []byte) error

type ItemIdentity added in v0.9.0

type ItemIdentity struct {
	Key      string
	ItemType string
}

ItemIdentity is the small invariant shared by every Zotero item envelope.

func DecodeItemIdentity added in v0.9.0

func DecodeItemIdentity(raw json.RawMessage) (ItemIdentity, error)

DecodeItemIdentity validates only an item's envelope shape and identity. It deliberately leaves every other Zotero-owned field untouched.

type ItemsOptions

type ItemsOptions struct {
	Top        bool
	Collection string
	Tags       []string
	Limit      int
	Start      int
	Query      string
	Everything bool
	ItemType   string
	// ItemKeys restricts the result to these item keys (`itemKey=`). Combined
	// with Top it selects exactly those items; on the unfiltered /items route
	// Zotero also returns their children.
	ItemKeys []string
}

ItemsOptions controls item-list and search requests.

type KeyWriteResult added in v0.9.0

type KeyWriteResult struct {
	Successful map[string]string
	Unchanged  map[string]string
	Failed     map[string]WriteFailure
}

KeyWriteResult decodes only created keys, so callers that need no returned object data are not coupled to unrelated Zotero envelope fields.

type Library

type Library struct {
	Type  string          `json:"type"`
	ID    int64           `json:"id"`
	Name  string          `json:"name"`
	Links map[string]Link `json:"links"`
}

Library identifies the library that owns an envelope.

type LibraryFiles added in v0.9.0

type LibraryFiles struct {
	// ID is the numeric library id (1 is My Library; a group's is its group id).
	ID int
	// Name is the library name as Zotero reports it.
	Name string
	// FilesEditable is true when the library accepts attachment files.
	FilesEditable bool
}

LibraryFiles reports whether one library the running Zotero can currently file into accepts attachment files. It answers what doctor's endpoint-level local-file-access capability cannot: a group library can be writable yet refuse files (its group has file storage disabled), and that otherwise surfaces only as attachments that silently never appear.

type LibraryRef

type LibraryRef struct {
	Kind string
	ID   int64
	Name string
}

LibraryRef is the route-level identity of a Zotero library.

func GroupLibrary

func GroupLibrary(id int64, name string) LibraryRef

GroupLibrary returns the Local API route for a Zotero group library.

func UserLibrary

func UserLibrary() LibraryRef

UserLibrary returns the Local API route for the logged-in user's library.

type Link struct {
	Href           string `json:"href"`
	Type           string `json:"type"`
	Title          string `json:"title"`
	Length         *int64 `json:"length"`
	AttachmentType string `json:"attachmentType"`
	AttachmentSize int64  `json:"attachmentSize"`
}

Link is a Zotero Local API link object. Attachment links include extra fields.

func (*Link) UnmarshalJSON added in v0.9.0

func (l *Link) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts href:false, which Zotero uses for an imported_file attachment whose metadata exists but whose managed bytes are not registered.

type Note added in v0.9.0

type Note struct {
	Key          string
	ParentKey    string
	DateAdded    string
	DateModified string
	Tags         []Tag
	HTML         string
}

Note is one Zotero note item's bounded metadata and exact rich-text HTML.

func DecodeNote added in v0.9.0

func DecodeNote(raw json.RawMessage) (Note, error)

DecodeNote decodes one note's bounded fields from a raw item envelope.

type Operation added in v0.9.0

type Operation string

Operation names a single mutating command for write-authorization scoping. The values are the stable, per-command vocabulary a write lease grants against, so least privilege can withhold, say, item.delete while allowing item.patch. They intentionally mirror the CLI's write commands one-to-one.

const (
	OpItemCreate       Operation = "item.create"
	OpItemPatch        Operation = "item.patch"
	OpItemReplace      Operation = "item.replace"
	OpItemDelete       Operation = "item.delete"
	OpCollectionCreate Operation = "collection.create"
	OpCollectionRename Operation = "collection.rename"
	OpCollectionMove   Operation = "collection.move"
	OpCollectionDelete Operation = "collection.delete"
	OpTagAdd           Operation = "tag.add"
	OpTagRemove        Operation = "tag.remove"
	OpTagDelete        Operation = "tag.delete"
	// OpAttachmentImport is reserved for the managed-file upload command (#52),
	// which conforms to the write-lease model once it lands.
	OpAttachmentImport Operation = "attachment.import"
)

type Option added in v0.3.0

type Option func(*Client)

An Option configures a Client at construction.

func WithHTTPClient added in v0.3.0

func WithHTTPClient(h *http.Client) Option

WithHTTPClient makes the Client issue its requests through h, which carries the transport, redirect policy, and timeout. Use it to supply a custom transport for retries, tracing, or tests. A nil h is ignored.

The Client copies h, so later changes to the caller's value — including those made by WithTimeout — do not affect it.

func WithTimeout added in v0.3.0

func WithTimeout(d time.Duration) Option

WithTimeout bounds each round-trip. It overrides any timeout carried by a client passed to WithHTTPClient, so pass it afterwards to take effect.

type Page

type Page struct {
	TotalResults        int
	NextURL             string
	LastModifiedVersion string
	SchemaVersion       string
	APIVersion          string
	// Backoff is the Web API's request to pause before further requests when its
	// servers are loaded (the Backoff header). Zero when absent. Pagination loops
	// honor it between pages.
	Backoff time.Duration
}

Page carries pagination and version headers for a Local API response.

type Profile added in v0.3.0

type Profile struct {
	Kind    EndpointKind
	BaseURL string
}

Profile is the printable identity of the endpoint a Client talks to: its kind and address, nothing more.

It deliberately carries no credential. The Web API key lives on the Client's Authenticator, not here, so a Profile is always safe to log or embed in machine output without leaking a secret. It also carries no server identity: the Zotero-Server-ID a write-capable build returns is a runtime value the Client caches for writes, not part of the endpoint's stable identity.

func LocalProfile added in v0.3.0

func LocalProfile(baseURL string) Profile

LocalProfile describes a running Zotero on this machine.

func WebProfile added in v0.5.0

func WebProfile(baseURL string) Profile

WebProfile describes the hosted Web API at baseURL (api.zotero.org).

func (Profile) LibraryPrefix added in v0.5.0

func (p Profile) LibraryPrefix(lib LibraryRef) string

LibraryPrefix is the API path prefix for a library's resources on this endpoint. The Local API namespaces everything under /api and addresses the logged-in user as the "0" sentinel; the Web API omits /api and uses the real numeric user id carried by the LibraryRef.

type Relation added in v0.9.0

type Relation struct {
	Predicate string
	Target    string
	TargetKey string
}

Relation is one outgoing predicate/target edge from an item. Target remains authoritative; TargetKey is only a convenience for strict Zotero item URIs.

func DecodeRelations added in v0.9.0

func DecodeRelations(raw json.RawMessage) ([]Relation, error)

DecodeRelations returns outgoing relations in stable predicate/target order. Zotero reads use arrays, while its write API also documents one string value.

type Stats

type Stats struct {
	Items       int
	TopItems    int
	Collections int
	Tags        int
}

Stats holds library-wide counts derived from Total-Results headers.

type StatusError

type StatusError struct {
	StatusCode int
	Body       string
}

StatusError preserves an unexpected non-2xx response from Zotero.

func (StatusError) Error

func (e StatusError) Error() string

type Tag

type Tag struct {
	Tag  string `json:"tag"`
	Type int    `json:"type"`
}

type WebAccess added in v0.5.0

type WebAccess struct {
	User   WebUserAccess             `json:"user"`
	Groups map[string]WebGroupAccess `json:"groups"`
}

WebAccess is the key's permission grants, split into the user's own library and the groups it can reach.

type WebGroupAccess added in v0.5.0

type WebGroupAccess struct {
	Library bool `json:"library"`
	Write   bool `json:"write"`
}

WebGroupAccess is what the key may do in a group library.

type WebKey added in v0.5.0

type WebKey struct {
	UserID   int64     `json:"userID"`
	Username string    `json:"username"`
	Access   WebAccess `json:"access"`
}

WebKey is what the Web API's /keys/current reports about the API key carried on the request: which user it belongs to and what it may reach. It is meaningful only on the Web endpoint; the Local API has no such route and needs no key.

The access grants are the endpoint's own answer to "what can this key do", which is why the Web profile can report capabilities honestly instead of guessing — unlike the local write capability, which no probe can derive.

type WebUserAccess added in v0.5.0

type WebUserAccess struct {
	Library bool `json:"library"`
	Files   bool `json:"files"`
	Notes   bool `json:"notes"`
	Write   bool `json:"write"`
}

WebUserAccess is what the key may do in its owner's personal library.

type WriteAuthorizer added in v0.9.0

type WriteAuthorizer interface {
	AuthorizeWrite(op Operation, lib LibraryRef) error
}

WriteAuthorizer decides whether one write may proceed. It is consulted at the write chokepoint before any mutating request reaches Zotero, so a denial costs no network I/O and fails closed. The returned error, if any, is surfaced to the caller unchanged, so an implementation can distinguish refusal reasons (missing authority, expired, out of scope) with its own sentinels.

func AllowAllWrites added in v0.9.0

func AllowAllWrites() WriteAuthorizer

AllowAllWrites returns a WriteAuthorizer that permits every write. Callers use it when authority is established out of band — an interactive human answered a confirmation prompt — and tests use it to exercise the write mechanism itself.

type WriteFailure added in v0.6.0

type WriteFailure struct {
	Key     string `json:"key"`
	Code    int    `json:"code"`
	Message string `json:"message"`
}

WriteFailure explains why one object in a batch was rejected.

type WriteResult added in v0.6.0

type WriteResult struct {
	Successful map[string]Envelope     `json:"successful"`
	Unchanged  map[string]string       `json:"unchanged"`
	Failed     map[string]WriteFailure `json:"failed"`
}

WriteResult is the outcome of a batch write, mirroring the Web API v3 shape the local endpoint reuses: objects that were written, ones left unchanged because they already matched, and ones that failed, each keyed by the object's index in the request.

func (WriteResult) FirstFailure added in v0.6.0

func (r WriteResult) FirstFailure() WriteFailure

FirstFailure returns a representative failure, or the zero value when none.

func (WriteResult) Ok added in v0.6.0

func (r WriteResult) Ok() bool

Ok reports whether every object in the batch was accepted.

Jump to

Keyboard shortcuts

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