datajunction-ui 0.0.1-a40.dev → 0.0.1-a41

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.1-a40.dev",
3
+ "version": "0.0.1a41",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -1,8 +1,10 @@
1
1
  import { useContext, useEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import DJClientContext from '../../providers/djclient';
4
- import { Field, Form, Formik } from 'formik';
5
- import { displayMessageAfterSubmit } from '../../../utils/form';
4
+ import { ErrorMessage, Field, Form, Formik } from 'formik';
5
+ import { FormikSelect } from '../AddEditNodePage/FormikSelect';
6
+ import EditIcon from '../../icons/EditIcon';
7
+ import { displayMessageAfterSubmit, labelize } from '../../../utils/form';
6
8
 
7
9
  export default function AddBackfillPopover({
8
10
  node,
@@ -5,8 +5,6 @@ import EditColumnPopover from './EditColumnPopover';
5
5
  import LinkDimensionPopover from './LinkDimensionPopover';
6
6
  import { labelize } from '../../../utils/form';
7
7
  import PartitionColumnPopover from './PartitionColumnPopover';
8
- import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
9
- import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
10
8
 
11
9
  export default function NodeColumnTab({ node, djClient }) {
12
10
  const [attributes, setAttributes] = useState([]);
@@ -177,65 +175,26 @@ export default function NodeColumnTab({ node, djClient }) {
177
175
  };
178
176
 
179
177
  return (
180
- <>
181
- <div className="table-responsive">
182
- <table className="card-inner-table table">
183
- <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
184
- <tr>
185
- <th className="text-start">Column</th>
186
- <th>Display Name</th>
187
- <th>Type</th>
188
- {node?.type !== 'cube' ? (
189
- <>
190
- <th>Linked Dimension</th>
191
- <th>Attributes</th>
192
- </>
193
- ) : (
194
- ''
195
- )}
196
- <th>Partition</th>
197
- </tr>
198
- </thead>
199
- <tbody>{columnList(columns)}</tbody>
200
- </table>
201
- </div>
202
- <div>
203
- <h3>Linked Dimensions (Custom Join SQL)</h3>
204
- <table className="card-inner-table table">
205
- <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
206
- <tr>
207
- <th className="text-start">Dimension Node</th>
208
- <th>Join Type</th>
209
- <th>Join SQL</th>
210
- <th>Role</th>
211
- </tr>
212
- </thead>
213
- <tbody>
214
- {node?.dimension_links.map(link => {
215
- return (
216
- <tr>
217
- <td>
218
- <a href={'/nodes/' + link.dimension.name}>
219
- {link.dimension.name}
220
- </a>
221
- </td>
222
- <td>{link.join_type.toUpperCase()}</td>
223
- <td style={{ width: '25rem', maxWidth: 'none' }}>
224
- <SyntaxHighlighter
225
- language="sql"
226
- style={foundation}
227
- wrapLongLines={true}
228
- >
229
- {link.join_sql}
230
- </SyntaxHighlighter>
231
- </td>
232
- <td>{link.role}</td>
233
- </tr>
234
- );
235
- })}
236
- </tbody>
237
- </table>
238
- </div>
239
- </>
178
+ <div className="table-responsive">
179
+ <table className="card-inner-table table">
180
+ <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
181
+ <tr>
182
+ <th className="text-start">Column</th>
183
+ <th>Display Name</th>
184
+ <th>Type</th>
185
+ {node?.type !== 'cube' ? (
186
+ <>
187
+ <th>Linked Dimension</th>
188
+ <th>Attributes</th>
189
+ </>
190
+ ) : (
191
+ ''
192
+ )}
193
+ <th>Partition</th>
194
+ </tr>
195
+ </thead>
196
+ <tbody>{columnList(columns)}</tbody>
197
+ </table>
198
+ </div>
240
199
  );
241
200
  }
@@ -1,7 +1,4 @@
1
1
  import { useEffect, useState } from 'react';
2
- import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
3
- import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
4
- import * as React from 'react';
5
2
 
6
3
  export default function NodeHistory({ node, djClient }) {
7
4
  const [history, setHistory] = useState([]);
@@ -178,15 +175,7 @@ export default function NodeHistory({ node, djClient }) {
178
175
  </td>
179
176
  <td>{revision.display_name}</td>
180
177
  <td>{revision.description}</td>
181
- <td>
182
- <SyntaxHighlighter
183
- language="sql"
184
- style={foundation}
185
- wrapLongLines={true}
186
- >
187
- {revision.query}
188
- </SyntaxHighlighter>
189
- </td>
178
+ <td>{revision.query}</td>
190
179
  <td>{revision.tags}</td>
191
180
  </tr>
192
181
  ));
@@ -68,7 +68,6 @@ describe('<NodePage />', () => {
68
68
  query:
69
69
  'SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n',
70
70
  availability: null,
71
- dimension_links: [],
72
71
  columns: [
73
72
  {
74
73
  name: 'default_DOT_avg_repair_price',
@@ -46,53 +46,10 @@ exports[`<NodePage /> renders the NodeHistory tab correctly 1`] = `
46
46
  Average repair price
47
47
  </td>
48
48
  <td>
49
- <pre
50
- style="display: block; overflow-x: auto; padding: 2rem; background: rgb(238, 238, 238); color: black;"
51
- >
52
- <code
53
- class="language-sql"
54
- style="white-space: pre-wrap;"
55
- >
56
- <span>
57
- <span
58
- style="color: rgb(0, 153, 153);"
59
- >
60
- SELECT
61
- </span>
62
- <span>
63
-
64
- </span>
65
- <span
66
- class="hljs-built_in"
67
- >
68
- avg
69
- </span>
70
- <span>
71
- (price) default_DOT_avg_repair_price
72
-
73
- </span>
74
- </span>
75
- <span>
76
- <span>
77
-
78
- </span>
79
- <span
80
- style="color: rgb(0, 153, 153);"
81
- >
82
- FROM
83
- </span>
84
- <span>
85
- default.repair_order_details
49
+ SELECT avg(price) default_DOT_avg_repair_price
50
+ FROM default.repair_order_details
86
51
 
87
- </span>
88
- </span>
89
- <span>
90
-
91
52
 
92
- </span>
93
- <span />
94
- </code>
95
- </pre>
96
53
  </td>
97
54
  <td />
98
55
  </tr>
@@ -15,7 +15,6 @@ import NodesWithDimension from './NodesWithDimension';
15
15
  import NodeColumnLineage from './NodeLineageTab';
16
16
  import EditIcon from '../../icons/EditIcon';
17
17
  import AlertIcon from '../../icons/AlertIcon';
18
- import NodeDimensionsTab from "./NodeDimensionsTab";
19
18
 
20
19
  export function NodePage() {
21
20
  const djClient = useContext(DJClientContext).DataJunctionAPI;
@@ -105,11 +104,6 @@ export function NodePage() {
105
104
  name: 'Lineage',
106
105
  display: node?.type === 'metric',
107
106
  },
108
- {
109
- id: 8,
110
- name: 'Dimensions',
111
- display: node?.type !== 'cube',
112
- },
113
107
  ];
114
108
  };
115
109
 
@@ -143,9 +137,6 @@ export function NodePage() {
143
137
  case 7:
144
138
  tabToDisplay = <NodeColumnLineage djNode={node} djClient={djClient} />;
145
139
  break;
146
- case 8:
147
- tabToDisplay = <NodeDimensionsTab node={node} djClient={djClient} />;
148
- break;
149
140
  default: /* istanbul ignore next */
150
141
  tabToDisplay = <NodeInfoTab node={node} />;
151
142
  }
@@ -543,13 +543,6 @@ export const DataJunctionAPI = {
543
543
  })
544
544
  ).json();
545
545
  },
546
- nodeDimensions: async function (nodeName) {
547
- return await (
548
- await fetch(`${DJ_URL}/nodes/${nodeName}/dimensions`, {
549
- credentials: 'include',
550
- })
551
- ).json();
552
- },
553
546
  linkDimension: async function (nodeName, columnName, dimensionName) {
554
547
  const response = await fetch(
555
548
  `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
@@ -43,7 +43,6 @@ export const mocks = {
43
43
  ],
44
44
  created_at: '2023-08-21T16:48:56.841631+00:00',
45
45
  tags: [{ name: 'purpose', display_name: 'Purpose' }],
46
- dimension_links: [],
47
46
  dimensions: [
48
47
  {
49
48
  value: 'default.date_dim.dateint',
@@ -1,7 +1,6 @@
1
1
  @import url('https://fonts.googleapis.com/css?family=Jost');
2
2
  @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;600&display=swap');
3
3
  @import url('https://fonts.googleapis.com/css?family=Lato');
4
- @import url('https://fonts.googleapis.com/css?family=Consolas');
5
4
 
6
5
  body {
7
6
  margin: 0;
@@ -1092,18 +1091,3 @@ pre {
1092
1091
  .partitionLink:hover {
1093
1092
  text-decoration: none;
1094
1093
  }
1095
-
1096
-
1097
- .dimensionsList {
1098
- padding: 12px;
1099
- opacity: 1;
1100
- border-radius: 0.5rem;
1101
- line-height: 1.55rem;
1102
- font-size: 0.95rem;
1103
- }
1104
-
1105
- .DimensionAttribute {
1106
- background: #effcff;
1107
- padding: 5px;
1108
- font-family: Consolas, serif;
1109
- }
package/dj.internal.db DELETED
File without changes
@@ -1,80 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import * as React from 'react';
3
- import { labelize } from '../../../utils/form';
4
-
5
- export default function NodeDimensionsTab({ node, djClient }) {
6
- const [dimensions, setDimensions] = useState([]);
7
- useEffect(() => {
8
- const fetchData = async () => {
9
- if (node) {
10
- const data = await djClient.nodeDimensions(node.name);
11
- const grouped = Object.entries(
12
- data.reduce((group, dimension) => {
13
- group[dimension.node_name + dimension.path] =
14
- group[dimension.node_name + dimension.path] ?? [];
15
- group[dimension.node_name + dimension.path].push(dimension);
16
- return group;
17
- }, {}),
18
- );
19
- setDimensions(grouped);
20
- }
21
- };
22
- fetchData().catch(console.error);
23
- }, [djClient, node]);
24
-
25
- // Builds the block of dimensions selectors, grouped by node name + path
26
- return (
27
- <div style={{ padding: '1rem' }}>
28
- {dimensions.map(grouping => {
29
- const dimensionsInGroup = grouping[1];
30
- const role = dimensionsInGroup[0].path
31
- .map(pathItem => pathItem.split('.').slice(-1))
32
- .join(' → ');
33
- const fullPath = dimensionsInGroup[0].path.join(' → ');
34
- const groupHeader = (
35
- <h4
36
- style={{
37
- fontWeight: 'normal',
38
- marginBottom: '5px',
39
- marginTop: '15px',
40
- }}
41
- >
42
- <a href={`/nodes/${dimensionsInGroup[0].node_name}`}>
43
- <b>{dimensionsInGroup[0].node_display_name}</b>
44
- </a>{' '}
45
- with role{' '}
46
- <span className="HighlightPath">
47
- <b>{role}</b>
48
- </span>{' '}
49
- via <span className="HighlightPath">{fullPath}</span>
50
- </h4>
51
- );
52
- const dimensionGroupOptions = dimensionsInGroup.map(dim => {
53
- return {
54
- value: dim.name,
55
- label:
56
- labelize(dim.name.split('.').slice(-1)[0]) +
57
- (dim.is_primary_key ? ' (PK)' : ''),
58
- };
59
- });
60
- return (
61
- <>
62
- {groupHeader}
63
- <div className="dimensionsList">
64
- {dimensionGroupOptions.map(dimension => {
65
- return (
66
- <div>
67
- {dimension.label.split('[').slice(0)[0]} ⇢{' '}
68
- <code className="DimensionAttribute">
69
- {dimension.value}
70
- </code>
71
- </div>
72
- );
73
- })}
74
- </div>
75
- </>
76
- );
77
- })}
78
- </div>
79
- );
80
- }
@@ -1,162 +0,0 @@
1
- import React from 'react';
2
- import { render, waitFor, screen } from '@testing-library/react';
3
- import NodeColumnTab from '../NodeColumnTab';
4
-
5
- describe('<NodeColumnTab />', () => {
6
- const mockDjClient = {
7
- node: jest.fn(),
8
- columns: jest.fn(),
9
- attributes: jest.fn(),
10
- dimensions: jest.fn(),
11
- };
12
-
13
- const mockNodeColumns = [
14
- {
15
- name: 'repair_order_id',
16
- display_name: 'Repair Order Id',
17
- type: 'int',
18
- attributes: [],
19
- dimension: null,
20
- },
21
- {
22
- name: 'municipality_id',
23
- display_name: 'Municipality Id',
24
- type: 'string',
25
- attributes: [],
26
- dimension: null,
27
- },
28
- {
29
- name: 'hard_hat_id',
30
- display_name: 'Hard Hat Id',
31
- type: 'int',
32
- attributes: [],
33
- dimension: null,
34
- },
35
- {
36
- name: 'order_date',
37
- display_name: 'Order Date',
38
- type: 'date',
39
- attributes: [],
40
- dimension: null,
41
- },
42
- {
43
- name: 'required_date',
44
- display_name: 'Required Date',
45
- type: 'date',
46
- attributes: [],
47
- dimension: null,
48
- },
49
- {
50
- name: 'dispatched_date',
51
- display_name: 'Dispatched Date',
52
- type: 'date',
53
- attributes: [],
54
- dimension: null,
55
- },
56
- {
57
- name: 'dispatcher_id',
58
- display_name: 'Dispatcher Id',
59
- type: 'int',
60
- attributes: [],
61
- dimension: null,
62
- },
63
- ];
64
-
65
- const mockNode = {
66
- node_revision_id: 1,
67
- node_id: 1,
68
- type: 'source',
69
- name: 'default.repair_orders',
70
- display_name: 'Default: Repair Orders',
71
- version: 'v1.0',
72
- status: 'valid',
73
- mode: 'published',
74
- catalog: {
75
- id: 1,
76
- uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
77
- created_at: '2023-08-21T16:48:51.146121+00:00',
78
- updated_at: '2023-08-21T16:48:51.146122+00:00',
79
- extra_params: {},
80
- name: 'warehouse',
81
- },
82
- schema_: 'roads',
83
- table: 'repair_orders',
84
- description: 'Repair orders',
85
- query: null,
86
- availability: null,
87
- columns: mockNodeColumns,
88
- updated_at: '2023-08-21T16:48:52.880498+00:00',
89
- materializations: [],
90
- parents: [],
91
- dimension_links: [
92
- {
93
- dimension: {
94
- name: 'default.contractor',
95
- },
96
- join_type: 'left',
97
- join_sql:
98
- 'default.contractor.contractor_id = default.repair_orders.contractor_id',
99
- join_cardinality: 'one_to_one',
100
- role: 'contractor',
101
- },
102
- ],
103
- };
104
-
105
- const mockAttributes = [
106
- {
107
- uniqueness_scope: [],
108
- namespace: 'system',
109
- name: 'primary_key',
110
- description:
111
- 'Points to a column which is part of the primary key of the node',
112
- allowed_node_types: ['source', 'transform', 'dimension'],
113
- id: 1,
114
- },
115
- {
116
- uniqueness_scope: [],
117
- namespace: 'system',
118
- name: 'dimension',
119
- description: 'Points to a dimension attribute column',
120
- allowed_node_types: ['source', 'transform'],
121
- id: 2,
122
- },
123
- ];
124
-
125
- const mockDimensions = ['default.contractor', 'default.hard_hat'];
126
-
127
- beforeEach(() => {
128
- // Reset the mocks before each test
129
- mockDjClient.node.mockReset();
130
- mockDjClient.columns.mockReset();
131
- mockDjClient.attributes.mockReset();
132
- mockDjClient.dimensions.mockReset();
133
- });
134
-
135
- it('renders node columns and dimension links', async () => {
136
- mockDjClient.node.mockReturnValue(mockNode);
137
- mockDjClient.columns.mockReturnValue(mockNodeColumns);
138
- mockDjClient.attributes.mockReturnValue(mockAttributes);
139
- mockDjClient.dimensions.mockReturnValue(mockDimensions);
140
-
141
- render(<NodeColumnTab node={mockNode} djClient={mockDjClient} />);
142
-
143
- await waitFor(() => {
144
- // Displays the columns
145
- for (const column of mockNode.columns) {
146
- expect(screen.getByText(column.name)).toBeInTheDocument();
147
- expect(screen.getByText(column.display_name)).toBeInTheDocument();
148
- }
149
-
150
- // Displays the dimension links
151
- for (const dimensionLink of mockNode.dimension_links) {
152
- const link = screen
153
- .getByText(dimensionLink.dimension.name)
154
- .closest('a');
155
- expect(link).toHaveAttribute(
156
- 'href',
157
- `/nodes/${dimensionLink.dimension.name}`,
158
- );
159
- }
160
- });
161
- });
162
- });
@@ -1,145 +0,0 @@
1
- import React from 'react';
2
- import { render, waitFor, screen } from '@testing-library/react';
3
- import NodeDimensionsTab from '../NodeDimensionsTab';
4
-
5
- describe('<NodeDimensionsTab />', () => {
6
- const mockDjClient = {
7
- node: jest.fn(),
8
- nodeDimensions: jest.fn(),
9
- };
10
-
11
- const mockNode = {
12
- node_revision_id: 1,
13
- node_id: 1,
14
- type: 'source',
15
- name: 'default.repair_orders',
16
- display_name: 'Default: Repair Orders',
17
- version: 'v1.0',
18
- status: 'valid',
19
- mode: 'published',
20
- catalog: {
21
- id: 1,
22
- uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
23
- created_at: '2023-08-21T16:48:51.146121+00:00',
24
- updated_at: '2023-08-21T16:48:51.146122+00:00',
25
- extra_params: {},
26
- name: 'warehouse',
27
- },
28
- schema_: 'roads',
29
- table: 'repair_orders',
30
- description: 'Repair orders',
31
- query: null,
32
- availability: null,
33
- columns: [
34
- {
35
- name: 'repair_order_id',
36
- type: 'int',
37
- attributes: [],
38
- dimension: null,
39
- },
40
- {
41
- name: 'municipality_id',
42
- type: 'string',
43
- attributes: [],
44
- dimension: null,
45
- },
46
- {
47
- name: 'hard_hat_id',
48
- type: 'int',
49
- attributes: [],
50
- dimension: null,
51
- },
52
- {
53
- name: 'order_date',
54
- type: 'date',
55
- attributes: [],
56
- dimension: null,
57
- },
58
- {
59
- name: 'required_date',
60
- type: 'date',
61
- attributes: [],
62
- dimension: null,
63
- },
64
- {
65
- name: 'dispatched_date',
66
- type: 'date',
67
- attributes: [],
68
- dimension: null,
69
- },
70
- {
71
- name: 'dispatcher_id',
72
- type: 'int',
73
- attributes: [],
74
- dimension: null,
75
- },
76
- ],
77
- updated_at: '2023-08-21T16:48:52.880498+00:00',
78
- materializations: [],
79
- parents: [],
80
- dimension_links: [
81
- {
82
- dimension: {
83
- name: 'default.contractor',
84
- },
85
- join_type: 'left',
86
- join_sql:
87
- 'default.contractor.contractor_id = default.repair_orders.contractor_id',
88
- join_cardinality: 'one_to_one',
89
- role: 'contractor',
90
- },
91
- ],
92
- };
93
-
94
- const mockDimensions = [
95
- {
96
- is_primary_key: false,
97
- name: 'default.dispatcher.company_name',
98
- node_display_name: 'Default: Dispatcher',
99
- node_name: 'default.dispatcher',
100
- path: ['default.repair_orders_fact.dispatcher_id'],
101
- type: 'string',
102
- },
103
- {
104
- is_primary_key: true,
105
- name: 'default.dispatcher.dispatcher_id',
106
- node_display_name: 'Default: Dispatcher',
107
- node_name: 'default.dispatcher',
108
- path: ['default.repair_orders_fact.dispatcher_id'],
109
- type: 'int',
110
- },
111
- {
112
- is_primary_key: false,
113
- name: 'default.hard_hat.city',
114
- node_display_name: 'Default: Hard Hat',
115
- node_name: 'default.hard_hat',
116
- path: ['default.repair_orders_fact.hard_hat_id'],
117
- type: 'string',
118
- },
119
- {
120
- is_primary_key: true,
121
- name: 'default.hard_hat.hard_hat_id',
122
- node_display_name: 'Default: Hard Hat',
123
- node_name: 'default.hard_hat',
124
- path: ['default.repair_orders_fact.hard_hat_id'],
125
- type: 'int',
126
- },
127
- ];
128
-
129
- beforeEach(() => {
130
- // Reset the mocks before each test
131
- mockDjClient.nodeDimensions.mockReset();
132
- });
133
-
134
- it('renders nodes with dimensions', async () => {
135
- mockDjClient.nodeDimensions.mockReturnValue(mockDimensions);
136
- render(<NodeDimensionsTab node={mockNode} djClient={mockDjClient} />);
137
- await waitFor(() => {
138
- for (const dimension of mockDimensions) {
139
- const link = screen.getByText(dimension.node_display_name).closest('a');
140
- expect(link).toHaveAttribute('href', `/nodes/${dimension.node_name}`);
141
- expect(screen.getByText(dimension.name)).toBeInTheDocument();
142
- }
143
- });
144
- });
145
- });