datajunction-ui 0.0.1-rc.9 → 0.0.2-0.dev1

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