datajunction-ui 0.0.9 → 0.0.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datajunction-ui",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -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
+ };
@@ -55,6 +55,7 @@ describe('AddEditNodePage submission failed', () => {
55
55
  undefined,
56
56
  undefined,
57
57
  undefined,
58
+ null,
58
59
  );
59
60
  expect(
60
61
  screen.getByText(/Some columns in the primary key \[] were not found/),
@@ -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
  });
@@ -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' }],