Define base codegen for resource translations
This commit is contained in:
@@ -9,3 +9,12 @@ rbac:
|
||||
record.value.update:
|
||||
canFnName: CanUpdateRecordValue
|
||||
description: Update field value on records
|
||||
|
||||
locale:
|
||||
resource:
|
||||
references: [ namespace, module, ID ]
|
||||
|
||||
skipSvc: true
|
||||
keys:
|
||||
- label
|
||||
- { name: validatorError, path: "expression.validator.{{validatorID}}.error", custom: true, customHandler: validatorError }
|
||||
|
||||
@@ -14,3 +14,11 @@ rbac:
|
||||
description: Create record
|
||||
records.search:
|
||||
description: List, search or filter records
|
||||
|
||||
locale:
|
||||
resource:
|
||||
references: [ namespace, ID ]
|
||||
|
||||
extended: true
|
||||
keys:
|
||||
- name
|
||||
|
||||
@@ -23,3 +23,9 @@ rbac:
|
||||
description: Create page on namespace
|
||||
pages.search:
|
||||
description: List, search or filter pages on namespace
|
||||
|
||||
locale:
|
||||
keys:
|
||||
- name
|
||||
- { path: subtitle, field: "Meta.Subtitle" }
|
||||
- { path: description, field: "Meta.Description" }
|
||||
|
||||
@@ -9,3 +9,15 @@ rbac:
|
||||
description: Update page
|
||||
delete:
|
||||
description: Delete page
|
||||
|
||||
locale:
|
||||
resource:
|
||||
references: [ namespace, ID ]
|
||||
|
||||
extended: true
|
||||
keys:
|
||||
- title
|
||||
- description
|
||||
- { name: blockTitle, path: "pageBlock.{{blockID}}.title", custom: true }
|
||||
- { name: blockDescription, path: "pageBlock.{{blockID}}.description", custom: true }
|
||||
- { name: blockAutomationButtonlabel, path: "pageBlock.{{blockID}}.automation.{{buttonID}}.label", custom: true }
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package {{ .Package }}
|
||||
|
||||
{{ template "header-gentext.tpl" }}
|
||||
{{ template "header-definitions.tpl" . }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
resourceTranslation struct {
|
||||
actionlog actionlog.Recorder
|
||||
locale locale.Resource
|
||||
store store.Storer
|
||||
ac localeAccessController
|
||||
}
|
||||
|
||||
localeAccessController interface {
|
||||
// CanManageResourceTranslation(context.Context) bool
|
||||
}
|
||||
|
||||
ResourceTranslationService interface {
|
||||
{{- range .Def }}
|
||||
{{ .Resource }}(ctx context.Context, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }} uint64, {{ end }}{{ end }}) (locale.ResourceTranslationSet, error)
|
||||
{{- end }}
|
||||
|
||||
Upsert(context.Context, locale.ResourceTranslationSet) error
|
||||
Locale() locale.Resource
|
||||
}
|
||||
)
|
||||
|
||||
func ResourceTranslation(ls locale.Resource) *resourceTranslation {
|
||||
return &resourceTranslation{
|
||||
actionlog: DefaultActionlog,
|
||||
store: DefaultStore,
|
||||
locale: ls,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc resourceTranslation) Upsert(ctx context.Context, rr locale.ResourceTranslationSet) (err error) {
|
||||
// @todo AC
|
||||
// @todo validation
|
||||
|
||||
defer locale.Global().ReloadResourceTranslations(ctx)
|
||||
me := auth.GetIdentityFromContext(ctx)
|
||||
|
||||
// - group by resource
|
||||
localeByRes := make(map[string]locale.ResourceTranslationSet)
|
||||
for _, r := range rr {
|
||||
localeByRes[r.Resource] = append(localeByRes[r.Resource], r)
|
||||
}
|
||||
|
||||
// - for each resource, fetch the current state
|
||||
sysLocale := make(systemTypes.ResourceTranslationSet, 0, len(rr))
|
||||
for res, rr := range localeByRes {
|
||||
current, _, err := store.SearchResourceTranslations(ctx, svc.store, systemTypes.ResourceTranslationFilter{
|
||||
Resource: res,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get deltas and prepare upsert accordingly
|
||||
aux := current.New(rr)
|
||||
aux.Walk(func(cc *systemTypes.ResourceTranslation) error {
|
||||
cc.ID = nextID()
|
||||
cc.CreatedAt = *now()
|
||||
cc.CreatedBy = me.Identity()
|
||||
|
||||
return nil
|
||||
})
|
||||
sysLocale = append(sysLocale, aux...)
|
||||
|
||||
aux = current.Old(rr)
|
||||
aux.Walk(func(cc *systemTypes.ResourceTranslation) error {
|
||||
cc.UpdatedAt = now()
|
||||
cc.UpdatedBy = me.Identity()
|
||||
return nil
|
||||
})
|
||||
sysLocale = append(sysLocale, aux...)
|
||||
}
|
||||
|
||||
err = store.UpsertResourceTranslation(ctx, svc.store, sysLocale...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc resourceTranslation) Locale() locale.Resource {
|
||||
return svc.locale
|
||||
}
|
||||
|
||||
{{- range .Def }}
|
||||
{{ $Resource := .Resource }}
|
||||
{{ $GoType := printf "types.%s" .Resource }}
|
||||
|
||||
func (svc resourceTranslation) {{ .Resource }}(ctx context.Context, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }} uint64, {{ end }}{{ end }}) (locale.ResourceTranslationSet, error) {
|
||||
var (
|
||||
err error
|
||||
out locale.ResourceTranslationSet
|
||||
res *{{ $GoType }}
|
||||
k types.LocaleKey
|
||||
)
|
||||
|
||||
res, err = svc.load{{$Resource}}(ctx, svc.store, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }}, {{ end }}{{ end }})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, tag := range svc.locale.Tags() {
|
||||
{{- range .Locale.Keys}}
|
||||
{{- if not .Custom }}
|
||||
k = types.LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: res.ResourceTranslation(),
|
||||
Lang: tag.String(),
|
||||
Key: k.Path,
|
||||
Msg: svc.locale.TRFor(tag, res.ResourceTranslation(), k.Path),
|
||||
})
|
||||
{{ end }}
|
||||
{{- end}}
|
||||
|
||||
{{- range .Locale.Keys}}
|
||||
{{- if and .Custom .CustomHandler }}
|
||||
auxResSet, err = svc.{{unexport $Resource}}{{export .CustomHandler}}Handler(ctx, tag, res, k.Path)
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
{{- if .Locale.Extended }}
|
||||
|
||||
tmp, err := svc.{{unexport $Resource}}Extended(ctx, res)
|
||||
return append(out, tmp...), err
|
||||
{{- else }}
|
||||
return out, nil
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,132 @@
|
||||
package {{ .Package }}
|
||||
|
||||
{{ template "header-gentext.tpl" }}
|
||||
{{ template "header-definitions.tpl" . }}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
)
|
||||
|
||||
type (
|
||||
LocaleKey struct {
|
||||
Name string
|
||||
Resource string
|
||||
Path string
|
||||
CustomHandler string
|
||||
}
|
||||
)
|
||||
|
||||
// Types and stuff
|
||||
const (
|
||||
{{- range .Def }}
|
||||
{{ .Resource }}ResourceTranslationType = "{{ .Locale.ResourceType }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range .Def }}
|
||||
{{- $Resource := .Resource }}
|
||||
{{- range .Locale.Keys}}
|
||||
LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }} = LocaleKey{
|
||||
Name: "{{.Name}}",
|
||||
Resource: {{ $Resource }}ResourceTranslationType,
|
||||
Path: "{{.Path }}",{{ if .CustomHandler }}
|
||||
CustomHandler: "{{ .CustomHandler }}",
|
||||
{{- end }}
|
||||
}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
{{- range .Def }}
|
||||
{{ $Resource := .Resource }}
|
||||
{{ $GoType := printf "types.%s" .Resource }}
|
||||
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for {{ .Resource }} by calling {{ .Resource }}ResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the {{ .Locale.ResourceType }}/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r {{ .Resource }}) ResourceTranslation() string {
|
||||
return {{ .Resource }}ResourceTranslation({{ if .Locale.Resource }}{{ range .Locale.Resource.References }}r.{{ export .Field }},{{ end }}{{ end }})
|
||||
}
|
||||
|
||||
// {{ .Resource }}ResourceTranslation returns string representation of Locale resource for {{ .Resource }}
|
||||
//
|
||||
// Locale resource is in the {{ .Locale.ResourceType }}/{{- if .Locale.Resource.References }}...{{ end }} format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func {{ .Resource }}ResourceTranslation({{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ unexport .Field }} uint64,{{ end }}{{ end }}) string {
|
||||
{{- if .Locale.Resource.References }}
|
||||
cpts := []interface{{"{}"}}{{"{"}}{{ .Resource }}ResourceTranslationType{{"}"}}
|
||||
cpts = append(cpts, {{range .Locale.Resource.References -}}
|
||||
strconv.FormatUint({{ unexport .Field }}, 10),
|
||||
{{- end }})
|
||||
|
||||
return fmt.Sprintf({{ .Resource }}ResourceTranslationTpl(), cpts...)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// @todo template
|
||||
func {{ .Resource }}ResourceTranslationTpl() string {
|
||||
{{- if .Locale.Resource.References }}
|
||||
return "%s
|
||||
{{- range .Locale.Resource.References }}/%s{{- end }}"
|
||||
|
||||
{{- else }}
|
||||
return "%s"
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
func (r *{{ .Resource }}) DecodeTranslations(tt locale.ResourceTranslationIndex) {
|
||||
var aux *locale.ResourceTranslation
|
||||
|
||||
{{- range .Locale.Keys}}
|
||||
{{- if not .Custom }}
|
||||
if aux = tt.FindByKey(LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}.Path); aux != nil {
|
||||
r.{{ .Field }} = aux.Msg
|
||||
}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
{{- range .Locale.Keys}}
|
||||
{{- if and .Custom .CustomHandler }}
|
||||
r.decodeTranslations{{export .CustomHandler }}(tt)
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
{{- if .Locale.Extended }}
|
||||
|
||||
r.decodeTranslations(tt)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
func (r *{{ .Resource }}) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = locale.ResourceTranslationSet{
|
||||
{{- range .Locale.Keys}}
|
||||
{{- if not .Custom }}
|
||||
{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}.Path,
|
||||
Msg: r.{{ .Field }},
|
||||
},
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}
|
||||
{{range .Locale.Keys}}
|
||||
{{- if and .Custom .CustomHandler }}
|
||||
out = append(out, r.encodeTranslations{{export .CustomHandler}}()...)
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
{{- if .Locale.Extended }}
|
||||
out = append(out, r.encodeTranslations()...)
|
||||
{{- end }}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
@@ -18,6 +18,7 @@ type (
|
||||
Resource string
|
||||
Source string
|
||||
RBAC *rbac
|
||||
Locale *locale
|
||||
Envoy bool `yaml:"envoy"`
|
||||
}
|
||||
)
|
||||
@@ -67,6 +68,10 @@ func (doc *Document) Proc(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := doc.Locale.proc(doc.Component, doc.Resource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doc.Resource = tpl.Export(doc.Resource)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package def
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl"
|
||||
"github.com/cortezaproject/corteza-server/pkg/y7s"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
locale struct {
|
||||
ResourceType string `yaml:"resourceType"`
|
||||
Resource *resourceTranslation
|
||||
Extended bool
|
||||
SkipSvc bool `yaml:"skipSvc"`
|
||||
Keys localeKeys
|
||||
}
|
||||
|
||||
resourceTranslation struct {
|
||||
References []*resourceTranslationRef
|
||||
}
|
||||
|
||||
resourceTranslationRef struct {
|
||||
Field string
|
||||
ResourceType string
|
||||
Resource string
|
||||
Component string
|
||||
|
||||
custom bool
|
||||
}
|
||||
|
||||
localeKeys []*localeKey
|
||||
|
||||
localeKey struct {
|
||||
Name string
|
||||
Path string
|
||||
Custom bool
|
||||
CustomHandler string `yaml:"customHandler"`
|
||||
Description string
|
||||
Field string
|
||||
}
|
||||
)
|
||||
|
||||
func (r *locale) proc(component, resource string) error {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.ResourceType == "" {
|
||||
if strings.ToLower(resource) == "component" {
|
||||
r.ResourceType = component
|
||||
} else {
|
||||
r.ResourceType = fmt.Sprintf("%s:%s", component, resource)
|
||||
}
|
||||
}
|
||||
|
||||
if r.Resource == nil {
|
||||
r.Resource = &resourceTranslation{References: []*resourceTranslationRef{{Field: "ID"}}}
|
||||
}
|
||||
|
||||
// check types of each referenced component
|
||||
// and self-references (field==ID) with own resource type
|
||||
for _, rc := range r.Resource.References {
|
||||
if !rc.custom {
|
||||
if rc.Field == "ID" {
|
||||
rc.ResourceType = r.ResourceType
|
||||
rc.Component = component
|
||||
rc.Resource = resource
|
||||
} else {
|
||||
rc.ResourceType = fmt.Sprintf("%s:%s", component, rc.Field)
|
||||
rc.Component = component
|
||||
rc.Resource = rc.Field
|
||||
rc.Field = rc.Field + "ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assure missing key field paths
|
||||
for _, k := range r.Keys {
|
||||
k.Custom = k.Custom || k.CustomHandler != ""
|
||||
|
||||
// Guess the key path
|
||||
if k.Field == "" {
|
||||
k.Field = tpl.Export(k.Path)
|
||||
}
|
||||
|
||||
// Guess the name
|
||||
if k.Name == "" {
|
||||
k.Name = k.Path
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (op *localeKey) UnmarshalYAML(n *yaml.Node) error {
|
||||
type auxType localeKey
|
||||
|
||||
switch {
|
||||
case y7s.IsKind(n, yaml.ScalarNode):
|
||||
return y7s.DecodeScalar(n, "locale key", &op.Path)
|
||||
|
||||
case y7s.IsKind(n, yaml.MappingNode):
|
||||
var aux = (*auxType)(op)
|
||||
return n.Decode(aux)
|
||||
}
|
||||
|
||||
return y7s.NodeErr(n, "unsupported formatting")
|
||||
}
|
||||
|
||||
func (op *resourceTranslationRef) UnmarshalYAML(n *yaml.Node) error {
|
||||
if y7s.IsKind(n, yaml.ScalarNode) {
|
||||
op.Field = n.Value
|
||||
if n.Value != "ID" {
|
||||
op.ResourceType = n.Value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type auxType resourceTranslationRef
|
||||
var aux = (*auxType)(op)
|
||||
aux.custom = true
|
||||
return n.Decode(aux)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def"
|
||||
"github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl"
|
||||
)
|
||||
|
||||
func Locale(t *template.Template, dd []*def.Document) error {
|
||||
return List{
|
||||
"types": localeTypes,
|
||||
"services": localeServices,
|
||||
}.Generate(t, dd)
|
||||
}
|
||||
|
||||
func localeTypes(t *template.Template, dd []*def.Document) (err error) {
|
||||
const (
|
||||
templateName = "locale/types.go.tpl"
|
||||
outputPathTpl = "%s/types/locale.gen.go"
|
||||
)
|
||||
|
||||
dd = filter(dd, func(d *def.Document) bool { return d.Locale != nil })
|
||||
|
||||
for component, perComponent := range partByComponent(dd) {
|
||||
w := tpl.Wrap{
|
||||
Package: "types",
|
||||
Component: component,
|
||||
Def: perComponent,
|
||||
}
|
||||
|
||||
err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func localeServices(t *template.Template, dd []*def.Document) (err error) {
|
||||
const (
|
||||
templateName = "locale/service.go.tpl"
|
||||
outputPathTpl = "%s/service/locale.gen.go"
|
||||
)
|
||||
|
||||
dd = filter(dd, func(d *def.Document) bool { return d.Locale != nil && !d.Locale.SkipSvc })
|
||||
|
||||
for component, perComponent := range partByComponent(dd) {
|
||||
w := tpl.Wrap{
|
||||
Package: "service",
|
||||
Component: component,
|
||||
Def: perComponent,
|
||||
}
|
||||
|
||||
err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, d := range dd {
|
||||
if d.Locale != nil {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -26,8 +26,9 @@ func main() {
|
||||
}
|
||||
|
||||
cli.HandleError(gen.List{
|
||||
"RBAC": gen.RBAC,
|
||||
"Envoy": gen.Envoy,
|
||||
"RBAC": gen.RBAC,
|
||||
"Envoy": gen.Envoy,
|
||||
"Locale": gen.Locale,
|
||||
}.Generate(tpls, dd))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user