datajunction-ui 0.0.1-rc.9 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/.env +2 -0
  2. package/.prettierignore +3 -1
  3. package/Makefile +9 -0
  4. package/dj-logo.svg +10 -0
  5. package/package.json +53 -14
  6. package/public/favicon.ico +0 -0
  7. package/public/index.html +1 -1
  8. package/src/__tests__/reportWebVitals.test.jsx +44 -0
  9. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -109
  10. package/src/app/components/AddNodeDropdown.jsx +44 -0
  11. package/src/app/components/ListGroupItem.jsx +9 -1
  12. package/src/app/components/NamespaceHeader.jsx +4 -13
  13. package/src/app/components/NodeListActions.jsx +69 -0
  14. package/src/app/components/NodeMaterializationDelete.jsx +90 -0
  15. package/src/app/components/QueryInfo.jsx +172 -0
  16. package/src/app/components/Search.jsx +94 -0
  17. package/src/app/components/Tab.jsx +8 -1
  18. package/src/app/components/ToggleSwitch.jsx +20 -0
  19. package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
  20. package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
  21. package/src/app/components/__tests__/Search.test.jsx +63 -0
  22. package/src/app/components/__tests__/Tab.test.jsx +27 -0
  23. package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
  24. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
  25. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  26. package/src/app/components/djgraph/Collapse.jsx +47 -0
  27. package/src/app/components/djgraph/DJNode.jsx +61 -83
  28. package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
  29. package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
  30. package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
  31. package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
  32. package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
  33. package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
  34. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
  35. package/src/app/components/forms/Action.jsx +8 -0
  36. package/src/app/components/forms/NodeNameField.jsx +64 -0
  37. package/src/app/components/search.css +17 -0
  38. package/src/app/constants.js +2 -0
  39. package/src/app/icons/AddItemIcon.jsx +16 -0
  40. package/src/app/icons/AlertIcon.jsx +33 -0
  41. package/src/app/icons/CollapsedIcon.jsx +15 -0
  42. package/src/app/icons/CommitIcon.jsx +45 -0
  43. package/src/app/icons/DJLogo.jsx +36 -0
  44. package/src/app/icons/DeleteIcon.jsx +21 -0
  45. package/src/app/icons/DiffIcon.jsx +63 -0
  46. package/src/app/icons/EditIcon.jsx +18 -0
  47. package/src/app/icons/ExpandedIcon.jsx +15 -0
  48. package/src/app/icons/EyeIcon.jsx +20 -0
  49. package/src/app/icons/FilterIcon.jsx +7 -0
  50. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  51. package/src/app/icons/InvalidIcon.jsx +16 -0
  52. package/src/app/icons/JupyterExportIcon.jsx +25 -0
  53. package/src/app/icons/LoadingIcon.jsx +14 -0
  54. package/src/app/icons/NodeIcon.jsx +49 -0
  55. package/src/app/icons/PythonIcon.jsx +14 -0
  56. package/src/app/icons/TableIcon.jsx +14 -0
  57. package/src/app/icons/ValidIcon.jsx +16 -0
  58. package/src/app/index.tsx +118 -37
  59. package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
  60. package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
  61. package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
  62. package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
  63. package/src/app/pages/AddEditNodePage/FormikSelect.jsx +51 -0
  64. package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
  65. package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
  66. package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
  67. package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
  68. package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
  69. package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
  70. package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
  71. package/src/app/pages/AddEditNodePage/OwnersField.jsx +54 -0
  72. package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
  73. package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
  74. package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
  75. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +109 -0
  76. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +287 -0
  77. package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
  78. package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
  79. package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
  80. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
  81. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
  82. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
  83. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
  84. package/src/app/pages/AddEditNodePage/index.jsx +506 -0
  85. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  86. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  87. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  88. package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
  89. package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
  90. package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
  91. package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
  92. package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
  93. package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
  94. package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
  95. package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
  96. package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
  97. package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
  98. package/src/app/pages/LoginPage/index.jsx +17 -0
  99. package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
  100. package/src/app/pages/NamespacePage/Explorer.jsx +61 -0
  101. package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
  102. package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
  103. package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
  104. package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
  105. package/src/app/pages/NamespacePage/__tests__/index.test.jsx +297 -0
  106. package/src/app/pages/NamespacePage/index.jsx +319 -42
  107. package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
  108. package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
  109. package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
  110. package/src/app/pages/NodePage/ClientCodePopover.jsx +94 -0
  111. package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
  112. package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
  113. package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
  114. package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
  115. package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
  116. package/src/app/pages/NodePage/NodeColumnTab.jsx +256 -30
  117. package/src/app/pages/NodePage/NodeDependenciesTab.jsx +153 -0
  118. package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
  119. package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
  120. package/src/app/pages/NodePage/NodeInfoTab.jsx +325 -49
  121. package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
  122. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
  123. package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
  124. package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
  125. package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
  126. package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
  127. package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
  128. package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
  129. package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
  130. package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
  131. package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
  132. package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
  133. package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
  134. package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
  135. package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
  136. package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
  137. package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +161 -0
  138. package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
  139. package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +151 -0
  140. package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
  141. package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
  142. package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
  143. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +872 -0
  144. package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
  145. package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
  146. package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
  147. package/src/app/pages/NodePage/index.jsx +190 -44
  148. package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
  149. package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
  150. package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
  151. package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
  152. package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
  153. package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
  154. package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
  155. package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
  156. package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
  157. package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
  158. package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
  159. package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
  160. package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
  161. package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
  162. package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
  163. package/src/app/pages/OverviewPage/index.jsx +22 -0
  164. package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
  165. package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +110 -0
  166. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
  167. package/src/app/pages/RegisterTablePage/index.jsx +142 -0
  168. package/src/app/pages/Root/__tests__/index.test.jsx +79 -0
  169. package/src/app/pages/Root/index.tsx +84 -6
  170. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  171. package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
  172. package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
  173. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  174. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  175. package/src/app/pages/TagPage/index.jsx +79 -0
  176. package/src/app/services/DJService.js +1314 -21
  177. package/src/app/services/__tests__/DJService.test.jsx +1559 -0
  178. package/src/index.tsx +1 -0
  179. package/src/mocks/mockNodes.jsx +1474 -0
  180. package/src/setupTests.ts +31 -1
  181. package/src/styles/dag.css +117 -5
  182. package/src/styles/index.css +1027 -30
  183. package/src/styles/loading.css +34 -0
  184. package/src/styles/login.css +81 -0
  185. package/src/styles/node-creation.scss +276 -0
  186. package/src/styles/node-list.css +4 -0
  187. package/src/styles/overview.css +72 -0
  188. package/src/styles/sorted-table.css +15 -0
  189. package/src/styles/styles.scss +44 -0
  190. package/src/styles/styles.scss.d.ts +9 -0
  191. package/src/utils/form.jsx +23 -0
  192. package/webpack.config.js +16 -6
  193. package/.babelrc +0 -4
  194. package/.env.local +0 -4
  195. package/.env.production +0 -1
  196. package/.github/pull_request_template.md +0 -11
  197. package/.github/workflows/ci.yml +0 -33
  198. package/.vscode/extensions.json +0 -7
  199. package/.vscode/launch.json +0 -15
  200. package/.vscode/settings.json +0 -25
  201. package/Dockerfile +0 -7
  202. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  203. package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
  204. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
  205. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
@@ -1,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,240 @@ 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
+ };
153
+
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 cubeElementsDiv = node?.cube_elements ? (
202
+ <div className="list-group-item d-flex">
203
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
204
+ <div
205
+ style={{
206
+ width: window.innerWidth * 0.8,
207
+ }}
208
+ >
209
+ <h6 className="mb-0 w-100">Cube Elements</h6>
210
+ <div className={`list-group-item`}>
211
+ {node.cube_elements.map(cubeElem =>
212
+ cubeElem.type === 'metric' ? displayCubeElement(cubeElem) : '',
213
+ )}
214
+ {node.cube_elements.map(cubeElem =>
215
+ cubeElem.type !== 'metric' ? displayCubeElement(cubeElem) : '',
216
+ )}
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ ) : (
222
+ <></>
223
+ );
52
224
 
53
- <p className="mb-0 opacity-75">
225
+ const primaryKeyOrRequiredDims = (
226
+ <div style={{ maxWidth: '25%' }}>
227
+ <h6 className="mb-0 w-100">
228
+ {node?.type !== 'metric' ? 'Primary Key' : 'Required Dimensions'}
229
+ </h6>
230
+ <p
231
+ className="mb-0 opacity-75"
232
+ role="dialog"
233
+ aria-hidden="false"
234
+ aria-label={
235
+ node?.type !== 'metric' ? 'PrimaryKey' : 'RequiredDimensions'
236
+ }
237
+ >
238
+ {node?.type !== 'metric'
239
+ ? node?.primary_key?.map(dim => (
240
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
241
+ <a href={`/nodes/${node?.name}`}>{dim}</a>
242
+ </span>
243
+ ))
244
+ : node?.required_dimensions?.map(dim => (
245
+ <span className="rounded-pill badge bg-secondary-soft PrimaryKey">
246
+ <a href={`/nodes/${node?.upstream_node}`}>{dim.name}</a>
247
+ </span>
248
+ ))}
249
+ </p>
250
+ </div>
251
+ );
252
+ return (
253
+ <div
254
+ className="list-group align-items-center justify-content-between flex-md-row gap-2"
255
+ style={{ minWidth: '700px' }}
256
+ >
257
+ {node?.type === 'metric' ? metricsWarning : ''}
258
+ <ListGroupItem label="Description" value={node?.description} />
259
+ <div className="list-group-item d-flex">
260
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
261
+ <div>
262
+ <h6 className="mb-0 w-100">Owners</h6>
263
+ <p className="mb-0 opacity-75">
264
+ {node?.owners.map(owner => (
54
265
  <span
55
- className="rounded-pill badge bg-secondary-soft"
56
- style={{ marginLeft: '0.5rem', fontSize: '100%' }}
266
+ className="badge node_type__transform"
267
+ style={{ margin: '2px', fontSize: '16px', cursor: 'pointer' }}
57
268
  >
58
- {this.props.node?.version}
269
+ {owner.username}
59
270
  </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>
271
+ ))}
272
+ </p>
273
+ </div>
274
+ </div>
275
+ </div>
276
+ <div className="list-group-item d-flex">
277
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
278
+ <div>
279
+ <h6 className="mb-0 w-100">Version</h6>
280
+
281
+ <p className="mb-0 opacity-75">
282
+ <span
283
+ className="rounded-pill badge bg-secondary-soft"
284
+ style={{ marginLeft: '0.5rem', fontSize: '100%' }}
285
+ role="dialog"
286
+ aria-hidden="false"
287
+ aria-label="Version"
288
+ >
289
+ {node?.version}
290
+ </span>
291
+ </p>
292
+ </div>
293
+ {node.type === 'source' ? (
68
294
  <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>
295
+ <h6 className="mb-0 w-100">Table</h6>
296
+ <p
297
+ className="mb-0 opacity-75"
298
+ role="dialog"
299
+ aria-hidden="false"
300
+ aria-label="Table"
301
+ >
302
+ {node?.catalog.name}.{node?.schema_}.{node?.table}
72
303
  </p>
73
304
  </div>
74
- <div>
75
- <h6 className="mb-0 w-100">Tags</h6>
76
- <p className="mb-0 opacity-75">{this.nodeTags}</p>
77
- </div>
305
+ ) : (
306
+ <></>
307
+ )}
308
+ <div>
309
+ <h6 className="mb-0 w-100">Status</h6>
310
+ <p
311
+ className="mb-0 opacity-75"
312
+ role="dialog"
313
+ aria-hidden="false"
314
+ aria-label="NodeStatus"
315
+ >
316
+ <NodeStatus node={node} />
317
+ </p>
318
+ </div>
319
+ <div>
320
+ <h6 className="mb-0 w-100">Mode</h6>
321
+ <p className="mb-0 opacity-75">
322
+ <span
323
+ className="status"
324
+ role="dialog"
325
+ aria-hidden="false"
326
+ aria-label="Mode"
327
+ >
328
+ {node?.mode}
329
+ </span>
330
+ </p>
331
+ </div>
332
+ <div>
333
+ <h6 className="mb-0 w-100">Tags</h6>
334
+ <p
335
+ className="mb-0 opacity-75"
336
+ role="dialog"
337
+ aria-hidden="false"
338
+ aria-label="Tags"
339
+ >
340
+ {nodeTags}
341
+ </p>
342
+ </div>
343
+ {primaryKeyOrRequiredDims}
344
+ <div>
345
+ <h6 className="mb-0 w-100">Last Updated</h6>
346
+ <p
347
+ className="mb-0 opacity-75"
348
+ role="dialog"
349
+ aria-hidden="false"
350
+ aria-label="UpdatedAt"
351
+ >
352
+ {new Date(node?.updated_at).toDateString()}
353
+ </p>
78
354
  </div>
79
- </div>
80
- {this.queryDiv}
81
- <div className="list-group-item d-flex">
82
- {this.props.node?.primary_key}
83
355
  </div>
84
356
  </div>
85
- );
86
- }
357
+ {metricMetadataDiv}
358
+ {node?.type !== 'cube' && node?.type !== 'metric' ? queryDiv : ''}
359
+ {node?.type === 'metric' ? metricQueryDiv : ''}
360
+ {cubeElementsDiv}
361
+ </div>
362
+ );
87
363
  }
@@ -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;