Documentation
¶
Index ¶
- Constants
- func DefineFieldIncludeArchived() *dmodel.FieldBuilder
- func DefineFieldSearchContext() *dmodel.FieldBuilder
- func DefineFieldSearchFields() *dmodel.FieldBuilder
- func DefineFieldSearchGraph() *dmodel.FieldBuilder
- func DefineFieldSearchLanguage() *dmodel.FieldBuilder
- func DefineFieldSearchName() *dmodel.FieldBuilder
- func DefineFieldSearchPage() *dmodel.FieldBuilder
- func DefineFieldSearchSize() *dmodel.FieldBuilder
- func DeleteOneQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
- func ExistsQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
- func GetOneQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
- func InitSubModule(params InitParams) error
- func ManageAssocsSchemaBuilder() *dmodel.ModelSchemaBuilder
- func ResolveLocale(ctx corectx.Context) *model.LanguageCode
- func SearchQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
- func SetArchivedCommandSchemaBuilder() *dmodel.ModelSchemaBuilder
- func SetLocaleResolver(fn LocaleResolver)
- type BaseDynamicRepository
- type DeleteOneCommand
- type DeleteOneCommandShape
- type DynamicModelPtr
- type DynamicModelRepository
- type DynamicModelSetterPtr
- type ExistsQuery
- type ExistsQueryShape
- type ExistsResultData
- type GetOneQuery
- type InitParams
- type LocaleResolver
- type MutateResultData
- type NewBaseDynamicRepositoryFn
- type NewBaseRepoParam
- type OpResult
- type PagedResultData
- type PagingOptions
- type RepoBeforeInsertM2mFn
- type RepoCountM2mParam
- type RepoExistsM2mParam
- type RepoExistsResult
- type RepoGetOneParam
- type RepoM2mAssociation
- type RepoManageM2mParam
- type RepoSearchParam
- type SearchQuery
- type SetIsArchivedCommand
- type SingleMetaData
- type SingleResultData
- type Validatable
- type ValidationFlow
- func (this *ValidationFlow) End() (ft.ClientErrors, error)
- func (this *ValidationFlow) Start() *ValidationFlow
- func (this *ValidationFlow) StartCopy(initialErrs *ft.ClientErrors) *ValidationFlow
- func (this *ValidationFlow) Step(fn func(vErrs *ft.ClientErrors) error, ignoreValidationError ...bool) (out *ValidationFlow)
- func (this *ValidationFlow) StepS(fn func(vErrs *ft.ClientErrors, stop func()) error, ...) (out *ValidationFlow)
Constants ¶
const DefaultSearchName = "default"
Variables ¶
This section is empty.
Functions ¶
func DefineFieldIncludeArchived ¶
func DefineFieldIncludeArchived() *dmodel.FieldBuilder
DefineFieldIncludeArchived defines the "include_archived" search query field.
It deliberately carries no Default(false): ModelField.Validate materializes a non-empty default into the *bool, which would erase the nil/false distinction before crud.Search ever sees it. The public-API default is applied in crud.Search instead, so that callers going straight to the repository keep the unfiltered legacy behaviour.
func DefineFieldSearchContext ¶
func DefineFieldSearchContext() *dmodel.FieldBuilder
DefineFieldSearchContext defines the "context" search query field: the whitelisted request values SQL-computed fields may bind inside their filters. It is a free-form JSON object here; the per-definition whitelist and type conversion are enforced where the subquery compiles.
func DefineFieldSearchFields ¶
func DefineFieldSearchFields() *dmodel.FieldBuilder
func DefineFieldSearchGraph ¶
func DefineFieldSearchGraph() *dmodel.FieldBuilder
func DefineFieldSearchLanguage ¶
func DefineFieldSearchLanguage() *dmodel.FieldBuilder
DefineFieldSearchLanguage defines the "language" search query field: which translation of a LangJson column to filter and sort on.
Declared here rather than left undeclared because an undeclared field survives validation only by accident -- ValidateStruct mutates the caller's own struct, and the mapper happens not to zero what the schema did not mention. A refactor to build a fresh target would silently drop it and un-localize every search, with no compile error and no failing test.
The rule is BCP47 shape rather than an enum of the supported locales: that set lives in the essential module, which this package must not import. A well-formed locale the application ships no translations for simply matches nothing, which is the same answer it would give anyway.
func DefineFieldSearchName ¶
func DefineFieldSearchName() *dmodel.FieldBuilder
func DefineFieldSearchPage ¶
func DefineFieldSearchPage() *dmodel.FieldBuilder
func DefineFieldSearchSize ¶
func DefineFieldSearchSize() *dmodel.FieldBuilder
func DeleteOneQuerySchemaBuilder ¶
func DeleteOneQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
func ExistsQuerySchemaBuilder ¶
func ExistsQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
func GetOneQuerySchemaBuilder ¶
func GetOneQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
func InitSubModule ¶
func InitSubModule(params InitParams) error
func ManageAssocsSchemaBuilder ¶
func ManageAssocsSchemaBuilder() *dmodel.ModelSchemaBuilder
func ResolveLocale ¶
func ResolveLocale(ctx corectx.Context) *model.LanguageCode
ResolveLocale returns the acting user's effective locale, memoized for the request.
Returns nil whenever the locale cannot be established -- no resolver installed, no acting user, a failed or panicking read. Every caller must treat nil as "do not localize" rather than as an error: a list that sorts by the raw column is worse than one sorted by the reader's language, but it is a great deal better than a 500.
The memo assumes a request context is used by one goroutine at a time, which is how corectx is used throughout and what every other WithValue caller already assumes.
func SearchQuerySchemaBuilder ¶
func SearchQuerySchemaBuilder() *dmodel.ModelSchemaBuilder
func SetArchivedCommandSchemaBuilder ¶
func SetArchivedCommandSchemaBuilder() *dmodel.ModelSchemaBuilder
func SetLocaleResolver ¶
func SetLocaleResolver(fn LocaleResolver)
SetLocaleResolver installs the process-wide resolver.
Call it once, during module Init, before any request is served.
Types ¶
type BaseDynamicRepository ¶
type BaseDynamicRepository interface {
Schema() *dmodel.ModelSchema
BeginTransaction(ctx corectx.Context) (database.DbTransaction, error)
ExtractClient(ctx corectx.Context) orm.DbClient
ExecFunc(ctx corectx.Context, sqlFuncName string, sqlFuncArgs ...any) error
QueryFunc(ctx corectx.Context, sqlFuncName string, sqlFuncArgs ...any) (*sql.Rows, error)
CheckUniqueCollisions(ctx corectx.Context, data dmodel.DynamicFields) (*OpResult[[][]string], error)
CountM2m(ctx corectx.Context, param RepoCountM2mParam) (*OpResult[int], error)
DeleteOne(ctx corectx.Context, keys dmodel.DynamicFields) (*OpResult[int], error)
Exists(ctx corectx.Context, keys []dmodel.DynamicFields) (*OpResult[RepoExistsResult], error)
ExistsM2m(ctx corectx.Context, param RepoExistsM2mParam) (bool, error)
InsertBulk(ctx corectx.Context, data []dmodel.DynamicFields) (*OpResult[int], error)
GetOne(ctx corectx.Context, param RepoGetOneParam) (*OpResult[dmodel.DynamicFields], error)
Insert(ctx corectx.Context, data dmodel.DynamicFields) (*OpResult[int], error)
// ManageM2m inserts and/or deletes junction rows for a finalized many-to-many link to dest schema.
// Source and destination are identified by id.
ManageM2m(ctx corectx.Context, param RepoManageM2mParam) (*OpResult[int], error)
Search(ctx corectx.Context, param RepoSearchParam) (*OpResult[PagedResultData[dmodel.DynamicFields]], error)
Update(ctx corectx.Context, data dmodel.DynamicFields) (*OpResult[dmodel.DynamicFields], error)
}
type DeleteOneCommand ¶
func (DeleteOneCommand) GetSchema ¶
func (this DeleteOneCommand) GetSchema() *dmodel.ModelSchema
type DeleteOneCommandShape ¶
Helper type for generic type constraints.
type DynamicModelPtr ¶
type DynamicModelPtr[T any] interface { *T dmodel.DynamicModel }
type DynamicModelRepository ¶
type DynamicModelRepository interface {
BeginTransaction(ctx corectx.Context) (database.DbTransaction, error)
GetBaseRepo() BaseDynamicRepository
}
type DynamicModelSetterPtr ¶
type DynamicModelSetterPtr[T any] interface { *T dmodel.DynamicModelSetter }
type ExistsQuery ¶
type ExistsQueryShape ¶
Helper type for generic type constraints.
type ExistsResultData ¶
type GetOneQuery ¶
type InitParams ¶
type LocaleResolver ¶
type LocaleResolver func(ctx corectx.Context) *model.LanguageCode
LocaleResolver reports the effective display locale for the acting user, or nil when none can be determined.
It is a function type rather than an interface because the settings module -- the only thing that can answer this -- imports THIS package. An interface would have to name the settings types to be useful, which would close the loop into an import cycle and abort the build. A func value carries no package dependency on its provider, so the edge stays one-directional and the module that owns both ends pushes the implementation in at boot.
type MutateResultData ¶
type MutateResultData struct {
AffectedCount int `json:"affected_count"`
AffectedAt model.ModelDateTime `json:"affected_at"`
Etag model.Etag `json:"etag,omitempty"`
}
type NewBaseDynamicRepositoryFn ¶
type NewBaseDynamicRepositoryFn func(param NewBaseRepoParam) BaseDynamicRepository
type NewBaseRepoParam ¶
type NewBaseRepoParam struct {
Client orm.DbClient
ConfigSvc config.ConfigService
Logger logging.LoggerService
QueryBuilder orm.QueryBuilder
Schema *dmodel.ModelSchema
}
type OpResult ¶
type OpResult[TData any] struct { // The result data when success. It is only meaningful if HasData is true and ClientErrors is nil. // Otherwise, it could be nil or an empty struct. Data TData // Contains validation errors, business errors..., // or nil if there is no violation. ClientErrors ft.ClientErrors // Indicates whether "Data" is present (non-zero value: non-empty struct, non-empty array, etc.). // // If ClientErrors is nil but HasData is false, // it means the query is successfull but no data is found. HasData bool }
type PagedResultData ¶
type PagedResultData[T any] struct { Items []T `json:"items"` Total int `json:"total"` Page int `json:"page"` Size int `json:"size"` // List of fields determined by `View` which are requested to read. DesiredFields []string `json:"desired_fields"` // Subset of `DesiredFields` that user doesn't have permission to read. // These fields will be returned as `nil` values. User can be aware of their existence // and ask for permission to read them. MaskedFields []string `json:"masked_fields"` // The etag of the schema used to generate the response. // This is used to check if the schema has changed since the last request. // If the schema has changed, the client should fetch the new schema and update its cache. SchemaEtag string `json:"schema_etag"` }
type PagingOptions ¶
type RepoBeforeInsertM2mFn ¶
type RepoBeforeInsertM2mFn func(ctx corectx.Context, dbRecords []dmodel.DynamicFields) error
type RepoCountM2mParam ¶
RepoCountM2mParam counts junction rows for one source record on an outgoing many-to-many edge.
type RepoExistsM2mParam ¶
type RepoExistsM2mParam struct {
M2mEdge string `json:"m2m_edge"`
SrcId model.Id `json:"src_id"`
DestId *model.Id `json:"dest_id"`
}
RepoExistsM2mParam checks the junction for an outgoing many-to-many edge on the repository schema. When dest_id is omitted, null, or empty, checks that SrcId has at least one junction row; otherwise checks the (SrcId, DestId) pair.
type RepoExistsResult ¶
type RepoExistsResult struct {
Existing []dmodel.DynamicFields `json:"existing"`
NotExisting []dmodel.DynamicFields `json:"not_existing"`
}
RepoExistsResult is the raw batch existence outcome per filter map (same order as input keys).
type RepoGetOneParam ¶
type RepoGetOneParam struct {
Filter dmodel.DynamicFields
Fields []string
}
type RepoM2mAssociation ¶
type RepoM2mAssociation struct {
SrcKeys dmodel.DynamicFields
DestKeys dmodel.DynamicFields
}
RepoM2mAssociation is one row to insert into the M2M junction: source entity keys and peer entity keys.
type RepoManageM2mParam ¶
type RepoManageM2mParam struct {
DestSchemaName string
SrcId model.Id
// Field name for the source ID used to include in the error message.
SrcIdFieldForError string
// M2M edge name on the source schema.
SrcEdgeName string
AssociatedIds datastructure.Set[model.Id]
DisassociatedIds datastructure.Set[model.Id]
BeforeInsert RepoBeforeInsertM2mFn
}
type RepoSearchParam ¶
type RepoSearchParam struct {
Fields []string
Filter []dmodel.DynamicFields
Page int
Size int
Graph *dmodel.SearchGraph
Language *model.LanguageCode
// ComputedContext carries the request's whitelisted context values for SQL-computed
// fields; the query builder binds them into "${ctx.key}" filter references.
ComputedContext map[string]any
// IncludeArchived controls whether archived records take part in the search.
// It is tri-state on purpose:
// - nil: no filtering at all. This is the legacy contract that every internal lookup
// relies on, e.g. models.FindTemplateVariants which needs archived rows by design.
// Leave it nil when reusing Search to fetch a single known record.
// - false: the repository prepends "is_archived = false" to the search graph.
// crud.Search sets this when the caller omitted the query parameter, so the public
// HTTP API hides archived records by default.
// - true: no filtering; archived records are returned alongside active ones.
//
// The condition is only prepended on schemas that actually carry the is_archived column
// (it comes from the optional basemodel.ArchivableModelSchemaBuilder mixin).
//
// Note this filters the searched schema only. Nested edge columns are hydrated by
// follow-up queries that are not filtered, so requesting fields=groups.name with
// IncludeArchived=false excludes archived rows of this schema but still hydrates
// archived rows of the edge.
IncludeArchived *bool
}
type SearchQuery ¶
type SearchQuery struct {
Fields []string `json:"fields" query:"fields"`
Page int `json:"page" query:"page"`
Size int `json:"size" query:"size"`
// Optional search graph for advanced search
Graph *dmodel.SearchGraph `json:"graph" query:"graph"`
// Optional language code to filter fields with LangJson type
Language *model.LanguageCode `json:"language" query:"language"`
// Optional flag to include archived records in the result.
// When omitted, crud.Search treats it as false, so archived records are hidden by default.
// Set it to true to search archived and active records together.
IncludeArchived *bool `json:"include_archived" query:"include_archived"`
// Determines the fields to be returned in the response.
// If not specified, return all fields that user has permission on.
// Otherwise, return fields specified by the search name.
SearchName *string `json:"search_name" query:"search_name"`
// Optional whitelisted context values for SQL-computed fields (aggregate/exists/lookup)
// whose filter declares context dependencies. Each value binds a "${ctx.key}" reference;
// a computed field whose declared key is absent here fails with a validation error.
Context map[string]any `json:"context" query:"context"`
}
func (SearchQuery) GetSchema ¶
func (SearchQuery) GetSchema() *dmodel.ModelSchema
type SetIsArchivedCommand ¶
type SingleMetaData ¶
type SingleMetaData struct {
// List of fields determined by `View` which are requested to read.
DesiredFields []string `json:"desired_fields"`
// Subset of `DesiredFields` that user doesn't have permission to read.
// These fields will be returned as `nil` values. User can be aware of their existence
// and ask for permission to read them.
MaskedFields []string `json:"masked_fields"`
// The etag of the schema used to generate the response.
// This is used to check if the schema has changed since the last request.
// If the schema has changed, the client should fetch the new schema and update its cache.
SchemaEtag string `json:"schema_etag"`
}
type SingleResultData ¶
type SingleResultData[T any] struct { Item T `json:"item"` Meta SingleMetaData `json:"meta"` }
type Validatable ¶
type Validatable interface {
Validate() ft.ClientErrors
}
type ValidationFlow ¶
type ValidationFlow struct {
// contains filtered or unexported fields
}
func StartValidationFlow ¶
func StartValidationFlow(startWith ...Validatable) *ValidationFlow
func StartValidationFlowCopy ¶
func StartValidationFlowCopy(initClientErrs *ft.ClientErrors, startWith ...Validatable) *ValidationFlow
func (*ValidationFlow) End ¶
func (this *ValidationFlow) End() (ft.ClientErrors, error)
func (*ValidationFlow) Start ¶
func (this *ValidationFlow) Start() *ValidationFlow
func (*ValidationFlow) StartCopy ¶
func (this *ValidationFlow) StartCopy(initialErrs *ft.ClientErrors) *ValidationFlow
func (*ValidationFlow) Step ¶
func (this *ValidationFlow) Step(fn func(vErrs *ft.ClientErrors) error, ignoreValidationError ...bool) (out *ValidationFlow)
func (*ValidationFlow) StepS ¶
func (this *ValidationFlow) StepS(fn func(vErrs *ft.ClientErrors, stop func()) error, ignoreValidationError ...bool) (out *ValidationFlow)