datajunction-ui 0.0.1-rc.9 → 0.0.2-3.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 (238) 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/NotificationBell.tsx +229 -0
  16. package/src/app/components/QueryInfo.jsx +172 -0
  17. package/src/app/components/Search.jsx +94 -0
  18. package/src/app/components/Tab.jsx +8 -1
  19. package/src/app/components/ToggleSwitch.jsx +20 -0
  20. package/src/app/components/UserMenu.tsx +92 -0
  21. package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
  22. package/src/app/components/__tests__/NodeMaterializationDelete.test.jsx +263 -0
  23. package/src/app/components/__tests__/NotificationBell.test.tsx +313 -0
  24. package/src/app/components/__tests__/QueryInfo.test.jsx +183 -0
  25. package/src/app/components/__tests__/Search.test.jsx +307 -0
  26. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  27. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  28. package/src/app/components/__tests__/UserMenu.test.tsx +248 -0
  29. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
  30. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  31. package/src/app/components/djgraph/Collapse.jsx +47 -0
  32. package/src/app/components/djgraph/DJNode.jsx +61 -83
  33. package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
  34. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  35. package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
  36. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  37. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  38. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  39. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  40. package/src/app/components/forms/Action.jsx +8 -0
  41. package/src/app/components/forms/NodeNameField.jsx +64 -0
  42. package/src/app/components/search.css +17 -0
  43. package/src/app/constants.js +2 -0
  44. package/src/app/icons/AddItemIcon.jsx +16 -0
  45. package/src/app/icons/AlertIcon.jsx +33 -0
  46. package/src/app/icons/CollapsedIcon.jsx +15 -0
  47. package/src/app/icons/CommitIcon.jsx +45 -0
  48. package/src/app/icons/DJLogo.jsx +36 -0
  49. package/src/app/icons/DeleteIcon.jsx +21 -0
  50. package/src/app/icons/DiffIcon.jsx +63 -0
  51. package/src/app/icons/EditIcon.jsx +18 -0
  52. package/src/app/icons/ExpandedIcon.jsx +15 -0
  53. package/src/app/icons/EyeIcon.jsx +20 -0
  54. package/src/app/icons/FilterIcon.jsx +7 -0
  55. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  56. package/src/app/icons/InvalidIcon.jsx +16 -0
  57. package/src/app/icons/JupyterExportIcon.jsx +25 -0
  58. package/src/app/icons/LoadingIcon.jsx +14 -0
  59. package/src/app/icons/NodeIcon.jsx +49 -0
  60. package/src/app/icons/NotificationIcon.jsx +27 -0
  61. package/src/app/icons/PythonIcon.jsx +14 -0
  62. package/src/app/icons/SettingsIcon.jsx +28 -0
  63. package/src/app/icons/TableIcon.jsx +14 -0
  64. package/src/app/icons/ValidIcon.jsx +16 -0
  65. package/src/app/index.tsx +138 -38
  66. package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
  67. package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
  68. package/src/app/pages/AddEditNodePage/CustomMetadataField.jsx +144 -0
  69. package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
  70. package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
  71. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +64 -0
  72. package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
  73. package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
  74. package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
  75. package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
  76. package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
  77. package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
  78. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
  79. package/src/app/pages/AddEditNodePage/OwnersField.jsx +53 -0
  80. package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
  81. package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
  82. package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
  83. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +110 -0
  84. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +291 -0
  85. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  86. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  87. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  88. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
  89. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
  90. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  91. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
  92. package/src/app/pages/AddEditNodePage/index.jsx +545 -0
  93. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  94. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  95. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  96. package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
  97. package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
  98. package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
  99. package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
  100. package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
  101. package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
  102. package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
  103. package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
  104. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  105. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  106. package/src/app/pages/LoginPage/index.jsx +17 -0
  107. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
  108. package/src/app/pages/NamespacePage/Explorer.jsx +232 -0
  109. package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
  110. package/src/app/pages/NamespacePage/NodeModeSelect.jsx +27 -0
  111. package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
  112. package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
  113. package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
  114. package/src/app/pages/NamespacePage/__tests__/AddNamespacePopover.test.jsx +283 -0
  115. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +331 -0
  116. package/src/app/pages/NamespacePage/index.jsx +354 -42
  117. package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
  118. package/src/app/pages/NodePage/AddComplexDimensionLinkPopover.jsx +367 -0
  119. package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
  120. package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
  121. package/src/app/pages/NodePage/ClientCodePopover.jsx +116 -0
  122. package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
  123. package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
  124. package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
  125. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
  126. package/src/app/pages/NodePage/ManageDimensionLinksDialog.jsx +526 -0
  127. package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
  128. package/src/app/pages/NodePage/NodeColumnTab.jsx +421 -30
  129. package/src/app/pages/NodePage/NodeDependenciesTab.jsx +155 -0
  130. package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
  131. package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
  132. package/src/app/pages/NodePage/NodeInfoTab.jsx +404 -49
  133. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  134. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
  135. package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
  136. package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
  137. package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
  138. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  139. package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
  140. package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
  141. package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
  142. package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
  143. package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
  144. package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
  145. package/src/app/pages/NodePage/__tests__/AddComplexDimensionLinkPopover.test.jsx +459 -0
  146. package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
  147. package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
  148. package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
  149. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +144 -0
  150. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +132 -0
  151. package/src/app/pages/NodePage/__tests__/ManageDimensionLinksDialog.test.jsx +390 -0
  152. package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
  153. package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +157 -0
  154. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
  155. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
  156. package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
  157. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +892 -0
  158. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  159. package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
  160. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
  161. package/src/app/pages/NodePage/index.jsx +186 -45
  162. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  163. package/src/app/pages/NotificationsPage/Loadable.jsx +6 -0
  164. package/src/app/pages/NotificationsPage/__tests__/index.test.jsx +287 -0
  165. package/src/app/pages/NotificationsPage/index.jsx +136 -0
  166. package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
  167. package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
  168. package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
  169. package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
  170. package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
  171. package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
  172. package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
  173. package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
  174. package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
  175. package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
  176. package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
  177. package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
  178. package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
  179. package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
  180. package/src/app/pages/OverviewPage/index.jsx +22 -0
  181. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  182. package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +112 -0
  183. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
  184. package/src/app/pages/RegisterTablePage/index.jsx +142 -0
  185. package/src/app/pages/Root/__tests__/index.test.jsx +44 -0
  186. package/src/app/pages/Root/index.tsx +92 -10
  187. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  188. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  189. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  190. package/src/app/pages/SettingsPage/CreateServiceAccountModal.jsx +152 -0
  191. package/src/app/pages/SettingsPage/Loadable.jsx +16 -0
  192. package/src/app/pages/SettingsPage/NotificationSubscriptionsSection.jsx +189 -0
  193. package/src/app/pages/SettingsPage/ProfileSection.jsx +41 -0
  194. package/src/app/pages/SettingsPage/ServiceAccountsSection.jsx +95 -0
  195. package/src/app/pages/SettingsPage/__tests__/CreateServiceAccountModal.test.jsx +318 -0
  196. package/src/app/pages/SettingsPage/__tests__/NotificationSubscriptionsSection.test.jsx +233 -0
  197. package/src/app/pages/SettingsPage/__tests__/ProfileSection.test.jsx +65 -0
  198. package/src/app/pages/SettingsPage/__tests__/ServiceAccountsSection.test.jsx +150 -0
  199. package/src/app/pages/SettingsPage/__tests__/index.test.jsx +187 -0
  200. package/src/app/pages/SettingsPage/index.jsx +148 -0
  201. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  202. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  203. package/src/app/pages/TagPage/index.jsx +79 -0
  204. package/src/app/providers/UserProvider.tsx +78 -0
  205. package/src/app/services/DJService.js +1487 -21
  206. package/src/app/services/__tests__/DJService.test.jsx +2194 -0
  207. package/src/app/utils/__tests__/date.test.js +198 -0
  208. package/src/app/utils/date.js +65 -0
  209. package/src/index.tsx +1 -0
  210. package/src/mocks/mockNodes.jsx +1477 -0
  211. package/src/setupTests.ts +31 -1
  212. package/src/styles/dag.css +117 -5
  213. package/src/styles/index.css +1028 -31
  214. package/src/styles/loading.css +34 -0
  215. package/src/styles/login.css +81 -0
  216. package/src/styles/nav-bar.css +274 -0
  217. package/src/styles/node-creation.scss +276 -0
  218. package/src/styles/node-list.css +4 -0
  219. package/src/styles/overview.css +72 -0
  220. package/src/styles/settings.css +787 -0
  221. package/src/styles/sorted-table.css +15 -0
  222. package/src/styles/styles.scss +44 -0
  223. package/src/styles/styles.scss.d.ts +9 -0
  224. package/src/utils/form.jsx +23 -0
  225. package/webpack.config.js +20 -7
  226. package/.babelrc +0 -4
  227. package/.env.local +0 -4
  228. package/.env.production +0 -1
  229. package/.github/pull_request_template.md +0 -11
  230. package/.github/workflows/ci.yml +0 -33
  231. package/.vscode/extensions.json +0 -7
  232. package/.vscode/launch.json +0 -15
  233. package/.vscode/settings.json +0 -25
  234. package/Dockerfile +0 -7
  235. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  236. package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
  237. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  238. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -1,18 +1,155 @@
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
+ // interface MetricInfo {
15
+ // name: string;
16
+ // current: MetricRevision;
17
+ // }
18
+
19
+ // interface MetricRevision {
20
+ // parents: array<string>;
21
+ // metricMetadata:
22
+ // }
23
+
24
+ // interface MetricMetadata {
25
+ // direction: string;
26
+ // unit: string;
27
+ // expression: string;
28
+ // significantDigits: string;
29
+ // incompatibleDruidFunctions: array<string>;
30
+ // }
31
+
32
+ export default function NodeInfoTab({ node }) {
33
+ const [compiledSQL, setCompiledSQL] = useState('');
34
+ const [checked, setChecked] = useState(false);
35
+
36
+ // For metrics
37
+ const [metricInfo, setMetricInfo] = useState(null);
38
+
39
+ const nodeTags = node?.tags.map(tag => (
40
+ <div className={'badge tag_value'}>
41
+ <a href={`/tags/${tag.name}`}>{tag.display_name}</a>
42
+ </div>
43
+ ));
44
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
45
+
46
+ useEffect(() => {
47
+ const fetchData = async () => {
48
+ if (checked === true) {
49
+ const data = await djClient.compiledSql(node.name);
50
+ if (data.sql) {
51
+ setCompiledSQL(data.sql);
52
+ } else {
53
+ setCompiledSQL(
54
+ '/* Ran into an issue while generating compiled SQL */',
55
+ );
56
+ }
57
+ }
58
+ };
59
+ fetchData().catch(console.error);
60
+ }, [node, djClient, checked]);
61
+
62
+ useEffect(() => {
63
+ const fetchData = async () => {
64
+ const metric = await djClient.getMetric(node.name);
65
+ setMetricInfo({
66
+ metric_metadata: metric.current.metricMetadata,
67
+ required_dimensions: metric.current.requiredDimensions,
68
+ upstream_node: metric.current.parents[0]?.name,
69
+ expression: metric.current.metricMetadata?.expression,
70
+ incompatible_druid_functions:
71
+ metric.current.metricMetadata?.incompatibleDruidFunctions || [],
72
+ });
73
+ };
74
+ if (node.type === 'metric') {
75
+ fetchData().catch(console.error);
76
+ }
77
+ }, [node, djClient]);
78
+
79
+ // For cubes
80
+ const [cubeElements, setCubeElements] = useState(null);
81
+
82
+ useEffect(() => {
83
+ const fetchData = async () => {
84
+ const cube = await djClient.cube(node.name);
85
+ setCubeElements(cube.cube_elements);
86
+ };
87
+ if (node.type === 'cube') {
88
+ fetchData().catch(console.error);
89
+ }
90
+ }, [node, djClient]);
91
+
92
+ function toggle(value) {
93
+ return !value;
94
+ }
95
+ const metricsWarning =
96
+ node?.type === 'metric' &&
97
+ metricInfo?.incompatible_druid_functions?.length > 0 ? (
98
+ <div className="message warning" style={{ marginTop: '0.7rem' }}>
99
+ ⚠{' '}
100
+ <small>
101
+ The following functions used in the metric definition may not be
102
+ compatible with Druid SQL:{' '}
103
+ {metricInfo?.incompatible_druid_functions.map(func => (
104
+ <li
105
+ style={{ listStyleType: 'none', margin: '0.7rem 0.7rem' }}
106
+ key={func}
107
+ >
108
+ ⇢{' '}
109
+ <span style={{ background: '#fff', padding: '0.3rem' }}>
110
+ {func}
111
+ </span>
112
+ </li>
113
+ ))}
114
+ If you need your metrics to be compatible with Druid, please use{' '}
115
+ <a
116
+ href={
117
+ 'https://druid.apache.org/docs/latest/querying/sql-functions/'
118
+ }
119
+ >
120
+ these supported functions
121
+ </a>
122
+ .
123
+ </small>
124
+ </div>
125
+ ) : (
126
+ ''
127
+ );
128
+
129
+ const metricQueryDiv =
130
+ node?.type === 'metric' ? (
131
+ <div className="list-group-item d-flex">
132
+ <div className="gap-2 w-100 justify-content-between py-3">
133
+ <div style={{ marginBottom: '30px' }}>
134
+ <h6 className="mb-0 w-100">Upstream Node</h6>
135
+ <p>
136
+ <a href={`/nodes/${metricInfo?.upstream_node}`}>
137
+ {metricInfo?.upstream_node}
138
+ </a>
139
+ </p>
140
+ </div>
141
+ <div>
142
+ <h6 className="mb-0 w-100">Aggregate Expression</h6>
143
+ <SyntaxHighlighter language="sql" style={foundation}>
144
+ {metricInfo?.expression}
145
+ </SyntaxHighlighter>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ ) : (
150
+ ''
151
+ );
152
+ const queryDiv = node?.query ? (
16
153
  <div className="list-group-item d-flex">
17
154
  <div className="d-flex gap-2 w-100 justify-content-between py-3">
18
155
  <div
@@ -21,15 +158,18 @@ export default class NodeInfoTab extends Component {
21
158
  }}
22
159
  >
23
160
  <h6 className="mb-0 w-100">Query</h6>
161
+ {['metric', 'dimension', 'transform'].indexOf(node?.type) > -1 ? (
162
+ <ToggleSwitch
163
+ id="toggleSwitch"
164
+ checked={checked}
165
+ onChange={() => setChecked(toggle)}
166
+ toggleName="Show Compiled SQL"
167
+ />
168
+ ) : (
169
+ <></>
170
+ )}
24
171
  <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
- })}
172
+ {checked ? compiledSQL : node?.query}
33
173
  </SyntaxHighlighter>
34
174
  </div>
35
175
  </div>
@@ -38,50 +178,265 @@ export default class NodeInfoTab extends Component {
38
178
  <></>
39
179
  );
40
180
 
41
- render() {
181
+ const displayCubeElement = cubeElem => {
42
182
  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>
183
+ <div
184
+ className="button-3 cube-element"
185
+ key={cubeElem.name}
186
+ role="cell"
187
+ aria-label="CubeElement"
188
+ aria-hidden="false"
189
+ >
190
+ <a href={`/nodes/${cubeElem.node_name}`}>
191
+ {cubeElem.type === 'dimension'
192
+ ? labelize(cubeElem.node_name.split('.').slice(-1)[0]) + ' → '
193
+ : ''}
194
+ {cubeElem.display_name}
195
+ </a>
196
+ <span
197
+ className={`badge node_type__${
198
+ cubeElem.type === 'metric' ? cubeElem.type : 'dimension'
199
+ }`}
200
+ style={{ fontSize: '100%', textTransform: 'capitalize' }}
201
+ >
202
+ {cubeElem.type === 'metric' ? cubeElem.type : 'dimension'}
203
+ </span>
204
+ </div>
205
+ );
206
+ };
207
+
208
+ const metricMetadataDiv =
209
+ node?.type === 'metric' ? (
210
+ <div className="list-group-item d-flex">
211
+ <div className="d-flex gap-2 w-100 py-3">
212
+ <div>
213
+ <h6 className="mb-0 w-100">Direction</h6>
214
+ <p
215
+ className="mb-0 opacity-75"
216
+ role="dialog"
217
+ aria-hidden="false"
218
+ aria-label="MetricDirection"
219
+ >
220
+ {metricInfo?.metric_metadata?.direction
221
+ ? labelize(
222
+ metricInfo?.metric_metadata?.direction?.toLowerCase(),
223
+ )
224
+ : 'None'}
225
+ </p>
226
+ </div>
227
+ <div style={{ marginRight: '2rem' }}>
228
+ <h6 className="mb-0 w-100">Unit</h6>
229
+ <p
230
+ className="mb-0 opacity-75"
231
+ role="dialog"
232
+ aria-hidden="false"
233
+ aria-label="MetricUnit"
234
+ >
235
+ {metricInfo?.metric_metadata?.unit?.name
236
+ ? labelize(
237
+ metricInfo?.metric_metadata?.unit?.name?.toLowerCase(),
238
+ )
239
+ : 'None'}
240
+ </p>
241
+ </div>
242
+ <div style={{ marginRight: '2rem' }}>
243
+ <h6 className="mb-0 w-100">Significant Digits</h6>
244
+ <p
245
+ className="mb-0 opacity-75"
246
+ role="dialog"
247
+ aria-hidden="false"
248
+ aria-label="SignificantDigits"
249
+ >
250
+ {metricInfo?.metric_metadata?.significantDigits || 'None'}
251
+ </p>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ ) : (
256
+ ''
257
+ );
258
+
259
+ const customMetadataDiv =
260
+ node?.custom_metadata && Object.keys(node.custom_metadata).length > 0 ? (
261
+ <div className="list-group-item d-flex">
262
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
263
+ <div
264
+ style={{
265
+ width: window.innerWidth * 0.8,
266
+ }}
267
+ >
268
+ <h6 className="mb-0 w-100">Custom Metadata</h6>
269
+ <SyntaxHighlighter language="json" style={foundation}>
270
+ {JSON.stringify(node.custom_metadata, null, 2)}
271
+ </SyntaxHighlighter>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ ) : (
276
+ ''
277
+ );
52
278
 
53
- <p className="mb-0 opacity-75">
279
+ const cubeElementsDiv = cubeElements ? (
280
+ <div className="list-group-item d-flex">
281
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
282
+ <div
283
+ style={{
284
+ width: window.innerWidth * 0.8,
285
+ }}
286
+ >
287
+ <h6 className="mb-0 w-100">Cube Elements</h6>
288
+ <div className={`list-group-item`}>
289
+ {cubeElements.map(cubeElem =>
290
+ cubeElem.type === 'metric' ? displayCubeElement(cubeElem) : '',
291
+ )}
292
+ {cubeElements.map(cubeElem =>
293
+ cubeElem.type !== 'metric' ? displayCubeElement(cubeElem) : '',
294
+ )}
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ ) : (
300
+ <></>
301
+ );
302
+
303
+ const primaryKeyOrRequiredDims = (
304
+ <div style={{ maxWidth: '25%' }}>
305
+ <h6 className="mb-0 w-100">
306
+ {node?.type !== 'metric' ? 'Primary Key' : 'Required Dimensions'}
307
+ </h6>
308
+ <p
309
+ className="mb-0 opacity-75"
310
+ role="dialog"
311
+ aria-hidden="false"
312
+ aria-label={
313
+ node?.type !== 'metric' ? 'PrimaryKey' : 'RequiredDimensions'
314
+ }
315
+ >
316
+ {node?.type !== 'metric'
317
+ ? node?.primary_key?.map(dim => (
318
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
319
+ <a href={`/nodes/${node?.name}`}>{dim}</a>
320
+ </span>
321
+ ))
322
+ : node?.required_dimensions?.map(dim => (
323
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
324
+ <a href={`/nodes/${node?.upstream_node}`}>{dim.name}</a>
325
+ </span>
326
+ ))}
327
+ </p>
328
+ </div>
329
+ );
330
+ return (
331
+ <div
332
+ className="list-group align-items-center justify-content-between flex-md-row gap-2"
333
+ style={{ minWidth: '700px' }}
334
+ >
335
+ {node?.type === 'metric' ? metricsWarning : ''}
336
+ <ListGroupItem label="Description" value={node?.description} />
337
+ <div className="list-group-item d-flex">
338
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
339
+ <div>
340
+ <h6 className="mb-0 w-100">Owners</h6>
341
+ <p className="mb-0 opacity-75">
342
+ {node?.owners.map(owner => (
54
343
  <span
55
- className="rounded-pill badge bg-secondary-soft"
56
- style={{ marginLeft: '0.5rem', fontSize: '100%' }}
344
+ className="badge node_type__transform"
345
+ style={{ margin: '2px', fontSize: '16px', cursor: 'pointer' }}
57
346
  >
58
- {this.props.node?.version}
347
+ {owner.username}
59
348
  </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>
349
+ ))}
350
+ </p>
351
+ </div>
352
+ </div>
353
+ </div>
354
+ <div className="list-group-item d-flex">
355
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
356
+ <div>
357
+ <h6 className="mb-0 w-100">Version</h6>
358
+
359
+ <p className="mb-0 opacity-75">
360
+ <span
361
+ className="rounded-pill badge bg-secondary-soft"
362
+ style={{ marginLeft: '0.5rem', fontSize: '100%' }}
363
+ role="dialog"
364
+ aria-hidden="false"
365
+ aria-label="Version"
366
+ >
367
+ {node?.version}
368
+ </span>
369
+ </p>
370
+ </div>
371
+ {node.type === 'source' ? (
68
372
  <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>
373
+ <h6 className="mb-0 w-100">Table</h6>
374
+ <p
375
+ className="mb-0 opacity-75"
376
+ role="dialog"
377
+ aria-hidden="false"
378
+ aria-label="Table"
379
+ >
380
+ {node?.catalog.name}.{node?.schema_}.{node?.table}
72
381
  </p>
73
382
  </div>
74
- <div>
75
- <h6 className="mb-0 w-100">Tags</h6>
76
- <p className="mb-0 opacity-75">{this.nodeTags}</p>
77
- </div>
383
+ ) : (
384
+ <></>
385
+ )}
386
+ <div>
387
+ <h6 className="mb-0 w-100">Status</h6>
388
+ <p
389
+ className="mb-0 opacity-75"
390
+ role="dialog"
391
+ aria-hidden="false"
392
+ aria-label="NodeStatus"
393
+ >
394
+ <NodeStatus node={node} />
395
+ </p>
396
+ </div>
397
+ <div>
398
+ <h6 className="mb-0 w-100">Mode</h6>
399
+ <p className="mb-0 opacity-75">
400
+ <span
401
+ className="status"
402
+ role="dialog"
403
+ aria-hidden="false"
404
+ aria-label="Mode"
405
+ >
406
+ {node?.mode}
407
+ </span>
408
+ </p>
409
+ </div>
410
+ <div>
411
+ <h6 className="mb-0 w-100">Tags</h6>
412
+ <p
413
+ className="mb-0 opacity-75"
414
+ role="dialog"
415
+ aria-hidden="false"
416
+ aria-label="Tags"
417
+ >
418
+ {nodeTags}
419
+ </p>
420
+ </div>
421
+ {primaryKeyOrRequiredDims}
422
+ <div>
423
+ <h6 className="mb-0 w-100">Last Updated</h6>
424
+ <p
425
+ className="mb-0 opacity-75"
426
+ role="dialog"
427
+ aria-hidden="false"
428
+ aria-label="UpdatedAt"
429
+ >
430
+ {new Date(node?.updated_at).toDateString()}
431
+ </p>
78
432
  </div>
79
- </div>
80
- {this.queryDiv}
81
- <div className="list-group-item d-flex">
82
- {this.props.node?.primary_key}
83
433
  </div>
84
434
  </div>
85
- );
86
- }
435
+ {metricMetadataDiv}
436
+ {node.type !== 'cube' && node.type !== 'metric' ? queryDiv : ''}
437
+ {node.type === 'metric' ? metricQueryDiv : ''}
438
+ {customMetadataDiv}
439
+ {cubeElementsDiv}
440
+ </div>
441
+ );
87
442
  }
@@ -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;