snapshot of https://github.com/ent/ent.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Atlas CI Public / lint (push) Failing after 13s
Atlas CI Public / sync (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 22:12:36 +05:00
commit 4b3ad0b611
2680 changed files with 719472 additions and 0 deletions

357
entc/load/load.go Normal file
View File

@@ -0,0 +1,357 @@
// 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 load is the interface for loading an ent/schema package into a Go program.
package load
import (
"bytes"
"embed"
"errors"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"go/types"
"maps"
"os"
"os/exec"
"path/filepath"
"reflect"
"slices"
"sort"
"strconv"
"strings"
"text/template"
"time"
"entgo.io/ent"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/packages"
)
type (
// A SchemaSpec holds a serializable version of an ent.Schema
// and its Go package and module information.
SchemaSpec struct {
// Schemas defines the loaded schema descriptors.
Schemas []*Schema
// PkgPath is the package path of the loaded
// ent.Schema package.
PkgPath string
// Module defines the module information for
// the user schema package if exists.
Module *packages.Module
}
// Config holds the configuration for loading an ent/schema package.
Config struct {
// Path is the path for the schema package.
Path string
// Names are the schema names to load. Empty means all schemas in the directory.
Names []string
// BuildFlags are forwarded to the package.Config when
// loading the schema package.
BuildFlags []string
}
)
// Load loads the schemas package and build the Go plugin with this info.
func (c *Config) Load() (*SchemaSpec, error) {
spec, pos, err := c.load()
if err != nil {
return nil, fmt.Errorf("entc/load: parse schema dir: %w", err)
}
if len(c.Names) == 0 {
return nil, fmt.Errorf("entc/load: no schema found in: %s", c.Path)
}
var b bytes.Buffer
err = buildTmpl.ExecuteTemplate(&b, "main", struct {
*Config
Package string
}{
Config: c,
Package: spec.PkgPath,
})
if err != nil {
return nil, fmt.Errorf("entc/load: execute template: %w", err)
}
buf, err := format.Source(b.Bytes())
if err != nil {
return nil, fmt.Errorf("entc/load: format template: %w", err)
}
if err := os.MkdirAll(".entc", os.ModePerm); err != nil {
return nil, err
}
target := fmt.Sprintf(".entc/%s.go", filename(spec.PkgPath))
if err := os.WriteFile(target, buf, 0644); err != nil {
return nil, fmt.Errorf("entc/load: write file %s: %w", target, err)
}
defer os.RemoveAll(".entc")
out, err := gorun(target, c.BuildFlags)
if err != nil {
return nil, err
}
for _, line := range strings.Split(out, "\n") {
schema, err := UnmarshalSchema([]byte(line))
if err != nil {
return nil, fmt.Errorf("entc/load: unmarshal schema %s: %w", line, err)
}
spec.Schemas = append(spec.Schemas, schema)
}
for _, s := range spec.Schemas {
s.Pos = pos[s.Name]
}
return spec, nil
}
// entInterface holds the reflect.Type of ent.Interface.
var entInterface = reflect.TypeOf(struct{ ent.Interface }{}).Field(0).Type
// load the ent/schema info.
func (c *Config) load() (*SchemaSpec, map[string]string, error) {
pkgs, err := packages.Load(&packages.Config{
BuildFlags: c.BuildFlags,
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedModule,
}, c.Path, entInterface.PkgPath())
if err != nil {
return nil, nil, fmt.Errorf("loading package: %w", err)
}
if len(pkgs) < 2 {
// Check if the package loading failed due to Go-related
// errors, such as 'missing go.sum entry'.
if err := golist(c.Path, c.BuildFlags); err != nil {
return nil, nil, err
}
return nil, nil, fmt.Errorf("missing package information for: %s", c.Path)
}
entPkg, pkg := pkgs[0], pkgs[1]
if len(pkg.Errors) != 0 {
return nil, nil, c.loadError(pkg.Errors[0])
}
if len(entPkg.Errors) != 0 {
return nil, nil, entPkg.Errors[0]
}
if pkgs[0].PkgPath != entInterface.PkgPath() {
entPkg, pkg = pkgs[1], pkgs[0]
}
names := make(map[string]string)
iface := entPkg.Types.Scope().Lookup(entInterface.Name()).Type().Underlying().(*types.Interface)
for k, v := range pkg.TypesInfo.Defs {
typ, ok := v.(*types.TypeName)
if !ok || !k.IsExported() || !types.Implements(typ.Type(), iface) {
continue
}
spec, ok := k.Obj.Decl.(*ast.TypeSpec)
if !ok {
return nil, nil, fmt.Errorf("invalid declaration %T for %s", k.Obj.Decl, k.Name)
}
if _, ok := spec.Type.(*ast.StructType); !ok {
return nil, nil, fmt.Errorf("invalid spec type %T for %s", spec.Type, k.Name)
}
p := pkg.Fset.Position(spec.Pos())
names[k.Name] = fmt.Sprintf("%s:%d", p.Filename, p.Line)
}
if len(c.Names) == 0 {
c.Names = slices.Sorted(maps.Keys(names))
} else {
sort.Strings(c.Names)
}
return &SchemaSpec{PkgPath: pkg.PkgPath, Module: pkg.Module}, names, nil
}
func (c *Config) loadError(perr packages.Error) (err error) {
if strings.Contains(perr.Msg, "import cycle not allowed") {
if cause := c.cycleCause(); cause != "" {
perr.Msg += "\n" + cause
}
}
err = perr
if perr.Pos == "" {
// Strip "-:" prefix in case of empty position.
err = errors.New(perr.Msg)
}
return err
}
func (c *Config) cycleCause() (cause string) {
dir, err := parser.ParseDir(token.NewFileSet(), c.Path, nil, 0)
// Ignore reporting in case of parsing
// error, or there no packages to parse.
if err != nil || len(dir) == 0 {
return
}
// Find the package that contains the schema, or
// extract the first package if there is only one.
pkg := dir[filepath.Base(c.Path)]
if pkg == nil {
for _, v := range dir {
pkg = v
break
}
}
// Package local declarations used by schema fields.
locals := make(map[string]bool)
for _, f := range pkg.Files {
for _, d := range f.Decls {
g, ok := d.(*ast.GenDecl)
if !ok || g.Tok != token.TYPE {
continue
}
for _, s := range g.Specs {
ts, ok := s.(*ast.TypeSpec)
if !ok || !ts.Name.IsExported() {
continue
}
// Non-struct types such as "type Role int".
st, ok := ts.Type.(*ast.StructType)
if !ok {
locals[ts.Name.Name] = true
continue
}
var embedSchema bool
astutil.Apply(st.Fields, func(c *astutil.Cursor) bool {
f, ok := c.Node().(*ast.Field)
if ok {
switch x := f.Type.(type) {
case *ast.SelectorExpr:
if x.Sel.Name == "Schema" || x.Sel.Name == "Mixin" {
embedSchema = true
}
case *ast.Ident:
// A common pattern is to create local base schema to be embedded by other schemas.
if name := strings.ToLower(x.Name); name == "schema" || name == "mixin" {
embedSchema = true
}
}
}
// Stop traversing the AST in case an ~ent.Schema is embedded.
return !embedSchema
}, nil)
if !embedSchema {
locals[ts.Name.Name] = true
}
}
}
}
// No local declarations to report.
if len(locals) == 0 {
return
}
// Usage of local declarations by schema fields.
goTypes := make(map[string]bool)
for _, f := range pkg.Files {
for _, d := range f.Decls {
f, ok := d.(*ast.FuncDecl)
if !ok || f.Name.Name != "Fields" || f.Type.Params.NumFields() != 0 || f.Type.Results.NumFields() != 1 {
continue
}
astutil.Apply(f.Body, func(cursor *astutil.Cursor) bool {
i, ok := cursor.Node().(*ast.Ident)
if ok && locals[i.Name] {
goTypes[i.Name] = true
}
return true
}, nil)
}
}
names := make([]string, 0, len(goTypes))
for k := range goTypes {
names = append(names, strconv.Quote(k))
}
sort.Strings(names)
if len(names) > 0 {
cause = fmt.Sprintf("To resolve this issue, move the custom types used by the generated code to a separate package: %s", strings.Join(names, ", "))
}
return
}
var (
//go:embed template/main.tmpl schema.go
files embed.FS
buildTmpl = templates()
)
func templates() *template.Template {
tmpls, err := schemaTemplates()
if err != nil {
panic(err)
}
tmpl := template.Must(template.New("templates").
ParseFS(files, "template/main.tmpl"))
for _, t := range tmpls {
tmpl = template.Must(tmpl.Parse(t))
}
return tmpl
}
// schemaTemplates turns the schema.go file and its import block into templates.
func schemaTemplates() ([]string, error) {
var (
imports []string
code bytes.Buffer
fset = token.NewFileSet()
src, _ = files.ReadFile("schema.go")
)
f, err := parser.ParseFile(fset, "schema.go", src, parser.AllErrors)
if err != nil {
return nil, fmt.Errorf("parse schema file: %w", err)
}
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
for _, spec := range decl.Specs {
imports = append(imports, spec.(*ast.ImportSpec).Path.Value)
}
continue
}
if err := format.Node(&code, fset, decl); err != nil {
return nil, fmt.Errorf("format node: %w", err)
}
code.WriteByte('\n')
}
return []string{
fmt.Sprintf(`{{ define "schema" }} %s {{ end }}`, code.String()),
fmt.Sprintf(`{{ define "imports" }} %s {{ end }}`, strings.Join(imports, "\n")),
}, nil
}
func filename(pkg string) string {
name := strings.ReplaceAll(pkg, "/", "_")
return fmt.Sprintf("entc_%s_%d", name, time.Now().Unix())
}
// run 'go run' command and return its output.
func gorun(target string, buildFlags []string) (string, error) {
s, err := gocmd("run", target, buildFlags)
if err != nil {
return "", fmt.Errorf("entc/load: %s", err)
}
return s, nil
}
// golist checks if 'go list' can be executed on the given target.
func golist(target string, buildFlags []string) error {
_, err := gocmd("list", target, buildFlags)
return err
}
// goCmd runs a go command and returns its output.
func gocmd(command, target string, buildFlags []string) (string, error) {
args := []string{command}
args = append(args, buildFlags...)
args = append(args, target)
cmd := exec.Command("go", args...)
stderr := bytes.NewBuffer(nil)
stdout := bytes.NewBuffer(nil)
cmd.Stderr = stderr
cmd.Stdout = stdout
if err := cmd.Run(); err != nil {
return "", errors.New(strings.TrimSpace(stderr.String()))
}
return stdout.String(), nil
}

99
entc/load/load_test.go Normal file
View File

@@ -0,0 +1,99 @@
// 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 load
import (
"testing"
"entgo.io/ent/schema/field"
"github.com/stretchr/testify/require"
)
func TestLoad(t *testing.T) {
cfg := &Config{Path: "./testdata/valid"}
spec, err := cfg.Load()
require.NoError(t, err)
require.Len(t, spec.Schemas, 3)
require.Equal(t, "entgo.io/ent/entc/load/testdata/valid", spec.PkgPath)
require.Equal(t, "Group", spec.Schemas[0].Name, "ordered alphabetically")
require.Equal(t, "Tag", spec.Schemas[1].Name)
require.Equal(t, "User", spec.Schemas[2].Name)
}
func TestLoadWrongPath(t *testing.T) {
cfg := &Config{Path: "./boring"}
plg, err := cfg.Load()
require.Error(t, err)
require.Nil(t, plg)
}
func TestLoadSpecific(t *testing.T) {
cfg := &Config{Path: "./testdata/valid", Names: []string{"User"}}
spec, err := cfg.Load()
require.NoError(t, err)
require.Len(t, spec.Schemas, 1)
require.Equal(t, "User", spec.Schemas[0].Name)
require.Equal(t, "entgo.io/ent/entc/load/testdata/valid", spec.PkgPath)
}
func TestLoadNoSchema(t *testing.T) {
cfg := &Config{Path: "./testdata/invalid"}
schemas, err := cfg.Load()
require.Error(t, err)
require.Empty(t, schemas)
}
func TestLoadSchemaFailure(t *testing.T) {
cfg := &Config{Path: "./testdata/failure"}
spec, err := cfg.Load()
require.Error(t, err)
require.Nil(t, spec)
}
func TestLoadBaseSchema(t *testing.T) {
cfg := &Config{Path: "./testdata/base"}
spec, err := cfg.Load()
require.NoError(t, err)
require.Len(t, spec.Schemas, 1)
require.Len(t, spec.Schemas[0].Fields, 2, "embedded base schema")
f1 := spec.Schemas[0].Fields[0]
require.Equal(t, "base_field", f1.Name)
require.Equal(t, field.TypeInt, f1.Info.Type)
f2 := spec.Schemas[0].Fields[1]
require.Equal(t, "user_field", f2.Name)
require.Equal(t, field.TypeString, f2.Info.Type)
}
func TestLoadTags(t *testing.T) {
all, err := (&Config{
Path: "./testdata/buildflags",
}).Load()
require.NoError(t, err)
require.Len(t, all.Schemas, 2)
require.Equal(t, "Group", all.Schemas[0].Name, "ordered alphabetically")
require.Equal(t, "User", all.Schemas[1].Name)
notags, err := (&Config{
Path: "./testdata/buildflags",
BuildFlags: []string{"-tags", "hidegroups"},
}).Load()
require.NoError(t, err)
require.Len(t, notags.Schemas, 1)
require.Equal(t, "User", notags.Schemas[0].Name)
require.Equal(t, all.Schemas[1], notags.Schemas[0])
}
func TestLoadCycleError(t *testing.T) {
cfg := &Config{Path: "./testdata/cycle"}
spec, err := cfg.Load()
require.Nil(t, spec)
require.EqualError(t, err, `entc/load: parse schema dir: import cycle not allowed: import stack: [entgo.io/ent/entc/load/testdata/cycle entgo.io/ent/entc/load/testdata/cycle/fakent entgo.io/ent/entc/load/testdata/cycle]
To resolve this issue, move the custom types used by the generated code to a separate package: "Enum", "Used"`)
}

521
entc/load/schema.go Normal file
View File

@@ -0,0 +1,521 @@
// 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 load
import (
"encoding/json"
"fmt"
"reflect"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// Schema represents an ent.Schema that was loaded from a complied user package.
type Schema struct {
Name string `json:"name,omitempty"`
Pos string `json:"-"`
View bool `json:"view,omitempty"`
Config ent.Config `json:"config,omitempty"`
Edges []*Edge `json:"edges,omitempty"`
Fields []*Field `json:"fields,omitempty"`
Indexes []*Index `json:"indexes,omitempty"`
Hooks []*Position `json:"hooks,omitempty"`
Interceptors []*Position `json:"interceptors,omitempty"`
Policy []*Position `json:"policy,omitempty"`
Annotations map[string]any `json:"annotations,omitempty"`
}
// Position describes a position in the schema.
type Position struct {
Index int // Index in the field/hook list.
MixedIn bool // Indicates if the schema object was mixed-in.
MixinIndex int // Mixin index in the mixin list.
}
// Field represents an ent.Field that was loaded from a complied user package.
type Field struct {
Name string `json:"name,omitempty"`
Info *field.TypeInfo `json:"type,omitempty"`
ValueScanner bool `json:"value_scanner,omitempty"`
Tag string `json:"tag,omitempty"`
Size *int64 `json:"size,omitempty"`
Enums []struct{ N, V string } `json:"enums,omitempty"`
Unique bool `json:"unique,omitempty"`
Nillable bool `json:"nillable,omitempty"`
Optional bool `json:"optional,omitempty"`
Default bool `json:"default,omitempty"`
DefaultValue any `json:"default_value,omitempty"`
DefaultKind reflect.Kind `json:"default_kind,omitempty"`
UpdateDefault bool `json:"update_default,omitempty"`
Immutable bool `json:"immutable,omitempty"`
Validators int `json:"validators,omitempty"`
StorageKey string `json:"storage_key,omitempty"`
Position *Position `json:"position,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
SchemaType map[string]string `json:"schema_type,omitempty"`
Annotations map[string]any `json:"annotations,omitempty"`
Comment string `json:"comment,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
DeprecatedReason string `json:"deprecated_reason,omitempty"`
BlobKey bool `json:"blob_key,omitempty"`
BlobDualWrite bool `json:"blob_dual_write,omitempty"`
BlobDWSchemaType map[string]string `json:"blob_dw_schema_type,omitempty"`
BlobLazy bool `json:"blob_lazy,omitempty"`
BlobCheckRefs bool `json:"blob_check_refs,omitempty"`
}
// Edge represents an ent.Edge that was loaded from a complied user package.
type Edge struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Tag string `json:"tag,omitempty"`
Field string `json:"field,omitempty"`
RefName string `json:"ref_name,omitempty"`
Ref *Edge `json:"ref,omitempty"`
Through *struct{ N, T string } `json:"through,omitempty"`
Unique bool `json:"unique,omitempty"`
Inverse bool `json:"inverse,omitempty"`
Required bool `json:"required,omitempty"`
Immutable bool `json:"immutable,omitempty"`
StorageKey *edge.StorageKey `json:"storage_key,omitempty"`
Annotations map[string]any `json:"annotations,omitempty"`
Comment string `json:"comment,omitempty"`
}
// Index represents an ent.Index that was loaded from a complied user package.
type Index struct {
Unique bool `json:"unique,omitempty"`
Edges []string `json:"edges,omitempty"`
Fields []string `json:"fields,omitempty"`
StorageKey string `json:"storage_key,omitempty"`
Annotations map[string]any `json:"annotations,omitempty"`
}
// NewEdge creates an loaded edge from edge descriptor.
func NewEdge(ed *edge.Descriptor) *Edge {
ne := &Edge{
Tag: ed.Tag,
Type: ed.Type,
Name: ed.Name,
Field: ed.Field,
Unique: ed.Unique,
Inverse: ed.Inverse,
Required: ed.Required,
Immutable: ed.Immutable,
RefName: ed.RefName,
Through: ed.Through,
StorageKey: ed.StorageKey,
Comment: ed.Comment,
Annotations: make(map[string]any),
}
for _, at := range ed.Annotations {
ne.addAnnotation(at)
}
if ref := ed.Ref; ref != nil {
ne.Ref = NewEdge(ref)
ne.StorageKey = ne.Ref.StorageKey
}
return ne
}
// NewField creates a loaded field from field descriptor.
func NewField(fd *field.Descriptor) (*Field, error) {
if fd.Err != nil {
return nil, fmt.Errorf("field %q: %v", fd.Name, fd.Err)
}
sf := &Field{
Name: fd.Name,
Info: fd.Info,
ValueScanner: fd.ValueScanner != nil,
Tag: fd.Tag,
Enums: fd.Enums,
Unique: fd.Unique,
Nillable: fd.Nillable,
Optional: fd.Optional,
Default: fd.Default != nil,
UpdateDefault: fd.UpdateDefault != nil,
Immutable: fd.Immutable,
StorageKey: fd.StorageKey,
Validators: len(fd.Validators),
Sensitive: fd.Sensitive,
SchemaType: fd.SchemaType,
Annotations: make(map[string]any),
Comment: fd.Comment,
Deprecated: fd.Deprecated,
DeprecatedReason: fd.DeprecatedReason,
BlobKey: fd.BlobKey != nil,
BlobDualWrite: fd.BlobDualWrite,
BlobLazy: fd.BlobLazy,
BlobCheckRefs: fd.BlobCheckRefs,
BlobDWSchemaType: fd.BlobDWSchemaType,
}
for _, at := range fd.Annotations {
sf.addAnnotation(at)
}
if sf.Info == nil {
return nil, fmt.Errorf("missing type info for field %q", sf.Name)
}
if size := int64(fd.Size); size != 0 {
sf.Size = &size
}
if sf.Default {
sf.DefaultKind = reflect.TypeOf(fd.Default).Kind()
}
// If the default value can be encoded to the generator.
// For example, not a function like time.Now.
if _, err := json.Marshal(fd.Default); err == nil {
sf.DefaultValue = fd.Default
}
return sf, nil
}
// NewIndex creates an loaded index from index descriptor.
func NewIndex(idx *index.Descriptor) *Index {
ni := &Index{
Edges: idx.Edges,
Fields: idx.Fields,
Unique: idx.Unique,
StorageKey: idx.StorageKey,
Annotations: make(map[string]any),
}
for _, at := range idx.Annotations {
ni.addAnnotation(at)
}
return ni
}
// MarshalSchema encodes the ent.Schema interface into a JSON
// that can be decoded into the Schema objects declared above.
func MarshalSchema(schema ent.Interface) (b []byte, err error) {
s := &Schema{
Config: schema.Config(),
Name: indirect(reflect.TypeOf(schema)).Name(),
Annotations: make(map[string]any),
}
_, s.View = schema.(ent.Viewer)
if err := s.loadMixin(schema); err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
// Schema annotations override mixed-in annotations.
for _, at := range schema.Annotations() {
if e, ok := at.(interface{ Err() error }); ok && e.Err() != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, e.Err())
}
s.addAnnotation(at)
}
if err := s.loadFields(schema); err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
edges, err := safeEdges(schema)
if err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
for _, e := range edges {
s.Edges = append(s.Edges, NewEdge(e.Descriptor()))
}
indexes, err := safeIndexes(schema)
if err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
for _, idx := range indexes {
s.Indexes = append(s.Indexes, NewIndex(idx.Descriptor()))
}
if err := s.loadHooks(schema); err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
if err := s.loadInterceptors(schema); err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
if err := s.loadPolicy(schema); err != nil {
return nil, fmt.Errorf("schema %q: %w", s.Name, err)
}
return json.Marshal(s)
}
// UnmarshalSchema decodes the given buffer to a loaded schema.
func UnmarshalSchema(buf []byte) (*Schema, error) {
s := &Schema{}
if err := json.Unmarshal(buf, s); err != nil {
return nil, err
}
for _, f := range s.Fields {
if err := f.defaults(); err != nil {
return nil, err
}
}
return s, nil
}
// loadMixin loads mixin to schema from ent.Interface.
func (s *Schema) loadMixin(schema ent.Interface) error {
mixin, err := safeMixin(schema)
if err != nil {
return err
}
for i, mx := range mixin {
name := indirect(reflect.TypeOf(mx)).Name()
fields, err := safeFields(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
for j, f := range fields {
sf, err := NewField(f.Descriptor())
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
sf.Position = &Position{
Index: j,
MixedIn: true,
MixinIndex: i,
}
s.Fields = append(s.Fields, sf)
}
edges, err := safeEdges(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
for _, e := range edges {
s.Edges = append(s.Edges, NewEdge(e.Descriptor()))
}
indexes, err := safeIndexes(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
for _, idx := range indexes {
s.Indexes = append(s.Indexes, NewIndex(idx.Descriptor()))
}
hooks, err := safeHooks(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
for j := range hooks {
s.Hooks = append(s.Hooks, &Position{
Index: j,
MixedIn: true,
MixinIndex: i,
})
}
inters, err := safeInterceptors(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
for j := range inters {
s.Interceptors = append(s.Interceptors, &Position{
Index: j,
MixedIn: true,
MixinIndex: i,
})
}
policy, err := safePolicy(mx)
if err != nil {
return fmt.Errorf("mixin %q: %w", name, err)
}
if policy != nil {
s.Policy = append(s.Policy, &Position{
MixedIn: true,
MixinIndex: i,
})
}
for _, at := range mx.Annotations() {
s.addAnnotation(at)
}
}
return nil
}
// loadFields loads field to schema from ent.Interface.
func (s *Schema) loadFields(schema ent.Interface) error {
fields, err := safeFields(schema)
if err != nil {
return err
}
for i, f := range fields {
sf, err := NewField(f.Descriptor())
if err != nil {
return err
}
sf.Position = &Position{Index: i}
s.Fields = append(s.Fields, sf)
}
return nil
}
func (s *Schema) loadHooks(schema ent.Interface) error {
hooks, err := safeHooks(schema)
if err != nil {
return err
}
for i := range hooks {
s.Hooks = append(s.Hooks, &Position{
Index: i,
MixedIn: false,
})
}
return nil
}
func (s *Schema) loadInterceptors(schema ent.Interface) error {
inters, err := safeInterceptors(schema)
if err != nil {
return err
}
for i := range inters {
s.Interceptors = append(s.Interceptors, &Position{
Index: i,
MixedIn: false,
})
}
return nil
}
func (s *Schema) loadPolicy(schema ent.Interface) error {
policy, err := safePolicy(schema)
if err != nil {
return err
}
if policy != nil {
s.Policy = append(s.Policy, &Position{})
}
return nil
}
func (s *Schema) addAnnotation(an schema.Annotation) {
curr, ok := s.Annotations[an.Name()]
if !ok {
s.Annotations[an.Name()] = an
return
}
if m, ok := curr.(schema.Merger); ok {
s.Annotations[an.Name()] = m.Merge(an)
}
}
func (e *Edge) addAnnotation(an schema.Annotation) {
addAnnotation(e.Annotations, an)
}
func (i *Index) addAnnotation(an schema.Annotation) {
addAnnotation(i.Annotations, an)
}
func (f *Field) addAnnotation(an schema.Annotation) {
addAnnotation(f.Annotations, an)
}
func addAnnotation(annotations map[string]any, an schema.Annotation) {
curr, ok := annotations[an.Name()]
if !ok {
annotations[an.Name()] = an
return
}
if m, ok := curr.(schema.Merger); ok {
annotations[an.Name()] = m.Merge(an)
}
}
func (f *Field) defaults() error {
if !f.Default || !f.Info.Numeric() || f.DefaultKind == reflect.Func {
return nil
}
n, ok := f.DefaultValue.(float64)
if !ok {
return fmt.Errorf("unexpected default value type for field: %q", f.Name)
}
switch t := f.Info.Type; {
case t >= field.TypeInt8 && t <= field.TypeInt64:
f.DefaultValue = int64(n)
case t >= field.TypeUint8 && t <= field.TypeUint64:
f.DefaultValue = uint64(n)
}
return nil
}
// safeFields wraps the schema.Fields and mixin.Fields method with recover to ensure no panics in marshaling.
func safeFields(fd interface{ Fields() []ent.Field }) (fields []ent.Field, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("%T.Fields panics: %v", fd, v)
fields = nil
}
}()
return fd.Fields(), nil
}
// safeEdges wraps the schema.Edges method with recover to ensure no panics in marshaling.
func safeEdges(schema interface{ Edges() []ent.Edge }) (edges []ent.Edge, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Edges panics: %v", v)
edges = nil
}
}()
return schema.Edges(), nil
}
// safeIndexes wraps the schema.Indexes method with recover to ensure no panics in marshaling.
func safeIndexes(schema interface{ Indexes() []ent.Index }) (indexes []ent.Index, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Indexes panics: %v", v)
indexes = nil
}
}()
return schema.Indexes(), nil
}
// safeMixin wraps the schema.Mixin method with recover to ensure no panics in marshaling.
func safeMixin(schema ent.Interface) (mixin []ent.Mixin, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Mixin panics: %v", v)
mixin = nil
}
}()
return schema.Mixin(), nil
}
// safeHooks wraps the schema.Hooks method with recover to ensure no panics in marshaling.
func safeHooks(schema interface{ Hooks() []ent.Hook }) (hooks []ent.Hook, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Hooks panics: %v", v)
hooks = nil
}
}()
return schema.Hooks(), nil
}
// safeInterceptors wraps the schema.Interceptors method with recover to ensure no panics in marshaling.
func safeInterceptors(schema interface{ Interceptors() []ent.Interceptor }) (inters []ent.Interceptor, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Interceptors panics: %v", v)
inters = nil
}
}()
return schema.Interceptors(), nil
}
// safePolicy wraps the schema.Policy method with recover to ensure no panics in marshaling.
func safePolicy(schema interface{ Policy() ent.Policy }) (policy ent.Policy, err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("schema.Policy panics: %v", v)
policy = nil
}
}()
return schema.Policy(), nil
}
func indirect(t reflect.Type) reflect.Type {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}

572
entc/load/schema_test.go Normal file
View File

@@ -0,0 +1,572 @@
// 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 load
import (
"context"
"encoding/json"
"math"
"net/http"
"reflect"
"testing"
"time"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"entgo.io/ent/schema/mixin"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
type OrderConfig struct {
FieldName string
}
func (OrderConfig) Name() string {
return "order_config"
}
func (o OrderConfig) Merge(ant schema.Annotation) schema.Annotation {
o.FieldName = ant.(OrderConfig).FieldName
return o
}
type IDConfig struct {
TagName string
}
func (IDConfig) Name() string {
return "id_config"
}
type PartialIndex struct {
WhereClause string
}
func (PartialIndex) Name() string {
return "partial_index"
}
func (p PartialIndex) Merge(ant schema.Annotation) schema.Annotation {
p.WhereClause = ant.(PartialIndex).WhereClause
return p
}
type AnnotationMixin struct {
mixin.Schema
}
func (AnnotationMixin) Annotations() []schema.Annotation {
return []schema.Annotation{
IDConfig{TagName: "id tag"},
OrderConfig{FieldName: "mixin annotations"},
}
}
type User struct {
ent.Schema
}
func (User) Mixin() []ent.Mixin {
return []ent.Mixin{
AnnotationMixin{},
}
}
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
OrderConfig{FieldName: "type annotations"},
}
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Comment("some comment"),
field.String("name").
Default("unknown").
Annotations(&OrderConfig{FieldName: "name"}),
field.String("nillable").
Nillable(),
field.String("optional").
Optional(),
field.Enum("state").
Values("on", "off").
Optional(),
field.String("sensitive").
Sensitive(),
field.Time("creation_time").
Default(time.Now),
field.UUID("uuid", uuid.UUID{}).
Default(uuid.New),
field.Int("parent_id").
Optional(),
}
}
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("groups", Group.Type).
Annotations(&OrderConfig{FieldName: "name"}),
edge.To("parent", User.Type).
Unique().
Required().
Immutable().
Field("parent_id").
StorageKey(edge.Column("parent_id")).
From("children"),
edge.To("following", User.Type).
Annotations(&OrderConfig{FieldName: "following"}).
From("followers").
Annotations(&OrderConfig{FieldName: "followers"}),
}
}
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("name", "address").
Unique(),
index.Fields("name").
Edges("parent").
StorageKey("user_parent_name").
Annotations(&PartialIndex{
WhereClause: "age > 20",
}).
Unique(),
}
}
type Group struct{ ent.Schema }
func (Group) Fields() []ent.Field { return nil }
func (Group) Edges() []ent.Edge {
return []ent.Edge{
edge.From("users", User.Type),
}
}
func TestMarshalSchema(t *testing.T) {
for _, u := range []ent.Interface{User{}, &User{}} {
buf, err := MarshalSchema(u)
require.NoError(t, err)
schema, err := UnmarshalSchema(buf)
require.NoError(t, err)
require.Equal(t, "User", schema.Name)
require.Len(t, schema.Annotations, 2)
ant := schema.Annotations["order_config"].(map[string]any)
require.Equal(t, ant["FieldName"], "type annotations")
require.Len(t, schema.Fields, 9)
require.Equal(t, "age", schema.Fields[0].Name)
require.Equal(t, field.TypeInt, schema.Fields[0].Info.Type)
require.Equal(t, "name", schema.Fields[1].Name)
require.Equal(t, field.TypeString, schema.Fields[1].Info.Type)
require.Equal(t, "unknown", schema.Fields[1].DefaultValue)
require.NotEmpty(t, schema.Fields[1].Annotations)
ant = schema.Fields[1].Annotations["order_config"].(map[string]any)
require.Equal(t, ant["FieldName"], "name")
require.Equal(t, "nillable", schema.Fields[2].Name)
require.Equal(t, field.TypeString, schema.Fields[2].Info.Type)
require.True(t, schema.Fields[2].Nillable)
require.False(t, schema.Fields[2].Optional)
require.False(t, schema.Fields[2].Sensitive)
require.Equal(t, "optional", schema.Fields[3].Name)
require.Equal(t, field.TypeString, schema.Fields[3].Info.Type)
require.False(t, schema.Fields[3].Nillable)
require.True(t, schema.Fields[3].Optional)
require.Equal(t, "state", schema.Fields[4].Name)
require.Equal(t, field.TypeEnum, schema.Fields[4].Info.Type)
require.Equal(t, "on", schema.Fields[4].Enums[0].V)
require.Equal(t, "off", schema.Fields[4].Enums[1].V)
require.Equal(t, "sensitive", schema.Fields[5].Name)
require.Equal(t, field.TypeString, schema.Fields[5].Info.Type)
require.True(t, schema.Fields[5].Sensitive)
require.Equal(t, reflect.Invalid, schema.Fields[5].DefaultKind)
require.Equal(t, "creation_time", schema.Fields[6].Name)
require.Equal(t, field.TypeTime, schema.Fields[6].Info.Type)
require.Nil(t, schema.Fields[6].DefaultValue)
require.Equal(t, reflect.Func, schema.Fields[6].DefaultKind)
require.Equal(t, "uuid", schema.Fields[7].Name)
require.Equal(t, field.TypeUUID, schema.Fields[7].Info.Type)
require.True(t, schema.Fields[7].Default)
require.Equal(t, "github.com/google/uuid", schema.Fields[7].Info.PkgPath)
require.Equal(t, "parent_id", schema.Fields[8].Name)
require.Equal(t, field.TypeInt, schema.Fields[8].Info.Type)
require.True(t, schema.Fields[8].Optional)
require.Len(t, schema.Edges, 3)
require.Equal(t, "groups", schema.Edges[0].Name)
require.Equal(t, "Group", schema.Edges[0].Type)
require.False(t, schema.Edges[0].Inverse)
require.NotEmpty(t, schema.Edges[0].Annotations)
ant = schema.Edges[0].Annotations["order_config"].(map[string]any)
require.Equal(t, ant["FieldName"], "name")
require.Equal(t, "children", schema.Edges[1].Name)
require.Equal(t, "parent_id", schema.Edges[1].StorageKey.Columns[0])
require.Equal(t, "User", schema.Edges[1].Type)
require.True(t, schema.Edges[1].Inverse)
require.Equal(t, "parent", schema.Edges[1].Ref.Name)
require.True(t, schema.Edges[1].Ref.Unique)
require.True(t, schema.Edges[1].Ref.Required)
require.True(t, schema.Edges[1].Ref.Immutable)
require.Equal(t, "parent_id", schema.Edges[1].Ref.StorageKey.Columns[0])
ant = schema.Edges[2].Annotations["order_config"].(map[string]any)
require.Equal(t, ant["FieldName"], "followers")
ant = schema.Edges[2].Ref.Annotations["order_config"].(map[string]any)
require.Equal(t, ant["FieldName"], "following")
require.Equal(t, []string{"name", "address"}, schema.Indexes[0].Fields)
require.True(t, schema.Indexes[0].Unique)
require.Equal(t, []string{"name"}, schema.Indexes[1].Fields)
require.Equal(t, []string{"parent"}, schema.Indexes[1].Edges)
require.Equal(t, "user_parent_name", schema.Indexes[1].StorageKey)
require.True(t, schema.Indexes[1].Unique)
ant = schema.Indexes[1].Annotations["partial_index"].(map[string]any)
require.Equal(t, "age > 20", ant["WhereClause"])
require.Equal(t, "some comment", schema.Fields[0].Comment)
require.Empty(t, schema.Fields[1].Comment)
}
}
type InvalidEdge struct {
ent.Schema
}
// Edge panics because the edge declaration is invalid.
func (InvalidEdge) Edges() []ent.Edge {
return []ent.Edge{
edge.From("invalid", InvalidEdge{}.Type),
}
}
type InvalidUUID struct {
ent.Schema
}
func (InvalidUUID) Fields() []ent.Field {
return []ent.Field{
field.UUID("invalid", uuid.New()).
Default(time.Now),
}
}
func TestMarshalFails(t *testing.T) {
i1 := InvalidEdge{}
buf, err := MarshalSchema(i1)
require.Error(t, err)
require.Nil(t, buf)
i2 := InvalidUUID{}
buf, err = MarshalSchema(i2)
require.Nil(t, buf)
require.EqualError(t, err, `schema "InvalidUUID": field "invalid": expect type (func() uuid.UUID) for uuid default value`)
}
type WithDefaults struct {
ent.Schema
}
func (WithDefaults) Fields() []ent.Field {
return []ent.Field{
field.Int("int").
Default(1),
field.Float("float").
Default(math.Pi),
field.String("string").
Default("foo"),
field.Bool("string").
Default(true),
field.Time("updated_at").
UpdateDefault(time.Now),
// see issue #1146
field.Int("int_default_func").
DefaultFunc(func() int {
return 1e9
}),
field.Float("balance").
Default(0),
field.JSON("dirs", []http.Dir{}).
Default([]http.Dir{"/tmp"}),
field.Float("float_default_func").
DefaultFunc(func() float64 {
return math.Pi
}),
}
}
func (WithDefaults) Edges() []ent.Edge {
return nil
}
func (WithDefaults) Indexes() []ent.Index {
return nil
}
func TestMarshalDefaults(t *testing.T) {
d := WithDefaults{}
buf, err := MarshalSchema(d)
require.NoError(t, err)
schema := &Schema{}
err = json.Unmarshal(buf, schema)
require.NoError(t, err)
require.Equal(t, "WithDefaults", schema.Name)
require.True(t, schema.Fields[0].Default)
require.True(t, schema.Fields[1].Default)
require.True(t, schema.Fields[2].Default)
require.True(t, schema.Fields[3].Default)
require.False(t, schema.Fields[4].Default)
require.True(t, schema.Fields[4].UpdateDefault)
require.True(t, schema.Fields[5].Default)
require.Equal(t, schema.Fields[5].DefaultKind, reflect.Func)
require.True(t, schema.Fields[6].Default)
require.True(t, schema.Fields[7].Default)
require.Equal(t, schema.Fields[8].DefaultKind, reflect.Func)
}
// BlobDoc is a test schema with blob-stored fields.
type BlobDoc struct {
ent.Schema
}
func (BlobDoc) Fields() []ent.Field {
return []ent.Field{
field.Blob("content").
Comment("blob content"),
field.Blob("thumbnail"),
}
}
func (BlobDoc) Edges() []ent.Edge { return nil }
func TestMarshalBlobSchema(t *testing.T) {
d := BlobDoc{}
buf, err := MarshalSchema(d)
require.NoError(t, err)
s := &Schema{}
err = json.Unmarshal(buf, s)
require.NoError(t, err)
require.Equal(t, "BlobDoc", s.Name)
require.Len(t, s.Fields, 2)
// First blob field: content.
f0 := s.Fields[0]
require.Equal(t, "content", f0.Name)
require.Equal(t, field.TypeBlob, f0.Info.Type)
require.Equal(t, "blob content", f0.Comment)
// Second blob field: thumbnail.
f1 := s.Fields[1]
require.Equal(t, "thumbnail", f1.Name)
require.Equal(t, field.TypeBlob, f1.Info.Type)
// Verify JSON roundtrip preserves type.
buf2, err := json.Marshal(s)
require.NoError(t, err)
s2 := &Schema{}
require.NoError(t, json.Unmarshal(buf2, s2))
require.Equal(t, field.TypeBlob, s2.Fields[0].Info.Type)
}
type TimeMixin struct {
mixin.Schema
}
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 HooksMixin struct {
mixin.Schema
}
func (HooksMixin) Fields() []ent.Field {
return []ent.Field{
field.String("boring"),
}
}
func (HooksMixin) Edges() []ent.Edge {
return []ent.Edge{
edge.To("user", User.Type).
Unique(),
}
}
func (HooksMixin) Indexes() []ent.Index {
return []ent.Index{
index.Fields("boring").
Edges("user"),
}
}
func (HooksMixin) Hooks() []ent.Hook {
return []ent.Hook{
func(ent.Mutator) ent.Mutator { return nil },
func(ent.Mutator) ent.Mutator { return nil },
}
}
type BoringPolicy struct{}
func (BoringPolicy) EvalMutation(context.Context, ent.Mutation) error { return nil }
func (BoringPolicy) EvalQuery(context.Context, ent.Query) error { return nil }
type PrivacyMixin struct {
mixin.Schema
}
func (PrivacyMixin) Policy() ent.Policy {
return BoringPolicy{}
}
type WithMixin struct {
ent.Schema
}
func (WithMixin) Mixin() []ent.Mixin {
return []ent.Mixin{
TimeMixin{},
HooksMixin{},
PrivacyMixin{},
}
}
func (WithMixin) Fields() []ent.Field {
return []ent.Field{
field.Int("field"),
}
}
func (WithMixin) Edges() []ent.Edge {
return []ent.Edge{
edge.To("owner", User.Type),
}
}
func (WithMixin) Indexes() []ent.Index {
return []ent.Index{
index.Fields("field").
Edges("owner").
Unique(),
}
}
func (WithMixin) Hooks() []ent.Hook {
return []ent.Hook{
func(ent.Mutator) ent.Mutator { return nil },
}
}
func (WithMixin) Policy() ent.Policy {
return BoringPolicy{}
}
func TestMarshalMixin(t *testing.T) {
d := WithMixin{}
buf, err := MarshalSchema(d)
require.NoError(t, err)
schema := &Schema{}
err = json.Unmarshal(buf, schema)
require.NoError(t, err)
t.Run("Fields", func(t *testing.T) {
require.Equal(t, "WithMixin", schema.Name)
require.Equal(t, "created_at", schema.Fields[0].Name)
require.True(t, schema.Fields[0].Default)
require.True(t, schema.Fields[0].Position.MixedIn)
require.Equal(t, 0, schema.Fields[0].Position.MixinIndex)
require.Equal(t, 0, schema.Fields[0].Position.Index)
require.Equal(t, "updated_at", schema.Fields[1].Name)
require.True(t, schema.Fields[1].Default)
require.True(t, schema.Fields[1].UpdateDefault)
require.True(t, schema.Fields[1].Position.MixedIn)
require.Equal(t, 0, schema.Fields[1].Position.MixinIndex)
require.Equal(t, 1, schema.Fields[1].Position.Index)
require.Equal(t, "boring", schema.Fields[2].Name)
require.False(t, schema.Fields[2].Default)
require.False(t, schema.Fields[2].UpdateDefault)
require.True(t, schema.Fields[2].Position.MixedIn)
require.Equal(t, 1, schema.Fields[2].Position.MixinIndex)
require.Equal(t, 0, schema.Fields[2].Position.Index)
require.Equal(t, "field", schema.Fields[3].Name)
require.False(t, schema.Fields[3].Default)
require.False(t, schema.Fields[3].Position.MixedIn)
require.Equal(t, 0, schema.Fields[3].Position.Index)
})
t.Run("Hooks", func(t *testing.T) {
require.True(t, schema.Hooks[0].MixedIn)
require.True(t, schema.Hooks[1].MixedIn)
require.Equal(t, 1, schema.Hooks[0].MixinIndex)
require.Equal(t, 1, schema.Hooks[1].MixinIndex)
require.Equal(t, 0, schema.Hooks[0].Index)
require.Equal(t, 1, schema.Hooks[1].Index)
require.False(t, schema.Hooks[2].MixedIn)
require.Equal(t, 0, schema.Hooks[2].Index)
require.Equal(t, 0, schema.Hooks[2].MixinIndex)
})
t.Run("Edges", func(t *testing.T) {
require.Len(t, schema.Edges, 2)
require.Equal(t, "user", schema.Edges[0].Name)
require.Equal(t, "User", schema.Edges[0].Type)
require.True(t, schema.Edges[0].Unique)
require.Equal(t, "owner", schema.Edges[1].Name)
require.Equal(t, "User", schema.Edges[1].Type)
require.False(t, schema.Edges[1].Unique)
})
t.Run("Indexes", func(t *testing.T) {
require.Len(t, schema.Indexes, 2)
require.Equal(t, []string{"boring"}, schema.Indexes[0].Fields)
require.Equal(t, []string{"user"}, schema.Indexes[0].Edges)
require.False(t, schema.Indexes[0].Unique)
require.Equal(t, []string{"field"}, schema.Indexes[1].Fields)
require.Equal(t, []string{"owner"}, schema.Indexes[1].Edges)
require.True(t, schema.Indexes[1].Unique)
})
t.Run("Policy", func(t *testing.T) {
require.Len(t, schema.Policy, 2)
require.True(t, schema.Policy[0].MixedIn)
require.False(t, schema.Policy[1].MixedIn)
})
}

View File

@@ -0,0 +1,44 @@
{{/*
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.
*/}}
{{ define "main" }}
// Code generated by 'entc generate'. DO NOT EDIT.
package main
import (
"os"
"bytes"
{{ template "imports" }}
entschema {{ printf "%q" .Package }}
)
var schemas = []ent.Interface{
{{- range $name := .Names }}
entschema.{{ print $name "{}" }},
{{- end }}
}
func main() {
var lines [][]byte
for _, schema := range schemas {
b, err := MarshalSchema(schema)
if err != nil {
fail(err)
}
lines = append(lines, b)
}
os.Stdout.Write(bytes.Join(lines, []byte("\n")))
}
func fail(err error) {
os.Stderr.WriteString(err.Error()+"\n")
os.Exit(1)
}
{{ template "schema" }}
{{ end }}

33
entc/load/testdata/base/schema.go vendored Normal file
View File

@@ -0,0 +1,33 @@
// 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 base
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// base schema for sharing fields and edges.
type base struct {
ent.Schema
}
func (base) Fields() []ent.Field {
return []ent.Field{
field.Int("base_field"),
}
}
// User holds the user schema.
type User struct {
base
}
func (u User) Fields() []ent.Field {
return append(
u.base.Fields(),
field.String("user_field"),
)
}

24
entc/load/testdata/buildflags/group.go vendored Normal file
View File

@@ -0,0 +1,24 @@
//go:build !hidegroups
// 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 buildflags
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Group holds the group schema.
type Group struct {
ent.Schema
}
func (Group) Fields() []ent.Field {
return []ent.Field{
field.Time("expired_at"),
field.String("organization"),
}
}

22
entc/load/testdata/buildflags/user.go vendored Normal file
View File

@@ -0,0 +1,22 @@
// 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 buildflags
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User holds the user schema.
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("name"),
}
}

View File

@@ -0,0 +1,15 @@
// 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 fakent fis a fake generated Ent package.
package fakent
import (
"entgo.io/ent"
"entgo.io/ent/entc/load/testdata/cycle"
)
type Hook = ent.Hook
var _ = &cycle.Used{}

36
entc/load/testdata/cycle/schema.go vendored Normal file
View File

@@ -0,0 +1,36 @@
// 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 cycle
import (
"entgo.io/ent"
"entgo.io/ent/entc/load/testdata/cycle/fakent"
"entgo.io/ent/schema/field"
)
// User holds the user schema.
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.JSON("used", &Used{}),
field.Enum("e").
GoType(Enum(0)),
}
}
type (
Used struct{}
NotUsed struct{}
notExported struct{}
Enum int
)
func (Enum) Values() []string { return nil }
// The cause for cycle.
var _ fakent.Hook = nil

20
entc/load/testdata/failure/user.go vendored Normal file
View File

@@ -0,0 +1,20 @@
// 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 failure
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
)
type User struct {
ent.Schema
}
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("panic", User{}.Type),
}
}

8
entc/load/testdata/invalid/schema.go vendored Normal file
View File

@@ -0,0 +1,8 @@
// 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 invalid
// User is invalid schema.
type User struct{}

45
entc/load/testdata/valid/schema.go vendored Normal file
View File

@@ -0,0 +1,45 @@
// 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 valid
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User holds the user schema.
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("name"),
}
}
// Group holds the group schema.
type Group struct {
ent.Schema
}
func (Group) Fields() []ent.Field {
return []ent.Field{
field.Time("expired_at"),
field.String("organization"),
}
}
// Tag holds the tag schema.
type Tag struct {
ent.Schema
}
func (Tag) Fields() []ent.Field {
return []ent.Field{
field.String("text"),
}
}