entities

package
v0.137.13 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: MIT Imports: 10 Imported by: 1

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 and an optional scope; 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

View Source
const AvatarPreset = "avatar"

AvatarPreset is the media preset avatars render in (media.AvatarPreset).

View Source
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.

View Source
const (
	EventCreated = "entity.created"
)
View Source
const (
	EventDeleted = "entity.deleted"
)
View Source
const (
	// EventMerged names the final survivor in MergedInto. Earlier merges into
	// the merged entity are re-recorded with the same survivor.
	EventMerged = "entity.merged"
)
View Source
const (
	EventRestored = "entity.restored"
)
View Source
const (
	EventUpdated = "entity.updated"
)
View Source
const MaxListIDs = 100

MaxListIDs bounds ListOptions.IDs.

View Source
const MaxMatchNames = 100

MaxMatchNames bounds MatchQuery.Names.

View Source
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

View Source
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 Change added in v0.135.0

type Change struct {
	Type       string
	Kind       string
	ID         EntityID
	MergedInto EntityID
}

Change is one committed entity mutation.

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

type CoverOf struct {
	Kind     string
	ID       EntityID
	Cover    Cover
	Language string
	Preset   string
}

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 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"`
	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

type EntityNames struct {
	ID    EntityID `json:"id"`
	Names []Name   `json:"names"`
}

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
	Code      string
	CreatedAt *time.Time
	UpdatedAt *time.Time
}

ImportEntity is an entity as the bulk import writes it: its id kept. Nil Names and Text 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 Merge

type Merge struct {
	From EntityID `json:"from"`
	Into EntityID `json:"into"`
}

Merge folds entity From into entity Into of the same kind.

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"`
	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

type NameMatch struct {
	Name string     `json:"name"`
	IDs  []EntityID `json:"ids"`
}

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 Ref

type Ref struct {
	Kind string
	ID   EntityID
}

Ref names one entity with its kind.

type Rendition

type Rendition struct {
	URL string `json:"url"`
	W   int    `json:"w"`
	H   int    `json:"h"`
}

Rendition is one public image of a cover, as media.PublicRendition.

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

type ScopeProvisioner interface {
	Provision(ctx context.Context, scopes []Scope) error
}

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"
)

type Text

type Text struct {
	Language    string `json:"language"`
	Description string `json:"description,omitempty"`
	SortKey     string `json:"sort_key,omitempty"`
}

Text is an entity's description and sort key in one language.

type Written

type Written struct {
	Count int `json:"count"`
}

Written counts the items a write named.

Jump to

Keyboard shortcuts

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