Documentation
¶
Overview ¶
Package entities is how content is grouped: tags, artists, series, characters, channels, playlists and featured rails. Entity kinds come from kinds.Config.EntityKinds. A entity has localized names and aliases, per-language text, declared attributes, a cover, a parent, an optional scope and edges to other entities; content joins it through memberships, at content level or for one version where the kind allows. A version's effective entities (EffectiveEntities) feed content_facets.entity_ids; counts derive from those facets.
Index ¶
- Constants
- Variables
- type AppBinding
- type Avatar
- type Change
- type ContentEntities
- type ContentMemberships
- type Count
- type Cover
- type CoverOf
- type CoverResolver
- type Edge
- type EdgeInput
- type Entity
- type EntityAccess
- type EntityCard
- type EntityDetail
- type EntityID
- type EntityInput
- type EntityNames
- type EntityPage
- type EntityUpdate
- type ImportEntity
- type LocalScopes
- type MatchQuery
- type MemberOptions
- type MemberPage
- type MemberRef
- type Membership
- type MembershipState
- type Merge
- type MergeInput
- type MergeReport
- type MyEntity
- type MyEntityPage
- type Name
- type NameKind
- type NameMatch
- type NewEntity
- type Order
- type OwnedContent
- type Ref
- type Rendition
- type Scope
- type ScopeProvisioner
- type Scopes
- type Sort
- type State
- type Text
- type Written
Constants ¶
const AvatarPreset = "avatar"
AvatarPreset is the media preset avatars render in (media.AvatarPreset).
const DefaultDeletedRetention = 30 * 24 * time.Hour
DefaultDeletedRetention is how long a deleted user-owned entity keeps its media folder, so a restore brings it back.
const (
EventCreated = "entity.created"
)
const (
EventDeleted = "entity.deleted"
)
const ( // EventMerged names the final survivor in MergedInto. Earlier merges into // the merged entity are re-recorded with the same survivor. EventMerged = "entity.merged" )
const (
EventRestored = "entity.restored"
)
const (
EventUpdated = "entity.updated"
)
const MaxListIDs = 100
MaxListIDs bounds ListOptions.IDs.
const MaxMatchNames = 100
MaxMatchNames bounds MatchQuery.Names.
const NameSeparators = `[[:space:]_‐‑‒–—―-]+`
NameSeparators are ignored when names match. Removing all punctuation would conflate distinct names such as C/C++ or AC/DC and ACDC.
Variables ¶
var (
ErrInvalid = errors.New("entities: invalid input")
)
Functions ¶
This section is empty.
Types ¶
type AppBinding ¶
type AppBinding interface {
ContentKitScope() (tenantID, appID string)
}
AppBinding identifies the tenant/app an authorization scope provider serves. A scoped engine requires a matching binding before enabling auth_scope kinds.
type Avatar ¶
type Avatar struct {
Image string `json:"image,omitempty"`
// Renditions are its public images, filled on reads (Options.Covers).
Renditions []Rendition `json:"renditions,omitempty"`
}
Avatar is an entity's square picture (kinds.EntityKind.HasAvatar): an image uploaded to the entity's media folder ("avatar/i-{uuid}"), selected by name. Without a selection a read shows the folder's first avatar, else the kind's default.
type ContentEntities ¶
type ContentEntities struct {
// ContentID names the content; routes take it from the path.
ContentID string `json:"-"`
// VersionScope limits replacement to one ownership scope. Nil preserves
// the legacy all-version replacement; empty means shared content links;
// otherwise it names the one version whose links may change.
VersionScope *string `json:"-"`
// Kinds limits the write to these entity kinds; empty means every entity
// kind the content's kind declares.
Kinds []string `json:"kinds,omitempty"`
Memberships []Membership `json:"memberships"`
// Create links these names too, creating entities of creatable kinds that
// no active entity names yet, in the same transaction.
Create []NewEntity `json:"create,omitempty"`
}
ContentEntities is one content's memberships in the entity kinds Kinds names.
type ContentMemberships ¶
type ContentMemberships struct {
ContentID string `json:"content_id"`
Memberships []Membership `json:"memberships"`
Entities []EntityCard `json:"entities"`
}
ContentMemberships is a content's memberships with the entities they name.
type Count ¶
type Count struct {
ContentKind string `json:"content_kind"`
Language string `json:"language"`
Count int `json:"count"`
}
Count is the number of distinct live, listed contents of one kind in a entity (its descendants included) or owned by it, serving one language; Language "" counts every language once.
type Cover ¶
type Cover struct {
Image string `json:"image,omitempty"`
ContentID string `json:"content_id,omitempty"`
Auto bool `json:"auto,omitempty"`
// Renditions are its public images, filled on reads (Options.Covers).
Renditions []Rendition `json:"renditions,omitempty"`
}
Cover is an entity's picture: an image uploaded to the entity's media folder ("i-{uuid}") or a member content's cover, the cover of the edition a reader's language opens by default. Auto covers are the most-favorited live member, selected at read time when none is explicitly chosen.
type CoverOf ¶
CoverOf is one entity's picture to resolve: its cover, or with Preset "avatar" its avatar (Cover.Image the selected one, if any). A content's cover is its default edition's in Language.
type CoverResolver ¶
type CoverResolver interface {
Covers(ctx context.Context, covers []CoverOf) ([][]Rendition, error)
}
CoverResolver turns covers and avatars into their public renditions, per picture in order, one call per page: media/versions.Service.EntityCovers.
type Edge ¶
type Edge struct {
From EntityID `json:"from"`
Relation string `json:"relation"`
To EntityID `json:"to"`
}
Edge is a typed relation between two entities beside parent and scope: "from is <relation> of to", such as a character member_of a second series.
type Entity ¶
type Entity struct {
ID EntityID `json:"id"`
Kind string `json:"kind"`
Slug string `json:"slug"`
// Internal marks a classification used by policy rather than public metadata.
Internal bool `json:"internal"`
// ScopeID is the entity the slug is unique within (a character's series).
ScopeID EntityID `json:"scope_id,omitempty"`
ParentID EntityID `json:"parent_id,omitempty"`
State State `json:"state"`
MergedInto EntityID `json:"merged_into,omitempty"`
Attributes map[string]any `json:"attributes"`
Cover *Cover `json:"cover,omitempty"`
Avatar *Avatar `json:"avatar,omitempty"`
OwnerUserID string `json:"-"`
// AuthScope is the AuthKit group whose holders of
// access.EntitySettingsManage edit it; ContentKit provisions it for a
// kind with auth_scope (Options.Scopes).
AuthScope string `json:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Revision int64 `json:"revision"`
}
Entity is one entity record.
type EntityAccess ¶ added in v0.125.0
type EntityAccess struct {
Key access.Key `json:"key"`
Member bool `json:"member"`
Editor bool `json:"editor,omitempty"`
Unknown bool `json:"unknown,omitempty"`
}
EntityAccess is an entity entitlement and the current viewer's standing. Product and price information belongs to the billing provider.
type EntityCard ¶
type EntityCard struct {
ID EntityID `json:"id"`
Kind string `json:"kind"`
Slug string `json:"slug"`
Internal bool `json:"internal"`
ScopeID EntityID `json:"scope_id,omitempty"`
ParentID EntityID `json:"parent_id,omitempty"`
State State `json:"state"`
// MergedInto is a merged entity's final survivor.
MergedInto EntityID `json:"merged_into,omitempty"`
Name string `json:"name"`
NameLanguage string `json:"name_language,omitempty"`
Code string `json:"code,omitempty"`
Path string `json:"path,omitempty"`
Cover *Cover `json:"cover,omitempty"`
// Avatar is a user-owned entity's avatar.
Avatar *Avatar `json:"avatar,omitempty"`
Count int `json:"count"`
Attributes map[string]any `json:"attributes"`
// Access is an entitlement-backed entity's key and the
// viewer's standing.
Access *EntityAccess `json:"access,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
EntityCard is a listed entity: what a directory page or a chip needs.
type EntityDetail ¶
type EntityDetail struct {
Entity
Name string `json:"name"`
NameLanguage string `json:"name_language,omitempty"`
Code string `json:"code,omitempty"`
Path string `json:"path,omitempty"`
Names []Name `json:"names"`
Text []Text `json:"text"`
Parent *EntityCard `json:"parent,omitempty"`
Scope *EntityCard `json:"scope,omitempty"`
Children []EntityCard `json:"children"`
Edges []Edge `json:"edges"`
Counts []Count `json:"counts"`
// Access is an entitlement-backed entity's key and the
// viewer's standing.
Access *EntityAccess `json:"access,omitempty"`
}
EntityDetail is one entity with everything its page shows.
func (EntityDetail) MatchesIdentity ¶ added in v0.131.0
func (d EntityDetail) MatchesIdentity(names []Name, text []Text) bool
MatchesIdentity compares caller-supplied names and localized text using the same normalization as writes. It is useful when replaying an acknowledged creation: omitted alias kinds, language casing and surrounding name/sort-key whitespace must not turn an identical request into a conflicting identity.
type EntityID ¶
type EntityID = contentref.EntityID
EntityID identifies one entity within a tenant: a UUIDv7, kept across import.
type EntityInput ¶
type EntityInput struct {
// ID is optional; omitted ids are generated.
ID EntityID `json:"id,omitempty"`
Kind string `json:"kind"`
Slug string `json:"slug"`
// Internal classifications are visible only through management reads.
Internal bool `json:"internal,omitempty"`
// ScopeID is required for a kind scoped by another (a character's series).
ScopeID EntityID `json:"scope_id,omitempty"`
ParentID EntityID `json:"parent_id,omitempty"`
Attributes map[string]any `json:"attributes,omitempty"`
Cover *Cover `json:"cover,omitempty"`
Avatar *Avatar `json:"avatar,omitempty"`
// OwnerUserID owns a user-owned entity; a person creating one owns it.
OwnerUserID string `json:"-"`
// AuthScope is staff's (an import's); ContentKit provisions it otherwise.
AuthScope string `json:"-"`
Names []Name `json:"names,omitempty"`
Text []Text `json:"text,omitempty"`
}
EntityInput creates one entity.
type EntityNames ¶
EntityNames is one entity's names in a names write.
type EntityPage ¶
type EntityPage struct {
Entities []EntityCard `json:"entities"`
// NextCursor pages on; empty on the last page.
NextCursor string `json:"next_cursor,omitempty"`
// Total is every matching entity, on numbered pages only.
Total *int `json:"total,omitempty"`
}
EntityPage is one page of a listing.
type EntityUpdate ¶
type EntityUpdate struct {
// ID names the entity; routes take it from the path.
ID EntityID `json:"-"`
Slug *string `json:"slug,omitempty"`
Internal *bool `json:"internal,omitempty"`
ScopeID *EntityID `json:"scope_id,omitempty"`
// ParentID "" detaches the entity from its parent.
ParentID *EntityID `json:"parent_id,omitempty"`
// State restores (active) or deletes an entity.
State *State `json:"state,omitempty"`
// Attributes replaces every attribute.
Attributes map[string]any `json:"attributes,omitempty"`
// Cover selects a cover; an empty one returns to the automatic cover.
Cover *Cover `json:"cover,omitempty"`
// Avatar selects the entity's avatar (kinds with avatars); an empty one returns to
// the first.
Avatar *Avatar `json:"avatar,omitempty"`
OwnerUserID *string `json:"-"`
AuthScope *string `json:"-"`
// Names replaces every name and alias.
Names []Name `json:"names,omitempty"`
// Text replaces the text of every language.
Text []Text `json:"text,omitempty"`
}
EntityUpdate changes one entity's given fields; nil fields are unchanged.
type ImportEntity ¶
type ImportEntity struct {
ID EntityID
Kind string
Slug string
// Internal preserves the stored classification when omitted.
Internal *bool
ScopeID EntityID
ParentID EntityID
State State
Attributes map[string]any
Cover *Cover
Avatar *Avatar
OwnerUserID string
// AuthScope names the entity's AuthKit scope; empty keeps the stored one.
AuthScope string
Names []Name
Text []Text
// Edges are the entity's edges to others (From is the entity).
Edges []Edge
Code string
CreatedAt *time.Time
UpdatedAt *time.Time
}
ImportEntity is an entity as the bulk import writes it: its id kept. Nil Names, Text and Edges keep what is stored; given ones replace it.
type LocalScopes ¶
type LocalScopes interface {
HoldingScopes(ctx context.Context, user string, kinds, scopeIDs []string) (map[string][]string, error)
}
LocalScopes optionally narrows authorization lookups to scope IDs verified by this app's store. It lets a shared identity directory resolve grants without enumerating or trusting entities belonging to other apps.
type MatchQuery ¶ added in v0.135.0
type MatchQuery struct {
Kind string
// Scope is the entity the names are unique within; empty matches unscoped
// entities only.
Scope EntityID
// Language matches names in it only; empty matches every language.
Language string
// Normalized compares ContentKit's normalized forms, folding case, accents
// and whitespace; otherwise names must be equal byte for byte.
Normalized bool
// Names are matched whole: a comma never separates two names.
Names []string
}
MatchQuery selects the entities MatchNames compares names against: active, public entities of Kind within Scope.
type MemberOptions ¶
type MemberOptions struct {
// Cursor is the previous page's NextCursor.
Cursor string
// Limit defaults to 50 and is capped at 500.
Limit int
}
MemberOptions pages Members.
type MemberPage ¶
type MemberPage struct {
Members []Membership `json:"members"`
// NextCursor pages on; empty on the last page.
NextCursor string `json:"next_cursor,omitempty"`
}
MemberPage is one page of an entity's memberships.
type MemberRef ¶
type MemberRef struct {
ContentID string `json:"content_id"`
VersionID string `json:"version_id,omitempty"`
}
MemberRef names a member of an entity: content, or one version of it.
type Membership ¶
type Membership struct {
EntityID EntityID `json:"group_id,omitempty"`
ContentID string `json:"content_id,omitempty"`
VersionID string `json:"version_id,omitempty"`
Role string `json:"role,omitempty"`
Position *int `json:"position,omitempty"`
State MembershipState `json:"state,omitempty"`
}
Membership places content, or one version of it, in an entity. Position orders an ordered entity (appended when omitted).
type MembershipState ¶
type MembershipState string
MembershipState: a proposed membership (a suggestion awaiting review) is stored but effective nowhere until it is accepted.
const (
MembershipActive MembershipState = "active"
)
const (
MembershipProposed MembershipState = "proposed"
)
type MergeInput ¶
type MergeInput struct {
Into EntityID `json:"into"`
}
MergeInput names the entity a merge folds into.
type MergeReport ¶
type MergeReport struct {
From EntityID `json:"from"`
Into EntityID `json:"into"`
MembershipsMoved int64 `json:"memberships_moved"`
NamesMoved int64 `json:"names_moved"`
EdgesMoved int64 `json:"edges_moved"`
EntitiesMoved int64 `json:"groups_moved"`
}
MergeReport summarizes one merge.
type MyEntity ¶
type MyEntity struct {
EntityCard
// Owner: the viewer is its recorded owner and retains ownership authority.
Owner bool `json:"owner"`
// Editor: the viewer holds its kind's access.EntitySettingsManage in its
// AuthKit scope.
Editor bool `json:"editor"`
// CanPost permits new personal posts in this channel.
CanPost bool `json:"can_post"`
// CanEditOwn permits editing the viewer's own posts, not other authors.
CanEditOwn bool `json:"can_edit_own"`
// Writer: the viewer holds its kind's access.EntityContentWrite there.
Writer bool `json:"writer"`
// Moderator: the viewer holds its kind's access.EntityCommentsModerate
// there, or owns it.
Moderator bool `json:"moderator"`
// Member: the viewer holds its key.
Member bool `json:"member"`
}
MyEntity is an entity of the viewer's, with how it is theirs.
type MyEntityPage ¶
type MyEntityPage struct {
Entities []MyEntity `json:"entities"`
NextCursor string `json:"next_cursor,omitempty"`
}
MyEntityPage is one page of the viewer's entities.
type Name ¶
type Name struct {
Language string `json:"language"`
Kind NameKind `json:"kind"`
Name string `json:"name"`
}
Name is one localized canonical name or alias.
type NameKind ¶
type NameKind string
NameKind distinguishes the one canonical name per language from aliases.
const (
NameAlias NameKind = "alias"
)
const (
NameCanonical NameKind = "name"
)
type NameMatch ¶ added in v0.135.0
NameMatch is the entities whose canonical name or alias matches Name, in id order: none is unresolved, two (at most) is ambiguous.
type NewEntity ¶
type NewEntity struct {
Kind string `json:"kind"`
Name string `json:"name"`
// Language of a created entity's canonical name; the catalog's first by default.
Language string `json:"language,omitempty"`
}
NewEntity is a name a content write links, creating the entity when no active entity of its kind carries that name or alias in any language.
type Order ¶
type Order struct {
// EntityID names the entity; routes take it from the path.
EntityID EntityID `json:"-"`
Members []MemberRef `json:"members"`
}
Order sets the member order of one ordered entity.
type OwnedContent ¶
type OwnedContent interface {
// DeleteOwned deletes the live content of the deleted ones among entities,
// recording each entity as its content's cause; bought content stays
// readable to its buyers.
DeleteOwned(ctx context.Context, ents []string) error
// RestoreOwned restores the content the active ones among entities
// deleted when they were, unless it was purged since; content deleted
// for another reason stays deleted.
RestoreOwned(ctx context.Context, ents []string) error
}
OwnedContent follows user-owned entities' deletion and restore with the content they own (owner_entity): the catalog. A nil entities is every entity.
type Scope ¶
type Scope struct {
// Restore permits deliberately restoring a previously retired group. Normal
// provisioning (including configured fixed owners) preserves live groups
// and refuses deleted groups rather than silently removing their grants.
Restore bool
ID string
Kind string
Owner string
}
Scope is one entity's AuthKit scope: ContentKit keys it by the entity's id.
type ScopeProvisioner ¶ added in v0.129.0
ScopeProvisioner ensures the declared authorization groups exist. It is the only provider capability needed by explicit fixed-owner migrations.
type Scopes ¶
type Scopes interface {
ScopeProvisioner
// Retire stops each scope resolving and granting.
Retire(ctx context.Context, ids []string) error
// Holding answers, per live scope of the kinds' personas user belongs
// to, the kind's access.ChannelPermissions user holds there.
Holding(ctx context.Context, user string, kinds []string) (map[string][]string, error)
}
Scopes keeps the AuthKit groups of auth_scope entity kinds. Provision and Retire are idempotent; runtime adds read/revocation behavior to provisioning.
type Sort ¶
type Sort string
Sort orders an entity listing; every order is made total by id.
const ( // SortCount orders by content count, largest first. SortCount Sort = "count" )
const ( // SortName orders by sort key, else display name. The default. SortName Sort = "name" )
const ( // SortNewest orders by creation, newest first. SortNewest Sort = "newest" )
type State ¶
type State string
State of an entity. A merged entity points at its survivor (MergedInto); deleted entities keep their memberships but are effective nowhere.
const (
StateActive State = "active"
)
const (
StateDeleted State = "deleted"
)
const (
StateMerged State = "merged"
)