diff --git a/pkg/provision/migrations_202203_report_identifiers.go b/pkg/provision/migrations_202203_report_identifiers.go new file mode 100644 index 000000000..32d6a99bd --- /dev/null +++ b/pkg/provision/migrations_202203_report_identifiers.go @@ -0,0 +1,68 @@ +package provision + +import ( + "context" + "strconv" + + "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" + "github.com/spf13/cast" + "go.uber.org/zap" +) + +// Migrates existing reports to include the newly added resource identifiers +func migrateReportIdentifiers(ctx context.Context, log *zap.Logger, s store.Storer) (err error) { + log.Info("migrating report identifiers") + + set, _, err := store.SearchReports(ctx, s, sysTypes.ReportFilter{Deleted: filter.StateInclusive}) + if err != nil { + return err + } + + return s.Tx(ctx, func(ctx context.Context, s store.Storer) error { + return set.Walk(func(r *sysTypes.Report) (err error) { + r = setIDs(r) + return store.UpdateReport(ctx, s, r) + }) + }) +} + +// duplicate from the report service +func setIDs(r *sysTypes.Report) *sysTypes.Report { + // scenarios + for _, s := range r.Scenarios { + if s.ScenarioID == 0 { + s.ScenarioID = id.Next() + } + } + + // blocks + for _, b := range r.Blocks { + if b.BlockID == 0 { + b.BlockID = id.Next() + } + + // elements + for _, elRaw := range b.Elements { + el, ok := elRaw.(map[string]interface{}) + if !ok { + continue + } + + elID, ok := el["elementID"] + sElID := cast.ToString(elID) + if sElID != "" && sElID != "0" { + continue + } + if cast.ToUint64(elID) != 0 { + continue + } + + el["elementID"] = strconv.FormatUint(id.Next(), 10) + } + } + + return r +} diff --git a/pkg/provision/provision.go b/pkg/provision/provision.go index 797e65cda..da2a66023 100644 --- a/pkg/provision/provision.go +++ b/pkg/provision/provision.go @@ -32,6 +32,7 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti 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) }, + func() error { return migrateReportIdentifiers(ctx, log.Named("report-identifiers"), s) }, // Config (full & partial) func() error { return importConfig(ctx, log.Named("config"), s, provisionOpt.Path) }, diff --git a/system/service/report.go b/system/service/report.go index e1959505b..c810b8c45 100644 --- a/system/service/report.go +++ b/system/service/report.go @@ -3,12 +3,14 @@ package service import ( "context" "fmt" + "strconv" "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/label" rep "github.com/cortezaproject/corteza-server/pkg/report" "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/system/types" + "github.com/spf13/cast" ) type ( @@ -140,6 +142,8 @@ func (svc *report) Create(ctx context.Context, new *types.Report) (report *types new.Meta = &types.ReportMeta{} } + new = svc.setIDs(new) + if err = store.CreateReport(ctx, svc.store, new); err != nil { return } @@ -193,6 +197,8 @@ func (svc *report) Update(ctx context.Context, upd *types.Report) (report *types report.Meta = upd.Meta } + report = svc.setIDs(report) + if err = store.UpdateReport(ctx, svc.store, report); err != nil { return err } @@ -426,6 +432,43 @@ func (svc *report) Run(ctx context.Context, reportID uint64, dd rep.FrameDefinit return out, svc.recordAction(ctx, aaProps, ReportActionRun, err) } +func (svc *report) setIDs(r *types.Report) *types.Report { + // scenarios + for _, s := range r.Scenarios { + if s.ScenarioID == 0 { + s.ScenarioID = nextID() + } + } + + // blocks + for _, b := range r.Blocks { + if b.BlockID == 0 { + b.BlockID = nextID() + } + + // elements + for _, elRaw := range b.Elements { + el, ok := elRaw.(map[string]interface{}) + if !ok { + continue + } + + elID, ok := el["elementID"] + sElID := cast.ToString(elID) + if sElID != "" && sElID != "0" { + continue + } + if cast.ToUint64(elID) != 0 { + continue + } + + el["elementID"] = strconv.FormatUint(nextID(), 10) + } + } + + return r +} + // toLabeledReports converts to []label.LabeledResource // // This function is auto-generated. diff --git a/system/types/report.go b/system/types/report.go index b71727573..ad464c134 100644 --- a/system/types/report.go +++ b/system/types/report.go @@ -8,6 +8,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/filter" "github.com/cortezaproject/corteza-server/pkg/report" + "github.com/spf13/cast" ) type ( @@ -35,9 +36,9 @@ type ( ReportScenarioSet []*ReportScenario ScenarioFilterMap map[string]*report.Filter ReportScenario struct { - // ScenarioID uint64 `json:"scenarioID,string,omitempty"` - Label string `json:"label"` - Filters ScenarioFilterMap `json:"filters,omitempty"` + ScenarioID uint64 `json:"scenarioID,string,omitempty"` + Label string `json:"label"` + Filters ScenarioFilterMap `json:"filters,omitempty"` } ReportDataSource struct { @@ -52,7 +53,7 @@ type ( } ReportBlock struct { - BlockID uint64 `json:"blockID"` + BlockID uint64 `json:"blockID,string"` Title string `json:"title"` Description string `json:"description"` Key string `json:"key"` @@ -87,6 +88,30 @@ type ( } ) +// Initial ReportBlock struct definition omitted string casting for the BlockID (sorry) +// so we need to handle that edge case when reading from DB. +func (b *ReportBlock) UnmarshalJSON(data []byte) (err error) { + type internalReportBlock ReportBlock + i := struct { + internalReportBlock + BlockID interface{} `json:"blockID"` + }{} + + if err = json.Unmarshal(data, &i); err != nil { + return + } + + bID, err := cast.ToUint64E(i.BlockID) + if err != nil { + return + } + + *b = ReportBlock(i.internalReportBlock) + b.BlockID = bID + + return nil +} + func (ss ReportDataSourceSet) ModelSteps() report.StepDefinitionSet { out := make(report.StepDefinitionSet, 0, 124)