schema/field: add annotation option to schema field (#622)

This commit is contained in:
Ariel Mashraki
2020-07-19 18:01:04 +03:00
committed by GitHub
parent bdd80ebb3f
commit 54f0a6769b
16 changed files with 418 additions and 30 deletions

View File

@@ -141,7 +141,7 @@ func TemplateDir(path string) Option {
if err != nil {
return fmt.Errorf("load template: %v", err)
}
if info.IsDir() {
if info.IsDir() || strings.HasSuffix(path, ".go") {
return nil
}
cfg.Template, err = cfg.Template.ParseFiles(path)

View File

@@ -65,6 +65,7 @@ var (
"hasKey": hasKey,
"list": list,
"fail": fail,
"replace": strings.ReplaceAll,
}
rules = ruleset()
acronyms = make(map[string]struct{})

View File

@@ -75,6 +75,9 @@ type (
// UserDefined indicates that this field was defined by the loaded schema.
// Unlike default id field, which is defined by the generator.
UserDefined bool
// Annotations that were defined in the field schema.
// The mapping is from the Annotation.Name() to the decoded object.
Annotations map[string]interface{}
}
// Edge of a graph between two types.
@@ -176,6 +179,7 @@ func NewType(c *Config, schema *load.Schema) (*Type, error) {
StructTag: structTag(f.Name, f.Tag),
Validators: f.Validators,
UserDefined: true,
Annotations: f.Annotations,
}
if err := typ.checkField(tf, f); err != nil {
return nil, err

View File

@@ -0,0 +1,13 @@
// 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.
// Code generated by entc, DO NOT EDIT.
package ent
// CardExtension is a type for holding the extension information defined in the schema.
type CardExtension struct {
Number string
Name string
}

View File

@@ -6,6 +6,7 @@ package schema
import (
"github.com/facebookincubator/ent"
"github.com/facebookincubator/ent/entc/integration/ent/template"
"github.com/facebookincubator/ent/schema/edge"
"github.com/facebookincubator/ent/schema/field"
"github.com/facebookincubator/ent/schema/mixin"
@@ -27,11 +28,17 @@ func (Card) Fields() []ent.Field {
return []ent.Field{
field.String("number").
Immutable().
NotEmpty(),
NotEmpty().
Annotations(&template.Extension{
Type: "string",
}),
field.String("name").
Optional().
Comment("Exact name written on card").
NotEmpty(),
NotEmpty().
Annotations(&template.Extension{
Type: "string",
}),
}
}

View File

@@ -0,0 +1,14 @@
// 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 template
// Extension is a template extension.
type Extension struct {
Type string
}
func (*Extension) Name() string {
return "Extension"
}

View File

@@ -0,0 +1,27 @@
{{/*
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 "extension" }}
{{ $pkg := base $.Config.Package }}
{{ template "header" $ }}
{{ range $n := $.Nodes }}
{{ $hasExt := false }}
{{ range $f := $n.Fields }}{{ if $f.Annotations.Extension }}{{ $hasExt = true }}{{ end }}{{ end }}
{{/* If one or fields contain the "Extension" annotation */}}
{{ if $hasExt }}
// {{ $n.Name }}Extension is a type for holding the extension information defined in the schema.
type {{ $n.Name }}Extension struct {
{{- range $f := $n.Fields }}
{{- with $ant := $f.Annotations.Extension }}
{{ $f.StructField }} {{ $ant.Type }}
{{- end }}
{{- end }}
}
{{ end }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,13 @@
// 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.
// Code generated by entc, DO NOT EDIT.
package ent
// CardExtension is a type for holding the extension information defined in the schema.
type CardExtension struct {
Number string
Name string
}

View File

@@ -1115,6 +1115,12 @@ func Mutation(t *testing.T, client *ent.Client) {
require.Equal(t, "boring", usr.Name)
}
// Test templates codegen.
var (
_ = ent.CardExtension{}
_ = ent.Card{}.StaticField
)
func drop(t *testing.T, client *ent.Client) {
t.Log("drop data from database")
ctx := context.Background()

View File

@@ -43,7 +43,7 @@ type Edge struct {
}
{{/* loop over all types and add implement the Node interface. */}}
{{ range $_, $n := $.Nodes -}}
{{ range $n := $.Nodes -}}
{{ $receiver := $n.Receiver }}
func ({{ $receiver }} *{{ $n.Name }}) Node(ctx context.Context) (node *Node, err error) {
node = &Node{

File diff suppressed because one or more lines are too long

View File

@@ -35,23 +35,24 @@ type Position struct {
// 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"`
Tag string `json:"tag,omitempty"`
Size *int64 `json:"size,omitempty"`
Enums []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 interface{} `json:"default_value,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"`
Name string `json:"name,omitempty"`
Info *field.TypeInfo `json:"type,omitempty"`
Tag string `json:"tag,omitempty"`
Size *int64 `json:"size,omitempty"`
Enums []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 interface{} `json:"default_value,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]interface{} `json:"annotations,omitempty"`
}
// Edge represents an ent.Edge that was loaded from a complied user package.
@@ -114,6 +115,10 @@ func NewField(fd *field.Descriptor) (*Field, error) {
Validators: len(fd.Validators),
Sensitive: fd.Sensitive,
SchemaType: fd.SchemaType,
Annotations: make(map[string]interface{}),
}
for _, at := range fd.Annotations {
sf.Annotations[at.Name()] = at
}
if sf.Info == nil {
return nil, fmt.Errorf("missing type info for field %q", sf.Name)

View File

@@ -20,6 +20,18 @@ import (
"github.com/stretchr/testify/require"
)
type OrderConfig struct {
FieldName string
}
func (OrderConfig) Name() string {
return "order_config"
}
func (o *OrderConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(*o)
}
type User struct {
ent.Schema
}
@@ -28,7 +40,8 @@ func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("name").
Default("unknown"),
Default("unknown").
Annotations(&OrderConfig{FieldName: "name"}),
field.String("nillable").
Nillable(),
field.String("optional").
@@ -81,8 +94,8 @@ func TestMarshalSchema(t *testing.T) {
buf, err := MarshalSchema(u)
require.NoError(t, err)
schema := &Schema{}
require.NoError(t, json.Unmarshal(buf, schema))
schema, err := UnmarshalSchema(buf)
require.NoError(t, err)
require.Equal(t, "User", schema.Name)
require.Len(t, schema.Fields, 8)
require.Equal(t, "age", schema.Fields[0].Name)
@@ -91,6 +104,9 @@ func TestMarshalSchema(t *testing.T) {
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]interface{})
require.Equal(t, ant["FieldName"], "name")
require.Equal(t, "nillable", schema.Fields[2].Name)
require.Equal(t, field.TypeString, schema.Fields[2].Info.Type)

View File

@@ -266,6 +266,19 @@ func (b *stringBuilder) GoType(typ interface{}) *stringBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.String("dir").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *stringBuilder) Annotations(annotations ...Annotation) *stringBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *stringBuilder) Descriptor() *Descriptor {
return b.desc
@@ -347,6 +360,19 @@ func (b *timeBuilder) GoType(typ interface{}) *timeBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Time("deleted_at").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *timeBuilder) Annotations(annotations ...Annotation) *timeBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *timeBuilder) Descriptor() *Descriptor {
return b.desc
@@ -425,6 +451,19 @@ func (b *boolBuilder) GoType(typ interface{}) *boolBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Bool("deleted").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *boolBuilder) Annotations(annotations ...Annotation) *boolBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *boolBuilder) Descriptor() *Descriptor {
return b.desc
@@ -462,7 +501,7 @@ func (b *bytesBuilder) Immutable() *bytesBuilder {
}
// Comment sets the comment of the field.
func (b *bytesBuilder) Comment(c string) *bytesBuilder {
func (b *bytesBuilder) Comment(string) *bytesBuilder {
return b
}
@@ -497,9 +536,17 @@ func (b *bytesBuilder) GoType(typ interface{}) *bytesBuilder {
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *bytesBuilder) Descriptor() *Descriptor {
return b.desc
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Bytes("ip").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *bytesBuilder) Annotations(annotations ...Annotation) *bytesBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// SchemaType overrides the default database type with a custom
@@ -516,6 +563,11 @@ func (b *bytesBuilder) SchemaType(types map[string]string) *bytesBuilder {
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *bytesBuilder) Descriptor() *Descriptor {
return b.desc
}
// jsonBuilder is the builder for json fields.
type jsonBuilder struct {
desc *Descriptor
@@ -566,6 +618,19 @@ func (b *jsonBuilder) SchemaType(types map[string]string) *jsonBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.JSON("json").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *jsonBuilder) Annotations(annotations ...Annotation) *jsonBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *jsonBuilder) Descriptor() *Descriptor {
return b.desc
@@ -639,6 +704,19 @@ func (b *enumBuilder) SchemaType(types map[string]string) *enumBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Enum("enum").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *enumBuilder) Annotations(annotations ...Annotation) *enumBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *enumBuilder) Descriptor() *Descriptor {
return b.desc
@@ -715,11 +793,32 @@ func (b *uuidBuilder) SchemaType(types map[string]string) *uuidBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.UUID("id", uuid.New()).
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uuidBuilder) Annotations(annotations ...Annotation) *uuidBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uuidBuilder) Descriptor() *Descriptor {
return b.desc
}
// Annotation is used to attach arbitrary metadata to the field object in codegen.
// The object must be serializable to JSON raw value (e.g. struct, map or slice).
// Template extensions can retrieve this metadata and use inside their templates.
type Annotation interface {
// Name defines the name of the annotation.
Name() string
}
// A Descriptor for field configuration.
type Descriptor struct {
Tag string // struct tag.
@@ -737,6 +836,7 @@ type Descriptor struct {
Enums []string // enum values.
Sensitive bool // sensitive info string field.
SchemaType map[string]string // override the schema type.
Annotations []Annotation // field annotations.
err error
}

View File

@@ -174,6 +174,19 @@ func (b *{{ $builder }}) GoType(typ interface{}) *{{ $builder }} {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.{{ $tt }}("{{ $t }}").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *{{ $builder }}) Annotations(annotations ...Annotation) *{{ $builder }} {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *{{ $builder }}) Descriptor() *Descriptor {
return b.desc
@@ -320,6 +333,19 @@ func (b *{{ $builder }}) GoType(typ interface{}) *{{ $builder }} {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.{{ $tt }}("{{ $t }}").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *{{ $builder }}) Annotations(annotations ...Annotation) *{{ $builder }} {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *{{ $builder }}) Descriptor() *Descriptor {
return b.desc

View File

@@ -239,6 +239,19 @@ func (b *intBuilder) GoType(typ interface{}) *intBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Int("int").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *intBuilder) Annotations(annotations ...Annotation) *intBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *intBuilder) Descriptor() *Descriptor {
return b.desc
@@ -366,6 +379,19 @@ func (b *uintBuilder) GoType(typ interface{}) *uintBuilder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Uint("uint").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uintBuilder) Annotations(annotations ...Annotation) *uintBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uintBuilder) Descriptor() *Descriptor {
return b.desc
@@ -503,6 +529,19 @@ func (b *int8Builder) GoType(typ interface{}) *int8Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Int8("int8").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *int8Builder) Annotations(annotations ...Annotation) *int8Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *int8Builder) Descriptor() *Descriptor {
return b.desc
@@ -640,6 +679,19 @@ func (b *int16Builder) GoType(typ interface{}) *int16Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Int16("int16").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *int16Builder) Annotations(annotations ...Annotation) *int16Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *int16Builder) Descriptor() *Descriptor {
return b.desc
@@ -777,6 +829,19 @@ func (b *int32Builder) GoType(typ interface{}) *int32Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Int32("int32").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *int32Builder) Annotations(annotations ...Annotation) *int32Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *int32Builder) Descriptor() *Descriptor {
return b.desc
@@ -914,6 +979,19 @@ func (b *int64Builder) GoType(typ interface{}) *int64Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Int64("int64").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *int64Builder) Annotations(annotations ...Annotation) *int64Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *int64Builder) Descriptor() *Descriptor {
return b.desc
@@ -1041,6 +1119,19 @@ func (b *uint8Builder) GoType(typ interface{}) *uint8Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Uint8("uint8").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uint8Builder) Annotations(annotations ...Annotation) *uint8Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uint8Builder) Descriptor() *Descriptor {
return b.desc
@@ -1168,6 +1259,19 @@ func (b *uint16Builder) GoType(typ interface{}) *uint16Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Uint16("uint16").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uint16Builder) Annotations(annotations ...Annotation) *uint16Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uint16Builder) Descriptor() *Descriptor {
return b.desc
@@ -1295,6 +1399,19 @@ func (b *uint32Builder) GoType(typ interface{}) *uint32Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Uint32("uint32").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uint32Builder) Annotations(annotations ...Annotation) *uint32Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uint32Builder) Descriptor() *Descriptor {
return b.desc
@@ -1422,6 +1539,19 @@ func (b *uint64Builder) GoType(typ interface{}) *uint64Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Uint64("uint64").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *uint64Builder) Annotations(annotations ...Annotation) *uint64Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *uint64Builder) Descriptor() *Descriptor {
return b.desc
@@ -1568,6 +1698,19 @@ func (b *float64Builder) GoType(typ interface{}) *float64Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Float64("float64").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *float64Builder) Annotations(annotations ...Annotation) *float64Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *float64Builder) Descriptor() *Descriptor {
return b.desc
@@ -1701,6 +1844,19 @@ func (b *float32Builder) GoType(typ interface{}) *float32Builder {
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.Float32("float32").
// Annotations(entgql.Config{
// Ordered: true,
// })
//
func (b *float32Builder) Annotations(annotations ...Annotation) *float32Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *float32Builder) Descriptor() *Descriptor {
return b.desc