Files
ent/ent.go
2024-07-28 19:00:47 +03:00

581 lines
18 KiB
Go

// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
// Package ent is the interface between end-user schemas and entc (ent codegen).
package ent
import (
"context"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
type (
// The Interface type describes the requirements for an exported type defined in the schema package.
// It functions as the interface between the user's schema types and codegen loader.
// Users should use the Schema type for embedding as follows:
//
// type T struct {
// ent.Schema
// }
//
Interface interface {
// Type is a dummy method, that is used in edge declaration.
//
// The Type method should be used as follows:
//
// type S struct { ent.Schema }
//
// type T struct { ent.Schema }
//
// func (T) Edges() []ent.Edge {
// return []ent.Edge{
// edge.To("S", S.Type),
// }
// }
//
Type()
// Fields returns the fields of the schema.
Fields() []Field
// Edges returns the edges of the schema.
Edges() []Edge
// Indexes returns the indexes of the schema.
Indexes() []Index
// Config returns an optional config for the schema.
//
// Deprecated: the Config method predates the Annotations method, and it
// is planned be removed in v0.5.0. New code should use Annotations instead.
//
// func (T) Annotations() []schema.Annotation {
// return []schema.Annotation{
// entsql.Annotation{Table: "Name"},
// }
// }
//
Config() Config
// Mixin returns an optional list of Mixin to extends
// the schema.
Mixin() []Mixin
// Hooks returns an optional list of Hook to apply on
// the executed mutations.
Hooks() []Hook
// Interceptors returns an optional list of Interceptor
// to apply on the executed queries.
Interceptors() []Interceptor
// Policy returns the privacy policy of the schema.
Policy() Policy
// Annotations returns a list of schema annotations to be used by
// codegen extensions.
Annotations() []schema.Annotation
}
// A Field interface returns a field descriptor for vertex fields/properties.
// The usage for the interface is as follows:
//
// func (T) Fields() []ent.Field {
// return []ent.Field{
// field.Int("int"),
// }
// }
//
Field interface {
Descriptor() *field.Descriptor
}
// An Edge interface returns an edge descriptor for vertex edges.
// The usage for the interface is as follows:
//
// func (T) Edges() []ent.Edge {
// return []ent.Edge{
// edge.To("S", S.Type),
// }
// }
//
Edge interface {
Descriptor() *edge.Descriptor
}
// An Index interface returns an index descriptor for vertex indexes.
// The usage for the interface is as follows:
//
// func (T) Indexes() []ent.Index {
// return []ent.Index{
// index.Fields("f1", "f2").
// Unique(),
// }
// }
//
Index interface {
Descriptor() *index.Descriptor
}
// A Config structure is used to configure an entity schema.
// The usage of this structure is as follows:
//
// func (T) Config() ent.Config {
// return ent.Config{
// Table: "Name",
// }
// }
//
// Deprecated: the Config object predates the schema.Annotation method and it
// is planned to be removed in future versions. New code should use Annotations
// instead.
//
// func (T) Annotations() []schema.Annotation {
// return []schema.Annotation{
// entsql.Annotation{Table: "Name"},
// }
// }
//
Config struct {
// A Table is an optional table name defined for the schema.
Table string
}
// The Mixin type describes a set of methods that can extend
// other methods in the schema without calling them directly.
//
// type TimeMixin struct {}
//
// func (TimeMixin) Fields() []ent.Field {
// return []ent.Field{
// field.Time("created_at").
// Immutable().
// Default(time.Now),
// field.Time("updated_at").
// Default(time.Now).
// UpdateDefault(time.Now),
// }
// }
//
// type T struct {
// ent.Schema
// }
//
// func(T) Mixin() []ent.Mixin {
// return []ent.Mixin{
// TimeMixin{},
// }
// }
//
Mixin interface {
// Fields returns a slice of fields to add to the schema.
Fields() []Field
// Edges returns a slice of edges to add to the schema.
Edges() []Edge
// Indexes returns a slice of indexes to add to the schema.
Indexes() []Index
// Hooks returns a slice of hooks to add to the schema.
// Note that mixin hooks are executed before schema hooks.
Hooks() []Hook
// Interceptors returns a slice of interceptors to add to the schema.
// Note that mixin interceptors are executed before schema interceptors.
Interceptors() []Interceptor
// Policy returns a privacy policy to add to the schema.
// Note that mixin policy are executed before schema policy.
Policy() Policy
// Annotations returns a list of schema annotations to add
// to the schema annotations.
Annotations() []schema.Annotation
}
// The Policy type defines the privacy policy of an entity.
// The usage for the interface is as follows:
//
// type T struct {
// ent.Schema
// }
//
// func(T) Policy() ent.Policy {
// return privacy.AlwaysAllowRule()
// }
//
Policy interface {
EvalMutation(context.Context, Mutation) error
EvalQuery(context.Context, Query) error
}
// Schema is the default implementation for the schema Interface.
// It can be embedded in end-user schemas as follows:
//
// type T struct {
// ent.Schema
// }
//
Schema struct {
Interface
}
// A View only schema describes an entity that all its operations
// are limited to read-only. For example, a database view.
//
// Users that wants to define a view schema should embed the View
// struct in their schema as follows:
//
// type V struct {
// ent.View
// }
//
View struct {
Schema
}
)
// Fields of the schema.
func (Schema) Fields() []Field { return nil }
// Edges of the schema.
func (Schema) Edges() []Edge { return nil }
// Indexes of the schema.
func (Schema) Indexes() []Index { return nil }
// Config of the schema.
func (Schema) Config() Config { return Config{} }
// Mixin of the schema.
func (Schema) Mixin() []Mixin { return nil }
// Hooks of the schema.
func (Schema) Hooks() []Hook { return nil }
// Interceptors of the schema.
func (Schema) Interceptors() []Interceptor { return nil }
// Policy of the schema.
func (Schema) Policy() Policy { return nil }
// Annotations of the schema.
func (Schema) Annotations() []schema.Annotation { return nil }
// Viewer is an interface that wraps the view method.
// Implemented by the View struct.
type Viewer interface{ view() }
// view is a dummy method to distinguish between Schema and View.
func (View) view() {}
type (
// Value represents a dynamic value returned by mutations or queries.
Value any
// Mutation represents an operation that mutate the graph.
// For example, adding a new node, updating many, or dropping
// data. The implementation is generated by entc (ent codegen).
Mutation interface {
// Op returns the operation name generated by entc.
Op() Op
// Type returns the schema type for this mutation.
Type() string
// Fields returns all fields that were changed during
// this mutation. Note that, in order to get all numeric
// fields that were in/decremented, call AddedFields().
Fields() []string
// Field returns the value of a field with the given name.
// The second boolean value indicates that this field was
// not set, or was not defined in the schema.
Field(name string) (Value, bool)
// SetField sets the value for the given name. It returns an
// error if the field is not defined in the schema, or if the
// type mismatch the field type.
SetField(name string, value Value) error
// AddedFields returns all numeric fields that were incremented
// or decremented during this mutation.
AddedFields() []string
// AddedField returns the numeric value that was in/decremented
// from a field with the given name. The second value indicates
// that this field was not set, or was not define in the schema.
AddedField(name string) (Value, bool)
// AddField adds the value for the given name. It returns an
// error if the field is not defined in the schema, or if the
// type mismatch the field type.
AddField(name string, value Value) error
// ClearedFields returns all nullable fields that were cleared
// during this mutation.
ClearedFields() []string
// FieldCleared returns a bool indicates if this field was
// cleared in this mutation.
FieldCleared(name string) bool
// ClearField clears the value for the given name. It returns an
// error if the field is not defined in the schema.
ClearField(name string) error
// ResetField resets all changes in the mutation regarding the
// given field name. It returns an error if the field is not
// defined in the schema.
ResetField(name string) error
// AddedEdges returns all edge names that were set/added in this
// mutation.
AddedEdges() []string
// AddedIDs returns all ids (to other nodes) that were added for
// the given edge name.
AddedIDs(name string) []Value
// RemovedEdges returns all edge names that were removed in this
// mutation.
RemovedEdges() []string
// RemovedIDs returns all ids (to other nodes) that were removed for
// the given edge name.
RemovedIDs(name string) []Value
// ClearedEdges returns all edge names that were cleared in this
// mutation.
ClearedEdges() []string
// EdgeCleared returns a bool indicates if this edge was
// cleared in this mutation.
EdgeCleared(name string) bool
// ClearEdge clears the value for the given name. It returns an
// error if the edge name is not defined in the schema.
ClearEdge(name string) error
// ResetEdge resets all changes in the mutation regarding the
// given edge name. It returns an error if the edge is not
// defined in the schema.
ResetEdge(name string) error
// OldField returns the old value of the field from the database.
// An error is returned if the mutation operation is not UpdateOne,
// or the query to the database was failed.
OldField(ctx context.Context, name string) (Value, error)
}
// Mutator is the interface that wraps the Mutate method.
Mutator interface {
// Mutate apply the given mutation on the graph. The returned
// ent.Value is changing according to the mutation operation:
//
// OpCreate, the returned value is the created node (T).
// OpUpdateOne, the returned value is the updated node (T).
// OpUpdate, the returned value is the amount of updated nodes (int).
// OpDeleteOne, OpDelete, the returned value is the amount of deleted nodes (int).
//
Mutate(context.Context, Mutation) (Value, error)
}
// The MutateFunc type is an adapter to allow the use of ordinary
// function as Mutator. If f is a function with the appropriate signature,
// MutateFunc(f) is a Mutator that calls f.
MutateFunc func(context.Context, Mutation) (Value, error)
// Hook defines the "mutation middleware". A function that gets a Mutator
// and returns a Mutator. For example:
//
// hook := func(next ent.Mutator) ent.Mutator {
// return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
// fmt.Printf("Type: %s, Operation: %s, ConcreteType: %T\n", m.Type(), m.Op(), m)
// return next.Mutate(ctx, m)
// })
// }
//
Hook func(Mutator) Mutator
)
// Mutate calls f(ctx, m).
func (f MutateFunc) Mutate(ctx context.Context, m Mutation) (Value, error) {
return f(ctx, m)
}
type (
// Query represents a query builder of an entity. It is
// usually one of the following types: <T>Query.
Query any
// Querier is the interface that wraps the Query method.
// Calling Querier.Query(ent.Query) triggers the execution
// of the query.
Querier interface {
// Query runs the given query on the graph and returns its result.
Query(context.Context, Query) (Value, error)
}
// The QuerierFunc type is an adapter to allow the use of ordinary
// function as Querier. If f is a function with the appropriate signature,
// QuerierFunc(f) is a Querier that calls f.
QuerierFunc func(context.Context, Query) (Value, error)
// Interceptor defines an execution middleware for various types of Ent queries.
// Contrary to Hooks, Interceptors are implemented as interfaces, allows them to
// intercept and modify the query at different stages, providing more fine-grained
// control over its behavior. For example, see the Traverser interface.
Interceptor interface {
// Intercept is a function that gets a Querier and returns a Querier. For example:
//
// ent.InterceptFunc(func(next ent.Querier) ent.Querier {
// return ent.QuerierFunc(func(ctx context.Context, query ent.Query) (ent.Value, error) {
// // Do something before the query execution.
// value, err := next.Query(ctx, query)
// // Do something after the query execution.
// return value, err
// })
// })
//
// Note that unlike Traverse functions, which are called at each traversal stage, Intercept functions
// are invoked before the query executions. This means that using Traverse functions is a better fit
// for adding default filters, while using Intercept functions is a better fit for implementing logging
// or caching.
//
//
// client.User.Query().
// QueryGroups(). // User traverse functions applied.
// QueryPosts(). // Group traverse functions applied.
// All(ctx) // Post traverse and intercept functions applied.
//
Intercept(Querier) Querier
}
// The InterceptFunc type is an adapter to allow the use of ordinary function as Interceptor.
// If f is a function with the appropriate signature, InterceptFunc(f) is an Interceptor that calls f.
InterceptFunc func(Querier) Querier
// Traverser defines a graph-traversal middleware for various types of Ent queries.
// Contrary to Interceptors, the Traverse are executed on graph traversals before the
// query is executed. For example:
//
// ent.TraverseFunc(func(ctx context.Context, q ent.Query) error {
// // Filter out deleted pets.
// if pq, ok := q.(*gen.PetQuery); ok {
// pq.Where(pet.DeletedAtIsNil())
// }
// return nil
// })
//
// client.Pet.Query().
// QueryOwner(). // Pet traverse functions are applied and filter deleted pets.
// All(ctx) // User traverse and interceptor functions are applied.
//
Traverser interface {
Traverse(context.Context, Query) error
}
// The TraverseFunc type is an adapter to allow the use of ordinary function as Traverser.
// If f is a function with the appropriate signature, TraverseFunc(f) is a Traverser that calls f.
TraverseFunc func(context.Context, Query) error
)
// Query calls f(ctx, q).
func (f QuerierFunc) Query(ctx context.Context, q Query) (Value, error) {
return f(ctx, q)
}
// Intercept calls f(ctx, q).
func (f InterceptFunc) Intercept(next Querier) Querier {
return f(next)
}
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseFunc) Intercept(next Querier) Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseFunc) Traverse(ctx context.Context, q Query) error {
return f(ctx, q)
}
//go:generate go run golang.org/x/tools/cmd/stringer -type Op
// An Op represents a mutation operation.
type Op uint
// Mutation operations.
const (
OpCreate Op = 1 << iota // node creation.
OpUpdate // update nodes by predicate (if any).
OpUpdateOne // update one node.
OpDelete // delete nodes by predicate (if any).
OpDeleteOne // delete one node.
)
// Is reports whether o is match the given operation.
func (i Op) Is(o Op) bool { return i&o != 0 }
// List of query operations used by the codegen.
const (
OpQueryFirst = "First"
OpQueryFirstID = "FirstID"
OpQueryOnly = "Only"
OpQueryOnlyID = "OnlyID"
OpQueryAll = "All"
OpQueryIDs = "IDs"
OpQueryCount = "Count"
OpQueryExist = "Exist"
OpQueryGroupBy = "GroupBy"
OpQuerySelect = "Select"
)
type (
// QueryContext contains additional information about
// the context in which the query is executed.
QueryContext struct {
// Op defines the operation name. e.g., First, All, Count, etc.
Op string
// Type defines the query type as defined in the generated code.
Type string
// Unique indicates if the Unique modifier was set on the query and
// its value. Calling Unique(false) sets the value of Unique to false.
Unique *bool
// Limit indicates if the Limit modifier was set on the query and
// its value. Calling Limit(10) sets the value of Limit to 10.
Limit *int
// Offset indicates if the Offset modifier was set on the query and
// its value. Calling Offset(10) sets the value of Offset to 10.
Offset *int
// Fields specifies the fields that were selected in the query.
Fields []string
}
queryCtxKey struct{}
)
// NewQueryContext returns a new context with the given QueryContext attached.
func NewQueryContext(parent context.Context, c *QueryContext) context.Context {
return context.WithValue(parent, queryCtxKey{}, c)
}
// QueryFromContext returns the QueryContext value stored in ctx, if any.
func QueryFromContext(ctx context.Context) *QueryContext {
c, _ := ctx.Value(queryCtxKey{}).(*QueryContext)
return c
}
// Clone returns a deep copy of the query context.
func (q *QueryContext) Clone() *QueryContext {
c := &QueryContext{
Op: q.Op,
Type: q.Type,
Fields: append([]string(nil), q.Fields...),
}
if q.Unique != nil {
v := *q.Unique
c.Unique = &v
}
if q.Limit != nil {
v := *q.Limit
c.Limit = &v
}
if q.Offset != nil {
v := *q.Offset
c.Offset = &v
}
return c
}
// AppendFieldOnce adds the given field to the spec if it is not already present.
func (q *QueryContext) AppendFieldOnce(f string) *QueryContext {
for _, f1 := range q.Fields {
if f == f1 {
return q
}
}
q.Fields = append(q.Fields, f)
return q
}