datajunction-ui 0.0.9 → 0.0.11
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/package.json +1 -1
- package/src/app/pages/AddEditNodePage/CustomMetadataField.jsx +61 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +1 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +4 -0
- package/src/app/pages/AddEditNodePage/index.jsx +9 -0
- package/src/app/pages/NodePage/NodeInfoTab.jsx +21 -0
- package/src/app/services/DJService.js +6 -1
- package/src/app/services/__tests__/DJService.test.jsx +13 -0
- package/src/mocks/mockNodes.jsx +3 -0
package/package.json
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom metadata field component for nodes
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage, Field } from 'formik';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
|
|
7
|
+
export const CustomMetadataField = ({ initialValue = {} }) => {
|
|
8
|
+
const [jsonString, setJsonString] = useState(
|
|
9
|
+
JSON.stringify(initialValue, null, 2),
|
|
10
|
+
);
|
|
11
|
+
const [error, setError] = useState('');
|
|
12
|
+
|
|
13
|
+
const handleChange = (e, setFieldValue) => {
|
|
14
|
+
const value = e.target.value;
|
|
15
|
+
setJsonString(value);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
if (value.trim() === '') {
|
|
19
|
+
setFieldValue('custom_metadata', null);
|
|
20
|
+
setError('');
|
|
21
|
+
} else {
|
|
22
|
+
const parsed = JSON.parse(value);
|
|
23
|
+
setFieldValue('custom_metadata', parsed);
|
|
24
|
+
setError('');
|
|
25
|
+
}
|
|
26
|
+
} catch (err) {
|
|
27
|
+
setError('Invalid JSON format');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="NodeCreationInput" style={{ marginTop: '20px' }}>
|
|
33
|
+
<ErrorMessage name="custom_metadata" component="span" />
|
|
34
|
+
<label htmlFor="CustomMetadata">Custom Metadata (JSON)</label>
|
|
35
|
+
<Field name="custom_metadata">
|
|
36
|
+
{({ field, form }) => (
|
|
37
|
+
<div>
|
|
38
|
+
<textarea
|
|
39
|
+
id="CustomMetadata"
|
|
40
|
+
value={jsonString}
|
|
41
|
+
onChange={e => handleChange(e, form.setFieldValue)}
|
|
42
|
+
style={{
|
|
43
|
+
width: '100%',
|
|
44
|
+
minHeight: '100px',
|
|
45
|
+
fontFamily: 'monospace',
|
|
46
|
+
fontSize: '12px',
|
|
47
|
+
padding: '8px',
|
|
48
|
+
border: error ? '1px solid red' : '1px solid #ccc',
|
|
49
|
+
borderRadius: '4px',
|
|
50
|
+
}}
|
|
51
|
+
placeholder='{"key": "value"}'
|
|
52
|
+
/>
|
|
53
|
+
{error && (
|
|
54
|
+
<span style={{ color: 'red', fontSize: '12px' }}>{error}</span>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</Field>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -67,6 +67,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
67
67
|
undefined,
|
|
68
68
|
undefined,
|
|
69
69
|
undefined,
|
|
70
|
+
null,
|
|
70
71
|
);
|
|
71
72
|
expect(screen.getByText(/default.some_test_dim/)).toBeInTheDocument();
|
|
72
73
|
});
|
|
@@ -140,6 +141,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
140
141
|
undefined,
|
|
141
142
|
undefined,
|
|
142
143
|
undefined,
|
|
144
|
+
null,
|
|
143
145
|
);
|
|
144
146
|
expect(
|
|
145
147
|
screen.getByText(/default.some_test_metric/),
|
|
@@ -201,6 +203,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
201
203
|
'',
|
|
202
204
|
undefined,
|
|
203
205
|
['dj'],
|
|
206
|
+
'', // custom_metadata is set to '' when null in the form
|
|
204
207
|
);
|
|
205
208
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
206
209
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledWith(
|
|
@@ -269,6 +272,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
269
272
|
5,
|
|
270
273
|
undefined,
|
|
271
274
|
['dj'],
|
|
275
|
+
{ key1: 'value1', key2: 'value2' },
|
|
272
276
|
);
|
|
273
277
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
274
278
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledWith(
|
|
@@ -25,6 +25,7 @@ import { NodeModeField } from './NodeModeField';
|
|
|
25
25
|
import { RequiredDimensionsSelect } from './RequiredDimensionsSelect';
|
|
26
26
|
import LoadingIcon from '../../icons/LoadingIcon';
|
|
27
27
|
import { ColumnsSelect } from './ColumnsSelect';
|
|
28
|
+
import { CustomMetadataField } from './CustomMetadataField';
|
|
28
29
|
|
|
29
30
|
class Action {
|
|
30
31
|
static Add = new Action('add');
|
|
@@ -52,6 +53,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
52
53
|
primary_key: '',
|
|
53
54
|
mode: 'published',
|
|
54
55
|
owners: [],
|
|
56
|
+
custom_metadata: null,
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
const validator = values => {
|
|
@@ -122,6 +124,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
122
124
|
values.metric_direction,
|
|
123
125
|
values.metric_unit,
|
|
124
126
|
values.required_dimensions,
|
|
127
|
+
values.custom_metadata,
|
|
125
128
|
);
|
|
126
129
|
if (status === 200 || status === 201) {
|
|
127
130
|
if (values.tags) {
|
|
@@ -157,6 +160,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
157
160
|
values.significant_digits,
|
|
158
161
|
values.required_dimensions,
|
|
159
162
|
values.owners,
|
|
163
|
+
values.custom_metadata,
|
|
160
164
|
);
|
|
161
165
|
const tagsResponse = await djClient.tagsNode(
|
|
162
166
|
values.name,
|
|
@@ -205,6 +209,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
205
209
|
tags: node.tags,
|
|
206
210
|
mode: node.current.mode.toLowerCase(),
|
|
207
211
|
owners: node.owners,
|
|
212
|
+
custom_metadata: node.current.customMetadata,
|
|
208
213
|
};
|
|
209
214
|
|
|
210
215
|
if (node.type === 'METRIC') {
|
|
@@ -265,6 +270,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
265
270
|
'metric_direction',
|
|
266
271
|
'significant_digits',
|
|
267
272
|
'owners',
|
|
273
|
+
'custom_metadata',
|
|
268
274
|
];
|
|
269
275
|
fields.forEach(field => {
|
|
270
276
|
if (field === 'tags') {
|
|
@@ -443,6 +449,9 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
443
449
|
) : (
|
|
444
450
|
''
|
|
445
451
|
)}
|
|
452
|
+
<CustomMetadataField
|
|
453
|
+
initialValue={node.custom_metadata || {}}
|
|
454
|
+
/>
|
|
446
455
|
{nodeType !== 'metric' && node.type !== 'metric' ? (
|
|
447
456
|
action === Action.Edit ? (
|
|
448
457
|
selectPrimaryKey
|
|
@@ -198,6 +198,26 @@ export default function NodeInfoTab({ node }) {
|
|
|
198
198
|
''
|
|
199
199
|
);
|
|
200
200
|
|
|
201
|
+
const customMetadataDiv =
|
|
202
|
+
node?.custom_metadata && Object.keys(node.custom_metadata).length > 0 ? (
|
|
203
|
+
<div className="list-group-item d-flex">
|
|
204
|
+
<div className="d-flex gap-2 w-100 justify-content-between py-3">
|
|
205
|
+
<div
|
|
206
|
+
style={{
|
|
207
|
+
width: window.innerWidth * 0.8,
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<h6 className="mb-0 w-100">Custom Metadata</h6>
|
|
211
|
+
<SyntaxHighlighter language="json" style={foundation}>
|
|
212
|
+
{JSON.stringify(node.custom_metadata, null, 2)}
|
|
213
|
+
</SyntaxHighlighter>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
) : (
|
|
218
|
+
''
|
|
219
|
+
);
|
|
220
|
+
|
|
201
221
|
const cubeElementsDiv = node?.cube_elements ? (
|
|
202
222
|
<div className="list-group-item d-flex">
|
|
203
223
|
<div className="d-flex gap-2 w-100 justify-content-between py-3">
|
|
@@ -357,6 +377,7 @@ export default function NodeInfoTab({ node }) {
|
|
|
357
377
|
{metricMetadataDiv}
|
|
358
378
|
{node?.type !== 'cube' && node?.type !== 'metric' ? queryDiv : ''}
|
|
359
379
|
{node?.type === 'metric' ? metricQueryDiv : ''}
|
|
380
|
+
{customMetadataDiv}
|
|
360
381
|
{cubeElementsDiv}
|
|
361
382
|
</div>
|
|
362
383
|
);
|
|
@@ -285,8 +285,9 @@ export const DataJunctionAPI = {
|
|
|
285
285
|
name
|
|
286
286
|
}
|
|
287
287
|
mode
|
|
288
|
+
customMetadata
|
|
288
289
|
}
|
|
289
|
-
tags {
|
|
290
|
+
tags {
|
|
290
291
|
name
|
|
291
292
|
displayName
|
|
292
293
|
}
|
|
@@ -471,6 +472,7 @@ export const DataJunctionAPI = {
|
|
|
471
472
|
metric_direction,
|
|
472
473
|
metric_unit,
|
|
473
474
|
required_dimensions,
|
|
475
|
+
custom_metadata,
|
|
474
476
|
) {
|
|
475
477
|
const metricMetadata =
|
|
476
478
|
metric_direction || metric_unit
|
|
@@ -494,6 +496,7 @@ export const DataJunctionAPI = {
|
|
|
494
496
|
primary_key: primary_key,
|
|
495
497
|
metric_metadata: metricMetadata,
|
|
496
498
|
required_dimensions: required_dimensions,
|
|
499
|
+
custom_metadata: custom_metadata,
|
|
497
500
|
}),
|
|
498
501
|
credentials: 'include',
|
|
499
502
|
});
|
|
@@ -512,6 +515,7 @@ export const DataJunctionAPI = {
|
|
|
512
515
|
significant_digits,
|
|
513
516
|
required_dimensions,
|
|
514
517
|
owners,
|
|
518
|
+
custom_metadata,
|
|
515
519
|
) {
|
|
516
520
|
try {
|
|
517
521
|
const metricMetadata =
|
|
@@ -536,6 +540,7 @@ export const DataJunctionAPI = {
|
|
|
536
540
|
metric_metadata: metricMetadata,
|
|
537
541
|
required_dimensions: required_dimensions,
|
|
538
542
|
owners: owners,
|
|
543
|
+
custom_metadata: custom_metadata,
|
|
539
544
|
}),
|
|
540
545
|
credentials: 'include',
|
|
541
546
|
});
|
|
@@ -154,6 +154,10 @@ describe('DataJunctionAPI', () => {
|
|
|
154
154
|
'mode',
|
|
155
155
|
'namespace',
|
|
156
156
|
'primary_key',
|
|
157
|
+
undefined, // metric_direction
|
|
158
|
+
undefined, // metric_unit
|
|
159
|
+
undefined, // required_dimensions
|
|
160
|
+
{ key: 'value' }, // custom_metadata
|
|
157
161
|
];
|
|
158
162
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
159
163
|
await DataJunctionAPI.createNode(...sampleArgs);
|
|
@@ -171,6 +175,8 @@ describe('DataJunctionAPI', () => {
|
|
|
171
175
|
namespace: sampleArgs[6],
|
|
172
176
|
primary_key: sampleArgs[7],
|
|
173
177
|
metric_metadata: null,
|
|
178
|
+
required_dimensions: undefined,
|
|
179
|
+
custom_metadata: { key: 'value' },
|
|
174
180
|
}),
|
|
175
181
|
credentials: 'include',
|
|
176
182
|
});
|
|
@@ -186,6 +192,10 @@ describe('DataJunctionAPI', () => {
|
|
|
186
192
|
'primary_key',
|
|
187
193
|
'neutral',
|
|
188
194
|
'',
|
|
195
|
+
null, // significant_digits
|
|
196
|
+
undefined, // required_dimensions
|
|
197
|
+
undefined, // owners
|
|
198
|
+
{ key: 'value' }, // custom_metadata
|
|
189
199
|
];
|
|
190
200
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
191
201
|
await DataJunctionAPI.patchNode(...sampleArgs);
|
|
@@ -205,6 +215,9 @@ describe('DataJunctionAPI', () => {
|
|
|
205
215
|
unit: '',
|
|
206
216
|
significant_digits: null,
|
|
207
217
|
},
|
|
218
|
+
required_dimensions: undefined,
|
|
219
|
+
owners: undefined,
|
|
220
|
+
custom_metadata: { key: 'value' },
|
|
208
221
|
}),
|
|
209
222
|
credentials: 'include',
|
|
210
223
|
});
|
package/src/mocks/mockNodes.jsx
CHANGED
|
@@ -108,6 +108,7 @@ export const mocks = {
|
|
|
108
108
|
owners: [{ username: 'dj' }],
|
|
109
109
|
dimension_links: [],
|
|
110
110
|
incompatible_druid_functions: ['IF'],
|
|
111
|
+
custom_metadata: { key1: 'value1', key2: 'value2' },
|
|
111
112
|
dimensions: [
|
|
112
113
|
{
|
|
113
114
|
value: 'default.date_dim.dateint',
|
|
@@ -332,6 +333,7 @@ export const mocks = {
|
|
|
332
333
|
},
|
|
333
334
|
requiredDimensions: [],
|
|
334
335
|
mode: 'PUBLISHED',
|
|
336
|
+
customMetadata: { key1: 'value1', key2: 'value2' },
|
|
335
337
|
},
|
|
336
338
|
tags: [{ name: 'purpose', displayName: 'Purpose' }],
|
|
337
339
|
owners: [{ username: 'dj' }],
|
|
@@ -354,6 +356,7 @@ export const mocks = {
|
|
|
354
356
|
metricMetadata: null,
|
|
355
357
|
requiredDimensions: [],
|
|
356
358
|
mode: 'PUBLISHED',
|
|
359
|
+
customMetadata: null,
|
|
357
360
|
},
|
|
358
361
|
tags: [],
|
|
359
362
|
owners: [{ username: 'dj' }],
|