datajunction-ui 0.0.1-a106 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datajunction-ui",
3
- "version": "0.0.1a106",
3
+ "version": "0.0.1a107",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -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
  });
@@ -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,