datajunction-ui 0.0.1-rc.9 → 0.0.2

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 (205) hide show
  1. package/.env +2 -0
  2. package/.prettierignore +3 -1
  3. package/Makefile +9 -0
  4. package/dj-logo.svg +10 -0
  5. package/package.json +53 -14
  6. package/public/favicon.ico +0 -0
  7. package/public/index.html +1 -1
  8. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  9. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -109
  10. package/src/app/components/AddNodeDropdown.jsx +44 -0
  11. package/src/app/components/ListGroupItem.jsx +9 -1
  12. package/src/app/components/NamespaceHeader.jsx +4 -13
  13. package/src/app/components/NodeListActions.jsx +69 -0
  14. package/src/app/components/NodeMaterializationDelete.jsx +90 -0
  15. package/src/app/components/QueryInfo.jsx +172 -0
  16. package/src/app/components/Search.jsx +94 -0
  17. package/src/app/components/Tab.jsx +8 -1
  18. package/src/app/components/ToggleSwitch.jsx +20 -0
  19. package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
  20. package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
  21. package/src/app/components/__tests__/Search.test.jsx +63 -0
  22. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  23. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  24. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
  25. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  26. package/src/app/components/djgraph/Collapse.jsx +47 -0
  27. package/src/app/components/djgraph/DJNode.jsx +61 -83
  28. package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
  29. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  30. package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
  31. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  32. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  33. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  34. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  35. package/src/app/components/forms/Action.jsx +8 -0
  36. package/src/app/components/forms/NodeNameField.jsx +64 -0
  37. package/src/app/components/search.css +17 -0
  38. package/src/app/constants.js +2 -0
  39. package/src/app/icons/AddItemIcon.jsx +16 -0
  40. package/src/app/icons/AlertIcon.jsx +33 -0
  41. package/src/app/icons/CollapsedIcon.jsx +15 -0
  42. package/src/app/icons/CommitIcon.jsx +45 -0
  43. package/src/app/icons/DJLogo.jsx +36 -0
  44. package/src/app/icons/DeleteIcon.jsx +21 -0
  45. package/src/app/icons/DiffIcon.jsx +63 -0
  46. package/src/app/icons/EditIcon.jsx +18 -0
  47. package/src/app/icons/ExpandedIcon.jsx +15 -0
  48. package/src/app/icons/EyeIcon.jsx +20 -0
  49. package/src/app/icons/FilterIcon.jsx +7 -0
  50. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  51. package/src/app/icons/InvalidIcon.jsx +16 -0
  52. package/src/app/icons/JupyterExportIcon.jsx +25 -0
  53. package/src/app/icons/LoadingIcon.jsx +14 -0
  54. package/src/app/icons/NodeIcon.jsx +49 -0
  55. package/src/app/icons/PythonIcon.jsx +14 -0
  56. package/src/app/icons/TableIcon.jsx +14 -0
  57. package/src/app/icons/ValidIcon.jsx +16 -0
  58. package/src/app/index.tsx +118 -37
  59. package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
  60. package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
  61. package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
  62. package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
  63. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +51 -0
  64. package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
  65. package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
  66. package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
  67. package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
  68. package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
  69. package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
  70. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
  71. package/src/app/pages/AddEditNodePage/OwnersField.jsx +54 -0
  72. package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
  73. package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
  74. package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
  75. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +109 -0
  76. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +287 -0
  77. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  78. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  79. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  80. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
  81. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
  82. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  83. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
  84. package/src/app/pages/AddEditNodePage/index.jsx +506 -0
  85. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  86. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  87. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  88. package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
  89. package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
  90. package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
  91. package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
  92. package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
  93. package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
  94. package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
  95. package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
  96. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  97. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  98. package/src/app/pages/LoginPage/index.jsx +17 -0
  99. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
  100. package/src/app/pages/NamespacePage/Explorer.jsx +61 -0
  101. package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
  102. package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
  103. package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
  104. package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
  105. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +297 -0
  106. package/src/app/pages/NamespacePage/index.jsx +319 -42
  107. package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
  108. package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
  109. package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
  110. package/src/app/pages/NodePage/ClientCodePopover.jsx +94 -0
  111. package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
  112. package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
  113. package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
  114. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
  115. package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
  116. package/src/app/pages/NodePage/NodeColumnTab.jsx +256 -30
  117. package/src/app/pages/NodePage/NodeDependenciesTab.jsx +153 -0
  118. package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
  119. package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
  120. package/src/app/pages/NodePage/NodeInfoTab.jsx +325 -49
  121. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  122. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
  123. package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
  124. package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
  125. package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
  126. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  127. package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
  128. package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
  129. package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
  130. package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
  131. package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
  132. package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
  133. package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
  134. package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
  135. package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
  136. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
  137. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +161 -0
  138. package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
  139. package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +151 -0
  140. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
  141. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
  142. package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
  143. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +872 -0
  144. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  145. package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
  146. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
  147. package/src/app/pages/NodePage/index.jsx +190 -44
  148. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  149. package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
  150. package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
  151. package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
  152. package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
  153. package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
  154. package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
  155. package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
  156. package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
  157. package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
  158. package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
  159. package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
  160. package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
  161. package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
  162. package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
  163. package/src/app/pages/OverviewPage/index.jsx +22 -0
  164. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  165. package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +110 -0
  166. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
  167. package/src/app/pages/RegisterTablePage/index.jsx +142 -0
  168. package/src/app/pages/Root/__tests__/index.test.jsx +79 -0
  169. package/src/app/pages/Root/index.tsx +84 -6
  170. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  171. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  172. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  173. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  174. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  175. package/src/app/pages/TagPage/index.jsx +79 -0
  176. package/src/app/services/DJService.js +1314 -21
  177. package/src/app/services/__tests__/DJService.test.jsx +1559 -0
  178. package/src/index.tsx +1 -0
  179. package/src/mocks/mockNodes.jsx +1474 -0
  180. package/src/setupTests.ts +31 -1
  181. package/src/styles/dag.css +117 -5
  182. package/src/styles/index.css +1027 -30
  183. package/src/styles/loading.css +34 -0
  184. package/src/styles/login.css +81 -0
  185. package/src/styles/node-creation.scss +276 -0
  186. package/src/styles/node-list.css +4 -0
  187. package/src/styles/overview.css +72 -0
  188. package/src/styles/sorted-table.css +15 -0
  189. package/src/styles/styles.scss +44 -0
  190. package/src/styles/styles.scss.d.ts +9 -0
  191. package/src/utils/form.jsx +23 -0
  192. package/webpack.config.js +16 -6
  193. package/.babelrc +0 -4
  194. package/.env.local +0 -4
  195. package/.env.production +0 -1
  196. package/.github/pull_request_template.md +0 -11
  197. package/.github/workflows/ci.yml +0 -33
  198. package/.vscode/extensions.json +0 -7
  199. package/.vscode/launch.json +0 -15
  200. package/.vscode/settings.json +0 -25
  201. package/Dockerfile +0 -7
  202. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  203. package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
  204. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  205. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -1,51 +1,923 @@
1
1
  import { MarkerType } from 'reactflow';
2
2
 
3
- const DJ_URL = 'http://localhost:8000'; //process.env.REACT_APP_DJ_URL;
3
+ const DJ_URL = process.env.REACT_APP_DJ_URL
4
+ ? process.env.REACT_APP_DJ_URL
5
+ : 'http://localhost:8000';
6
+
7
+ const DJ_GQL = process.env.REACT_APP_DJ_GQL
8
+ ? process.env.REACT_APP_DJ_GQL
9
+ : process.env.REACT_APP_DJ_URL + '/graphql';
4
10
 
5
11
  export const DataJunctionAPI = {
12
+ listNodesForLanding: async function (
13
+ namespace,
14
+ nodeTypes,
15
+ tags,
16
+ editedBy,
17
+ before,
18
+ after,
19
+ limit,
20
+ sortConfig,
21
+ ) {
22
+ const query = `
23
+ query ListNodes($namespace: String, $nodeTypes: [NodeType!], $tags: [String!], $editedBy: String, $before: String, $after: String, $limit: Int, $orderBy: NodeSortField, $ascending: Boolean) {
24
+ findNodesPaginated(
25
+ namespace: $namespace
26
+ nodeTypes: $nodeTypes
27
+ tags: $tags
28
+ editedBy: $editedBy
29
+ limit: $limit
30
+ before: $before
31
+ after: $after
32
+ orderBy: $orderBy,
33
+ ascending: $ascending
34
+ ) {
35
+ pageInfo {
36
+ hasNextPage
37
+ endCursor
38
+ hasPrevPage
39
+ startCursor
40
+ }
41
+ edges {
42
+ node {
43
+ name
44
+ type
45
+ currentVersion
46
+ tags {
47
+ name
48
+ tagType
49
+ }
50
+ editedBy
51
+ current {
52
+ displayName
53
+ status
54
+ updatedAt
55
+ }
56
+ createdBy {
57
+ username
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ `;
64
+ const sortOrderMapping = {
65
+ name: 'NAME',
66
+ displayName: 'DISPLAY_NAME',
67
+ type: 'TYPE',
68
+ status: 'STATUS',
69
+ updatedAt: 'UPDATED_AT',
70
+ };
71
+
72
+ return await (
73
+ await fetch(DJ_GQL, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ },
78
+ credentials: 'include',
79
+ body: JSON.stringify({
80
+ query,
81
+ variables: {
82
+ namespace: namespace,
83
+ nodeTypes: nodeTypes,
84
+ tags: tags,
85
+ editedBy: editedBy,
86
+ before: before,
87
+ after: after,
88
+ limit: limit,
89
+ orderBy: sortOrderMapping[sortConfig.key],
90
+ ascending: sortConfig.direction === 'ascending',
91
+ },
92
+ }),
93
+ })
94
+ ).json();
95
+ },
96
+
97
+ whoami: async function () {
98
+ return await (
99
+ await fetch(`${DJ_URL}/whoami/`, { credentials: 'include' })
100
+ ).json();
101
+ },
102
+
103
+ querySystemMetric: async function ({
104
+ metric,
105
+ dimensions = [],
106
+ filters = [],
107
+ orderby = [],
108
+ }) {
109
+ const params = new URLSearchParams();
110
+ dimensions.forEach(d => params.append('dimensions', d));
111
+ filters.forEach(f => params.append('filters', f));
112
+ orderby.forEach(o => params.append('orderby', o));
113
+
114
+ const url = `${DJ_URL}/system/data/${metric}?${params.toString()}`;
115
+ const res = await fetch(url, { credentials: 'include' });
116
+
117
+ if (!res.ok) {
118
+ throw new Error(`Failed to fetch metric data ${metric}: ${res.status}`);
119
+ }
120
+ return await res.json();
121
+ },
122
+
123
+ querySystemMetricSingleDimension: async function ({
124
+ metric,
125
+ dimension,
126
+ filters = [],
127
+ orderby = [],
128
+ }) {
129
+ const results = await DataJunctionAPI.querySystemMetric({
130
+ metric: metric,
131
+ dimensions: [dimension],
132
+ filters: filters,
133
+ orderby: orderby,
134
+ });
135
+ return results.map(row => {
136
+ return {
137
+ name:
138
+ row.find(entry => entry.col === dimension)?.value?.toString() ??
139
+ 'unknown',
140
+ value: row.find(entry => entry.col === metric)?.value ?? 0,
141
+ };
142
+ });
143
+ },
144
+
145
+ system: {
146
+ node_counts_by_active: async function () {
147
+ return DataJunctionAPI.querySystemMetricSingleDimension({
148
+ metric: 'system.dj.number_of_nodes',
149
+ dimension: 'system.dj.is_active.active_id',
150
+ });
151
+ },
152
+ node_counts_by_type: async function () {
153
+ return DataJunctionAPI.querySystemMetricSingleDimension({
154
+ metric: 'system.dj.number_of_nodes',
155
+ dimension: 'system.dj.node_type.type',
156
+ filters: ['system.dj.is_active.active_id=true'],
157
+ orderby: ['system.dj.node_type.type'],
158
+ });
159
+ },
160
+ node_counts_by_status: async function () {
161
+ return DataJunctionAPI.querySystemMetricSingleDimension({
162
+ metric: 'system.dj.number_of_nodes',
163
+ dimension: 'system.dj.nodes.status',
164
+ filters: ['system.dj.is_active.active_id=true'],
165
+ orderby: ['system.dj.nodes.status'],
166
+ });
167
+ },
168
+ nodes_without_description: async function () {
169
+ return DataJunctionAPI.querySystemMetricSingleDimension({
170
+ metric: 'system.dj.node_without_description',
171
+ dimension: 'system.dj.node_type.type',
172
+ filters: ['system.dj.is_active.active_id=true'],
173
+ orderby: ['system.dj.node_type.type'],
174
+ });
175
+ },
176
+ node_trends: async function () {
177
+ const results = await (
178
+ await fetch(
179
+ `${DJ_URL}/system/data/system.dj.number_of_nodes?dimensions=system.dj.nodes.created_at_week&dimensions=system.dj.node_type.type&filters=system.dj.nodes.created_at_week>=20240101&orderby=system.dj.nodes.created_at_week`,
180
+ { credentials: 'include' },
181
+ )
182
+ ).json();
183
+ const byDateint = {};
184
+ results.forEach(row => {
185
+ const dateint = row.find(
186
+ r => r.col === 'system.dj.nodes.created_at_week',
187
+ )?.value;
188
+ const nodeType = row.find(
189
+ r => r.col === 'system.dj.node_type.type',
190
+ )?.value;
191
+ const count = row.find(
192
+ r => r.col === 'system.dj.number_of_nodes',
193
+ )?.value;
194
+ if (!byDateint[dateint]) {
195
+ byDateint[dateint] = { date: dateint };
196
+ }
197
+ byDateint[dateint][nodeType] =
198
+ (byDateint[dateint][nodeType] || 0) + count;
199
+ });
200
+ return Object.entries(byDateint).map(([dateint, data]) => {
201
+ return {
202
+ date: dateint,
203
+ ...data,
204
+ };
205
+ });
206
+ },
207
+ materialization_counts_by_type: async function () {
208
+ return DataJunctionAPI.querySystemMetricSingleDimension({
209
+ metric: 'system.dj.number_of_materializations',
210
+ dimension: 'system.dj.node_type.type',
211
+ filters: ['system.dj.is_active.active_id=true'],
212
+ orderby: ['system.dj.node_type.type'],
213
+ });
214
+ },
215
+
216
+ dimensions: async function () {
217
+ return await (
218
+ await fetch(`${DJ_URL}/system/dimensions`, {
219
+ credentials: 'include',
220
+ })
221
+ ).json();
222
+ },
223
+ },
224
+
225
+ logout: async function () {
226
+ return await fetch(`${DJ_URL}/logout/`, {
227
+ credentials: 'include',
228
+ method: 'POST',
229
+ });
230
+ },
231
+
232
+ catalogs: async function () {
233
+ return await (
234
+ await fetch(`${DJ_URL}/catalogs`, {
235
+ credentials: 'include',
236
+ })
237
+ ).json();
238
+ },
239
+
240
+ engines: async function () {
241
+ return await (
242
+ await fetch(`${DJ_URL}/engines`, {
243
+ credentials: 'include',
244
+ })
245
+ ).json();
246
+ },
247
+
6
248
  node: async function (name) {
7
- const data = await (await fetch(DJ_URL + '/nodes/' + name + '/')).json();
249
+ const data = await (
250
+ await fetch(`${DJ_URL}/nodes/${name}/`, {
251
+ credentials: 'include',
252
+ })
253
+ ).json();
254
+ if (data.message !== undefined) {
255
+ return data;
256
+ }
257
+ data.primary_key = data.columns
258
+ .filter(col =>
259
+ col.attributes.some(attr => attr.attribute_type.name === 'primary_key'),
260
+ )
261
+ .map(col => col.name);
8
262
  return data;
9
263
  },
10
264
 
265
+ getNodeForEditing: async function (name) {
266
+ const query = `
267
+ query GetNodeForEditing($name: String!) {
268
+ findNodes (names: [$name]) {
269
+ name
270
+ type
271
+ current {
272
+ displayName
273
+ description
274
+ primaryKey
275
+ query
276
+ parents { name }
277
+ metricMetadata {
278
+ direction
279
+ unit { name }
280
+ expression
281
+ significantDigits
282
+ incompatibleDruidFunctions
283
+ }
284
+ requiredDimensions {
285
+ name
286
+ }
287
+ mode
288
+ }
289
+ tags {
290
+ name
291
+ displayName
292
+ }
293
+ owners {
294
+ username
295
+ }
296
+ }
297
+ }
298
+ `;
299
+
300
+ const results = await (
301
+ await fetch(DJ_GQL, {
302
+ method: 'POST',
303
+ headers: {
304
+ 'Content-Type': 'application/json',
305
+ },
306
+ credentials: 'include',
307
+ body: JSON.stringify({
308
+ query,
309
+ variables: {
310
+ name: name,
311
+ },
312
+ }),
313
+ })
314
+ ).json();
315
+ if (results.data.findNodes.length === 0) {
316
+ return null;
317
+ }
318
+ return results.data.findNodes[0];
319
+ },
320
+
321
+ getMetric: async function (name) {
322
+ const query = `
323
+ query GetMetric($name: String!) {
324
+ findNodes (names: [$name]) {
325
+ name
326
+ current {
327
+ parents { name }
328
+ metricMetadata {
329
+ direction
330
+ unit { name }
331
+ expression
332
+ significantDigits
333
+ incompatibleDruidFunctions
334
+ }
335
+ requiredDimensions {
336
+ name
337
+ }
338
+ }
339
+ }
340
+ }
341
+ `;
342
+
343
+ const results = await (
344
+ await fetch(DJ_GQL, {
345
+ method: 'POST',
346
+ headers: {
347
+ 'Content-Type': 'application/json',
348
+ },
349
+ credentials: 'include',
350
+ body: JSON.stringify({
351
+ query,
352
+ variables: {
353
+ name: name,
354
+ },
355
+ }),
356
+ })
357
+ ).json();
358
+ return results.data.findNodes[0];
359
+ },
360
+
361
+ getCubeForEditing: async function (name) {
362
+ const query = `
363
+ query GetCubeForEditing($name: String!) {
364
+ findNodes(names: [$name]) {
365
+ name
366
+ type
367
+ owners {
368
+ username
369
+ }
370
+ current {
371
+ displayName
372
+ description
373
+ mode
374
+ cubeMetrics {
375
+ name
376
+ }
377
+ cubeDimensions {
378
+ name
379
+ attribute
380
+ properties
381
+ }
382
+ }
383
+ tags {
384
+ name
385
+ displayName
386
+ }
387
+ }
388
+ }
389
+ `;
390
+
391
+ const results = await (
392
+ await fetch(DJ_GQL, {
393
+ method: 'POST',
394
+ headers: {
395
+ 'Content-Type': 'application/json',
396
+ },
397
+ credentials: 'include',
398
+ body: JSON.stringify({
399
+ query,
400
+ variables: {
401
+ name: name,
402
+ },
403
+ }),
404
+ })
405
+ ).json();
406
+ if (results.data.findNodes.length === 0) {
407
+ return null;
408
+ }
409
+ return results.data.findNodes[0];
410
+ },
411
+
412
+ nodes: async function (prefix) {
413
+ const queryParams = prefix ? `?prefix=${prefix}` : '';
414
+ return await (
415
+ await fetch(`${DJ_URL}/nodes/${queryParams}`, {
416
+ credentials: 'include',
417
+ })
418
+ ).json();
419
+ },
420
+
421
+ nodesWithType: async function (nodeType) {
422
+ return await (
423
+ await fetch(`${DJ_URL}/nodes/?node_type=${nodeType}`, {
424
+ credentials: 'include',
425
+ })
426
+ ).json();
427
+ },
428
+
429
+ nodeDetails: async () => {
430
+ return await (
431
+ await fetch(`${DJ_URL}/nodes/details/`, {
432
+ credentials: 'include',
433
+ })
434
+ ).json();
435
+ },
436
+
437
+ validateNode: async function (
438
+ nodeType,
439
+ name,
440
+ display_name,
441
+ description,
442
+ query,
443
+ ) {
444
+ const response = await fetch(`${DJ_URL}/nodes/validate`, {
445
+ method: 'POST',
446
+ headers: {
447
+ 'Content-Type': 'application/json',
448
+ },
449
+ body: JSON.stringify({
450
+ name: name,
451
+ display_name: display_name,
452
+ description: description,
453
+ query: query,
454
+ type: nodeType,
455
+ mode: 'published',
456
+ }),
457
+ credentials: 'include',
458
+ });
459
+ return { status: response.status, json: await response.json() };
460
+ },
461
+
462
+ createNode: async function (
463
+ nodeType,
464
+ name,
465
+ display_name,
466
+ description,
467
+ query,
468
+ mode,
469
+ namespace,
470
+ primary_key,
471
+ metric_direction,
472
+ metric_unit,
473
+ required_dimensions,
474
+ ) {
475
+ const metricMetadata =
476
+ metric_direction || metric_unit
477
+ ? {
478
+ direction: metric_direction,
479
+ unit: metric_unit,
480
+ }
481
+ : null;
482
+ const response = await fetch(`${DJ_URL}/nodes/${nodeType}`, {
483
+ method: 'POST',
484
+ headers: {
485
+ 'Content-Type': 'application/json',
486
+ },
487
+ body: JSON.stringify({
488
+ name: name,
489
+ display_name: display_name,
490
+ description: description,
491
+ query: query,
492
+ mode: mode,
493
+ namespace: namespace,
494
+ primary_key: primary_key,
495
+ metric_metadata: metricMetadata,
496
+ required_dimensions: required_dimensions,
497
+ }),
498
+ credentials: 'include',
499
+ });
500
+ return { status: response.status, json: await response.json() };
501
+ },
502
+
503
+ patchNode: async function (
504
+ name,
505
+ display_name,
506
+ description,
507
+ query,
508
+ mode,
509
+ primary_key,
510
+ metric_direction,
511
+ metric_unit,
512
+ significant_digits,
513
+ required_dimensions,
514
+ owners,
515
+ ) {
516
+ try {
517
+ const metricMetadata =
518
+ metric_direction || metric_unit
519
+ ? {
520
+ direction: metric_direction,
521
+ unit: metric_unit,
522
+ significant_digits: significant_digits || null,
523
+ }
524
+ : null;
525
+ const response = await fetch(`${DJ_URL}/nodes/${name}`, {
526
+ method: 'PATCH',
527
+ headers: {
528
+ 'Content-Type': 'application/json',
529
+ },
530
+ body: JSON.stringify({
531
+ display_name: display_name,
532
+ description: description,
533
+ query: query,
534
+ mode: mode,
535
+ primary_key: primary_key,
536
+ metric_metadata: metricMetadata,
537
+ required_dimensions: required_dimensions,
538
+ owners: owners,
539
+ }),
540
+ credentials: 'include',
541
+ });
542
+ return { status: response.status, json: await response.json() };
543
+ } catch (error) {
544
+ return { status: 500, json: { message: 'Update failed' } };
545
+ }
546
+ },
547
+
548
+ createCube: async function (
549
+ name,
550
+ display_name,
551
+ description,
552
+ mode,
553
+ metrics,
554
+ dimensions,
555
+ filters,
556
+ ) {
557
+ const response = await fetch(`${DJ_URL}/nodes/cube`, {
558
+ method: 'POST',
559
+ headers: {
560
+ 'Content-Type': 'application/json',
561
+ },
562
+ body: JSON.stringify({
563
+ name: name,
564
+ display_name: display_name,
565
+ description: description,
566
+ metrics: metrics,
567
+ dimensions: dimensions,
568
+ filters: filters,
569
+ mode: mode,
570
+ }),
571
+ credentials: 'include',
572
+ });
573
+ return { status: response.status, json: await response.json() };
574
+ },
575
+
576
+ patchCube: async function (
577
+ name,
578
+ display_name,
579
+ description,
580
+ mode,
581
+ metrics,
582
+ dimensions,
583
+ filters,
584
+ owners,
585
+ ) {
586
+ const url = `${DJ_URL}/nodes/${name}`;
587
+ const response = await fetch(url, {
588
+ method: 'PATCH',
589
+ headers: {
590
+ 'Content-Type': 'application/json',
591
+ },
592
+ body: JSON.stringify({
593
+ display_name: display_name,
594
+ description: description,
595
+ metrics: metrics,
596
+ dimensions: dimensions,
597
+ filters: filters || [],
598
+ mode: mode,
599
+ owners: owners,
600
+ }),
601
+ credentials: 'include',
602
+ });
603
+ return { status: response.status, json: await response.json() };
604
+ },
605
+
606
+ refreshLatestMaterialization: async function (name) {
607
+ const url = `${DJ_URL}/nodes/${name}?refresh_materialization=true`;
608
+ const response = await fetch(url, {
609
+ method: 'PATCH',
610
+ headers: {
611
+ 'Content-Type': 'application/json',
612
+ },
613
+ body: JSON.stringify({}),
614
+ credentials: 'include',
615
+ });
616
+ return { status: response.status, json: await response.json() };
617
+ },
618
+
619
+ registerTable: async function (catalog, schema, table) {
620
+ const response = await fetch(
621
+ `${DJ_URL}/register/table/${catalog}/${schema}/${table}`,
622
+ {
623
+ method: 'POST',
624
+ headers: {
625
+ 'Content-Type': 'application/json',
626
+ },
627
+ credentials: 'include',
628
+ },
629
+ );
630
+ return { status: response.status, json: await response.json() };
631
+ },
632
+
11
633
  upstreams: async function (name) {
12
- const data = await (
13
- await fetch(DJ_URL + '/nodes/' + name + '/upstream/')
634
+ return await (
635
+ await fetch(`${DJ_URL}/nodes/${name}/upstream/`, {
636
+ credentials: 'include',
637
+ })
14
638
  ).json();
15
- return data;
16
639
  },
17
640
 
18
641
  downstreams: async function (name) {
19
- const data = await (
20
- await fetch(DJ_URL + '/nodes/' + name + '/downstream/')
642
+ return await (
643
+ await fetch(`${DJ_URL}/nodes/${name}/downstream/`, {
644
+ credentials: 'include',
645
+ })
646
+ ).json();
647
+ },
648
+
649
+ node_dag: async function (name) {
650
+ return await (
651
+ await fetch(`${DJ_URL}/nodes/${name}/dag/`, {
652
+ credentials: 'include',
653
+ })
654
+ ).json();
655
+ },
656
+
657
+ node_lineage: async function (name) {
658
+ return await (
659
+ await fetch(`${DJ_URL}/nodes/${name}/lineage/`, {
660
+ credentials: 'include',
661
+ })
21
662
  ).json();
22
- return data;
23
663
  },
24
664
 
25
665
  metric: async function (name) {
26
- const data = await (await fetch(DJ_URL + '/metrics/' + name + '/')).json();
27
- return data;
666
+ return await (
667
+ await fetch(`${DJ_URL}/metrics/${name}/`, {
668
+ credentials: 'include',
669
+ })
670
+ ).json();
671
+ },
672
+
673
+ clientCode: async function (name) {
674
+ return await (
675
+ await fetch(`${DJ_URL}/datajunction-clients/python/new_node/${name}`, {
676
+ credentials: 'include',
677
+ })
678
+ ).json();
679
+ },
680
+
681
+ cube: async function (name) {
682
+ return await (
683
+ await fetch(`${DJ_URL}/cubes/${name}/`, {
684
+ credentials: 'include',
685
+ })
686
+ ).json();
687
+ },
688
+
689
+ metrics: async function (name) {
690
+ return await (
691
+ await fetch(`${DJ_URL}/metrics/`, {
692
+ credentials: 'include',
693
+ })
694
+ ).json();
695
+ },
696
+
697
+ commonDimensions: async function (metrics) {
698
+ const metricsQuery = '?' + metrics.map(m => `metric=${m}`).join('&');
699
+ return await (
700
+ await fetch(`${DJ_URL}/metrics/common/dimensions/${metricsQuery}`, {
701
+ credentials: 'include',
702
+ })
703
+ ).json();
704
+ },
705
+
706
+ history: async function (type, name, offset, limit) {
707
+ return await (
708
+ await fetch(
709
+ `${DJ_URL}/history?node=${name}&offset=${offset ? offset : 0}&limit=${
710
+ limit ? limit : 100
711
+ }`,
712
+ {
713
+ credentials: 'include',
714
+ },
715
+ )
716
+ ).json();
717
+ },
718
+
719
+ revisions: async function (name) {
720
+ return await (
721
+ await fetch(`${DJ_URL}/nodes/${name}/revisions/`, {
722
+ credentials: 'include',
723
+ })
724
+ ).json();
725
+ },
726
+
727
+ namespace: async function (nmspce, editedBy) {
728
+ return await (
729
+ await fetch(
730
+ `${DJ_URL}/namespaces/${nmspce}?edited_by=${editedBy}&with_edited_by=true`,
731
+ {
732
+ credentials: 'include',
733
+ },
734
+ )
735
+ ).json();
28
736
  },
29
737
 
30
- namespace: async function (nmspce) {
738
+ namespaces: async function () {
739
+ return await (
740
+ await fetch(`${DJ_URL}/namespaces/`, {
741
+ credentials: 'include',
742
+ })
743
+ ).json();
744
+ },
745
+
746
+ sql: async function (metric_name, selection) {
747
+ const params = new URLSearchParams(selection);
748
+ for (const [key, value] of Object.entries(selection)) {
749
+ if (Array.isArray(value)) {
750
+ params.delete(key);
751
+ value.forEach(v => params.append(key, v));
752
+ }
753
+ }
754
+
755
+ return await (
756
+ await fetch(`${DJ_URL}/sql/${metric_name}?${params}`, {
757
+ credentials: 'include',
758
+ })
759
+ ).json();
760
+ },
761
+
762
+ nodesWithDimension: async function (name) {
763
+ return await (
764
+ await fetch(`${DJ_URL}/dimensions/${name}/nodes/`, {
765
+ credentials: 'include',
766
+ })
767
+ ).json();
768
+ },
769
+
770
+ materializations: async function (node) {
31
771
  const data = await (
32
- await fetch(DJ_URL + '/namespaces/' + nmspce + '/')
772
+ await fetch(
773
+ `${DJ_URL}/nodes/${node}/materializations?show_inactive=true&include_all_revisions=true`,
774
+ {
775
+ credentials: 'include',
776
+ },
777
+ )
33
778
  ).json();
779
+
34
780
  return data;
35
781
  },
36
782
 
37
- namespaces: async function () {
38
- const data = await (await fetch(DJ_URL + '/namespaces/')).json();
783
+ availabilityStates: async function (node) {
784
+ const data = await (
785
+ await fetch(`${DJ_URL}/nodes/${node}/availability/`, {
786
+ credentials: 'include',
787
+ })
788
+ ).json();
789
+
39
790
  return data;
40
791
  },
41
792
 
793
+ columns: async function (node) {
794
+ return await Promise.all(
795
+ node.columns.map(async col => {
796
+ return col;
797
+ }),
798
+ );
799
+ },
800
+
801
+ sqls: async function (metricSelection, dimensionSelection, filters) {
802
+ const params = new URLSearchParams();
803
+ metricSelection.map(metric => params.append('metrics', metric));
804
+ dimensionSelection.map(dimension => params.append('dimensions', dimension));
805
+ params.append('filters', filters);
806
+ return await (
807
+ await fetch(`${DJ_URL}/sql/?${params}`, {
808
+ credentials: 'include',
809
+ })
810
+ ).json();
811
+ },
812
+
813
+ data: async function (metricSelection, dimensionSelection) {
814
+ const params = new URLSearchParams();
815
+ metricSelection.map(metric => params.append('metrics', metric));
816
+ dimensionSelection.map(dimension => params.append('dimensions', dimension));
817
+ return await (
818
+ await fetch(`${DJ_URL}/data/?` + params + '&limit=10000', {
819
+ credentials: 'include',
820
+ })
821
+ ).json();
822
+ },
823
+
824
+ nodeData: async function (nodeName, selection = null) {
825
+ if (selection === null) {
826
+ selection = {
827
+ dimensions: [],
828
+ filters: [],
829
+ };
830
+ }
831
+ const params = new URLSearchParams(selection);
832
+ for (const [key, value] of Object.entries(selection)) {
833
+ if (Array.isArray(value)) {
834
+ params.delete(key);
835
+ value.forEach(v => params.append(key, v));
836
+ }
837
+ }
838
+ params.append('limit', '1000');
839
+ params.append('async_', 'true');
840
+
841
+ return await (
842
+ await fetch(`${DJ_URL}/data/${nodeName}?${params}`, {
843
+ credentials: 'include',
844
+ headers: { 'Cache-Control': 'max-age=86400' },
845
+ })
846
+ ).json();
847
+ },
848
+
849
+ notebookExportCube: async function (cube) {
850
+ return await fetch(
851
+ `${DJ_URL}/datajunction-clients/python/notebook/?cube=${cube}`,
852
+ {
853
+ credentials: 'include',
854
+ },
855
+ );
856
+ },
857
+
858
+ notebookExportNamespace: async function (namespace) {
859
+ return await (
860
+ await fetch(
861
+ `${DJ_URL}/datajunction-clients/python/notebook/?namespace=${namespace}`,
862
+ {
863
+ credentials: 'include',
864
+ },
865
+ )
866
+ ).json();
867
+ },
868
+
869
+ stream: async function (metricSelection, dimensionSelection, filters) {
870
+ const params = new URLSearchParams();
871
+ metricSelection.map(metric => params.append('metrics', metric));
872
+ dimensionSelection.map(dimension => params.append('dimensions', dimension));
873
+ params.append('filters', filters);
874
+ return new EventSource(
875
+ `${DJ_URL}/stream/?${params}&limit=10000&async_=true`,
876
+ {
877
+ withCredentials: true,
878
+ },
879
+ );
880
+ },
881
+
882
+ streamNodeData: async function (nodeName, selection = null) {
883
+ if (selection === null) {
884
+ selection = {
885
+ dimensions: [],
886
+ filters: [],
887
+ };
888
+ }
889
+ const params = new URLSearchParams(selection);
890
+ for (const [key, value] of Object.entries(selection)) {
891
+ if (Array.isArray(value)) {
892
+ params.delete(key);
893
+ value.forEach(v => params.append(key, v));
894
+ }
895
+ }
896
+ params.append('limit', '1000');
897
+ params.append('async_', 'true');
898
+
899
+ return new EventSource(`${DJ_URL}/stream/${nodeName}?${params}`, {
900
+ withCredentials: true,
901
+ });
902
+ },
903
+
42
904
  lineage: async function (node) {},
43
905
 
906
+ compiledSql: async function (node) {
907
+ return await (
908
+ await fetch(`${DJ_URL}/sql/${node}/`, {
909
+ credentials: 'include',
910
+ })
911
+ ).json();
912
+ },
913
+
44
914
  dag: async function (namespace = 'default') {
45
915
  const edges = [];
46
- const data = await (await fetch(DJ_URL + '/nodes/')).json();
47
-
48
- // const metrics = await (await fetch(DJ_URL + '/metrics/')).json();
916
+ const data = await (
917
+ await fetch(`${DJ_URL}/nodes/`, {
918
+ credentials: 'include',
919
+ })
920
+ ).json();
49
921
 
50
922
  data.forEach(obj => {
51
923
  obj.parents.forEach(parent => {
@@ -97,7 +969,6 @@ export const DataJunctionAPI = {
97
969
  const column_names = node.columns.map(col => {
98
970
  return { name: col.name, type: col.type };
99
971
  });
100
- // const dimensions = node.type === "metric" ? metrics.filter(metric => metric.name === node.name)[0].dimensions : [];
101
972
  return {
102
973
  id: String(node.name),
103
974
  type: 'DJNode',
@@ -112,13 +983,435 @@ export const DataJunctionAPI = {
112
983
  type: node.type,
113
984
  primary_key: primary_key,
114
985
  column_names: column_names,
115
- // dimensions: dimensions,
116
986
  },
117
- // parentNode: [node.name.split(".").slice(-2, -1)],
118
- // extent: 'parent',
119
987
  };
120
988
  });
121
989
 
122
990
  return { edges: edges, nodes: nodes, namespaces: namespaceNodes };
123
991
  },
992
+ attributes: async function () {
993
+ return await (
994
+ await fetch(`${DJ_URL}/attributes`, {
995
+ credentials: 'include',
996
+ })
997
+ ).json();
998
+ },
999
+ setAttributes: async function (nodeName, columnName, attributes) {
1000
+ const response = await fetch(
1001
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}/attributes`,
1002
+ {
1003
+ method: 'POST',
1004
+ headers: {
1005
+ 'Content-Type': 'application/json',
1006
+ },
1007
+ body: JSON.stringify(
1008
+ attributes.map(attribute => {
1009
+ return {
1010
+ namespace: 'system',
1011
+ name: attribute,
1012
+ };
1013
+ }),
1014
+ ),
1015
+ credentials: 'include',
1016
+ },
1017
+ );
1018
+ return { status: response.status, json: await response.json() };
1019
+ },
1020
+
1021
+ setColumnDescription: async function (nodeName, columnName, description) {
1022
+ const response = await fetch(
1023
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}/description?description=${encodeURIComponent(
1024
+ description,
1025
+ )}`,
1026
+ {
1027
+ method: 'PATCH',
1028
+ credentials: 'include',
1029
+ },
1030
+ );
1031
+ return { status: response.status, json: await response.json() };
1032
+ },
1033
+ dimensions: async function () {
1034
+ return await (
1035
+ await fetch(`${DJ_URL}/dimensions`, {
1036
+ credentials: 'include',
1037
+ })
1038
+ ).json();
1039
+ },
1040
+ nodeDimensions: async function (nodeName) {
1041
+ return await (
1042
+ await fetch(`${DJ_URL}/nodes/${nodeName}/dimensions`, {
1043
+ credentials: 'include',
1044
+ })
1045
+ ).json();
1046
+ },
1047
+ linkDimension: async function (nodeName, columnName, dimensionName) {
1048
+ const response = await fetch(
1049
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
1050
+ {
1051
+ method: 'POST',
1052
+ headers: {
1053
+ 'Content-Type': 'application/json',
1054
+ },
1055
+ credentials: 'include',
1056
+ },
1057
+ );
1058
+ return { status: response.status, json: await response.json() };
1059
+ },
1060
+ unlinkDimension: async function (nodeName, columnName, dimensionName) {
1061
+ const response = await fetch(
1062
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
1063
+ {
1064
+ method: 'DELETE',
1065
+ headers: {
1066
+ 'Content-Type': 'application/json',
1067
+ },
1068
+ credentials: 'include',
1069
+ },
1070
+ );
1071
+ return { status: response.status, json: await response.json() };
1072
+ },
1073
+
1074
+ addComplexDimensionLink: async function (
1075
+ nodeName,
1076
+ dimensionNode,
1077
+ joinOn,
1078
+ joinType = null,
1079
+ joinCardinality = null,
1080
+ role = null,
1081
+ ) {
1082
+ const response = await fetch(`${DJ_URL}/nodes/${nodeName}/link`, {
1083
+ method: 'POST',
1084
+ headers: {
1085
+ 'Content-Type': 'application/json',
1086
+ },
1087
+ body: JSON.stringify({
1088
+ dimensionNode: dimensionNode,
1089
+ joinType: joinType,
1090
+ joinOn: joinOn,
1091
+ joinCardinality: joinCardinality,
1092
+ role: role,
1093
+ }),
1094
+ credentials: 'include',
1095
+ });
1096
+ return { status: response.status, json: await response.json() };
1097
+ },
1098
+
1099
+ removeComplexDimensionLink: async function (
1100
+ nodeName,
1101
+ dimensionNode,
1102
+ role = null,
1103
+ ) {
1104
+ const response = await fetch(`${DJ_URL}/nodes/${nodeName}/link`, {
1105
+ method: 'DELETE',
1106
+ headers: {
1107
+ 'Content-Type': 'application/json',
1108
+ },
1109
+ body: JSON.stringify({
1110
+ dimensionNode: dimensionNode,
1111
+ role: role,
1112
+ }),
1113
+ credentials: 'include',
1114
+ });
1115
+ return { status: response.status, json: await response.json() };
1116
+ },
1117
+
1118
+ deactivate: async function (nodeName) {
1119
+ const response = await fetch(`${DJ_URL}/nodes/${nodeName}`, {
1120
+ method: 'DELETE',
1121
+ headers: {
1122
+ 'Content-Type': 'application/json',
1123
+ },
1124
+ credentials: 'include',
1125
+ });
1126
+ return { status: response.status, json: await response.json() };
1127
+ },
1128
+ addNamespace: async function (namespace) {
1129
+ const response = await fetch(`${DJ_URL}/namespaces/${namespace}`, {
1130
+ method: 'POST',
1131
+ headers: {
1132
+ 'Content-Type': 'application/json',
1133
+ },
1134
+ credentials: 'include',
1135
+ });
1136
+ return { status: response.status, json: await response.json() };
1137
+ },
1138
+ listTags: async function () {
1139
+ const response = await fetch(`${DJ_URL}/tags`, {
1140
+ method: 'GET',
1141
+ headers: {
1142
+ 'Content-Type': 'application/json',
1143
+ },
1144
+ credentials: 'include',
1145
+ });
1146
+ return await response.json();
1147
+ },
1148
+ users: async function () {
1149
+ return await (
1150
+ await fetch(`${DJ_URL}/users?with_activity=true`, {
1151
+ credentials: 'include',
1152
+ })
1153
+ ).json();
1154
+ },
1155
+ getTag: async function (tagName) {
1156
+ const response = await fetch(`${DJ_URL}/tags/${tagName}`, {
1157
+ method: 'GET',
1158
+ headers: {
1159
+ 'Content-Type': 'application/json',
1160
+ },
1161
+ credentials: 'include',
1162
+ });
1163
+ return await response.json();
1164
+ },
1165
+ listNodesForTag: async function (tagName) {
1166
+ const response = await fetch(`${DJ_URL}/tags/${tagName}/nodes`, {
1167
+ method: 'GET',
1168
+ headers: {
1169
+ 'Content-Type': 'application/json',
1170
+ },
1171
+ credentials: 'include',
1172
+ });
1173
+ return await response.json();
1174
+ },
1175
+ tagsNode: async function (nodeName, tagNames) {
1176
+ const url = tagNames
1177
+ .map(value => `tag_names=${encodeURIComponent(value)}`)
1178
+ .join('&');
1179
+ const response = await fetch(`${DJ_URL}/nodes/${nodeName}/tags?${url}`, {
1180
+ method: 'POST',
1181
+ headers: {
1182
+ 'Content-Type': 'application/json',
1183
+ },
1184
+ credentials: 'include',
1185
+ });
1186
+ return { status: response.status, json: await response.json() };
1187
+ },
1188
+ addTag: async function (name, displayName, tagType, description) {
1189
+ const response = await fetch(`${DJ_URL}/tags`, {
1190
+ method: 'POST',
1191
+ headers: {
1192
+ 'Content-Type': 'application/json',
1193
+ },
1194
+ body: JSON.stringify({
1195
+ name: name,
1196
+ display_name: displayName,
1197
+ tag_type: tagType,
1198
+ description: description,
1199
+ }),
1200
+ credentials: 'include',
1201
+ });
1202
+ return { status: response.status, json: await response.json() };
1203
+ },
1204
+ editTag: async function (name, description, displayName) {
1205
+ const updates = {};
1206
+ if (description) {
1207
+ updates.description = description;
1208
+ }
1209
+ if (displayName) {
1210
+ updates.display_name = displayName;
1211
+ }
1212
+
1213
+ const response = await fetch(`${DJ_URL}/tags/${name}`, {
1214
+ method: 'PATCH',
1215
+ headers: {
1216
+ 'Content-Type': 'application/json',
1217
+ },
1218
+ body: JSON.stringify(updates),
1219
+ credentials: 'include',
1220
+ });
1221
+ return { status: response.status, json: await response.json() };
1222
+ },
1223
+ setPartition: async function (
1224
+ nodeName,
1225
+ columnName,
1226
+ partitionType,
1227
+ format,
1228
+ granularity,
1229
+ ) {
1230
+ const body = {
1231
+ type_: partitionType,
1232
+ };
1233
+ if (format) {
1234
+ body.format = format;
1235
+ }
1236
+ if (granularity) {
1237
+ body.granularity = granularity;
1238
+ }
1239
+ const response = await fetch(
1240
+ `${DJ_URL}/nodes/${nodeName}/columns/${columnName}/partition`,
1241
+ {
1242
+ method: 'POST',
1243
+ headers: {
1244
+ 'Content-Type': 'application/json',
1245
+ },
1246
+ body: JSON.stringify(body),
1247
+ credentials: 'include',
1248
+ },
1249
+ );
1250
+ return { status: response.status, json: await response.json() };
1251
+ },
1252
+ materialize: async function (nodeName, jobType, strategy, schedule, config) {
1253
+ const response = await fetch(
1254
+ `${DJ_URL}/nodes/${nodeName}/materialization`,
1255
+ {
1256
+ method: 'POST',
1257
+ headers: {
1258
+ 'Content-Type': 'application/json',
1259
+ },
1260
+ body: JSON.stringify({
1261
+ job: jobType,
1262
+ strategy: strategy,
1263
+ schedule: schedule,
1264
+ config: config,
1265
+ }),
1266
+ credentials: 'include',
1267
+ },
1268
+ );
1269
+ return { status: response.status, json: await response.json() };
1270
+ },
1271
+ materializeCube: async function (
1272
+ nodeName,
1273
+ jobType,
1274
+ strategy,
1275
+ schedule,
1276
+ lookbackWindow,
1277
+ ) {
1278
+ const response = await fetch(
1279
+ `${DJ_URL}/nodes/${nodeName}/materialization`,
1280
+ {
1281
+ method: 'POST',
1282
+ headers: {
1283
+ 'Content-Type': 'application/json',
1284
+ },
1285
+ body: JSON.stringify({
1286
+ job: jobType,
1287
+ strategy: strategy,
1288
+ schedule: schedule,
1289
+ lookback_window: lookbackWindow,
1290
+ }),
1291
+ credentials: 'include',
1292
+ },
1293
+ );
1294
+ return { status: response.status, json: await response.json() };
1295
+ },
1296
+ runBackfill: async function (nodeName, materializationName, partitionValues) {
1297
+ const response = await fetch(
1298
+ `${DJ_URL}/nodes/${nodeName}/materializations/${materializationName}/backfill`,
1299
+ {
1300
+ method: 'POST',
1301
+ headers: {
1302
+ 'Content-Type': 'application/json',
1303
+ },
1304
+ body: JSON.stringify(
1305
+ partitionValues.map(partitionValue => {
1306
+ return {
1307
+ column_name: partitionValue.columnName,
1308
+ range: partitionValue.range,
1309
+ values: partitionValue.values,
1310
+ };
1311
+ }),
1312
+ ),
1313
+ credentials: 'include',
1314
+ },
1315
+ );
1316
+ return { status: response.status, json: await response.json() };
1317
+ },
1318
+ deleteMaterialization: async function (
1319
+ nodeName,
1320
+ materializationName,
1321
+ nodeVersion = null,
1322
+ ) {
1323
+ let url = `${DJ_URL}/nodes/${nodeName}/materializations?materialization_name=${materializationName}`;
1324
+ if (nodeVersion) {
1325
+ url += `&node_version=${nodeVersion}`;
1326
+ }
1327
+ const response = await fetch(url, {
1328
+ method: 'DELETE',
1329
+ headers: {
1330
+ 'Content-Type': 'application/json',
1331
+ },
1332
+ credentials: 'include',
1333
+ });
1334
+ return { status: response.status, json: await response.json() };
1335
+ },
1336
+ listMetricMetadata: async function () {
1337
+ const response = await fetch(`${DJ_URL}/metrics/metadata`, {
1338
+ method: 'GET',
1339
+ headers: {
1340
+ 'Content-Type': 'application/json',
1341
+ },
1342
+ credentials: 'include',
1343
+ });
1344
+ return await response.json();
1345
+ },
1346
+ materializationInfo: async function () {
1347
+ return await (
1348
+ await fetch(`${DJ_URL}/materialization/info`, {
1349
+ credentials: 'include',
1350
+ })
1351
+ ).json();
1352
+ },
1353
+ revalidate: async function (node) {
1354
+ return await (
1355
+ await fetch(`${DJ_URL}/nodes/${node}/validate`, {
1356
+ method: 'POST',
1357
+ headers: {
1358
+ 'Content-Type': 'application/json',
1359
+ },
1360
+ credentials: 'include',
1361
+ })
1362
+ ).json();
1363
+ },
1364
+ // GET /notifications/
1365
+ getNotificationPreferences: async function (params = {}) {
1366
+ const query = new URLSearchParams(params).toString();
1367
+ return await (
1368
+ await fetch(`${DJ_URL}/notifications/${query ? `?${query}` : ''}`, {
1369
+ credentials: 'include',
1370
+ })
1371
+ ).json();
1372
+ },
1373
+
1374
+ // POST /notifications/subscribe
1375
+ subscribeToNotifications: async function ({
1376
+ entity_type,
1377
+ entity_name,
1378
+ activity_types,
1379
+ alert_types,
1380
+ }) {
1381
+ const response = await fetch(`${DJ_URL}/notifications/subscribe`, {
1382
+ method: 'POST',
1383
+ headers: {
1384
+ 'Content-Type': 'application/json',
1385
+ },
1386
+ credentials: 'include',
1387
+ body: JSON.stringify({
1388
+ entity_type,
1389
+ entity_name,
1390
+ activity_types,
1391
+ alert_types,
1392
+ }),
1393
+ });
1394
+
1395
+ return {
1396
+ status: response.status,
1397
+ json: await response.json(),
1398
+ };
1399
+ },
1400
+
1401
+ // DELETE /notifications/unsubscribe
1402
+ unsubscribeFromNotifications: async function ({ entity_type, entity_name }) {
1403
+ const url = new URL(`${DJ_URL}/notifications/unsubscribe`);
1404
+ url.searchParams.append('entity_type', entity_type);
1405
+ url.searchParams.append('entity_name', entity_name);
1406
+
1407
+ const response = await fetch(url, {
1408
+ method: 'DELETE',
1409
+ credentials: 'include',
1410
+ });
1411
+
1412
+ return {
1413
+ status: response.status,
1414
+ json: await response.json(),
1415
+ };
1416
+ },
124
1417
  };