@genesislcap/pbc-reporting-ui 14.396.4 → 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/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 +19 -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 +126 -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.test.js +39 -7
- 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 +41 -5
- 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 +39 -7
- 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 +58 -7
- 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
|
@@ -3,14 +3,17 @@ import { Combobox } from '@genesislcap/foundation-ui';
|
|
|
3
3
|
import { attr, customElement, GenesisElement, observable } from '@genesislcap/web-core';
|
|
4
4
|
import {
|
|
5
5
|
actions,
|
|
6
|
+
DatasourceGroupingStrategy,
|
|
6
7
|
DatasourceInputTypes,
|
|
7
8
|
DatasourceName,
|
|
8
9
|
DatasourceOutputTypes,
|
|
10
|
+
selectors,
|
|
9
11
|
} from '../../../store';
|
|
10
12
|
import { Display } from '../../../types';
|
|
11
13
|
import {
|
|
12
14
|
buildDatasourceName,
|
|
13
15
|
datasourceInputFromDisplay,
|
|
16
|
+
generateUniqueAlias,
|
|
14
17
|
getDatasourceSchema,
|
|
15
18
|
showNotificationToast,
|
|
16
19
|
} from '../../../utils';
|
|
@@ -30,11 +33,23 @@ export class DatasourceConfigItem extends GenesisElement {
|
|
|
30
33
|
|
|
31
34
|
@observable datasourceName: DatasourceName | null = null;
|
|
32
35
|
@observable datasourceChoices: DatasourceChoice[] | null = null;
|
|
36
|
+
@observable groupingByFieldsNames: string[] | null = null;
|
|
33
37
|
@attr({ mode: 'boolean', attribute: 'can-remove' }) canRemove: boolean = false;
|
|
34
38
|
|
|
35
39
|
@attr position: 'below' | 'above' = 'below';
|
|
36
40
|
@attr error: 'none' | 'invalid-datasource' | 'unknown' = 'none';
|
|
37
41
|
|
|
42
|
+
async connectedCallback(): Promise<void> {
|
|
43
|
+
super.connectedCallback();
|
|
44
|
+
try {
|
|
45
|
+
const datasource = selectors.datasourceConfig.getDatasource(this.datasourceName);
|
|
46
|
+
const schema = await this.getSchema(datasource.NAME);
|
|
47
|
+
this.groupingByFieldsNames = Object.keys(schema.properties);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error(e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
deleteDatasourceHandler() {
|
|
39
54
|
if (!this.canRemove) throw new Error('Trying to remove final datasource!');
|
|
40
55
|
if (!this.datasourceName) throw new Error('deleteDatasourceHandler - Datasource key unset!');
|
|
@@ -43,13 +58,31 @@ export class DatasourceConfigItem extends GenesisElement {
|
|
|
43
58
|
});
|
|
44
59
|
}
|
|
45
60
|
|
|
46
|
-
updateKeyHandler(
|
|
61
|
+
updateKeyHandler(newName: string, event?: Event) {
|
|
47
62
|
if (!this.datasourceName) {
|
|
48
63
|
throw new Error('updateKeyHandler - datasourceName unset');
|
|
49
64
|
}
|
|
50
|
-
|
|
65
|
+
|
|
66
|
+
const allDatasources = selectors.datasourceConfig.getAllConfigSet();
|
|
67
|
+
const nameExists = Object.entries(allDatasources)
|
|
68
|
+
.filter(([key]) => key !== this.datasourceName)
|
|
69
|
+
.some(([, ds]) => ds.KEY.toLowerCase() === newName.toLowerCase());
|
|
70
|
+
|
|
71
|
+
if (nameExists) {
|
|
72
|
+
showNotificationToast({
|
|
73
|
+
title: 'Reporting Config',
|
|
74
|
+
body: `Name '${newName}' is already in use by another datasource.`,
|
|
75
|
+
toast: { type: 'error' as any },
|
|
76
|
+
});
|
|
77
|
+
if (event) {
|
|
78
|
+
(event.target as HTMLInputElement).value = allDatasources[this.datasourceName].KEY;
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
actions.datasourceConfig.renameDatasource({
|
|
51
84
|
key: this.datasourceName,
|
|
52
|
-
|
|
85
|
+
newName,
|
|
53
86
|
});
|
|
54
87
|
}
|
|
55
88
|
|
|
@@ -63,6 +96,45 @@ export class DatasourceConfigItem extends GenesisElement {
|
|
|
63
96
|
});
|
|
64
97
|
}
|
|
65
98
|
|
|
99
|
+
async updateGroupingStrategyHandler(value: DatasourceGroupingStrategy) {
|
|
100
|
+
try {
|
|
101
|
+
if (value === 'NONE') {
|
|
102
|
+
actions.datasourceConfig.deleteGrouping({ key: this.datasourceName });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const dsResource = selectors.datasourceConfig.getDatasource(this.datasourceName).NAME;
|
|
107
|
+
const schema = await this.getSchema(dsResource);
|
|
108
|
+
if (!schema.properties) throw new Error(`schema properties for ${dsResource} undefined`);
|
|
109
|
+
|
|
110
|
+
const fields = Object.keys(schema.properties);
|
|
111
|
+
actions.datasourceConfig.setGrouping({
|
|
112
|
+
key: this.datasourceName,
|
|
113
|
+
grouping: { GROUPING_STRATEGY: value, GROUP_KEY: fields[0] },
|
|
114
|
+
});
|
|
115
|
+
this.groupingByFieldsNames = Object.keys(schema.properties);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
const errorMsg = e instanceof Error ? `\n\n${e.message}` : '';
|
|
118
|
+
showNotificationToast({
|
|
119
|
+
title: 'Reporting Config',
|
|
120
|
+
body: `Error fetching field data for datasource ${this.datasourceName}.${errorMsg}`,
|
|
121
|
+
toast: { type: 'critical' as any },
|
|
122
|
+
});
|
|
123
|
+
this.error = 'unknown';
|
|
124
|
+
console.error(e);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
updateGroupingByHandler(value: string) {
|
|
129
|
+
const datasource = selectors.datasourceConfig.getDatasource(this.datasourceName);
|
|
130
|
+
if (datasource.GROUPING_STRATEGY === 'NONE')
|
|
131
|
+
throw new Error('Cannot set group by field when GROUPING_STRATEGY is NONE');
|
|
132
|
+
actions.datasourceConfig.setGrouping({
|
|
133
|
+
key: this.datasourceName,
|
|
134
|
+
grouping: { GROUPING_STRATEGY: datasource.GROUPING_STRATEGY, GROUP_KEY: value },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
66
138
|
async updateDatasourceSourceHandler(selectedOption: Combobox) {
|
|
67
139
|
try {
|
|
68
140
|
if ((selectedOption?.selectedOptions ?? []).filter((x) => x).length === 0) {
|
|
@@ -72,7 +144,7 @@ export class DatasourceConfigItem extends GenesisElement {
|
|
|
72
144
|
return;
|
|
73
145
|
}
|
|
74
146
|
const option = selectedOption.selectedOptions[0];
|
|
75
|
-
const
|
|
147
|
+
const RESOURCE_NAME = option.value;
|
|
76
148
|
const dataAttr = <Display.DatasourceInputTypes | null>(
|
|
77
149
|
option.getAttribute('data-bubble-content')
|
|
78
150
|
);
|
|
@@ -83,22 +155,49 @@ export class DatasourceConfigItem extends GenesisElement {
|
|
|
83
155
|
throw new Error('updateKeyHandler - datasourceName or INPUT_TYPE null');
|
|
84
156
|
}
|
|
85
157
|
|
|
86
|
-
const schema = await this.getSchema(
|
|
87
|
-
if (!schema.properties) throw new Error(`schema properties for ${
|
|
158
|
+
const schema = await this.getSchema(RESOURCE_NAME);
|
|
159
|
+
if (!schema.properties) throw new Error(`schema properties for ${RESOURCE_NAME} undefined`);
|
|
88
160
|
|
|
89
161
|
const fields = Object.keys(schema.properties);
|
|
90
162
|
|
|
163
|
+
// Check for duplicate aliases
|
|
164
|
+
const allDatasources = selectors.datasourceConfig.getAllConfigSet();
|
|
165
|
+
const existingAliases = new Set(
|
|
166
|
+
Object.entries(allDatasources)
|
|
167
|
+
.filter(([key]) => key !== this.datasourceName)
|
|
168
|
+
.map(([, ds]) => ds.KEY),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const uniqueAlias = generateUniqueAlias(RESOURCE_NAME, existingAliases);
|
|
172
|
+
|
|
173
|
+
// Preserve index
|
|
174
|
+
const originalIndex = Object.keys(allDatasources).indexOf(this.datasourceName);
|
|
175
|
+
|
|
91
176
|
actions.datasourceConfig.deleteDatasource({ key: this.datasourceName });
|
|
92
177
|
actions.datasourceConfig.initDatasourceConfiguration({
|
|
93
178
|
base: {
|
|
94
|
-
KEY,
|
|
179
|
+
KEY: uniqueAlias,
|
|
180
|
+
NAME: RESOURCE_NAME,
|
|
95
181
|
INPUT_TYPE: INPUT_TYPE as DatasourceInputTypes,
|
|
96
182
|
},
|
|
97
183
|
fields,
|
|
98
184
|
});
|
|
99
|
-
const datasourceName = buildDatasourceName(
|
|
185
|
+
const datasourceName = buildDatasourceName(
|
|
186
|
+
uniqueAlias,
|
|
187
|
+
RESOURCE_NAME,
|
|
188
|
+
INPUT_TYPE as DatasourceInputTypes,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (originalIndex !== -1) {
|
|
192
|
+
actions.datasourceConfig.reorderDatasource({
|
|
193
|
+
key: datasourceName,
|
|
194
|
+
newIndex: originalIndex,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
100
198
|
setDefaultFormatsForDatasource(datasourceName, schema);
|
|
101
199
|
this.error = 'none';
|
|
200
|
+
this.groupingByFieldsNames = Object.keys(schema.properties);
|
|
102
201
|
} catch (e: unknown) {
|
|
103
202
|
const errorMsg = e instanceof Error ? `\n\n${e.message}` : '';
|
|
104
203
|
showNotificationToast({
|
package/src/new/main/edit-config/datasource-config/datasources-config-container.helpers.test.ts
CHANGED
|
@@ -190,15 +190,17 @@ CreateNewDatasourceConfig.before.each(() => {
|
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
CreateNewDatasourceConfig('should exit when no valid datasources are available', async () => {
|
|
193
|
-
const mockChoices = [{ name: '
|
|
193
|
+
const mockChoices = [{ name: 'testSource', inputType: 'REQ_REP' as DatasourceInputTypes }];
|
|
194
194
|
|
|
195
195
|
const getAllConfigSetStub = sinon.stub(selectors.datasourceConfig, 'getAllConfigSet').returns({
|
|
196
|
-
|
|
197
|
-
KEY: '
|
|
198
|
-
NAME: '
|
|
196
|
+
REQ_REP_testSource_testSource: {
|
|
197
|
+
KEY: 'testSource',
|
|
198
|
+
NAME: 'testSource',
|
|
199
199
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
200
200
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
201
201
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: [] },
|
|
202
|
+
GROUPING_STRATEGY: 'NONE',
|
|
203
|
+
GROUP_KEY: null,
|
|
202
204
|
},
|
|
203
205
|
});
|
|
204
206
|
|
|
@@ -215,15 +217,17 @@ CreateNewDatasourceConfig('should exit when no valid datasources are available',
|
|
|
215
217
|
CreateNewDatasourceConfig(
|
|
216
218
|
'should exit when trying to add multiple datasources with CSV format',
|
|
217
219
|
async () => {
|
|
218
|
-
const mockChoices = [{ name: '
|
|
220
|
+
const mockChoices = [{ name: 'testSource', inputType: 'REQ_REP' as DatasourceInputTypes }];
|
|
219
221
|
|
|
220
222
|
const getAllConfigSetStub = sinon.stub(selectors.datasourceConfig, 'getAllConfigSet').returns({
|
|
221
|
-
|
|
222
|
-
KEY: '
|
|
223
|
-
NAME: '
|
|
223
|
+
REQ_REP_existingSource_existingAlias: {
|
|
224
|
+
KEY: 'existingAlias',
|
|
225
|
+
NAME: 'existingSource',
|
|
224
226
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
225
227
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
226
228
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: [] },
|
|
229
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
230
|
+
GROUP_KEY: null,
|
|
227
231
|
},
|
|
228
232
|
});
|
|
229
233
|
|
|
@@ -238,7 +242,7 @@ CreateNewDatasourceConfig(
|
|
|
238
242
|
|
|
239
243
|
await createNewDatasourceConfig(getSchemaStub)(mockChoices);
|
|
240
244
|
|
|
241
|
-
assert.equal(getAllConfigSetStub.callCount,
|
|
245
|
+
assert.equal(getAllConfigSetStub.callCount, 1, 'getAllConfigSetStub should be called once');
|
|
242
246
|
assert.equal(getConfigStub.callCount, 1, 'getConfigStub should be called once');
|
|
243
247
|
// assert.ok(loggerStub.calledWith('Cannot add multiple datasources when using csv file without a template'));
|
|
244
248
|
if (loggerStub.args.length > 0) {
|
|
@@ -256,15 +260,17 @@ CreateNewDatasourceConfig(
|
|
|
256
260
|
CreateNewDatasourceConfig(
|
|
257
261
|
'should allow adding multiple datasources with CSV format IF template is selected',
|
|
258
262
|
async () => {
|
|
259
|
-
const mockChoices = [{ name: '
|
|
263
|
+
const mockChoices = [{ name: 'testSource', inputType: 'REQ_REP' as DatasourceInputTypes }];
|
|
260
264
|
|
|
261
265
|
const getAllConfigSetStub = sinon.stub(selectors.datasourceConfig, 'getAllConfigSet').returns({
|
|
262
|
-
|
|
263
|
-
KEY: '
|
|
264
|
-
NAME: '
|
|
266
|
+
REQ_REP_existingSource_existingAlias: {
|
|
267
|
+
KEY: 'existingAlias',
|
|
268
|
+
NAME: 'existingSource',
|
|
265
269
|
INPUT_TYPE: 'REQ_REP' as const,
|
|
266
270
|
OUTPUT_TYPE: 'TABLE' as const,
|
|
267
271
|
TRANSFORMER_CONFIGURATION: { INCLUDE_COLUMNS: [] },
|
|
272
|
+
GROUPING_STRATEGY: 'NONE' as const,
|
|
273
|
+
GROUP_KEY: null,
|
|
268
274
|
},
|
|
269
275
|
});
|
|
270
276
|
|
|
@@ -287,14 +293,14 @@ CreateNewDatasourceConfig(
|
|
|
287
293
|
|
|
288
294
|
await createNewDatasourceConfig(getSchemaStub)(mockChoices);
|
|
289
295
|
|
|
290
|
-
assert.ok(getAllConfigSetStub.
|
|
296
|
+
assert.ok(getAllConfigSetStub.calledOnce);
|
|
291
297
|
assert.ok(getSchemaStub.calledOnce);
|
|
292
298
|
assert.ok(initConfigStub.called);
|
|
293
299
|
},
|
|
294
300
|
);
|
|
295
301
|
|
|
296
302
|
CreateNewDatasourceConfig('should successfully create new datasource configuration', async () => {
|
|
297
|
-
const mockChoices = [{ name: '
|
|
303
|
+
const mockChoices = [{ name: 'testSource', inputType: 'REQ_REP' as DatasourceInputTypes }];
|
|
298
304
|
|
|
299
305
|
const getAllConfigSetStub = sinon.stub(selectors.datasourceConfig, 'getAllConfigSet').returns({});
|
|
300
306
|
|
|
@@ -316,16 +322,19 @@ CreateNewDatasourceConfig('should successfully create new datasource configurati
|
|
|
316
322
|
|
|
317
323
|
await createNewDatasourceConfig(getSchemaStub)(mockChoices);
|
|
318
324
|
|
|
319
|
-
assert.ok(getAllConfigSetStub.
|
|
325
|
+
assert.ok(getAllConfigSetStub.calledOnce);
|
|
320
326
|
assert.ok(getSchemaStub.calledOnce);
|
|
321
327
|
assert.ok(
|
|
322
|
-
initConfigStub.calledWith(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
328
|
+
initConfigStub.calledWith(
|
|
329
|
+
sinon.match({
|
|
330
|
+
base: sinon.match({
|
|
331
|
+
KEY: 'testSource',
|
|
332
|
+
NAME: 'testSource',
|
|
333
|
+
INPUT_TYPE: 'REQ_REP',
|
|
334
|
+
}),
|
|
335
|
+
fields: sinon.match.array.deepEquals(['field1', 'field2']),
|
|
336
|
+
}),
|
|
337
|
+
),
|
|
329
338
|
);
|
|
330
339
|
});
|
|
331
340
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Connect, logger } from '@genesislcap/foundation-comms';
|
|
2
2
|
import { actions, selectors } from '../../../store';
|
|
3
3
|
import { Genesis } from '../../../types';
|
|
4
|
-
import { buildDatasourceName } from '../../../utils';
|
|
4
|
+
import { buildDatasourceName, generateUniqueAlias } from '../../../utils';
|
|
5
5
|
import { setDefaultFormatsForDatasource } from '../data-transforms-derived-fields/data-transforms.helpers';
|
|
6
6
|
import { DatasourceChoice } from './types';
|
|
7
7
|
|
|
@@ -10,21 +10,19 @@ export type GetGenesisSchema = (resource: string) => Promise<Genesis.JSONSchema7
|
|
|
10
10
|
export const createNewDatasourceConfig =
|
|
11
11
|
(getSchema: GetGenesisSchema) =>
|
|
12
12
|
async (choices: DatasourceChoice[]): Promise<void> => {
|
|
13
|
+
const allDatasources = selectors.datasourceConfig.getAllConfigSet();
|
|
13
14
|
const validDatasources =
|
|
14
15
|
choices.filter(
|
|
15
|
-
(ds) =>
|
|
16
|
-
!(
|
|
17
|
-
buildDatasourceName(ds.name, ds.inputType) in
|
|
18
|
-
selectors.datasourceConfig.getAllConfigSet()
|
|
19
|
-
),
|
|
16
|
+
(ds) => !(buildDatasourceName(ds.name, ds.name, ds.inputType) in allDatasources),
|
|
20
17
|
) ?? [];
|
|
18
|
+
|
|
21
19
|
if (validDatasources?.length < 1) {
|
|
22
20
|
logger.error('No datasources available to create new config from');
|
|
23
21
|
return;
|
|
24
22
|
}
|
|
25
23
|
const baseConfig = selectors.baseConfig.getConfig();
|
|
26
24
|
if (
|
|
27
|
-
Object.keys(
|
|
25
|
+
Object.keys(allDatasources).length > 0 &&
|
|
28
26
|
baseConfig.OUTPUT_FORMAT === 'CSV' &&
|
|
29
27
|
!baseConfig.DOCUMENT_TEMPLATE_ID
|
|
30
28
|
) {
|
|
@@ -32,13 +30,23 @@ export const createNewDatasourceConfig =
|
|
|
32
30
|
return;
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
KEY: RESOURCE_NAME,
|
|
35
|
+
INPUT_TYPE,
|
|
36
|
+
schema,
|
|
37
|
+
} = await findFirstValidDatasource(getSchema)(validDatasources);
|
|
38
|
+
|
|
39
|
+
// Check for duplicate aliases
|
|
40
|
+
const existingAliases = new Set(Object.values(allDatasources).map((ds) => ds.KEY));
|
|
41
|
+
|
|
42
|
+
const uniqueAlias = generateUniqueAlias(RESOURCE_NAME, existingAliases);
|
|
36
43
|
|
|
37
|
-
const datasourceName = buildDatasourceName(
|
|
44
|
+
const datasourceName = buildDatasourceName(uniqueAlias, RESOURCE_NAME, INPUT_TYPE);
|
|
38
45
|
|
|
39
46
|
actions.datasourceConfig.initDatasourceConfiguration({
|
|
40
47
|
base: {
|
|
41
|
-
KEY,
|
|
48
|
+
KEY: uniqueAlias,
|
|
49
|
+
NAME: RESOURCE_NAME,
|
|
42
50
|
INPUT_TYPE,
|
|
43
51
|
},
|
|
44
52
|
fields: Object.keys(schema.properties!), // Properties is valid as we check in findFirstValidDatasource
|
|
@@ -37,6 +37,12 @@ const container = html<DatasourcesConfigContainer>`
|
|
|
37
37
|
${repeat(
|
|
38
38
|
(_) => Object.keys(selectors.datasourceConfig.getAllConfigSet()),
|
|
39
39
|
html<string, DatasourcesConfigContainer>`
|
|
40
|
+
${when(
|
|
41
|
+
(_, ctx) => !ctx.isFirst,
|
|
42
|
+
html`
|
|
43
|
+
<rapid-divider></rapid-divider>
|
|
44
|
+
`,
|
|
45
|
+
)}
|
|
40
46
|
<datasource-config-item
|
|
41
47
|
:datasourceName=${(x) => x}
|
|
42
48
|
:datasourceChoices=${(_, c) => c.parent.datasourceChoices}
|
|
@@ -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,
|
|
@@ -224,6 +259,22 @@ export const datasourceSlice = createSlice({
|
|
|
224
259
|
const { [column]: _, ...rest } = state[key].TRANSFORMER_CONFIGURATION?.COLUMN_FORMATS ?? {};
|
|
225
260
|
state[key].TRANSFORMER_CONFIGURATION.COLUMN_FORMATS = rest;
|
|
226
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
|
+
},
|
|
227
278
|
},
|
|
228
279
|
selectors: {
|
|
229
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
|
|