entc/gen: introduce the entc.Dependency option

This commit is contained in:
Ariel Mashraki
2021-10-25 21:56:48 +03:00
committed by Ariel Mashraki
parent 5bbd973ffa
commit 82eeeb5a1c
13 changed files with 338 additions and 52 deletions

View File

@@ -12,12 +12,14 @@ import (
"go/token"
"path"
"path/filepath"
"reflect"
"strings"
"entgo.io/ent/entc/gen"
"entgo.io/ent/entc/internal"
"entgo.io/ent/entc/load"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
"golang.org/x/tools/go/packages"
)
@@ -173,51 +175,49 @@ func TemplateDir(path string) Option {
})
}
type (
// Extension describes an Ent code generation extension that
// allows customizing the code generation and integrate with
// other tools and libraries (e.g. GraphQL, gRPC, OpenAPI) by
// by registering hooks, templates and global annotations in
// one simple call.
// Extension describes an Ent code generation extension that
// allows customizing the code generation and integrate with
// other tools and libraries (e.g. GraphQL, gRPC, OpenAPI) by
// registering hooks, templates and global annotations in one
// simple call.
//
// ex, err := entgql.NewExtension(
// entgql.WithConfig("../gqlgen.yml"),
// entgql.WithSchema("../schema.graphql"),
// )
// if err != nil {
// log.Fatalf("creating graphql extension: %v", err)
// }
// err = entc.Generate("./schema", &gen.Config{
// Templates: entswag.Templates,
// }, entc.Extensions(ex))
// if err != nil {
// log.Fatalf("running ent codegen: %v", err)
// }
//
type Extension interface {
// Hooks holds an optional list of Hooks to apply
// on the graph before/after the code-generation.
Hooks() []gen.Hook
// Annotations injects global annotations to the gen.Config object that
// can be accessed globally in all templates. Unlike schema annotations,
// being serializable to JSON raw value is not mandatory.
//
// ex, err := entgql.NewExtension(
// entgql.WithConfig("../gqlgen.yml"),
// entgql.WithSchema("../schema.graphql"),
// )
// if err != nil {
// log.Fatalf("creating graphql extension: %v", err)
// }
// err = entc.Generate("./schema", &gen.Config{
// Templates: entswag.Templates,
// }, entc.Extensions(ex))
// if err != nil {
// log.Fatalf("running ent codegen: %v", err)
// }
// {{- with $.Config.Annotations.GQL }}
// {{/* Annotation usage goes here. */}}
// {{- end }}
//
Extension interface {
// Hooks holds an optional list of Hooks to apply
// on the graph before/after the code-generation.
Hooks() []gen.Hook
Annotations() []Annotation
// Annotations injects global annotations to the gen.Config object that
// can be accessed globally in all templates. Unlike schema annotations,
// being serializable to JSON raw value is not mandatory.
//
// {{- with $.Config.Annotations.GQL }}
// {{/* Annotation usage goes here. */}}
// {{- end }}
//
Annotations() []Annotation
// Templates specifies a list of alternative templates
// to execute or to override the default.
Templates() []*gen.Template
// Templates specifies a list of alternative templates
// to execute or to override the default.
Templates() []*gen.Template
// Options specifies a list of entc.Options to evaluate on
// the gen.Config before executing the code generation.
Options() []Option
}
)
// Options specifies a list of entc.Options to evaluate on
// the gen.Config before executing the code generation.
Options() []Option
}
// Extensions evaluates the list of Extensions on the gen.Config.
func Extensions(extensions ...Extension) Option {
@@ -263,6 +263,95 @@ func (DefaultExtension) Options() []Option { return nil }
var _ Extension = (*DefaultExtension)(nil)
// DependencyOption allows configuring optional dependencies using functional options.
type DependencyOption func(*gen.DependencyAnnotation) error
// DependencyType sets the type of the struct field in
// the generated builders for the configured dependency.
func DependencyType(v interface{}) DependencyOption {
return func(d *gen.DependencyAnnotation) error {
if v == nil {
return errors.New("nil dependency type")
}
t := reflect.TypeOf(v)
tv := indirect(t)
d.Type = &field.TypeInfo{
Ident: t.String(),
PkgPath: tv.PkgPath(),
RType: &field.RType{
Kind: t.Kind(),
Name: tv.Name(),
Ident: tv.String(),
PkgPath: tv.PkgPath(),
},
}
return nil
}
}
// DependencyTypeInfo is similar to DependencyType, but
// allows setting the field.TypeInfo explicitly.
func DependencyTypeInfo(t *field.TypeInfo) DependencyOption {
return func(d *gen.DependencyAnnotation) error {
if t == nil {
return errors.New("nil dependency type info")
}
d.Type = t
return nil
}
}
// DependencyField sets the struct field and the option name
// of the dependency in the generated builders.
func DependencyName(name string) DependencyOption {
return func(d *gen.DependencyAnnotation) error {
d.Field = name
d.Option = name
return nil
}
}
// Dependency allows configuring optional dependencies as struct fields on the
// generated builders. For example:
//
// opts := []entc.Option{
// entc.Dependency(
// entc.DependencyType(&http.Client{}),
// ),
// entc.Dependency(
// entc.DependencyName("DB"),
// entc.DependencyType(&sql.DB{}),
// )
// }
// if err := entc.Generate("./ent/path", &gen.Config{}, opts...); err != nil {
// log.Fatalf("running ent codegen: %v", err)
// }
//
func Dependency(opts ...DependencyOption) Option {
return func(cfg *gen.Config) error {
d := &gen.DependencyAnnotation{}
for _, opt := range opts {
if err := opt(d); err != nil {
return err
}
}
if err := d.Build(); err != nil {
return err
}
if cfg.Annotations == nil {
cfg.Annotations = gen.Annotations{}
}
v, ok := cfg.Annotations[d.Name()]
if !ok {
v = []*gen.DependencyAnnotation{d}
} else {
v = append(v.([]*gen.DependencyAnnotation), d)
}
cfg.Annotations[d.Name()] = v
return nil
}
}
// templateOption ensures the template instantiate
// once for config and execute the given Option.
func templateOption(next func(t *gen.Template) (*gen.Template, error)) Option {
@@ -307,3 +396,11 @@ func mayRecover(err error, schemaPath string, cfg *gen.Config) error {
target := filepath.Join(cfg.Target, "internal/schema.go")
return (&internal.Snapshot{Path: target, Config: cfg}).Restore()
}
// indirect returns the type at the end of indirection.
func indirect(t reflect.Type) reflect.Type {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}

View File

@@ -8,11 +8,13 @@ package gen
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath"
"reflect"
"runtime/debug"
"strings"
"text/template/parse"
@@ -754,6 +756,76 @@ func (a assets) format() error {
return nil
}
// DependencyAnnotation allows configuring optional dependencies as struct fields on the
// generated builders. For example:
//
// DependencyAnnotation{
// Field: "HTTPClient",
// Type: "*http.Client",
// Option: "WithClient",
// }
//
// Although the DependencyAnnotation is exported, used should use the entc.OptionalDependency
// option in order to build this annotation.
type DependencyAnnotation struct {
// Field defines the struct field name on the builders.
// It defaults to the full type name. For example:
//
// http.Client => HTTPClient
// net.Conn => NetConn
// url.URL => URL
//
Field string
// Type defines the type identifier. For example, `*http.Client`.
Type *field.TypeInfo
// Option defines the name of the config option.
// It defaults to the field name.
Option string
}
// Name describes the annotation name.
func (DependencyAnnotation) Name() string {
return "Dependencies"
}
// Build builds the annotation and fails if it is invalid.
func (d *DependencyAnnotation) Build() error {
if d.Type == nil {
return errors.New("entc/gen: missing dependency type")
}
if d.Field == "" {
name, err := d.defaultName()
if err != nil {
return err
}
d.Field = name
}
if d.Option == "" {
d.Option = d.Field
}
return nil
}
func (d *DependencyAnnotation) defaultName() (string, error) {
var pkg, name string
switch parts := strings.Split(strings.TrimLeft(d.Type.Ident, "[]*"), "."); len(parts) {
case 1:
name = parts[0]
case 2:
name = parts[1]
// Avoid stuttering.
if !strings.EqualFold(parts[0], name) {
pkg = parts[0]
}
default:
return "", fmt.Errorf("entc/gen: unexpected number of parts: %q", parts)
}
if r := d.Type.RType; r != nil && (r.Kind == reflect.Array || r.Kind == reflect.Slice) {
name = plural(name)
}
return pascal(pkg) + pascal(name), nil
}
// expect panics if the condition is false.
func expect(cond bool, msg string, args ...interface{}) {
if !cond {

View File

@@ -396,3 +396,46 @@ func TestGraph_Hooks(t *testing.T) {
require.NotNil(graph)
require.EqualError(graph.Gen(), `struct tag "yaml" is missing for field T1.age`)
}
func TestDependencyAnnotation_Build(t *testing.T) {
tests := []struct {
typ *field.TypeInfo
field string
}{
{
typ: &field.TypeInfo{
Ident: "*http.Client",
},
field: "HTTPClient",
},
{
typ: &field.TypeInfo{
Ident: "[]*http.Client",
RType: &field.RType{
Kind: reflect.Slice,
},
},
field: "HTTPClients",
},
{
typ: &field.TypeInfo{
Ident: "[]*url.URL",
RType: &field.RType{
Kind: reflect.Slice,
},
},
field: "URLs",
},
{
typ: &field.TypeInfo{
Ident: "*net.Conn",
},
field: "NetConn",
},
}
for _, tt := range tests {
d := &DependencyAnnotation{Type: tt.typ}
require.NoError(t, d.Build())
require.Equal(t, tt.field, d.Field)
}
}

View File

@@ -9,10 +9,21 @@ in the LICENSE file in the root directory of this source tree.
{{ define "config" }}
{{ $pkg := base $.Config.Package }}
{{/* Additional dependencies. */}}
{{ $deps := list }}{{ with $.Config.Annotations }}{{ $deps = $.Config.Annotations.Dependencies }}{{ end }}
{{ template "header" $ }}
{{ template "import" $ }}
{{ with $deps }}
import (
{{- range $dep := $deps }}
{{ $dep.Type.PkgName }} "{{ $dep.Type.PkgPath }}"
{{- end }}
)
{{ end }}
// Option function to configure the client.
type Option func(*config)
@@ -26,6 +37,10 @@ type config struct {
log func(...interface{})
// hooks to execute on mutations.
hooks *hooks
{{- /* Additional dependency fields. */}}
{{- range $dep := $deps }}
{{ $dep.Field }} {{ $dep.Type }}
{{- end }}
{{- /* Support adding config fields from both global or dialect-specific templates. */}}
{{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }}
{{- with $tmpls := matchTemplate (print $prefix "config/fields/*") }}
@@ -74,6 +89,16 @@ func Driver(driver dialect.Driver) Option {
}
}
{{- /* Additional dependency options. */}}
{{- range $dep := $deps }}
// {{ $dep.Option }} configures the {{ $dep.Field }}.
func {{ $dep.Option }}(v {{ $dep.Type }}) Option {
return func(c *config) {
c.{{ $dep.Field }} = v
}
}
{{- end }}
{{- /* Support adding config options from both global or dialect-specific templates. */}}
{{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }}
{{- with $tmpls := matchTemplate (print $prefix "config/options/*") }}

View File

@@ -10,4 +10,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"entgo.io/ent/entc/integration/hooks/ent/schema","Package":"entgo.io/ent/entc/integration/hooks/ent","Schemas":[{"name":"Card","config":{"Table":""},"edges":[{"name":"owner","type":"User","ref_name":"cards","unique":true,"inverse":true}],"fields":[{"name":"number","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"unknown","default_kind":24,"immutable":true,"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0},"comment":"Exact name written on card"},{"name":"created_at","type":{"Type":2,"Ident":"","PkgPath":"time","Nillable":false,"RType":null},"default":true,"default_kind":19,"position":{"Index":2,"MixedIn":false,"MixinIndex":0}},{"name":"in_hook","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"position":{"Index":3,"MixedIn":false,"MixinIndex":0},"comment":"A mandatory field that is set by the hook"}],"hooks":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0},{"Index":1,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"cards","type":"Card"},{"name":"friends","type":"User"},{"name":"best_friend","type":"User","unique":true}],"fields":[{"name":"version","type":{"Type":12,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":0,"default_kind":2,"position":{"Index":0,"MixedIn":true,"MixinIndex":0}},{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"worth","type":{"Type":17,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"hooks":[{"Index":0,"MixedIn":true,"MixinIndex":0}]}],"Features":["schema/snapshot"]}`
const Schema = `{"Schema":"entgo.io/ent/entc/integration/hooks/ent/schema","Package":"entgo.io/ent/entc/integration/hooks/ent","Schemas":[{"name":"Card","config":{"Table":""},"edges":[{"name":"owner","type":"User","ref_name":"cards","unique":true,"inverse":true}],"fields":[{"name":"number","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_value":"unknown","default_kind":24,"immutable":true,"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0},"comment":"Exact name written on card"},{"name":"created_at","type":{"Type":2,"Ident":"","PkgPath":"time","PkgName":"","Nillable":false,"RType":null},"default":true,"default_kind":19,"position":{"Index":2,"MixedIn":false,"MixinIndex":0}},{"name":"in_hook","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"position":{"Index":3,"MixedIn":false,"MixinIndex":0},"comment":"A mandatory field that is set by the hook"}],"hooks":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0},{"Index":1,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"cards","type":"Card"},{"name":"friends","type":"User"},{"name":"best_friend","type":"User","unique":true}],"fields":[{"name":"version","type":{"Type":12,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_value":0,"default_kind":2,"position":{"Index":0,"MixedIn":true,"MixinIndex":0}},{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"worth","type":{"Type":17,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"hooks":[{"Index":0,"MixedIn":true,"MixinIndex":0}]}],"Features":["schema/snapshot"]}`

View File

@@ -10,4 +10,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"entgo.io/ent/entc/integration/privacy/ent/schema","Package":"entgo.io/ent/entc/integration/privacy/ent","Schemas":[{"name":"Task","config":{"Table":""},"edges":[{"name":"teams","type":"Team"},{"name":"owner","type":"User","ref_name":"tasks","unique":true,"inverse":true}],"fields":[{"name":"title","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"description","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}},{"name":"status","type":{"Type":6,"Ident":"task.Status","PkgPath":"","Nillable":false,"RType":null},"enums":[{"N":"planned","V":"planned"},{"N":"in_progress","V":"in_progress"},{"N":"closed","V":"closed"}],"default":true,"default_value":"planned","default_kind":24,"position":{"Index":2,"MixedIn":false,"MixinIndex":0}},{"name":"uuid","type":{"Type":4,"Ident":"uuid.UUID","PkgPath":"github.com/google/uuid","Nillable":true,"RType":{"Name":"UUID","Ident":"uuid.UUID","Kind":17,"PkgPath":"github.com/google/uuid","Methods":{"ClockSequence":{"In":[],"Out":[{"Name":"int","Ident":"int","Kind":2,"PkgPath":"","Methods":null}]},"Domain":{"In":[],"Out":[{"Name":"Domain","Ident":"uuid.Domain","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]},"ID":{"In":[],"Out":[{"Name":"uint32","Ident":"uint32","Kind":10,"PkgPath":"","Methods":null}]},"MarshalBinary":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"MarshalText":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"NodeID":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}]},"Scan":{"In":[{"Name":"","Ident":"interface {}","Kind":20,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"String":{"In":[],"Out":[{"Name":"string","Ident":"string","Kind":24,"PkgPath":"","Methods":null}]},"Time":{"In":[],"Out":[{"Name":"Time","Ident":"uuid.Time","Kind":6,"PkgPath":"github.com/google/uuid","Methods":null}]},"URN":{"In":[],"Out":[{"Name":"string","Ident":"string","Kind":24,"PkgPath":"","Methods":null}]},"UnmarshalBinary":{"In":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"UnmarshalText":{"In":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"Value":{"In":[],"Out":[{"Name":"Value","Ident":"driver.Value","Kind":20,"PkgPath":"database/sql/driver","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"Variant":{"In":[],"Out":[{"Name":"Variant","Ident":"uuid.Variant","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]},"Version":{"In":[],"Out":[{"Name":"Version","Ident":"uuid.Version","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]}}}},"optional":true,"position":{"Index":3,"MixedIn":false,"MixinIndex":0}}],"hooks":[{"Index":0,"MixedIn":false,"MixinIndex":0}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Team","config":{"Table":""},"edges":[{"name":"tasks","type":"Task","ref_name":"teams","inverse":true},{"name":"users","type":"User","ref_name":"teams","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"teams","type":"Team"},{"name":"tasks","type":"Task"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"unique":true,"immutable":true,"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"age","type":{"Type":17,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]}],"Features":["privacy","entql","schema/snapshot"]}`
const Schema = `{"Schema":"entgo.io/ent/entc/integration/privacy/ent/schema","Package":"entgo.io/ent/entc/integration/privacy/ent","Schemas":[{"name":"Task","config":{"Table":""},"edges":[{"name":"teams","type":"Team"},{"name":"owner","type":"User","ref_name":"tasks","unique":true,"inverse":true}],"fields":[{"name":"title","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"description","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}},{"name":"status","type":{"Type":6,"Ident":"task.Status","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"enums":[{"N":"planned","V":"planned"},{"N":"in_progress","V":"in_progress"},{"N":"closed","V":"closed"}],"default":true,"default_value":"planned","default_kind":24,"position":{"Index":2,"MixedIn":false,"MixinIndex":0}},{"name":"uuid","type":{"Type":4,"Ident":"uuid.UUID","PkgPath":"github.com/google/uuid","PkgName":"","Nillable":true,"RType":{"Name":"UUID","Ident":"uuid.UUID","Kind":17,"PkgPath":"github.com/google/uuid","Methods":{"ClockSequence":{"In":[],"Out":[{"Name":"int","Ident":"int","Kind":2,"PkgPath":"","Methods":null}]},"Domain":{"In":[],"Out":[{"Name":"Domain","Ident":"uuid.Domain","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]},"ID":{"In":[],"Out":[{"Name":"uint32","Ident":"uint32","Kind":10,"PkgPath":"","Methods":null}]},"MarshalBinary":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"MarshalText":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"NodeID":{"In":[],"Out":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}]},"Scan":{"In":[{"Name":"","Ident":"interface {}","Kind":20,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"String":{"In":[],"Out":[{"Name":"string","Ident":"string","Kind":24,"PkgPath":"","Methods":null}]},"Time":{"In":[],"Out":[{"Name":"Time","Ident":"uuid.Time","Kind":6,"PkgPath":"github.com/google/uuid","Methods":null}]},"URN":{"In":[],"Out":[{"Name":"string","Ident":"string","Kind":24,"PkgPath":"","Methods":null}]},"UnmarshalBinary":{"In":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"UnmarshalText":{"In":[{"Name":"","Ident":"[]uint8","Kind":23,"PkgPath":"","Methods":null}],"Out":[{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"Value":{"In":[],"Out":[{"Name":"Value","Ident":"driver.Value","Kind":20,"PkgPath":"database/sql/driver","Methods":null},{"Name":"error","Ident":"error","Kind":20,"PkgPath":"","Methods":null}]},"Variant":{"In":[],"Out":[{"Name":"Variant","Ident":"uuid.Variant","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]},"Version":{"In":[],"Out":[{"Name":"Version","Ident":"uuid.Version","Kind":8,"PkgPath":"github.com/google/uuid","Methods":null}]}}}},"optional":true,"position":{"Index":3,"MixedIn":false,"MixinIndex":0}}],"hooks":[{"Index":0,"MixedIn":false,"MixinIndex":0}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Team","config":{"Table":""},"edges":[{"name":"tasks","type":"Task","ref_name":"teams","inverse":true},{"name":"users","type":"User","ref_name":"teams","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"teams","type":"Team"},{"name":"tasks","type":"Task"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"unique":true,"immutable":true,"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"age","type":{"Type":17,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]}],"Features":["privacy","entql","schema/snapshot"]}`

View File

@@ -7,6 +7,9 @@
package ent
import (
"io"
"net/http"
"entgo.io/ent"
"entgo.io/ent/dialect"
)
@@ -23,7 +26,9 @@ type config struct {
// log used for logging on debug mode.
log func(...interface{})
// hooks to execute on mutations.
hooks *hooks
hooks *hooks
HTTPClient *http.Client
Writer io.Writer
}
// hooks per client, for fast access.
@@ -61,3 +66,17 @@ func Driver(driver dialect.Driver) Option {
c.driver = driver
}
}
// HTTPClient configures the HTTPClient.
func HTTPClient(v *http.Client) Option {
return func(c *config) {
c.HTTPClient = v
}
}
// Writer configures the Writer.
func Writer(v io.Writer) Option {
return func(c *config) {
c.Writer = v
}
}

View File

@@ -10,12 +10,14 @@ package main
import (
"fmt"
"log"
"net/http"
"strings"
"text/template"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
)
func main() {
@@ -23,9 +25,20 @@ func main() {
if err != nil {
log.Fatalf("creating extension: %v", err)
}
// A usage for custom options to configure the
// A usage for custom options to configure the code generator to use
// an extension and inject external dependencies in the generated API.
opts := []entc.Option{
entc.Extensions(ex),
entc.Dependency(
entc.DependencyType(&http.Client{}),
),
entc.Dependency(
entc.DependencyName("Writer"),
entc.DependencyTypeInfo(&field.TypeInfo{
Ident: "io.Writer",
PkgPath: "io",
}),
),
}
err = entc.Generate("./schema", &gen.Config{
Header: `

View File

@@ -8,20 +8,36 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"entgo.io/ent/examples/entcpkg/ent"
"entgo.io/ent/examples/entcpkg/ent/hook"
_ "github.com/mattn/go-sqlite3"
)
func Example_EntcPkg() {
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
client, err := ent.Open(
"sqlite3",
"file:ent?mode=memory&cache=shared&_fk=1",
ent.Writer(os.Stdout),
ent.HTTPClient(http.DefaultClient),
)
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
// An example for using the injected dependencies in the generated builders.
client.User.Use(func(next ent.Mutator) ent.Mutator {
return hook.UserFunc(func(ctx context.Context, m *ent.UserMutation) (ent.Value, error) {
_ = m.HTTPClient
_ = m.Writer
return next.Mutate(ctx, m)
})
})
ctx := context.Background()
// run the auto migration tool.
// Run the auto migration tool.
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}

View File

@@ -10,4 +10,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"entgo.io/ent/examples/privacyadmin/ent/schema","Package":"entgo.io/ent/examples/privacyadmin/ent","Schemas":[{"name":"User","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":false,"MixinIndex":0}]}],"Features":["schema/snapshot","privacy"]}`
const Schema = `{"Schema":"entgo.io/ent/examples/privacyadmin/ent/schema","Package":"entgo.io/ent/examples/privacyadmin/ent","Schemas":[{"name":"User","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":false,"MixinIndex":0}]}],"Features":["schema/snapshot","privacy"]}`

View File

@@ -10,4 +10,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"entgo.io/ent/examples/privacytenant/ent/schema","Package":"entgo.io/ent/examples/privacytenant/ent","Schemas":[{"name":"Group","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"users","type":"User","ref_name":"groups","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Tenant","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"groups","type":"Group"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"foods","type":{"Type":3,"Ident":"[]string","PkgPath":"","Nillable":true,"RType":{"Name":"","Ident":"[]string","Kind":23,"PkgPath":"","Methods":{}}},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1}]}],"Features":["privacy","entql","schema/snapshot"]}`
const Schema = `{"Schema":"entgo.io/ent/examples/privacytenant/ent/schema","Package":"entgo.io/ent/examples/privacytenant/ent","Schemas":[{"name":"Group","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"users","type":"User","ref_name":"groups","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Tenant","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"groups","type":"Group"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"foods","type":{"Type":3,"Ident":"[]string","PkgPath":"","PkgName":"","Nillable":true,"RType":{"Name":"","Ident":"[]string","Kind":23,"PkgPath":"","Methods":{}}},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1}]}],"Features":["privacy","entql","schema/snapshot"]}`

View File

@@ -10,4 +10,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"entgo.io/ent/examples/version/ent/schema","Package":"entgo.io/ent/examples/version/ent","Schemas":[{"name":"User","config":{"Table":""},"fields":[{"name":"version","type":{"Type":13,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_kind":19,"position":{"Index":0,"MixedIn":true,"MixinIndex":0},"comment":"Unix time of when the latest update occurred"},{"name":"status","type":{"Type":6,"Ident":"user.Status","PkgPath":"","Nillable":false,"RType":null},"enums":[{"N":"online","V":"online"},{"N":"offline","V":"offline"}],"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}]}],"Features":["schema/snapshot"]}`
const Schema = `{"Schema":"entgo.io/ent/examples/version/ent/schema","Package":"entgo.io/ent/examples/version/ent","Schemas":[{"name":"User","config":{"Table":""},"fields":[{"name":"version","type":{"Type":13,"Ident":"","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"default":true,"default_kind":19,"position":{"Index":0,"MixedIn":true,"MixinIndex":0},"comment":"Unix time of when the latest update occurred"},{"name":"status","type":{"Type":6,"Ident":"user.Status","PkgPath":"","PkgName":"","Nillable":false,"RType":null},"enums":[{"N":"online","V":"online"},{"N":"offline","V":"offline"}],"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}]}],"Features":["schema/snapshot"]}`

View File

@@ -85,8 +85,9 @@ func (t Type) ConstName() string {
type TypeInfo struct {
Type Type
Ident string
PkgPath string
Nillable bool // slices or pointers.
PkgPath string // import path.
PkgName string // local package name.
Nillable bool // slices or pointers.
RType *RType
}