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
@@ -0,0 +1,49 @@
1
+ const NodeIcon = ({
2
+ width = '45px',
3
+ height = '45px',
4
+ color = '#cccccc',
5
+ style = {},
6
+ }) => (
7
+ <svg
8
+ width={width}
9
+ height={height}
10
+ viewBox="0 0 30 30"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ style={style}
14
+ data-testid="node-icon"
15
+ >
16
+ <path
17
+ opacity="0.317243"
18
+ fillRule="evenodd"
19
+ clipRule="evenodd"
20
+ d="M15 30C23.2843 30 30 23.2843 30 15C30 6.71573 23.2843 0 15 0C6.71573 0 0 6.71573 0 15C0 23.2843 6.71573 30 15 30Z"
21
+ fill={color}
22
+ ></path>
23
+ <path
24
+ fillRule="evenodd"
25
+ clipRule="evenodd"
26
+ d="M16.3333 7.6665C16.5144 7.6665 16.6787 7.73873 16.7988 7.85594C16.9229 7.97702 17 8.1461 17 8.33317V9.94265L18.7239 11.6665H20.3333C20.7015 11.6665 21 11.965 21 12.3332V20.3332C21 21.4377 20.1046 22.3332 19 22.3332H11C9.89543 22.3332 9 21.4377 9 20.3332V9.6665C9 8.56193 9.89543 7.6665 11 7.6665H16.3333ZM11 8.99984H15.6667L19.6667 12.9998V20.3332C19.6667 20.7014 19.3682 20.9998 19 20.9998H11C10.6318 20.9998 10.3333 20.7014 10.3333 20.3332V9.6665C10.3333 9.29831 10.6318 8.99984 11 8.99984ZM12.3333 14.9998H17.6667C18.0349 14.9998 18.3333 15.2983 18.3333 15.6665C18.3333 16.0347 18.0349 16.3332 17.6667 16.3332H12.3333C11.9651 16.3332 11.6667 16.0347 11.6667 15.6665C11.6667 15.2983 11.9651 14.9998 12.3333 14.9998ZM17.6667 17.6665H12.3333C11.9651 17.6665 11.6667 17.965 11.6667 18.3332C11.6667 18.7014 11.9651 18.9998 12.3333 18.9998H17.6667C18.0349 18.9998 18.3333 18.7014 18.3333 18.3332C18.3333 17.965 18.0349 17.6665 17.6667 17.6665ZM12.3333 12.3332H13.6667C14.0349 12.3332 14.3333 12.6316 14.3333 12.9998C14.3333 13.368 14.0349 13.6665 13.6667 13.6665H12.3333C11.9651 13.6665 11.6667 13.368 11.6667 12.9998C11.6667 12.6316 11.9651 12.3332 12.3333 12.3332Z"
27
+ fill={color}
28
+ ></path>
29
+ <mask
30
+ id="mask0"
31
+ type="alpha"
32
+ maskUnits="userSpaceOnUse"
33
+ x="9"
34
+ y="7"
35
+ width="12"
36
+ height="16"
37
+ >
38
+ <path
39
+ fillRule="evenodd"
40
+ clipRule="evenodd"
41
+ d="M16.3333 7.6665C16.5144 7.6665 16.6787 7.73873 16.7988 7.85594C16.9229 7.97702 17 8.1461 17 8.33317V9.94265L18.7239 11.6665H20.3333C20.7015 11.6665 21 11.965 21 12.3332V20.3332C21 21.4377 20.1046 22.3332 19 22.3332H11C9.89543 22.3332 9 21.4377 9 20.3332V9.6665C9 8.56193 9.89543 7.6665 11 7.6665H16.3333ZM11 8.99984H15.6667L19.6667 12.9998V20.3332C19.6667 20.7014 19.3682 20.9998 19 20.9998H11C10.6318 20.9998 10.3333 20.7014 10.3333 20.3332V9.6665C10.3333 9.29831 10.6318 8.99984 11 8.99984ZM12.3333 14.9998H17.6667C18.0349 14.9998 18.3333 15.2983 18.3333 15.6665C18.3333 16.0347 18.0349 16.3332 17.6667 16.3332H12.3333C11.9651 16.3332 11.6667 16.0347 11.6667 15.6665C11.6667 15.2983 11.9651 14.9998 12.3333 14.9998ZM17.6667 17.6665H12.3333C11.9651 17.6665 11.6667 17.965 11.6667 18.3332C11.6667 18.7014 11.9651 18.9998 12.3333 18.9998H17.6667C18.0349 18.9998 18.3333 18.7014 18.3333 18.3332C18.3333 17.965 18.0349 17.6665 17.6667 17.6665ZM12.3333 12.3332H13.6667C14.0349 12.3332 14.3333 12.6316 14.3333 12.9998C14.3333 13.368 14.0349 13.6665 13.6667 13.6665H12.3333C11.9651 13.6665 11.6667 13.368 11.6667 12.9998C11.6667 12.6316 11.9651 12.3332 12.3333 12.3332Z"
42
+ fill="white"
43
+ ></path>
44
+ </mask>
45
+ <g mask="url(#mask0)"></g>
46
+ </svg>
47
+ );
48
+
49
+ export default NodeIcon;
@@ -0,0 +1,14 @@
1
+ const PythonIcon = props => (
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ x="0px"
5
+ y="0px"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 48 48"
9
+ >
10
+ <path d="M 24 3 C 20.271429 3 18.240267 3.9470561 16.792969 4.5742188 L 16.791016 4.5742188 C 15.488673 5.1421213 14.704771 6.3187748 14.365234 7.4296875 C 14.025697 8.5406002 14 9.6506515 14 10.640625 L 14 14 L 10.640625 14 C 9.6506515 14 8.5406002 14.0257 7.4296875 14.365234 C 6.3187748 14.704771 5.1421212 15.488626 4.5742188 16.791016 L 4.5742188 16.792969 C 3.947056 18.24022 3 20.271429 3 24 C 3 27.728571 3.9470561 29.759733 4.5742188 31.207031 L 4.5742188 31.208984 C 5.1421212 32.511374 6.3187748 33.295229 7.4296875 33.634766 C 8.5406002 33.974256 9.6506515 34 10.640625 34 L 14 34 L 14 37.359375 C 14 38.349349 14.0257 39.459401 14.365234 40.570312 C 14.704771 41.681225 15.488626 42.857879 16.791016 43.425781 L 16.792969 43.425781 C 18.24022 44.052944 20.271429 45 24 45 C 27.728571 45 29.759733 44.052944 31.207031 43.425781 L 31.208984 43.425781 C 32.511374 42.857879 33.295229 41.681225 33.634766 40.570312 C 33.974256 39.459401 34 38.349349 34 37.359375 L 34 34 L 37.359375 34 C 38.349349 34 39.459401 33.9743 40.570312 33.634766 C 41.681225 33.295229 42.857879 32.511374 43.425781 31.208984 L 43.425781 31.207031 C 44.052944 29.75978 45 27.728571 45 24 C 45 20.271429 44.052944 18.240267 43.425781 16.792969 L 43.425781 16.791016 C 42.857879 15.488673 41.681225 14.704771 40.570312 14.365234 C 39.459401 14.025697 38.349349 14 37.359375 14 L 34 14 L 34 10.640625 C 34 9.6506515 33.974303 8.5406002 33.634766 7.4296875 C 33.295229 6.3187748 32.511374 5.1421213 31.208984 4.5742188 L 31.207031 4.5742188 C 29.75978 3.9470561 27.728571 3 24 3 z M 24 6 C 27.268623 6 28.459017 6.6519922 30.009766 7.3242188 C 30.427376 7.5063161 30.590162 7.7325533 30.765625 8.3066406 C 30.941088 8.8807279 31 9.7405985 31 10.640625 L 31 15.253906 A 1.50015 1.50015 0 0 0 31 15.740234 L 31 19 C 31 20.950062 29.450062 22.5 27.5 22.5 L 20.5 22.5 C 16.928062 22.5 14 25.428062 14 29 L 14 31 L 10.640625 31 C 9.7405985 31 8.8807279 30.941088 8.3066406 30.765625 C 7.7325533 30.590162 7.5063162 30.427376 7.3242188 30.009766 C 6.6519921 28.459017 6 27.268623 6 24 C 6 20.731377 6.6519921 19.540983 7.3242188 17.990234 C 7.5063161 17.572624 7.7325533 17.409838 8.3066406 17.234375 C 8.8807279 17.058912 9.7405985 17 10.640625 17 L 23.5 17 A 1.50015 1.50015 0 1 0 23.5 14 L 17 14 L 17 10.640625 C 17 9.7405985 17.058912 8.8807279 17.234375 8.3066406 C 17.409838 7.7325533 17.572624 7.5063162 17.990234 7.3242188 C 19.540983 6.6519921 20.731377 6 24 6 z M 20.5 9 A 1.5 1.5 0 0 0 20.5 12 A 1.5 1.5 0 0 0 20.5 9 z M 34 17 L 37.359375 17 C 38.259401 17 39.119272 17.05891 39.693359 17.234375 C 40.267447 17.409838 40.493684 17.572624 40.675781 17.990234 C 41.348008 19.540983 42 20.731377 42 24 C 42 27.268623 41.348008 28.459017 40.675781 30.009766 C 40.493684 30.427376 40.267447 30.590162 39.693359 30.765625 C 39.119272 30.941088 38.259401 31 37.359375 31 L 24.5 31 A 1.50015 1.50015 0 1 0 24.5 34 L 31 34 L 31 37.359375 C 31 38.259401 30.94109 39.119272 30.765625 39.693359 C 30.590162 40.267447 30.427376 40.493684 30.009766 40.675781 C 28.459017 41.348008 27.268623 42 24 42 C 20.731377 42 19.540983 41.348008 17.990234 40.675781 C 17.572624 40.493684 17.409838 40.267447 17.234375 39.693359 C 17.058912 39.119272 17 38.259401 17 37.359375 L 17 29 C 17 27.049938 18.549938 25.5 20.5 25.5 L 27.5 25.5 C 31.071938 25.5 34 22.571938 34 19 L 34 17 z M 27.5 36 A 1.5 1.5 0 0 0 27.5 39 A 1.5 1.5 0 0 0 27.5 36 z"></path>
11
+ </svg>
12
+ );
13
+
14
+ export default PythonIcon;
@@ -0,0 +1,14 @@
1
+ const TableIcon = props => (
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="16"
5
+ height="16"
6
+ fill="currentColor"
7
+ className="bi bi-table"
8
+ viewBox="0 0 16 16"
9
+ >
10
+ <path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z" />
11
+ </svg>
12
+ );
13
+
14
+ export default TableIcon;
@@ -0,0 +1,16 @@
1
+ const ValidIcon = ({ width = '25px', height = '25px', style = {} }) => (
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width={width}
5
+ height={height}
6
+ style={style}
7
+ fill="currentColor"
8
+ className="bi bi-check-circle-fill"
9
+ viewBox="0 0 16 16"
10
+ data-testid="valid-icon"
11
+ >
12
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
13
+ </svg>
14
+ );
15
+
16
+ export default ValidIcon;
package/src/app/index.tsx CHANGED
@@ -8,50 +8,131 @@ import { Helmet } from 'react-helmet-async';
8
8
  import { BrowserRouter, Routes, Route } from 'react-router-dom';
9
9
 
10
10
  import { NamespacePage } from './pages/NamespacePage/Loadable';
11
+ import { OverviewPage } from './pages/OverviewPage/Loadable';
11
12
  import { NodePage } from './pages/NodePage/Loadable';
13
+ import RevisionDiff from './pages/NodePage/RevisionDiff';
14
+ import { SQLBuilderPage } from './pages/SQLBuilderPage/Loadable';
15
+ import { CubeBuilderPage } from './pages/CubeBuilderPage/Loadable';
16
+ import { TagPage } from './pages/TagPage/Loadable';
17
+ import { AddEditNodePage } from './pages/AddEditNodePage/Loadable';
18
+ import { AddEditTagPage } from './pages/AddEditTagPage/Loadable';
12
19
  import { NotFoundPage } from './pages/NotFoundPage/Loadable';
20
+ import { LoginPage } from './pages/LoginPage';
21
+ import { RegisterTablePage } from './pages/RegisterTablePage';
13
22
  import { Root } from './pages/Root/Loadable';
14
- import { ListNamespacesPage } from './pages/ListNamespacesPage';
15
23
  import DJClientContext from './providers/djclient';
16
- import {DataJunctionAPI} from "./services/DJService";
24
+ import { DataJunctionAPI } from './services/DJService';
25
+ import { CookiesProvider, useCookies } from 'react-cookie';
26
+ import * as Constants from './constants';
17
27
 
18
28
  export function App() {
29
+ const [cookies] = useCookies([Constants.LOGGED_IN_FLAG_COOKIE]);
19
30
  return (
20
- <BrowserRouter>
21
- <Helmet
22
- titleTemplate="DataJunction: %s"
23
- defaultTitle="DataJunction: A Metrics Platform"
24
- >
25
- <meta
26
- name="description"
27
- content="DataJunction serves as a semantic layer to help manage metrics"
28
- />
29
- </Helmet>
30
- <DJClientContext.Provider value={{ DataJunctionAPI }}>
31
- <Routes>
32
- <Route
33
- path="/"
34
- element={<Root />}
35
- children={
36
- <>
37
- <Route path="nodes" key="nodes">
38
- <Route path=":name" element={<NodePage />} />
39
- </Route>
31
+ <CookiesProvider>
32
+ <BrowserRouter>
33
+ {cookies.__djlif || process.env.REACT_DISABLE_AUTH === 'true' ? (
34
+ <>
35
+ <Helmet
36
+ titleTemplate="DataJunction: %s"
37
+ defaultTitle="DataJunction: A Metrics Platform"
38
+ >
39
+ <meta
40
+ name="description"
41
+ content="DataJunction serves as a semantic layer to help manage metrics"
42
+ />
43
+ </Helmet>
44
+ <DJClientContext.Provider value={{ DataJunctionAPI }}>
45
+ <Routes>
46
+ <Route
47
+ path="/"
48
+ element={<Root />}
49
+ children={
50
+ <>
51
+ <Route path="nodes" key="nodes">
52
+ <Route path=":name" element={<NodePage />} />
53
+ <Route
54
+ path=":name/edit"
55
+ key="edit"
56
+ element={<AddEditNodePage />}
57
+ />
58
+ <Route
59
+ path=":name/edit-cube"
60
+ key="edit-cube"
61
+ element={<CubeBuilderPage />}
62
+ />
63
+ <Route
64
+ path=":name/revisions/:revision"
65
+ element={<RevisionDiff />}
66
+ />
67
+ <Route path=":name/:tab" element={<NodePage />} />
68
+ </Route>
40
69
 
41
- <Route path="/" element={<ListNamespacesPage />} key="index" />
42
- <Route path="namespaces">
43
- <Route
44
- path=":namespace"
45
- element={<NamespacePage />}
46
- key="namespaces"
47
- />
48
- </Route>
49
- </>
50
- }
51
- />
52
- <Route path="*" element={<NotFoundPage />} />
53
- </Routes>
54
- </DJClientContext.Provider>
55
- </BrowserRouter>
70
+ <Route path="/" element={<NamespacePage />} key="index" />
71
+ <Route path="namespaces">
72
+ <Route
73
+ path=":namespace"
74
+ element={<NamespacePage />}
75
+ key="namespaces"
76
+ />
77
+ </Route>
78
+ <Route
79
+ path="create/tag"
80
+ key="createtag"
81
+ element={<AddEditTagPage />}
82
+ ></Route>
83
+ <Route
84
+ path="create/source"
85
+ key="register"
86
+ element={<RegisterTablePage />}
87
+ ></Route>
88
+ <Route path="/create/cube">
89
+ <Route
90
+ path=":initialNamespace"
91
+ key="create"
92
+ element={<CubeBuilderPage />}
93
+ />
94
+ <Route
95
+ path=""
96
+ key="create"
97
+ element={<CubeBuilderPage />}
98
+ />
99
+ </Route>
100
+ <Route path="create/:nodeType">
101
+ <Route
102
+ path=":initialNamespace"
103
+ key="create"
104
+ element={<AddEditNodePage />}
105
+ />
106
+ <Route
107
+ path=""
108
+ key="create"
109
+ element={<AddEditNodePage />}
110
+ />
111
+ </Route>
112
+ <Route
113
+ path="sql"
114
+ key="sql"
115
+ element={<SQLBuilderPage />}
116
+ />
117
+ <Route path="tags" key="tags">
118
+ <Route path=":name" element={<TagPage />} />
119
+ </Route>
120
+ <Route
121
+ path="overview"
122
+ key="overview"
123
+ element={<OverviewPage />}
124
+ />
125
+ </>
126
+ }
127
+ />
128
+ <Route path="*" element={<NotFoundPage />} />
129
+ </Routes>
130
+ </DJClientContext.Provider>
131
+ </>
132
+ ) : (
133
+ <LoginPage />
134
+ )}
135
+ </BrowserRouter>
136
+ </CookiesProvider>
56
137
  );
57
138
  }
@@ -0,0 +1,10 @@
1
+ import AlertIcon from '../../icons/AlertIcon';
2
+
3
+ export const AlertMessage = ({ message }) => {
4
+ return (
5
+ <div className="message alert">
6
+ <AlertIcon />
7
+ {message}
8
+ </div>
9
+ );
10
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Component for selecting node columns based on the current form state
3
+ */
4
+ import { ErrorMessage, useFormikContext } from 'formik';
5
+ import { useContext, useMemo, useState, useEffect } from 'react';
6
+ import DJClientContext from '../../providers/djclient';
7
+ import { FormikSelect } from './FormikSelect';
8
+
9
+ export const ColumnsSelect = ({
10
+ defaultValue,
11
+ fieldName,
12
+ label,
13
+ labelStyle = {},
14
+ isMulti = false,
15
+ isClearable = true,
16
+ }) => {
17
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
18
+
19
+ // Used to pull out current form values for node validation
20
+ const { values } = useFormikContext();
21
+
22
+ // The available columns, determined from validating the node query
23
+ const [availableColumns, setAvailableColumns] = useState([]);
24
+ const selectableOptions = useMemo(() => {
25
+ if (availableColumns && availableColumns.length > 0) {
26
+ return availableColumns;
27
+ }
28
+ }, [availableColumns]);
29
+
30
+ // Fetch columns by validating the latest node query
31
+ const fetchColumns = async () => {
32
+ try {
33
+ const { status, json } = await djClient.validateNode(
34
+ values.type,
35
+ values.name,
36
+ values.display_name,
37
+ values.description,
38
+ values.query,
39
+ );
40
+ if (json?.columns) {
41
+ setAvailableColumns(
42
+ json.columns.map(col => ({ value: col.name, label: col.name })),
43
+ );
44
+ }
45
+ } catch (error) {
46
+ console.error('Error fetching columns:', error);
47
+ }
48
+ };
49
+
50
+ useEffect(() => {
51
+ fetchColumns();
52
+ }, [values.type, values.name, values.query]);
53
+
54
+ return (
55
+ <div className="CubeCreationInput">
56
+ <ErrorMessage name={fieldName} component="span" />
57
+ <label htmlFor={fieldName} style={labelStyle}>
58
+ {label}
59
+ </label>
60
+ <span data-testid={`select-${fieldName}`}>
61
+ <FormikSelect
62
+ className={isMulti ? 'MultiSelectInput' : 'SelectInput'}
63
+ defaultValue={
64
+ isMulti
65
+ ? defaultValue.map(val => {
66
+ return {
67
+ value: val,
68
+ label: val,
69
+ };
70
+ })
71
+ : defaultValue
72
+ ? { value: defaultValue, label: defaultValue }
73
+ : null
74
+ }
75
+ selectOptions={selectableOptions}
76
+ formikFieldName={fieldName}
77
+ onFocus={event => fetchColumns(event)}
78
+ isMulti={isMulti}
79
+ isClearable={isClearable}
80
+ />
81
+ </span>
82
+ </div>
83
+ );
84
+ };
@@ -0,0 +1,17 @@
1
+ import { ErrorMessage, Field } from 'formik';
2
+
3
+ export const DescriptionField = () => {
4
+ return (
5
+ <div className="DescriptionInput NodeCreationInput">
6
+ <ErrorMessage name="description" component="span" />
7
+ <label htmlFor="Description">Description</label>
8
+ <Field
9
+ type="textarea"
10
+ as="textarea"
11
+ name="description"
12
+ id="Description"
13
+ placeholder="Describe your node"
14
+ />
15
+ </div>
16
+ );
17
+ };
@@ -0,0 +1,16 @@
1
+ import { ErrorMessage, Field } from 'formik';
2
+
3
+ export const DisplayNameField = () => {
4
+ return (
5
+ <div className="DisplayNameInput NodeCreationInput">
6
+ <ErrorMessage name="display_name" component="span" />
7
+ <label htmlFor="displayName">Display Name *</label>
8
+ <Field
9
+ type="text"
10
+ name="display_name"
11
+ id="displayName"
12
+ placeholder="Human readable display name"
13
+ />
14
+ </div>
15
+ );
16
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A React Select component for use in Formik forms.
3
+ */
4
+ import { useField } from 'formik';
5
+ import Select from 'react-select';
6
+
7
+ export const FormikSelect = ({
8
+ selectOptions,
9
+ formikFieldName,
10
+ placeholder,
11
+ defaultValue,
12
+ style,
13
+ className = 'SelectInput',
14
+ isMulti = false,
15
+ isClearable = false,
16
+ onFocus = event => {},
17
+ }) => {
18
+ // eslint-disable-next-line no-unused-vars
19
+ const [field, _, helpers] = useField(formikFieldName);
20
+ const { setValue } = helpers;
21
+
22
+ // handles both multi-select and single-select cases
23
+ const getValue = options => {
24
+ if (options) {
25
+ return isMulti ? options.map(option => option.value) : options.value;
26
+ } else {
27
+ return isMulti ? [] : '';
28
+ }
29
+ };
30
+
31
+ return (
32
+ <Select
33
+ className={className}
34
+ defaultValue={defaultValue}
35
+ options={selectOptions}
36
+ name={field.name}
37
+ placeholder={placeholder}
38
+ onBlur={field.onBlur}
39
+ onChange={selected => setValue(getValue(selected))}
40
+ styles={style}
41
+ isMulti={isMulti}
42
+ isClearable={isClearable}
43
+ onFocus={event => onFocus(event)}
44
+ id={field.name}
45
+ />
46
+ );
47
+ };
48
+
49
+ FormikSelect.defaultProps = {
50
+ placeholder: '',
51
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * A field for the full node name, which is generated based on the node's input
3
+ * namespace and display name.
4
+ */
5
+ import { useField, useFormikContext } from 'formik';
6
+ import { useEffect } from 'react';
7
+
8
+ export const FullNameField = props => {
9
+ const { values, setFieldValue } = useFormikContext();
10
+ const [field, meta] = useField(props);
11
+
12
+ useEffect(() => {
13
+ // Set the value of the node's full name based on its namespace and display name
14
+ if (values.namespace || values.display_name) {
15
+ setFieldValue(
16
+ props.name,
17
+ `${values.namespace}.${values.display_name
18
+ .toLowerCase()
19
+ .replace(/ /g, '_')
20
+ .replace(/[^a-zA-Z0-9_]/g, '')}` || '',
21
+ );
22
+ }
23
+ }, [setFieldValue, props.name, values]);
24
+
25
+ return (
26
+ <>
27
+ <input
28
+ {...props}
29
+ {...field}
30
+ className="FullNameField"
31
+ disabled="disabled"
32
+ id="FullName"
33
+ value={values.name || ''}
34
+ />
35
+ {!!meta.touched && !!meta.error && <div>{meta.error}</div>}
36
+ </>
37
+ );
38
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Asynchronously loads the component for the Node page
3
+ */
4
+
5
+ import * as React from 'react';
6
+ import { lazyLoad } from '../../../utils/loadable';
7
+
8
+ export const LazyAddEditNodePage = props => {
9
+ return lazyLoad(
10
+ () => import('./index'),
11
+ module => module.AddEditNodePage,
12
+ {
13
+ fallback: <div></div>,
14
+ },
15
+ )(props);
16
+ };
17
+
18
+ export const AddEditNodePage = props => {
19
+ return <LazyAddEditNodePage {...props} />;
20
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Metric unit select component
3
+ */
4
+ import { ErrorMessage, Field } from 'formik';
5
+ import { useContext, useEffect, useState } from 'react';
6
+ import DJClientContext from '../../providers/djclient';
7
+ import { labelize } from '../../../utils/form';
8
+
9
+ export const MetricMetadataFields = () => {
10
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
11
+
12
+ // Metric metadata
13
+ const [metricUnits, setMetricUnits] = useState([]);
14
+ const [metricDirections, setMetricDirections] = useState([]);
15
+
16
+ // Get metric metadata values
17
+ useEffect(() => {
18
+ const fetchData = async () => {
19
+ const metadata = await djClient.listMetricMetadata();
20
+ setMetricDirections(metadata.directions);
21
+ setMetricUnits(metadata.units);
22
+ };
23
+ fetchData().catch(console.error);
24
+ }, [djClient]);
25
+
26
+ return (
27
+ <div
28
+ style={{
29
+ borderRadius: '8px',
30
+ padding: '10px 10px 20px 10px',
31
+ margin: '32px 0',
32
+ background: '#f9f9f9',
33
+ width: 'max-content',
34
+ display: 'flex',
35
+ }}
36
+ >
37
+ <div style={{ margin: '15px 25px' }}>
38
+ <ErrorMessage name="metric_direction" component="span" />
39
+ <label htmlFor="MetricDirection">Metric Direction</label>
40
+ <Field as="select" name="metric_direction" id="MetricDirection">
41
+ <option value=""></option>
42
+ {metricDirections.map(direction => (
43
+ <option value={direction} key={direction}>
44
+ {labelize(direction)}
45
+ </option>
46
+ ))}
47
+ </Field>
48
+ </div>
49
+ <div style={{ margin: '15px 25px' }}>
50
+ <ErrorMessage name="metric_unit" component="span" />
51
+ <label htmlFor="MetricUnit">Metric Unit</label>
52
+ <Field as="select" name="metric_unit" id="MetricUnit">
53
+ <option value=""></option>
54
+ {metricUnits.map(unit => (
55
+ <option value={unit.name} key={unit.name}>
56
+ {unit.label}
57
+ </option>
58
+ ))}
59
+ </Field>
60
+ </div>
61
+ <div style={{ margin: '15px 25px' }}>
62
+ <ErrorMessage name="significant_digits" component="span" />
63
+ <label htmlFor="SignificantDigits">Significant Digits</label>
64
+ <Field as="select" name="significant_digits" id="SignificantDigits">
65
+ <option value=""></option>
66
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(val => (
67
+ <option value={val} key={val}>
68
+ {val}
69
+ </option>
70
+ ))}
71
+ </Field>
72
+ </div>
73
+ </div>
74
+ );
75
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Metric aggregate expression input field, which consists of a CodeMirror SQL
3
+ * editor with autocompletion for node columns and syntax highlighting.
4
+ */
5
+ import React from 'react';
6
+ import { ErrorMessage, Field, useFormikContext } from 'formik';
7
+ import CodeMirror from '@uiw/react-codemirror';
8
+ import { langs } from '@uiw/codemirror-extensions-langs';
9
+
10
+ export const MetricQueryField = ({ djClient, value }) => {
11
+ const [schema, setSchema] = React.useState([]);
12
+ const formik = useFormikContext();
13
+ const sqlExt = langs.sql({ schema: schema });
14
+
15
+ const initialAutocomplete = async context => {
16
+ // Based on the selected upstream, we load the upstream node's columns
17
+ // into the autocomplete schema
18
+ const nodeName = formik.values['upstream_node'];
19
+ const nodeDetails = await djClient.node(nodeName);
20
+ nodeDetails.columns.forEach(col => {
21
+ schema[col.name] = [];
22
+ });
23
+ setSchema(schema);
24
+ };
25
+
26
+ const updateFormik = val => {
27
+ formik.setFieldValue('aggregate_expression', val);
28
+ };
29
+
30
+ return (
31
+ <div className="QueryInput MetricQueryInput NodeCreationInput">
32
+ <ErrorMessage name="query" component="span" />
33
+ <label htmlFor="Query">Aggregate Expression *</label>
34
+ <Field
35
+ type="textarea"
36
+ style={{ display: 'none' }}
37
+ as="textarea"
38
+ name="aggregate_expression"
39
+ id="Query"
40
+ />
41
+ <div role="button" tabIndex={0} className="relative flex bg-[#282a36]">
42
+ <CodeMirror
43
+ id={'aggregate_expression'}
44
+ name={'aggregate_expression'}
45
+ extensions={[
46
+ sqlExt,
47
+ sqlExt.language.data.of({
48
+ autocomplete: initialAutocomplete,
49
+ }),
50
+ ]}
51
+ value={value}
52
+ options={{
53
+ theme: 'default',
54
+ lineNumbers: true,
55
+ }}
56
+ width="100%"
57
+ height="100px"
58
+ style={{
59
+ margin: '0 0 23px 0',
60
+ flex: 1,
61
+ fontSize: '150%',
62
+ textAlign: 'left',
63
+ }}
64
+ onChange={(value, viewUpdate) => {
65
+ updateFormik(value);
66
+ }}
67
+ />
68
+ </div>
69
+ </div>
70
+ );
71
+ };