Resource translation migration
This commit is contained in:
Generated
+1
-1
@@ -90,7 +90,7 @@ var (
|
||||
LocaleKeyPageBlockAutomationButtonlabel = LocaleKey{
|
||||
Name: "blockAutomationButtonlabel",
|
||||
Resource: PageResourceTranslationType,
|
||||
Path: "pageBlock.{{blockID}}.automation.{{buttonID}}.label",
|
||||
Path: "pageBlock.{{blockID}}.button.{{buttonID}}.label",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ locale:
|
||||
- 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 }
|
||||
- { name: blockAutomationButtonlabel, path: "pageBlock.{{blockID}}.button.{{buttonID}}.label", custom: true }
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
package provision
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cmpTypes "github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
sysTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Migrates resource translations from the resource
|
||||
// struct to the dedicated store (table)
|
||||
//
|
||||
// While doing this, we also modify some resource substructure:
|
||||
// - page blocks (assign page block IDs
|
||||
// - automation buttons on page blocks (assign automation button IDs)
|
||||
// - field expressions (assign expression IDs)
|
||||
//
|
||||
// Note: we will migrate all translations to current default language
|
||||
// If you do not like that, shut down Corteza after migrations and fix this directly in the store
|
||||
func migrateResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) (err error) {
|
||||
log.Info("migrating resource locales")
|
||||
|
||||
var (
|
||||
migrated = make(map[string]bool)
|
||||
)
|
||||
|
||||
// might be useful when developing or debugging migration
|
||||
//_ = store.TruncateResourceTranslations(ctx, s)
|
||||
|
||||
set, _, err := store.SearchResourceTranslations(ctx, s, sysTypes.ResourceTranslationFilter{})
|
||||
set.Walk(func(r *sysTypes.ResourceTranslation) error {
|
||||
var pos = strings.Index(r.Resource, "/")
|
||||
if pos < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
migrated[r.Resource[0:pos]] = true
|
||||
return nil
|
||||
})
|
||||
|
||||
if !migrated[cmpTypes.NamespaceResourceTranslationType] {
|
||||
if err = migrateComposeNamespaceResourceTranslations(ctx, log, s); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !migrated[cmpTypes.ModuleResourceTranslationType] {
|
||||
// @todo migrate module locales
|
||||
if err = migrateComposeModuleResourceTranslations(ctx, log, s); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !migrated[cmpTypes.PageResourceTranslationType] {
|
||||
if err = migrateComposePageResourceTranslations(ctx, log, s); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// helper fn that creates ResourceTranslation from the given resource, key and message
|
||||
// and default language
|
||||
func makeResourceTranslation(lr interface{ ResourceTranslation() string }, k, m string) *sysTypes.ResourceTranslation {
|
||||
return &sysTypes.ResourceTranslation{
|
||||
ID: id.Next(),
|
||||
Lang: sysTypes.Lang{Tag: locale.Global().Default().Tag},
|
||||
Resource: lr.ResourceTranslation(),
|
||||
K: k,
|
||||
Message: m,
|
||||
CreatedAt: *now(),
|
||||
}
|
||||
}
|
||||
|
||||
// migrate resource translations for compose namespace
|
||||
func migrateComposeNamespaceResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) error {
|
||||
set, _, err := store.SearchComposeNamespaces(ctx, s, cmpTypes.NamespaceFilter{Deleted: filter.StateInclusive})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("migrating compose namespaces", zap.Int("count", len(set)))
|
||||
|
||||
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
|
||||
return set.Walk(func(res *cmpTypes.Namespace) error {
|
||||
return store.CreateResourceTranslation(ctx, s,
|
||||
makeResourceTranslation(res, "name", res.Name),
|
||||
makeResourceTranslation(res, "subtitle", res.Meta.Subtitle),
|
||||
makeResourceTranslation(res, "description", res.Meta.Description),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// migrate resource translations for compose module
|
||||
func migrateComposeModuleResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) error {
|
||||
set, _, err := store.SearchComposeModules(ctx, s, cmpTypes.ModuleFilter{Deleted: filter.StateInclusive})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("migrating compose modules", zap.Int("count", len(set)))
|
||||
|
||||
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
|
||||
return set.Walk(func(res *cmpTypes.Module) (err error) {
|
||||
if err = migrateComposeModuleFieldResourceTranslations(ctx, s, res.NamespaceID, res.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.CreateResourceTranslation(ctx, s,
|
||||
makeResourceTranslation(res, "name", res.Name),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.UpdateComposeModule(ctx, s, res)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// migrate module fields translations for compose module field
|
||||
//
|
||||
// Adds validatorID on field expressions
|
||||
func migrateComposeModuleFieldResourceTranslations(ctx context.Context, s store.Storer, namespaceID uint64, moduleIDs ...uint64) error {
|
||||
set, _, err := store.SearchComposeModuleFields(ctx, s, cmpTypes.ModuleFieldFilter{ModuleID: moduleIDs})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
|
||||
return set.Walk(func(res *cmpTypes.ModuleField) (err error) {
|
||||
tt := sysTypes.ResourceTranslationSet{}
|
||||
|
||||
// store does not contain this info,
|
||||
// but we need it to generate resource id
|
||||
res.NamespaceID = namespaceID
|
||||
tt = append(tt, makeResourceTranslation(res, "label", res.Name))
|
||||
|
||||
var update = len(res.Expressions.Validators) > 0
|
||||
|
||||
for i := range res.Expressions.Validators {
|
||||
validatorID := i + 1
|
||||
res.Expressions.Validators[i].ValidatorID = uint64(validatorID)
|
||||
tt = append(tt, makeResourceTranslation(res, fmt.Sprintf("expression.validator.%d.error", validatorID), res.Name))
|
||||
}
|
||||
|
||||
if update {
|
||||
if err = store.CreateResourceTranslation(ctx, s, tt...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = store.UpdateComposeModuleField(ctx, s, res); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// migrate resource translations for compose module
|
||||
func migrateComposePageResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) error {
|
||||
var tt sysTypes.ResourceTranslationSet
|
||||
set, _, err := store.SearchComposePages(ctx, s, cmpTypes.PageFilter{Deleted: filter.StateInclusive})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// @todo migrate page locales
|
||||
// @todo page block IDs
|
||||
// @todo automation button IDs
|
||||
|
||||
log.Info("migrating compose pages", zap.Int("count", len(set)))
|
||||
|
||||
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
|
||||
return set.Walk(func(res *cmpTypes.Page) (err error) {
|
||||
tt = sysTypes.ResourceTranslationSet{
|
||||
makeResourceTranslation(res, "title", res.Title),
|
||||
makeResourceTranslation(res, "description", res.Description),
|
||||
}
|
||||
|
||||
if pbtt, err := convertComposePageBlockTranslations(res); err != nil {
|
||||
return err
|
||||
} else {
|
||||
tt = append(tt, pbtt...)
|
||||
}
|
||||
|
||||
if err = store.CreateResourceTranslation(ctx, s, tt...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = store.UpdateComposePage(ctx, s, res); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// collects translations for compose page blocks and alters them (adding block ID)
|
||||
func convertComposePageBlockTranslations(res *cmpTypes.Page) (sysTypes.ResourceTranslationSet, error) {
|
||||
var tt sysTypes.ResourceTranslationSet
|
||||
|
||||
for i, b := range res.Blocks {
|
||||
blockID := i + 1
|
||||
res.Blocks[i].BlockID = uint64(blockID)
|
||||
pfx := fmt.Sprintf("pageBlock.%d.", blockID)
|
||||
|
||||
tt = append(tt,
|
||||
makeResourceTranslation(res, pfx+"title", b.Title),
|
||||
makeResourceTranslation(res, pfx+"description", b.Description),
|
||||
)
|
||||
|
||||
switch b.Kind {
|
||||
case "Automation":
|
||||
att, bb, err := convertComposeAutomationPageBlockTranslations(res, pfx, res.Blocks[i].Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Blocks[i].Options["buttons"] = bb
|
||||
tt = append(tt, att...)
|
||||
}
|
||||
}
|
||||
|
||||
return tt, nil
|
||||
}
|
||||
|
||||
// takes automation options for page block and returns translations + fixed set of buttons (containing content ID)
|
||||
func convertComposeAutomationPageBlockTranslations(res *cmpTypes.Page, pfx string, opt map[string]interface{}) (tt sysTypes.ResourceTranslationSet, bb []interface{}, err error) {
|
||||
if tmp, ok := opt["buttons"]; !ok {
|
||||
return nil, nil, nil
|
||||
} else if bb, ok = tmp.([]interface{}); !ok {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
for i := range bb {
|
||||
b, is := bb[i].(map[string]interface{})
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
buttonID := i + 1
|
||||
|
||||
b["buttonID"] = buttonID
|
||||
bb[i] = b
|
||||
|
||||
var label string
|
||||
if _, has := b["label"]; has {
|
||||
label = b["label"].(string)
|
||||
}
|
||||
|
||||
tt = append(tt, makeResourceTranslation(res, fmt.Sprintf("%sbuttons.%d.label", pfx, buttonID), label))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -31,6 +31,7 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti
|
||||
func() error { return migratePre202109Roles(ctx, log.Named("pre-202109-roles"), s) },
|
||||
func() error { return migratePre202109RbacRules(ctx, log.Named("pre-202109-rbac-rules"), s) },
|
||||
func() error { return cleanupPre202109Settings(ctx, log.Named("pre-202109-settings"), s) },
|
||||
func() error { return migrateResourceTranslations(ctx, log.Named("resource-translations"), s) },
|
||||
|
||||
// Config (full & partial)
|
||||
func() error { return importConfig(ctx, log.Named("config"), s, provisionOpt.Path) },
|
||||
|
||||
Reference in New Issue
Block a user