datajunction-ui 0.0.1-rc.2 → 0.0.1-rc.20

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.
Files changed (135) hide show
  1. package/.env +1 -0
  2. package/.prettierignore +3 -1
  3. package/dj-logo.svg +10 -0
  4. package/package.json +43 -13
  5. package/public/favicon.ico +0 -0
  6. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  7. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -39
  8. package/src/app/components/DeleteNode.jsx +79 -0
  9. package/src/app/components/ListGroupItem.jsx +8 -1
  10. package/src/app/components/NamespaceHeader.jsx +4 -13
  11. package/src/app/components/QueryInfo.jsx +77 -0
  12. package/src/app/components/Tab.jsx +3 -2
  13. package/src/app/components/ToggleSwitch.jsx +20 -0
  14. package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
  15. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  16. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  17. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +3 -0
  18. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  19. package/src/app/components/djgraph/Collapse.jsx +46 -0
  20. package/src/app/components/djgraph/DJNode.jsx +60 -82
  21. package/src/app/components/djgraph/DJNodeColumns.jsx +71 -0
  22. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  23. package/src/app/components/djgraph/LayoutFlow.jsx +104 -0
  24. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  25. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  26. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  27. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  28. package/src/app/constants.js +2 -0
  29. package/src/app/icons/AlertIcon.jsx +32 -0
  30. package/src/app/icons/CollapsedIcon.jsx +15 -0
  31. package/src/app/icons/DJLogo.jsx +36 -0
  32. package/src/app/icons/DeleteIcon.jsx +21 -0
  33. package/src/app/icons/EditIcon.jsx +18 -0
  34. package/src/app/icons/ExpandedIcon.jsx +15 -0
  35. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  36. package/src/app/icons/InvalidIcon.jsx +14 -0
  37. package/src/app/icons/PythonIcon.jsx +52 -0
  38. package/src/app/icons/TableIcon.jsx +14 -0
  39. package/src/app/icons/ValidIcon.jsx +14 -0
  40. package/src/app/index.tsx +79 -26
  41. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +46 -0
  42. package/src/app/pages/AddEditNodePage/FullNameField.jsx +37 -0
  43. package/src/app/pages/AddEditNodePage/Loadable.jsx +16 -0
  44. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +89 -0
  45. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +77 -0
  46. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +93 -0
  47. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  48. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  49. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  50. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +53 -0
  51. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +53 -0
  52. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  53. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +178 -0
  54. package/src/app/pages/AddEditNodePage/index.jsx +357 -0
  55. package/src/app/pages/LoginPage/__tests__/index.test.jsx +70 -0
  56. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  57. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  58. package/src/app/pages/LoginPage/index.jsx +90 -0
  59. package/src/app/pages/NamespacePage/Explorer.jsx +57 -0
  60. package/src/app/pages/NamespacePage/Loadable.jsx +9 -7
  61. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +95 -0
  62. package/src/app/pages/NamespacePage/index.jsx +131 -31
  63. package/src/app/pages/NodePage/ClientCodePopover.jsx +32 -0
  64. package/src/app/pages/NodePage/EditColumnPopover.jsx +102 -0
  65. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +135 -0
  66. package/src/app/pages/NodePage/Loadable.jsx +9 -7
  67. package/src/app/pages/NodePage/NodeColumnTab.jsx +106 -27
  68. package/src/app/pages/NodePage/NodeGraphTab.jsx +94 -148
  69. package/src/app/pages/NodePage/NodeHistory.jsx +212 -0
  70. package/src/app/pages/NodePage/NodeInfoTab.jsx +166 -51
  71. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  72. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +174 -0
  73. package/src/app/pages/NodePage/NodeSQLTab.jsx +82 -0
  74. package/src/app/pages/NodePage/NodeStatus.jsx +14 -20
  75. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  76. package/src/app/pages/NodePage/__tests__/ClientCodePopover.test.jsx +49 -0
  77. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
  78. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +165 -0
  79. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +591 -0
  80. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +57 -0
  81. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +725 -0
  82. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  83. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +402 -0
  84. package/src/app/pages/NodePage/index.jsx +151 -41
  85. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  86. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  87. package/src/app/pages/RegisterTablePage/index.jsx +163 -0
  88. package/src/app/pages/Root/__tests__/index.test.jsx +77 -0
  89. package/src/app/pages/Root/index.tsx +32 -4
  90. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  91. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  92. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  93. package/src/app/providers/djclient.jsx +5 -0
  94. package/src/app/services/DJService.js +388 -22
  95. package/src/app/services/__tests__/DJService.test.jsx +609 -0
  96. package/src/mocks/mockNodes.jsx +1397 -0
  97. package/src/setupTests.ts +31 -1
  98. package/src/styles/dag.css +111 -5
  99. package/src/styles/index.css +467 -31
  100. package/src/styles/login.css +67 -0
  101. package/src/styles/node-creation.scss +197 -0
  102. package/src/styles/styles.scss +44 -0
  103. package/src/styles/styles.scss.d.ts +9 -0
  104. package/src/utils/form.jsx +23 -0
  105. package/tsconfig.json +1 -5
  106. package/webpack.config.js +29 -6
  107. package/.babelrc +0 -4
  108. package/.env.local +0 -4
  109. package/.env.production +0 -1
  110. package/.github/pull_request_template.md +0 -11
  111. package/.github/workflows/ci.yml +0 -33
  112. package/.vscode/extensions.json +0 -7
  113. package/.vscode/launch.json +0 -15
  114. package/.vscode/settings.json +0 -25
  115. package/Dockerfile +0 -7
  116. package/dist/5fa71a03d45dc2e3d61447f3013a303d.png +0 -0
  117. package/dist/index.html +0 -1
  118. package/dist/main.js +0 -23303
  119. package/dist/static/main.05a86d446163fd5f17d3.js +0 -2
  120. package/dist/static/main.05a86d446163fd5f17d3.js.LICENSE.txt +0 -98
  121. package/dist/static/main.9e53bed734dae98e5b10.js +0 -2
  122. package/dist/static/main.9e53bed734dae98e5b10.js.LICENSE.txt +0 -98
  123. package/dist/static/main.js +0 -2
  124. package/dist/static/main.js.LICENSE.txt +0 -98
  125. package/dist/static/vendor.05a86d446163fd5f17d3.js +0 -2
  126. package/dist/static/vendor.05a86d446163fd5f17d3.js.LICENSE.txt +0 -29
  127. package/dist/static/vendor.9e53bed734dae98e5b10.js +0 -2
  128. package/dist/static/vendor.9e53bed734dae98e5b10.js.LICENSE.txt +0 -29
  129. package/dist/static/vendor.js +0 -2
  130. package/dist/static/vendor.js.LICENSE.txt +0 -29
  131. package/dist/vendor.js +0 -281
  132. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  133. package/src/app/pages/ListNamespacesPage/index.jsx +0 -52
  134. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  135. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -0,0 +1,609 @@
1
+ import { DataJunctionAPI } from '../DJService';
2
+ import { mocks } from '../../../mocks/mockNodes';
3
+
4
+ describe('DataJunctionAPI', () => {
5
+ let originalEventSource;
6
+
7
+ const DJ_URL = 'http://localhost:8000';
8
+ process.env.REACT_APP_DJ_URL = DJ_URL;
9
+
10
+ beforeEach(() => {
11
+ fetch.resetMocks();
12
+ originalEventSource = global.EventSource;
13
+ global.EventSource = jest.fn();
14
+ });
15
+
16
+ afterEach(() => {
17
+ global.EventSource = originalEventSource;
18
+ });
19
+
20
+ it('calls whoami correctly', async () => {
21
+ fetch.mockResponseOnce(JSON.stringify({}));
22
+ await DataJunctionAPI.whoami();
23
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/whoami/`, {
24
+ credentials: 'include',
25
+ });
26
+ });
27
+
28
+ it('calls logout correctly', async () => {
29
+ fetch.mockResponseOnce(JSON.stringify({}));
30
+ await DataJunctionAPI.logout();
31
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/basic/logout/`, {
32
+ credentials: 'include',
33
+ method: 'POST',
34
+ });
35
+ });
36
+
37
+ it('calls node correctly', async () => {
38
+ fetch.mockResponseOnce(JSON.stringify(mocks.mockMetricNode));
39
+ const nodeData = await DataJunctionAPI.node(mocks.mockMetricNode.name);
40
+ expect(fetch).toHaveBeenCalledWith(
41
+ `${DJ_URL}/nodes/${mocks.mockMetricNode.name}/`,
42
+ {
43
+ credentials: 'include',
44
+ },
45
+ );
46
+ expect(nodeData.name).toEqual(mocks.mockMetricNode.name);
47
+ expect(nodeData.display_name).toEqual(mocks.mockMetricNode.display_name);
48
+ expect(nodeData.type).toEqual(mocks.mockMetricNode.type);
49
+ expect(nodeData.primary_key).toEqual([]);
50
+ });
51
+
52
+ it('node with errors are handled', async () => {
53
+ const mockNotFound = { message: 'node not found' };
54
+ fetch.mockResponseOnce(JSON.stringify(mockNotFound));
55
+ const nodeData = await DataJunctionAPI.node(mocks.mockMetricNode.name);
56
+ expect(nodeData.message).toEqual(mockNotFound.message);
57
+ });
58
+
59
+ it('calls nodes correctly', async () => {
60
+ const prefix = 'samplePrefix';
61
+ fetch.mockResponseOnce(JSON.stringify({}));
62
+ await DataJunctionAPI.nodes(prefix);
63
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/?prefix=${prefix}`, {
64
+ credentials: 'include',
65
+ });
66
+ });
67
+
68
+ it('calls createNode correctly', async () => {
69
+ const sampleArgs = [
70
+ 'type',
71
+ 'name',
72
+ 'display_name',
73
+ 'description',
74
+ 'query',
75
+ 'mode',
76
+ 'namespace',
77
+ 'primary_key',
78
+ ];
79
+ fetch.mockResponseOnce(JSON.stringify({}));
80
+ await DataJunctionAPI.createNode(...sampleArgs);
81
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${sampleArgs[0]}`, {
82
+ method: 'POST',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ },
86
+ body: JSON.stringify({
87
+ name: sampleArgs[1],
88
+ display_name: sampleArgs[2],
89
+ description: sampleArgs[3],
90
+ query: sampleArgs[4],
91
+ mode: sampleArgs[5],
92
+ namespace: sampleArgs[6],
93
+ primary_key: sampleArgs[7],
94
+ }),
95
+ credentials: 'include',
96
+ });
97
+ });
98
+
99
+ it('calls patchNode correctly', async () => {
100
+ const sampleArgs = [
101
+ 'name',
102
+ 'display_name',
103
+ 'description',
104
+ 'query',
105
+ 'mode',
106
+ 'primary_key',
107
+ ];
108
+ fetch.mockResponseOnce(JSON.stringify({}));
109
+ await DataJunctionAPI.patchNode(...sampleArgs);
110
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${sampleArgs[0]}`, {
111
+ method: 'PATCH',
112
+ headers: {
113
+ 'Content-Type': 'application/json',
114
+ },
115
+ body: JSON.stringify({
116
+ display_name: sampleArgs[1],
117
+ description: sampleArgs[2],
118
+ query: sampleArgs[3],
119
+ mode: sampleArgs[4],
120
+ primary_key: sampleArgs[5],
121
+ }),
122
+ credentials: 'include',
123
+ });
124
+
125
+ fetch.mockResponseOnce([
126
+ JSON.stringify({ message: 'Update failed' }),
127
+ { status: 200 },
128
+ ]);
129
+ const response = await DataJunctionAPI.patchNode(...sampleArgs);
130
+ expect(response).toEqual({
131
+ json: { message: 'Update failed' },
132
+ status: 500,
133
+ });
134
+ });
135
+
136
+ it('calls upstreams correctly', async () => {
137
+ const nodeName = 'sampleNode';
138
+ fetch.mockResponseOnce(JSON.stringify({}));
139
+ await DataJunctionAPI.upstreams(nodeName);
140
+ expect(fetch).toHaveBeenCalledWith(
141
+ `${DJ_URL}/nodes/${nodeName}/upstream/`,
142
+ {
143
+ credentials: 'include',
144
+ },
145
+ );
146
+ });
147
+
148
+ it('calls downstreams correctly', async () => {
149
+ const nodeName = 'sampleNode';
150
+ fetch.mockResponseOnce(JSON.stringify({}));
151
+ await DataJunctionAPI.downstreams(nodeName);
152
+ expect(fetch).toHaveBeenCalledWith(
153
+ `${DJ_URL}/nodes/${nodeName}/downstream/`,
154
+ {
155
+ credentials: 'include',
156
+ },
157
+ );
158
+ });
159
+
160
+ it('calls node_dag correctly', async () => {
161
+ const nodeName = 'sampleNode';
162
+ fetch.mockResponseOnce(JSON.stringify({}));
163
+ await DataJunctionAPI.node_dag(nodeName);
164
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/dag/`, {
165
+ credentials: 'include',
166
+ });
167
+ });
168
+
169
+ it('calls node_lineage correctly', async () => {
170
+ const nodeName = 'sampleNode';
171
+ fetch.mockResponseOnce(JSON.stringify({}));
172
+ await DataJunctionAPI.node_lineage(nodeName);
173
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/lineage/`, {
174
+ credentials: 'include',
175
+ });
176
+ });
177
+
178
+ it('calls metric correctly', async () => {
179
+ const metricName = 'sampleMetric';
180
+ fetch.mockResponseOnce(JSON.stringify({}));
181
+ await DataJunctionAPI.metric(metricName);
182
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/metrics/${metricName}/`, {
183
+ credentials: 'include',
184
+ });
185
+ });
186
+
187
+ it('calls metrics correctly', async () => {
188
+ fetch.mockResponseOnce(JSON.stringify({}));
189
+ await DataJunctionAPI.metrics('');
190
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/metrics/`, {
191
+ credentials: 'include',
192
+ });
193
+ });
194
+
195
+ it('calls namespaces correctly', async () => {
196
+ fetch.mockResponseOnce(JSON.stringify({}));
197
+ await DataJunctionAPI.namespaces();
198
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/`, {
199
+ credentials: 'include',
200
+ });
201
+ });
202
+
203
+ it('calls clientCode correctly', async () => {
204
+ const name = 'sampleName';
205
+ fetch.mockResponseOnce(JSON.stringify({}));
206
+ await DataJunctionAPI.clientCode(name);
207
+ expect(fetch).toHaveBeenCalledWith(
208
+ `${DJ_URL}/datajunction-clients/python/new_node/${name}`,
209
+ {
210
+ credentials: 'include',
211
+ },
212
+ );
213
+ });
214
+
215
+ it('calls cube correctly', async () => {
216
+ const name = 'sampleCube';
217
+ fetch.mockResponseOnce(JSON.stringify({}));
218
+ await DataJunctionAPI.cube(name);
219
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/cubes/${name}/`, {
220
+ credentials: 'include',
221
+ });
222
+ });
223
+
224
+ it('calls commonDimensions correctly', async () => {
225
+ const metrics = ['metric1', 'metric2'];
226
+ const query = metrics.map(m => `metric=${m}`).join('&');
227
+ fetch.mockResponseOnce(JSON.stringify({}));
228
+ await DataJunctionAPI.commonDimensions(metrics);
229
+ expect(fetch).toHaveBeenCalledWith(
230
+ `${DJ_URL}/metrics/common/dimensions/?${query}`,
231
+ {
232
+ credentials: 'include',
233
+ },
234
+ );
235
+ });
236
+
237
+ it('calls history correctly', async () => {
238
+ const type = 'sampleType';
239
+ const name = 'sampleName';
240
+ const offset = 10;
241
+ const limit = 100;
242
+ fetch.mockResponseOnce(JSON.stringify({}));
243
+ await DataJunctionAPI.history(type, name, offset, limit);
244
+ expect(fetch).toHaveBeenCalledWith(
245
+ `${DJ_URL}/history?node=${name}&offset=${offset}&limit=${limit}`,
246
+ {
247
+ credentials: 'include',
248
+ },
249
+ );
250
+ });
251
+
252
+ it('calls revisions correctly', async () => {
253
+ const name = 'sampleName';
254
+ fetch.mockResponseOnce(JSON.stringify({}));
255
+ await DataJunctionAPI.revisions(name);
256
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${name}/revisions/`, {
257
+ credentials: 'include',
258
+ });
259
+ });
260
+
261
+ it('calls namespace correctly', async () => {
262
+ const nmspce = 'sampleNamespace';
263
+ fetch.mockResponseOnce(JSON.stringify({}));
264
+ await DataJunctionAPI.namespace(nmspce);
265
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/${nmspce}/`, {
266
+ credentials: 'include',
267
+ });
268
+ });
269
+
270
+ it('calls sql correctly', async () => {
271
+ const metric_name = 'sampleMetric';
272
+ const selection = { key: 'value' };
273
+ const params = new URLSearchParams(selection).toString();
274
+ fetch.mockResponseOnce(JSON.stringify({}));
275
+ await DataJunctionAPI.sql(metric_name, selection);
276
+ expect(fetch).toHaveBeenCalledWith(
277
+ `${DJ_URL}/sql/${metric_name}?${params}`,
278
+ {
279
+ credentials: 'include',
280
+ },
281
+ );
282
+ });
283
+
284
+ it('calls nodesWithDimension correctly', async () => {
285
+ const name = 'sampleName';
286
+ fetch.mockResponseOnce(JSON.stringify({}));
287
+ await DataJunctionAPI.nodesWithDimension(name);
288
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/dimensions/${name}/nodes/`, {
289
+ credentials: 'include',
290
+ });
291
+ });
292
+
293
+ it('calls materializations correctly', async () => {
294
+ const nodeName = 'default.sample_node';
295
+ const mockMaterializations = [
296
+ { name: 'materialization1', clientCode: 'from dj import DJClient' },
297
+ { name: 'materialization2', clientCode: 'from dj import DJClient' },
298
+ ];
299
+
300
+ // Mock the first fetch call to return the list of materializations
301
+ fetch.mockResponseOnce(JSON.stringify(mockMaterializations));
302
+
303
+ // Mock the subsequent fetch calls to return clientCode for each materialization
304
+ fetch.mockResponses(
305
+ ...mockMaterializations.map(mat => [
306
+ JSON.stringify('from dj import DJClient'),
307
+ ]),
308
+ );
309
+
310
+ const result = await DataJunctionAPI.materializations(nodeName);
311
+
312
+ // Check the first fetch call
313
+ expect(fetch).toHaveBeenCalledWith(
314
+ `${DJ_URL}/nodes/${nodeName}/materializations/`,
315
+ {
316
+ credentials: 'include',
317
+ },
318
+ );
319
+
320
+ // Check the subsequent fetch calls for clientCode
321
+ mockMaterializations.forEach(mat => {
322
+ expect(fetch).toHaveBeenCalledWith(
323
+ `${DJ_URL}/datajunction-clients/python/add_materialization/${nodeName}/${mat.name}`,
324
+ { credentials: 'include' },
325
+ );
326
+ });
327
+
328
+ // Ensure the result contains the clientCode for each materialization
329
+ expect(result).toEqual(mockMaterializations);
330
+ });
331
+
332
+ it('calls columns correctly', async () => {
333
+ const sampleNode = {
334
+ name: 'sampleNode',
335
+ columns: [
336
+ { name: 'column1', dimension: { name: 'dimension1' }, clientCode: {} },
337
+ { name: 'column2', dimension: null },
338
+ { name: 'column3', dimension: { name: 'dimension2' }, clientCode: {} },
339
+ ],
340
+ };
341
+
342
+ // Mock the fetch calls to return clientCode for columns with a dimension
343
+ const mockClientCodeResponses = sampleNode.columns
344
+ .filter(col => col.dimension)
345
+ .map(col => [JSON.stringify({ clientCode: col.clientCode })]);
346
+
347
+ fetch.mockResponses(...mockClientCodeResponses);
348
+
349
+ const result = await DataJunctionAPI.columns(sampleNode);
350
+
351
+ // Check the fetch calls for clientCode for columns with a dimension
352
+ sampleNode.columns.forEach(col => {
353
+ if (col.dimension) {
354
+ expect(fetch).toHaveBeenCalledWith(
355
+ `${DJ_URL}/datajunction-clients/python/link_dimension/${sampleNode.name}/${col.name}/${col.dimension.name}`,
356
+ { credentials: 'include' },
357
+ );
358
+ }
359
+ });
360
+
361
+ // Ensure the result contains the clientCode for columns with a dimension and leaves others unchanged
362
+ expect(result).toEqual(sampleNode.columns);
363
+ });
364
+
365
+ it('calls sqls correctly', async () => {
366
+ const metricSelection = ['metric1'];
367
+ const dimensionSelection = ['dimension1'];
368
+ const filters = 'sampleFilter';
369
+ const params = new URLSearchParams();
370
+ metricSelection.forEach(metric => params.append('metrics', metric));
371
+ dimensionSelection.forEach(dimension =>
372
+ params.append('dimensions', dimension),
373
+ );
374
+ params.append('filters', filters);
375
+ fetch.mockResponseOnce(JSON.stringify({}));
376
+ await DataJunctionAPI.sqls(metricSelection, dimensionSelection, filters);
377
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/sql/?${params}`, {
378
+ credentials: 'include',
379
+ });
380
+ });
381
+
382
+ it('calls data correctly', async () => {
383
+ const metricSelection = ['metric1'];
384
+ const dimensionSelection = ['dimension1'];
385
+ const params = new URLSearchParams();
386
+ metricSelection.forEach(metric => params.append('metrics', metric));
387
+ dimensionSelection.forEach(dimension =>
388
+ params.append('dimensions', dimension),
389
+ );
390
+ fetch.mockResponseOnce(JSON.stringify({}));
391
+ await DataJunctionAPI.data(metricSelection, dimensionSelection);
392
+ expect(fetch).toHaveBeenCalledWith(
393
+ `${DJ_URL}/data/?${params}&limit=10000`,
394
+ {
395
+ credentials: 'include',
396
+ },
397
+ );
398
+ });
399
+
400
+ it('calls compiledSql correctly', async () => {
401
+ const node = 'sampleNode';
402
+ fetch.mockResponseOnce(JSON.stringify({}));
403
+ await DataJunctionAPI.compiledSql(node);
404
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/sql/${node}/`, {
405
+ credentials: 'include',
406
+ });
407
+ });
408
+
409
+ it('transforms node data correctly', async () => {
410
+ fetch.mockResolvedValueOnce({
411
+ json: () =>
412
+ Promise.resolve({
413
+ message: undefined,
414
+ columns: [
415
+ {
416
+ name: 'id',
417
+ attributes: [
418
+ {
419
+ attribute_type: {
420
+ name: 'primary_key',
421
+ },
422
+ },
423
+ ],
424
+ },
425
+ ],
426
+ }),
427
+ status: 200,
428
+ });
429
+
430
+ const nodeData = await DataJunctionAPI.node('sampleNodeName');
431
+ expect(nodeData.primary_key).toEqual(['id']);
432
+ });
433
+
434
+ it('calls stream correctly', () => {
435
+ const metricSelection = ['metric1', 'metric2'];
436
+ const dimensionSelection = ['dimension1', 'dimension2'];
437
+ const filters = 'sampleFilter';
438
+
439
+ DataJunctionAPI.stream(metricSelection, dimensionSelection, filters);
440
+
441
+ const params = new URLSearchParams();
442
+ metricSelection.map(metric => params.append('metrics', metric));
443
+ dimensionSelection.map(dimension => params.append('dimensions', dimension));
444
+ params.append('filters', filters);
445
+
446
+ expect(global.EventSource).toHaveBeenCalledWith(
447
+ `${DJ_URL}/stream/?${params}&limit=10000&async_=true`,
448
+ { withCredentials: true },
449
+ );
450
+ });
451
+
452
+ it('calls dag correctly and processes response', async () => {
453
+ const mockResponse = [
454
+ {
455
+ name: 'namespace.node1',
456
+ parents: [{ name: 'parent1' }, { name: null }],
457
+ columns: [
458
+ {
459
+ dimension: { name: 'dimension1' },
460
+ name: 'col1',
461
+ type: 'type1',
462
+ attributes: [],
463
+ },
464
+ {
465
+ dimension: null,
466
+ name: 'col2',
467
+ type: 'type2',
468
+ attributes: [{ attribute_type: { name: 'primary_key' } }],
469
+ },
470
+ ],
471
+ table: 'table1',
472
+ schema_: 'schema1',
473
+ display_name: 'DisplayName1',
474
+ type: 'type1',
475
+ },
476
+ ];
477
+
478
+ fetch.mockResponseOnce(JSON.stringify(mockResponse));
479
+ const result = await DataJunctionAPI.dag();
480
+
481
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/`, {
482
+ credentials: 'include',
483
+ });
484
+
485
+ expect(result.edges).toEqual([
486
+ {
487
+ id: 'namespace.node1-parent1',
488
+ target: 'namespace.node1',
489
+ source: 'parent1',
490
+ animated: true,
491
+ markerEnd: { type: 'arrow' },
492
+ },
493
+ {
494
+ id: 'namespace.node1-dimension1',
495
+ target: 'namespace.node1',
496
+ source: 'dimension1',
497
+ draggable: true,
498
+ },
499
+ ]);
500
+
501
+ expect(result.nodes).toEqual([
502
+ {
503
+ id: 'namespace.node1',
504
+ type: 'DJNode',
505
+ data: {
506
+ label: 'schema1.table1',
507
+ table: 'table1',
508
+ name: 'namespace.node1',
509
+ display_name: 'DisplayName1',
510
+ type: 'type1',
511
+ primary_key: ['col2'],
512
+ column_names: [
513
+ { name: 'col1', type: 'type1' },
514
+ { name: 'col2', type: 'type2' },
515
+ ],
516
+ },
517
+ },
518
+ ]);
519
+
520
+ expect(result.namespaces).toEqual([
521
+ {
522
+ id: 'namespace',
523
+ type: 'DJNamespace',
524
+ data: {
525
+ label: 'namespace',
526
+ },
527
+ },
528
+ ]);
529
+ });
530
+
531
+ it('calls link and unlink dimension correctly', async () => {
532
+ const nodeName = 'default.transform1';
533
+ const dimensionName = 'default.dimension1';
534
+ const columnName = 'column1';
535
+ fetch.mockResponseOnce(JSON.stringify({}));
536
+ await DataJunctionAPI.unlinkDimension(nodeName, columnName, dimensionName);
537
+ expect(fetch).toHaveBeenCalledWith(
538
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
539
+ {
540
+ credentials: 'include',
541
+ headers: {
542
+ 'Content-Type': 'application/json',
543
+ },
544
+ method: 'DELETE',
545
+ },
546
+ );
547
+
548
+ fetch.mockResponseOnce(JSON.stringify({}));
549
+ await DataJunctionAPI.linkDimension(nodeName, columnName, dimensionName);
550
+ expect(fetch).toHaveBeenCalledWith(
551
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
552
+ {
553
+ credentials: 'include',
554
+ headers: {
555
+ 'Content-Type': 'application/json',
556
+ },
557
+ method: 'POST',
558
+ },
559
+ );
560
+ });
561
+
562
+ it('calls deactivate correctly', async () => {
563
+ const nodeName = 'default.transform1';
564
+ fetch.mockResponseOnce(JSON.stringify({}));
565
+ await DataJunctionAPI.deactivate(nodeName);
566
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}`, {
567
+ credentials: 'include',
568
+ headers: {
569
+ 'Content-Type': 'application/json',
570
+ },
571
+ method: 'DELETE',
572
+ });
573
+ });
574
+
575
+ it('calls attributes correctly', async () => {
576
+ fetch.mockResponseOnce(JSON.stringify(mocks.attributes));
577
+ await DataJunctionAPI.attributes();
578
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/attributes`, {
579
+ credentials: 'include',
580
+ });
581
+ });
582
+
583
+ it('calls dimensions correctly', async () => {
584
+ fetch.mockResponseOnce(JSON.stringify(mocks.dimensions));
585
+ await DataJunctionAPI.dimensions();
586
+ expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/dimensions`, {
587
+ credentials: 'include',
588
+ });
589
+ });
590
+
591
+ it('calls setAttributes correctly', async () => {
592
+ const nodeName = 'default.transform1';
593
+ const attributes = ['system'];
594
+ const columnName = 'column1';
595
+ fetch.mockResponseOnce(JSON.stringify({}));
596
+ await DataJunctionAPI.setAttributes(nodeName, columnName, attributes);
597
+ expect(fetch).toHaveBeenCalledWith(
598
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}/attributes`,
599
+ {
600
+ credentials: 'include',
601
+ body: JSON.stringify([{ namespace: 'system', name: 'system' }]),
602
+ headers: {
603
+ 'Content-Type': 'application/json',
604
+ },
605
+ method: 'POST',
606
+ },
607
+ );
608
+ });
609
+ });