@genesislcap/pbc-reporting-ui 14.396.3 → 14.397.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dts/new/main/edit-config/col-filters/col-filters-grid.helpers.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/datasource-config/datasource-config-item.d.ts +6 -2
- package/dist/dts/new/main/edit-config/datasource-config/datasource-config-item.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/datasource-config/datasource-config-item.styles.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/datasource-config/datasource-config-item.template.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/datasource-config/datasources-config-container.helpers.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/datasource-config/datasources-config-container.template.d.ts.map +1 -1
- package/dist/dts/new/main/edit-config/shared/datasource-data-base-component.helpers.ts.d.ts.map +1 -1
- package/dist/dts/new/store/slices/datasources-config.d.ts +20 -6
- package/dist/dts/new/store/slices/datasources-config.d.ts.map +1 -1
- package/dist/dts/new/store/slices/types.d.ts +19 -2
- package/dist/dts/new/store/slices/types.d.ts.map +1 -1
- package/dist/dts/new/store/store.d.ts +133 -35
- package/dist/dts/new/store/store.d.ts.map +1 -1
- package/dist/dts/new/types/misc.d.ts +4 -2
- package/dist/dts/new/types/misc.d.ts.map +1 -1
- package/dist/dts/new/utils/alias-generator.d.ts +8 -0
- package/dist/dts/new/utils/alias-generator.d.ts.map +1 -0
- package/dist/dts/new/utils/alias-generator.test.d.ts +2 -0
- package/dist/dts/new/utils/alias-generator.test.d.ts.map +1 -0
- package/dist/dts/new/utils/index.d.ts +1 -0
- package/dist/dts/new/utils/index.d.ts.map +1 -1
- package/dist/dts/new/utils/tooltip.d.ts +3 -0
- package/dist/dts/new/utils/tooltip.d.ts.map +1 -1
- package/dist/dts/new/utils/transformers.d.ts +1 -1
- package/dist/dts/new/utils/transformers.d.ts.map +1 -1
- package/dist/esm/new/main/edit-config/col-filters/col-filters-grid.helpers.js +3 -1
- package/dist/esm/new/main/edit-config/col-filters/col-filters-grid.helpers.test.js +71 -6
- package/dist/esm/new/main/edit-config/col-rename-alias/col-rename-alias-grid.helpers.test.js +32 -4
- package/dist/esm/new/main/edit-config/data-transforms-derived-fields/data-transforms.helpers.test.js +10 -2
- package/dist/esm/new/main/edit-config/datasource-config/datasource-config-item.js +100 -10
- package/dist/esm/new/main/edit-config/datasource-config/datasource-config-item.styles.js +6 -0
- package/dist/esm/new/main/edit-config/datasource-config/datasource-config-item.template.js +38 -3
- package/dist/esm/new/main/edit-config/datasource-config/datasources-config-container.helpers.js +11 -7
- package/dist/esm/new/main/edit-config/datasource-config/datasources-config-container.helpers.test.js +29 -22
- package/dist/esm/new/main/edit-config/datasource-config/datasources-config-container.template.js +3 -0
- package/dist/esm/new/main/edit-config/shared/datasource-data-base-component.helpers.ts.js +16 -8
- package/dist/esm/new/main/edit-config/shared/datasource-data-base-component.test.js +13 -2
- package/dist/esm/new/main/edit-config/tabbed-datasource-container/tabbed-datasource-container.template.js +1 -1
- package/dist/esm/new/store/slices/datasources-config.js +50 -11
- package/dist/esm/new/store/slices/types.js +1 -0
- package/dist/esm/new/utils/alias-generator.js +16 -0
- package/dist/esm/new/utils/alias-generator.test.js +36 -0
- package/dist/esm/new/utils/index.js +1 -0
- package/dist/esm/new/utils/tooltip.js +16 -0
- package/dist/esm/new/utils/transformers.js +20 -6
- package/dist/esm/new/utils/transformers.test.js +61 -11
- package/dist/esm/new/utils/validators.test.js +35 -21
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/new/main/edit-config/col-filters/col-filters-grid.helpers.test.ts +76 -6
- package/src/new/main/edit-config/col-filters/col-filters-grid.helpers.ts +4 -1
- package/src/new/main/edit-config/col-rename-alias/col-rename-alias-grid.helpers.test.ts +32 -4
- package/src/new/main/edit-config/data-transforms-derived-fields/data-transforms.helpers.test.ts +10 -2
- package/src/new/main/edit-config/datasource-config/datasource-config-item.styles.ts +6 -0
- package/src/new/main/edit-config/datasource-config/datasource-config-item.template.ts +62 -3
- package/src/new/main/edit-config/datasource-config/datasource-config-item.ts +107 -8
- package/src/new/main/edit-config/datasource-config/datasources-config-container.helpers.test.ts +32 -23
- package/src/new/main/edit-config/datasource-config/datasources-config-container.helpers.ts +18 -10
- package/src/new/main/edit-config/datasource-config/datasources-config-container.template.ts +6 -0
- package/src/new/main/edit-config/shared/datasource-data-base-component.helpers.ts.ts +21 -11
- package/src/new/main/edit-config/shared/datasource-data-base-component.test.ts +14 -2
- package/src/new/main/edit-config/tabbed-datasource-container/tabbed-datasource-container.template.ts +1 -1
- package/src/new/store/slices/datasources-config.ts +71 -16
- package/src/new/store/slices/types.ts +22 -4
- package/src/new/types/misc.ts +9 -2
- package/src/new/utils/alias-generator.test.ts +44 -0
- package/src/new/utils/alias-generator.ts +18 -0
- package/src/new/utils/index.ts +1 -0
- package/src/new/utils/tooltip.ts +19 -0
- package/src/new/utils/transformers.test.ts +73 -11
- package/src/new/utils/transformers.ts +30 -6
- package/src/new/utils/validators.test.ts +35 -21
|
@@ -127,9 +127,22 @@ export function datasourceNameFromDisplay(input: string): DatasourceName {
|
|
|
127
127
|
.replace(/[)]/g, '')
|
|
128
128
|
.split('(')
|
|
129
129
|
.map((x) => x.trim());
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
const type = datasourceInputFromDisplay(parts[1] as Display.DatasourceInputTypes);
|
|
131
|
+
const name = parts[0];
|
|
132
|
+
|
|
133
|
+
const match = Object.entries(selectors.datasourceConfig.getAllConfigSet()).find(
|
|
134
|
+
([_, ds]) => ds.KEY === name && ds.INPUT_TYPE === type,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (!match) {
|
|
138
|
+
/**
|
|
139
|
+
* This can occur when the datasource is first being initialized and the name
|
|
140
|
+
* hasn't yet been persisted to the store.
|
|
141
|
+
*/
|
|
142
|
+
return `${type}_${name}_${name}` as DatasourceName;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return match[0] as DatasourceName;
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
/**
|
|
@@ -198,14 +211,11 @@ export async function fetchDatasourceSpecs(
|
|
|
198
211
|
|
|
199
212
|
try {
|
|
200
213
|
return await Promise.all(
|
|
201
|
-
Object.
|
|
202
|
-
.filter(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.map(async ({ NAME }) => ({
|
|
207
|
-
spec: await getSchema(NAME),
|
|
208
|
-
name: datasourceNameForDisplay(NAME, 'REQ_REP'),
|
|
214
|
+
Object.entries(selectors.datasourceConfig.getAllConfigSet())
|
|
215
|
+
.filter(([key, ds]) => ds.INPUT_TYPE === 'REQ_REP' && key === datasourceName)
|
|
216
|
+
.map(async ([_, { KEY, NAME }]) => ({
|
|
217
|
+
spec: await getSchema(NAME), // Fetch schema by Resource Name
|
|
218
|
+
name: datasourceNameForDisplay(KEY, 'REQ_REP'), // Display Alias
|
|
209
219
|
})),
|
|
210
220
|
);
|
|
211
221
|
} catch (e) {
|
|
@@ -42,11 +42,15 @@ export type SelectRendererParams = Parameters<SelectRenderer['init']>[typeof FIR
|
|
|
42
42
|
// DatasourceNameFromDisplay tests
|
|
43
43
|
const DatasourceNameFromDisplayTests = suite('datasourceNameFromDisplay()');
|
|
44
44
|
|
|
45
|
+
DatasourceNameFromDisplayTests.before.each(() => {
|
|
46
|
+
sinon.restore();
|
|
47
|
+
});
|
|
48
|
+
|
|
45
49
|
DatasourceNameFromDisplayTests(
|
|
46
50
|
'Correctly builds datasource key from Snapshot (REQ_REP) input',
|
|
47
51
|
() => {
|
|
48
52
|
const input = 'myService (Snapshot)';
|
|
49
|
-
const expected = '
|
|
53
|
+
const expected = 'REQ_REP_myService_myService';
|
|
50
54
|
assert.equal(datasourceNameFromDisplay(input), expected);
|
|
51
55
|
},
|
|
52
56
|
);
|
|
@@ -55,7 +59,7 @@ DatasourceNameFromDisplayTests(
|
|
|
55
59
|
'Correctly builds datasource key from Data Pipeline (DATA_PIPELINE) input',
|
|
56
60
|
() => {
|
|
57
61
|
const input = 'dataFlow (Data Pipeline)';
|
|
58
|
-
const expected = '
|
|
62
|
+
const expected = 'DATA_PIPELINE_dataFlow_dataFlow';
|
|
59
63
|
assert.equal(datasourceNameFromDisplay(input), expected);
|
|
60
64
|
},
|
|
61
65
|
);
|
|
@@ -101,6 +105,8 @@ LookupColumnIsIncluded('returns true when column is in included', () => {
|
|
|
101
105
|
NAME: 'Test Datasource',
|
|
102
106
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
103
107
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
108
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
109
|
+
GROUP_KEY: null,
|
|
104
110
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: ['testColumn', 'otherColumn'] },
|
|
105
111
|
};
|
|
106
112
|
|
|
@@ -122,6 +128,8 @@ LookupColumnIsIncluded('returns false when column is not in filters', () => {
|
|
|
122
128
|
NAME: 'Test Datasource',
|
|
123
129
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
124
130
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
131
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
132
|
+
GROUP_KEY: null,
|
|
125
133
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: ['testColumn', 'otherColumn'] },
|
|
126
134
|
};
|
|
127
135
|
|
|
@@ -143,6 +151,8 @@ LookupColumnIsIncluded('returns false when INCLUDE_COLUMNS is undefined', () =>
|
|
|
143
151
|
NAME: 'Test Datasource',
|
|
144
152
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
145
153
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
154
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
155
|
+
GROUP_KEY: null,
|
|
146
156
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: undefined },
|
|
147
157
|
};
|
|
148
158
|
|
|
@@ -164,6 +174,8 @@ LookupColumnIsIncluded('returns false when INCLUDE_COLUMNS is empty array', () =
|
|
|
164
174
|
NAME: 'Test Datasource',
|
|
165
175
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
166
176
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
177
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
178
|
+
GROUP_KEY: null,
|
|
167
179
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: [] },
|
|
168
180
|
};
|
|
169
181
|
|
package/src/new/main/edit-config/tabbed-datasource-container/tabbed-datasource-container.template.ts
CHANGED
|
@@ -11,7 +11,7 @@ export const template = html<TabbedDatasourceContainer>`
|
|
|
11
11
|
(_) => Object.entries(selectors.datasourceConfig.getAllConfigSet()),
|
|
12
12
|
html<[DatasourceName, DatasourceConfig[DatasourceName]], TabbedDatasourceContainer>`
|
|
13
13
|
<rapid-tab @click=${(x, c) => c.parent.handleDatasourceChanged(x[0])}>
|
|
14
|
-
${(x) => x[1].
|
|
14
|
+
${(x) => x[1].KEY}
|
|
15
15
|
</rapid-tab>
|
|
16
16
|
`,
|
|
17
17
|
)}
|
|
@@ -6,6 +6,7 @@ import { buildDatasourceName } from '../../utils';
|
|
|
6
6
|
import { getDefaultFormat } from '../../utils/format-utils';
|
|
7
7
|
import {
|
|
8
8
|
DatasourceConfig,
|
|
9
|
+
DatasourceGrouping,
|
|
9
10
|
DatasourceName,
|
|
10
11
|
DatasourceOutputTypes,
|
|
11
12
|
deselectedColumns,
|
|
@@ -21,18 +22,20 @@ export const datasourceSlice = createSlice({
|
|
|
21
22
|
initDatasourceConfiguration(
|
|
22
23
|
state: DatasourceConfig,
|
|
23
24
|
action: PayloadAction<{
|
|
24
|
-
base: Pick<DatasourceConfig[DatasourceName], 'KEY' | 'INPUT_TYPE'>;
|
|
25
|
+
base: Pick<DatasourceConfig[DatasourceName], 'KEY' | 'NAME' | 'INPUT_TYPE'>;
|
|
25
26
|
fields: string[];
|
|
26
27
|
}>,
|
|
27
28
|
) {
|
|
28
|
-
const { KEY, INPUT_TYPE } = action.payload.base;
|
|
29
|
-
const key: DatasourceName = buildDatasourceName(KEY, INPUT_TYPE);
|
|
29
|
+
const { KEY, NAME, INPUT_TYPE } = action.payload.base;
|
|
30
|
+
const key: DatasourceName = buildDatasourceName(KEY, NAME, INPUT_TYPE);
|
|
30
31
|
state[key] = {
|
|
31
32
|
KEY: KEY,
|
|
32
33
|
INPUT_TYPE: INPUT_TYPE,
|
|
33
|
-
NAME:
|
|
34
|
+
NAME: NAME,
|
|
34
35
|
OUTPUT_TYPE: 'TABLE',
|
|
35
36
|
TRANSFORMER_CONFIGURATION: {},
|
|
37
|
+
GROUPING_STRATEGY: 'NONE',
|
|
38
|
+
GROUP_KEY: null,
|
|
36
39
|
};
|
|
37
40
|
action.payload.fields.forEach((field) => {
|
|
38
41
|
datasourceSlice.caseReducers.setColumnRename(state, {
|
|
@@ -54,11 +57,43 @@ export const datasourceSlice = createSlice({
|
|
|
54
57
|
});
|
|
55
58
|
});
|
|
56
59
|
},
|
|
57
|
-
|
|
60
|
+
reorderDatasource(
|
|
58
61
|
state: DatasourceConfig,
|
|
59
|
-
action: PayloadAction<{ key: DatasourceName;
|
|
62
|
+
action: PayloadAction<{ key: DatasourceName; newIndex: number }>,
|
|
60
63
|
) {
|
|
61
|
-
|
|
64
|
+
const { key, newIndex } = action.payload;
|
|
65
|
+
const entries = Object.entries(state);
|
|
66
|
+
const currentIndex = entries.findIndex(([k]) => k === key);
|
|
67
|
+
|
|
68
|
+
if (currentIndex === -1) return state;
|
|
69
|
+
|
|
70
|
+
const [item] = entries.splice(currentIndex, 1);
|
|
71
|
+
entries.splice(newIndex, 0, item);
|
|
72
|
+
|
|
73
|
+
return Object.fromEntries(entries) as DatasourceConfig;
|
|
74
|
+
},
|
|
75
|
+
renameDatasource(
|
|
76
|
+
state: DatasourceConfig,
|
|
77
|
+
action: PayloadAction<{ key: DatasourceName; newName: string }>,
|
|
78
|
+
) {
|
|
79
|
+
const { key: oldKey, newName } = action.payload;
|
|
80
|
+
const datasource = state[oldKey];
|
|
81
|
+
if (!datasource) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const newKey = buildDatasourceName(newName, datasource.NAME, datasource.INPUT_TYPE);
|
|
85
|
+
if (state[newKey] && newKey !== oldKey) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Object.fromEntries(
|
|
90
|
+
Object.entries(state).map(([k, v]) => {
|
|
91
|
+
if (k === oldKey) {
|
|
92
|
+
return [newKey, { ...v, KEY: newName }];
|
|
93
|
+
}
|
|
94
|
+
return [k, v];
|
|
95
|
+
}),
|
|
96
|
+
) as DatasourceConfig;
|
|
62
97
|
},
|
|
63
98
|
updateDatasourceOutputType(
|
|
64
99
|
state: DatasourceConfig,
|
|
@@ -100,22 +135,26 @@ export const datasourceSlice = createSlice({
|
|
|
100
135
|
column: string;
|
|
101
136
|
included: boolean;
|
|
102
137
|
type?: Genesis.GenesisFieldTypes;
|
|
138
|
+
setDefaultFormat?: boolean;
|
|
103
139
|
}>,
|
|
104
140
|
) {
|
|
105
|
-
const { key, column, included, type } = action.payload;
|
|
141
|
+
const { key, column, included, type, setDefaultFormat = true } = action.payload;
|
|
106
142
|
if (included) {
|
|
107
143
|
state[key].TRANSFORMER_CONFIGURATION.INCLUDE_COLUMNS = [
|
|
108
144
|
...(state[key].TRANSFORMER_CONFIGURATION.INCLUDE_COLUMNS ?? []),
|
|
109
145
|
column,
|
|
110
146
|
];
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
147
|
+
const defaultFormat = getDefaultFormat(type);
|
|
148
|
+
if (defaultFormat && setDefaultFormat) {
|
|
149
|
+
datasourceSlice.caseReducers.setColumnFormat(state, {
|
|
150
|
+
...action,
|
|
151
|
+
payload: {
|
|
152
|
+
key,
|
|
153
|
+
column,
|
|
154
|
+
format: defaultFormat,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
119
158
|
} else {
|
|
120
159
|
state[key].TRANSFORMER_CONFIGURATION.INCLUDE_COLUMNS = (
|
|
121
160
|
state[key].TRANSFORMER_CONFIGURATION.INCLUDE_COLUMNS ?? []
|
|
@@ -220,6 +259,22 @@ export const datasourceSlice = createSlice({
|
|
|
220
259
|
const { [column]: _, ...rest } = state[key].TRANSFORMER_CONFIGURATION?.COLUMN_FORMATS ?? {};
|
|
221
260
|
state[key].TRANSFORMER_CONFIGURATION.COLUMN_FORMATS = rest;
|
|
222
261
|
},
|
|
262
|
+
setGrouping(
|
|
263
|
+
state: DatasourceConfig,
|
|
264
|
+
action: PayloadAction<{
|
|
265
|
+
key: DatasourceName;
|
|
266
|
+
grouping: Exclude<DatasourceGrouping, { GROUPING_STRATEGY: 'NONE' }>;
|
|
267
|
+
}>,
|
|
268
|
+
) {
|
|
269
|
+
const { key, grouping } = action.payload;
|
|
270
|
+
state[key].GROUPING_STRATEGY = grouping.GROUPING_STRATEGY;
|
|
271
|
+
state[key].GROUP_KEY = grouping.GROUP_KEY;
|
|
272
|
+
},
|
|
273
|
+
deleteGrouping(state: DatasourceConfig, action: PayloadAction<{ key: DatasourceName }>) {
|
|
274
|
+
const { key } = action.payload;
|
|
275
|
+
state[key].GROUPING_STRATEGY = 'NONE';
|
|
276
|
+
state[key].GROUP_KEY = null;
|
|
277
|
+
},
|
|
223
278
|
},
|
|
224
279
|
selectors: {
|
|
225
280
|
getAllConfigSet(state: DatasourceConfig) {
|
|
@@ -16,9 +16,27 @@ export const datasourceOutputs = [
|
|
|
16
16
|
] as const;
|
|
17
17
|
export type DatasourceOutputTypes = (typeof datasourceOutputs)[number];
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export type
|
|
19
|
+
export const datasourceGroupingStrategies = ['NONE', 'MULTI_SHEET', 'LOOKUP_TABLE'] as const;
|
|
20
|
+
export type DatasourceGroupingStrategy = (typeof datasourceGroupingStrategies)[number];
|
|
21
|
+
export type DatasourceGrouping =
|
|
22
|
+
| {
|
|
23
|
+
GROUPING_STRATEGY: Extract<DatasourceGroupingStrategy, 'NONE'>;
|
|
24
|
+
GROUP_KEY: null;
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
GROUPING_STRATEGY: Extract<DatasourceGroupingStrategy, 'MULTI_SHEET' | 'LOOKUP_TABLE'>;
|
|
28
|
+
GROUP_KEY: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Server KEY => This is an alias (this needs to be unique). This is presented as "Name" input on the UI, rapid-text-field
|
|
33
|
+
* Server NAME => The name of the datasource (matches req_rep). This is presented as rapid-select
|
|
34
|
+
* DatasoureName to uniquely identify each data source, must be keyed on the input type as
|
|
35
|
+
* the names are not unique between the different data sources
|
|
36
|
+
*/
|
|
37
|
+
export type AliasKey = string;
|
|
38
|
+
export type ServerDatasourceName = string;
|
|
39
|
+
export type DatasourceName = `${DatasourceInputTypes}_${ServerDatasourceName}_${AliasKey}`;
|
|
22
40
|
|
|
23
41
|
export type TransformerConfig = {
|
|
24
42
|
COLUMN_RENAMES?: { [k: string]: string };
|
|
@@ -44,7 +62,7 @@ export type DatasourceConfig = {
|
|
|
44
62
|
INPUT_TYPE: DatasourceInputTypes;
|
|
45
63
|
OUTPUT_TYPE: DatasourceOutputTypes;
|
|
46
64
|
TRANSFORMER_CONFIGURATION: TransformerConfig;
|
|
47
|
-
};
|
|
65
|
+
} & DatasourceGrouping;
|
|
48
66
|
};
|
|
49
67
|
|
|
50
68
|
// BASE-CONFIG
|
package/src/new/types/misc.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { JSONSchema7 as JSONShemaBase } from 'json-schema';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
BaseConfig,
|
|
4
|
+
DatasourceConfig,
|
|
5
|
+
DatasourceName,
|
|
6
|
+
Schedule,
|
|
7
|
+
DatasourceGrouping,
|
|
8
|
+
} from '../store';
|
|
3
9
|
|
|
4
10
|
export namespace Genesis {
|
|
5
11
|
export const genesisFieldTypes = [
|
|
@@ -28,7 +34,8 @@ export namespace Genesis {
|
|
|
28
34
|
};
|
|
29
35
|
|
|
30
36
|
export type ServerReportConfig = Omit<BaseConfig, 'SCHEDULE'> & {
|
|
31
|
-
DATA_SOURCES: DatasourceConfig[DatasourceName]
|
|
37
|
+
DATA_SOURCES: (Omit<DatasourceConfig[DatasourceName], 'GROUPING_STRATEGY' | 'GROUP_KEY'> &
|
|
38
|
+
(Exclude<DatasourceGrouping, { GROUPING_STRATEGY: 'NONE' }> | {}))[];
|
|
32
39
|
SCHEDULES: Schedule[];
|
|
33
40
|
};
|
|
34
41
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { assert, suite } from '@genesislcap/foundation-testing';
|
|
2
|
+
import { generateUniqueAlias } from './alias-generator';
|
|
3
|
+
|
|
4
|
+
const GenerateUniqueAlias = suite('generateUniqueAlias');
|
|
5
|
+
|
|
6
|
+
GenerateUniqueAlias('returns the base alias if it does not exist', () => {
|
|
7
|
+
const existingAliases = new Set(['other']);
|
|
8
|
+
const alias = generateUniqueAlias('test', existingAliases);
|
|
9
|
+
assert.equal(alias, 'test');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
GenerateUniqueAlias('increments the alias if it already exists', () => {
|
|
13
|
+
const existingAliases = new Set(['test']);
|
|
14
|
+
const alias = generateUniqueAlias('test', existingAliases);
|
|
15
|
+
assert.equal(alias, 'test_1');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
GenerateUniqueAlias('increments the alias until it is unique', () => {
|
|
19
|
+
const existingAliases = new Set(['test', 'test_1']);
|
|
20
|
+
const alias = generateUniqueAlias('test', existingAliases);
|
|
21
|
+
assert.equal(alias, 'test_2');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
GenerateUniqueAlias('handles case-insensitivity when checking for existing aliases', () => {
|
|
25
|
+
const existingAliases = new Set(['Test']);
|
|
26
|
+
const alias = generateUniqueAlias('test', existingAliases);
|
|
27
|
+
assert.equal(alias, 'test_1');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
GenerateUniqueAlias('handles case-insensitivity for new alias generation', () => {
|
|
31
|
+
const existingAliases = new Set(['test']);
|
|
32
|
+
const alias = generateUniqueAlias('Test', existingAliases);
|
|
33
|
+
// Since input baseAlias is 'Test', it tries 'Test'. 'Test' matches 'test' case-insensitively.
|
|
34
|
+
// Next attempt 'Test_1'.
|
|
35
|
+
assert.equal(alias, 'Test_1');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
GenerateUniqueAlias('handles mixed case existing aliases', () => {
|
|
39
|
+
const existingAliases = new Set(['TeSt', 'tEsT_1']);
|
|
40
|
+
const alias = generateUniqueAlias('test', existingAliases);
|
|
41
|
+
assert.equal(alias, 'test_2');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
GenerateUniqueAlias.run();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a unique alias by appending a counter if the base alias already exists in the set.
|
|
3
|
+
* @param baseAlias The desired alias name (usually the resource name).
|
|
4
|
+
* @param existingAliases A Set of currently used aliases to check against.
|
|
5
|
+
* @returns A unique alias string.
|
|
6
|
+
*/
|
|
7
|
+
export function generateUniqueAlias(baseAlias: string, existingAliases: Set<string>): string {
|
|
8
|
+
const existingAliasesLower = new Set(
|
|
9
|
+
Array.from(existingAliases).map((alias) => alias.toLowerCase()),
|
|
10
|
+
);
|
|
11
|
+
let uniqueAlias = baseAlias;
|
|
12
|
+
let counter = 1;
|
|
13
|
+
while (existingAliasesLower.has(uniqueAlias.toLowerCase())) {
|
|
14
|
+
uniqueAlias = `${baseAlias}_${counter}`;
|
|
15
|
+
counter += 1;
|
|
16
|
+
}
|
|
17
|
+
return uniqueAlias;
|
|
18
|
+
}
|
package/src/new/utils/index.ts
CHANGED
package/src/new/utils/tooltip.ts
CHANGED
|
@@ -52,6 +52,11 @@ export namespace TOOLTIP {
|
|
|
52
52
|
</ul>
|
|
53
53
|
`;
|
|
54
54
|
|
|
55
|
+
export const DATASOURCES_DATASOURCE = `
|
|
56
|
+
<h3>Datasource</h3>
|
|
57
|
+
<p>This is the name of the server resource that data will be pulled from to generate the report.</p>
|
|
58
|
+
`;
|
|
59
|
+
|
|
55
60
|
export const DATASOURCES_NAME = `
|
|
56
61
|
<h3>Datasource name</h3>
|
|
57
62
|
<p>Allows you to set a name for the datasource which must correspond with your template data references where the report is configured to use a template file.</p>
|
|
@@ -63,6 +68,20 @@ export namespace TOOLTIP {
|
|
|
63
68
|
<p>Configures the method by which the server constructs the report configuration. Currently only TABLE is supported, which means the entire datasource is loaded into memory when generating the report using this report configuration.</p>
|
|
64
69
|
`;
|
|
65
70
|
|
|
71
|
+
export const DATASOURCES_GROUPING_STRATEGY = `
|
|
72
|
+
<h3>Grouping Strategy</h3>
|
|
73
|
+
<p>Select "None" to disable grouping strategies, or select the strategy you'd like to use.</p>
|
|
74
|
+
<ul>
|
|
75
|
+
<li><strong>Multi-sheet</strong>: Group data for <code>JXLS</code> template Multi-sheet functionality, to split different groups across multiple excel worksheets.</li>
|
|
76
|
+
<li><strong>Lookup-table</strong>: Create a lookup table of key value pairs in the template data, where the group key field is the map key.</li>
|
|
77
|
+
</ul>
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
export const DATASOURCES_GROUPING_KEY = `
|
|
81
|
+
<h3>Grouping Key</h3>
|
|
82
|
+
<p>Select the field for this datasource you want to operate the grouping strategy on.</p>
|
|
83
|
+
`;
|
|
84
|
+
|
|
66
85
|
export const DELIVERY_TIMEZONE = `
|
|
67
86
|
<h3>Timezone</h3>
|
|
68
87
|
<p>Select the timezone to be used for this schedule.</p>
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { assert, sinon, suite } from '@genesislcap/foundation-testing';
|
|
2
2
|
import type { Genesis } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildDatasourceName,
|
|
5
|
+
transformFromServerPayload,
|
|
6
|
+
transformToServerPayload,
|
|
7
|
+
} from './transformers';
|
|
4
8
|
|
|
5
9
|
const TransformFromServerPayload = suite('transformFromServerPayload');
|
|
6
10
|
|
|
@@ -39,7 +43,7 @@ TransformFromServerPayload('cleans up orphaned fields from INCLUDE_COLUMNS', asy
|
|
|
39
43
|
|
|
40
44
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
41
45
|
|
|
42
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
46
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
43
47
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
44
48
|
|
|
45
49
|
assert.equal(cleanedConfig.INCLUDE_COLUMNS?.length, 2);
|
|
@@ -83,7 +87,7 @@ TransformFromServerPayload('cleans up orphaned fields from COLUMN_RENAMES', asyn
|
|
|
83
87
|
|
|
84
88
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
85
89
|
|
|
86
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
90
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
87
91
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
88
92
|
|
|
89
93
|
assert.equal(Object.keys(cleanedConfig.COLUMN_RENAMES || {}).length, 2);
|
|
@@ -127,7 +131,7 @@ TransformFromServerPayload('cleans up orphaned fields from COLUMN_FORMATS', asyn
|
|
|
127
131
|
|
|
128
132
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
129
133
|
|
|
130
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
134
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
131
135
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
132
136
|
|
|
133
137
|
assert.equal(Object.keys(cleanedConfig.COLUMN_FORMATS || {}).length, 2);
|
|
@@ -171,7 +175,7 @@ TransformFromServerPayload('cleans up orphaned fields from COLUMN_TRANSFORMS', a
|
|
|
171
175
|
|
|
172
176
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
173
177
|
|
|
174
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
178
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
175
179
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
176
180
|
|
|
177
181
|
assert.equal(Object.keys(cleanedConfig.COLUMN_TRANSFORMS || {}).length, 2);
|
|
@@ -209,7 +213,7 @@ TransformFromServerPayload('handles schema fetch failure gracefully', async () =
|
|
|
209
213
|
|
|
210
214
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
211
215
|
|
|
212
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
216
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
213
217
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
214
218
|
|
|
215
219
|
// Should keep original config when schema fetch fails
|
|
@@ -266,8 +270,8 @@ TransformFromServerPayload('handles multiple datasources independently', async (
|
|
|
266
270
|
|
|
267
271
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
268
272
|
|
|
269
|
-
const counterpartyKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
270
|
-
const tradeKey = buildDatasourceName('TRADE', 'REQ_REP');
|
|
273
|
+
const counterpartyKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
274
|
+
const tradeKey = buildDatasourceName('TRADE', 'TRADE', 'REQ_REP');
|
|
271
275
|
|
|
272
276
|
assert.equal(
|
|
273
277
|
result.datasourceConfig[counterpartyKey].TRANSFORMER_CONFIGURATION.INCLUDE_COLUMNS?.length,
|
|
@@ -318,7 +322,7 @@ TransformFromServerPayload('handles empty transformer configuration', async () =
|
|
|
318
322
|
|
|
319
323
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
320
324
|
|
|
321
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
325
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
322
326
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
323
327
|
|
|
324
328
|
assert.equal(JSON.stringify(cleanedConfig), JSON.stringify({}));
|
|
@@ -350,7 +354,7 @@ TransformFromServerPayload('handles missing schema properties', async () => {
|
|
|
350
354
|
|
|
351
355
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
352
356
|
|
|
353
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
357
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
354
358
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
355
359
|
|
|
356
360
|
// All fields should be removed when schema has no properties
|
|
@@ -460,7 +464,7 @@ TransformFromServerPayload('handles all transformer config types together', asyn
|
|
|
460
464
|
|
|
461
465
|
const result = await transformFromServerPayload(payload, mockGetSchema);
|
|
462
466
|
|
|
463
|
-
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'REQ_REP');
|
|
467
|
+
const datasourceKey = buildDatasourceName('COUNTERPARTY', 'COUNTERPARTY', 'REQ_REP');
|
|
464
468
|
const cleanedConfig = result.datasourceConfig[datasourceKey].TRANSFORMER_CONFIGURATION;
|
|
465
469
|
|
|
466
470
|
// INCLUDE_COLUMNS should only have valid fields
|
|
@@ -485,3 +489,61 @@ TransformFromServerPayload('handles all transformer config types together', asyn
|
|
|
485
489
|
});
|
|
486
490
|
|
|
487
491
|
TransformFromServerPayload.run();
|
|
492
|
+
|
|
493
|
+
const TransformToServerPayload = suite('transformToServerPayload');
|
|
494
|
+
|
|
495
|
+
TransformToServerPayload('omits grouping fields when strategy is NONE', () => {
|
|
496
|
+
const state: any = {
|
|
497
|
+
baseConfig: {
|
|
498
|
+
DESTINATION_IDS: [],
|
|
499
|
+
OUTPUT_DIRECTORY: '/reports',
|
|
500
|
+
},
|
|
501
|
+
datasourceConfig: {
|
|
502
|
+
REQ_REP_COUNTERPARTY_COUNTERPARTY: {
|
|
503
|
+
KEY: 'COUNTERPARTY',
|
|
504
|
+
INPUT_TYPE: 'REQ_REP',
|
|
505
|
+
NAME: 'COUNTERPARTY',
|
|
506
|
+
OUTPUT_TYPE: 'TABLE',
|
|
507
|
+
TRANSFORMER_CONFIGURATION: {},
|
|
508
|
+
GROUPING_STRATEGY: 'NONE',
|
|
509
|
+
GROUP_KEY: null,
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const result = transformToServerPayload(state);
|
|
515
|
+
const ds = result.DATA_SOURCES[0];
|
|
516
|
+
|
|
517
|
+
assert.equal(ds.KEY, 'COUNTERPARTY');
|
|
518
|
+
assert.ok(!('GROUPING_STRATEGY' in ds));
|
|
519
|
+
assert.ok(!('GROUP_KEY' in ds));
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
TransformToServerPayload('includes grouping fields when strategy is not NONE', () => {
|
|
523
|
+
const state: any = {
|
|
524
|
+
baseConfig: {
|
|
525
|
+
DESTINATION_IDS: [],
|
|
526
|
+
OUTPUT_DIRECTORY: '/reports',
|
|
527
|
+
},
|
|
528
|
+
datasourceConfig: {
|
|
529
|
+
REQ_REP_COUNTERPARTY_COUNTERPARTY: {
|
|
530
|
+
KEY: 'COUNTERPARTY',
|
|
531
|
+
INPUT_TYPE: 'REQ_REP',
|
|
532
|
+
NAME: 'COUNTERPARTY',
|
|
533
|
+
OUTPUT_TYPE: 'TABLE',
|
|
534
|
+
TRANSFORMER_CONFIGURATION: {},
|
|
535
|
+
GROUPING_STRATEGY: 'MULTI_SHEET',
|
|
536
|
+
GROUP_KEY: 'some_key',
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const result = transformToServerPayload(state);
|
|
542
|
+
const ds = result.DATA_SOURCES[0] as any;
|
|
543
|
+
|
|
544
|
+
assert.equal(ds.KEY, 'COUNTERPARTY');
|
|
545
|
+
assert.equal(ds.GROUPING_STRATEGY, 'MULTI_SHEET');
|
|
546
|
+
assert.equal(ds.GROUP_KEY, 'some_key');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
TransformToServerPayload.run();
|
|
@@ -18,7 +18,14 @@ export function transformToServerPayload(state: ReportingConfig): Genesis.Server
|
|
|
18
18
|
DOCUMENT_TEMPLATE_ID: restBaseConfig.DOCUMENT_TEMPLATE_ID
|
|
19
19
|
? restBaseConfig.DOCUMENT_TEMPLATE_ID
|
|
20
20
|
: null,
|
|
21
|
-
DATA_SOURCES: Object.values(datasourceConfig)
|
|
21
|
+
DATA_SOURCES: Object.values(datasourceConfig).map((ds) => {
|
|
22
|
+
// For NONE case we just don't send it to the server to be backwards compatible
|
|
23
|
+
if (ds.GROUPING_STRATEGY === 'NONE') {
|
|
24
|
+
const { GROUPING_STRATEGY, GROUP_KEY, ...rest } = ds;
|
|
25
|
+
return rest;
|
|
26
|
+
}
|
|
27
|
+
return ds;
|
|
28
|
+
}),
|
|
22
29
|
};
|
|
23
30
|
}
|
|
24
31
|
|
|
@@ -85,10 +92,18 @@ export async function transformFromServerPayload(
|
|
|
85
92
|
|
|
86
93
|
const cleanedDatasources = await Promise.all(
|
|
87
94
|
DATA_SOURCES.map(
|
|
88
|
-
async ({
|
|
95
|
+
async ({
|
|
96
|
+
KEY,
|
|
97
|
+
INPUT_TYPE,
|
|
98
|
+
NAME: DS_NAME,
|
|
99
|
+
OUTPUT_TYPE,
|
|
100
|
+
TRANSFORMER_CONFIGURATION,
|
|
101
|
+
...rest
|
|
102
|
+
}) => {
|
|
89
103
|
try {
|
|
90
|
-
const schema = await getSchema(
|
|
104
|
+
const schema = await getSchema(DS_NAME);
|
|
91
105
|
const validFields = new Set(Object.keys(schema.properties || {}));
|
|
106
|
+
const { GROUPING_STRATEGY, GROUP_KEY } = rest as any;
|
|
92
107
|
|
|
93
108
|
const cleanedConfig = cleanupTransformerConfig(
|
|
94
109
|
TRANSFORMER_CONFIGURATION || {},
|
|
@@ -101,16 +116,21 @@ export async function transformFromServerPayload(
|
|
|
101
116
|
NAME: DS_NAME,
|
|
102
117
|
OUTPUT_TYPE,
|
|
103
118
|
TRANSFORMER_CONFIGURATION: cleanedConfig,
|
|
119
|
+
GROUPING_STRATEGY: GROUPING_STRATEGY || 'NONE',
|
|
120
|
+
GROUP_KEY: GROUP_KEY || null,
|
|
104
121
|
};
|
|
105
122
|
} catch (error) {
|
|
106
123
|
// If schema fetch fails, log warning but keep original config
|
|
107
124
|
console.warn(`Failed to fetch schema for ${KEY}, skipping cleanup:`, error);
|
|
125
|
+
const { GROUPING_STRATEGY, GROUP_KEY } = rest as any;
|
|
108
126
|
return {
|
|
109
127
|
KEY,
|
|
110
128
|
INPUT_TYPE,
|
|
111
129
|
NAME: DS_NAME,
|
|
112
130
|
OUTPUT_TYPE,
|
|
113
131
|
TRANSFORMER_CONFIGURATION: TRANSFORMER_CONFIGURATION || {},
|
|
132
|
+
GROUPING_STRATEGY: GROUPING_STRATEGY || 'NONE',
|
|
133
|
+
GROUP_KEY: GROUP_KEY || null,
|
|
114
134
|
};
|
|
115
135
|
}
|
|
116
136
|
},
|
|
@@ -122,7 +142,7 @@ export async function transformFromServerPayload(
|
|
|
122
142
|
datasourceConfig: cleanedDatasources.reduce(
|
|
123
143
|
(acum, ds) => ({
|
|
124
144
|
...acum,
|
|
125
|
-
[buildDatasourceName(ds.NAME, ds.INPUT_TYPE)]: ds,
|
|
145
|
+
[buildDatasourceName(ds.KEY, ds.NAME, ds.INPUT_TYPE)]: ds,
|
|
126
146
|
}),
|
|
127
147
|
{},
|
|
128
148
|
),
|
|
@@ -145,6 +165,10 @@ export function datasourceInputFromDisplay(
|
|
|
145
165
|
else throw new Error(`datasourceInputFromDisplay - Uncaught case ${display}`);
|
|
146
166
|
}
|
|
147
167
|
|
|
148
|
-
export function buildDatasourceName(
|
|
149
|
-
|
|
168
|
+
export function buildDatasourceName(
|
|
169
|
+
alias: string,
|
|
170
|
+
resourceName: string,
|
|
171
|
+
type: DatasourceInputTypes,
|
|
172
|
+
): DatasourceName {
|
|
173
|
+
return `${type}_${resourceName}_${alias}`;
|
|
150
174
|
}
|