datajunction-ui 0.0.1-a105 → 0.0.1-a107
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/OwnersField.jsx +54 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +14 -2
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +14 -0
- package/src/app/pages/AddEditNodePage/index.jsx +31 -2
- package/src/app/pages/NodePage/NodeHistory.jsx +20 -0
- package/src/app/pages/NodePage/NodeInfoTab.jsx +17 -0
- package/src/app/services/DJService.js +5 -0
- package/src/mocks/mockNodes.jsx +6 -0
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner select field
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage } from 'formik';
|
|
5
|
+
import { useContext, useEffect, useState } from 'react';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import { FormikSelect } from './FormikSelect';
|
|
8
|
+
|
|
9
|
+
export const OwnersField = ({ defaultValue }) => {
|
|
10
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
11
|
+
|
|
12
|
+
const [availableUsers, setAvailableUsers] = useState([]);
|
|
13
|
+
const [currentUser, setCurrentUser] = useState(null);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
async function fetchData() {
|
|
17
|
+
const users = await djClient.users();
|
|
18
|
+
setAvailableUsers(
|
|
19
|
+
users.map(user => {
|
|
20
|
+
return {
|
|
21
|
+
value: user.username,
|
|
22
|
+
label: user.username,
|
|
23
|
+
};
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
const current = await djClient.whoami();
|
|
27
|
+
setCurrentUser(current);
|
|
28
|
+
}
|
|
29
|
+
fetchData();
|
|
30
|
+
}, [djClient]);
|
|
31
|
+
|
|
32
|
+
return defaultValue || currentUser ? (
|
|
33
|
+
<div className="NodeCreationInput">
|
|
34
|
+
<ErrorMessage name="owners" component="span" />
|
|
35
|
+
<label htmlFor="Owners">Owners</label>
|
|
36
|
+
<span data-testid="select-owner">
|
|
37
|
+
<FormikSelect
|
|
38
|
+
className="MultiSelectInput"
|
|
39
|
+
defaultValue={
|
|
40
|
+
defaultValue || [
|
|
41
|
+
{ value: currentUser.username, label: currentUser.username },
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
selectOptions={availableUsers}
|
|
45
|
+
formikFieldName="owners"
|
|
46
|
+
placeholder="Select Owners"
|
|
47
|
+
isMulti={true}
|
|
48
|
+
/>
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
) : (
|
|
52
|
+
''
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -103,6 +103,11 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
103
103
|
mocks.metricMetadata,
|
|
104
104
|
);
|
|
105
105
|
|
|
106
|
+
mockDjClient.DataJunctionAPI.whoami.mockReturnValue({
|
|
107
|
+
id: 123,
|
|
108
|
+
username: 'test_user',
|
|
109
|
+
});
|
|
110
|
+
|
|
106
111
|
const element = testElement(mockDjClient);
|
|
107
112
|
const { container } = renderCreateMetric(element);
|
|
108
113
|
|
|
@@ -128,7 +133,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
128
133
|
'default.some_test_metric',
|
|
129
134
|
'Some Test Metric',
|
|
130
135
|
'',
|
|
131
|
-
'SELECT SUM(a) FROM default.repair_orders',
|
|
136
|
+
'SELECT SUM(a) \n FROM default.repair_orders',
|
|
132
137
|
'published',
|
|
133
138
|
'default',
|
|
134
139
|
null,
|
|
@@ -195,6 +200,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
195
200
|
'',
|
|
196
201
|
'',
|
|
197
202
|
undefined,
|
|
203
|
+
['dj'],
|
|
198
204
|
);
|
|
199
205
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
200
206
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledWith(
|
|
@@ -233,6 +239,11 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
233
239
|
{ name: 'intent', display_name: 'Intent' },
|
|
234
240
|
]);
|
|
235
241
|
|
|
242
|
+
mockDjClient.DataJunctionAPI.whoami.mockReturnValue({
|
|
243
|
+
id: 123,
|
|
244
|
+
username: 'test_user',
|
|
245
|
+
});
|
|
246
|
+
|
|
236
247
|
const element = testElement(mockDjClient);
|
|
237
248
|
const { getByTestId } = renderEditNode(element);
|
|
238
249
|
|
|
@@ -250,13 +261,14 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
250
261
|
'default.num_repair_orders',
|
|
251
262
|
'Default: Num Repair Orders!!!',
|
|
252
263
|
'Number of repair orders!!!',
|
|
253
|
-
'SELECT count(repair_order_id) FROM default.repair_orders',
|
|
264
|
+
'SELECT count(repair_order_id) \n FROM default.repair_orders',
|
|
254
265
|
'published',
|
|
255
266
|
['repair_order_id', 'country'],
|
|
256
267
|
'neutral',
|
|
257
268
|
'unitless',
|
|
258
269
|
5,
|
|
259
270
|
undefined,
|
|
271
|
+
['dj'],
|
|
260
272
|
);
|
|
261
273
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
262
274
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledWith(
|
|
@@ -66,6 +66,20 @@ export const initializeMockDJClient = () => {
|
|
|
66
66
|
{ name: 'second', label: 'Second' },
|
|
67
67
|
],
|
|
68
68
|
}),
|
|
69
|
+
users: jest.fn().mockReturnValue([
|
|
70
|
+
{
|
|
71
|
+
id: 123,
|
|
72
|
+
username: 'test_user',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 1111,
|
|
76
|
+
username: 'dj',
|
|
77
|
+
},
|
|
78
|
+
]),
|
|
79
|
+
whoami: jest.fn().mockReturnValue({
|
|
80
|
+
id: 123,
|
|
81
|
+
username: 'test_user',
|
|
82
|
+
}),
|
|
69
83
|
},
|
|
70
84
|
};
|
|
71
85
|
};
|
|
@@ -15,6 +15,7 @@ import { displayMessageAfterSubmit } from '../../../utils/form';
|
|
|
15
15
|
import { NodeQueryField } from './NodeQueryField';
|
|
16
16
|
import { MetricMetadataFields } from './MetricMetadataFields';
|
|
17
17
|
import { UpstreamNodeField } from './UpstreamNodeField';
|
|
18
|
+
import { OwnersField } from './OwnersField';
|
|
18
19
|
import { TagsField } from './TagsField';
|
|
19
20
|
import { NamespaceField } from './NamespaceField';
|
|
20
21
|
import { AlertMessage } from './AlertMessage';
|
|
@@ -50,6 +51,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
50
51
|
description: '',
|
|
51
52
|
primary_key: '',
|
|
52
53
|
mode: 'published',
|
|
54
|
+
owners: [],
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
const validator = values => {
|
|
@@ -112,7 +114,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
112
114
|
values.display_name,
|
|
113
115
|
values.description,
|
|
114
116
|
values.type === 'metric'
|
|
115
|
-
? `SELECT ${values.aggregate_expression} FROM ${values.upstream_node}`
|
|
117
|
+
? `SELECT ${values.aggregate_expression} \n FROM ${values.upstream_node}`
|
|
116
118
|
: values.query,
|
|
117
119
|
values.mode,
|
|
118
120
|
values.namespace,
|
|
@@ -146,7 +148,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
146
148
|
values.display_name,
|
|
147
149
|
values.description,
|
|
148
150
|
values.type === 'metric'
|
|
149
|
-
? `SELECT ${values.aggregate_expression} FROM ${values.upstream_node}`
|
|
151
|
+
? `SELECT ${values.aggregate_expression} \n FROM ${values.upstream_node}`
|
|
150
152
|
: values.query,
|
|
151
153
|
values.mode,
|
|
152
154
|
values.primary_key ? primaryKeyToList(values.primary_key) : null,
|
|
@@ -154,6 +156,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
154
156
|
values.metric_unit,
|
|
155
157
|
values.significant_digits,
|
|
156
158
|
values.required_dimensions,
|
|
159
|
+
values.owners,
|
|
157
160
|
);
|
|
158
161
|
const tagsResponse = await djClient.tagsNode(
|
|
159
162
|
values.name,
|
|
@@ -201,6 +204,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
201
204
|
query: node.current.query,
|
|
202
205
|
tags: node.tags,
|
|
203
206
|
mode: node.current.mode.toLowerCase(),
|
|
207
|
+
owners: node.owners,
|
|
204
208
|
};
|
|
205
209
|
|
|
206
210
|
if (node.type === 'METRIC') {
|
|
@@ -244,6 +248,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
244
248
|
setSelectPrimaryKey,
|
|
245
249
|
setSelectUpstreamNode,
|
|
246
250
|
setSelectRequiredDims,
|
|
251
|
+
setSelectOwners,
|
|
247
252
|
) => {
|
|
248
253
|
// Update fields with existing data to prepare for edit
|
|
249
254
|
const fields = [
|
|
@@ -259,6 +264,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
259
264
|
'metric_unit',
|
|
260
265
|
'metric_direction',
|
|
261
266
|
'significant_digits',
|
|
267
|
+
'owners',
|
|
262
268
|
];
|
|
263
269
|
fields.forEach(field => {
|
|
264
270
|
if (field === 'tags') {
|
|
@@ -266,6 +272,11 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
266
272
|
field,
|
|
267
273
|
data[field].map(tag => tag.name),
|
|
268
274
|
);
|
|
275
|
+
} else if (field === 'owners') {
|
|
276
|
+
setFieldValue(
|
|
277
|
+
field,
|
|
278
|
+
data[field].map(owner => owner.username),
|
|
279
|
+
);
|
|
269
280
|
} else {
|
|
270
281
|
setFieldValue(field, data[field] || '', false);
|
|
271
282
|
}
|
|
@@ -306,6 +317,15 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
306
317
|
}}
|
|
307
318
|
/>,
|
|
308
319
|
);
|
|
320
|
+
if (data.owners) {
|
|
321
|
+
setSelectOwners(
|
|
322
|
+
<OwnersField
|
|
323
|
+
defaultValue={data.owners.map(owner => {
|
|
324
|
+
return { value: owner.username, label: owner.username };
|
|
325
|
+
})}
|
|
326
|
+
/>,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
309
329
|
};
|
|
310
330
|
|
|
311
331
|
return (
|
|
@@ -345,6 +365,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
345
365
|
useState(null);
|
|
346
366
|
const [selectUpstreamNode, setSelectUpstreamNode] =
|
|
347
367
|
useState(null);
|
|
368
|
+
const [selectOwners, setSelectOwners] = useState(null);
|
|
348
369
|
const [selectTags, setSelectTags] = useState(null);
|
|
349
370
|
const [message, setMessage] = useState('');
|
|
350
371
|
|
|
@@ -361,6 +382,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
361
382
|
setSelectPrimaryKey,
|
|
362
383
|
setSelectUpstreamNode,
|
|
363
384
|
setSelectRequiredDims,
|
|
385
|
+
setSelectOwners,
|
|
364
386
|
);
|
|
365
387
|
}
|
|
366
388
|
};
|
|
@@ -393,6 +415,13 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
393
415
|
)}
|
|
394
416
|
<br />
|
|
395
417
|
<br />
|
|
418
|
+
{action === Action.Edit ? (
|
|
419
|
+
selectOwners
|
|
420
|
+
) : (
|
|
421
|
+
<OwnersField />
|
|
422
|
+
)}
|
|
423
|
+
<br />
|
|
424
|
+
<br />
|
|
396
425
|
{nodeType === 'metric' || node.type === 'metric' ? (
|
|
397
426
|
<MetricQueryField
|
|
398
427
|
djClient={djClient}
|
|
@@ -89,6 +89,26 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
if (event.activity_type === 'update' && event.entity_type === 'node') {
|
|
92
|
+
if (event.details?.old_owners) {
|
|
93
|
+
return (
|
|
94
|
+
<div className="history-left">
|
|
95
|
+
<b style={{ textTransform: 'capitalize' }}>Ownership Change</b> for{' '}
|
|
96
|
+
{event.entity_type}{' '}
|
|
97
|
+
<b>
|
|
98
|
+
<a href={'/nodes/' + event.entity_name}>{event.entity_name}</a>
|
|
99
|
+
</b>
|
|
100
|
+
<small>
|
|
101
|
+
{' '}
|
|
102
|
+
to{' '}
|
|
103
|
+
{event.details?.new_owners.map(owner => (
|
|
104
|
+
<span className="badge version" style={{ margin: '2px' }}>
|
|
105
|
+
{owner}
|
|
106
|
+
</span>
|
|
107
|
+
))}
|
|
108
|
+
</small>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
92
112
|
return (
|
|
93
113
|
<div className="history-left">
|
|
94
114
|
<b style={{ textTransform: 'capitalize' }}>{event.activity_type}</b>{' '}
|
|
@@ -256,6 +256,23 @@ export default function NodeInfoTab({ node }) {
|
|
|
256
256
|
>
|
|
257
257
|
{node?.type === 'metric' ? metricsWarning : ''}
|
|
258
258
|
<ListGroupItem label="Description" value={node?.description} />
|
|
259
|
+
<div className="list-group-item d-flex">
|
|
260
|
+
<div className="d-flex gap-2 w-100 justify-content-between py-3">
|
|
261
|
+
<div>
|
|
262
|
+
<h6 className="mb-0 w-100">Owners</h6>
|
|
263
|
+
<p className="mb-0 opacity-75">
|
|
264
|
+
{node?.owners.map(owner => (
|
|
265
|
+
<span
|
|
266
|
+
className="badge node_type__transform"
|
|
267
|
+
style={{ margin: '2px', fontSize: '16px', cursor: 'pointer' }}
|
|
268
|
+
>
|
|
269
|
+
{owner.username}
|
|
270
|
+
</span>
|
|
271
|
+
))}
|
|
272
|
+
</p>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
259
276
|
<div className="list-group-item d-flex">
|
|
260
277
|
<div className="d-flex gap-2 w-100 justify-content-between py-3">
|
|
261
278
|
<div>
|
|
@@ -156,6 +156,9 @@ export const DataJunctionAPI = {
|
|
|
156
156
|
name
|
|
157
157
|
displayName
|
|
158
158
|
}
|
|
159
|
+
owners {
|
|
160
|
+
username
|
|
161
|
+
}
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
`;
|
|
@@ -323,6 +326,7 @@ export const DataJunctionAPI = {
|
|
|
323
326
|
metric_unit,
|
|
324
327
|
significant_digits,
|
|
325
328
|
required_dimensions,
|
|
329
|
+
owners,
|
|
326
330
|
) {
|
|
327
331
|
try {
|
|
328
332
|
const metricMetadata =
|
|
@@ -346,6 +350,7 @@ export const DataJunctionAPI = {
|
|
|
346
350
|
primary_key: primary_key,
|
|
347
351
|
metric_metadata: metricMetadata,
|
|
348
352
|
required_dimensions: required_dimensions,
|
|
353
|
+
owners: owners,
|
|
349
354
|
}),
|
|
350
355
|
credentials: 'include',
|
|
351
356
|
});
|
package/src/mocks/mockNodes.jsx
CHANGED
|
@@ -57,6 +57,7 @@ export const mocks = {
|
|
|
57
57
|
dimension_links: [],
|
|
58
58
|
created_at: '2024-01-24T16:39:14.028077+00:00',
|
|
59
59
|
tags: [],
|
|
60
|
+
owners: [{ username: 'dj' }],
|
|
60
61
|
current_version: 'v1.0',
|
|
61
62
|
missing_table: false,
|
|
62
63
|
},
|
|
@@ -104,6 +105,7 @@ export const mocks = {
|
|
|
104
105
|
],
|
|
105
106
|
created_at: '2023-08-21T16:48:56.841631+00:00',
|
|
106
107
|
tags: [{ name: 'purpose', display_name: 'Purpose' }],
|
|
108
|
+
owners: [{ username: 'dj' }],
|
|
107
109
|
dimension_links: [],
|
|
108
110
|
incompatible_druid_functions: ['IF'],
|
|
109
111
|
dimensions: [
|
|
@@ -302,6 +304,7 @@ export const mocks = {
|
|
|
302
304
|
mode: 'PUBLISHED',
|
|
303
305
|
},
|
|
304
306
|
tags: [],
|
|
307
|
+
owners: [{ username: 'dj' }],
|
|
305
308
|
},
|
|
306
309
|
|
|
307
310
|
mockGetMetricNode: {
|
|
@@ -331,6 +334,7 @@ export const mocks = {
|
|
|
331
334
|
mode: 'PUBLISHED',
|
|
332
335
|
},
|
|
333
336
|
tags: [{ name: 'purpose', displayName: 'Purpose' }],
|
|
337
|
+
owners: [{ username: 'dj' }],
|
|
334
338
|
},
|
|
335
339
|
|
|
336
340
|
mockGetTransformNode: {
|
|
@@ -352,6 +356,7 @@ export const mocks = {
|
|
|
352
356
|
mode: 'PUBLISHED',
|
|
353
357
|
},
|
|
354
358
|
tags: [],
|
|
359
|
+
owners: [{ username: 'dj' }],
|
|
355
360
|
},
|
|
356
361
|
|
|
357
362
|
attributes: [
|
|
@@ -789,6 +794,7 @@ export const mocks = {
|
|
|
789
794
|
],
|
|
790
795
|
created_at: '2023-08-21T16:49:01.671395+00:00',
|
|
791
796
|
tags: [],
|
|
797
|
+
owners: [{ username: 'dj' }],
|
|
792
798
|
},
|
|
793
799
|
mockCubesCube: {
|
|
794
800
|
node_revision_id: 33,
|