diff --git a/extension.go b/extension.go index 9be9987..2ba207e 100644 --- a/extension.go +++ b/extension.go @@ -21,6 +21,9 @@ type ( Target string // The Views created by entoas. Views map[string]*entoas.View + // Whether to allow the client to supply IDs in case uuids are used. + // AllowClientUUIDs, when enabled, allows the built-in "id" field as part of the payload for create, allowing the client to supply UUIDs as primary keys and for idempotency. + AllowClientUUIDs bool } // Extension implements entc.Extension interface providing integration with ogen. Extension struct { @@ -41,12 +44,14 @@ func NewExtension(spec *ogen.Spec, opts ...ExtensionOption) (*Extension, error) if spec == nil { return nil, errors.New("ogent: spec cannot be nil") } - ex := &Extension{spec: spec, cfg: new(Config), templates: []*gen.Template{templates}} + ex := &Extension{spec: spec, cfg: new(Config)} for _, opt := range opts { if err := opt(ex); err != nil { return nil, err } } + ex.templates = []*gen.Template{genTemplates(ex.cfg)} + return ex, nil } @@ -58,6 +63,14 @@ func Target(t string) ExtensionOption { } } +// AllowClientUUIDs allows the client to supply IDs in case uuids are used. +func AllowClientUUIDs() ExtensionOption { + return func(ex *Extension) error { + ex.cfg.AllowClientUUIDs = true + return nil + } +} + // Templates adds the given templates to the code generator. func Templates(ts ...*gen.Template) ExtensionOption { return func(ex *Extension) error { diff --git a/template.go b/template.go index ccfe02b..822dfdc 100644 --- a/template.go +++ b/template.go @@ -18,8 +18,14 @@ import ( var ( //go:embed template templateDir embed.FS +) + +func genTemplates(cfg *Config) *gen.Template { // funcMap contains extra template functions used by ogent. - funcMap = template.FuncMap{ + funcMap := template.FuncMap{ + "allowClientUUIDs": func() bool { + return cfg.AllowClientUUIDs + }, "convertTo": convertTo, "eagerLoad": eagerLoad, "edgeOperations": entoas.EdgeOperations, @@ -43,8 +49,9 @@ var ( "viewNameEdge": entoas.ViewNameEdge, } // templates holds all templates used by ogent. - templates = gen.MustParse(gen.NewTemplate("ogent").Funcs(funcMap).ParseFS(templateDir, "template/*tmpl")) -) + templates := gen.MustParse(gen.NewTemplate("ogent").Funcs(funcMap).ParseFS(templateDir, "template/*tmpl")) + return templates +} // eagerLoad returns the Go expression to eager load the required edges on the node operation. func eagerLoad(n *gen.Type, op entoas.Operation) (string, error) { @@ -237,7 +244,7 @@ func setFieldExpr(f *gen.Field, schema, rec, ident string) (string, error) { case Date: opt = "Date" case Time: - opt = "Time" + opt = "Time" case Duration: opt = "Duration" case UUID: diff --git a/template/create.tmpl b/template/create.tmpl index cb4f5dc..62e17a5 100644 --- a/template/create.tmpl +++ b/template/create.tmpl @@ -1,5 +1,12 @@ {{ define "ogent/ogent/helper/create" }}{{/* gotype: entgo.io/ent/entc/gen.Type */}} b := h.client.{{ $.Name }}.Create() + {{ if allowClientUUIDs }} + {{ if eq $.ID.Type.Ident "uuid.UUID" }} + if v, ok := req.{{ $.ID.StructField }}.Get(); ok && v != uuid.Nil { + b.Set{{ $.ID.StructField }}(v) + } + {{ end }} + {{ end }} // Add all fields. {{- range $f := $.Fields }} {{- $a := fieldAnnotation $f }}