3
0

Add rest endpoints for chart translations

It allows saving/updating yAxis label and metric label translations
This commit is contained in:
Vivek Patel
2022-04-26 16:40:58 +05:30
parent 7cdf51adc4
commit 86431fe82f
20 changed files with 713 additions and 63 deletions

View File

@@ -154,7 +154,6 @@ func (svc resourceTranslationsManager) {{ .expIdent }}(ctx context.Context, {{ r
err error
out locale.ResourceTranslationSet
res *types.{{ .expIdent }}
k types.LocaleKey
)
res, err = svc.load{{ .expIdent }}(ctx, svc.store, {{ range .references }}{{ .param }}, {{ end }})
@@ -162,19 +161,29 @@ func (svc resourceTranslationsManager) {{ .expIdent }}(ctx context.Context, {{ r
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}}
}
{{ $customHandlerExist := false }}
{{range .keys}}
{{if not .customHandler}}
{{ $customHandlerExist = true }}
{{end}}
{{end}}
{{ if $customHandlerExist }}
var k types.LocaleKey
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}}
}
{{ end }}
{{ if .extended }}
tmp, err := svc.{{ .ident }}Extended(ctx, res)

View File

@@ -73,7 +73,16 @@ func {{ .expIdent }}ResourceTranslationTpl() string {
}
func (r *{{ .expIdent }}) DecodeTranslations(tt locale.ResourceTranslationIndex) {
var aux *locale.ResourceTranslation
{{- $decodeFuncExist := false }}
{{- range .keys }}
{{- if not .decodeFunc }}
{{- $decodeFuncExist = true }}
{{- end }}
{{- end}}
{{- if $decodeFuncExist }}
var aux *locale.ResourceTranslation
{{- end }}
{{- range .keys }}
{{ if .decodeFunc }}

View File

@@ -43,6 +43,21 @@ chart: schema.#Resource & {
}
}
locale: {
extended: true
keys: {
reportsYaxisLabel: {
path: ["yAxis", "label"]
customHandler: true
}
reportsMetricLabel: {
path: ["metrics", {part: "metricID", var: true}, "label"]
customHandler: true
}
}
}
store: {
ident: "composeChart"

View File

@@ -1089,6 +1089,32 @@ endpoints:
name: chartID
required: true
title: Chart ID
- name: listTranslations
method: GET
title: List chart translation
path: "/{chartID}/translation"
parameters:
path:
- type: uint64
name: chartID
required: true
title: ID
- name: updateTranslations
method: PATCH
title: Update chart translation
path: "/{chartID}/translation"
parameters:
path:
- type: uint64
name: chartID
required: true
title: ID
post:
- name: translations
type: locale.ResourceTranslationSet
title: Resource translation to upsert
required: true
- title: Notifications
description: Compose Notifications
entrypoint: notification

View File

@@ -37,7 +37,8 @@ type (
Update(ctx context.Context, chart *types.Chart) (*types.Chart, error)
DeleteByID(ctx context.Context, namespaceID, chartID uint64) error
}
ac chartAccessController
locale service.ResourceTranslationsManagerService
ac chartAccessController
}
chartAccessController interface {
@@ -50,8 +51,9 @@ type (
func (Chart) New() *Chart {
return &Chart{
chart: service.DefaultChart,
ac: service.DefaultAccessControl,
chart: service.DefaultChart,
locale: service.DefaultResourceTranslation,
ac: service.DefaultAccessControl,
}
}
@@ -134,6 +136,14 @@ func (ctrl Chart) Delete(ctx context.Context, r *request.ChartDelete) (interface
return api.OK(), ctrl.chart.DeleteByID(ctx, r.NamespaceID, r.ChartID)
}
func (ctrl Chart) ListTranslations(ctx context.Context, r *request.ChartListTranslations) (interface{}, error) {
return ctrl.locale.Chart(ctx, r.NamespaceID, r.ChartID)
}
func (ctrl Chart) UpdateTranslations(ctx context.Context, r *request.ChartUpdateTranslations) (interface{}, error) {
return api.OK(), ctrl.locale.Upsert(ctx, r.Translations)
}
func (ctrl Chart) makePayload(ctx context.Context, c *types.Chart, err error) (*chartPayload, error) {
if err != nil || c == nil {
return nil, err

View File

@@ -24,15 +24,19 @@ type (
Read(context.Context, *request.ChartRead) (interface{}, error)
Update(context.Context, *request.ChartUpdate) (interface{}, error)
Delete(context.Context, *request.ChartDelete) (interface{}, error)
ListTranslations(context.Context, *request.ChartListTranslations) (interface{}, error)
UpdateTranslations(context.Context, *request.ChartUpdateTranslations) (interface{}, error)
}
// HTTP API interface
Chart struct {
List func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
List func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
ListTranslations func(http.ResponseWriter, *http.Request)
UpdateTranslations func(http.ResponseWriter, *http.Request)
}
)
@@ -116,6 +120,38 @@ func NewChart(h ChartAPI) *Chart {
return
}
api.Send(w, r, value)
},
ListTranslations: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewChartListTranslations()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.ListTranslations(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
UpdateTranslations: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewChartUpdateTranslations()
if err := params.Fill(r); err != nil {
api.Send(w, r, err)
return
}
value, err := h.UpdateTranslations(r.Context(), params)
if err != nil {
api.Send(w, r, err)
return
}
api.Send(w, r, value)
},
}
@@ -129,5 +165,7 @@ func (h Chart) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.
r.Get("/namespace/{namespaceID}/chart/{chartID}", h.Read)
r.Post("/namespace/{namespaceID}/chart/{chartID}", h.Update)
r.Delete("/namespace/{namespaceID}/chart/{chartID}", h.Delete)
r.Get("/namespace/{namespaceID}/chart/{chartID}/translation", h.ListTranslations)
r.Patch("/namespace/{namespaceID}/chart/{chartID}/translation", h.UpdateTranslations)
})
}

View File

@@ -12,6 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
@@ -160,6 +161,35 @@ type (
// Chart ID
ChartID uint64 `json:",string"`
}
ChartListTranslations struct {
// NamespaceID PATH parameter
//
// Namespace ID
NamespaceID uint64 `json:",string"`
// ChartID PATH parameter
//
// ID
ChartID uint64 `json:",string"`
}
ChartUpdateTranslations struct {
// NamespaceID PATH parameter
//
// Namespace ID
NamespaceID uint64 `json:",string"`
// ChartID PATH parameter
//
// ID
ChartID uint64 `json:",string"`
// Translations POST parameter
//
// Resource translation to upsert
Translations locale.ResourceTranslationSet
}
)
// NewChartList request
@@ -711,3 +741,139 @@ func (r *ChartDelete) Fill(req *http.Request) (err error) {
return err
}
// NewChartListTranslations request
func NewChartListTranslations() *ChartListTranslations {
return &ChartListTranslations{}
}
// Auditable returns all auditable/loggable parameters
func (r ChartListTranslations) Auditable() map[string]interface{} {
return map[string]interface{}{
"namespaceID": r.NamespaceID,
"chartID": r.ChartID,
}
}
// Auditable returns all auditable/loggable parameters
func (r ChartListTranslations) GetNamespaceID() uint64 {
return r.NamespaceID
}
// Auditable returns all auditable/loggable parameters
func (r ChartListTranslations) GetChartID() uint64 {
return r.ChartID
}
// Fill processes request and fills internal variables
func (r *ChartListTranslations) Fill(req *http.Request) (err error) {
{
var val string
// path params
val = chi.URLParam(req, "namespaceID")
r.NamespaceID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
val = chi.URLParam(req, "chartID")
r.ChartID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
}
return err
}
// NewChartUpdateTranslations request
func NewChartUpdateTranslations() *ChartUpdateTranslations {
return &ChartUpdateTranslations{}
}
// Auditable returns all auditable/loggable parameters
func (r ChartUpdateTranslations) Auditable() map[string]interface{} {
return map[string]interface{}{
"namespaceID": r.NamespaceID,
"chartID": r.ChartID,
"translations": r.Translations,
}
}
// Auditable returns all auditable/loggable parameters
func (r ChartUpdateTranslations) GetNamespaceID() uint64 {
return r.NamespaceID
}
// Auditable returns all auditable/loggable parameters
func (r ChartUpdateTranslations) GetChartID() uint64 {
return r.ChartID
}
// Auditable returns all auditable/loggable parameters
func (r ChartUpdateTranslations) GetTranslations() locale.ResourceTranslationSet {
return r.Translations
}
// Fill processes request and fills internal variables
func (r *ChartUpdateTranslations) Fill(req *http.Request) (err error) {
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return fmt.Errorf("error parsing http request body: %w", err)
}
}
{
// Caching 32MB to memory, the rest to disk
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
return err
} else if err == nil {
// Multipart params
}
}
{
if err = req.ParseForm(); err != nil {
return err
}
// POST params
//if val, ok := req.Form["translations[]"]; ok && len(val) > 0 {
// r.Translations, err = locale.ResourceTranslationSet(val), nil
// if err != nil {
// return err
// }
//}
}
{
var val string
// path params
val = chi.URLParam(req, "namespaceID")
r.NamespaceID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
val = chi.URLParam(req, "chartID")
r.ChartID, err = payload.ParseUint64(val), nil
if err != nil {
return err
}
}
return err
}

View File

@@ -2,7 +2,9 @@ package service
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/locale"
"reflect"
"strconv"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/actionlog"
@@ -17,9 +19,11 @@ type (
actionlog actionlog.Recorder
ac chartAccessController
store store.Storer
locale ResourceTranslationsManagerService
}
chartAccessController interface {
CanManageResourceTranslations(ctx context.Context) bool
CanSearchChartsOnNamespace(context.Context, *types.Namespace) bool
CanReadNamespace(context.Context, *types.Namespace) bool
CanCreateChartOnNamespace(context.Context, *types.Namespace) bool
@@ -44,12 +48,14 @@ func Chart() *chart {
ac: DefaultAccessControl,
actionlog: DefaultActionlog,
store: DefaultStore,
locale: DefaultResourceTranslation,
}
}
func (svc chart) Find(ctx context.Context, filter types.ChartFilter) (set types.ChartSet, f types.ChartFilter, err error) {
var (
aProps = &chartActionProps{filter: &filter}
ns *types.Namespace
)
// For each fetched item, store backend will check if it is valid or not
@@ -62,7 +68,7 @@ func (svc chart) Find(ctx context.Context, filter types.ChartFilter) (set types.
}
err = func() error {
ns, err := loadNamespace(ctx, svc.store, filter.NamespaceID)
ns, err = loadNamespace(ctx, svc.store, filter.NamespaceID)
if err != nil {
return err
}
@@ -99,6 +105,13 @@ func (svc chart) Find(ctx context.Context, filter types.ChartFilter) (set types.
return err
}
// i18n
tag := locale.GetAcceptLanguageFromContext(ctx)
set.Walk(func(p *types.Chart) error {
p.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, p.ResourceTranslation()))
return nil
})
return nil
}()
@@ -158,10 +171,23 @@ func (svc chart) Create(ctx context.Context, new *types.Chart) (*types.Chart, er
new.UpdatedAt = nil
new.DeletedAt = nil
// Ensure chart report IDs
for i, report := range new.Config.Reports {
new.Config.Reports[i].ReportID = nextID()
// Ensure chart report metric IDs
for j := range report.Metrics {
new.Config.Reports[i].Metrics[j]["metricID"] = strconv.FormatUint(nextID(), 10)
}
}
if err = store.CreateComposeChart(ctx, s, new); err != nil {
return err
}
if err = updateTranslations(ctx, svc.ac, svc.locale, new.EncodeTranslations()...); err != nil {
return
}
if err = label.Create(ctx, s, new); err != nil {
return
}
@@ -249,6 +275,10 @@ func (svc chart) updater(ctx context.Context, namespaceID, chartID uint64, actio
}
}
if err = updateTranslations(ctx, svc.ac, svc.locale, c.EncodeTranslations()...); err != nil {
return
}
if changes&chartLabelsChanged > 0 {
if err = label.Update(ctx, s, c); err != nil {
return
@@ -304,6 +334,25 @@ func (svc chart) handleUpdate(ctx context.Context, upd *types.Chart) chartUpdate
res.Config = upd.Config
}
// Assure ReportIDs
for i, r := range res.Config.Reports {
if r.ReportID == 0 {
r.ReportID = nextID()
res.Config.Reports[i] = r
changes |= chartChanged
}
// Ensure chart report metric IDs
for j, m := range r.Metrics {
if val, ok := m["metricID"]; !ok || val == 0 {
m["metricID"] = strconv.FormatUint(nextID(), 10)
res.Config.Reports[i].Metrics[j] = m
changes |= chartChanged
}
}
}
if changes&chartChanged > 0 {
res.UpdatedAt = now()
}

View File

@@ -37,6 +37,7 @@ type (
}
ResourceTranslationsManagerService interface {
Chart(ctx context.Context, namespaceID uint64, id uint64) (locale.ResourceTranslationSet, error)
Module(ctx context.Context, namespaceID uint64, id uint64) (locale.ResourceTranslationSet, error)
Namespace(ctx context.Context, id uint64) (locale.ResourceTranslationSet, error)
Page(ctx context.Context, namespaceID uint64, id uint64) (locale.ResourceTranslationSet, error)
@@ -148,12 +149,27 @@ func (svc resourceTranslationsManager) Locale() locale.Resource {
return svc.locale
}
func (svc resourceTranslationsManager) Chart(ctx context.Context, namespaceID uint64, id uint64) (locale.ResourceTranslationSet, error) {
var (
err error
out locale.ResourceTranslationSet
res *types.Chart
)
res, err = svc.loadChart(ctx, svc.store, namespaceID, id)
if err != nil {
return nil, err
}
tmp, err := svc.chartExtended(ctx, res)
return append(out, tmp...), err
}
func (svc resourceTranslationsManager) Module(ctx context.Context, namespaceID uint64, id uint64) (locale.ResourceTranslationSet, error) {
var (
err error
out locale.ResourceTranslationSet
res *types.Module
k types.LocaleKey
)
res, err = svc.loadModule(ctx, svc.store, namespaceID, id)
@@ -161,6 +177,7 @@ func (svc resourceTranslationsManager) Module(ctx context.Context, namespaceID u
return nil, err
}
var k types.LocaleKey
for _, tag := range svc.locale.Tags() {
k = types.LocaleKeyModuleName
out = append(out, &locale.ResourceTranslation{
@@ -181,7 +198,6 @@ func (svc resourceTranslationsManager) Namespace(ctx context.Context, id uint64)
err error
out locale.ResourceTranslationSet
res *types.Namespace
k types.LocaleKey
)
res, err = svc.loadNamespace(ctx, svc.store, id)
@@ -189,6 +205,7 @@ func (svc resourceTranslationsManager) Namespace(ctx context.Context, id uint64)
return nil, err
}
var k types.LocaleKey
for _, tag := range svc.locale.Tags() {
k = types.LocaleKeyNamespaceName
out = append(out, &locale.ResourceTranslation{
@@ -224,7 +241,6 @@ func (svc resourceTranslationsManager) Page(ctx context.Context, namespaceID uin
err error
out locale.ResourceTranslationSet
res *types.Page
k types.LocaleKey
)
res, err = svc.loadPage(ctx, svc.store, namespaceID, id)
@@ -232,6 +248,7 @@ func (svc resourceTranslationsManager) Page(ctx context.Context, namespaceID uin
return nil, err
}
var k types.LocaleKey
for _, tag := range svc.locale.Tags() {
k = types.LocaleKeyPageTitle
out = append(out, &locale.ResourceTranslation{

View File

@@ -272,6 +272,45 @@ func (svc resourceTranslationsManager) pageExtended(ctx context.Context, res *ty
return
}
func (svc resourceTranslationsManager) chartExtended(_ context.Context, res *types.Chart) (out locale.ResourceTranslationSet, err error) {
var (
yAxisLabelK = types.LocaleKeyChartYAxisLabel
metricLabelK = types.LocaleKeyChartMetricsMetricIDLabel
)
for _, report := range res.Config.Reports {
for _, tag := range svc.locale.Tags() {
out = append(out, &locale.ResourceTranslation{
Resource: res.ResourceTranslation(),
Lang: tag.String(),
Key: yAxisLabelK.Path,
Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), yAxisLabelK.Path),
})
}
for _, metric := range report.Metrics {
if _, ok := metric["metricID"]; ok {
mID, is := metric["metricID"].(string)
if !is {
continue
}
mpl := strings.NewReplacer("{{metricID}}", mID)
for _, tag := range svc.locale.Tags() {
out = append(out, &locale.ResourceTranslation{
Resource: res.ResourceTranslation(),
Lang: tag.String(),
Key: mpl.Replace(metricLabelK.Path),
Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), mpl.Replace(metricLabelK.Path)),
})
}
}
}
}
return
}
func (svc resourceTranslationsManager) pageExtendedAutomationBlock(tag language.Tag, res *types.Page, block types.PageBlock, blockID uint64) (locale.ResourceTranslationSet, error) {
var (
k = types.LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel

View File

@@ -3,6 +3,8 @@ package types
import (
"database/sql/driver"
"encoding/json"
"github.com/cortezaproject/corteza-server/pkg/locale"
"strings"
"time"
"github.com/cortezaproject/corteza-server/pkg/filter"
@@ -31,6 +33,7 @@ type (
}
ChartConfigReport struct {
ReportID uint64 `json:"reportID,string,omitempty"`
Filter string `json:"filter"`
ModuleID uint64 `json:"moduleID,string,omitempty"`
Metrics []map[string]interface{} `json:"metrics,omitempty"`
@@ -65,6 +68,76 @@ type (
}
)
func (c Chart) decodeTranslations(tt locale.ResourceTranslationIndex) {
var aux *locale.ResourceTranslation
for i, report := range c.Config.Reports {
if aux = tt.FindByKey(LocaleKeyChartYAxisLabel.Path); aux != nil {
c.Config.Reports[i].YAxis["label"] = aux.Msg
}
for j, metric := range report.Metrics {
if metricID, ok := metric["metricID"]; ok {
mID, is := metricID.(string)
if !is {
continue
}
mpl := strings.NewReplacer(
"{{metricID}}", mID,
)
if aux = tt.FindByKey(mpl.Replace(LocaleKeyChartMetricsMetricIDLabel.Path)); aux != nil {
c.Config.Reports[i].Metrics[j]["label"] = aux.Msg
}
}
}
}
}
func (c Chart) encodeTranslations() (out locale.ResourceTranslationSet) {
out = make(locale.ResourceTranslationSet, 0, 12)
for _, report := range c.Config.Reports {
if mLabel, ok := report.YAxis["label"]; ok {
ml, is := mLabel.(string)
if !is {
continue
}
out = append(out, &locale.ResourceTranslation{
Resource: c.ResourceTranslation(),
Key: LocaleKeyChartYAxisLabel.Path,
Msg: ml,
})
}
for _, metric := range report.Metrics {
if metricID, ok := metric["metricID"]; ok {
mID, is := metricID.(string)
if !is {
continue
}
mpl := strings.NewReplacer(
"{{metricID}}", mID,
)
if mLabel, ok := metric["label"]; ok {
ml, is := mLabel.(string)
if !is {
continue
}
out = append(out, &locale.ResourceTranslation{
Resource: c.ResourceTranslation(),
Key: mpl.Replace(LocaleKeyChartMetricsMetricIDLabel.Path),
Msg: ml,
})
}
}
}
}
return
}
// FindByHandle finds chart by it's handle
func (set ChartSet) FindByHandle(handle string) *Chart {
for i := range set {

View File

@@ -23,6 +23,7 @@ type (
// Types and stuff
const (
ChartResourceTranslationType = "compose:chart"
ModuleResourceTranslationType = "compose:module"
ModuleFieldResourceTranslationType = "compose:module-field"
NamespaceResourceTranslationType = "compose:namespace"
@@ -31,6 +32,8 @@ const (
var (
// @todo can we remove LocaleKey struct for string constant?
LocaleKeyChartYAxisLabel = LocaleKey{Path: "yAxis.label"}
LocaleKeyChartMetricsMetricIDLabel = LocaleKey{Path: "metrics.{{metricID}}.label"}
LocaleKeyModuleName = LocaleKey{Path: "name"}
LocaleKeyModuleFieldLabel = LocaleKey{Path: "label"}
LocaleKeyModuleFieldMetaDescriptionView = LocaleKey{Path: "meta.description.view"}
@@ -57,6 +60,47 @@ var (
LocaleKeyPagePageBlockBlockIDContentBody = LocaleKey{Path: "pageBlock.{{blockID}}.content.body"}
)
// ResourceTranslation returns string representation of Locale resource for Chart by calling ChartResourceTranslation fn
//
// Locale resource is in "compose:chart/..." format
//
// This function is auto-generated
func (r Chart) ResourceTranslation() string {
return ChartResourceTranslation(r.NamespaceID, r.ID)
}
// ChartResourceTranslation returns string representation of Locale resource for Chart
//
// Locale resource is in the compose:chart/... format
//
// This function is auto-generated
func ChartResourceTranslation(NamespaceID uint64, ID uint64) string {
cpts := []interface{}{
ChartResourceTranslationType,
strconv.FormatUint(NamespaceID, 10),
strconv.FormatUint(ID, 10),
}
return fmt.Sprintf(ChartResourceTranslationTpl(), cpts...)
}
func ChartResourceTranslationTpl() string {
return "%s/%s/%s"
}
func (r *Chart) DecodeTranslations(tt locale.ResourceTranslationIndex) {
r.decodeTranslations(tt)
}
func (r *Chart) EncodeTranslations() (out locale.ResourceTranslationSet) {
out = locale.ResourceTranslationSet{}
out = append(out, r.encodeTranslations()...)
return out
}
// ResourceTranslation returns string representation of Locale resource for Module by calling ModuleResourceTranslation fn
//
// Locale resource is in "compose:module/..." format

View File

@@ -65,21 +65,25 @@ func (r *ComposeChart) SysID() uint64 {
return r.Res.ID
}
func (r *ComposeChart) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
func (r *ComposeChart) resourceParts(tpl string) (resource string, ref *Ref, path []*Ref) {
ref = r.Ref().Constraint(r.RefNs)
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.ChartRbacResourceTpl(), types.ChartResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// func (r *ComposeChart) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
// ref = r.Ref()
// path = []*Ref{r.RefNs}
// resource = fmt.Sprintf(types.ChartResourceTranslationTpl(), types.ChartResourceTranslationType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
func (r *ComposeChart) RBACParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ChartRbacResourceTpl())
}
// return
// }
func (r *ComposeChart) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
return r.resourceParts(types.ChartResourceTranslationTpl())
}
func (r *ComposeChart) encodeTranslations() ([]*ResourceTranslation, error) {
return nil, nil
}
// FindComposeChart looks for the chart in the resources
func FindComposeChart(rr InterfaceSet, ii Identifiers) (ch *types.Chart) {

View File

@@ -10,6 +10,18 @@ import (
systemTypes "github.com/cortezaproject/corteza-server/system/types"
)
func (r *ComposeChart) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)
rr := r.Res.EncodeTranslations()
rr.SetLanguage(defaultLanguage)
res, ref, pp := r.ResourceTranslationParts()
out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...))
tmp, err := r.encodeTranslations()
return append(out, tmp...), err
}
func (r *ComposeModule) EncodeTranslations() ([]*ResourceTranslation, error) {
out := make([]*ResourceTranslation, 0, 10)

View File

@@ -47,6 +47,16 @@ func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) {
// make the resource provide the slice of parent resources we should nest under
switch resourceType {
case composeTypes.ChartResourceTranslationType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))
}
ref, pp, err := ComposeChartResourceTranslationReferences(
path[0],
path[1],
)
return composeTypes.ChartResourceTranslationType, ref, pp, err
case composeTypes.ModuleResourceTranslationType:
if len(path) != 2 {
return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path))

View File

@@ -10,6 +10,16 @@ import (
"github.com/cortezaproject/corteza-server/compose/types"
)
// ComposeChartResourceTranslationReferences generates Locale references
//
// This function is auto-generated
func ComposeChartResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) {
res = &Ref{ResourceType: types.ChartResourceType, Identifiers: MakeIdentifiers(self)}
pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)})
return
}
// ComposeModuleResourceTranslationReferences generates Locale references
//
// This function is auto-generated

View File

@@ -172,20 +172,20 @@ func (n *resourceTranslation) makeResourceTranslation(pl *payload) (string, erro
return composeTypes.PageResourceTranslation(p0ID, p1ID), nil
// case composeTypes.ChartResourceType:
// p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
// if p0 == nil {
// return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
// }
// p0ID = p0.ID
case composeTypes.ChartResourceType:
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
// p1 := resource.FindComposeChart(pl.state.ParentResources, n.refLocaleRes.Identifiers)
// if p1 == nil {
// return "", resource.ComposeChartErrUnresolved(n.refLocaleRes.Identifiers)
// }
// p1ID = p1.ID
p1 := resource.FindComposeChart(pl.state.ParentResources, n.refLocaleRes.Identifiers)
if p1 == nil {
return "", resource.ComposeChartErrUnresolved(n.refLocaleRes.Identifiers)
}
p1ID = p1.ID
// return composeTypes.ChartResourceTranslation(p0ID, p1ID), nil
return composeTypes.ChartResourceTranslation(p0ID, p1ID), nil
case composeTypes.ModuleFieldResourceType:
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)

View File

@@ -162,24 +162,24 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
return fmt.Sprintf(composeTypes.ModuleFieldResourceTranslationTpl(), composeTypes.ModuleFieldResourceTranslationType, p0ID, p1ID, p2ID), nil
// case composeTypes.ChartResourceType:
// if len(res.RefPath) > 0 {
// p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
// if p0 == nil {
// return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
// }
// p0ID = p0.Slug
// }
case composeTypes.ChartResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
// if res.RefRes != nil {
// p1 := resource.FindComposeChart(state.ParentResources, res.RefRes.Identifiers)
// if p1 == nil {
// return "", resource.ComposeChartErrUnresolved(res.RefRes.Identifiers)
// }
// p1ID = p1.Handle
// }
if res.RefRes != nil {
p1 := resource.FindComposeChart(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return "", resource.ComposeChartErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
// return fmt.Sprintf(composeTypes.ChartResourceTranslationTpl(), composeTypes.ChartResourceTranslationType, p0ID, p1ID), nil
return fmt.Sprintf(composeTypes.ChartResourceTranslationTpl(), composeTypes.ChartResourceTranslationType, p0ID, p1ID), nil
// case automationTypes.WorkflowResourceType:
// if res.RefRes != nil {

View File

@@ -0,0 +1,116 @@
package provision
import (
"context"
"fmt"
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/store"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"go.uber.org/zap"
"strings"
)
// Migrates resource translations from the resource
// struct to the dedicated store (table)
//
// While doing this, we also modify some resource substructure:
// - chart reports (assign report 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 migratePost202203ResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) (err error) {
log.Info("migrating post 202203 resource translations")
var (
migrated = make(map[string]bool)
)
set, _, err := store.SearchResourceTranslations(ctx, s, sysTypes.ResourceTranslationFilter{})
if err != nil {
return
}
err = 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 err != nil {
return
}
if !migrated[cmpTypes.ChartResourceTranslationType] {
if err = migrateComposeChartResourceTranslations(ctx, log, s); err != nil {
return
}
}
return
}
// migrate resource translations for compose chart
func migrateComposeChartResourceTranslations(ctx context.Context, log *zap.Logger, s store.Storer) error {
var (
tt sysTypes.ResourceTranslationSet
)
set, _, err := store.SearchComposeCharts(ctx, s, cmpTypes.ChartFilter{Deleted: filter.StateInclusive})
if err != nil {
return err
}
log.Info("migrating compose charts", zap.Int("count", len(set)))
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
return set.Walk(func(res *cmpTypes.Chart) (err error) {
if tt, err = convertComposeChartReportsTranslations(res); err != nil {
return err
}
if err = store.CreateResourceTranslation(ctx, s, tt...); err != nil {
return
}
if err = store.UpdateComposeChart(ctx, s, res); err != nil {
return
}
return
})
})
}
// collects translations for compose chart blocks and alters them (adding block ID)
func convertComposeChartReportsTranslations(res *cmpTypes.Chart) (sysTypes.ResourceTranslationSet, error) {
var tt sysTypes.ResourceTranslationSet
for i, r := range res.Config.Reports {
reportID := id.Next()
res.Config.Reports[i].ReportID = reportID
if _, ok := r.YAxis["label"]; ok {
tt = append(tt,
makeResourceTranslation(res, "yAxis.label", r.YAxis["label"].(string)),
)
}
// Ensure chart report metric IDs
for j, m := range r.Metrics {
metricID := id.Next()
res.Config.Reports[i].Metrics[j]["metricID"] = fmt.Sprintf("%d", metricID)
cmx := fmt.Sprintf("metrics.%d.", metricID)
if _, ok := m["label"]; ok {
tt = append(tt,
makeResourceTranslation(res, cmx+"label", m["label"].(string)),
)
}
}
}
return tt, nil
}

View File

@@ -41,6 +41,9 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti
func() error { return cleanupPre202109Settings(ctx, log.Named("pre-202109-settings"), s) },
func() error { return migrateResourceTranslations(ctx, log.Named("resource-translations"), s) },
func() error { return migrateReportIdentifiers(ctx, log.Named("report-identifiers"), s) },
func() error {
return migratePost202203ResourceTranslations(ctx, log.Named("post-202203-resource-translations"), s)
},
// Config (full & partial)
func() error { return importConfig(ctx, log.Named("config"), s, provisionOpt.Path) },