3
0

Migrate codegen for locale to CUE

This commit is contained in:
Denis Arh
2021-12-28 21:36:59 +01:00
committed by Tomaž Jerman
parent 2404ce9f8d
commit 3bddce4d35
2 changed files with 326 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
package {{ .package }}
{{ template "gocode/header-gentext.tpl" }}
import (
"context"
{{- range .imports }}
{{ . }}
{{- end }}
"github.com/cortezaproject/corteza-server/pkg/actionlog"
intAuth "github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/store"
systemTypes "github.com/cortezaproject/corteza-server/system/types"
"golang.org/x/text/language"
)
type (
localeAccessControl interface {
CanManageResourceTranslations(ctx context.Context) bool
}
resourceTranslationsManager struct {
actionlog actionlog.Recorder
locale locale.Resource
store store.Storer
ac localeAccessController
}
localeAccessController interface {
CanManageResourceTranslations(context.Context) bool
}
ResourceTranslationsManagerService interface {
{{- range .resources }}
{{ .expIdent }}(ctx context.Context, {{ range .references }}{{ . }} uint64, {{ end }}) (locale.ResourceTranslationSet, error)
{{- end }}
Upsert(context.Context, locale.ResourceTranslationSet) error
Locale() locale.Resource
}
)
var ErrNotAllowedToManageResourceTranslations = errors.Unauthorized("not allowed to manage resource translations")
func ResourceTranslationsManager(ls locale.Resource) *resourceTranslationsManager {
return &resourceTranslationsManager{
actionlog: DefaultActionlog,
store: DefaultStore,
ac: DefaultAccessControl,
locale: ls,
}
}
func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.ResourceTranslationSet) (err error) {
// User is allowed to manage resource translations when:
// - managed resource translation strings are all for default language
// or
// - user is allowed to manage resource translations
if rr.ContainsForeign(svc.Locale().Default().Tag) {
if !svc.ac.CanManageResourceTranslations(ctx) {
return ErrNotAllowedToManageResourceTranslations
}
}
for _, r := range rr {
r.Msg = locale.SanitizeMessage(r.Msg)
}
// @todo validation
me := intAuth.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
}
// Reload ALL resource translations
// @todo we could probably do this more selectively and refresh only updated resources?
_ = locale.Global().ReloadResourceTranslations(ctx)
return nil
}
func (svc resourceTranslationsManager) Locale() locale.Resource {
return svc.locale
}
{{- range .resources }}
func (svc resourceTranslationsManager) {{ .expIdent }}(ctx context.Context, {{ range .references }}{{ . }} uint64, {{ end }}) (locale.ResourceTranslationSet, error) {
var (
err error
out locale.ResourceTranslationSet
res *types.{{ .expIdent }}
k types.LocaleKey
)
res, err = svc.load{{ .expIdent }}(ctx, svc.store, {{ range .references }}{{ . }}, {{ end }})
if err != nil {
return nil, err
}
for _, tag := range svc.locale.Tags() {
{{- range .keys}}
{{- if not .customHandler }}
k = types.{{ .struct }}
out = append(out, &locale.ResourceTranslation{
Resource: res.ResourceTranslation(),
Lang: tag.String(),
Key: k.Path,
Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path),
})
{{ end }}
{{- end}}
}
{{ if .extended }}
tmp, err := svc.{{ .ident }}Extended(ctx, res)
return append(out, tmp...), err
{{- else }}
return out, nil
{{- end }}
}
{{- end }}
func updateTranslations(ctx context.Context, ac localeAccessControl, lsvc ResourceTranslationsManagerService, tt ...*locale.ResourceTranslation) error {
if lsvc == nil || lsvc.Locale() == nil || lsvc.Locale().Default() == nil {
// gracefully handle partial initializations
return nil
}
var (
// assuming options will not change after start
contentLang = lsvc.Locale().Default().Tag
)
if options.Locale().ResourceTranslationsEnabled {
contentLang = locale.GetContentLanguageFromContext(ctx)
// Resource translations enabled
if contentLang == language.Und {
// If no content-language meta (HTTP header) info was
// used, do not run update translations - we do not know
// what is the language that we're sending in
return nil
}
if !lsvc.Locale().SupportedLang(contentLang) {
// unsupported language
return errors.InvalidData("unsupported language")
}
if !ac.CanManageResourceTranslations(ctx) {
return errors.Unauthorized("not allowed to manage resource translations")
}
}
locale.ResourceTranslationSet(tt).SetLanguage(contentLang)
if err := lsvc.Upsert(ctx, tt); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,120 @@
package {{ .package }}
{{ template "gocode/header-gentext.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 .resources }}
{{ .const }} = "{{ .type }}"
{{- end }}
)
var (
// @todo can we remove LocaleKey struct for string constant?
{{- range .resources }}
{{- range .keys }}
{{ .struct }} = LocaleKey{ Path: {{ printf "%q" .path }} }
{{- end }}
{{- end }}
)
{{- range .resources }}
// ResourceTranslation returns string representation of Locale resource for {{ .expIdent }} by calling {{ .expIdent }}ResourceTranslation fn
//
// Locale resource is in "{{ .type }}/..." format
//
// This function is auto-generated
func (r {{ .expIdent }}) ResourceTranslation() string {
return {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}r.{{ . }},{{ end }}{{ end }})
}
// {{ .expIdent }}ResourceTranslation returns string representation of Locale resource for {{ .expIdent }}
//
// Locale resource is in the {{ .type }}/{{- if .references }}...{{ end }} format
//
// This function is auto-generated
func {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}{{ . }} uint64,{{ end }}{{ end }}) string {
{{- if .references }}
cpts := []interface{{"{}"}}{
{{ .expIdent }}ResourceTranslationType,
{{- range .references }}
strconv.FormatUint({{ . }}, 10),
{{- end }}
}
return fmt.Sprintf({{ .expIdent }}ResourceTranslationTpl(), cpts...)
{{- end }}
}
func {{ .expIdent }}ResourceTranslationTpl() string {
{{- if .references }}
return "%s
{{- range .references }}/%s{{- end }}"
{{- else }}
return "%s"
{{- end }}
}
func (r *{{ .expIdent }}) DecodeTranslations(tt locale.ResourceTranslationIndex) {
var aux *locale.ResourceTranslation
{{- range .keys }}
{{ if .decodeFunc }}
{{ if not .extended }}
r.{{ .decodeFunc }}(tt)
{{- end}}
{{ else }}
if aux = tt.FindByKey({{ .struct }}.Path); aux != nil {
r.{{ .fieldPath }} = aux.Msg
}
{{- end}}
{{- end}}
{{- if .extended }}
r.decodeTranslations(tt)
{{- end }}
}
func (r *{{ .expIdent }}) EncodeTranslations() (out locale.ResourceTranslationSet) {
out = locale.ResourceTranslationSet{}
{{- range .keys }}
{{ if .encodeFunc }}
{{ if not .extended }}
out = append(out, r.{{ .encodeFunc }}()...)
{{- end}}
{{ else }}
if r.{{ .fieldPath }} != "" {
out = append(out, &locale.ResourceTranslation{
Resource: r.ResourceTranslation(),
Key: {{ .struct }}.Path,
Msg: locale.SanitizeMessage(r.{{ .fieldPath }}),
})
}
{{- end}}
{{- end}}
{{- if .extended }}
out = append(out, r.encodeTranslations()...)
{{- end }}
return out
}
{{- end }}