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,18 +1,101 @@
1
- import { Component } from 'react';
1
+ import { useState, useContext, useEffect } from 'react';
2
2
  import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
3
3
  import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
4
4
  import sql from 'react-syntax-highlighter/dist/esm/languages/hljs/sql';
5
- import { format } from 'sql-formatter';
6
-
7
5
  import NodeStatus from './NodeStatus';
8
6
  import ListGroupItem from '../../components/ListGroupItem';
7
+ import ToggleSwitch from '../../components/ToggleSwitch';
8
+ import DJClientContext from '../../providers/djclient';
9
+ import { labelize } from '../../../utils/form';
9
10
 
10
11
  SyntaxHighlighter.registerLanguage('sql', sql);
11
12
  foundation.hljs['padding'] = '2rem';
12
13
 
13
- export default class NodeInfoTab extends Component {
14
- nodeTags = this.props.node?.tags.map(tag => <div>{tag}</div>);
15
- queryDiv = this.props.node?.query ? (
14
+ export default function NodeInfoTab({ node }) {
15
+ const [compiledSQL, setCompiledSQL] = useState('');
16
+ const [checked, setChecked] = useState(false);
17
+ const nodeTags = node?.tags.map(tag => (
18
+ <div className={'badge tag_value'}>
19
+ <a href={`/tags/${tag.name}`}>{tag.display_name}</a>
20
+ </div>
21
+ ));
22
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
23
+
24
+ useEffect(() => {
25
+ const fetchData = async () => {
26
+ if (checked === true) {
27
+ const data = await djClient.compiledSql(node.name);
28
+ if (data.sql) {
29
+ setCompiledSQL(data.sql);
30
+ } else {
31
+ setCompiledSQL(
32
+ '/* Ran into an issue while generating compiled SQL */',
33
+ );
34
+ }
35
+ }
36
+ };
37
+ fetchData().catch(console.error);
38
+ }, [node, djClient, checked]);
39
+ function toggle(value) {
40
+ return !value;
41
+ }
42
+ const metricsWarning =
43
+ node?.type === 'metric' && node?.incompatible_druid_functions.length > 0 ? (
44
+ <div className="message warning" style={{ marginTop: '0.7rem' }}>
45
+ ⚠{' '}
46
+ <small>
47
+ The following functions used in the metric definition may not be
48
+ compatible with Druid SQL:{' '}
49
+ {node?.incompatible_druid_functions.map(func => (
50
+ <li
51
+ style={{ listStyleType: 'none', margin: '0.7rem 0.7rem' }}
52
+ key={func}
53
+ >
54
+ ⇢{' '}
55
+ <span style={{ background: '#fff', padding: '0.3rem' }}>
56
+ {func}
57
+ </span>
58
+ </li>
59
+ ))}
60
+ If you need your metrics to be compatible with Druid, please use{' '}
61
+ <a
62
+ href={
63
+ 'https://druid.apache.org/docs/latest/querying/sql-functions/'
64
+ }
65
+ >
66
+ these supported functions
67
+ </a>
68
+ .
69
+ </small>
70
+ </div>
71
+ ) : (
72
+ ''
73
+ );
74
+
75
+ const metricQueryDiv =
76
+ node?.type === 'metric' ? (
77
+ <div className="list-group-item d-flex">
78
+ <div className="gap-2 w-100 justify-content-between py-3">
79
+ <div style={{ marginBottom: '30px' }}>
80
+ <h6 className="mb-0 w-100">Upstream Node</h6>
81
+ <p>
82
+ <a href={`/nodes/${node?.upstream_node}`}>
83
+ {node?.upstream_node}
84
+ </a>
85
+ </p>
86
+ </div>
87
+ <div>
88
+ <h6 className="mb-0 w-100">Aggregate Expression</h6>
89
+ <SyntaxHighlighter language="sql" style={foundation}>
90
+ {node?.expression}
91
+ </SyntaxHighlighter>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ ) : (
96
+ ''
97
+ );
98
+ const queryDiv = node?.query ? (
16
99
  <div className="list-group-item d-flex">
17
100
  <div className="d-flex gap-2 w-100 justify-content-between py-3">
18
101
  <div
@@ -21,15 +104,18 @@ export default class NodeInfoTab extends Component {
21
104
  }}
22
105
  >
23
106
  <h6 className="mb-0 w-100">Query</h6>
107
+ {['metric', 'dimension', 'transform'].indexOf(node?.type) > -1 ? (
108
+ <ToggleSwitch
109
+ id="toggleSwitch"
110
+ checked={checked}
111
+ onChange={() => setChecked(toggle)}
112
+ toggleName="Show Compiled SQL"
113
+ />
114
+ ) : (
115
+ <></>
116
+ )}
24
117
  <SyntaxHighlighter language="sql" style={foundation}>
25
- {format(this.props.node?.query, {
26
- language: 'spark',
27
- tabWidth: 2,
28
- keywordCase: 'upper',
29
- denseOperators: true,
30
- logicalOperatorNewline: 'before',
31
- expressionWidth: 10,
32
- })}
118
+ {checked ? compiledSQL : node?.query}
33
119
  </SyntaxHighlighter>
34
120
  </div>
35
121
  </div>
@@ -38,50 +124,261 @@ export default class NodeInfoTab extends Component {
38
124
  <></>
39
125
  );
40
126
 
41
- render() {
127
+ const displayCubeElement = cubeElem => {
42
128
  return (
43
- <div className="list-group align-items-center justify-content-between flex-md-row gap-2">
44
- <ListGroupItem
45
- label="Description"
46
- value={this.props.node?.description}
47
- />
48
- <div className="list-group-item d-flex">
49
- <div className="d-flex gap-2 w-100 justify-content-between py-3">
50
- <div>
51
- <h6 className="mb-0 w-100">Version</h6>
129
+ <div
130
+ className="button-3 cube-element"
131
+ key={cubeElem.name}
132
+ role="cell"
133
+ aria-label="CubeElement"
134
+ aria-hidden="false"
135
+ >
136
+ <a href={`/nodes/${cubeElem.node_name}`}>
137
+ {cubeElem.type === 'dimension'
138
+ ? labelize(cubeElem.node_name.split('.').slice(-1)[0]) + ' → '
139
+ : ''}
140
+ {cubeElem.display_name}
141
+ </a>
142
+ <span
143
+ className={`badge node_type__${
144
+ cubeElem.type === 'metric' ? cubeElem.type : 'dimension'
145
+ }`}
146
+ style={{ fontSize: '100%', textTransform: 'capitalize' }}
147
+ >
148
+ {cubeElem.type === 'metric' ? cubeElem.type : 'dimension'}
149
+ </span>
150
+ </div>
151
+ );
152
+ };
52
153
 
53
- <p className="mb-0 opacity-75">
154
+ const metricMetadataDiv =
155
+ node?.type === 'metric' ? (
156
+ <div className="list-group-item d-flex">
157
+ <div className="d-flex gap-2 w-100 py-3">
158
+ <div>
159
+ <h6 className="mb-0 w-100">Direction</h6>
160
+ <p
161
+ className="mb-0 opacity-75"
162
+ role="dialog"
163
+ aria-hidden="false"
164
+ aria-label="MetricDirection"
165
+ >
166
+ {node?.metric_metadata?.direction
167
+ ? labelize(node?.metric_metadata?.direction?.toLowerCase())
168
+ : 'None'}
169
+ </p>
170
+ </div>
171
+ <div style={{ marginRight: '2rem' }}>
172
+ <h6 className="mb-0 w-100">Unit</h6>
173
+ <p
174
+ className="mb-0 opacity-75"
175
+ role="dialog"
176
+ aria-hidden="false"
177
+ aria-label="MetricUnit"
178
+ >
179
+ {node?.metric_metadata?.unit?.name
180
+ ? labelize(node?.metric_metadata?.unit?.name?.toLowerCase())
181
+ : 'None'}
182
+ </p>
183
+ </div>
184
+ <div style={{ marginRight: '2rem' }}>
185
+ <h6 className="mb-0 w-100">Significant Digits</h6>
186
+ <p
187
+ className="mb-0 opacity-75"
188
+ role="dialog"
189
+ aria-hidden="false"
190
+ aria-label="SignificantDigits"
191
+ >
192
+ {node?.metric_metadata?.significantDigits || 'None'}
193
+ </p>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ ) : (
198
+ ''
199
+ );
200
+
201
+ const customMetadataDiv =
202
+ node?.custom_metadata && Object.keys(node.custom_metadata).length > 0 ? (
203
+ <div className="list-group-item d-flex">
204
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
205
+ <div
206
+ style={{
207
+ width: window.innerWidth * 0.8,
208
+ }}
209
+ >
210
+ <h6 className="mb-0 w-100">Custom Metadata</h6>
211
+ <SyntaxHighlighter language="json" style={foundation}>
212
+ {JSON.stringify(node.custom_metadata, null, 2)}
213
+ </SyntaxHighlighter>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ ) : (
218
+ ''
219
+ );
220
+
221
+ const cubeElementsDiv = node?.cube_elements ? (
222
+ <div className="list-group-item d-flex">
223
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
224
+ <div
225
+ style={{
226
+ width: window.innerWidth * 0.8,
227
+ }}
228
+ >
229
+ <h6 className="mb-0 w-100">Cube Elements</h6>
230
+ <div className={`list-group-item`}>
231
+ {node.cube_elements.map(cubeElem =>
232
+ cubeElem.type === 'metric' ? displayCubeElement(cubeElem) : '',
233
+ )}
234
+ {node.cube_elements.map(cubeElem =>
235
+ cubeElem.type !== 'metric' ? displayCubeElement(cubeElem) : '',
236
+ )}
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ ) : (
242
+ <></>
243
+ );
244
+
245
+ const primaryKeyOrRequiredDims = (
246
+ <div style={{ maxWidth: '25%' }}>
247
+ <h6 className="mb-0 w-100">
248
+ {node?.type !== 'metric' ? 'Primary Key' : 'Required Dimensions'}
249
+ </h6>
250
+ <p
251
+ className="mb-0 opacity-75"
252
+ role="dialog"
253
+ aria-hidden="false"
254
+ aria-label={
255
+ node?.type !== 'metric' ? 'PrimaryKey' : 'RequiredDimensions'
256
+ }
257
+ >
258
+ {node?.type !== 'metric'
259
+ ? node?.primary_key?.map(dim => (
260
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
261
+ <a href={`/nodes/${node?.name}`}>{dim}</a>
262
+ </span>
263
+ ))
264
+ : node?.required_dimensions?.map(dim => (
265
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
266
+ <a href={`/nodes/${node?.upstream_node}`}>{dim.name}</a>
267
+ </span>
268
+ ))}
269
+ </p>
270
+ </div>
271
+ );
272
+ return (
273
+ <div
274
+ className="list-group align-items-center justify-content-between flex-md-row gap-2"
275
+ style={{ minWidth: '700px' }}
276
+ >
277
+ {node?.type === 'metric' ? metricsWarning : ''}
278
+ <ListGroupItem label="Description" value={node?.description} />
279
+ <div className="list-group-item d-flex">
280
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
281
+ <div>
282
+ <h6 className="mb-0 w-100">Owners</h6>
283
+ <p className="mb-0 opacity-75">
284
+ {node?.owners.map(owner => (
54
285
  <span
55
- className="rounded-pill badge bg-secondary-soft"
56
- style={{ marginLeft: '0.5rem', fontSize: '100%' }}
286
+ className="badge node_type__transform"
287
+ style={{ margin: '2px', fontSize: '16px', cursor: 'pointer' }}
57
288
  >
58
- {this.props.node?.version}
289
+ {owner.username}
59
290
  </span>
60
- </p>
61
- </div>
62
- <div>
63
- <h6 className="mb-0 w-100">Status</h6>
64
- <p className="mb-0 opacity-75">
65
- <NodeStatus node={this.props.node} />
66
- </p>
67
- </div>
291
+ ))}
292
+ </p>
293
+ </div>
294
+ </div>
295
+ </div>
296
+ <div className="list-group-item d-flex">
297
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
298
+ <div>
299
+ <h6 className="mb-0 w-100">Version</h6>
300
+
301
+ <p className="mb-0 opacity-75">
302
+ <span
303
+ className="rounded-pill badge bg-secondary-soft"
304
+ style={{ marginLeft: '0.5rem', fontSize: '100%' }}
305
+ role="dialog"
306
+ aria-hidden="false"
307
+ aria-label="Version"
308
+ >
309
+ {node?.version}
310
+ </span>
311
+ </p>
312
+ </div>
313
+ {node.type === 'source' ? (
68
314
  <div>
69
- <h6 className="mb-0 w-100">Mode</h6>
70
- <p className="mb-0 opacity-75">
71
- <span className="status">{this.props.node?.mode}</span>
315
+ <h6 className="mb-0 w-100">Table</h6>
316
+ <p
317
+ className="mb-0 opacity-75"
318
+ role="dialog"
319
+ aria-hidden="false"
320
+ aria-label="Table"
321
+ >
322
+ {node?.catalog.name}.{node?.schema_}.{node?.table}
72
323
  </p>
73
324
  </div>
74
- <div>
75
- <h6 className="mb-0 w-100">Tags</h6>
76
- <p className="mb-0 opacity-75">{this.nodeTags}</p>
77
- </div>
325
+ ) : (
326
+ <></>
327
+ )}
328
+ <div>
329
+ <h6 className="mb-0 w-100">Status</h6>
330
+ <p
331
+ className="mb-0 opacity-75"
332
+ role="dialog"
333
+ aria-hidden="false"
334
+ aria-label="NodeStatus"
335
+ >
336
+ <NodeStatus node={node} />
337
+ </p>
338
+ </div>
339
+ <div>
340
+ <h6 className="mb-0 w-100">Mode</h6>
341
+ <p className="mb-0 opacity-75">
342
+ <span
343
+ className="status"
344
+ role="dialog"
345
+ aria-hidden="false"
346
+ aria-label="Mode"
347
+ >
348
+ {node?.mode}
349
+ </span>
350
+ </p>
351
+ </div>
352
+ <div>
353
+ <h6 className="mb-0 w-100">Tags</h6>
354
+ <p
355
+ className="mb-0 opacity-75"
356
+ role="dialog"
357
+ aria-hidden="false"
358
+ aria-label="Tags"
359
+ >
360
+ {nodeTags}
361
+ </p>
362
+ </div>
363
+ {primaryKeyOrRequiredDims}
364
+ <div>
365
+ <h6 className="mb-0 w-100">Last Updated</h6>
366
+ <p
367
+ className="mb-0 opacity-75"
368
+ role="dialog"
369
+ aria-hidden="false"
370
+ aria-label="UpdatedAt"
371
+ >
372
+ {new Date(node?.updated_at).toDateString()}
373
+ </p>
78
374
  </div>
79
- </div>
80
- {this.queryDiv}
81
- <div className="list-group-item d-flex">
82
- {this.props.node?.primary_key}
83
375
  </div>
84
376
  </div>
85
- );
86
- }
377
+ {metricMetadataDiv}
378
+ {node?.type !== 'cube' && node?.type !== 'metric' ? queryDiv : ''}
379
+ {node?.type === 'metric' ? metricQueryDiv : ''}
380
+ {customMetadataDiv}
381
+ {cubeElementsDiv}
382
+ </div>
383
+ );
87
384
  }
@@ -0,0 +1,84 @@
1
+ import { useContext } from 'react';
2
+ import { MarkerType } from 'reactflow';
3
+
4
+ import '../../../styles/dag.css';
5
+ import 'reactflow/dist/style.css';
6
+ import DJClientContext from '../../providers/djclient';
7
+ import LayoutFlow from '../../components/djgraph/LayoutFlow';
8
+
9
+ const createDJNode = node => {
10
+ return {
11
+ id: node.node_name,
12
+ type: 'DJNode',
13
+ data: {
14
+ label: node.node_name,
15
+ name: node.node_name,
16
+ type: node.node_type,
17
+ table: node.node_type === 'source' ? node.node_name : '',
18
+ display_name:
19
+ node.node_type === 'source' ? node.node_name : node.display_name,
20
+ column_names: [{ name: node.column_name, type: '' }],
21
+ primary_key: [],
22
+ },
23
+ };
24
+ };
25
+
26
+ const NodeColumnLineage = djNode => {
27
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
28
+ const dagFetch = async (getLayoutedElements, setNodes, setEdges) => {
29
+ let relatedNodes = await djClient.node_lineage(djNode.djNode.name);
30
+ let nodesMapping = {};
31
+ let edgesMapping = {};
32
+ let processing = relatedNodes;
33
+ while (processing.length > 0) {
34
+ let current = processing.pop();
35
+ let node = createDJNode(current);
36
+ if (node.id in nodesMapping) {
37
+ nodesMapping[node.id].data.column_names = Array.from(
38
+ new Set([
39
+ ...nodesMapping[node.id].data.column_names.map(x => x.name),
40
+ ...node.data.column_names.map(x => x.name),
41
+ ]),
42
+ ).map(x => {
43
+ return { name: x, type: '' };
44
+ });
45
+ } else {
46
+ nodesMapping[node.id] = node;
47
+ }
48
+
49
+ current.lineage.forEach(lineageColumn => {
50
+ const sourceHandle =
51
+ lineageColumn.node_name + '.' + lineageColumn.column_name;
52
+ const targetHandle = current.node_name + '.' + current.column_name;
53
+ edgesMapping[sourceHandle + '->' + targetHandle] = {
54
+ id: sourceHandle + '->' + targetHandle,
55
+ source: lineageColumn.node_name,
56
+ sourceHandle: sourceHandle,
57
+ target: current.node_name,
58
+ targetHandle: targetHandle,
59
+ animated: true,
60
+ markerEnd: {
61
+ type: MarkerType.Arrow,
62
+ },
63
+ style: {
64
+ strokeWidth: 3,
65
+ stroke: '#b0b9c2',
66
+ },
67
+ };
68
+ processing.push(lineageColumn);
69
+ });
70
+ }
71
+
72
+ // use dagre to determine the position of the parents (the DJ nodes)
73
+ // the positions of the columns are relative to each DJ node
74
+ const elements = getLayoutedElements(
75
+ Object.keys(nodesMapping).map(key => nodesMapping[key]),
76
+ Object.keys(edgesMapping).map(key => edgesMapping[key]),
77
+ );
78
+
79
+ setNodes(elements.nodes);
80
+ setEdges(elements.edges);
81
+ };
82
+ return LayoutFlow(djNode, dagFetch);
83
+ };
84
+ export default NodeColumnLineage;