Documentation
¶
Overview ¶
Package entigo provides a generic entity framework for Go built on GORM.
It features dynamic DTO generation via reflection-based struct tags, generic CRUD operations with optimistic locking, flexible query filtering, caching, audit logging, tracing, and snowflake-based ID generation.
Key types ¶
- Entity and BaseEntity -- domain model interface and base implementation
- EntityService -- generic CRUD service with optimistic locking and transactions
- Converter -- automatic request/response DTO generation from ent struct tags
- ConditionBuilder -- fluent, composable SQL WHERE clause construction
- SQLBuilder -- complex query builder with CTE and DISTINCT ON support
- ContextExtractor -- injectable interface for resolving actor info from context
- CacheService -- pluggable caching with InMemCache and DummyCache
- AuditService -- pluggable audit logging with functional option events
- Tracer and Span -- pluggable distributed tracing with NoopTracer default
Module path ¶
github.com/githonllc/entigo
The ginx sub-package (github.com/githonllc/entigo/ginx) provides Gin-specific HTTP handler support via [BaseHandler].
Index ¶
- Constants
- Variables
- func ArgsToMap(args ...any) map[string]any
- func BuildFilters(queryMap QueryMap, scopes []FilterScope) map[string]any
- func BuildFiltersForType[T any](params QueryMap) map[string]any
- func Copy(from, to any) error
- func CreateTypeFromScopeTag(model any, scope string, usePointer bool) reflect.Type
- func ExecutePaginatedQuery[T any](ctx context.Context, db *gorm.DB, qb *SQLBuilder, offset, size int) (int64, []T, error)
- func GetDbFieldName(field reflect.StructField) string
- func GetDbFields(s any) []string
- func GetJSONName(field reflect.StructField) string
- func GetObjectCache[T any](cache CacheService, key string, dest *T) error
- func GetOrDefault[T comparable](value, defaultValue T) T
- func GetScopeAttributeValue(tag, scope, attrName string) (string, bool)
- func GetScopeFields(s any, action string) []string
- func HasScopeAttribute(tag, scope, attrName string) bool
- func InitIDGenerator(nodeID int64) error
- func IsEmptyStructType(t reflect.Type) bool
- func IsInvalidID(id any) bool
- func IsNil(i any) bool
- func IsValidColumnName(name string) bool
- func NewInstance[T any]() T
- func NotEmpty(value string) func() bool
- func NotNil(value any) func() bool
- func NotZero(value int) func() bool
- func ParseIntOrDefault(s string, defaultVal int) int
- func SetObjectCache(cache CacheService, key string, value any) error
- func SetObjectCacheExp(cache CacheService, key string, value any, exp time.Duration) error
- func ToSnakeCase(s string) string
- type ActorInfo
- type ActorType
- type AuditLogEvent
- type AuditLogEventOption
- func WithAction(action string) AuditLogEventOption
- func WithActorID(actorID ID) AuditLogEventOption
- func WithActorType(actorType ActorType) AuditLogEventOption
- func WithDetails(details map[string]any) AuditLogEventOption
- func WithErrorMessage(errorMessage string) AuditLogEventOption
- func WithIPAddress(ipAddress string) AuditLogEventOption
- func WithIdentityID(identityID ID) AuditLogEventOption
- func WithResourceID(resourceID ID) AuditLogEventOption
- func WithResourceType(resourceType string) AuditLogEventOption
- func WithStatus(status string) AuditLogEventOption
- func WithUserAgent(userAgent string) AuditLogEventOption
- type AuditService
- type BaseEntity
- func (e *BaseEntity) AfterCreate(tx *gorm.DB) error
- func (e *BaseEntity) AfterDelete(tx *gorm.DB) error
- func (e *BaseEntity) AfterFind(tx *gorm.DB) error
- func (e *BaseEntity) AfterUpdate(tx *gorm.DB) error
- func (e *BaseEntity) BeforeCreate(tx *gorm.DB) error
- func (e *BaseEntity) BeforeDelete(tx *gorm.DB) error
- func (e *BaseEntity) BeforeUpdate(tx *gorm.DB) error
- func (e *BaseEntity) GenerateID() ID
- func (e *BaseEntity) GetCreatedAt() time.Time
- func (e *BaseEntity) GetDeletedAt() *time.Time
- func (e *BaseEntity) GetEntityName() string
- func (e *BaseEntity) GetID() ID
- func (e *BaseEntity) GetRevision() int
- func (e *BaseEntity) GetUpdatedAt() time.Time
- func (e *BaseEntity) IncrementRevision()
- func (e *BaseEntity) Init()
- func (e *BaseEntity) IsDeleted() bool
- func (e *BaseEntity) NewInstance() Entity
- func (e *BaseEntity) Restore()
- func (e *BaseEntity) SetCreatedAt(t time.Time)
- func (e *BaseEntity) SetID(id ID)
- func (e *BaseEntity) SetUpdatedAt(t time.Time)
- func (e *BaseEntity) SoftDelete()
- func (e *BaseEntity) Validate() error
- type CacheService
- type CacheServiceWithExp
- type Condition
- type ConditionBuilder
- func (cb *ConditionBuilder) And(args ...any) *ConditionBuilder
- func (cb *ConditionBuilder) AndIf(check func() bool, args ...any) *ConditionBuilder
- func (cb *ConditionBuilder) Build() (string, []any)
- func (cb *ConditionBuilder) Condition(args ...any) *ConditionBuilder
- func (cb *ConditionBuilder) ConditionIf(check func() bool, args ...any) *ConditionBuilder
- func (cb *ConditionBuilder) GroupEnd() *ConditionBuilder
- func (cb *ConditionBuilder) GroupStart() *ConditionBuilder
- func (cb *ConditionBuilder) HasConditions() bool
- func (cb *ConditionBuilder) IsEmpty() bool
- func (cb *ConditionBuilder) Or(args ...any) *ConditionBuilder
- func (cb *ConditionBuilder) OrGroupStart() *ConditionBuilder
- func (cb *ConditionBuilder) OrIf(check func() bool, args ...any) *ConditionBuilder
- type ContextExtractor
- type ContextKey
- type Converter
- func (c *Converter[T]) GenCreateRequest() any
- func (c *Converter[T]) GenPatchRequest() any
- func (c *Converter[T]) GenResponse() any
- func (c *Converter[T]) GenUpdateRequest() any
- func (c *Converter[T]) GetCreateType() reflect.Type
- func (c *Converter[T]) GetPatchType() reflect.Type
- func (c *Converter[T]) GetResponseType() reflect.Type
- func (c *Converter[T]) GetUpdateType() reflect.Type
- func (c *Converter[T]) HasCreateType() bool
- func (c *Converter[T]) HasPatchType() bool
- func (c *Converter[T]) HasResponseType() bool
- func (c *Converter[T]) HasUpdateType() bool
- func (c *Converter[T]) NewModelInstance() T
- func (c *Converter[T]) ToExistingModel(input any, model *T) error
- func (c *Converter[T]) ToListResponse(models []T) ([]any, error)
- func (c *Converter[T]) ToModel(input any) (T, error)
- func (c *Converter[T]) ToResponse(model T) (any, error)
- type DummyCache
- type Entity
- type EntityService
- type FilterScope
- type GeneralService
- type GeneralServiceImpl
- func (s *GeneralServiceImpl) GetAuditService() AuditService
- func (s *GeneralServiceImpl) GetCacheService() CacheService
- func (s *GeneralServiceImpl) GetContextExtractor() ContextExtractor
- func (s *GeneralServiceImpl) GetLogger() *slog.Logger
- func (s *GeneralServiceImpl) GetServiceName() string
- func (s *GeneralServiceImpl) GetServiceOptions() *ServiceOptions
- func (s *GeneralServiceImpl) GetTracer() Tracer
- type ID
- type InMemCache
- type M
- type NoopTracer
- type Operator
- type OptionKey
- type QueryMap
- type QueryOption
- func WithConditionBuilder(qb *ConditionBuilder) QueryOption
- func WithFilter(filters map[string]any) QueryOption
- func WithFilterFrom[T any](queryMap QueryMap) QueryOption
- func WithOffsetLimit(offset, limit int) QueryOption
- func WithOrder(field string, desc bool) QueryOption
- func WithOrderFrom(queryMap QueryMap) QueryOption
- func WithPagination(page, size int) QueryOption
- func WithPaginationFrom(queryMap QueryMap) QueryOption
- func WithWhere(condition string, args ...any) QueryOption
- func WithWhereArgs(args ...any) QueryOption
- type SQLBuilder
- func (b *SQLBuilder) ApplyFilter(filters map[string]any) *SQLBuilder
- func (b *SQLBuilder) Build() (string, []any)
- func (b *SQLBuilder) DistinctOn(columns ...string) *SQLBuilder
- func (b *SQLBuilder) From(fromPart string, args ...any) *SQLBuilder
- func (b *SQLBuilder) GroupBy(columns ...string) *SQLBuilder
- func (b *SQLBuilder) OrderBy(columns ...string) *SQLBuilder
- func (b *SQLBuilder) Select(selectPart string, args ...any) *SQLBuilder
- func (b *SQLBuilder) Where(condition string, args ...any) *SQLBuilder
- func (b *SQLBuilder) With(name string, query string, args ...any) *SQLBuilder
- func (b *SQLBuilder) WithBuilder(name string, builder *SQLBuilder) *SQLBuilder
- type ScopeAttribute
- type ServiceOptions
- type Span
- type Tracer
Constants ¶
const ( // DefaultPageSize is the default number of items returned per page in list queries. DefaultPageSize = 25 // MaxPageSize is the maximum number of items allowed per page in list queries. MaxPageSize = 1000 // MaxExportSize is the maximum number of items allowed in a single export operation. MaxExportSize = 1000 )
Variables ¶
var ( // ErrCacheMiss indicates the requested key was not found in the cache. ErrCacheMiss = errors.New("cache miss") // ErrEntityNil indicates that a nil entity was passed where a non-nil value is required. ErrEntityNil = errors.New("entity is nil") // ErrEntityAlreadyDeleted indicates an attempt to delete an entity that has already been soft-deleted. ErrEntityAlreadyDeleted = errors.New("entity already deleted") // ErrNoRowsDeleted indicates that a delete operation affected zero rows. ErrNoRowsDeleted = errors.New("no rows deleted") // ErrConcurrentModification indicates an optimistic locking conflict // where the entity was modified by another transaction. ErrConcurrentModification = errors.New("concurrent modification detected") // ErrPermissionDenied indicates that the caller lacks permission for the requested operation. ErrPermissionDenied = errors.New("permission denied") // ErrFieldNotFound indicates that the specified field does not exist on the entity. ErrFieldNotFound = errors.New("field not found") )
Functions ¶
func ArgsToMap ¶
ArgsToMap converts variadic key-value pairs to a map. Keys must be strings; non-string keys are silently skipped. If the number of arguments is odd, an empty map is returned and a warning is logged.
Usage:
ArgsToMap("name", "john", "age", 18) -> map[string]any{"name": "john", "age": 18}
func BuildFilters ¶
func BuildFilters(queryMap QueryMap, scopes []FilterScope) map[string]any
BuildFilters converts a QueryMap to a filter map based on FilterScopes. The returned map uses column names as keys and can be used with WithFilter or SQLBuilder.ApplyFilter.
func BuildFiltersForType ¶
BuildFiltersForType builds a filter map for a given struct type using its ent tag filter scopes and the provided query parameters.
func Copy ¶
Copy copies fields from src to dst using the copier library. Returns an error instead of panicking on failure.
func CreateTypeFromScopeTag ¶
CreateTypeFromScopeTag creates a new reflect.Type based on the model and scope. It filters fields by scope tag, handles embedded structs, and caches the result for thread-safe reuse.
func ExecutePaginatedQuery ¶
func ExecutePaginatedQuery[T any]( ctx context.Context, db *gorm.DB, qb *SQLBuilder, offset, size int, ) (int64, []T, error)
ExecutePaginatedQuery is a generic paginated query executor. It runs a count query to determine the total number of matching records, then fetches the requested page using LIMIT/OFFSET.
func GetDbFieldName ¶
func GetDbFieldName(field reflect.StructField) string
GetDbFieldName extracts the database column name from a struct field. It checks the gorm tag first (for column: directive), then falls back to the json tag, and finally converts the Go field name to snake_case.
func GetDbFields ¶
GetDbFields extracts all database field names from a struct, including fields from embedded structs.
func GetJSONName ¶
func GetJSONName(field reflect.StructField) string
GetJSONName returns the JSON field name for a struct field. It reads the "json" struct tag; if absent or set to "-", it falls back to converting the Go field name to snake_case.
func GetObjectCache ¶
func GetObjectCache[T any](cache CacheService, key string, dest *T) error
GetObjectCache retrieves a JSON-serialized object from the cache and unmarshals it into dest.
func GetOrDefault ¶
func GetOrDefault[T comparable](value, defaultValue T) T
GetOrDefault returns value if it is not the zero value for its type; otherwise it returns defaultValue.
func GetScopeAttributeValue ¶
GetScopeAttributeValue returns the value of a specific attribute in the given scope. The second return value indicates whether the attribute was found.
func GetScopeFields ¶
GetScopeFields extracts the database field names for fields that have the specified scope.
func HasScopeAttribute ¶
HasScopeAttribute checks whether a specific attribute exists in the given scope.
func InitIDGenerator ¶
InitIDGenerator initializes the snowflake node with the given nodeID (0-1023). It is safe to call multiple times; only the first call takes effect. If not called before NewID, the generator auto-initializes with node 0.
func IsEmptyStructType ¶
IsEmptyStructType checks if the given reflect.Type represents an empty struct (zero fields).
func IsInvalidID ¶
IsInvalidID reports whether the given value represents an invalid (non-positive) ID. Supported input types: ID, int64, string.
func IsNil ¶
IsNil checks whether the given value is nil or a nil-valued nillable type (pointer, map, slice, channel, function, or interface).
func IsValidColumnName ¶
IsValidColumnName checks whether a string is a safe SQL column/field name. Prevents SQL injection via field name interpolation.
func NewInstance ¶
func NewInstance[T any]() T
NewInstance creates a new instance of the generic type T. If T is a pointer type, it allocates and returns a pointer to a new zero value. If T is a value type, it returns a new zero value.
func NotEmpty ¶
NotEmpty returns a check function that returns true when the string value is non-empty.
func ParseIntOrDefault ¶
ParseIntOrDefault converts a string to int, returning defaultVal if parsing fails.
func SetObjectCache ¶
func SetObjectCache(cache CacheService, key string, value any) error
SetObjectCache stores an object in the cache by marshaling it to JSON, using the default expiration.
func SetObjectCacheExp ¶
SetObjectCacheExp stores an object in the cache with a specific TTL. It requires a CacheServiceWithExp implementation; otherwise it falls back to Set.
func ToSnakeCase ¶
ToSnakeCase converts PascalCase to snake_case, handling acronyms correctly. e.g. "APIKey" -> "api_key", "ProofOfDelivery" -> "proof_of_delivery".
Types ¶
type ActorInfo ¶
type ActorInfo struct {
ActorType ActorType
ActorID ID
IdentityID ID
IsAdmin bool
IPAddress string
UserAgent string
}
ActorInfo holds the identity and metadata of the actor performing an operation. It is extracted from context.Context by a ContextExtractor and used for access control and audit logging.
type ActorType ¶
type ActorType string
ActorType identifies the type of actor performing an audited operation.
type AuditLogEvent ¶
type AuditLogEvent struct {
ActorType ActorType
ActorID ID
IdentityID ID
ResourceType string
ResourceID ID
Action string
Status string
Details map[string]any
IPAddress string
UserAgent string
ErrorMessage string
}
AuditLogEvent represents a single auditable action with contextual metadata.
func NewAuditLogEvent ¶
func NewAuditLogEvent(opts ...AuditLogEventOption) *AuditLogEvent
NewAuditLogEvent creates a new AuditLogEvent with the given functional options applied.
func (AuditLogEvent) ToMap ¶
func (e AuditLogEvent) ToMap() map[string]any
ToMap converts the event to a flat map, suitable for structured logging or storage.
type AuditLogEventOption ¶
type AuditLogEventOption func(*AuditLogEvent)
AuditLogEventOption is a functional option for configuring an AuditLogEvent.
func WithAction ¶
func WithAction(action string) AuditLogEventOption
WithAction sets the action name (e.g., "create", "delete") on the audit event.
func WithActorID ¶
func WithActorID(actorID ID) AuditLogEventOption
WithActorID sets the actor ID on the audit event.
func WithActorType ¶
func WithActorType(actorType ActorType) AuditLogEventOption
WithActorType sets the actor type on the audit event.
func WithDetails ¶
func WithDetails(details map[string]any) AuditLogEventOption
WithDetails sets the additional details map on the audit event.
func WithErrorMessage ¶
func WithErrorMessage(errorMessage string) AuditLogEventOption
WithErrorMessage sets the error message on the audit event, typically used when the audited operation failed.
func WithIPAddress ¶
func WithIPAddress(ipAddress string) AuditLogEventOption
WithIPAddress sets the client IP address on the audit event.
func WithIdentityID ¶
func WithIdentityID(identityID ID) AuditLogEventOption
WithIdentityID sets the identity ID on the audit event.
func WithResourceID ¶
func WithResourceID(resourceID ID) AuditLogEventOption
WithResourceID sets the resource ID on the audit event.
func WithResourceType ¶
func WithResourceType(resourceType string) AuditLogEventOption
WithResourceType sets the resource type (e.g., "user", "device") on the audit event.
func WithStatus ¶
func WithStatus(status string) AuditLogEventOption
WithStatus sets the status (e.g., "success", "failure") on the audit event.
func WithUserAgent ¶
func WithUserAgent(userAgent string) AuditLogEventOption
WithUserAgent sets the client user agent string on the audit event.
type AuditService ¶
type AuditService interface {
LogEvent(ctx context.Context, entry *AuditLogEvent)
}
AuditService records audit log events for entity operations.
func NewDummyAuditService ¶
func NewDummyAuditService() AuditService
NewDummyAuditService creates an AuditService that silently discards all audit events. Useful for testing or when audit logging is not needed.
type BaseEntity ¶
type BaseEntity struct {
ID ID `json:"id" gorm:"primaryKey;<-:create" ent:"scope=response,filter"`
CreatedAt time.Time `json:"created_at" gorm:"<-:create" ent:"scope=response,filter"`
UpdatedAt time.Time `json:"updated_at" ent:"scope=response,filter"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;<-:update"`
Revision int `json:"-" gorm:"default:0"`
}
BaseEntity provides a default implementation of the Entity interface. Embed this struct in domain models to get standard fields and behavior.
func (*BaseEntity) AfterCreate ¶
func (e *BaseEntity) AfterCreate(tx *gorm.DB) error
AfterCreate is a GORM hook called after inserting a new record.
func (*BaseEntity) AfterDelete ¶
func (e *BaseEntity) AfterDelete(tx *gorm.DB) error
AfterDelete is a GORM hook called after deleting a record.
func (*BaseEntity) AfterFind ¶
func (e *BaseEntity) AfterFind(tx *gorm.DB) error
AfterFind is a GORM hook called after querying a record.
func (*BaseEntity) AfterUpdate ¶
func (e *BaseEntity) AfterUpdate(tx *gorm.DB) error
AfterUpdate is a GORM hook called after updating a record.
func (*BaseEntity) BeforeCreate ¶
func (e *BaseEntity) BeforeCreate(tx *gorm.DB) error
BeforeCreate is a GORM hook called before inserting a new record. It initializes the entity if no valid ID is set and runs validation.
func (*BaseEntity) BeforeDelete ¶
func (e *BaseEntity) BeforeDelete(tx *gorm.DB) error
BeforeDelete is a GORM hook called before deleting a record.
func (*BaseEntity) BeforeUpdate ¶
func (e *BaseEntity) BeforeUpdate(tx *gorm.DB) error
BeforeUpdate is a GORM hook called before updating a record. It updates the timestamp and runs validation.
func (*BaseEntity) GenerateID ¶
func (e *BaseEntity) GenerateID() ID
GenerateID generates a new unique ID using the default ID generator.
func (*BaseEntity) GetCreatedAt ¶
func (e *BaseEntity) GetCreatedAt() time.Time
GetCreatedAt returns the creation timestamp.
func (*BaseEntity) GetDeletedAt ¶
func (e *BaseEntity) GetDeletedAt() *time.Time
GetDeletedAt returns the soft-delete timestamp, or nil if not deleted.
func (*BaseEntity) GetEntityName ¶
func (e *BaseEntity) GetEntityName() string
GetEntityName returns the fully-qualified type name of the entity.
func (*BaseEntity) GetRevision ¶
func (e *BaseEntity) GetRevision() int
GetRevision returns the current revision number for optimistic locking.
func (*BaseEntity) GetUpdatedAt ¶
func (e *BaseEntity) GetUpdatedAt() time.Time
GetUpdatedAt returns the last update timestamp.
func (*BaseEntity) IncrementRevision ¶
func (e *BaseEntity) IncrementRevision()
IncrementRevision increments the revision number.
func (*BaseEntity) Init ¶
func (e *BaseEntity) Init()
Init initializes the entity with a new ID and timestamps. Called automatically by BeforeCreate if no valid ID is set.
func (*BaseEntity) IsDeleted ¶
func (e *BaseEntity) IsDeleted() bool
IsDeleted returns true if the entity has been soft-deleted.
func (*BaseEntity) NewInstance ¶
func (e *BaseEntity) NewInstance() Entity
NewInstance creates a new BaseEntity instance.
func (*BaseEntity) Restore ¶
func (e *BaseEntity) Restore()
Restore clears the soft-delete marker, restoring the entity.
func (*BaseEntity) SetCreatedAt ¶
func (e *BaseEntity) SetCreatedAt(t time.Time)
SetCreatedAt sets the creation timestamp.
func (*BaseEntity) SetUpdatedAt ¶
func (e *BaseEntity) SetUpdatedAt(t time.Time)
SetUpdatedAt sets the last update timestamp.
func (*BaseEntity) SoftDelete ¶
func (e *BaseEntity) SoftDelete()
SoftDelete marks the entity as soft-deleted with the current UTC time.
func (*BaseEntity) Validate ¶
func (e *BaseEntity) Validate() error
Validate performs entity validation. Override in embedding structs for custom logic.
type CacheService ¶
type CacheService interface {
// Get retrieves the value for the given key.
// Returns ErrCacheMiss if the key does not exist or has expired.
Get(key string) (string, error)
// Set stores a value with the default expiration (5 minutes).
Set(key string, value string) error
// Delete removes the value for the given key.
Delete(key string) error
}
CacheService defines the interface for a string-based key-value cache.
type CacheServiceWithExp ¶
type CacheServiceWithExp interface {
CacheService
SetWithExp(key string, value string, exp time.Duration) error
}
CacheServiceWithExp extends CacheService with explicit expiration support.
type Condition ¶
type Condition struct {
// contains filtered or unexported fields
}
Condition represents a single SQL condition or a group of nested conditions.
type ConditionBuilder ¶
type ConditionBuilder struct {
// contains filtered or unexported fields
}
ConditionBuilder provides a fluent API for constructing complex SQL WHERE clauses with support for AND/OR operators, nested groups, and conditional inclusion.
func BuildFilterConditions ¶
func BuildFilterConditions(params QueryMap, scopes []FilterScope) *ConditionBuilder
BuildFilterConditions builds query conditions from query parameters based on filter scopes. It returns a ConditionBuilder that can be used with WithConditionBuilder to apply the conditions to a GORM query.
For multi-value parameters, an IN clause is generated. For single string values, wildcards (* and ?) are converted to SQL LIKE patterns. Boolean, integer, unsigned integer, and float types are parsed and matched exactly.
func NewConditionBuilder ¶
func NewConditionBuilder() *ConditionBuilder
NewConditionBuilder creates a new ConditionBuilder with an AND root group.
func ParseAndBuildFilters ¶
func ParseAndBuildFilters[T any](params QueryMap) *ConditionBuilder
ParseAndBuildFilters combines parsing filter scopes from a type and building conditions from query parameters in a single call.
func (*ConditionBuilder) And ¶
func (cb *ConditionBuilder) And(args ...any) *ConditionBuilder
And adds a condition joined with the AND operator.
func (*ConditionBuilder) AndIf ¶
func (cb *ConditionBuilder) AndIf(check func() bool, args ...any) *ConditionBuilder
AndIf adds an AND condition only when the check function returns true.
func (*ConditionBuilder) Build ¶
func (cb *ConditionBuilder) Build() (string, []any)
Build generates the final SQL WHERE clause and its arguments. Returns an empty string and nil args if no conditions have been added.
func (*ConditionBuilder) Condition ¶
func (cb *ConditionBuilder) Condition(args ...any) *ConditionBuilder
Condition adds one or more conditions without specifying an operator. When multiple pairs of (query, args) are provided, they are each added as AND conditions. A trailing single argument is also added as an AND condition.
func (*ConditionBuilder) ConditionIf ¶
func (cb *ConditionBuilder) ConditionIf(check func() bool, args ...any) *ConditionBuilder
ConditionIf adds a condition only when the check function returns true.
func (*ConditionBuilder) GroupEnd ¶
func (cb *ConditionBuilder) GroupEnd() *ConditionBuilder
GroupEnd closes the current nested group and returns to the parent scope.
func (*ConditionBuilder) GroupStart ¶
func (cb *ConditionBuilder) GroupStart() *ConditionBuilder
GroupStart begins a new nested AND group. All subsequent conditions are added to this group until GroupEnd is called.
func (*ConditionBuilder) HasConditions ¶
func (cb *ConditionBuilder) HasConditions() bool
HasConditions returns true if the builder contains at least one condition.
func (*ConditionBuilder) IsEmpty ¶
func (cb *ConditionBuilder) IsEmpty() bool
IsEmpty returns true if the builder has no conditions.
func (*ConditionBuilder) Or ¶
func (cb *ConditionBuilder) Or(args ...any) *ConditionBuilder
Or adds a condition joined with the OR operator.
func (*ConditionBuilder) OrGroupStart ¶
func (cb *ConditionBuilder) OrGroupStart() *ConditionBuilder
OrGroupStart begins a new nested OR group. All subsequent conditions are added to this group until GroupEnd is called.
func (*ConditionBuilder) OrIf ¶
func (cb *ConditionBuilder) OrIf(check func() bool, args ...any) *ConditionBuilder
OrIf adds an OR condition only when the check function returns true.
type ContextExtractor ¶
ContextExtractor extracts actor information from a request context. Implement this interface to customize how entigo resolves the current user, admin status, IP address, and other metadata from your application's context.
Inject via ServiceOptions:
opts.With(entigo.OptionKeyContextExtractor, myExtractor)
type ContextKey ¶
type ContextKey string
ContextKey is a typed key for storing and retrieving values from context.Context. Using a distinct type prevents collisions with keys from other packages. Consumers can define their own domain-specific keys using this type.
const ( CtxKeyUserID ContextKey = "entigo.user_id" CtxKeyIdentityID ContextKey = "entigo.identity_id" CtxKeyApiKeyID ContextKey = "entigo.api_key_id" CtxKeyIsAdmin ContextKey = "entigo.is_admin" CtxKeyRealIP ContextKey = "entigo.real_ip" CtxKeyUserAgent ContextKey = "entigo.user_agent" CtxKeyClientIP ContextKey = "entigo.client_ip" )
Built-in context key constants. These are used by the defaultContextExtractor and the ginx.RequireContext helper. If you provide a custom ContextExtractor, you do not need to use these keys.
type Converter ¶
type Converter[T any] struct { // contains filtered or unexported fields }
Converter handles conversion between DTOs and models using reflection. It pre-computes the response, create, update, and patch types from the model's ent scope tags for efficient runtime DTO generation.
func NewConverter ¶
NewConverter creates a new Converter instance with pre-computed types derived from the model's ent scope tags.
func (*Converter[T]) GenCreateRequest ¶
GenCreateRequest generates a new create request DTO instance.
func (*Converter[T]) GenPatchRequest ¶
GenPatchRequest generates a new patch request DTO instance. Patch DTOs use pointer fields to distinguish between zero values and absent fields.
func (*Converter[T]) GenResponse ¶
GenResponse generates a new response DTO instance.
func (*Converter[T]) GenUpdateRequest ¶
GenUpdateRequest generates a new update request DTO instance.
func (*Converter[T]) GetCreateType ¶
GetCreateType returns the pre-computed create struct type.
func (*Converter[T]) GetPatchType ¶
GetPatchType returns the pre-computed patch struct type.
func (*Converter[T]) GetResponseType ¶
GetResponseType returns the pre-computed response struct type.
func (*Converter[T]) GetUpdateType ¶
GetUpdateType returns the pre-computed update struct type.
func (*Converter[T]) HasCreateType ¶
HasCreateType returns true if the model has fields tagged with the "create" scope.
func (*Converter[T]) HasPatchType ¶
HasPatchType returns true if the model has fields tagged with the "patch" scope.
func (*Converter[T]) HasResponseType ¶
HasResponseType returns true if the model has fields tagged with the "response" scope.
func (*Converter[T]) HasUpdateType ¶
HasUpdateType returns true if the model has fields tagged with the "update" scope.
func (*Converter[T]) NewModelInstance ¶
func (c *Converter[T]) NewModelInstance() T
NewModelInstance creates a new zero-value instance of the model type T.
func (*Converter[T]) ToExistingModel ¶
ToExistingModel converts input data into an existing model instance.
func (*Converter[T]) ToListResponse ¶
ToListResponse converts a slice of models to a slice of response DTOs.
func (*Converter[T]) ToResponse ¶
ToResponse converts a model to a response DTO.
type DummyCache ¶
type DummyCache struct{}
DummyCache is a no-operation cache that never stores anything. Get always returns ErrCacheMiss; Set and Delete are silent no-ops.
func (*DummyCache) Delete ¶
func (c *DummyCache) Delete(key string) error
type Entity ¶
type Entity interface {
NewInstance() Entity
GenerateID() ID
GetEntityName() string
GetID() ID
SetID(id ID)
GetCreatedAt() time.Time
SetCreatedAt(t time.Time)
GetUpdatedAt() time.Time
SetUpdatedAt(t time.Time)
GetDeletedAt() *time.Time
IsDeleted() bool
SoftDelete()
Restore()
Validate() error
BeforeCreate(tx *gorm.DB) error
AfterCreate(tx *gorm.DB) error
BeforeUpdate(tx *gorm.DB) error
AfterUpdate(tx *gorm.DB) error
BeforeDelete(tx *gorm.DB) error
AfterDelete(tx *gorm.DB) error
AfterFind(tx *gorm.DB) error
GetRevision() int
IncrementRevision()
}
Entity defines the interface for all database entities. It provides lifecycle hooks, soft delete support, optimistic locking, and standard accessor methods for common fields.
type EntityService ¶
type EntityService[T Entity] interface { GeneralService GetZeroValue() T NewModelInstance() T GetEntityName() string GetDB(ctx context.Context) *gorm.DB GetReplicaDB(ctx context.Context) *gorm.DB GetSchema() (*schema.Schema, error) Create(ctx context.Context, entity T) error GetOrCreate(ctx context.Context, entity T) (T, error) Upsert(ctx context.Context, entity T, keyColumn string, keyValue any) error GetByID(ctx context.Context, id ID) (T, error) Update(ctx context.Context, entity T, fieldsToUpdate ...string) error Patch(ctx context.Context, id ID, data any) error Delete(ctx context.Context, id ID) error List(ctx context.Context, opts ...QueryOption) ([]T, error) Exec(ctx context.Context, sql string, values ...any) error ExecWithDB(db *gorm.DB, sql string, values ...any) error Query(ctx context.Context, opts ...QueryOption) ([]T, error) QueryFirst(ctx context.Context, opts ...QueryOption) (T, error) QueryWithDB(db *gorm.DB, opts ...QueryOption) ([]T, error) QueryFirstWithDB(db *gorm.DB, opts ...QueryOption) (T, error) Count(ctx context.Context, opts ...QueryOption) (int64, error) WithTransaction(ctx context.Context, fn func(txCtx context.Context, txDb *gorm.DB) error) error IsZeroValue(entity T) bool InvalidCache(ctx context.Context, id ID) UpdateCache(ctx context.Context, entity T) ValidateAccessibleAsUser(ctx context.Context, userID ID) error LogAuditEvent(ctx context.Context, entry *AuditLogEvent) NewAuditLogEvent(ctx context.Context, action string, id ID, data map[string]any) *AuditLogEvent NewAuditLogEventExtra(ctx context.Context, opts ...AuditLogEventOption) *AuditLogEvent }
EntityService defines the generic service interface for CRUD operations on entities.
Usage:
service := NewEntityService[*YourEntityType](options)
// Create
entity := &YourEntityType{...}
err := service.Create(ctx, entity)
// Read
entity, err := service.GetByID(ctx, id)
// Update
entity.SomeField = newValue
err := service.Update(ctx, entity)
// Delete
err := service.Delete(ctx, id)
// Complex query
entities, err := service.Query(ctx,
WithWhere("field = ?", value),
WithOrder("created_at", true),
WithPagination(1, 10),
)
// Transaction
err := service.WithTransaction(ctx, func(txCtx context.Context, txDb *gorm.DB) error {
// ... multiple operations ...
return nil
})
func NewEntityService ¶
func NewEntityService[T Entity](options *ServiceOptions) EntityService[T]
NewEntityService creates a new EntityService for the given entity type. The options context map must contain OptionKeyDB and OptionKeyReplicaDB with *gorm.DB values.
type FilterScope ¶
type FilterScope struct {
Field string // struct field name
QueryName string // query parameter name (from tag or JSON name)
ColumnName string // database column name (from tag or JSON name)
Type reflect.Type // field type for value parsing
}
FilterScope describes a filterable field extracted from struct tags.
func ParseFilterScopes ¶
func ParseFilterScopes(t reflect.Type) []FilterScope
ParseFilterScopes parses all filter scopes from a struct type, including nested/embedded structs. It inspects ent struct tags to find fields with the "filter" scope and extracts query and column name mappings.
type GeneralService ¶
type GeneralService interface {
GetServiceName() string
GetServiceOptions() *ServiceOptions
GetTracer() Tracer
GetCacheService() CacheService
GetAuditService() AuditService
GetContextExtractor() ContextExtractor
GetLogger() *slog.Logger
}
GeneralService defines the common interface shared by all services, providing access to the logger, tracer, cache, audit, context extractor, and service options.
type GeneralServiceImpl ¶
GeneralServiceImpl provides a default implementation of GeneralService. Embed this in concrete service types to inherit logging, tracing, caching, and audit capabilities.
func NewGeneralService ¶
func NewGeneralService(options *ServiceOptions, logger ...*slog.Logger) *GeneralServiceImpl
NewGeneralService creates a GeneralServiceImpl from the given options. Dependencies (tracer, cache, audit) are extracted from the options context map with safe fallbacks: NoopTracer, DummyCache, and DummyAuditService. An optional logger can be passed; otherwise slog.Default() is used.
func (*GeneralServiceImpl) GetAuditService ¶
func (s *GeneralServiceImpl) GetAuditService() AuditService
GetAuditService returns the audit logging service.
func (*GeneralServiceImpl) GetCacheService ¶
func (s *GeneralServiceImpl) GetCacheService() CacheService
GetCacheService returns the cache service.
func (*GeneralServiceImpl) GetContextExtractor ¶
func (s *GeneralServiceImpl) GetContextExtractor() ContextExtractor
GetContextExtractor returns the context extractor used to resolve actor info.
func (*GeneralServiceImpl) GetLogger ¶
func (s *GeneralServiceImpl) GetLogger() *slog.Logger
GetLogger returns the structured logger.
func (*GeneralServiceImpl) GetServiceName ¶
func (s *GeneralServiceImpl) GetServiceName() string
GetServiceName returns the default service name.
func (*GeneralServiceImpl) GetServiceOptions ¶
func (s *GeneralServiceImpl) GetServiceOptions() *ServiceOptions
GetServiceOptions returns the service options used to configure this service.
func (*GeneralServiceImpl) GetTracer ¶
func (s *GeneralServiceImpl) GetTracer() Tracer
GetTracer returns the tracer used for distributed tracing.
type ID ¶
type ID int64
ID is a snowflake-based unique identifier represented as a 64-bit integer. It serializes to JSON as a string to preserve precision in JavaScript clients.
func NewID ¶
func NewID() ID
NewID generates a new unique snowflake ID. If the generator has not been initialized, it auto-initializes with node 0.
func ParseID ¶
ParseID converts various types to an ID. Supported input types: ID, int, int64, string.
func (ID) MarshalJSON ¶
MarshalJSON encodes the ID as a JSON string to avoid precision loss in JavaScript.
func (*ID) UnmarshalJSON ¶
UnmarshalJSON decodes the ID from either a JSON string or a JSON number.
type InMemCache ¶
type InMemCache struct {
// contains filtered or unexported fields
}
InMemCache is a thread-safe in-memory cache with lazy TTL expiration. Expired entries are removed on read access.
func (*InMemCache) Delete ¶
func (c *InMemCache) Delete(key string) error
Delete removes a value by key.
func (*InMemCache) Get ¶
func (c *InMemCache) Get(key string) (string, error)
Get retrieves a value by key. If the entry has expired, it is lazily deleted and ErrCacheMiss is returned.
func (*InMemCache) Set ¶
func (c *InMemCache) Set(key string, value string) error
Set stores a value with the default expiration (5 minutes).
func (*InMemCache) SetWithExp ¶
SetWithExp stores a value with a specific expiration duration.
type M ¶
M is a convenient alias for map[string]any, commonly used for passing dynamic key-value data such as GORM update maps or JSON payloads.
type NoopTracer ¶
type NoopTracer struct{}
NoopTracer is a tracer that produces no-operation spans. It is useful as a default when no tracing backend is configured.
type Operator ¶
type Operator string
Operator represents logical operators used to combine conditions.
type OptionKey ¶
type OptionKey string
OptionKey identifies a named entry in the ServiceOptions context map.
const ( OptionKeyServiceContext OptionKey = "service_context" OptionKeyAudit OptionKey = "audit" OptionKeyCache OptionKey = "cache" OptionKeyDB OptionKey = "db" OptionKeyReplicaDB OptionKey = "replica_db" OptionKeyRedis OptionKey = "redis" OptionKeyTracer OptionKey = "tracer" OptionKeyContextExtractor OptionKey = "context_extractor" )
type QueryMap ¶
QueryMap is a multi-valued map typically populated from URL query parameters. Keys are parameter names and values are slices of strings supporting multi-value parameters.
func (QueryMap) Filter ¶
Filter removes all keys from the QueryMap that are not present in the provided columns slice. This is useful for restricting filter parameters to a known set of allowed columns.
type QueryOption ¶
QueryOption is a function that modifies a GORM query. It is the primary building block for composing query behavior (pagination, filtering, ordering, etc.).
func WithConditionBuilder ¶
func WithConditionBuilder(qb *ConditionBuilder) QueryOption
WithConditionBuilder creates a QueryOption from a ConditionBuilder. If the builder has no conditions, the query is returned unchanged.
func WithFilter ¶
func WithFilter(filters map[string]any) QueryOption
WithFilter creates a QueryOption from a filter map. Each key is a column name, and the value is processed through processFilter to support operator prefixes (like:, gt:, in:, etc.). Common pagination and sorting parameters are automatically ignored.
func WithFilterFrom ¶
func WithFilterFrom[T any](queryMap QueryMap) QueryOption
WithFilterFrom creates a QueryOption by parsing filter scopes from the type parameter T and building filters from the provided query map.
func WithOffsetLimit ¶
func WithOffsetLimit(offset, limit int) QueryOption
WithOffsetLimit creates a QueryOption that applies raw offset/limit pagination.
func WithOrder ¶
func WithOrder(field string, desc bool) QueryOption
WithOrder creates a QueryOption that orders by a single field. When desc is true, " DESC" is appended to the field name.
func WithOrderFrom ¶
func WithOrderFrom(queryMap QueryMap) QueryOption
WithOrderFrom creates a QueryOption for ordering based on query parameters. Supports multiple order params with format: field:desc or field:asc. Example: ?order=created_at:desc,id:desc Falls back to "id DESC" when no order parameters are provided.
func WithPagination ¶
func WithPagination(page, size int) QueryOption
WithPagination creates a QueryOption that applies page-based pagination. Pages are 1-indexed: page=1 returns the first `size` records.
func WithPaginationFrom ¶
func WithPaginationFrom(queryMap QueryMap) QueryOption
WithPaginationFrom creates a pagination QueryOption from query parameters. Supports "page" and "size" parameters (e.g., ?page=1&size=10). Defaults to page=1, size=DefaultPageSize. Size is capped at MaxPageSize.
func WithWhere ¶
func WithWhere(condition string, args ...any) QueryOption
WithWhere creates a QueryOption that adds a WHERE condition with explicit condition string and arguments.
func WithWhereArgs ¶
func WithWhereArgs(args ...any) QueryOption
WithWhereArgs creates a QueryOption that adds a WHERE condition using variadic arguments. The first argument is the condition (string or struct), and the rest are bind parameters.
type SQLBuilder ¶
type SQLBuilder struct {
// contains filtered or unexported fields
}
SQLBuilder provides a fluent API for constructing complex SQL queries with support for CTEs, DISTINCT ON, subqueries, and filter application. Arguments are stored per-section and concatenated in SQL clause order by Build().
func NewSQLBuilder ¶
func NewSQLBuilder() *SQLBuilder
NewSQLBuilder creates a new SQLBuilder instance with pre-allocated slices.
func (*SQLBuilder) ApplyFilter ¶
func (b *SQLBuilder) ApplyFilter(filters map[string]any) *SQLBuilder
ApplyFilter applies a filter map to the query builder. Each entry is processed through processFilter to support operator prefixes.
func (*SQLBuilder) Build ¶
func (b *SQLBuilder) Build() (string, []any)
Build generates the final SQL query string and its arguments. Arguments are collected in SQL clause order: WITH, SELECT, FROM, WHERE.
func (*SQLBuilder) DistinctOn ¶
func (b *SQLBuilder) DistinctOn(columns ...string) *SQLBuilder
DistinctOn adds DISTINCT ON columns to the query (PostgreSQL-specific).
func (*SQLBuilder) From ¶
func (b *SQLBuilder) From(fromPart string, args ...any) *SQLBuilder
From sets the FROM part of the query with optional arguments for subqueries or parameterized table expressions.
func (*SQLBuilder) GroupBy ¶
func (b *SQLBuilder) GroupBy(columns ...string) *SQLBuilder
GroupBy adds columns to the GROUP BY clause.
func (*SQLBuilder) OrderBy ¶
func (b *SQLBuilder) OrderBy(columns ...string) *SQLBuilder
OrderBy adds columns to the ORDER BY clause.
func (*SQLBuilder) Select ¶
func (b *SQLBuilder) Select(selectPart string, args ...any) *SQLBuilder
Select sets the SELECT part of the query with optional arguments for parameterized expressions.
func (*SQLBuilder) Where ¶
func (b *SQLBuilder) Where(condition string, args ...any) *SQLBuilder
Where adds a WHERE condition with arguments. Multiple calls produce conditions joined with AND.
func (*SQLBuilder) With ¶
func (b *SQLBuilder) With(name string, query string, args ...any) *SQLBuilder
With adds a CTE (Common Table Expression) with a raw query string and optional arguments.
func (*SQLBuilder) WithBuilder ¶
func (b *SQLBuilder) WithBuilder(name string, builder *SQLBuilder) *SQLBuilder
WithBuilder adds a CTE whose query is built from another SQLBuilder.
type ScopeAttribute ¶
ScopeAttribute represents a parsed attribute from an ent scope tag.
Example:
type User struct {
entigo.BaseEntity
Name string `json:"name" ent:"scope=update(required,max=100),sort(default=asc)"`
Email string `json:"email" ent:"scope=update(readonly,format=email)"`
Status string `json:"status" ent:"scope=update(readonly,default=active),filter(name=status)"`
Score int `json:"score" ent:"scope=update(min=0,max=100)"`
}
func GetScopeAndAttributes ¶
func GetScopeAndAttributes(tag, scopeName string) (bool, []ScopeAttribute)
GetScopeAndAttributes returns whether the given tag contains the specified scope, and if so, returns its parsed attributes.
func ParseScopeAttributes ¶
func ParseScopeAttributes(scopeStr string) []ScopeAttribute
ParseScopeAttributes parses the attributes within a scope's parentheses.
Valueless attribute:
scope=update(readonly) scope=update(required,readonly)
Valued attribute:
scope=update(max=100) scope=update(type=json,max=100)
Mixed attributes:
scope=update(readonly,max=100,min=0) scope=update(required,type=string,max=100)
type ServiceOptions ¶
ServiceOptions holds configuration for entity services, including debug flags and a key-value context map for injecting dependencies like DB connections, cache, tracer, and audit service.
func NewServiceOptions ¶
func NewServiceOptions() *ServiceOptions
NewServiceOptions creates a new ServiceOptions with an empty context map.
func (*ServiceOptions) Clone ¶
func (s *ServiceOptions) Clone() *ServiceOptions
Clone creates a deep copy of the ServiceOptions, including the context map.
func (*ServiceOptions) Get ¶
func (s *ServiceOptions) Get(key OptionKey) any
Get retrieves the value for the given key from the context map.
func (*ServiceOptions) With ¶
func (s *ServiceOptions) With(key OptionKey, value any) *ServiceOptions
With sets a key-value pair in the context map and returns the receiver for method chaining.