Documentation
¶
Index ¶
- Constants
- Variables
- func BuildOps(batchOps []BatchOp) func(*Work) []Op
- func Decrypt(key, ciphertext []byte) ([]byte, error)
- func EncodeWork(format string, work *Work) ([]byte, error)
- func Encrypt(key, plaintext []byte) ([]byte, error)
- func HasWorkEncoder(format string) bool
- func MigrateDown(ctx context.Context, dsn string) error
- func MigrateDownTo(ctx context.Context, dsn string, version int) error
- func MigrateUp(ctx context.Context, dsn string) error
- func MigrateUpTo(ctx context.Context, dsn string, version int) error
- func NewBatchEditTask(s *Services) *catbird.Task
- func NewRebuildWorkRepresentationTask(s *Services) *catbird.Task
- func ReadWorks(r io.Reader, format string) (iter.Seq2[*ImportWorkInput, error], error)
- func RegisterWorkDecoder(format string, factory func() WorkDecoder)
- func RegisterWorkEncoder(format string, factory func() WorkEncoder)
- func RegisterWorkReader(format string, factory func() WorkReader)
- func RegisterWorkWriter(format string, factory func() WorkWriter)
- func SearchAllOrganizations(ctx context.Context, idx OrganizationIndex, opts *SearchOpts) iter.Seq2[OrganizationHit, error]
- func SearchAllPeople(ctx context.Context, idx PersonIndex, opts *SearchOpts) iter.Seq2[PersonHit, error]
- func SearchAllProjects(ctx context.Context, idx ProjectIndex, opts *SearchOpts) iter.Seq2[ProjectHit, error]
- func SearchAllWorks(ctx context.Context, idx WorkIndex, opts *SearchOpts) iter.Seq2[WorkHit, error]
- func Slugify(name, fallback string) string
- func WorkDecoderFormats() []string
- func WorkDecoderFormatsHelp() string
- func WorkEncoderFormats() []string
- func WorkEncoderFormatsHelp() string
- func WorkReaderFormats() []string
- func WorkReaderFormatsHelp() string
- func WorkWriterFormats() []string
- func WorkWriterFormatsHelp() string
- func WriteWork(w io.Writer, exp WorkWriter, work *Work) error
- func WriteWorks(w io.Writer, exp WorkWriter, works iter.Seq2[*Work, error]) (int, error)
- type AndCondition
- type Append
- type AuthProvider
- type Authorizer
- type BatchEditInput
- type BatchEditOutput
- type BatchError
- type BatchOp
- type BatchResult
- type ClaimWorkUploads
- type Conference
- type CreateOrganization
- type CreatePerson
- type CreateProject
- type CreateWork
- type DeleteOrganization
- type DeletePerson
- type DeleteProject
- type DeleteWork
- type DownloadURLOpts
- type Extent
- type Facet
- type FacetValue
- type FieldEdit
- type FieldStrategy
- type FieldType
- type FieldVal
- type FileStore
- type Grant
- type Hide
- type ID
- type Identifier
- type ImportOrganizationInput
- type ImportOrganizationRel
- type ImportPersonAffiliation
- type ImportPersonInput
- type ImportProjectInput
- type ImportProjectParticipant
- type ImportUserInput
- type ImportWorkContributor
- type ImportWorkInput
- type ImportWorkOrganization
- type ImportWorkProject
- type ImportWorkRel
- type Index
- type Keyword
- type List
- type ListItem
- type ListPublicWorksOpts
- type ListPublicWorksResult
- type ListQuery
- type ListRepresentationOpts
- type ListRepresentationResult
- type LogEntry
- type Note
- type Op
- type OrCondition
- type Organization
- type OrganizationHit
- type OrganizationHits
- type OrganizationIndex
- type OrganizationRel
- type OrganizationSource
- type Person
- func (p *Person) Affiliations() []PersonAffiliation
- func (p *Person) FamilyName() string
- func (p *Person) GivenName() string
- func (p *Person) Identifiers() []Identifier
- func (p *Person) MarshalJSON() ([]byte, error)
- func (p *Person) MiddleName() string
- func (p *Person) Name() string
- func (p *Person) StringField(name string) string
- type PersonAffiliation
- type PersonHit
- type PersonHits
- type PersonIndex
- type PersonSource
- type ProfileField
- type Profiles
- type Project
- type ProjectHit
- type ProjectHits
- type ProjectIndex
- type ProjectParticipant
- type ProjectSource
- type QueryFilter
- type RecordEvent
- type RecordOp
- type RecordState
- type Ref
- type Remove
- type RemoveWorkFiles
- type Repo
- func (r *Repo) AddListItems(ctx context.Context, listID ID, items []ListItem) error
- func (r *Repo) AddWorkCollectionItems(ctx context.Context, collectionID ID, workIDs []ID) error
- func (r *Repo) AddWorkFile(ctx context.Context, wf *WorkFile) error
- func (r *Repo) CleanupUploads(ctx context.Context, olderThan time.Duration) ([]string, error)
- func (r *Repo) Close()
- func (r *Repo) ConfirmUpload(ctx context.Context, id ID, size int, sha256 string) error
- func (r *Repo) CreateList(ctx context.Context, list *List) error
- func (r *Repo) CreateUpload(ctx context.Context, u *Upload) error
- func (r *Repo) CreateUser(ctx context.Context, attrs UserAttrs) (*User, error)
- func (r *Repo) CreateWorkCollection(ctx context.Context, c *WorkCollection) error
- func (r *Repo) DeleteList(ctx context.Context, id ID) error
- func (r *Repo) DeleteUpload(ctx context.Context, id ID) (string, error)
- func (r *Repo) DeleteWorkCollection(ctx context.Context, id ID) error
- func (r *Repo) DeleteWorkFile(ctx context.Context, id ID) error
- func (r *Repo) DeleteWorkRepresentation(ctx context.Context, workID ID, scheme string) error
- func (r *Repo) EachListWork(ctx context.Context, listID ID, statuses []string) iter.Seq2[*Work, error]
- func (r *Repo) EachOrganization(ctx context.Context) iter.Seq2[*Organization, error]
- func (r *Repo) EachOrganizationSince(ctx context.Context, since time.Time) iter.Seq2[*Organization, error]
- func (r *Repo) EachPerson(ctx context.Context) iter.Seq2[*Person, error]
- func (r *Repo) EachPersonSince(ctx context.Context, since time.Time) iter.Seq2[*Person, error]
- func (r *Repo) EachProject(ctx context.Context) iter.Seq2[*Project, error]
- func (r *Repo) EachProjectSince(ctx context.Context, since time.Time) iter.Seq2[*Project, error]
- func (r *Repo) EachWork(ctx context.Context) iter.Seq2[*Work, error]
- func (r *Repo) EachWorkCollectionWork(ctx context.Context, collectionID ID) iter.Seq2[*Work, error]
- func (r *Repo) EachWorkSince(ctx context.Context, since time.Time) iter.Seq2[*Work, error]
- func (r *Repo) GetEarliestWorkTimestamp(ctx context.Context) (time.Time, error)
- func (r *Repo) GetGrants(ctx context.Context, userID ID) ([]Grant, error)
- func (r *Repo) GetList(ctx context.Context, id ID) (*List, error)
- func (r *Repo) GetListBySlug(ctx context.Context, slug string) (*List, error)
- func (r *Repo) GetOrganization(ctx context.Context, id ID) (*Organization, error)
- func (r *Repo) GetPerson(ctx context.Context, id ID) (*Person, error)
- func (r *Repo) GetProject(ctx context.Context, id ID) (*Project, error)
- func (r *Repo) GetUpload(ctx context.Context, id ID) (*Upload, error)
- func (r *Repo) GetUser(ctx context.Context, id ID) (*User, error)
- func (r *Repo) GetUserByIdentifier(ctx context.Context, scheme, val string) (*User, error)
- func (r *Repo) GetUserByUsername(ctx context.Context, username string) (*User, error)
- func (r *Repo) GetUserWithGrants(ctx context.Context, id ID) (*User, []Grant, error)
- func (r *Repo) GetWork(ctx context.Context, id ID) (*Work, error)
- func (r *Repo) GetWorkByIdentifier(ctx context.Context, scheme, val string) (*Work, error)
- func (r *Repo) GetWorkCollection(ctx context.Context, id ID) (*WorkCollection, error)
- func (r *Repo) GetWorkCollectionBySlug(ctx context.Context, slug string) (*WorkCollection, error)
- func (r *Repo) GetWorkHistory(ctx context.Context, workID ID) ([]WorkHistoryEntry, error)
- func (r *Repo) GetWorkRepresentation(ctx context.Context, workID ID, scheme string) (*Representation, error)
- func (r *Repo) GetWorkWithEdits(ctx context.Context, id ID) (*Work, map[string]FieldEdit, error)
- func (r *Repo) GetWorks(ctx context.Context, ids []ID) ([]*Work, error)
- func (r *Repo) HasWorkCollection(ctx context.Context, slug string) (bool, error)
- func (r *Repo) ImportOrganizations(ctx context.Context, source string, ...) (int, error)
- func (r *Repo) ImportPeople(ctx context.Context, source string, seq iter.Seq2[*ImportPersonInput, error]) (int, error)
- func (r *Repo) ImportProjects(ctx context.Context, source string, seq iter.Seq2[*ImportProjectInput, error]) (int, error)
- func (r *Repo) ImportUsers(ctx context.Context, source, authProvider, defaultRole string, ...) (int, error)
- func (r *Repo) ImportWorks(ctx context.Context, source string, seq iter.Seq2[*ImportWorkInput, error]) (int, error)
- func (r *Repo) ListPublicWorks(ctx context.Context, opts ListPublicWorksOpts) (*ListPublicWorksResult, error)
- func (r *Repo) ListWorkCollectionItems(ctx context.Context, collectionID ID, offset, limit int) ([]*Work, int, error)
- func (r *Repo) ListWorkCollections(ctx context.Context) ([]*WorkCollection, error)
- func (r *Repo) ListWorkItems(ctx context.Context, listID ID, statuses []string, offset, limit int) ([]*Work, int, error)
- func (r *Repo) ListWorkRepresentations(ctx context.Context, opts ListRepresentationOpts) (*ListRepresentationResult, error)
- func (r *Repo) Pool() *pgxpool.Pool
- func (r *Repo) RemoveListItems(ctx context.Context, listID ID, items []ListItem) error
- func (r *Repo) RemoveWorkCollectionItems(ctx context.Context, collectionID ID, workIDs []ID) error
- func (r *Repo) SearchUsers(ctx context.Context, query string, limit, offset int) (*UserHits, error)
- func (r *Repo) Update(ctx context.Context, ops []Op, opts *UpdateOpts) (bool, []RevEffect, error)
- func (r *Repo) UpdateList(ctx context.Context, list *List) error
- func (r *Repo) UpdateWorkCollection(ctx context.Context, c *WorkCollection) error
- func (r *Repo) UpsertSource(ctx context.Context, id string) error
- func (r *Repo) UpsertWorkRepresentation(ctx context.Context, workID ID, scheme string, record []byte, ...) error
- func (r *Repo) UserLists(ctx context.Context, userID ID) ([]*List, error)
- type Representation
- type ResolvedRecord
- type RevEffect
- type RevState
- type RoleConfig
- type SearchOpts
- type Services
- func (s *Services) BatchUpdateAndIndex(ctx context.Context, works iter.Seq2[*Work, error], buildOps func(*Work) []Op, ...) (*BatchResult, error)
- func (s *Services) ConfirmUpload(ctx context.Context, id ID, size int, sha256 string) error
- func (s *Services) DeleteUpload(ctx context.Context, id ID) error
- func (s *Services) DeleteWorkFile(ctx context.Context, fileID ID, blobID string) error
- func (s *Services) EachListWork(ctx context.Context, list *List, statuses []string) iter.Seq2[*Work, error]
- func (s *Services) ImportOrganizationsAndIndex(ctx context.Context, source string, ...) (int, error)
- func (s *Services) ImportPeopleAndIndex(ctx context.Context, source, authProvider string, ...) (int, error)
- func (s *Services) ImportProjectsAndIndex(ctx context.Context, source string, seq iter.Seq2[*ImportProjectInput, error]) (int, error)
- func (s *Services) ImportWorksAndIndex(ctx context.Context, source string, seq iter.Seq2[*ImportWorkInput, error]) (int, error)
- func (s *Services) ListWorks(ctx context.Context, list *List, statuses []string, offset, limit int) ([]*Work, int, error)
- func (s *Services) SearchAllWorkRecords(ctx context.Context, opts *SearchOpts) iter.Seq2[*Work, error]
- func (s *Services) SearchPublicWorkRecords(ctx context.Context, opts *SearchOpts) (*WorkRecordHits, error)
- func (s *Services) SearchWorkRecords(ctx context.Context, opts *SearchOpts) (*WorkRecordHits, error)
- func (s *Services) UpdateAndIndex(ctx context.Context, ops []Op, opts *UpdateOpts) (bool, error)
- type Set
- type SetWorkReviewStatus
- type SetWorkStatus
- type TermsFilter
- type Text
- type Title
- type Unset
- type UpdateOpts
- type Upload
- type User
- type UserAttrs
- type UserGrants
- func (ug *UserGrants) Can(capability, scope string, isOwner bool, recordID *ID) bool
- func (ug *UserGrants) CanBatchEditWork() bool
- func (ug *UserGrants) CanCreateWork() bool
- func (ug *UserGrants) CanDeleteWork(work *Work) bool
- func (ug *UserGrants) CanEditWork(work *Work) bool
- func (ug *UserGrants) CanImpersonate() bool
- func (ug *UserGrants) CanManageUsers() bool
- func (ug *UserGrants) CanReviewWork(work *Work) bool
- func (ug *UserGrants) Outranks(role string) bool
- type UserHit
- type UserHits
- type UserIdentifier
- type UserSource
- type Work
- func (w *Work) Abstracts() []Text
- func (w *Work) ArticleNumber() string
- func (w *Work) BookTitle() string
- func (w *Work) Classifications() []Identifier
- func (w *Work) Conference() Conference
- func (w *Work) Contributors() []WorkContributor
- func (w *Work) Edition() string
- func (w *Work) Identifiers() []Identifier
- func (w *Work) Issue() string
- func (w *Work) IssueTitle() string
- func (w *Work) JournalAbbreviation() string
- func (w *Work) JournalTitle() string
- func (w *Work) Keywords() []Keyword
- func (w *Work) LaySummaries() []Text
- func (w *Work) MarshalJSON() ([]byte, error)
- func (w *Work) Notes() []Note
- func (w *Work) Organizations() []ID
- func (w *Work) Pages() Extent
- func (w *Work) PlaceOfPublication() string
- func (w *Work) Projects() []ID
- func (w *Work) PublicationStatus() string
- func (w *Work) PublicationYear() string
- func (w *Work) Publisher() string
- func (w *Work) Rels() []WorkRel
- func (w *Work) ReportNumber() string
- func (w *Work) SeriesTitle() string
- func (w *Work) StringField(name string) string
- func (w *Work) TextField(name string) []Text
- func (w *Work) Titles() []Title
- func (w *Work) TotalPages() string
- func (w *Work) Volume() string
- type WorkCollection
- type WorkContributor
- type WorkDecoder
- type WorkEncoder
- type WorkFile
- type WorkHistoryEntry
- type WorkHit
- type WorkHits
- type WorkIndex
- type WorkReader
- type WorkRecordHit
- type WorkRecordHits
- type WorkRel
- type WorkSourceGetter
- type WorkSourceIter
- type WorkWriter
Constants ¶
const ( OrganizationStatusPublic = "public" OrganizationStatusDeleted = "deleted" )
const ( PersonStatusPublic = "public" PersonStatusDeleted = "deleted" )
const ( ProjectStatusPublic = "public" ProjectStatusDeleted = "deleted" )
const ( RecordTypeOrganization = "organization" RecordTypePerson = "person" RecordTypeProject = "project" RecordTypeWork = "work" )
Record type discriminators for entity types.
const ( UploadStatusPending = "pending" UploadStatusComplete = "complete" )
const ( WorkStatusPrivate = "private" WorkStatusPublic = "public" WorkStatusDeleted = "deleted" )
Work status values.
const ( WorkReviewPending = "pending" WorkReviewInReview = "in_review" WorkReviewReturned = "returned" )
Work review status values. Empty string means not in review.
const ( WorkDeleteWithdrawn = "withdrawn" WorkDeleteRetracted = "retracted" WorkDeleteTakedown = "takedown" )
Work delete kind values (set when status = deleted).
const TaskBatchEdit = "batch_edit"
const TaskRebuildWorkRepresentation = "rebuild_work_representation"
Variables ¶
Functions ¶
func BuildOps ¶
BuildOps converts batch ops into a buildOps callback for BatchUpdateAndIndex. The callback stamps each op with the work's RecordID and returns concrete Ops.
func EncodeWork ¶
EncodeWork is a convenience for encoding a single work.
func Encrypt ¶
Encrypt encrypts plaintext using AES-256-GCM with the given 32-byte key. The returned ciphertext includes a random nonce prefix.
func HasWorkEncoder ¶
HasWorkEncoder reports whether a work encoder is registered for the given format.
func NewBatchEditTask ¶
func RegisterWorkDecoder ¶
func RegisterWorkDecoder(format string, factory func() WorkDecoder)
RegisterWorkDecoder registers a custom work decoder format.
func RegisterWorkEncoder ¶
func RegisterWorkEncoder(format string, factory func() WorkEncoder)
RegisterWorkEncoder registers a custom work encoder format.
func RegisterWorkReader ¶
func RegisterWorkReader(format string, factory func() WorkReader)
RegisterWorkReader registers a custom work reader format.
func RegisterWorkWriter ¶
func RegisterWorkWriter(format string, factory func() WorkWriter)
RegisterWorkWriter registers a custom work writer format.
func SearchAllOrganizations ¶
func SearchAllOrganizations(ctx context.Context, idx OrganizationIndex, opts *SearchOpts) iter.Seq2[OrganizationHit, error]
SearchAllOrganizations returns an iterator over all organization hits matching the query.
func SearchAllPeople ¶
func SearchAllPeople(ctx context.Context, idx PersonIndex, opts *SearchOpts) iter.Seq2[PersonHit, error]
SearchAllPeople returns an iterator over all person hits matching the query.
func SearchAllProjects ¶
func SearchAllProjects(ctx context.Context, idx ProjectIndex, opts *SearchOpts) iter.Seq2[ProjectHit, error]
SearchAllProjects returns an iterator over all project hits matching the query.
func SearchAllWorks ¶
SearchAllWorks returns an iterator over all work hits matching the query, using cursor-based pagination internally.
func Slugify ¶
Slugify returns a URL-safe, lowercase slug derived from name. It decomposes unicode (NFKD), strips non-ASCII, lowercases, and replaces non-alphanumeric runs with a single hyphen. Returns fallback if the result would be empty.
func WorkDecoderFormats ¶
func WorkDecoderFormats() []string
WorkDecoderFormats returns the available decoder format names.
func WorkDecoderFormatsHelp ¶
func WorkDecoderFormatsHelp() string
WorkDecoderFormatsHelp returns a comma-separated list of available decoder formats.
func WorkEncoderFormats ¶
func WorkEncoderFormats() []string
WorkEncoderFormats returns the available encoder format names.
func WorkEncoderFormatsHelp ¶
func WorkEncoderFormatsHelp() string
WorkEncoderFormatsHelp returns a comma-separated list of available encoder formats.
func WorkReaderFormats ¶
func WorkReaderFormats() []string
WorkReaderFormats returns the available reader format names.
func WorkReaderFormatsHelp ¶
func WorkReaderFormatsHelp() string
WorkReaderFormatsHelp returns a comma-separated list of available reader formats.
func WorkWriterFormats ¶
func WorkWriterFormats() []string
WorkWriterFormats returns the available writer format names.
func WorkWriterFormatsHelp ¶
func WorkWriterFormatsHelp() string
WorkWriterFormatsHelp returns a comma-separated list of available writer formats.
Types ¶
type AndCondition ¶
type AndCondition struct {
Or []*OrCondition `json:"or,omitempty"`
Terms *TermsFilter `json:"terms,omitempty"`
}
AndCondition is a single clause in a conjunction. It is either an OR group or a terms filter.
type Append ¶
type Append struct {
RecordType string `json:"record_type"`
RecordID ID `json:"id"`
Field string `json:"field"`
Val any `json:"val"` // single item, e.g. Keyword{Val: "polymer"}
}
Append appends an item to a collection field if not already present. Decomposes to Set — the engine sees a primitive "set" in edits and log.
type AuthProvider ¶
type AuthProvider struct {
Provider string `json:"provider"`
}
AuthProvider is an entry in User.AuthProviders. Stored as a jsonb array to allow additional fields (e.g. added_at) in future.
type Authorizer ¶
type Authorizer struct {
// contains filtered or unexported fields
}
Authorizer checks capabilities against role config and ad-hoc grants.
func NewAuthorizer ¶
func NewAuthorizer(roles map[string]*RoleConfig) *Authorizer
NewAuthorizer creates an Authorizer from parsed role configs.
func (*Authorizer) Priority ¶
func (a *Authorizer) Priority(role string) int
Priority returns the assertion priority for a role name.
func (*Authorizer) Resolve ¶
func (a *Authorizer) Resolve(user *User, adHocGrants []Grant)
Resolve populates user.UserGrants with role config grants, ad-hoc grants, and priority.
func (*Authorizer) Role ¶
func (a *Authorizer) Role(name string) *RoleConfig
Role returns the config for a role name, or nil if unknown.
func (*Authorizer) ValidRole ¶
func (a *Authorizer) ValidRole(role string) bool
ValidRole returns true if the role name exists in the config.
type BatchEditInput ¶
type BatchEditOutput ¶
type BatchError ¶
BatchError records a per-work error during batch update.
type BatchOp ¶
type BatchOp struct {
Op string // "set", "hide", "unset", "append", "remove"
Field string // "volume", "keywords", etc.
Value string // raw value — empty for hide/unset
}
BatchOp is a serialized op template — no RecordID yet. Parsed from CSV, stamped with a work ID during batch iteration.
type BatchResult ¶
type BatchResult struct {
Updated int
Skipped int
Conflicts []ID
Errors []BatchError
}
BatchResult holds the outcome of a batch update.
type ClaimWorkUploads ¶
type ClaimWorkUploads struct {
WorkID ID `json:"id"`
UploadIDs []ID `json:"upload_ids"`
// contains filtered or unexported fields
}
ClaimWorkUploads is an Op that moves completed uploads into a work's files. Participates in the Update transaction so file claiming is atomic with field updates.
type Conference ¶
type Conference struct {
Name string `json:"name,omitempty"`
Organizer string `json:"organizer,omitempty"`
Location string `json:"location,omitempty"`
StartDate time.Time `json:"start_date,omitzero"`
EndDate time.Time `json:"end_date,omitzero"`
}
Conference is a conference associated with a work.
type CreateOrganization ¶
type CreateOrganization struct {
ID ID `json:"id"`
Kind string `json:"kind"`
StartDate *time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
}
CreateOrganization creates a new organization entity.
type CreatePerson ¶
type CreatePerson struct {
ID ID `json:"id"`
}
CreatePerson creates a new person entity.
type CreateProject ¶
type CreateProject struct {
ID ID `json:"id"`
Status string `json:"status"`
StartDate *time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
}
CreateProject creates a new project entity.
type CreateWork ¶
type CreateWork struct {
ID ID `json:"id"`
Kind string `json:"kind"`
Status string `json:"status"` // defaults to WorkStatusPrivate
}
CreateWork creates a new work entity.
type DeleteOrganization ¶
type DeleteOrganization struct {
OrganizationID ID `json:"id"`
}
DeleteOrganization soft-deletes an organization.
type DeletePerson ¶
type DeletePerson struct {
PersonID ID `json:"id"`
}
DeletePerson soft-deletes a person.
type DeleteProject ¶
type DeleteProject struct {
ProjectID ID `json:"id"`
}
DeleteProject soft-deletes a project.
type DeleteWork ¶
DeleteWork soft-deletes a work.
type DownloadURLOpts ¶
type Facet ¶
type Facet struct {
Name string `json:"name"`
Vals []FacetValue `json:"vals"`
}
Facet represents a faceted search result with counts per value.
type FacetValue ¶
FacetValue is a single value in a facet with its document count.
type FieldEdit ¶
type FieldEdit struct {
Val json.RawMessage `json:"val,omitempty"`
Hidden bool `json:"hidden,omitempty"`
Role string `json:"role"`
Rev int64 `json:"rev"`
}
FieldEdit is a human assertion for a single field. Stored in the record's field_edits jsonb column, keyed by field name.
type FieldStrategy ¶
type FieldStrategy int
FieldStrategy determines how a field is resolved from multiple sources.
const ( // FieldExclusive: one asserter wins (highest priority source, or human edit). FieldExclusive FieldStrategy = iota )
type FieldType ¶
type FieldType struct {
Name string
// contains filtered or unexported fields
}
FieldType describes a field value type: its name and the runtime operations (unmarshal, equality, validation). Each type is independent — no scalar/collection hierarchy. A type that holds a slice implements unmarshal/equal/validate directly for the whole slice.
type FieldVal ¶
type FieldVal struct {
// contains filtered or unexported fields
}
FieldVal is a lazy field value that carries its type spec alongside both the raw JSON and typed Go representations. Conversion between the two is on-demand and cached — at most one marshal and one unmarshal per value, regardless of how many times it's accessed.
func (*FieldVal) Raw ¶
func (fv *FieldVal) Raw() (json.RawMessage, error)
Raw returns the JSON representation, marshaling lazily if needed.
type FileStore ¶
type FileStore interface {
Upload(ctx context.Context, r io.Reader) (id string, err error)
Download(ctx context.Context, id string, w io.Writer) error
Delete(ctx context.Context, id string) error
Exists(ctx context.Context, id string) (bool, error)
NewUploadURL(ctx context.Context, ttl time.Duration) (id string, url string, err error)
NewDownloadURL(ctx context.Context, id string, opts DownloadURLOpts) (string, error)
}
FileStore is an object storage backend for file uploads/downloads. The store generates blob IDs — callers treat them as opaque strings.
type Grant ¶
type Grant struct {
Capability string `json:"capability"`
Scope string `json:"scope"`
Match string `json:"match,omitempty"`
MatchID *ID `json:"match_id,omitempty"`
}
Grant is a capability on a scope. Used in both role config and bbl_grants.
Role config (static): Grant{Capability: "edit_private", Scope: "work", Match: "own"} DB grant (ad-hoc): Grant{Capability: "edit_private", Scope: "work", MatchID: &id} Type-wide: Grant{Capability: "edit_private", Scope: "work", Match: "all"} Global: Grant{Capability: "manage_users"}
func ParseGrant ¶
ParseGrant parses a DSL string like "edit_private:work:own".
type Hide ¶
type Hide struct {
RecordType string `json:"record_type"`
RecordID ID `json:"id"`
Field string `json:"field"`
}
Hide asserts that a field intentionally has no value.
type ID ¶
type ID [16]byte
ID is a sortable, UUID-compatible unique identifier. Generated as a ID for sequential B-tree inserts; stored as the PostgreSQL uuid type. Implements pgtype.UUIDScanner and pgtype.UUIDValuer so pgx works directly with the raw bytes, bypassing string-format conversion.
func (ID) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
func (*ID) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler. Accepts both ULID (Crockford base32) and UUID (hyphenated hex) formats, since PostgreSQL's uuid type serializes as hyphenated hex in JSON.
type Identifier ¶
Identifier is a scheme/val pair used for entity identifiers across all entity types.
type ImportOrganizationInput ¶
type ImportOrganizationInput struct {
ID *ID `json:"id,omitempty"`
SourceID string `json:"source_id"`
Kind string `json:"kind"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
SourceUpdatedAt *time.Time `json:"source_updated_at,omitempty"`
SourceRecord []byte `json:"-"`
// contains filtered or unexported fields
}
ImportOrganizationInput carries all data for one organization record arriving from a source.
func (*ImportOrganizationInput) MarshalJSON ¶
func (in *ImportOrganizationInput) MarshalJSON() ([]byte, error)
func (*ImportOrganizationInput) SetField ¶
func (in *ImportOrganizationInput) SetField(name string, val any)
func (*ImportOrganizationInput) UnmarshalJSON ¶
func (in *ImportOrganizationInput) UnmarshalJSON(data []byte) error
type ImportOrganizationRel ¶
type ImportOrganizationRel struct {
Ref Ref `json:"ref"`
Kind string `json:"kind"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
}
ImportOrganizationRel describes a relationship to another organization.
type ImportPersonAffiliation ¶
type ImportPersonAffiliation struct {
Ref Ref `json:"ref"`
}
ImportPersonAffiliation links a person to an organization during import.
type ImportPersonInput ¶
type ImportPersonInput struct {
ID *ID `json:"id,omitempty"`
SourceID string `json:"source_id"`
SourceUpdatedAt *time.Time `json:"source_updated_at,omitempty"`
SourceRecord []byte `json:"-"`
// contains filtered or unexported fields
}
ImportPersonInput carries all data for one person record arriving from a source.
func (*ImportPersonInput) MarshalJSON ¶
func (in *ImportPersonInput) MarshalJSON() ([]byte, error)
func (*ImportPersonInput) SetField ¶
func (in *ImportPersonInput) SetField(name string, val any)
func (*ImportPersonInput) UnmarshalJSON ¶
func (in *ImportPersonInput) UnmarshalJSON(data []byte) error
type ImportProjectInput ¶
type ImportProjectInput struct {
ID *ID `json:"id,omitempty"`
SourceID string `json:"source_id"`
Status string `json:"status,omitempty"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
SourceUpdatedAt *time.Time `json:"source_updated_at,omitempty"`
SourceRecord []byte `json:"-"`
// contains filtered or unexported fields
}
ImportProjectInput carries all data for one project record arriving from a source.
func (*ImportProjectInput) MarshalJSON ¶
func (in *ImportProjectInput) MarshalJSON() ([]byte, error)
func (*ImportProjectInput) SetField ¶
func (in *ImportProjectInput) SetField(name string, val any)
func (*ImportProjectInput) UnmarshalJSON ¶
func (in *ImportProjectInput) UnmarshalJSON(data []byte) error
type ImportProjectParticipant ¶
ImportProjectParticipant links a project to a person during import.
type ImportUserInput ¶
type ImportUserInput struct {
SourceName string `json:"source_name,omitempty"`
SourceID string `json:"source_id"`
ExpiresAt *time.Time `json:"expires_at,omitempty"` // nil = permanent; set for recurring directory sources
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
Role string `json:"role,omitempty"`
Identifiers []UserIdentifier `json:"identifiers,omitempty"`
AuthProvider string `json:"auth_provider,omitempty"` // optional — name of the auth provider this source drives (e.g. "ugent_oidc")
}
ImportUserInput carries all data for one user record arriving from a source. Role is only applied on creation; subsequent imports do not overwrite a role set by an admin.
type ImportWorkContributor ¶
type ImportWorkContributor struct {
PersonRef *Ref `json:"person_ref,omitempty"`
Kind string `json:"kind,omitempty"` // "person" (default) or "organization"
Roles []string `json:"roles,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
}
ImportWorkContributor is a contributor arriving from a source.
type ImportWorkInput ¶
type ImportWorkInput struct {
ID *ID `json:"id,omitempty"`
SourceID string `json:"source_id"`
Kind string `json:"kind"`
Status string `json:"status,omitempty"`
SourceUpdatedAt *time.Time `json:"source_updated_at,omitempty"`
SourceRecord []byte `json:"-"`
// contains filtered or unexported fields
}
ImportWorkInput carries all data for one work record arriving from a source. IMPORTANT: when adding struct fields, also add the JSON key to importWorkStructuralKeys — otherwise UnmarshalJSON will route it through the domain field registry.
func DecodeWork ¶
func DecodeWork(format string, data []byte) (*ImportWorkInput, error)
DecodeWork is a convenience for decoding a single work.
func (*ImportWorkInput) MarshalJSON ¶
func (in *ImportWorkInput) MarshalJSON() ([]byte, error)
func (*ImportWorkInput) SetField ¶
func (in *ImportWorkInput) SetField(name string, val any)
func (*ImportWorkInput) UnmarshalJSON ¶
func (in *ImportWorkInput) UnmarshalJSON(data []byte) error
type ImportWorkOrganization ¶
type ImportWorkOrganization struct {
Ref Ref `json:"ref"`
}
ImportWorkOrganization links a work to an organization during import.
type ImportWorkProject ¶
type ImportWorkProject struct {
Ref Ref `json:"ref"`
}
ImportWorkProject links a work to a project during import.
type ImportWorkRel ¶
ImportWorkRel links a work to a related work during import.
type Index ¶
type Index interface {
Works() WorkIndex
People() PersonIndex
Projects() ProjectIndex
Organizations() OrganizationIndex
}
Index provides access to per-entity search indexes.
type Keyword ¶
type Keyword struct {
Val string `json:"val"`
}
Keyword is a keyword/subject term on a work.
type List ¶
type List struct {
ID ID `json:"id"`
Version int `json:"version"`
Slug string `json:"slug"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
RecordType string `json:"record_type,omitempty"`
Query *ListQuery `json:"query,omitempty"`
AccessLevel string `json:"access_level"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedByID ID `json:"created_by_id"`
}
type ListPublicWorksOpts ¶
type ListPublicWorksOpts struct {
From time.Time
Until time.Time
Cursor string // opaque, from previous result
Limit int
}
WorkCursor is a keyset pagination cursor for ListPublicWorks. ListPublicWorksOpts holds parameters for ListPublicWorks.
type ListPublicWorksResult ¶
ListPublicWorksResult holds the result of ListPublicWorks.
type ListQuery ¶
type ListQuery struct {
Query string `json:"query,omitempty"`
Filter *QueryFilter `json:"filter,omitempty"`
}
type ListRepresentationOpts ¶
type ListRepresentationResult ¶
type ListRepresentationResult struct {
Representations []*Representation
Cursor string
}
type LogEntry ¶
type LogEntry struct {
Op string `json:"op"` // "create", "delete", "set", "hide", "unset"
Data json.RawMessage `json:"data,omitempty"` // op-specific: {"field":"volume","prev_val":...}
}
LogEntry is an entry written to bbl_log. Produced by all ops — field edits, state changes, lifecycle. Record identity comes from the ResolvedRecord, not the entry.
type Op ¶
type Op interface {
// contains filtered or unexported methods
}
Op is the unit of change submitted to Update. plan() runs during the I/O phase — it may query the DB to discover affected records, then returns pure single-record mutations.
func DecodeUpdate ¶
DecodeUpdate decodes a JSON-encoded update into a concrete update type.
Wire format:
{"set": "work:volume", "id": "01J...", "val": "42"}
{"hide": "work:volume", "id": "01J..."}
{"unset": "work:volume", "id": "01J..."}
{"append": "work:keywords", "id": "01J...", "val": {"val": "polymer"}}
{"remove": "work:keywords", "id": "01J...", "val": {"val": "draft"}}
{"create": "work", "id": "01J...", "kind": "journal_article"}
{"delete": "work", "id": "01J..."}
type OrCondition ¶
type OrCondition struct {
And []*AndCondition `json:"and,omitempty"`
Terms *TermsFilter `json:"terms,omitempty"`
}
OrCondition is a single clause in a disjunction. It is either an AND group or a terms filter.
type Organization ¶
type Organization struct {
ID ID `json:"id"`
RevID int64 `json:"rev_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedByID *ID `json:"created_by_id,omitempty"`
UpdatedByID *ID `json:"updated_by_id,omitempty"`
Kind string `json:"kind"`
Status string `json:"status"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
DeletedByID *ID `json:"deleted_by_id,omitempty"`
// contains filtered or unexported fields
}
func (*Organization) Identifiers ¶
func (o *Organization) Identifiers() []Identifier
func (*Organization) MarshalJSON ¶
func (o *Organization) MarshalJSON() ([]byte, error)
func (*Organization) Names ¶
func (o *Organization) Names() []Text
func (*Organization) Rels ¶
func (o *Organization) Rels() []OrganizationRel
type OrganizationHit ¶
type OrganizationHit struct {
ID ID `json:"id"`
Kind string `json:"kind"`
Name string `json:"name"`
}
OrganizationHit is a search result for an organization, containing display fields.
type OrganizationHits ¶
type OrganizationHits struct {
Hits []OrganizationHit `json:"hits"`
Total int `json:"total"`
Cursor string `json:"cursor,omitempty"`
Facets []Facet `json:"facets,omitempty"`
}
OrganizationHits is the result of an organization search query.
type OrganizationIndex ¶
type OrganizationIndex interface {
Add(ctx context.Context, org *Organization) error
Delete(ctx context.Context, id ID) error
DeleteAll(ctx context.Context) error
Search(ctx context.Context, opts *SearchOpts) (*OrganizationHits, error)
Reindex(ctx context.Context, all iter.Seq2[*Organization, error], changed func(since time.Time) iter.Seq2[*Organization, error]) error
}
OrganizationIndex is the search index for organizations.
type OrganizationRel ¶
type OrganizationRel struct {
RelOrganizationID ID `json:"rel_organization_id"`
Kind string `json:"kind"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
}
OrganizationRel links two organizations with a typed, optionally temporal relationship.
type OrganizationSource ¶
type OrganizationSource interface {
Iter(ctx context.Context) (iter.Seq2[*ImportOrganizationInput, error], error)
}
OrganizationSource is the interface implemented by organization import sources.
type Person ¶
type Person struct {
ID ID `json:"id"`
RevID int64 `json:"rev_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedByID *ID `json:"created_by_id,omitempty"`
UpdatedByID *ID `json:"updated_by_id,omitempty"`
Status string `json:"status"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
DeletedByID *ID `json:"deleted_by_id,omitempty"`
// contains filtered or unexported fields
}
func (*Person) Affiliations ¶
func (p *Person) Affiliations() []PersonAffiliation
func (*Person) FamilyName ¶
func (*Person) Identifiers ¶
func (p *Person) Identifiers() []Identifier
func (*Person) MarshalJSON ¶
func (*Person) MiddleName ¶
func (*Person) StringField ¶
type PersonAffiliation ¶
type PersonAffiliation struct {
OrganizationID ID `json:"organization_id"`
}
PersonAffiliation is an affiliation in the resolved fields.
type PersonHits ¶
type PersonHits struct {
Hits []PersonHit `json:"hits"`
Total int `json:"total"`
Cursor string `json:"cursor,omitempty"`
Facets []Facet `json:"facets,omitempty"`
}
PersonHits is the result of a person search query.
type PersonIndex ¶
type PersonIndex interface {
Add(ctx context.Context, person *Person) error
Delete(ctx context.Context, id ID) error
DeleteAll(ctx context.Context) error
Search(ctx context.Context, opts *SearchOpts) (*PersonHits, error)
Reindex(ctx context.Context, all iter.Seq2[*Person, error], changed func(since time.Time) iter.Seq2[*Person, error]) error
}
PersonIndex is the search index for people.
type PersonSource ¶
type PersonSource interface {
Iter(ctx context.Context) (iter.Seq2[*ImportPersonInput, error], error)
}
PersonSource is the interface implemented by person import sources.
type ProfileField ¶
type ProfileField struct {
Name string
Type *FieldType
Required string // "", "always", "public"
Schemes []string // for identifier, classification
}
ProfileField is a field definition with profile-level constraints.
func (ProfileField) IsRequired ¶
func (f ProfileField) IsRequired() bool
IsRequired reports whether the field has any required constraint.
type Profiles ¶
type Profiles struct {
Work map[string][]ProfileField
Organization map[string][]ProfileField
Person []ProfileField
Project []ProfileField
// contains filtered or unexported fields
}
Profiles holds resolved profiles for all entity types, loaded once at startup.
func LoadProfiles ¶
LoadProfiles reads the YAML profile config and validates field names against the field registry. Returns an error if any field name is unknown or any section is missing.
func (*Profiles) FieldDefs ¶
func (p *Profiles) FieldDefs(recordType, kind string) []ProfileField
FieldDefs returns the field definitions for a record type and kind. Returns nil if the record type or kind is unknown.
func (*Profiles) OrganizationKinds ¶
OrganizationKinds returns organization kind names in definition order.
type Project ¶
type Project struct {
ID ID `json:"id"`
RevID int64 `json:"rev_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedByID *ID `json:"created_by_id,omitempty"`
UpdatedByID *ID `json:"updated_by_id,omitempty"`
Status string `json:"status"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
DeletedByID *ID `json:"deleted_by_id,omitempty"`
// contains filtered or unexported fields
}
func (*Project) Descriptions ¶
func (*Project) Identifiers ¶
func (p *Project) Identifiers() []Identifier
func (*Project) MarshalJSON ¶
func (*Project) Participants ¶
func (p *Project) Participants() []ProjectParticipant
type ProjectHit ¶
type ProjectHit struct {
ID ID `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
}
ProjectHit is a search result for a project, containing display fields.
type ProjectHits ¶
type ProjectHits struct {
Hits []ProjectHit `json:"hits"`
Total int `json:"total"`
Cursor string `json:"cursor,omitempty"`
Facets []Facet `json:"facets,omitempty"`
}
ProjectHits is the result of a project search query.
func SearchPublicProjects ¶
func SearchPublicProjects(ctx context.Context, idx ProjectIndex, opts *SearchOpts) (*ProjectHits, error)
SearchPublicProjects searches for projects with status=public.
type ProjectIndex ¶
type ProjectIndex interface {
Add(ctx context.Context, project *Project) error
Delete(ctx context.Context, id ID) error
DeleteAll(ctx context.Context) error
Search(ctx context.Context, opts *SearchOpts) (*ProjectHits, error)
Reindex(ctx context.Context, all iter.Seq2[*Project, error], changed func(since time.Time) iter.Seq2[*Project, error]) error
}
ProjectIndex is the search index for projects.
type ProjectParticipant ¶
type ProjectParticipant struct {
PersonID ID `json:"person_id"`
Role string `json:"role,omitempty"`
}
ProjectParticipant is a participant in the resolved fields.
type ProjectSource ¶
type ProjectSource interface {
Iter(ctx context.Context) (iter.Seq2[*ImportProjectInput, error], error)
}
ProjectSource is the interface implemented by project import sources.
type QueryFilter ¶
type QueryFilter struct {
And []*AndCondition `json:"and"`
}
QueryFilter represents a conjunction of conditions for search filtering.
Filter expressions select documents by field values using a simple query language:
field=value exact match field=val1|val2 match any of the values field=a field=b AND (both must match) field=a or field=b OR (either must match) (field=a or field=b) grouping with parentheses
Examples:
status=public status=public kind=book|article kind=book or kind=conference_paper status=public (kind=book or kind=article) (status=public and kind=book) or (status=private and kind=article)
Use ParseQueryFilter to parse a filter expression string into a QueryFilter.
func ParseQueryFilter ¶
func ParseQueryFilter(str string) (*QueryFilter, error)
ParseQueryFilter parses a search filter expression like "status=public kind=book|article".
func (*QueryFilter) HasTerm ¶
func (qf *QueryFilter) HasTerm(field, term string) bool
HasTerm returns true if the filter contains a terms filter for the given field and term.
type RecordEvent ¶
type RecordEvent struct {
RecordID ID `json:"record_id"`
RevID int64 `json:"rev_id"`
Status string `json:"status"`
MadePublicAt *time.Time `json:"made_public_at,omitempty"`
}
RecordEvent is the payload published to record.{type}.changed topics.
type RecordOp ¶
type RecordOp struct {
RecordID ID
RecordType string
IsCreate bool // if true, gather skips loading (record doesn't exist yet)
Apply func(rec *RecordState, user *User) ([]LogEntry, error)
// Write is optional lifecycle SQL (create/delete). If non-nil, called during
// the write phase. Field ops leave this nil.
Write lifecycleWriteFn
}
RecordOp is a pure single-record mutation produced by Op.plan().
type RecordState ¶
type RecordState struct {
RecordType string
RecordID ID
RevID int64
Kind string
Status string
MadePublicAt *time.Time
// All source assertions (scalar + relational fields JSONB per source).
Sources []sourceAssertion
// Human field edits (scalar + relational field values).
FieldEdits map[string]FieldEdit
// Current resolved fields JSONB (for noop detection in prune).
Fields map[string]json.RawMessage
// Source priorities (for resolving winning asserter in noop detection).
Priorities map[string]int
// Work files (not assertions — loaded and written directly).
Files []WorkFile
// Lifecycle flags (set by apply).
IsNew bool
IsDeleted bool
// contains filtered or unexported fields
}
RecordState is one record's complete write-path state. Groups data by asserter for resolution.
type Ref ¶
type Ref struct {
ID *ID `json:"id,omitempty"`
SourceID string `json:"source_id,omitempty"`
Identifier *Identifier `json:"identifier,omitempty"`
}
Ref identifies an entity for cross-entity linking during import. Exactly one field should be set.
type Remove ¶
type Remove struct {
RecordType string `json:"record_type"`
RecordID ID `json:"id"`
Field string `json:"field"`
Val any `json:"val"` // item to match, e.g. Keyword{Val: "draft"}
}
Remove removes matching item(s) from a collection field. Decomposes to Set or Hide — the engine sees a primitive "set" or "hide".
type RemoveWorkFiles ¶
type RemoveWorkFiles struct {
WorkID ID `json:"id"`
FileIDs []ID `json:"file_ids"`
// contains filtered or unexported fields
}
RemoveWorkFiles is an Op that removes files from a work on form submit.
func (*RemoveWorkFiles) RemovedBlobIDs ¶
func (m *RemoveWorkFiles) RemovedBlobIDs() []string
type Repo ¶
type Repo struct {
Profiles *Profiles // nil = no profile validation
// contains filtered or unexported fields
}
Repo is the single repository backed by PostgreSQL. No interface is defined — PostgreSQL features are used pervasively and there are no plans to support another database.
func (*Repo) AddListItems ¶
func (*Repo) AddWorkCollectionItems ¶
func (*Repo) CleanupUploads ¶
CleanupUploads deletes stale uploads older than the given duration. Returns the blob_ids of deleted rows so the caller can clean the store.
func (*Repo) ConfirmUpload ¶
func (*Repo) CreateUser ¶
CreateUser inserts a new user. Intended for manual admin creation. Returns ErrConflict if the username is already taken.
func (*Repo) CreateWorkCollection ¶
func (r *Repo) CreateWorkCollection(ctx context.Context, c *WorkCollection) error
func (*Repo) DeleteWorkCollection ¶
func (*Repo) DeleteWorkRepresentation ¶
func (*Repo) EachListWork ¶
func (r *Repo) EachListWork(ctx context.Context, listID ID, statuses []string) iter.Seq2[*Work, error]
EachListWork iterates over all works in an items-based list, filtered by statuses.
func (*Repo) EachOrganization ¶
EachOrganization returns an iterator over all organizations, ordered by id.
func (*Repo) EachOrganizationSince ¶
func (r *Repo) EachOrganizationSince(ctx context.Context, since time.Time) iter.Seq2[*Organization, error]
EachOrganizationSince returns an iterator over organizations updated since the given time, ordered by id.
func (*Repo) EachPerson ¶
EachPerson returns an iterator over all people, ordered by id.
func (*Repo) EachPersonSince ¶
EachPersonSince returns an iterator over people updated since the given time, ordered by id.
func (*Repo) EachProject ¶
EachProject returns an iterator over all projects, ordered by id.
func (*Repo) EachProjectSince ¶
EachProjectSince returns an iterator over projects updated since the given time, ordered by id.
func (*Repo) EachWorkCollectionWork ¶
func (*Repo) EachWorkSince ¶
EachWorkSince returns an iterator over works updated since the given time, ordered by id.
func (*Repo) GetEarliestWorkTimestamp ¶
GetEarliestWorkTimestamp returns the earliest updated_at of any public work.
func (*Repo) GetGrants ¶
GetGrants loads all active (non-revoked, non-expired) grants for a user. Used once per request to preload grants for the access check.
func (*Repo) GetListBySlug ¶
func (*Repo) GetOrganization ¶
func (*Repo) GetUserByIdentifier ¶
GetUserByIdentifier looks up a user by auth claim (scheme, val). This is the primary login lookup path. Returns ErrNotFound if no match.
func (*Repo) GetUserByUsername ¶
GetUserByUsername looks up a user by username. Returns ErrNotFound if no match.
func (*Repo) GetUserWithGrants ¶
GetUserWithGrants loads a user and their active grants in one round-trip.
func (*Repo) GetWork ¶
GetWork fetches a work by primary key. The returned Work includes its cache (inlined display data). Returns ErrNotFound if no row exists.
func (*Repo) GetWorkByIdentifier ¶
GetWorkByIdentifier fetches the work that owns the given scheme:val identifier. Returns ErrNotFound if no match.
func (*Repo) GetWorkCollection ¶
func (*Repo) GetWorkCollectionBySlug ¶
func (*Repo) GetWorkHistory ¶
GetWorkHistory returns the history for a work, grouped by field. For each field it shows:
- The current value (from human edit or source)
- If human-edited: the source value as a history entry
- Previous human edits from bbl_log
func (*Repo) GetWorkRepresentation ¶
func (*Repo) GetWorkWithEdits ¶
GetWorkWithEdits fetches a work and its human edits in a single query.
func (*Repo) GetWorks ¶
GetWorks fetches multiple works by ID, preserving the input order. Missing IDs are silently skipped.
func (*Repo) HasWorkCollection ¶
func (*Repo) ImportOrganizations ¶
func (*Repo) ImportPeople ¶
func (*Repo) ImportProjects ¶
func (*Repo) ImportUsers ¶
func (r *Repo) ImportUsers(ctx context.Context, source, authProvider, defaultRole string, seq iter.Seq2[*ImportUserInput, error]) (int, error)
ImportUsers runs a full sweep from seq, importing all records in batches. source is the source name stored on each record; authProvider is optional and, if non-empty, is attached to the user's auth providers on creation. defaultRole is assigned on user creation when the input record has no role set. Returns the number of successfully imported users and the first fatal error.
func (*Repo) ImportWorks ¶
func (r *Repo) ImportWorks(ctx context.Context, source string, seq iter.Seq2[*ImportWorkInput, error]) (int, error)
ImportWorks runs a full sweep from seq, importing all records in batches. Re-import = delete all of this source's assertions for the entity + insert new ones. Returns the number of records that resulted in a create or update.
func (*Repo) ListPublicWorks ¶
func (r *Repo) ListPublicWorks(ctx context.Context, opts ListPublicWorksOpts) (*ListPublicWorksResult, error)
ListPublicWorks returns a page of public works ordered by (updated_at, id) for keyset pagination.
func (*Repo) ListWorkCollectionItems ¶
func (*Repo) ListWorkCollections ¶
func (r *Repo) ListWorkCollections(ctx context.Context) ([]*WorkCollection, error)
func (*Repo) ListWorkItems ¶
func (r *Repo) ListWorkItems(ctx context.Context, listID ID, statuses []string, offset, limit int) ([]*Work, int, error)
ListWorkItems returns a paginated slice of works from an items-based list, filtered by the given statuses. Returns the works and visible total count.
func (*Repo) ListWorkRepresentations ¶
func (r *Repo) ListWorkRepresentations(ctx context.Context, opts ListRepresentationOpts) (*ListRepresentationResult, error)
func (*Repo) RemoveListItems ¶
func (*Repo) RemoveWorkCollectionItems ¶
func (*Repo) SearchUsers ¶
SearchUsers performs a simple text search over users by name or username. Returns paginated results ordered by name.
func (*Repo) Update ¶
Update executes a batch of human edits atomically.
Pipeline:
- Plan+gather: run each Op's plan() to produce RecordOps, then bulk-load all affected records (record rows, source assertions) in one batch per record type.
- Apply: run each RecordOp's pure Apply func against the in-memory RecordState. All edits (scalar and relational) store the value in Edits.
- Resolve: compute the winning value per field (source priority with human override).
- Prune: mark records where nothing changed as noop.
- Write: persist everything — lifecycle SQL, log entries, edits JSONB, and resolved fields.
func (*Repo) UpdateWorkCollection ¶
func (r *Repo) UpdateWorkCollection(ctx context.Context, c *WorkCollection) error
func (*Repo) UpsertSource ¶
UpsertSource registers a source in bbl_sources if it does not already exist. All tables that reference bbl_sources require the source to be present first.
func (*Repo) UpsertWorkRepresentation ¶
type Representation ¶
type ResolvedRecord ¶
type ResolvedRecord struct {
*RecordState
// Resolved fields (output of resolveFields).
ResolvedFields map[string]json.RawMessage
// Log entries collected from apply.
LogEntries []LogEntry
// Set by prune — if true, nothing changed for this record.
Noop bool
// contains filtered or unexported fields
}
ResolvedRecord is one record's post-resolution state.
type RevState ¶
type RevState struct {
Records map[ID]*RecordState
Priorities map[string]int
}
RevState is the pre-change state for all records in a revision.
type RoleConfig ¶
RoleConfig defines a configured role.
type SearchOpts ¶
type SearchOpts struct {
Query string `json:"query,omitempty"`
Filter *QueryFilter `json:"filter,omitempty"`
Facets []string `json:"facets,omitempty"`
Size int `json:"size"`
Cursor string `json:"cursor,omitempty"` // base64-encoded search_after; mutually exclusive with Offset
Offset int `json:"offset,omitempty"` // for UI pagination; hard max enforced by implementation
}
SearchOpts controls a search query.
func (*SearchOpts) WithFilter ¶
func (o *SearchOpts) WithFilter(field string, terms ...string) *SearchOpts
WithFilter adds a terms filter to the search opts and returns them for chaining.
type Services ¶
type Services struct {
Repo *Repo
Index Index
FileStore FileStore
Catbird *catbird.Client
Authorizer *Authorizer
UserSources map[string]UserSource
WorkIterSources map[string]WorkSourceIter
WorkGetSources map[string]WorkSourceGetter
WorkEncoders map[string]WorkEncoder
}
Services bundles the core runtime dependencies.
func (*Services) BatchUpdateAndIndex ¶
func (s *Services) BatchUpdateAndIndex(ctx context.Context, works iter.Seq2[*Work, error], buildOps func(*Work) []Op, user *User) (*BatchResult, error)
BatchUpdateAndIndex applies ops to each work from the iterator, one at a time. Each work is updated independently with OCC via its RevID.
func (*Services) ConfirmUpload ¶
ConfirmUpload verifies the blob exists in the store, then updates the staging row.
func (*Services) DeleteUpload ¶
DeleteUpload removes the staging row and deletes the blob from the store (best-effort).
func (*Services) DeleteWorkFile ¶
DeleteWorkFile removes the DB row, then deletes the blob from the store (best-effort).
func (*Services) EachListWork ¶
func (s *Services) EachListWork(ctx context.Context, list *List, statuses []string) iter.Seq2[*Work, error]
EachListWork iterates over all works in a list, filtered by statuses. Dispatches to the repo for items-based lists or the search index for query-based lists.
func (*Services) ImportOrganizationsAndIndex ¶
func (s *Services) ImportOrganizationsAndIndex(ctx context.Context, source string, seq iter.Seq2[*ImportOrganizationInput, error]) (int, error)
ImportOrganizationsAndIndex imports organizations and best-effort indexes changed records.
func (*Services) ImportPeopleAndIndex ¶
func (s *Services) ImportPeopleAndIndex(ctx context.Context, source, authProvider string, seq iter.Seq2[*ImportPersonInput, error]) (int, error)
ImportPeopleAndIndex imports people and best-effort indexes changed records.
func (*Services) ImportProjectsAndIndex ¶
func (s *Services) ImportProjectsAndIndex(ctx context.Context, source string, seq iter.Seq2[*ImportProjectInput, error]) (int, error)
ImportProjectsAndIndex imports projects and best-effort indexes changed records.
func (*Services) ImportWorksAndIndex ¶
func (s *Services) ImportWorksAndIndex(ctx context.Context, source string, seq iter.Seq2[*ImportWorkInput, error]) (int, error)
ImportWorksAndIndex imports works and best-effort indexes changed records. Uses a timestamp taken before the import to re-fetch changed works from the DB, because the in-memory Work during import doesn't have the cache populated.
func (*Services) ListWorks ¶
func (s *Services) ListWorks(ctx context.Context, list *List, statuses []string, offset, limit int) ([]*Work, int, error)
ListWorks returns a paginated slice of works from a list, filtered by statuses. Dispatches to the repo for items-based lists or the search index for query-based lists.
func (*Services) SearchAllWorkRecords ¶
func (s *Services) SearchAllWorkRecords(ctx context.Context, opts *SearchOpts) iter.Seq2[*Work, error]
SearchAllWorkRecords returns an iterator over full work records matching the query, using cursor-based pagination internally. Each page of index hits is batch-fetched from the repo.
func (*Services) SearchPublicWorkRecords ¶
func (s *Services) SearchPublicWorkRecords(ctx context.Context, opts *SearchOpts) (*WorkRecordHits, error)
SearchPublicWorkRecords searches the index for public works and fetches full records.
func (*Services) SearchWorkRecords ¶
func (s *Services) SearchWorkRecords(ctx context.Context, opts *SearchOpts) (*WorkRecordHits, error)
SearchWorkRecords searches the index and fetches full work records from the repo.
func (*Services) UpdateAndIndex ¶
UpdateAndIndex writes a revision to the DB and best-effort indexes affected records.
type Set ¶
type Set struct {
RecordType string `json:"record_type"`
RecordID ID `json:"id"`
Field string `json:"field"`
Val any `json:"val"`
}
Set asserts a value for a field.
type SetWorkReviewStatus ¶
type SetWorkReviewStatus struct {
WorkID ID `json:"id"`
ReviewStatus *string `json:"review_status"`
}
SetWorkReviewStatus changes a work's review status.
type SetWorkStatus ¶
SetWorkStatus changes a work's status.
type TermsFilter ¶
TermsFilter matches documents where the field contains any of the given terms.
type Title ¶
Title is a language-tagged title (for projects, works, etc.). Separate from Text to allow future expansion (e.g. kind: translated_title).
type Unset ¶
type Unset struct {
RecordType string `json:"record_type"`
RecordID ID `json:"id"`
Field string `json:"field"`
}
Unset withdraws the human edit for a field.
type UpdateOpts ¶
type UpdateOpts struct {
User *User // who is making the change (nil for system jobs)
Rev int64 // if non-zero, reject if any affected record has rev_id > this
}
UpdateOpts configures optional behavior for Update.
type UserGrants ¶
type UserGrants struct {
// contains filtered or unexported fields
}
UserGrants holds the resolved authorization state for a user in a request. Usable from both handlers and templates.
func (*UserGrants) Can ¶
func (ug *UserGrants) Can(capability, scope string, isOwner bool, recordID *ID) bool
Check checks if the user has a capability on a scope.
func (*UserGrants) CanBatchEditWork ¶
func (ug *UserGrants) CanBatchEditWork() bool
func (*UserGrants) CanCreateWork ¶
func (ug *UserGrants) CanCreateWork() bool
func (*UserGrants) CanDeleteWork ¶
func (ug *UserGrants) CanDeleteWork(work *Work) bool
func (*UserGrants) CanEditWork ¶
func (ug *UserGrants) CanEditWork(work *Work) bool
func (*UserGrants) CanImpersonate ¶
func (ug *UserGrants) CanImpersonate() bool
func (*UserGrants) CanManageUsers ¶
func (ug *UserGrants) CanManageUsers() bool
func (*UserGrants) CanReviewWork ¶
func (ug *UserGrants) CanReviewWork(work *Work) bool
func (*UserGrants) Outranks ¶
func (ug *UserGrants) Outranks(role string) bool
Outranks returns true if this user's priority is >= the given role's priority. Used for the field assertion lock: a user can only overwrite a field asserted by an equal-or-lower priority role.
type UserIdentifier ¶
UserIdentifier is an auth claim identifier (e.g. scheme="ugent_id", val="abc123").
type UserSource ¶
UserSource is implemented by packages that stream user records from an external directory (LDAP, SCIM, CSV, …). Iter connects eagerly and returns a fatal setup error (e.g. connection refused, bad credentials) as the second return value. Per-entry errors are yielded inline so the caller can skip individual bad records without aborting the sweep.
type Work ¶
type Work struct {
ID ID `json:"id"`
RevID int64 `json:"rev_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedByID *ID `json:"created_by_id,omitempty"`
UpdatedByID *ID `json:"updated_by_id,omitempty"`
Kind string `json:"kind"`
Status string `json:"status"`
ReviewStatus string `json:"review_status,omitempty"` // empty = not in review
DeleteKind string `json:"delete_kind,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
DeletedByID *ID `json:"deleted_by_id,omitempty"`
MadePublicAt *time.Time `json:"made_public_at,omitempty"`
Files []WorkFile `json:"files,omitempty"`
// contains filtered or unexported fields
}
func (*Work) Classifications ¶
func (w *Work) Classifications() []Identifier
func (*Work) Conference ¶
func (w *Work) Conference() Conference
func (*Work) Contributors ¶
func (w *Work) Contributors() []WorkContributor
func (*Work) Identifiers ¶
func (w *Work) Identifiers() []Identifier
func (*Work) IssueTitle ¶
func (*Work) JournalAbbreviation ¶
func (*Work) JournalTitle ¶
func (*Work) LaySummaries ¶
func (*Work) MarshalJSON ¶
func (*Work) Organizations ¶
func (*Work) PlaceOfPublication ¶
func (*Work) PublicationStatus ¶
func (*Work) PublicationYear ¶
func (*Work) ReportNumber ¶
func (*Work) SeriesTitle ¶
func (*Work) StringField ¶
Dynamic access for extensible field types.
func (*Work) TotalPages ¶
type WorkCollection ¶
type WorkCollection struct {
ID ID `json:"id"`
Version int `json:"version"`
Slug string `json:"slug"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Query *ListQuery `json:"query,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (*WorkCollection) IsQuery ¶
func (c *WorkCollection) IsQuery() bool
type WorkContributor ¶
type WorkContributor struct {
Kind string `json:"kind,omitempty"` // "person" (default) or "organization"
PersonID *ID `json:"person_id,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
Roles []string `json:"roles,omitempty"`
}
WorkContributor is a contributor in the resolved fields.
type WorkDecoder ¶
type WorkDecoder interface {
Decode(data []byte) (*ImportWorkInput, error)
}
WorkDecoder decodes a single work import record from bytes.
func NewWorkDecoder ¶
func NewWorkDecoder(format string) (WorkDecoder, error)
NewWorkDecoder creates a new decoder for the given format.
type WorkEncoder ¶
WorkEncoder encodes a single work into a self-contained document.
func NewWorkEncoder ¶
func NewWorkEncoder(format string) (WorkEncoder, error)
NewWorkEncoder creates a new encoder for the given format.
type WorkFile ¶
type WorkFile struct {
ID ID `json:"id"`
WorkID ID `json:"work_id,omitempty"`
Position int `json:"position,omitempty"`
BlobID string `json:"blob_id"`
Name string `json:"name"`
ContentType string `json:"content_type"`
Size int `json:"size"`
SHA256 string `json:"sha256,omitempty"`
AccessLevel string `json:"access_level"`
EmbargoUntil *time.Time `json:"embargo_until,omitempty"`
EmbargoAccessLevel string `json:"embargo_access_level,omitempty"`
EmbargoLiftedAt *time.Time `json:"embargo_lifted_at,omitempty"`
}
type WorkHistoryEntry ¶
type WorkHistoryEntry struct {
RevID int64
RevAt time.Time
Field string
Op string // 'set', 'hide', 'unset'
Val json.RawMessage
By string // user name or source name
IsHistory bool // true = previous value, false = current
}
WorkHistoryEntry is one entry in the history view for a work.
type WorkHit ¶
type WorkHit struct {
ID ID `json:"id"`
Kind string `json:"kind"`
Status string `json:"status"`
Title string `json:"title"`
}
WorkHit is a search result for a work, containing display fields.
type WorkHits ¶
type WorkHits struct {
Hits []WorkHit `json:"hits"`
Total int `json:"total"`
Cursor string `json:"cursor,omitempty"`
Facets []Facet `json:"facets,omitempty"`
}
WorkHits is the result of a work search query.
func SearchPublicWorks ¶
SearchPublicWorks searches for works with status=public.
type WorkIndex ¶
type WorkIndex interface {
Add(ctx context.Context, work *Work) error
Delete(ctx context.Context, id ID) error
DeleteAll(ctx context.Context) error
Search(ctx context.Context, opts *SearchOpts) (*WorkHits, error)
Reindex(ctx context.Context, all iter.Seq2[*Work, error], changed func(since time.Time) iter.Seq2[*Work, error]) error
}
WorkIndex is the search index for works.
type WorkReader ¶
WorkReader reads a stream of work import records from a reader.
func NewWorkReader ¶
func NewWorkReader(format string) (WorkReader, error)
NewWorkReader creates a new reader for the given format.
type WorkRecordHit ¶
type WorkRecordHit struct {
Work *Work `json:"work"`
}
WorkRecordHit is a search result containing the full work record.
type WorkRecordHits ¶
type WorkRecordHits struct {
Hits []WorkRecordHit `json:"hits"`
Total int `json:"total"`
Cursor string `json:"cursor,omitempty"`
Facets []Facet `json:"facets,omitempty"`
}
WorkRecordHits is the result of a work search with full records.
type WorkSourceGetter ¶
type WorkSourceGetter interface {
Get(ctx context.Context, id string) (*ImportWorkInput, error)
}
WorkSourceGetter is implemented by sources that can fetch a single record by ID.
type WorkSourceIter ¶
type WorkSourceIter interface {
Iter(ctx context.Context) (iter.Seq2[*ImportWorkInput, error], error)
}
WorkSourceIter is implemented by sources that can iterate all records.
type WorkWriter ¶
type WorkWriter interface {
Begin(w io.Writer) error
Encode(w io.Writer, work *Work) error
End(w io.Writer) error
}
WorkWriter writes a stream of works to a writer. Begin writes any preamble (e.g. CSV header, XML root open tag). Encode writes a single work. End writes any postamble (e.g. XML root close tag).
func NewWorkWriter ¶
func NewWorkWriter(format string) (WorkWriter, error)
NewWorkWriter creates a new writer for the given format.
Source Files
¶
- batch.go
- collection.go
- collections.go
- crypto.go
- errors.go
- events.go
- field_catalog.go
- field_type.go
- field_update.go
- field_val.go
- field_validation.go
- file_store.go
- gather.go
- grant.go
- grants.go
- id.go
- import.go
- index.go
- list.go
- lists.go
- migrate.go
- organization.go
- organization_updaters.go
- organizations.go
- people.go
- person.go
- person_updaters.go
- profile.go
- project.go
- project_updaters.go
- projects.go
- query.go
- record.go
- record_encoding.go
- record_state.go
- repo.go
- representations.go
- resolve.go
- revs.go
- services.go
- slug.go
- sources.go
- tasks.go
- update_decoding.go
- updaters.go
- uploads.go
- user.go
- user_source.go
- users.go
- util.go
- values.go
- work.go
- work_decoding.go
- work_encoding.go
- work_files.go
- work_history.go
- work_updaters.go
- works.go
- write.go
Directories
¶
| Path | Synopsis |
|---|---|
|
views
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
Package citeformat formats works as citations using a citeproc-js-server.
|
Package citeformat formats works as citations using a citeproc-js-server. |
|
cmd
|
|
|
bbl
command
|
|
|
Package csvformat encodes works as CSV.
|
Package csvformat encodes works as CSV. |
|
Package dcformat encodes works as Dublin Core XML.
|
Package dcformat encodes works as Dublin Core XML. |
|
Package oidcauth implements app.AuthProvider using OpenID Connect.
|
Package oidcauth implements app.AuthProvider using OpenID Connect. |
|
Package sru implements a minimal SRU (Search/Retrieve via URL) 1.2 server.
|
Package sru implements a minimal SRU (Search/Retrieve via URL) 1.2 server. |
|
ugent
|
|
|
cmd/bbl
command
|
|