entitlement

package
v1.0.0-beta.227 Latest Latest
Warning

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

Go to latest
Published: Feb 12, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Index

Constants

View Source
const ErrCodeEntitlementCreatePropertyMismatch models.ErrorCode = "entitlement_create_property_mismatch"
View Source
const ErrCodeEntitlementGrantsOnlySupportedForMeteredEntitlements models.ErrorCode = "entitlement_grants_only_supported_for_metered_entitlements"
View Source
const (
	EventSubsystem metadata.EventSubsystem = "entitlement"
)

Variables

View Source
var ErrEntitlementCreatePropertyMismatch = models.NewValidationIssue(
	ErrCodeEntitlementCreatePropertyMismatch,
	"entitlement create property mismatch",
)
View Source
var ErrEntitlementGrantsOnlySupportedForMeteredEntitlements = models.NewValidationIssue(
	ErrCodeEntitlementGrantsOnlySupportedForMeteredEntitlements,
	"grants are only supported for metered entitlements",
)
View Source
var MAX_SAFE_ITERATIONS = 1000000

Functions

func ValidateUniqueConstraint

func ValidateUniqueConstraint(ents []Entitlement) error

ValidateUniqueConstraint validates the uniqueness constraints of the entitlements The constraint is formally stated as follows:

For entitlement E 1. The ActiveFromTime is E.ActiveFrom or E.CreatedAt (if E.ActiveFrom is nil) 2. The ActiveToTime is E.ActiveTo or E.DeletedAt (if E.ActiveTo is nil). This can be nil.

Entitlement E is active at time T if and only if: 1. E.ActiveFromTime <= T and E.ActiveToTime > T 2. E.ActiveFromTime <= T and E.ActiveToTime is nil

For a set of unique entitlements S, where all E in S share the same feature (by key) and customer: 1. Let T1 be the first ActiveFromTime for any E in S sorted ascending 2. Let T2 be the last ActiveToTime for any E in S sorted ascending

The constraint:

For all E in S at any time T where T1 <= T < T2, there is at most one E that is active.

Types

type Access

type Access struct {
	// Map of featureKey to entitlement value + ID
	Entitlements map[string]EntitlementValueWithId
}

type AlreadyDeletedError

type AlreadyDeletedError struct {
	EntitlementID string
}

func (*AlreadyDeletedError) Error

func (e *AlreadyDeletedError) Error() string

type AlreadyExistsError

type AlreadyExistsError struct {
	EntitlementID string
	FeatureID     string
	CustomerID    string
}

func (*AlreadyExistsError) Error

func (e *AlreadyExistsError) Error() string

type CreateEntitlementGrantInputs

type CreateEntitlementGrantInputs struct {
	credit.CreateGrantInput
}

type CreateEntitlementInputs

type CreateEntitlementInputs struct {
	Namespace        string                             `json:"namespace"`
	FeatureID        *string                            `json:"featureId"`
	FeatureKey       *string                            `json:"featureKey"`
	UsageAttribution streaming.CustomerUsageAttribution `json:"usageAttribution"`
	EntitlementType  EntitlementType                    `json:"type"`
	Metadata         map[string]string                  `json:"metadata,omitempty"`
	Annotations      models.Annotations                 `json:"annotations,omitempty"`

	// ActiveFrom allows entitlements to be scheduled for future activation.
	// If not set, the entitlement is active immediately.
	ActiveFrom *time.Time `json:"activeFrom,omitempty"`
	// ActiveTo allows entitlements to be descheduled for future activation.
	// If not set, the entitlement is active until deletion.
	ActiveTo *time.Time `json:"activeTo,omitempty"`

	MeasureUsageFrom        *MeasureUsageFromInput `json:"measureUsageFrom,omitempty"`
	IssueAfterReset         *float64               `json:"issueAfterReset,omitempty"`
	IssueAfterResetPriority *uint8                 `json:"issueAfterResetPriority,omitempty"`
	IsSoftLimit             *bool                  `json:"isSoftLimit,omitempty"`
	Config                  *string                `json:"config,omitempty"`
	UsagePeriod             *UsagePeriodInput      `json:"usagePeriod,omitempty"`
	PreserveOverageAtReset  *bool                  `json:"preserveOverageAtReset,omitempty"`

	SubscriptionManaged bool `json:"subscriptionManaged,omitempty"`
}

func (CreateEntitlementInputs) Equal

func (CreateEntitlementInputs) GetType

func (CreateEntitlementInputs) Validate

func (c CreateEntitlementInputs) Validate() error

type CreateEntitlementRepoInputs

type CreateEntitlementRepoInputs struct {
	Namespace        string                             `json:"namespace"`
	FeatureID        string                             `json:"featureId"`
	FeatureKey       string                             `json:"featureKey"`
	UsageAttribution streaming.CustomerUsageAttribution `json:"usageAttribution"`
	EntitlementType  EntitlementType                    `json:"type"`
	Metadata         map[string]string                  `json:"metadata,omitempty"`
	ActiveFrom       *time.Time                         `json:"activeFrom,omitempty"`
	ActiveTo         *time.Time                         `json:"activeTo,omitempty"`

	Annotations models.Annotations `json:"annotations,omitempty"`

	MeasureUsageFrom        *time.Time             `json:"measureUsageFrom,omitempty"`
	IssueAfterReset         *float64               `json:"issueAfterReset,omitempty"`
	IssueAfterResetPriority *uint8                 `json:"issueAfterResetPriority,omitempty"`
	IsSoftLimit             *bool                  `json:"isSoftLimit,omitempty"`
	Config                  *string                `json:"config,omitempty"`
	UsagePeriod             *UsagePeriodInput      `json:"usagePeriod,omitempty"`
	CurrentUsagePeriod      *timeutil.ClosedPeriod `json:"currentUsagePeriod,omitempty"`
	PreserveOverageAtReset  *bool                  `json:"preserveOverageAtReset,omitempty"`
}

type Entitlement

type Entitlement struct {
	GenericProperties

	// All none-core fields are optional
	// metered
	MeasureUsageFrom        *time.Time `json:"measureUsageFrom,omitempty"`
	IssueAfterReset         *float64   `json:"issueAfterReset,omitempty"`
	IssueAfterResetPriority *uint8     `json:"issueAfterResetPriority,omitempty"`
	IsSoftLimit             *bool      `json:"isSoftLimit,omitempty"`
	LastReset               *time.Time `json:"lastReset,omitempty"`
	PreserveOverageAtReset  *bool      `json:"preserveOverageAtReset,omitempty"`

	// static
	Config *string `json:"config,omitempty"`
}

Normalized representation of an entitlement in the system

func (Entitlement) AsCreateEntitlementInputs

func (e Entitlement) AsCreateEntitlementInputs(cust customer.Customer) CreateEntitlementInputs

func (Entitlement) GetCadence

func (e Entitlement) GetCadence() models.CadencedModel

func (Entitlement) GetType

func (e Entitlement) GetType() EntitlementType

func (Entitlement) IsActive

func (e Entitlement) IsActive(at time.Time) bool

IsActive returns if the entitlement is active at the given time Period start is determined by

type EntitlementCreatedEventV2

type EntitlementCreatedEventV2 entitlementEventV2

func NewEntitlementCreatedEventPayloadV2

func NewEntitlementCreatedEventPayloadV2(ent Entitlement, c *customer.Customer) EntitlementCreatedEventV2

func (EntitlementCreatedEventV2) EventMetadata

func (EntitlementCreatedEventV2) EventName

func (e EntitlementCreatedEventV2) EventName() string

func (EntitlementCreatedEventV2) Validate

func (e EntitlementCreatedEventV2) Validate() error

type EntitlementDeletedEventV2

type EntitlementDeletedEventV2 entitlementEventV2

func NewEntitlementDeletedEventPayloadV2

func NewEntitlementDeletedEventPayloadV2(ent Entitlement, c *customer.Customer) EntitlementDeletedEventV2

func (EntitlementDeletedEventV2) EventMetadata

func (EntitlementDeletedEventV2) EventName

func (e EntitlementDeletedEventV2) EventName() string

func (EntitlementDeletedEventV2) Validate

func (e EntitlementDeletedEventV2) Validate() error

type EntitlementRepo

type EntitlementRepo interface {
	// GetActiveEntitlementOfSubjectAt returns the active entitlement of a customer at a given time by feature key
	GetActiveEntitlementOfCustomerAt(ctx context.Context, namespace string, customerID string, featureKey string, at time.Time) (*Entitlement, error)

	// GetScheduledEntitlements returns all scheduled entitlements for a given customer-feature pair that become inactive after the given time, sorted by the time they become active
	GetScheduledEntitlements(ctx context.Context, namespace string, customerID string, featureKey string, starting time.Time) ([]Entitlement, error)

	// DeactivateEntitlement deactivates an entitlement by setting the activeTo time. If the entitlement is already deactivated, it returns an error.
	DeactivateEntitlement(ctx context.Context, entitlementID models.NamespacedID, at time.Time) error

	CreateEntitlement(ctx context.Context, entitlement CreateEntitlementRepoInputs) (*Entitlement, error)
	GetEntitlement(ctx context.Context, entitlementID models.NamespacedID) (*Entitlement, error)
	DeleteEntitlement(ctx context.Context, entitlementID models.NamespacedID, at time.Time) error

	ListEntitlements(ctx context.Context, params ListEntitlementsParams) (pagination.Result[Entitlement], error)

	// ListNamespacesWithActiveEntitlements returns a list of namespaces that have active entitlements
	//
	// Active in this context means the entitlement is active at any point between now and the given time.
	// If includeDeletedAfter is before the current time, it will include namespaces that have entitlements active at that instance.
	ListNamespacesWithActiveEntitlements(ctx context.Context, includeDeletedAfter time.Time) ([]string, error)

	UpdateEntitlementUsagePeriod(ctx context.Context, entitlementID models.NamespacedID, params UpdateEntitlementUsagePeriodParams) error

	// ListActiveEntitlementsWithExpiredUsagePeriod returns a list of active entitlements with usage period that expired before the highwatermark
	// - Only entitlements active at the highwatermark are considered.
	// - The list is sorted by the current usage period end, then by created at, then by id.
	// - The list is paginated by the cursor & limit.
	// - CurrentUsagePeriod won't be mapped to the calculated values
	ListActiveEntitlementsWithExpiredUsagePeriod(ctx context.Context, params ListExpiredEntitlementsParams) ([]Entitlement, error)

	// UpsertEntitlementCurrentPeriods upserts the current usage period for a list of entitlements
	// - If an entitlement is found, it will be updated
	// - If any update fails, the entire operation will fail
	UpsertEntitlementCurrentPeriods(ctx context.Context, updates []UpsertEntitlementCurrentPeriodElement) error

	LockEntitlementForTx(ctx context.Context, tx *entutils.TxDriver, entitlementID models.NamespacedID) error

	entutils.TxCreator
}

type EntitlementType

type EntitlementType string
const (
	// EntitlementTypeMetered represents entitlements where access is determined by usage and balance calculations
	EntitlementTypeMetered EntitlementType = "metered"
	// EntitlementTypeStatic represents entitlements where access is described by a static configuration
	EntitlementTypeStatic EntitlementType = "static"
	// EntitlementTypeBoolean represents boolean access
	EntitlementTypeBoolean EntitlementType = "boolean"
)

func (EntitlementType) StrValues

func (e EntitlementType) StrValues() []string

func (EntitlementType) String

func (e EntitlementType) String() string

func (EntitlementType) Values

func (e EntitlementType) Values() []EntitlementType

type EntitlementValue

type EntitlementValue interface {
	HasAccess() bool
}

type EntitlementValueWithId

type EntitlementValueWithId struct {
	Type  EntitlementType
	Value EntitlementValue
	ID    string
}

type EntitlementWithCustomer

type EntitlementWithCustomer struct {
	Entitlement
	Customer customer.Customer
}

type ForbiddenError

type ForbiddenError struct {
	Message string
}

func (*ForbiddenError) Error

func (e *ForbiddenError) Error() string

type GenericProperties

type GenericProperties struct {
	models.NamespacedModel
	models.ManagedModel
	models.MetadataModel
	models.Annotations

	// ActiveFrom allows entitlements to be scheduled for future activation.
	// If not set, the entitlement is active immediately.
	ActiveFrom *time.Time `json:"activeFrom,omitempty"`
	// ActiveTo allows entitlements to be descheduled for future activation.
	// If not set, the entitlement is active until deletion.
	ActiveTo *time.Time `json:"activeTo,omitempty"`

	ID         string `json:"id,omitempty"`
	FeatureID  string `json:"featureId,omitempty"`
	FeatureKey string `json:"featureKey,omitempty"`

	CustomerID string `json:"customerId,omitempty"`

	EntitlementType           EntitlementType        `json:"type,omitempty"`
	UsagePeriod               *UsagePeriod           `json:"usagePeriod,omitempty"`
	CurrentUsagePeriod        *timeutil.ClosedPeriod `json:"currentUsagePeriod,omitempty"`
	OriginalUsagePeriodAnchor *time.Time             `json:"originalUsagePeriodAnchor,omitempty"`
}

GenericProperties is the core fields of an entitlement that are always applicable regadless of type

func (GenericProperties) ActiveFromTime

func (e GenericProperties) ActiveFromTime() time.Time

ActiveFromTime returns the time the entitlement is active from. Its either the ActiveFrom field or the CreatedAt field

func (GenericProperties) ActiveToTime

func (e GenericProperties) ActiveToTime() *time.Time

ActiveToTime returns the time the entitlement is active to. Its either the ActiveTo field or the DeletedAt field or nil

func (GenericProperties) Validate

func (e GenericProperties) Validate() error

type InvalidFeatureError

type InvalidFeatureError struct {
	FeatureID string
	Message   string
}

func (*InvalidFeatureError) Error

func (e *InvalidFeatureError) Error() string

type InvalidValueError

type InvalidValueError struct {
	Message string
	Type    EntitlementType
}

func (*InvalidValueError) Error

func (e *InvalidValueError) Error() string

type ListEntitlementsOrderBy

type ListEntitlementsOrderBy string
const (
	ListEntitlementsOrderByCreatedAt ListEntitlementsOrderBy = "created_at"
	ListEntitlementsOrderByUpdatedAt ListEntitlementsOrderBy = "updated_at"
)

func (ListEntitlementsOrderBy) StrValues

func (o ListEntitlementsOrderBy) StrValues() []string

func (ListEntitlementsOrderBy) Values

type ListEntitlementsParams

type ListEntitlementsParams struct {
	IDs              []string
	Namespaces       []string
	SubjectKeys      []string
	CustomerIDs      []string
	CustomerKeys     []string
	FeatureIDs       []string
	FeatureKeys      []string
	FeatureIDsOrKeys []string
	EntitlementTypes []EntitlementType
	OrderBy          ListEntitlementsOrderBy
	Order            sortx.Order
	// TODO[galexi]: We should clean up how these 4 fields are used together.
	IncludeDeleted      bool
	IncludeDeletedAfter time.Time
	ExcludeInactive     bool
	ActiveAt            *time.Time

	Page pagination.Page
	// will be deprecated
	Limit int
	// will be deprecated
	Offset int
}

type ListEntitlementsWithCustomerResult

type ListEntitlementsWithCustomerResult struct {
	Entitlements  pagination.Result[Entitlement]
	CustomersByID map[models.NamespacedID]*customer.Customer
}

type ListExpiredEntitlementsParams

type ListExpiredEntitlementsParams struct {
	Namespaces    []string
	Highwatermark time.Time
	Limit         int
	// Cursor is the ID of the last entitlement in the previous page
	// If not provided, the query will return the first page of results
	Cursor *paginationv2.Cursor
}

type MeasureUsageFromEnum

type MeasureUsageFromEnum string
const (
	MeasureUsageFromCurrentPeriodStart MeasureUsageFromEnum = "CURRENT_PERIOD_START"
	MeasureUsageFromNow                MeasureUsageFromEnum = "NOW"
)

func (MeasureUsageFromEnum) Validate

func (e MeasureUsageFromEnum) Validate() error

func (MeasureUsageFromEnum) Values

type MeasureUsageFromInput

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

func (MeasureUsageFromInput) Equal

func (*MeasureUsageFromInput) FromEnum

func (*MeasureUsageFromInput) FromTime

func (m *MeasureUsageFromInput) FromTime(t time.Time) error

func (MeasureUsageFromInput) Get

type NoAccessValue

type NoAccessValue struct{}

func (*NoAccessValue) HasAccess

func (*NoAccessValue) HasAccess() bool

type NotFoundError

type NotFoundError struct {
	EntitlementID models.NamespacedID
}

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

type Service

type Service interface {
	models.ServiceHooks[Entitlement]

	// Meant for API use primarily
	CreateEntitlement(ctx context.Context, input CreateEntitlementInputs, grants []CreateEntitlementGrantInputs) (*Entitlement, error)
	// OverrideEntitlement replaces a currently active entitlement with a new one.
	OverrideEntitlement(ctx context.Context, customerID string, entitlementIdOrFeatureKey string, input CreateEntitlementInputs, grants []CreateEntitlementGrantInputs) (*Entitlement, error)

	ScheduleEntitlement(ctx context.Context, input CreateEntitlementInputs) (*Entitlement, error)
	// SupersedeEntitlement replaces an entitlement by scheduling a new one
	SupersedeEntitlement(ctx context.Context, entitlementId string, input CreateEntitlementInputs) (*Entitlement, error)

	GetEntitlement(ctx context.Context, namespace string, id string) (*Entitlement, error)
	GetEntitlementWithCustomer(ctx context.Context, namespace string, id string) (*EntitlementWithCustomer, error)
	DeleteEntitlement(ctx context.Context, namespace string, id string, at time.Time) error

	GetEntitlementValue(ctx context.Context, namespace string, customerID string, idOrFeatureKey string, at time.Time) (EntitlementValue, error)

	GetEntitlementsOfCustomer(ctx context.Context, namespace string, customerId string, at time.Time) ([]Entitlement, error)
	ListEntitlements(ctx context.Context, params ListEntitlementsParams) (pagination.Result[Entitlement], error)
	ListEntitlementsWithCustomer(ctx context.Context, params ListEntitlementsParams) (ListEntitlementsWithCustomerResult, error)

	// Attempts to get the entitlement in an ambiguous situation where it's unclear if the entitlement is referenced by ID or FeatureKey + CustomerID.
	// First attempts to resolve by ID, then by FeatureKey + CustomerID.
	//
	// For consistency, it is forbidden for entitlements to be created for featueres the keys of which could be mistaken for entitlement IDs.
	GetEntitlementOfCustomerAt(ctx context.Context, namespace string, customerID string, idOrFeatureKey string, at time.Time) (*Entitlement, error)

	// GetAccess returns the access of a customer.
	// It returns a map of featureKey to entitlement value + ID.
	GetAccess(ctx context.Context, namespace string, customerID string) (Access, error)
}

type SubTypeConnector

type SubTypeConnector interface {
	GetValue(ctx context.Context, entitlement *Entitlement, at time.Time) (EntitlementValue, error)

	// Runs before creating the entitlement, building the Repository inputs.
	// If it returns an error the operation has to fail.
	BeforeCreate(entitlement CreateEntitlementInputs, feature feature.Feature) (*CreateEntitlementRepoInputs, error)

	// Runs after entitlement creation.
	// If it returns an error the operation has to fail.
	AfterCreate(ctx context.Context, entitlement *Entitlement) error
}

FIXME[galexi]: we can get rid of this concept due to better hierarchy

type TypedEntitlement

type TypedEntitlement interface {
	GetType() EntitlementType
}

type UniquenessConstraintError

type UniquenessConstraintError struct {
	E1, E2 Entitlement
}

func (*UniquenessConstraintError) Error

func (e *UniquenessConstraintError) Error() string

type UpdateEntitlementUsagePeriodParams

type UpdateEntitlementUsagePeriodParams struct {
	CurrentUsagePeriod timeutil.ClosedPeriod
}

type UpsertEntitlementCurrentPeriodElement

type UpsertEntitlementCurrentPeriodElement struct {
	models.NamespacedID
	CurrentUsagePeriod timeutil.ClosedPeriod
}

type UsagePeriod

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

func NewStartingUsagePeriod

func NewStartingUsagePeriod(rec timeutil.Recurrence, start time.Time) UsagePeriod

func NewUsagePeriod

func NewUsagePeriod(recs []timeutil.Timed[timeutil.Recurrence]) UsagePeriod

When providing an initial value (single element in list), for metered entitlements, timed.GetTime() should return measureUsageFrom!

func NewUsagePeriodFromRecurrence

func NewUsagePeriodFromRecurrence(rec timeutil.Recurrence) UsagePeriod

Intended for testing mainly

func (UsagePeriod) Equal

func (u UsagePeriod) Equal(other UsagePeriod) bool

func (UsagePeriod) GetCurrentPeriodAt

func (u UsagePeriod) GetCurrentPeriodAt(at time.Time) (timeutil.ClosedPeriod, error)

func (*UsagePeriod) GetOriginalValueAsUsagePeriodInput

func (u *UsagePeriod) GetOriginalValueAsUsagePeriodInput() *UsagePeriodInput

func (UsagePeriod) GetResetTimelineInclusive

func (u UsagePeriod) GetResetTimelineInclusive(inPeriod timeutil.ClosedPeriod) (timeutil.SimpleTimeline, error)

func (UsagePeriod) GetUsagePeriodInputAt

func (u UsagePeriod) GetUsagePeriodInputAt(at time.Time) (UsagePeriodInput, int, error)

func (UsagePeriod) MarshalJSON

func (u UsagePeriod) MarshalJSON() ([]byte, error)

func (*UsagePeriod) UnmarshalJSON

func (u *UsagePeriod) UnmarshalJSON(data []byte) error

func (UsagePeriod) Validate

func (u UsagePeriod) Validate() error

type UsagePeriodInput

type UsagePeriodInput = timeutil.Timed[timeutil.Recurrence]

func NewStartingUsagePeriodInput

func NewStartingUsagePeriodInput(rec timeutil.Recurrence, start time.Time) UsagePeriodInput

func NewUsagePeriodInputFromRecurrence

func NewUsagePeriodInputFromRecurrence(rec timeutil.Recurrence) UsagePeriodInput

Intended for testing mainly

type WrongTypeError

type WrongTypeError struct {
	Expected EntitlementType
	Actual   EntitlementType
}

func (*WrongTypeError) Error

func (e *WrongTypeError) Error() string

Directories

Path Synopsis
v2
hooks
validators

Jump to

Keyboard shortcuts

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