Cleaner store API
This commit is contained in:
+3
-11
@@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -94,20 +93,13 @@ type (
|
||||
)
|
||||
|
||||
// Processes multiple action definitions
|
||||
func procActions() ([]*actionsDef, error) {
|
||||
func procActions(mm ...string) (dd []*actionsDef, err error) {
|
||||
var (
|
||||
f io.ReadCloser
|
||||
d *actionsDef
|
||||
|
||||
dd = make([]*actionsDef, 0)
|
||||
)
|
||||
|
||||
// <app>/service/<kind>_actions.yaml
|
||||
mm, err := filepath.Glob(filepath.Join("*", "service", "*_actions.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("glob failed: %w", err)
|
||||
}
|
||||
|
||||
dd = make([]*actionsDef, 0)
|
||||
for _, m := range mm {
|
||||
err = func() error {
|
||||
if f, err = os.Open(m); err != nil {
|
||||
@@ -284,7 +276,7 @@ func severityConstName(s string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func genActions(tpl *template.Template, dd []*actionsDef) (err error) {
|
||||
func genActions(tpl *template.Template, dd ...*actionsDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplActionsGen = tpl.Lookup("actions.gen.go.tpl")
|
||||
|
||||
@@ -20,40 +20,40 @@ import (
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
{{ pubIdent $.Endpoint.Entrypoint }}API interface {
|
||||
{{ export $.Endpoint.Entrypoint }}API interface {
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
{{ pubIdent $a.Name }}(context.Context, *request.{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) (interface{}, error)
|
||||
{{ export $a.Name }}(context.Context, *request.{{ export $.Endpoint.Entrypoint $a.Name }}) (interface{}, error)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
{{ pubIdent .Endpoint.Entrypoint }} struct {
|
||||
{{ export .Endpoint.Entrypoint }} struct {
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
{{ pubIdent $a.Name }} func(http.ResponseWriter, *http.Request)
|
||||
{{ export $a.Name }} func(http.ResponseWriter, *http.Request)
|
||||
{{- end }}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
func {{ pubIdent "New" $.Endpoint.Entrypoint }}(h {{ pubIdent $.Endpoint.Entrypoint }}API) *{{ pubIdent $.Endpoint.Entrypoint }} {
|
||||
return &{{ pubIdent $.Endpoint.Entrypoint }}{
|
||||
func {{ export "New" $.Endpoint.Entrypoint }}(h {{ export $.Endpoint.Entrypoint }}API) *{{ export $.Endpoint.Entrypoint }} {
|
||||
return &{{ export $.Endpoint.Entrypoint }}{
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
{{ pubIdent $a.Name }}: func(w http.ResponseWriter, r *http.Request) {
|
||||
{{ export $a.Name }}: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.New{{ pubIdent $.Endpoint.Entrypoint $a.Name }}()
|
||||
params := request.New{{ export $.Endpoint.Entrypoint $a.Name }}()
|
||||
if err := params.Fill(r); err != nil {
|
||||
logger.LogParamError("{{ pubIdent $.Endpoint.Entrypoint }}.{{ pubIdent $a.Name }}", r, err)
|
||||
logger.LogParamError("{{ export $.Endpoint.Entrypoint }}.{{ export $a.Name }}", r, err)
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.{{ pubIdent $a.Name }}(r.Context(), params)
|
||||
value, err := h.{{ export $a.Name }}(r.Context(), params)
|
||||
if err != nil {
|
||||
logger.LogControllerError("{{ pubIdent $.Endpoint.Entrypoint }}.{{ pubIdent $a.Name }}", r, err, params.Auditable())
|
||||
logger.LogControllerError("{{ export $.Endpoint.Entrypoint }}.{{ export $a.Name }}", r, err, params.Auditable())
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
logger.LogControllerCall("{{ pubIdent $.Endpoint.Entrypoint }}.{{ pubIdent $a.Name }}", r, params.Auditable())
|
||||
logger.LogControllerCall("{{ export $.Endpoint.Entrypoint }}.{{ export $a.Name }}", r, params.Auditable())
|
||||
if !serveHTTP(value, w, r) {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
@@ -62,12 +62,12 @@ func {{ pubIdent "New" $.Endpoint.Entrypoint }}(h {{ pubIdent $.Endpoint.Entrypo
|
||||
}
|
||||
}
|
||||
|
||||
func (h {{ pubIdent $.Endpoint.Entrypoint }}) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
func (h {{ export $.Endpoint.Entrypoint }}) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
r.{{ pubIdent ( toLower $a.Method ) }}("{{ $.Endpoint.Path }}{{ $a.Path }}", h.{{ pubIdent $a.Name }})
|
||||
r.{{ export ( toLower $a.Method ) }}("{{ $.Endpoint.Path }}{{ $a.Path }}", h.{{ export $a.Name }})
|
||||
{{- end }}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,43 +33,43 @@ var (
|
||||
type (
|
||||
// Internal API interface
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
{{ pubIdent $.Endpoint.Entrypoint $a.Name }} struct {
|
||||
{{ export $.Endpoint.Entrypoint $a.Name }} struct {
|
||||
{{- range $p := $a.Params.All }}
|
||||
// {{ pubIdent $p.Name }} {{ $p.Origin }} parameter
|
||||
// {{ export $p.Name }} {{ $p.Origin }} parameter
|
||||
//
|
||||
// {{ $p.Title }}
|
||||
{{ pubIdent $p.Name }} {{ $p.Type }} {{ $p.FieldTag }}
|
||||
{{ export $p.Name }} {{ $p.Type }} {{ $p.FieldTag }}
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
// {{ pubIdent "New" $.Endpoint.Entrypoint $a.Name }} request
|
||||
func {{ pubIdent "New" $.Endpoint.Entrypoint $a.Name }}() *{{ pubIdent $.Endpoint.Entrypoint $a.Name }} {
|
||||
return &{{ pubIdent $.Endpoint.Entrypoint $a.Name }}{}
|
||||
// {{ export "New" $.Endpoint.Entrypoint $a.Name }} request
|
||||
func {{ export "New" $.Endpoint.Entrypoint $a.Name }}() *{{ export $.Endpoint.Entrypoint $a.Name }} {
|
||||
return &{{ export $.Endpoint.Entrypoint $a.Name }}{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r {{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Auditable() map[string]interface{} {
|
||||
func (r {{ export $.Endpoint.Entrypoint $a.Name }}) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
{{- range $p := $a.Params.All }}
|
||||
"{{ $p.Name }}": r.{{ pubIdent $p.Name }},
|
||||
"{{ $p.Name }}": r.{{ export $p.Name }},
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
{{- range $p := $a.Params.All }}
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r {{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Get{{ pubIdent $p.Name }}() {{ $p.Type }} {
|
||||
return r.{{ pubIdent $p.Name }}
|
||||
func (r {{ export $.Endpoint.Entrypoint $a.Name }}) Get{{ export $p.Name }}() {{ $p.Type }} {
|
||||
return r.{{ export $p.Name }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (err error) {
|
||||
func (r *{{ export $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (err error) {
|
||||
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
@@ -89,7 +89,7 @@ func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (
|
||||
{{ range $p := $a.Params.Get }}
|
||||
{{- if not $p.IsSlice }}
|
||||
if val, ok := tmp["{{ $p.Name }}"]; ok && len(val) > 0 {
|
||||
r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,12 +97,12 @@ func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (
|
||||
{{- end }}
|
||||
{{- if $p.IsSlice }}
|
||||
if val, ok := tmp["{{ $p.Name }}[]"]; ok {
|
||||
r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["{{ $p.Name }}"]; ok {
|
||||
r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -121,13 +121,13 @@ func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (
|
||||
// POST params
|
||||
{{ range $p := $a.Params.Post }}
|
||||
{{ if $p.IsUpload }}
|
||||
if _, r.{{ pubIdent $p.Name }}, err = req.FormFile("{{ $p.Name }}"); err != nil {
|
||||
if _, r.{{ export $p.Name }}, err = req.FormFile("{{ $p.Name }}"); err != nil {
|
||||
return fmt.Errorf("error processing uploaded file: %w", err)
|
||||
}
|
||||
{{ else }}
|
||||
{{- if not $p.IsSlice }}
|
||||
if val, ok := req.Form["{{ $p.Name }}"]; ok && len(val) > 0 {
|
||||
r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (
|
||||
{{- end }}
|
||||
{{- if $p.IsSlice }}
|
||||
//if val, ok := req.Form["{{ $p.Name }}[]"]; ok && len(val) > 0 {
|
||||
// r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
// r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
@@ -153,7 +153,7 @@ func (r *{{ pubIdent $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (
|
||||
// path params
|
||||
{{ range $p := $a.Params.Path }}
|
||||
val = chi.URLParam(req, "{{ $p.Name }}")
|
||||
r.{{ pubIdent $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package store
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Template: pkg/codegen/assets/store_base.gen.go.tpl
|
||||
// Definitions: {{ .Source }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
||||
import (
|
||||
"context"
|
||||
{{- range .Import }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
type (
|
||||
{{- $Types := .Types }}
|
||||
{{- $Fields := .Fields }}
|
||||
|
||||
{{ export .Types.Plural }} interface {
|
||||
|
||||
{{ if .Publish }}
|
||||
{{- if .Search.Enable }}
|
||||
Search{{ export $Types.Plural }}(ctx context.Context{{ template "extraArgsDef" . }}, f {{ $Types.GoFilterType }}) ({{ $Types.GoSetType }}, {{ $Types.GoFilterType }}, error)
|
||||
{{- end }}
|
||||
{{- range .Lookups }}
|
||||
Lookup{{ export $Types.Singular }}By{{ export .Suffix }}(ctx context.Context{{ template "extraArgsDef" $ }}{{- range $field := .Fields }}, {{ cc2underscore $field }} {{ ($field | $Fields.Find).Type }}{{- end }}) (*{{ $Types.GoType }}, error)
|
||||
{{- end }}
|
||||
{{ if .Create.Enable }}
|
||||
Create{{ export $Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error
|
||||
{{- end }}
|
||||
{{ if .Update.Enable }}
|
||||
Update{{ export $Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error
|
||||
Partial{{ export $Types.Singular }}Update(ctx context.Context{{ template "extraArgsDef" . }}, onlyColumns []string, rr ... *{{ $Types.GoType }}) error
|
||||
{{- end }}
|
||||
{{ if .Upsert.Enable }}
|
||||
Upsert{{ export $Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error
|
||||
{{- end }}
|
||||
{{ if .Delete.Enable }}
|
||||
Delete{{ export $Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error
|
||||
Delete{{ export $Types.Singular }}By{{ template "primaryKeySuffix" $Fields }}(ctx context.Context{{ template "extraArgsDef" . }} {{ template "primaryKeyArgsDef" $Fields }}) error
|
||||
{{- end }}
|
||||
|
||||
Truncate{{ export $Types.Plural }}(ctx context.Context{{ template "extraArgsDef" . }}) error
|
||||
{{ end }}
|
||||
|
||||
{{- if .Functions}}
|
||||
// Additional custom functions
|
||||
{{- range .Functions }}
|
||||
|
||||
// {{ .Name }} (custom function)
|
||||
{{ .Name }}(ctx context.Context{{ template "extraArgsDef" . }}) ({{ join ", " .Return }})
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
}
|
||||
)
|
||||
|
||||
{{/* convering scenario with non-exported main functions and no additional functions defined
|
||||
where we get "imported and not used error" */}}
|
||||
var _ *{{ $Types.GoType }}
|
||||
var _ context.Context
|
||||
|
||||
{{ if .Publish }}
|
||||
{{- if .Search.Enable }}
|
||||
// Search{{ export $.Types.Plural }} returns all matching {{ $.Types.Plural }} from store
|
||||
func Search{{ export $Types.Plural }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, f {{ $Types.GoFilterType }}) ({{ $Types.GoSetType }}, {{ $Types.GoFilterType }}, error) {
|
||||
return s.Search{{ export $Types.Plural }}(ctx{{ template "extraArgsCall" . }}, f)
|
||||
}
|
||||
{{- end -}}
|
||||
|
||||
{{ range .Lookups }}
|
||||
|
||||
// Lookup{{ export $.Types.Singular }}By{{ export .Suffix }} {{ comment .Description true -}}
|
||||
func Lookup{{ export $Types.Singular }}By{{ export .Suffix }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" $ }}{{- range $field := .Fields }}, {{ cc2underscore $field }} {{ ($field | $Fields.Find).Type }}{{- end }}) (*{{ $Types.GoType }}, error) {
|
||||
return s.Lookup{{ export $Types.Singular }}By{{ export .Suffix }}(ctx{{ template "extraArgsCall" $ }}{{- range $field := .Fields }}, {{ cc2underscore $field }}{{- end }})
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// Create{{ export $.Types.Singular }} creates one or more {{ $.Types.Plural }} in store
|
||||
func Create{{ export $Types.Singular }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error {
|
||||
return s.Create{{ export $Types.Singular }}(ctx{{ template "extraArgsCall" . }}, rr... )
|
||||
}
|
||||
|
||||
{{ if .Update.Enable }}
|
||||
// Update{{ export $.Types.Singular }} updates one or more (existing) {{ $.Types.Plural }} in store
|
||||
func Update{{ export $Types.Singular }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error {
|
||||
return s.Update{{ export $Types.Singular }}(ctx{{ template "extraArgsCall" . }}, rr... )
|
||||
}
|
||||
|
||||
// Partial{{ export $.Types.Singular }}Update updates one or more existing {{ $.Types.Plural }} in store
|
||||
func Partial{{ export $Types.Singular }}Update(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, onlyColumns []string, rr ... *{{ $Types.GoType }}) error {
|
||||
return s.Partial{{ export $Types.Singular }}Update(ctx{{ template "extraArgsCall" . }}, onlyColumns, rr...)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Upsert.Enable }}
|
||||
// Upsert{{ export $.Types.Singular }} creates new or updates existing one or more {{ $.Types.Plural }} in store
|
||||
func Upsert{{ export $Types.Singular }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error {
|
||||
return s.Upsert{{ export $Types.Singular }}(ctx{{ template "extraArgsCall" . }}, rr... )
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Delete.Enable }}
|
||||
// Delete{{ export $.Types.Singular }} Deletes one or more {{ $.Types.Plural }} from store
|
||||
func Delete{{ export $Types.Singular }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}, rr ... *{{ $Types.GoType }}) error {
|
||||
return s.Delete{{ export $Types.Singular }}(ctx{{ template "extraArgsCall" . }}, rr...)
|
||||
}
|
||||
|
||||
// Delete{{ export $.Types.Singular }}By{{ template "primaryKeySuffix" $.Fields }} Deletes {{ $.Types.Singular }} from store
|
||||
func Delete{{ export $Types.Singular }}By{{ template "primaryKeySuffix" $Fields }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }} {{ template "primaryKeyArgsDef" $Fields }}) error {
|
||||
return s.Delete{{ export $Types.Singular }}By{{ template "primaryKeySuffix" $Fields }}(ctx{{ template "extraArgsCall" . }}{{ template "primaryKeyArgsCall" $Fields }})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Truncate{{ export $.Types.Plural }} Deletes all {{ $.Types.Plural }} from store
|
||||
func Truncate{{ export $Types.Plural }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}) error {
|
||||
return s.Truncate{{ export $Types.Plural }}(ctx{{ template "extraArgsCall" . }})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range .Functions }}
|
||||
func {{ .Name }}(ctx context.Context, s {{ export $Types.Plural }}{{ template "extraArgsDef" . }}) ({{ join ", " .Return }}) {
|
||||
return s.{{ .Name }}(ctx{{ template "extraArgsCall" . }})
|
||||
}
|
||||
{{ end }}
|
||||
@@ -1,74 +0,0 @@
|
||||
package bulk
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// Definitions file that controls how this file is generated:
|
||||
// {{ .Source }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
{{- range $import := $.Import }}
|
||||
{{ normalizeImport $import }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
type (
|
||||
{{ unpubIdent $.Types.Singular }}Create struct {
|
||||
Done chan struct{}
|
||||
res *{{ $.Types.GoType }}
|
||||
err error
|
||||
}
|
||||
|
||||
{{ unpubIdent $.Types.Singular }}Update struct {
|
||||
Done chan struct{}
|
||||
res *{{ $.Types.GoType }}
|
||||
err error
|
||||
}
|
||||
|
||||
{{ unpubIdent $.Types.Singular }}Remove struct {
|
||||
Done chan struct{}
|
||||
res *{{ $.Types.GoType }}
|
||||
err error
|
||||
}
|
||||
)
|
||||
|
||||
// Create{{ pubIdent $.Types.Singular }} creates a new {{ pubIdent $.Types.Singular }}
|
||||
// create job that can be pushed to store's transaction handler
|
||||
func Create{{ pubIdent $.Types.Singular }}(res *{{ $.Types.GoType }}) *{{ unpubIdent $.Types.Singular }}Create {
|
||||
return &{{ unpubIdent $.Types.Singular }}Create{res: res}
|
||||
}
|
||||
|
||||
// Do Executes {{ unpubIdent $.Types.Singular }}Create job
|
||||
func (j *{{ unpubIdent $.Types.Singular }}Create) Do(ctx context.Context, s storeInterface) error {
|
||||
j.err = s.Create{{ pubIdent $.Types.Singular }}(ctx, j.res)
|
||||
j.Done <- struct{}{}
|
||||
return j.err
|
||||
}
|
||||
|
||||
// Update{{ pubIdent $.Types.Singular }} creates a new {{ pubIdent $.Types.Singular }}
|
||||
// update job that can be pushed to store's transaction handler
|
||||
func Update{{ pubIdent $.Types.Singular }}(res *{{ $.Types.GoType }}) *{{ unpubIdent $.Types.Singular }}Update {
|
||||
return &{{ unpubIdent $.Types.Singular }}Update{res: res}
|
||||
}
|
||||
|
||||
// Do Executes {{ unpubIdent $.Types.Singular }}Update job
|
||||
func (j *{{ unpubIdent $.Types.Singular }}Update) Do(ctx context.Context, s storeInterface) error {
|
||||
j.err = s.Update{{ pubIdent $.Types.Singular }}(ctx, j.res)
|
||||
j.Done <- struct{}{}
|
||||
return j.err
|
||||
}
|
||||
|
||||
// Remove{{ pubIdent $.Types.Singular }} creates a new {{ pubIdent $.Types.Singular }}
|
||||
// remove job that can be pushed to store's transaction handler
|
||||
func Remove{{ pubIdent $.Types.Singular }}(res *{{ $.Types.GoType }}) *{{ unpubIdent $.Types.Singular }}Remove {
|
||||
return &{{ unpubIdent $.Types.Singular }}Remove{res: res}
|
||||
}
|
||||
|
||||
// Do Executes {{ unpubIdent $.Types.Singular }}Remove job
|
||||
func (j *{{ unpubIdent $.Types.Singular }}Remove) Do(ctx context.Context, s storeInterface) error {
|
||||
j.err = s.Remove{{ pubIdent $.Types.Singular }}(ctx, j.res)
|
||||
j.Done <- struct{}{}
|
||||
return j.err
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package {{ .Package }}
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// - {{ .Source }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
{{- range .Import }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
type (
|
||||
{{- $Types := .Types }}
|
||||
{{- $Fields := .Fields }}
|
||||
|
||||
{{ unpubIdent .Types.Plural }}Store interface {
|
||||
{{- if not .Search.Disable }}
|
||||
Search{{ pubIdent $Types.Plural }}(ctx context.Context, f {{ $Types.GoFilterType }}) ({{ $Types.GoSetType }}, {{ $Types.GoFilterType }}, error)
|
||||
{{- end }}
|
||||
{{- range .Lookups }}
|
||||
Lookup{{ pubIdent $Types.Singular }}By{{ pubIdent .Suffix }}(ctx context.Context{{- range $field := .Fields }}, {{ cc2underscore $field }} {{ ($field | $Fields.Find).Type }}{{- end }}) (*{{ $Types.GoType }}, error)
|
||||
{{- end }}
|
||||
Create{{ pubIdent $Types.Singular }}(ctx context.Context, rr ... *{{ $Types.GoType }}) error
|
||||
Update{{ pubIdent $Types.Singular }}(ctx context.Context, rr ... *{{ $Types.GoType }}) error
|
||||
PartialUpdate{{ pubIdent $Types.Singular }}(ctx context.Context, onlyColumns []string, rr ... *{{ $Types.GoType }}) error
|
||||
Remove{{ pubIdent $Types.Singular }}(ctx context.Context, rr ... *{{ $Types.GoType }}) error
|
||||
Remove{{ pubIdent $Types.Singular }}By{{ template "primaryKeySuffix" $Fields }}(ctx context.Context {{ template "primaryKeyArgs" $Fields }}) error
|
||||
|
||||
Truncate{{ pubIdent $Types.Plural }}(ctx context.Context) error
|
||||
}
|
||||
)
|
||||
@@ -1,8 +1,8 @@
|
||||
package {{ .Package }}
|
||||
package store
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Template: pkg/store_interfaces_joined.gen.go.tpl
|
||||
// Template: pkg/codegen/assets/store_interfaces_joined.gen.go.tpl
|
||||
// Definitions:
|
||||
{{- range .Definitions }}
|
||||
// - {{ .Source }}
|
||||
@@ -12,11 +12,21 @@ package {{ .Package }}
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
// Interface combines interfaces of all supported store interfaces
|
||||
storeGeneratedInterfaces interface {
|
||||
Transactionable interface {
|
||||
Tx(context.Context, func(context.Context, Storable) error) error
|
||||
}
|
||||
|
||||
// Sortable interface combines interfaces of all supported store interfaces
|
||||
Storable interface {
|
||||
Transactionable
|
||||
|
||||
{{ range .Definitions -}}
|
||||
{{ unpubIdent .Types.Plural }}Store
|
||||
{{ export .Types.Plural }}
|
||||
{{ end }}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{- define "primaryKeyArgs" -}}
|
||||
{{- define "primaryKeyArgsDef" -}}
|
||||
{{- range $field := . -}}
|
||||
{{- if $field.IsPrimaryKey -}}
|
||||
, {{ $field.Arg }} {{ camelCase $field.Type }}
|
||||
@@ -6,12 +6,44 @@
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "primaryKeyArgsCall" -}}
|
||||
{{- range $field := . -}}
|
||||
{{- if $field.IsPrimaryKey -}}
|
||||
, {{ $field.Arg }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "primaryKeySuffix" -}}
|
||||
{{- range $field := . }}{{ if $field.IsPrimaryKey }}{{ $field.Field }}{{ end }}{{ end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "partialUpdateArgs" -}}
|
||||
{{- range .Args -}}
|
||||
, {{ .Arg }} {{ .Type }}
|
||||
{{ define "extraArgsDefFirst" }}
|
||||
{{- range .Arguments -}}
|
||||
_{{ .Name }} {{ .Type }},
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ define "extraArgsDef" }}
|
||||
{{- range .Arguments -}}
|
||||
, _{{ .Name }} {{ .Type }}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ define "extraArgsDefTypesOnly" }}
|
||||
{{- range .Arguments -}}
|
||||
, {{ .Type }}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ define "extraArgsCallFirst" }}
|
||||
{{- range .Arguments -}}
|
||||
_{{ .Name }},
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ define "extraArgsCall" }}
|
||||
{{- range .Arguments -}}
|
||||
, _{{ .Name }}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
@@ -10,11 +10,13 @@ package rdbms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
{{- if not $.Search.DisablePaging }}
|
||||
{{- if $.Search.EnablePaging }}
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"strings"
|
||||
{{- end }}
|
||||
{{- range $import := $.Import }}
|
||||
@@ -22,24 +24,41 @@ import (
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
{{ if not $.Search.Disable }}
|
||||
// Search{{ pubIdent $.Types.Plural }} returns all matching rows
|
||||
var _ = errors.Is
|
||||
|
||||
const (
|
||||
{{- if .Create.Enable }}
|
||||
TriggerBefore{{ export $.Types.Singular }}Create triggerKey = "{{ unexport $.Types.Singular }}BeforeCreate"
|
||||
{{- end }}
|
||||
{{- if .Update.Enable }}
|
||||
TriggerBefore{{ export $.Types.Singular }}Update triggerKey = "{{ unexport $.Types.Singular }}BeforeUpdate"
|
||||
{{- end }}
|
||||
{{- if .Upsert.Enable }}
|
||||
TriggerBefore{{ export $.Types.Singular }}Upsert triggerKey = "{{ unexport $.Types.Singular }}BeforeUpsert"
|
||||
{{- end }}
|
||||
{{- if .Delete.Enable }}
|
||||
TriggerBefore{{ export $.Types.Singular }}Delete triggerKey = "{{ unexport $.Types.Singular }}BeforeDelete"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
{{ if $.Search.Enable }}
|
||||
// {{ toggleExport .Search.Export "Search" $.Types.Plural }} returns all matching rows
|
||||
//
|
||||
// This function calls convert{{ pubIdent $.Types.Singular }}Filter with the given
|
||||
// This function calls convert{{ export $.Types.Singular }}Filter with the given
|
||||
// {{ $.Types.GoFilterType }} and expects to receive a working squirrel.SelectBuilder
|
||||
func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.Types.GoFilterType }}) ({{ $.Types.GoSetType }}, {{ $.Types.GoFilterType }}, error) {
|
||||
func (s Store) {{ toggleExport .Search.Export "Search" $.Types.Plural }}(ctx context.Context{{ template "extraArgsDef" . }}, f {{ $.Types.GoFilterType }}) ({{ $.Types.GoSetType }}, {{ $.Types.GoFilterType }}, error) {
|
||||
var scap uint
|
||||
|
||||
{{- if .RDBMS.CustomFilterConverter }}
|
||||
q, err := s.convert{{ pubIdent $.Types.Singular }}Filter(f)
|
||||
q, err := s.convert{{ export $.Types.Singular }}Filter({{ template "extraArgsCallFirst" . }}f)
|
||||
if err != nil {
|
||||
return nil, f, err
|
||||
}
|
||||
{{- else }}
|
||||
q := s.Query{{ pubIdent $.Types.Plural }}()
|
||||
q := s.{{ unexport $.Types.Plural }}SelectBuilder()
|
||||
{{- end }}
|
||||
|
||||
{{ if not $.Search.DisablePaging }}
|
||||
{{ if $.Search.EnablePaging }}
|
||||
scap = f.Limit
|
||||
|
||||
// Cleanup anything we've accidentally received...
|
||||
@@ -50,22 +69,8 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
reverseCursor := f.PageCursor != nil && f.PageCursor.Reverse
|
||||
{{ end }}
|
||||
|
||||
{{ if $.Search.DisableSorting }}
|
||||
{{ if not $.Search.DisablePaging }}
|
||||
// Sorting is disabled in definition yaml file
|
||||
// {search: {disableSorting:true}}
|
||||
//
|
||||
// We still need to sort the results by primary key for paging purposes
|
||||
sort := store.SortExprSet{
|
||||
{{ range $.Fields }}
|
||||
{{- if or .IsPrimaryKey -}}
|
||||
&store.SortExpr{Column: {{ printf "%q" .Column }}, {{ if .SortDescending }}Descending: true, {{ end }}},
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
if err = f.Sort.Validate(s.sortable{{ pubIdent $.Types.Singular }}Columns()...); err != nil {
|
||||
{{ if $.Search.EnableSorting }}
|
||||
if err := f.Sort.Validate(s.sortable{{ export $.Types.Singular }}Columns()...); err != nil {
|
||||
return nil, f, fmt.Errorf("could not validate sort: %v", err)
|
||||
}
|
||||
|
||||
@@ -88,6 +93,18 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
|
||||
q = q.OrderBy(sqlSort...)
|
||||
}
|
||||
{{ else if $.Search.EnablePaging }}
|
||||
// Sorting is disabled in definition yaml file
|
||||
// {search: {enablePaging:false}}
|
||||
//
|
||||
// We still need to sort the results by primary key for paging purposes
|
||||
sort := filter.SortExprSet{
|
||||
{{ range $.Fields }}
|
||||
{{- if or .IsPrimaryKey -}}
|
||||
&filter.SortExpr{Column: {{ printf "%q" .Column }}, {{ if .SortDescending }}Descending: true, {{ end }}},
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
if scap == 0 {
|
||||
@@ -98,9 +115,9 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
var (
|
||||
set = make([]*{{ $.Types.GoType }}, 0, scap)
|
||||
|
||||
{{- if $.Search.DisablePaging }}
|
||||
{{- if not $.Search.EnablePaging }}
|
||||
// Paging is disabled in definition yaml file
|
||||
// {search: {disablePaging:true}} and this allows
|
||||
// {search: {enablePaging:false}} and this allows
|
||||
// a much simpler row fetching logic
|
||||
fetch = func() error {
|
||||
var (
|
||||
@@ -113,21 +130,42 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
if res, err = s.internal{{ pubIdent $.Types.Singular }}RowScanner(rows, rows.Err()); err != nil {
|
||||
if rows.Err() == nil {
|
||||
res, err = s.internal{{ export $.Types.Singular }}RowScanner({{ template "extraArgsCallFirst" . }}rows)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if cerr := rows.Close(); cerr != nil {
|
||||
return fmt.Errorf("could not close rows (%v) after scan error: %w", cerr, err)
|
||||
err = fmt.Errorf("could not close rows (%v) after scan error: %w", cerr, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// If check function is set, call it and act accordingly
|
||||
{{ if $.Search.EnableFilterCheckFn }}
|
||||
if f.Check != nil {
|
||||
if chk, err := f.Check(res); err != nil {
|
||||
if cerr := rows.Close(); cerr != nil {
|
||||
err = fmt.Errorf("could not close rows (%v) after check error: %w", cerr, err)
|
||||
}
|
||||
|
||||
return err
|
||||
} else if !chk {
|
||||
// did not pass the check
|
||||
// go with the next row
|
||||
continue
|
||||
}
|
||||
}
|
||||
{{ end -}}
|
||||
|
||||
set = append(set, res)
|
||||
}
|
||||
|
||||
return rows.Close()
|
||||
}
|
||||
{{ else }}
|
||||
// fetches rows and scans them into {{ pubIdent $.Types.GoType }} resource this is then passed to Check function on filter
|
||||
// fetches rows and scans them into {{ $.Types.GoType }} resource this is then passed to Check function on filter
|
||||
// to help determine if fetched resource fits or not
|
||||
//
|
||||
// Note that limit is passed explicitly and is not necessarily equal to filter's limit. We want
|
||||
@@ -136,7 +174,7 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
// The value for cursor is used and set directly from/to the filter!
|
||||
//
|
||||
// It returns total number of fetched pages and modifies PageCursor value for paging
|
||||
fetchPage = func(cursor *store.PagingCursor, limit uint) (fetched uint, err error) {
|
||||
fetchPage = func(cursor *filter.PagingCursor, limit uint) (fetched uint, err error) {
|
||||
var (
|
||||
res *{{ $.Types.GoType }}
|
||||
|
||||
@@ -167,7 +205,12 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
|
||||
for rows.Next() {
|
||||
fetched++
|
||||
if res, err = s.internal{{ pubIdent $.Types.Singular }}RowScanner(rows, rows.Err()); err != nil {
|
||||
|
||||
if rows.Err() == nil {
|
||||
res, err = s.internal{{ export $.Types.Singular }}RowScanner({{ template "extraArgsCallFirst" . }}rows)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if cerr := rows.Close(); cerr != nil {
|
||||
err = fmt.Errorf("could not close rows (%v) after scan error: %w", cerr, err)
|
||||
}
|
||||
@@ -176,7 +219,7 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
}
|
||||
|
||||
// If check function is set, call it and act accordingly
|
||||
{{ if not $.Search.DisableFilterCheckFn }}
|
||||
{{ if $.Search.EnableFilterCheckFn }}
|
||||
if f.Check != nil {
|
||||
var chk bool
|
||||
if chk, err = f.Check(res); err != nil {
|
||||
@@ -265,13 +308,13 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
|
||||
if f.Limit > 0 && len(set) > 0 {
|
||||
if f.PageCursor != nil && (!f.PageCursor.Reverse || lastSetFull) {
|
||||
f.PrevPage = s.collect{{ pubIdent $.Types.Singular }}CursorValues(set[0], sort.Columns()...)
|
||||
f.PrevPage = s.collect{{ export $.Types.Singular }}CursorValues(set[0], sort.Columns()...)
|
||||
f.PrevPage.Reverse = true
|
||||
}
|
||||
|
||||
// Less items fetched then requested by page-limit
|
||||
// not very likely there's another page
|
||||
f.NextPage = s.collect{{ pubIdent $.Types.Singular }}CursorValues(set[len(set)-1], sort.Columns()...)
|
||||
f.NextPage = s.collect{{ export $.Types.Singular }}CursorValues(set[len(set)-1], sort.Columns()...)
|
||||
}
|
||||
|
||||
f.PageCursor = nil
|
||||
@@ -285,49 +328,69 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T
|
||||
{{ end }}
|
||||
|
||||
{{- range $lookup := $.Lookups }}
|
||||
// Lookup{{ pubIdent $.Types.Singular }}By{{ pubIdent $lookup.Suffix }} {{ comment $lookup.Description true -}}
|
||||
func (s Store) Lookup{{ pubIdent $.Types.Singular }}By{{ pubIdent $lookup.Suffix }}(ctx context.Context{{- range $field := $lookup.Fields }}, {{ cc2underscore $field }} {{ ($field | $.Fields.Find).Type }}{{- end }}) (*{{ $.Types.GoType }}, error) {
|
||||
return s.{{ $.Types.Singular }}Lookup(ctx, squirrel.Eq{
|
||||
// {{ toggleExport $lookup.Export "Lookup" $.Types.Singular "By" $lookup.Suffix }} {{ comment $lookup.Description true -}}
|
||||
func (s Store) {{ toggleExport $lookup.Export "Lookup" $.Types.Singular "By" $lookup.Suffix }}(ctx context.Context{{ template "extraArgsDef" $ }}{{- range $field := $lookup.Fields }}, {{ cc2underscore $field }} {{ ($field | $.Fields.Find).Type }}{{- end }}) (*{{ $.Types.GoType }}, error) {
|
||||
return s.execLookup{{ $.Types.Singular }}(ctx{{ template "extraArgsCall" $ }}, squirrel.Eq{
|
||||
{{- range $field := $lookup.Fields }}
|
||||
"{{ ($field | $.Fields.Find).AliasedColumn }}": {{ cc2underscore $field }},
|
||||
s.preprocessColumn({{ printf "%q" ($field | $.Fields.Find).AliasedColumn }}, {{ printf "%q" ($field | $.Fields.Find).LookupFilterPreprocess }}): s.preprocessValue({{ cc2underscore $field }}, {{ printf "%q" ($field | $.Fields.Find).LookupFilterPreprocess }}),
|
||||
{{- end }}
|
||||
{{- range $field, $value := $lookup.Filter }}
|
||||
|
||||
{{ range $field, $value := $lookup.Filter }}
|
||||
"{{ ($field | $.Fields.Find).AliasedColumn }}": {{ $value }},
|
||||
{{- end }}
|
||||
})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Create{{ pubIdent $.Types.Singular }} creates one or more rows in {{ $.RDBMS.Table }} table
|
||||
func (s Store) Create{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
{{ if .Create.Enable }}
|
||||
// {{ toggleExport .Create.Export "Create" $.Types.Singular }} creates one or more rows in {{ $.RDBMS.Table }} table
|
||||
func (s Store) {{ toggleExport .Create.Export "Create" $.Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
for _, res := range rr {
|
||||
err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.{{ $.Types.Singular }}Table()).SetMap(s.internal{{ pubIdent $.Types.Singular }}Encoder(res)))
|
||||
err = s.check{{ export $.Types.Singular }}Constraints(ctx {{ template "extraArgsCall" $ }}, res)
|
||||
if err != nil {
|
||||
return s.config.ErrorHandler(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// err = s.{{ unexport $.Types.Singular }}Hook(ctx, TriggerBefore{{ export $.Types.Singular }}Create{{ template "extraArgsCall" . }}, res)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
err = s.execCreate{{ export $.Types.Plural }}(ctx, s.internal{{ export $.Types.Singular }}Encoder(res))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Update{{ pubIdent $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }}
|
||||
func (s Store) Update{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... *{{ $.Types.GoType }}) error {
|
||||
return s.config.ErrorHandler(s.PartialUpdate{{ pubIdent $.Types.Singular }}(ctx, nil, rr...))
|
||||
{{ if .Update.Enable }}
|
||||
// {{ toggleExport .Update.Export "Update" $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }}
|
||||
func (s Store) {{ toggleExport .Update.Export "Update" $.Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $.Types.GoType }}) error {
|
||||
return s.config.ErrorHandler(s.{{ toggleExport .Update.Export "Partial" $.Types.Singular "Update" }}(ctx{{ template "extraArgsCall" . }}, nil, rr...))
|
||||
}
|
||||
|
||||
// PartialUpdate{{ pubIdent $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }}
|
||||
//
|
||||
// It wraps the update into transaction and can perform partial update by providing list of updatable columns
|
||||
func (s Store) PartialUpdate{{ pubIdent $.Types.Singular }}(ctx context.Context, onlyColumns []string, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
// {{ toggleExport .Update.Export "Partial" $.Types.Singular "Update" }} updates one or more existing rows in {{ $.RDBMS.Table }}
|
||||
func (s Store) {{ toggleExport .Update.Export "Partial" $.Types.Singular "Update" }}(ctx context.Context{{ template "extraArgsDef" . }}, onlyColumns []string, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
for _, res := range rr {
|
||||
err = s.ExecUpdate{{ pubIdent $.Types.Plural }}(
|
||||
err = s.check{{ export $.Types.Singular }}Constraints(ctx {{ template "extraArgsCall" $ }}, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// err = s.{{ unexport $.Types.Singular }}Hook(ctx, TriggerBefore{{ export $.Types.Singular }}Update{{ template "extraArgsCall" . }}, res)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
err = s.execUpdate{{ export $.Types.Plural }}(
|
||||
ctx,
|
||||
{{ template "filterByPrimaryKeys" $.Fields }},
|
||||
s.internal{{ pubIdent $.Types.Singular }}Encoder(res).Skip(
|
||||
{{- range $field := $.Fields -}}
|
||||
{{- if $field.IsPrimaryKey -}}
|
||||
{{ printf "%q" $field.Column }},
|
||||
{{- end -}}
|
||||
{{ template "filterByPrimaryKeys" $.Fields.PrimaryKeyFields }},
|
||||
s.internal{{ export $.Types.Singular }}Encoder(res).Skip(
|
||||
{{- range $field := $.Fields.PrimaryKeyFields -}}
|
||||
{{ printf "%q" $field.Column }},
|
||||
{{- end -}}
|
||||
).Only(onlyColumns...))
|
||||
if err != nil {
|
||||
@@ -337,11 +400,42 @@ func (s Store) PartialUpdate{{ pubIdent $.Types.Singular }}(ctx context.Context,
|
||||
|
||||
return
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Remove{{ pubIdent $.Types.Singular }} removes one or more rows from {{ $.RDBMS.Table }} table
|
||||
func (s Store) Remove{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
{{ if .Upsert.Enable }}
|
||||
// {{ toggleExport .Delete.Export "Upsert" $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }}
|
||||
func (s Store) {{ toggleExport .Delete.Export "Upsert" $.Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
for _, res := range rr {
|
||||
err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where({{ template "filterByPrimaryKeys" $.Fields }},))
|
||||
err = s.check{{ export $.Types.Singular }}Constraints(ctx {{ template "extraArgsCall" $ }}, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// err = s.{{ unexport $.Types.Singular }}Hook(ctx, TriggerBefore{{ export $.Types.Singular }}Upsert{{ template "extraArgsCall" . }}, res)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
err = s.config.ErrorHandler(s.execUpsert{{ export $.Types.Plural }}(ctx, s.internal{{ export $.Types.Singular }}Encoder(res)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Delete.Enable }}
|
||||
// {{ toggleExport .Delete.Export "Delete" $.Types.Singular }} Deletes one or more rows from {{ $.RDBMS.Table }} table
|
||||
func (s Store) {{ toggleExport .Delete.Export "Delete" $.Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, rr ... *{{ $.Types.GoType }}) (err error) {
|
||||
for _, res := range rr {
|
||||
// err = s.{{ unexport $.Types.Singular }}Hook(ctx, TriggerBefore{{ export $.Types.Singular }}Delete{{ template "extraArgsCall" . }}, res)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
err = s.execDelete{{ export $.Types.Plural }}(ctx,{{ template "filterByPrimaryKeys" $.Fields.PrimaryKeyFields }})
|
||||
if err != nil {
|
||||
return s.config.ErrorHandler(err)
|
||||
}
|
||||
@@ -350,42 +444,90 @@ func (s Store) Remove{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ...
|
||||
return nil
|
||||
}
|
||||
|
||||
// {{ toggleExport .Delete.Export "Delete" $.Types.Singular "By" }}{{ template "primaryKeySuffix" $.Fields }} Deletes row from the {{ $.RDBMS.Table }} table
|
||||
func (s Store) {{ toggleExport .Delete.Export "Delete" $.Types.Singular "By" }}{{ template "primaryKeySuffix" $.Fields }}(ctx context.Context{{ template "extraArgsDef" . }}{{ template "primaryKeyArgsDef" $.Fields }}) error {
|
||||
return s.execDelete{{ export $.Types.Plural }}(ctx, {{ template "filterByPrimaryKeysWithArgs" $.Fields.PrimaryKeyFields }})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Remove{{ pubIdent $.Types.Singular }}By{{ template "primaryKeySuffix" $.Fields }} removes row from the {{ $.RDBMS.Table }} table
|
||||
func (s Store) Remove{{ pubIdent $.Types.Singular }}By{{ template "primaryKeySuffix" $.Fields }}(ctx context.Context {{ template "primaryKeyArgs" $.Fields }}) error {
|
||||
return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where({{ template "filterByPrimaryKeysWithArgs" $.Fields }},)))
|
||||
// {{ toggleExport .Truncate.Export "Truncate" $.Types.Plural }} Deletes all rows from the {{ $.RDBMS.Table }} table
|
||||
func (s Store) {{ toggleExport .Truncate.Export "Truncate" $.Types.Plural }}(ctx context.Context{{ template "extraArgsDef" . }}) error {
|
||||
return s.config.ErrorHandler(s.Truncate(ctx, s.{{ unexport $.Types.Singular }}Table()))
|
||||
}
|
||||
|
||||
|
||||
// Truncate{{ pubIdent $.Types.Plural }} removes all rows from the {{ $.RDBMS.Table }} table
|
||||
func (s Store) Truncate{{ pubIdent $.Types.Plural }}(ctx context.Context) error {
|
||||
return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.{{ $.Types.Singular }}Table()))
|
||||
}
|
||||
|
||||
|
||||
// ExecUpdate{{ pubIdent $.Types.Plural }} updates all matched (by cnd) rows in {{ $.RDBMS.Table }} with given data
|
||||
func (s Store) ExecUpdate{{ pubIdent $.Types.Plural }}(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error {
|
||||
return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where(cnd).SetMap(set)))
|
||||
}
|
||||
|
||||
// {{ $.Types.Singular }}Lookup prepares {{ $.Types.Singular }} query and executes it,
|
||||
// execLookup{{ $.Types.Singular }} prepares {{ $.Types.Singular }} query and executes it,
|
||||
// returning {{ $.Types.GoType }} (or error)
|
||||
func (s Store) {{ $.Types.Singular }}Lookup(ctx context.Context, cnd squirrel.Sqlizer) (*{{ $.Types.GoType }}, error) {
|
||||
return s.internal{{ $.Types.Singular }}RowScanner(s.QueryRow(ctx, s.Query{{ pubIdent $.Types.Plural }}().Where(cnd)))
|
||||
}
|
||||
func (s Store) execLookup{{ $.Types.Singular }}(ctx context.Context{{ template "extraArgsDef" . }}, cnd squirrel.Sqlizer) (res *{{ $.Types.GoType }}, err error) {
|
||||
var (
|
||||
row rowScanner
|
||||
)
|
||||
|
||||
func (s Store) internal{{ $.Types.Singular }}RowScanner(row rowScanner, err error) (*{{ $.Types.GoType }}, error) {
|
||||
row, err = s.QueryRow(ctx, s.{{ unexport $.Types.Plural }}SelectBuilder().Where(cnd))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
|
||||
var res = &{{ $.Types.GoType }}{}
|
||||
if _, has := s.config.RowScanners[{{ printf "%q" (unpubIdent $.Types.Singular) }}]; has {
|
||||
scanner := s.config.RowScanners[{{ printf "%q" (unpubIdent $.Types.Singular) }}].(func(rowScanner, *{{ $.Types.GoType }}) error)
|
||||
err = scanner(row, res)
|
||||
res, err = s.internal{{ $.Types.Singular }}RowScanner({{ template "extraArgsCallFirst" . }}row)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
{{ if .Create.Enable }}
|
||||
// execCreate{{ export $.Types.Plural }} updates all matched (by cnd) rows in {{ $.RDBMS.Table }} with given data
|
||||
func (s Store) execCreate{{ export $.Types.Plural }}(ctx context.Context, payload store.Payload) error {
|
||||
return s.config.ErrorHandler(s.Exec(ctx, s.InsertBuilder(s.{{ unexport $.Types.Singular }}Table()).SetMap(payload)))
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Update.Enable }}
|
||||
// execUpdate{{ export $.Types.Plural }} updates all matched (by cnd) rows in {{ $.RDBMS.Table }} with given data
|
||||
func (s Store) execUpdate{{ export $.Types.Plural }}(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error {
|
||||
return s.config.ErrorHandler(s.Exec(ctx, s.UpdateBuilder(s.{{ unexport $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where(cnd).SetMap(set)))
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Upsert.Enable }}
|
||||
// execUpsert{{ export $.Types.Plural }} inserts new or updates matching (by-primary-key) rows in {{ $.RDBMS.Table }} with given data
|
||||
func (s Store) execUpsert{{ export $.Types.Plural }}(ctx context.Context, set store.Payload) error {
|
||||
upsert, err := s.config.UpsertBuilder(
|
||||
s.config,
|
||||
s.{{ unexport $.Types.Singular }}Table(),
|
||||
set,
|
||||
{{ range $.Fields }}
|
||||
{{- if or .IsPrimaryKey -}}
|
||||
{{ printf "%q" .Column }},
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.config.ErrorHandler(s.Exec(ctx, upsert))
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Delete.Enable }}
|
||||
// execDelete{{ export $.Types.Plural }} Deletes all matched (by cnd) rows in {{ $.RDBMS.Table }} with given data
|
||||
func (s Store) execDelete{{ export $.Types.Plural }}(ctx context.Context, cnd squirrel.Sqlizer) error {
|
||||
return s.config.ErrorHandler(s.Exec(ctx, s.DeleteBuilder(s.{{ unexport $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where(cnd)))
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (s Store) internal{{ $.Types.Singular }}RowScanner({{ template "extraArgsDefFirst" . }}row rowScanner) (res *{{ $.Types.GoType }}, err error) {
|
||||
res = &{{ $.Types.GoType }}{}
|
||||
|
||||
if _, has := s.config.RowScanners[{{ printf "%q" (unexport $.Types.Singular) }}]; has {
|
||||
scanner := s.config.RowScanners[{{ printf "%q" (unexport $.Types.Singular) }}].(func({{ template "extraArgsDefFirst" . }}_ rowScanner, _ *{{ $.Types.GoType }}) error)
|
||||
err = scanner({{ template "extraArgsCallFirst" . }}row, res)
|
||||
} else {
|
||||
{{- if .RDBMS.CustomRowScanner }}
|
||||
err = s.scan{{ $.Types.Singular }}Row(row, res)
|
||||
err = s.scan{{ $.Types.Singular }}Row({{ template "extraArgsCallFirst" . }}row, res)
|
||||
{{- else }}
|
||||
err = row.Scan(
|
||||
{{- range $.Fields }}
|
||||
@@ -406,13 +548,13 @@ func (s Store) internal{{ $.Types.Singular }}RowScanner(row rowScanner, err erro
|
||||
}
|
||||
}
|
||||
|
||||
// Query{{ pubIdent $.Types.Plural }} returns squirrel.SelectBuilder with set table and all columns
|
||||
func (s Store) Query{{ pubIdent $.Types.Plural }}() squirrel.SelectBuilder {
|
||||
return s.Select(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }}), s.{{ $.Types.Singular }}Columns({{ printf "%q" $.RDBMS.Alias }})...)
|
||||
// Query{{ export $.Types.Plural }} returns squirrel.SelectBuilder with set table and all columns
|
||||
func (s Store) {{ unexport $.Types.Plural }}SelectBuilder() squirrel.SelectBuilder {
|
||||
return s.SelectBuilder(s.{{ unexport $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }}), s.{{ unexport $.Types.Singular }}Columns({{ printf "%q" $.RDBMS.Alias }})...)
|
||||
}
|
||||
|
||||
// {{ $.Types.Singular }}Table name of the db table
|
||||
func (Store) {{ $.Types.Singular }}Table(aa ... string) string {
|
||||
// {{ unexport $.Types.Singular }}Table name of the db table
|
||||
func (Store) {{ unexport $.Types.Singular }}Table(aa ... string) string {
|
||||
var alias string
|
||||
if len(aa) > 0 {
|
||||
alias = " AS " + aa[0]
|
||||
@@ -424,7 +566,7 @@ func (Store) {{ $.Types.Singular }}Table(aa ... string) string {
|
||||
// {{ $.Types.Singular }}Columns returns all defined table columns
|
||||
//
|
||||
// With optional string arg, all columns are returned aliased
|
||||
func (Store) {{ $.Types.Singular }}Columns(aa ... string) []string {
|
||||
func (Store) {{ unexport $.Types.Singular }}Columns(aa ... string) []string {
|
||||
var alias string
|
||||
if len(aa) > 0 {
|
||||
alias = aa[0] + "."
|
||||
@@ -439,7 +581,7 @@ func (Store) {{ $.Types.Singular }}Columns(aa ... string) []string {
|
||||
|
||||
// {{ printf "%v" .Search }}
|
||||
|
||||
{{ if not $.Search.DisableSorting }}
|
||||
{{ if $.Search.EnableSorting }}
|
||||
// sortable{{ $.Types.Singular }}Columns returns all {{ $.Types.Singular }} columns flagged as sortable
|
||||
//
|
||||
// With optional string arg, all columns are returned aliased
|
||||
@@ -454,13 +596,13 @@ func (Store) sortable{{ $.Types.Singular }}Columns() []string {
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// internal{{ pubIdent $.Types.Singular }}Encoder encodes fields from {{ $.Types.GoType }} to store.Payload (map)
|
||||
// internal{{ export $.Types.Singular }}Encoder encodes fields from {{ $.Types.GoType }} to store.Payload (map)
|
||||
//
|
||||
// Encoding is done by using generic approach or by calling encode{{ pubIdent $.Types.Singular }}
|
||||
// Encoding is done by using generic approach or by calling encode{{ export $.Types.Singular }}
|
||||
// func when rdbms.customEncoder=true
|
||||
func (s Store) internal{{ pubIdent $.Types.Singular }}Encoder(res *{{ $.Types.GoType }}) store.Payload {
|
||||
func (s Store) internal{{ export $.Types.Singular }}Encoder(res *{{ $.Types.GoType }}) store.Payload {
|
||||
{{- if .RDBMS.CustomEncoder }}
|
||||
return s.encode{{ pubIdent $.Types.Singular }}(res)
|
||||
return s.encode{{ export $.Types.Singular }}(res)
|
||||
{{- else }}
|
||||
return store.Payload{
|
||||
{{- range $.Fields }}
|
||||
@@ -470,10 +612,10 @@ func (s Store) internal{{ pubIdent $.Types.Singular }}Encoder(res *{{ $.Types.Go
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if not $.Search.DisablePaging }}
|
||||
func (s Store) collect{{ pubIdent $.Types.Singular }}CursorValues(res *{{ $.Types.GoType }}, cc ...string) *store.PagingCursor {
|
||||
{{ if $.Search.EnablePaging }}
|
||||
func (s Store) collect{{ export $.Types.Singular }}CursorValues(res *{{ $.Types.GoType }}, cc ...string) *filter.PagingCursor {
|
||||
var (
|
||||
cursor = &store.PagingCursor{}
|
||||
cursor = &filter.PagingCursor{}
|
||||
|
||||
hasUnique bool
|
||||
|
||||
@@ -507,26 +649,46 @@ func (s Store) collect{{ pubIdent $.Types.Singular }}CursorValues(res *{{ $.Type
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (s *Store) check{{ export $.Types.Singular }}Constraints(ctx context.Context{{ template "extraArgsDef" $ }}, res *{{ $.Types.GoType }}) error {
|
||||
{{- range $lookup := $.Lookups }}
|
||||
{{ if $lookup.UniqueConstraintCheck }}
|
||||
{
|
||||
ex, err := s.{{ toggleExport $lookup.Export "Lookup" $.Types.Singular "By" $lookup.Suffix }}(ctx{{ template "extraArgsCall" $ }}{{- range $field := $lookup.Fields }}, res.{{ $field }} {{- end }})
|
||||
if err == nil && ex != nil && ex.ID != res.ID {
|
||||
return store.ErrNotUnique
|
||||
} else if !errors.Is(err, store.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (s *Store) {{ unexport $.Types.Singular }}Hook(ctx context.Context, key triggerKey{{ template "extraArgsDef" . }}, res *{{ $.Types.GoType }}) error {
|
||||
// if fn, has := s.config.TriggerHandlers[key]; has {
|
||||
// return fn.(func (ctx context.Context, s *Store{{ template "extraArgsDef" . }}, res *{{ $.Types.GoType }}) error)(ctx, s{{ template "extraArgsCall" . }}, res)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
|
||||
|
||||
{{/* ************************************************************ */}}
|
||||
|
||||
{{- define "filterByPrimaryKeys" -}}
|
||||
squirrel.Eq{
|
||||
{{- range $field := . -}}
|
||||
{{- if $field.IsPrimaryKey -}}
|
||||
s.preprocessColumn({{ printf "%q" $field.AliasedColumn }}, {{ printf "%q" $field.LookupFilterPreprocess }}): s.preprocessValue(res.{{ $field.Field }}, {{ printf "%q" $field.LookupFilterPreprocess }}),
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
{{ range $field := . -}}
|
||||
s.preprocessColumn({{ printf "%q" $field.AliasedColumn }}, {{ printf "%q" $field.LookupFilterPreprocess }}): s.preprocessValue(res.{{ $field.Field }}, {{ printf "%q" $field.LookupFilterPreprocess }}),
|
||||
{{- end }}
|
||||
}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "filterByPrimaryKeysWithArgs" -}}
|
||||
squirrel.Eq{
|
||||
{{- range $field := . }}
|
||||
{{- if $field.IsPrimaryKey -}}
|
||||
s.preprocessColumn({{ printf "%q" $field.AliasedColumn }}, {{ printf "%q" $field.LookupFilterPreprocess }}): s.preprocessValue({{ $field.Arg }}, {{ printf "%q" $field.LookupFilterPreprocess }}),
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ range $field := . -}}
|
||||
s.preprocessColumn({{ printf "%q" $field.AliasedColumn }}, {{ printf "%q" $field.LookupFilterPreprocess }}): s.preprocessValue({{ $field.Arg }}, {{ printf "%q" $field.LookupFilterPreprocess }}),
|
||||
{{ end }}
|
||||
}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
@@ -2,23 +2,29 @@ package tests
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Template: pkg/codegen/assets/store_test_all.gen.go
|
||||
// Definitions:
|
||||
{{ range . }}
|
||||
{{- if .Exported -}}
|
||||
// - {{ .Source }}
|
||||
{{ end -}}{{- end }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testAllGenerated(t *testing.T, all interface{}) {
|
||||
{{ range . }}
|
||||
// Run generated tests for {{ .Types.Base }}
|
||||
t.Run({{ printf "%q" .Types.Base }}, func(t *testing.T) {
|
||||
var s = all.({{ unpubIdent .Types.Plural }}Store)
|
||||
require.New(t).NoError(s.Truncate{{ pubIdent .Types.Plural }}(context.Background()))
|
||||
test{{ pubIdent .Types.Base }}(t, s)
|
||||
})
|
||||
{{ end }}
|
||||
func testAllGenerated(t *testing.T, s store.Storable) {
|
||||
{{- range . }}
|
||||
{{- if .Exported }}
|
||||
// Run generated tests for {{ .Types.Base }}
|
||||
t.Run({{ printf "%q" .Types.Base }}, func(t *testing.T) {
|
||||
test{{ export .Types.Base }}(t, s)
|
||||
})
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
}
|
||||
|
||||
+190
-59
@@ -1,83 +1,214 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type (
|
||||
definitions struct {
|
||||
App string
|
||||
|
||||
Rest []*restDef
|
||||
Actions []*actionsDef
|
||||
Events []*eventsDef
|
||||
Types []*typesDef
|
||||
Store []*storeDef
|
||||
}
|
||||
)
|
||||
|
||||
func Proc() {
|
||||
var (
|
||||
err error
|
||||
|
||||
def = &definitions{}
|
||||
watchChanges bool
|
||||
beVerbose bool
|
||||
|
||||
tpls = template.New("").Funcs(map[string]interface{}{
|
||||
"camelCase": camelCase,
|
||||
"pubIdent": pubIdent,
|
||||
"unpubIdent": unpubIdent,
|
||||
"toLower": strings.ToLower,
|
||||
"cc2underscore": cc2underscore,
|
||||
"normalizeImport": normalizeImport,
|
||||
"comment": func(text string, skip1st bool) string {
|
||||
ll := strings.Split(text, "\n")
|
||||
s := 0
|
||||
out := ""
|
||||
if skip1st {
|
||||
s = 1
|
||||
out = ll[0] + "\n"
|
||||
}
|
||||
fileList []string
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
for ; s < len(ll); s++ {
|
||||
out += "// " + ll[s] + "\n"
|
||||
}
|
||||
templatesPath = filepath.Join("pkg", "codegen", "assets", "*.tpl")
|
||||
templatesSrc []string
|
||||
|
||||
return out
|
||||
},
|
||||
})
|
||||
actionSrcPath = filepath.Join("*", "service", "*_actions.yaml")
|
||||
actionSrc []string
|
||||
actionDefs []*actionsDef
|
||||
|
||||
eventSrcPath = filepath.Join("*", "service", "event", "events.yaml")
|
||||
eventSrc []string
|
||||
eventDefs []*eventsDef
|
||||
|
||||
typeSrcPath = filepath.Join("*", "*", "types.yaml")
|
||||
typeSrc []string
|
||||
typeDefs []*typesDef
|
||||
|
||||
restSrcPath = filepath.Join("*", "rest.yaml")
|
||||
restSrc []string
|
||||
restDefs []*restDef
|
||||
|
||||
storeSrcPath = filepath.Join("store", "*.yaml")
|
||||
storeSrc []string
|
||||
storeDefs []*storeDef
|
||||
|
||||
tpls *template.Template
|
||||
tplBase = template.New("").
|
||||
Funcs(map[string]interface{}{
|
||||
"camelCase": camelCase,
|
||||
"export": export,
|
||||
"unexport": unexport,
|
||||
"toggleExport": toggleExport,
|
||||
"toLower": strings.ToLower,
|
||||
"cc2underscore": cc2underscore,
|
||||
"normalizeImport": normalizeImport,
|
||||
"comment": func(text string, skip1st bool) string {
|
||||
ll := strings.Split(text, "\n")
|
||||
s := 0
|
||||
out := ""
|
||||
if skip1st {
|
||||
s = 1
|
||||
out = ll[0] + "\n"
|
||||
}
|
||||
|
||||
for ; s < len(ll); s++ {
|
||||
out += "// " + ll[s] + "\n"
|
||||
}
|
||||
|
||||
return out
|
||||
},
|
||||
}).
|
||||
Funcs(sprig.TxtFuncMap())
|
||||
|
||||
output = func(format string, aa ...interface{}) {
|
||||
if beVerbose {
|
||||
fmt.Fprintf(os.Stdout, format, aa...)
|
||||
}
|
||||
}
|
||||
|
||||
outputErr = func(err error, format string, aa ...interface{}) bool {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stdout, format, aa...)
|
||||
fmt.Fprintf(os.Stdout, "%v\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
tpls = template.Must(tpls.ParseGlob("pkg/codegen/assets/*.tpl"))
|
||||
flag.BoolVar(&watchChanges, "w", false, "regenerate code on template or definition change")
|
||||
flag.BoolVar(&beVerbose, "v", false, "output loaded definitions, templates and outputs")
|
||||
flag.Parse()
|
||||
|
||||
if def.Actions, err = procActions(); err != nil {
|
||||
cli.HandleError(err)
|
||||
} else {
|
||||
cli.HandleError(genActions(tpls, def.Actions))
|
||||
}
|
||||
defer func() {
|
||||
if watcher != nil {
|
||||
watcher.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if def.Events, err = procEvents(); err != nil {
|
||||
cli.HandleError(err)
|
||||
} else {
|
||||
cli.HandleError(genEvents(tpls, def.Events))
|
||||
}
|
||||
for {
|
||||
fileList = make([]string, 0, 100)
|
||||
|
||||
if def.Types, err = procTypes(); err != nil {
|
||||
cli.HandleError(err)
|
||||
} else {
|
||||
cli.HandleError(genTypes(tpls, def.Types))
|
||||
}
|
||||
templatesSrc = glob(templatesPath)
|
||||
output("loaded %d templates from %s\n", len(templatesSrc), templatesPath)
|
||||
|
||||
if def.Rest, err = procRest(); err != nil {
|
||||
cli.HandleError(err)
|
||||
} else {
|
||||
cli.HandleError(genRest(tpls, def.Rest))
|
||||
}
|
||||
actionSrc = glob(actionSrcPath)
|
||||
output("loaded %d action definitions from %s\n", len(actionSrc), actionSrcPath)
|
||||
|
||||
if def.Store, err = procStore(); err != nil {
|
||||
cli.HandleError(err)
|
||||
} else {
|
||||
cli.HandleError(genStore(tpls, def.Store))
|
||||
eventSrc = glob(eventSrcPath)
|
||||
output("loaded %d event definitions from %s\n", len(eventSrc), eventSrcPath)
|
||||
|
||||
typeSrc = glob(typeSrcPath)
|
||||
output("loaded %d type definitions from %s\n", len(typeSrc), typeSrcPath)
|
||||
|
||||
restSrc = glob(restSrcPath)
|
||||
output("loaded %d rest definitions from %s\n", len(restSrc), restSrcPath)
|
||||
|
||||
storeSrc = glob(storeSrcPath)
|
||||
output("loaded %d store definitions from %s\n", len(storeSrc), storeSrcPath)
|
||||
|
||||
if watchChanges {
|
||||
if watcher != nil {
|
||||
watcher.Close()
|
||||
}
|
||||
|
||||
watcher, err = fsnotify.NewWatcher()
|
||||
cli.HandleError(err)
|
||||
|
||||
fileList = append(fileList, templatesSrc...)
|
||||
fileList = append(fileList, actionSrc...)
|
||||
fileList = append(fileList, eventSrc...)
|
||||
fileList = append(fileList, typeSrc...)
|
||||
fileList = append(fileList, restSrc...)
|
||||
fileList = append(fileList, storeSrc...)
|
||||
|
||||
for _, d := range fileList {
|
||||
cli.HandleError(watcher.Add(d))
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
tpls, err = tplBase.ParseFiles(templatesSrc...)
|
||||
if outputErr(err, "could not parse templates:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if actionDefs, err = procActions(actionSrc...); err == nil {
|
||||
err = genActions(tpls, actionDefs...)
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process actions:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if eventDefs, err = procEvents(eventSrc...); err == nil {
|
||||
err = genEvents(tpls, eventDefs...)
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process events:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if typeDefs, err = procTypes(typeSrc...); err == nil {
|
||||
err = genTypes(tpls, typeDefs...)
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process types:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if restDefs, err = procRest(restSrc...); err == nil {
|
||||
err = genRest(tpls, restDefs...)
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process rest:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if storeDefs, err = procStore(storeSrc...); err == nil {
|
||||
err = genStore(tpls, storeDefs...)
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process store:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if !watchChanges {
|
||||
break
|
||||
}
|
||||
|
||||
// @todo fix this (without causing too many "too-many-files" issues :)
|
||||
output("waiting for changes (if you add a new file, restart codegen manually)\n")
|
||||
|
||||
select {
|
||||
case <-watcher.Events:
|
||||
case err = <-watcher.Errors:
|
||||
cli.HandleError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func glob(path string) []string {
|
||||
src, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
cli.HandleError(fmt.Errorf("failed to glob %q: %w", path, err))
|
||||
}
|
||||
|
||||
return src
|
||||
}
|
||||
|
||||
+3
-12
@@ -6,7 +6,6 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -59,22 +58,14 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func procEvents() ([]*eventsDef, error) {
|
||||
func procEvents(mm ...string) (dd []*eventsDef, err error) {
|
||||
// <app>/service/event/events.yaml
|
||||
const (
|
||||
importTypePathTpl = "github.com/cortezaproject/corteza-server/%s/types"
|
||||
importAuthPath = "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
dd = make([]*eventsDef, 0)
|
||||
)
|
||||
|
||||
mm, err := filepath.Glob(filepath.Join("*", "service", "event", "events.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("glob failed: %w", err)
|
||||
}
|
||||
|
||||
dd = make([]*eventsDef, 0)
|
||||
for _, m := range mm {
|
||||
f, err := os.Open(m)
|
||||
if err != nil {
|
||||
@@ -156,7 +147,7 @@ func procEvents() ([]*eventsDef, error) {
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
func genEvents(tpl *template.Template, dd []*eventsDef) (err error) {
|
||||
func genEvents(tpl *template.Template, dd ...*eventsDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplEvents = tpl.Lookup("events.go.tpl")
|
||||
|
||||
+4
-14
@@ -5,7 +5,6 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -59,17 +58,8 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func procRest() ([]*restDef, error) {
|
||||
// <app>/rest.yaml
|
||||
|
||||
var (
|
||||
dd = make([]*restDef, 0)
|
||||
)
|
||||
|
||||
mm, err := filepath.Glob(filepath.Join("*", "rest.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("glob failed: %w", err)
|
||||
}
|
||||
func procRest(mm ...string) (dd []*restDef, err error) {
|
||||
dd = make([]*restDef, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
err = func() error {
|
||||
@@ -109,7 +99,7 @@ func procRest() ([]*restDef, error) {
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
func genRest(tpl *template.Template, dd []*restDef) (err error) {
|
||||
func genRest(tpl *template.Template, dd ...*restDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplHandler = tpl.Lookup("rest_handler.go.tpl")
|
||||
@@ -212,7 +202,7 @@ func (d *restEndpointParamDef) Parser(arg string) string {
|
||||
case "sqlxTypes.JSONText":
|
||||
return fmt.Sprintf("payload.ParseJSONTextWithErr(%s)", arg)
|
||||
case "int", "uint", "uint64", "int64", "float", "float64", "bool":
|
||||
return fmt.Sprintf("payload.Parse%s(%s), nil", pubIdent(d.Type), arg)
|
||||
return fmt.Sprintf("payload.Parse%s(%s), nil", export(d.Type), arg)
|
||||
case "string", "[]string":
|
||||
return fmt.Sprintf("%s, nil", arg)
|
||||
default:
|
||||
|
||||
+200
-143
@@ -6,7 +6,7 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -21,9 +21,6 @@ type (
|
||||
|
||||
Import []string `yaml:"import"`
|
||||
|
||||
// List of all locations where we should export the store interface to
|
||||
Interface []string `yaml:"interface"`
|
||||
|
||||
// Tries to autogenerate type by changing it to singular and prefixing it with *types.
|
||||
Types storeTypeDef `yaml:"types"`
|
||||
|
||||
@@ -32,12 +29,21 @@ type (
|
||||
// For now, this set does not variate between different implementation
|
||||
// To support that, a (sub)set will need to be defined under each implementation (rdbms, mysql, mongo...)
|
||||
//
|
||||
Fields storeTypeFieldSetDef `yaml:"fields"`
|
||||
Lookups []*storeTypeLookups `yaml:"lookups"`
|
||||
PartialUpdates []*storeTypePartialUpdate `yaml:"partialUpdates"`
|
||||
RDBMS *storeTypeRdbmsDef `yaml:"rdbms"`
|
||||
Fields storeTypeFieldSetDef `yaml:"fields"`
|
||||
RDBMS storeTypeRdbmsDef `yaml:"rdbms"`
|
||||
Functions []*storeTypeFunctionsDef `yaml:"functions"`
|
||||
Arguments []*storeTypeExtraArgumentDef `yaml:"arguments"`
|
||||
|
||||
Search storeTypeSearchDef `yaml:"search"`
|
||||
Search storeTypeSearchDef `yaml:"search"`
|
||||
Lookups []*storeTypeLookups `yaml:"lookups"`
|
||||
Create storeTypeCreateDef `yaml:"create"`
|
||||
Update storeTypeUpdateDef `yaml:"update"`
|
||||
Upsert storeTypeUpsertDef `yaml:"upsert"`
|
||||
Delete storeTypeDeleteDef `yaml:"Delete"`
|
||||
Truncate storeTypeTruncateDef `yaml:"truncate"`
|
||||
|
||||
// Make interfaces and store functions
|
||||
Publish bool `yaml:"publish"`
|
||||
}
|
||||
|
||||
storeTypeDef struct {
|
||||
@@ -80,6 +86,17 @@ type (
|
||||
CustomEncoder bool `yaml:"customEncoder"`
|
||||
}
|
||||
|
||||
storeTypeFunctionsDef struct {
|
||||
Name string `yaml:"name"`
|
||||
Arguments []storeTypeExtraArgumentDef `yaml:"arguments"`
|
||||
Return []string `yaml:"return"`
|
||||
}
|
||||
|
||||
storeTypeExtraArgumentDef struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
storeTypeFieldSetDef []*storeTypeFieldDef
|
||||
|
||||
storeTypeFieldDef struct {
|
||||
@@ -98,7 +115,7 @@ type (
|
||||
// If field name ends with ID (<base>ID), it converts that to rel_<snake-cased-base>
|
||||
Column string `yaml:"column"`
|
||||
|
||||
// If field is flagged as PK it is used in update & remove conditions
|
||||
// If field is flagged as PK it is used in update & Delete conditions
|
||||
// Note: if no other field is set as primary and field with ID name
|
||||
// exists, that field is auto-set as primary.
|
||||
IsPrimaryKey bool `yaml:"isPrimaryKey"`
|
||||
@@ -128,36 +145,99 @@ type (
|
||||
storeTypeLookups struct {
|
||||
// LookupBy<suffix>
|
||||
// When not explicitly defined, it names of all fields
|
||||
Suffix string `yaml:"suffix"`
|
||||
Description string `yaml:"description"`
|
||||
Fields []string `yaml:"fields"`
|
||||
Filter map[string]string `yaml:"filter"`
|
||||
fields storeTypeFieldSetDef
|
||||
}
|
||||
|
||||
storeTypePartialUpdate struct {
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
Set map[string]string `yaml:"set"`
|
||||
XX_Args []string `yaml:"args"`
|
||||
fields storeTypeFieldSetDef
|
||||
Export bool `yaml:"export"`
|
||||
Suffix string `yaml:"suffix"`
|
||||
Description string `yaml:"description"`
|
||||
UniqueConstraintCheck bool `yaml:"uniqueConstraintCheck"`
|
||||
Fields []string `yaml:"fields"`
|
||||
Filter map[string]string `yaml:"filter"`
|
||||
fields storeTypeFieldSetDef
|
||||
}
|
||||
|
||||
storeTypeSearchDef struct {
|
||||
Disable bool `yaml:"disable"`
|
||||
DisablePaging bool `yaml:"disablePaging"`
|
||||
DisableSorting bool `yaml:"disableSorting"`
|
||||
DisableFilterCheckFn bool `yaml:"disableFilterCheckFunction"`
|
||||
Enable bool `yaml:"enable"`
|
||||
Export bool `yaml:"export"`
|
||||
EnablePaging bool `yaml:"enablePaging"`
|
||||
EnableSorting bool `yaml:"enableSorting"`
|
||||
EnableFilterCheckFn bool `yaml:"enableFilterCheckFunction"`
|
||||
}
|
||||
|
||||
storeTypeCreateDef struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
Export bool `yaml:"export"`
|
||||
}
|
||||
|
||||
storeTypeUpdateDef struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
Export bool `yaml:"export"`
|
||||
}
|
||||
|
||||
storeTypeUpsertDef struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
Export bool `yaml:"export"`
|
||||
}
|
||||
|
||||
storeTypeDeleteDef struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
Export bool `yaml:"export"`
|
||||
}
|
||||
|
||||
storeTypeTruncateDef struct {
|
||||
Export bool `yaml:"export"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
outputDir string = "store"
|
||||
outputDir string = "store"
|
||||
spaceSplit = regexp.MustCompile(`\s+`)
|
||||
)
|
||||
|
||||
func procStore() ([]*storeDef, error) {
|
||||
func procStore(mm ...string) ([]*storeDef, error) {
|
||||
procDef := func(m string) (*storeDef, error) {
|
||||
def := &storeDef{Source: m}
|
||||
// initialize & set default
|
||||
def := &storeDef{
|
||||
Source: m,
|
||||
|
||||
RDBMS: storeTypeRdbmsDef{
|
||||
CustomRowScanner: false,
|
||||
CustomFilterConverter: false,
|
||||
CustomEncoder: false,
|
||||
},
|
||||
|
||||
Search: storeTypeSearchDef{
|
||||
Enable: true,
|
||||
Export: true,
|
||||
EnablePaging: true,
|
||||
EnableSorting: true,
|
||||
EnableFilterCheckFn: true,
|
||||
},
|
||||
|
||||
Create: storeTypeCreateDef{
|
||||
Enable: true,
|
||||
Export: true,
|
||||
},
|
||||
|
||||
Update: storeTypeUpdateDef{
|
||||
Enable: true,
|
||||
Export: true,
|
||||
},
|
||||
|
||||
Upsert: storeTypeUpsertDef{
|
||||
Enable: true,
|
||||
Export: true,
|
||||
},
|
||||
|
||||
Delete: storeTypeDeleteDef{
|
||||
Enable: true,
|
||||
Export: true,
|
||||
},
|
||||
|
||||
Truncate: storeTypeTruncateDef{
|
||||
Export: true,
|
||||
},
|
||||
|
||||
Publish: true,
|
||||
}
|
||||
f, err := os.Open(m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s read failed: %w", m, err)
|
||||
@@ -172,18 +252,35 @@ func procStore() ([]*storeDef, error) {
|
||||
def.Filename = path.Base(m)
|
||||
def.Filename = def.Filename[:len(def.Filename)-5]
|
||||
|
||||
if def.Search.Disable {
|
||||
if !def.Search.Enable {
|
||||
// No use for any of that if search is disabled...
|
||||
def.Search.DisablePaging = true
|
||||
def.Search.DisableSorting = true
|
||||
def.Search.DisableFilterCheckFn = true
|
||||
def.Search.EnablePaging = false
|
||||
def.Search.EnableSorting = false
|
||||
def.Search.EnableFilterCheckFn = false
|
||||
}
|
||||
|
||||
// Always generate interface in store/tests and store/bulk
|
||||
def.Interface = append(def.Interface, "store/tests", "store/bulk")
|
||||
if !def.Create.Enable {
|
||||
// No use for any of that if operation is disabled...
|
||||
def.Create.Export = false
|
||||
}
|
||||
|
||||
if !def.Update.Enable {
|
||||
// No use for any of that if operation is disabled...
|
||||
def.Update.Export = false
|
||||
}
|
||||
|
||||
if !def.Upsert.Enable {
|
||||
// No use for any of that if operation is disabled...
|
||||
def.Upsert.Export = false
|
||||
}
|
||||
|
||||
if !def.Delete.Enable {
|
||||
// No use for any of that if operation is disabled...
|
||||
def.Delete.Export = false
|
||||
}
|
||||
|
||||
if def.Types.Base == "" {
|
||||
def.Types.Base = pubIdent(strings.Split(def.Filename, "_")...)
|
||||
def.Types.Base = export(strings.Split(def.Filename, "_")...)
|
||||
}
|
||||
|
||||
if def.Types.Singular == "" {
|
||||
@@ -198,7 +295,7 @@ func procStore() ([]*storeDef, error) {
|
||||
}
|
||||
|
||||
if def.Types.GoType == "" {
|
||||
def.Types.GoType = def.Types.Package + "." + pubIdent(def.Types.Singular)
|
||||
def.Types.GoType = def.Types.Package + "." + export(def.Types.Singular)
|
||||
}
|
||||
|
||||
if def.Types.GoSetType == "" {
|
||||
@@ -213,75 +310,40 @@ func procStore() ([]*storeDef, error) {
|
||||
def.RDBMS.Alias = def.Types.Base[0:1]
|
||||
}
|
||||
|
||||
var hasPrimaryKey = false
|
||||
for _, f := range def.Fields {
|
||||
if f.IsPrimaryKey {
|
||||
hasPrimaryKey = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range def.Fields {
|
||||
if !hasPrimaryKey && f.Field == "ID" {
|
||||
f.IsPrimaryKey = true
|
||||
f.IsSortable = true
|
||||
var hasPrimaryKey = def.Fields.HasPrimaryKey()
|
||||
for _, field := range def.Fields {
|
||||
if !hasPrimaryKey && field.Field == "ID" {
|
||||
field.IsPrimaryKey = true
|
||||
field.IsSortable = true
|
||||
}
|
||||
|
||||
// copy alias from global spec so we can
|
||||
// generate aliased columsn
|
||||
f.alias = def.RDBMS.Alias
|
||||
field.alias = def.RDBMS.Alias
|
||||
|
||||
if f.Column == "" {
|
||||
if field.Column == "" {
|
||||
switch {
|
||||
case f.Field != "ID" && strings.HasSuffix(f.Field, "ID"):
|
||||
f.Column = "rel_" + cc2underscore(f.Field[:len(f.Field)-2])
|
||||
case field.Field != "ID" && strings.HasSuffix(field.Field, "ID"):
|
||||
field.Column = "rel_" + cc2underscore(field.Field[:len(field.Field)-2])
|
||||
default:
|
||||
f.Column = cc2underscore(f.Field)
|
||||
field.Column = cc2underscore(field.Field)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.Type != "":
|
||||
case field.Type != "":
|
||||
// type set
|
||||
case strings.HasSuffix(f.Field, "ID") || strings.HasSuffix(f.Field, "By"):
|
||||
f.Type = "uint64"
|
||||
case f.Field == "CreatedAt":
|
||||
f.Type = "time.Time"
|
||||
case strings.HasSuffix(f.Field, "At"):
|
||||
f.Type = "uint64"
|
||||
case strings.HasSuffix(field.Field, "ID") || strings.HasSuffix(field.Field, "By"):
|
||||
field.Type = "uint64"
|
||||
case field.Field == "CreatedAt":
|
||||
field.Type = "time.Time"
|
||||
case strings.HasSuffix(field.Field, "At"):
|
||||
field.Type = "uint64"
|
||||
default:
|
||||
f.Type = "string"
|
||||
field.Type = "string"
|
||||
}
|
||||
}
|
||||
|
||||
if len(def.PartialUpdates) > 0 && def.Fields.Find("ID") == nil {
|
||||
return nil, fmt.Errorf("partial updates without ID field are not supported")
|
||||
}
|
||||
|
||||
// Checking if filters exist in the fields
|
||||
for i, p := range def.PartialUpdates {
|
||||
// Check and normalize set
|
||||
for f, v := range p.Set {
|
||||
if def.Fields.Find(f) == nil {
|
||||
return nil, fmt.Errorf("undefined field %q used in partialUpdate #%d set", f, i)
|
||||
}
|
||||
|
||||
if v == "" {
|
||||
// Set empty strings to nil
|
||||
p.Set[f] = "nil"
|
||||
}
|
||||
}
|
||||
|
||||
for _, a := range p.XX_Args {
|
||||
if def.Fields.Find(a) == nil {
|
||||
return nil, fmt.Errorf("undefined field %q used in partialUpdate #%d arguments", a, i)
|
||||
}
|
||||
}
|
||||
|
||||
p.fields = def.Fields
|
||||
|
||||
}
|
||||
|
||||
for i, l := range def.Lookups {
|
||||
if len(l.Fields) == 0 {
|
||||
return nil, fmt.Errorf("define at least one lookup field in lookup #%d", i)
|
||||
@@ -318,12 +380,7 @@ func procStore() ([]*storeDef, error) {
|
||||
return def, nil
|
||||
}
|
||||
|
||||
mm, err := filepath.Glob(filepath.Join(outputDir, "*.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to glob: %w", err)
|
||||
}
|
||||
|
||||
dd := []*storeDef{}
|
||||
dd := make([]*storeDef, 0, len(mm))
|
||||
for _, m := range mm {
|
||||
def, err := procDef(m)
|
||||
if err != nil {
|
||||
@@ -339,11 +396,11 @@ func procStore() ([]*storeDef, error) {
|
||||
// genStore generates all store related code, functions, interfaces...
|
||||
//
|
||||
// Templates can be found under assets/store*.tpl
|
||||
func genStore(tpl *template.Template, dd []*storeDef) (err error) {
|
||||
func genStore(tpl *template.Template, dd ...*storeDef) (err error) {
|
||||
var (
|
||||
// general interfaces
|
||||
tplInterfacesJoined = tpl.Lookup("store_interfaces_joined.gen.go.tpl")
|
||||
tplInterfaces = tpl.Lookup("store_interfaces.gen.go.tpl")
|
||||
tplBase = tpl.Lookup("store_base.gen.go.tpl")
|
||||
|
||||
// general tests
|
||||
tplTestAll = tpl.Lookup("store_test_all.gen.go.tpl")
|
||||
@@ -357,11 +414,7 @@ func genStore(tpl *template.Template, dd []*storeDef) (err error) {
|
||||
// @todo mongodb
|
||||
// @todo elasticsearch
|
||||
|
||||
// bulk specific
|
||||
tplBulk = tpl.Lookup("store_bulk.gen.go.tpl")
|
||||
|
||||
dst string
|
||||
joinedInterface = make(map[string][]*storeDef)
|
||||
dst string
|
||||
)
|
||||
|
||||
// Output all test setup into a single file
|
||||
@@ -377,40 +430,15 @@ func genStore(tpl *template.Template, dd []*storeDef) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
dst = path.Join(outputDir, "bulk", d.Filename+".gen.go")
|
||||
if err = goTemplate(dst, tplBulk, d); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Collect and map all interface output locations
|
||||
// and their corresponding definitions
|
||||
for _, dst = range d.Interface {
|
||||
if err = genStoreInterfaces(tplInterfaces, path.Join(dst, "store_interface_"+d.Filename+".gen.go"), path.Base(dst), d); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if joinedInterface[dst] == nil {
|
||||
joinedInterface[dst] = make([]*storeDef, 0, len(dd))
|
||||
}
|
||||
|
||||
joinedInterface[dst] = append(joinedInterface[dst], d)
|
||||
}
|
||||
}
|
||||
|
||||
// Add joined interfaces for each interface destination
|
||||
for dst, dd := range joinedInterface {
|
||||
if err = genStoreInterfacesJoined(tplInterfacesJoined, path.Join(dst, "store_interface.gen.go"), path.Base(dst), dd); err != nil {
|
||||
dst = path.Join(outputDir, d.Filename+".gen.go")
|
||||
if err = goTemplate(dst, tplBase, d); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//for _, d := range dd {
|
||||
// d.Package = "tests"
|
||||
// dst = path.Join("store/tests", "store_interface_"+d.Filename+".gen.go")
|
||||
// if err = goTemplate(dst, tplInterfaces, d); err != nil {
|
||||
// return
|
||||
// }
|
||||
//}
|
||||
if err = genStoreInterfacesJoined(tplInterfacesJoined, path.Join("store", "interfaces.gen.go"), path.Base(dst), dd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -443,8 +471,17 @@ func collectStoreDefImports(basePkg string, dd ...*storeDef) []string {
|
||||
return ii
|
||||
}
|
||||
|
||||
func (s storeTypeFieldSetDef) Find(name string) *storeTypeFieldDef {
|
||||
for _, f := range s {
|
||||
// Exported returns true if at least one of the functions is exported
|
||||
func (d storeDef) Exported() bool {
|
||||
return d.Search.Export ||
|
||||
d.Create.Export ||
|
||||
d.Update.Export ||
|
||||
d.Upsert.Export ||
|
||||
d.Delete.Export
|
||||
}
|
||||
|
||||
func (ff storeTypeFieldSetDef) Find(name string) *storeTypeFieldDef {
|
||||
for _, f := range ff {
|
||||
if f.Field == name {
|
||||
return f
|
||||
}
|
||||
@@ -453,6 +490,27 @@ func (s storeTypeFieldSetDef) Find(name string) *storeTypeFieldDef {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ff storeTypeFieldSetDef) HasPrimaryKey() bool {
|
||||
for _, f := range ff {
|
||||
if f.IsPrimaryKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ff storeTypeFieldSetDef) PrimaryKeyFields() storeTypeFieldSetDef {
|
||||
pkSet := storeTypeFieldSetDef{}
|
||||
for _, f := range ff {
|
||||
if f.IsPrimaryKey {
|
||||
pkSet = append(pkSet, f)
|
||||
}
|
||||
}
|
||||
|
||||
return pkSet
|
||||
}
|
||||
|
||||
func (f storeTypeFieldDef) Arg() string {
|
||||
if f.Field == "ID" {
|
||||
return f.Field
|
||||
@@ -465,11 +523,10 @@ func (f storeTypeFieldDef) AliasedColumn() string {
|
||||
return fmt.Sprintf("%s.%s", f.alias, f.Column)
|
||||
}
|
||||
|
||||
func (p storeTypePartialUpdate) Args() []*storeTypeFieldDef {
|
||||
ff := make([]*storeTypeFieldDef, len(p.XX_Args))
|
||||
for a := range p.XX_Args {
|
||||
ff[a] = p.fields.Find(p.XX_Args[a])
|
||||
}
|
||||
|
||||
return ff
|
||||
// UnmarshalYAML makes sure that export flag is set to true when not explicity disabled
|
||||
func (d *storeTypeLookups) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type dAux storeTypeLookups
|
||||
var aux = (*dAux)(d)
|
||||
aux.Export = true
|
||||
return unmarshal(aux)
|
||||
}
|
||||
|
||||
@@ -84,14 +84,14 @@ func camelCase(pp ...string) (out string) {
|
||||
// input, cammelcasing it and removing ident unfriendly characters
|
||||
var nonIdentChars = regexp.MustCompile(`[\s\\/]+`)
|
||||
|
||||
func pubIdent(pp ...string) (out string) {
|
||||
func export(pp ...string) (out string) {
|
||||
for _, p := range pp {
|
||||
if len(p) > 1 {
|
||||
p = strings.ToUpper(p[:1]) + p[1:]
|
||||
}
|
||||
|
||||
if ss := nonIdentChars.Split(p, -1); len(ss) > 1 {
|
||||
p = pubIdent(ss...)
|
||||
p = export(ss...)
|
||||
}
|
||||
|
||||
out = out + p
|
||||
@@ -100,11 +100,19 @@ func pubIdent(pp ...string) (out string) {
|
||||
return out
|
||||
}
|
||||
|
||||
func unpubIdent(pp ...string) (out string) {
|
||||
out = pubIdent(pp...)
|
||||
func unexport(pp ...string) (out string) {
|
||||
out = export(pp...)
|
||||
return strings.ToLower(out[:1]) + out[1:]
|
||||
}
|
||||
|
||||
func toggleExport(e bool, pp ...string) (out string) {
|
||||
if e {
|
||||
return export(pp...)
|
||||
}
|
||||
|
||||
return unexport(pp...)
|
||||
}
|
||||
|
||||
// convets to underscore
|
||||
func cc2underscore(cc string) string {
|
||||
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
||||
|
||||
+3
-11
@@ -5,7 +5,6 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
@@ -28,15 +27,8 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func procTypes() ([]*typesDef, error) {
|
||||
var (
|
||||
dd = make([]*typesDef, 0)
|
||||
)
|
||||
|
||||
mm, err := filepath.Glob(filepath.Join("*", "*", "types.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("glob failed: %w", err)
|
||||
}
|
||||
func procTypes(mm ...string) (dd []*typesDef, err error) {
|
||||
dd = make([]*typesDef, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
var (
|
||||
@@ -69,7 +61,7 @@ func procTypes() ([]*typesDef, error) {
|
||||
// Generates all type set files & accompanying tests
|
||||
//
|
||||
// generates 2 files per type definition
|
||||
func genTypes(tpl *template.Template, dd []*typesDef) (err error) {
|
||||
func genTypes(tpl *template.Template, dd ...*typesDef) (err error) {
|
||||
var (
|
||||
typeGen = tpl.Lookup("type_set.gen.go.tpl")
|
||||
typeGenTest = tpl.Lookup("type_set.gen_test.go.tpl")
|
||||
|
||||
Reference in New Issue
Block a user