Skip to content

Commit

Permalink
UI-9384 - Preserve KPI titles when migrated KPI widgets from 4.3 to 5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Nouzbe committed May 3, 2024
1 parent 0f2732f commit 61ad7e1
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 5 deletions.
11 changes: 8 additions & 3 deletions src/4.3_to_5.0/__test_resources__/legacyComparisonValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ export const legacyComparisonValues = {
showTitleBar: true,
body: {
serverUrl: "http://localhost:9090",
mdx:
"SELECT NON EMPTY Hierarchize(DrilldownLevel([Currency].[Currency].[ALL].[AllMember])) ON ROWS, NON EMPTY [Measures].[pnl.FOREX] ON COLUMNS, {[Booking].[Desk].[ALL].[AllMember].[LegalEntityA], [Booking].[Desk].[ALL].[AllMember].[LegalEntityB]} ON PAGES FROM [EquityDerivativesCube] CELL PROPERTIES VALUE, FORMATTED_VALUE, BACK_COLOR, FORE_COLOR, FONT_FLAGS",
mdx: "SELECT NON EMPTY Hierarchize(DrilldownLevel([Currency].[Currency].[ALL].[AllMember])) ON ROWS, NON EMPTY [Measures].[pnl.FOREX] ON COLUMNS, {[Booking].[Desk].[ALL].[AllMember].[LegalEntityA], [Booking].[Desk].[ALL].[AllMember].[LegalEntityB]} ON PAGES FROM [EquityDerivativesCube] CELL PROPERTIES VALUE, FORMATTED_VALUE, BACK_COLOR, FORE_COLOR, FONT_FLAGS",
contextValues: {},
updateMode: "once",
ranges: {
Expand All @@ -24,7 +23,13 @@ export const legacyComparisonValues = {
},
},
configuration: {
featuredValues: {},
featuredValues: {
locations: {
"[Currency].[Currency].[AllMember].[GBP],[Measures].[pnl.FOREX]": {
title: "Hello World",
},
},
},
},
},
containerKey: "featured-values",
Expand Down
10 changes: 9 additions & 1 deletion src/4.3_to_5.0/__test_resources__/legacyKpi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ export const legacyKpi: LegacyWidgetState = {
},
},
configuration: {
featuredValues: {},
featuredValues: {
locations: {
// "EUR, USD" is not a member of the sandbox, but it is used here to check that the script supports member names including ",".
"[Currency].[Currency].[AllMember].[EUR, USD],[Measures].[contributors.COUNT]":
{
title: "Hello World",
},
},
},
},
},
containerKey: "featured-values",
Expand Down
30 changes: 30 additions & 0 deletions src/4.3_to_5.0/getMigratedKpiTitles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { dataModelsForTests } from "@activeviam/data-model-5.0";
import { getMigratedKpiTitles } from "./getMigratedKpiTitles";
import { legacyKpi } from "./__test_resources__/legacyKpi";

const cube = dataModelsForTests.sandbox.catalogs[0].cubes[0];

describe("getMigratedKpiTitles", () => {
it("returns the migrated KPI titles corresponding to the legacy KPI state, ready to be used in Atoti UI 5.0", () => {
const migratedKpiTitles = getMigratedKpiTitles(legacyKpi, {
cube,
mapping: {
columns: [{ type: "allMeasures" }],
measures: [{ type: "measure", measureName: "contributors.COUNT" }],
rows: [
{
type: "hierarchy",
dimensionName: "Currency",
hierarchyName: "Currency",
levelName: "Currency",
},
],
},
});
expect(migratedKpiTitles).toMatchInlineSnapshot(`
{
"[Measures].[contributors.COUNT],[Currency].[Currency].[AllMember].[EUR, USD]": "Hello World",
}
`);
});
});
101 changes: 101 additions & 0 deletions src/4.3_to_5.0/getMigratedKpiTitles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
Cube,
DataVisualizationWidgetMapping,
KpiWidgetState,
MdxUnknownCompoundIdentifier,
parse,
} from "@activeviam/activeui-sdk-5.0";
import { getSpecificCompoundIdentifier, quote } from "@activeviam/mdx-5.0";

interface LegacyKpiTitle {
title: string;
tuple: { [hierarchyUniqueName: string]: string[] };
}

/**
* Returns the legacy KPI titles attached to `legacyKpiState`.
*/
function _getLegacyKpiTitles(
// Legacy widget states are not typed.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
legacyKpiState: any,
{ cube }: { cube: Cube },
): LegacyKpiTitle[] {
const locations =
legacyKpiState?.value?.body?.configuration?.featuredValues?.locations;

if (!locations) {
return [];
}

const legacyTitles: LegacyKpiTitle[] = [];

for (const tupleKey in locations) {
const { title } = locations[tupleKey];
const tuple: {
[hierarchyUniqueName: string]: string[];
} = {};
const memberUniqueNames = tupleKey.split(/,(?![^\[]*\])/);
for (const memberUniqueName of memberUniqueNames) {
const compoundIdentifier =
parse<MdxUnknownCompoundIdentifier>(memberUniqueName);
const specificCompoundIdentifier = getSpecificCompoundIdentifier(
compoundIdentifier,
{ cube },
);
if (specificCompoundIdentifier.type === "measure") {
tuple[`[Measures].[Measures]`] = [
specificCompoundIdentifier.measureName,
];
} else if (specificCompoundIdentifier.type === "member") {
tuple[
`[${specificCompoundIdentifier.dimensionName}].[${specificCompoundIdentifier.hierarchyName}]`
] = specificCompoundIdentifier.path;
}
}
legacyTitles.push({ title, tuple });
}

return legacyTitles;
}

/**
* Returns the migrated KPI widget titles corresponding to legacyKpiState.
*/
export function getMigratedKpiTitles(
// Legacy widget states are not typed.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
legacyKpiState: any,
{ mapping, cube }: { mapping: DataVisualizationWidgetMapping; cube: Cube },
): KpiWidgetState["titles"] {
const legacyTitles = _getLegacyKpiTitles(legacyKpiState, { cube });
const migratedTitles: KpiWidgetState["titles"] = {};

const memberUniqueNames: string[] = [];
legacyTitles.forEach(({ title, tuple }) => {
// Atoti UI 5.0 KPI widgets expect tuple keys to express members in the following order:
// - column fields first
// - then row fields
const fields = [...(mapping.columns ?? []), ...(mapping.rows ?? [])];
fields.forEach((field) => {
if (field.type === "allMeasures") {
const measureName = tuple[`[Measures].[Measures]`]?.[0];
if (measureName) {
memberUniqueNames.push(`[Measures].[${measureName}]`);
}
} else if (field.type === "hierarchy") {
const hierarchyUniqueName = `[${field.dimensionName}].[${field.hierarchyName}]`;
const namePath = tuple[hierarchyUniqueName];
if (namePath) {
memberUniqueNames.push(
`${hierarchyUniqueName}.${quote(...namePath)}`,
);
}
}
});
const tupleKey = memberUniqueNames.join(",");
migratedTitles[tupleKey] = title;
});

return migratedTitles;
}
6 changes: 6 additions & 0 deletions src/4.3_to_5.0/migrateKpi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ describe("migrateKpi", () => {
},
],
"serverKey": "my-server",
"titles": {
"[Measures].[contributors.COUNT],[Currency].[Currency].[AllMember].[EUR, USD]": "Hello World",
},
"widgetKey": "kpi",
}
`);
Expand Down Expand Up @@ -81,6 +84,9 @@ describe("migrateKpi", () => {
},
"queryContext": [],
"serverKey": "my-server",
"titles": {
"[Measures].[pnl.FOREX],[Currency].[Currency].[AllMember].[GBP]": "Hello World",
},
"widgetKey": "kpi",
}
`);
Expand Down
32 changes: 31 additions & 1 deletion src/4.3_to_5.0/migrateKpi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { _getQueryInLegacyWidgetState } from "./_getQueryInLegacyWidgetState";
import { _getTargetCubeFromServerUrl } from "./_getTargetCubeFromServerUrl";
import { _migrateQuery } from "./_migrateQuery";
import { produce } from "immer";
import { getMigratedKpiTitles } from "./getMigratedKpiTitles";

const moveExpressionToWithClause = (
draft: any,
Expand Down Expand Up @@ -189,7 +190,13 @@ export function migrateKpi(
const mapping = deriveMappingFromMdx({
mdx: query.mdx,
cube,
widgetPlugin: pluginWidgetKpi,
widgetPlugin: {
...pluginWidgetKpi,
// This makes the "allMeasures" tile part of the generated mapping.
// This tile indicates the position of the measures on the queries axes, necessary to migrate KPI titles.
// It is then removed from the mapping, as Atoti UI 5.0 does not expect it.
doesSupportMeasuresRedirection: true,
},
});

const migratedWidgetState: KpiWidgetState = {
Expand All @@ -206,6 +213,29 @@ export function migrateKpi(
}),
};

try {
const migratedTitles = getMigratedKpiTitles(legacyKpiState, {
cube,
mapping,
});
if (migratedTitles && Object.keys(migratedTitles).length > 0) {
migratedWidgetState.titles = migratedTitles;
}
} catch (error) {
// Migrating the KPI titles is a best effort.
// The migration script should not fail if this part errors.
console.warn(
`Could not migrate the titles of the featured values widget named "${legacyKpiState.name}". Underlying error:\n`,
error,
);
}

Object.keys(mapping).forEach((attributeName) => {
mapping[attributeName] = mapping[attributeName].filter(
(field) => field.type !== "allMeasures",
);
});

const serializedWidgetState = serializeWidgetState(migratedWidgetState);

if (isUsingUnsupportedUpdateMode) {
Expand Down

0 comments on commit 61ad7e1

Please sign in to comment.