datajunction-ui 0.0.1-rc.12 → 0.0.1-rc.14
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.
- package/.env +1 -0
- package/package.json +1 -1
- package/public/favicon.ico +0 -0
- package/src/app/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/src/app/components/QueryInfo.jsx +2 -34
- package/src/app/components/djgraph/DJNode.jsx +5 -3
- package/src/app/components/djgraph/DJNodeDimensions.jsx +1 -2
- package/src/app/components/djgraph/LayoutFlow.jsx +104 -0
- package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +6 -1
- package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/src/app/pages/NamespacePage/index.jsx +8 -2
- package/src/app/pages/NodePage/NodeColumnTab.jsx +1 -1
- package/src/app/pages/NodePage/NodeGraphTab.jsx +92 -174
- package/src/app/pages/NodePage/NodeHistory.jsx +110 -1
- package/src/app/pages/NodePage/NodeInfoTab.jsx +2 -19
- package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
- package/src/app/pages/NodePage/NodeSQLTab.jsx +1 -9
- package/src/app/pages/NodePage/NodesWithDimension.jsx +40 -0
- package/src/app/pages/NodePage/index.jsx +71 -30
- package/src/app/pages/Root/index.tsx +7 -1
- package/src/app/pages/SQLBuilderPage/index.jsx +64 -55
- package/src/app/services/DJService.js +36 -13
- package/src/styles/index.css +16 -8
- package/webpack.config.js +1 -0
|
@@ -4,11 +4,11 @@ import { DataJunctionAPI } from '../../services/DJService';
|
|
|
4
4
|
import DJClientContext from '../../providers/djclient';
|
|
5
5
|
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
6
6
|
import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
|
|
7
|
-
import { format } from 'sql-formatter';
|
|
8
7
|
import Select from 'react-select';
|
|
9
8
|
import QueryInfo from '../../components/QueryInfo';
|
|
10
9
|
|
|
11
10
|
export function SQLBuilderPage() {
|
|
11
|
+
const DEFAULT_NUM_ROWS = 100;
|
|
12
12
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
13
13
|
const [stagedMetrics, setStagedMetrics] = useState([]);
|
|
14
14
|
const [metrics, setMetrics] = useState([]);
|
|
@@ -17,12 +17,11 @@ export function SQLBuilderPage() {
|
|
|
17
17
|
const [stagedDimensions, setStagedDimensions] = useState([]);
|
|
18
18
|
const [selectedMetrics, setSelectedMetrics] = useState([]);
|
|
19
19
|
const [query, setQuery] = useState('');
|
|
20
|
-
const [
|
|
20
|
+
const [queryInfo, setQueryInfo] = useState({});
|
|
21
21
|
const [data, setData] = useState(null);
|
|
22
22
|
const [loadingData, setLoadingData] = useState(false);
|
|
23
23
|
const [viewData, setViewData] = useState(false);
|
|
24
|
-
const [
|
|
25
|
-
const [showNumRows, setShowNumRows] = useState(100);
|
|
24
|
+
const [showNumRows, setShowNumRows] = useState(DEFAULT_NUM_ROWS);
|
|
26
25
|
const [displayedRows, setDisplayedRows] = useState(<></>);
|
|
27
26
|
const numRowsOptions = [
|
|
28
27
|
{
|
|
@@ -46,20 +45,39 @@ export function SQLBuilderPage() {
|
|
|
46
45
|
// Get data for the current selection of metrics and dimensions
|
|
47
46
|
const getData = () => {
|
|
48
47
|
setLoadingData(true);
|
|
48
|
+
setQueryInfo({});
|
|
49
49
|
const fetchData = async () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
if (process.env.REACT_USE_SSE) {
|
|
51
|
+
const sse = await djClient.stream(selectedMetrics, selectedDimensions);
|
|
52
|
+
sse.onmessage = e => {
|
|
53
|
+
const messageData = JSON.parse(JSON.parse(e.data));
|
|
54
|
+
setQueryInfo(messageData);
|
|
55
|
+
if (messageData.results) {
|
|
56
|
+
setLoadingData(false);
|
|
57
|
+
setData(messageData.results);
|
|
58
|
+
messageData.numRows = messageData.results?.length
|
|
59
|
+
? messageData.results[0].rows.length
|
|
60
|
+
: [];
|
|
61
|
+
setViewData(true);
|
|
62
|
+
setShowNumRows(DEFAULT_NUM_ROWS);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
sse.onerror = () => sse.close();
|
|
66
|
+
} else {
|
|
67
|
+
const response = await djClient.data(
|
|
68
|
+
selectedMetrics,
|
|
69
|
+
selectedDimensions,
|
|
70
|
+
);
|
|
71
|
+
setQueryInfo(response);
|
|
72
|
+
if (response.results) {
|
|
73
|
+
setLoadingData(false);
|
|
74
|
+
setData(response.results);
|
|
75
|
+
response.numRows = response.results?.length
|
|
76
|
+
? response.results[0].rows.length
|
|
77
|
+
: [];
|
|
78
|
+
setViewData(true);
|
|
79
|
+
setShowNumRows(DEFAULT_NUM_ROWS);
|
|
80
|
+
}
|
|
63
81
|
}
|
|
64
82
|
};
|
|
65
83
|
fetchData().catch(console.error);
|
|
@@ -69,25 +87,7 @@ export function SQLBuilderPage() {
|
|
|
69
87
|
setQuery('');
|
|
70
88
|
setData(null);
|
|
71
89
|
setViewData(false);
|
|
72
|
-
|
|
73
|
-
const handleMetricSelect = event => {
|
|
74
|
-
const metrics = event.map(m => m.value);
|
|
75
|
-
resetView();
|
|
76
|
-
setStagedMetrics(metrics);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const handleMetricSelectorClose = () => {
|
|
80
|
-
setSelectedMetrics(stagedMetrics);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const handleDimensionSelect = event => {
|
|
84
|
-
const dimensions = event.map(d => d.value);
|
|
85
|
-
resetView();
|
|
86
|
-
setStagedDimensions(dimensions);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const handleDimensionSelectorClose = () => {
|
|
90
|
-
setSelectedDimensions(stagedDimensions);
|
|
90
|
+
setQueryInfo({});
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
// Get metrics
|
|
@@ -125,11 +125,9 @@ export function SQLBuilderPage() {
|
|
|
125
125
|
const fetchData = async () => {
|
|
126
126
|
if (selectedMetrics.length && selectedDimensions.length) {
|
|
127
127
|
const query = await djClient.sqls(selectedMetrics, selectedDimensions);
|
|
128
|
-
setShowHelp(false);
|
|
129
128
|
setQuery(query.sql);
|
|
130
129
|
} else {
|
|
131
130
|
resetView();
|
|
132
|
-
setShowHelp(true);
|
|
133
131
|
}
|
|
134
132
|
};
|
|
135
133
|
fetchData().catch(console.error);
|
|
@@ -139,7 +137,7 @@ export function SQLBuilderPage() {
|
|
|
139
137
|
useEffect(() => {
|
|
140
138
|
if (data) {
|
|
141
139
|
setDisplayedRows(
|
|
142
|
-
data[0]
|
|
140
|
+
data[0]?.rows.slice(0, showNumRows).map((rowData, index) => (
|
|
143
141
|
<tr key={`data-row:${index}`}>
|
|
144
142
|
{rowData.map(rowValue => (
|
|
145
143
|
<td key={rowValue}>{rowValue}</td>
|
|
@@ -167,15 +165,28 @@ export function SQLBuilderPage() {
|
|
|
167
165
|
<Select
|
|
168
166
|
name="metrics"
|
|
169
167
|
options={metrics}
|
|
168
|
+
isDisabled={
|
|
169
|
+
selectedMetrics.length && selectedDimensions.length
|
|
170
|
+
? true
|
|
171
|
+
: false
|
|
172
|
+
}
|
|
170
173
|
noOptionsMessage={() => 'No metrics found.'}
|
|
171
174
|
placeholder={`${metrics.length} Available Metrics`}
|
|
172
175
|
isMulti
|
|
173
176
|
isClearable
|
|
174
177
|
closeMenuOnSelect={false}
|
|
175
|
-
onChange={
|
|
176
|
-
|
|
178
|
+
onChange={e => {
|
|
179
|
+
setSelectedDimensions([]);
|
|
180
|
+
resetView();
|
|
181
|
+
setStagedMetrics(e.map(m => m.value));
|
|
182
|
+
}}
|
|
183
|
+
onMenuClose={() => {
|
|
184
|
+
resetView();
|
|
185
|
+
setSelectedDimensions([]);
|
|
186
|
+
setSelectedMetrics(stagedMetrics);
|
|
187
|
+
}}
|
|
177
188
|
/>
|
|
178
|
-
<h4>
|
|
189
|
+
<h4>Group By</h4>
|
|
179
190
|
<Select
|
|
180
191
|
name="dimensions"
|
|
181
192
|
formatOptionLabel={formatOptionLabel}
|
|
@@ -187,12 +198,17 @@ export function SQLBuilderPage() {
|
|
|
187
198
|
isMulti
|
|
188
199
|
isClearable
|
|
189
200
|
closeMenuOnSelect={false}
|
|
190
|
-
onChange={
|
|
191
|
-
|
|
201
|
+
onChange={e => {
|
|
202
|
+
resetView();
|
|
203
|
+
setStagedDimensions(e.map(d => d.value));
|
|
204
|
+
}}
|
|
205
|
+
onMenuClose={() => {
|
|
206
|
+
setSelectedDimensions(stagedDimensions);
|
|
207
|
+
}}
|
|
192
208
|
/>
|
|
193
209
|
</div>
|
|
194
210
|
<div className="card-header">
|
|
195
|
-
{
|
|
211
|
+
{!viewData && !query ? (
|
|
196
212
|
<div className="card-light-shadow">
|
|
197
213
|
<h6>Using the SQL Builder</h6>
|
|
198
214
|
<p>
|
|
@@ -272,18 +288,11 @@ export function SQLBuilderPage() {
|
|
|
272
288
|
) : (
|
|
273
289
|
<></>
|
|
274
290
|
)}
|
|
275
|
-
{
|
|
291
|
+
{queryInfo && queryInfo.id ? <QueryInfo {...queryInfo} /> : <></>}
|
|
276
292
|
<div>
|
|
277
293
|
{query && !viewData ? (
|
|
278
294
|
<SyntaxHighlighter language="sql" style={foundation}>
|
|
279
|
-
{
|
|
280
|
-
language: 'spark',
|
|
281
|
-
tabWidth: 2,
|
|
282
|
-
keywordCase: 'upper',
|
|
283
|
-
denseOperators: true,
|
|
284
|
-
logicalOperatorNewline: 'before',
|
|
285
|
-
expressionWidth: 10,
|
|
286
|
-
})}
|
|
295
|
+
{query}
|
|
287
296
|
</SyntaxHighlighter>
|
|
288
297
|
) : (
|
|
289
298
|
''
|
|
@@ -294,7 +303,7 @@ export function SQLBuilderPage() {
|
|
|
294
303
|
<table className="card-inner-table table">
|
|
295
304
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
296
305
|
<tr>
|
|
297
|
-
{data[0]
|
|
306
|
+
{data[0]?.columns.map(columnName => (
|
|
298
307
|
<th key={columnName.name}>{columnName.name}</th>
|
|
299
308
|
))}
|
|
300
309
|
</tr>
|
|
@@ -36,6 +36,13 @@ export const DataJunctionAPI = {
|
|
|
36
36
|
return data;
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
+
node_lineage: async function (name) {
|
|
40
|
+
const data = await (
|
|
41
|
+
await fetch(DJ_URL + '/nodes/' + name + '/lineage/')
|
|
42
|
+
).json();
|
|
43
|
+
return data;
|
|
44
|
+
},
|
|
45
|
+
|
|
39
46
|
metric: async function (name) {
|
|
40
47
|
const data = await (await fetch(DJ_URL + '/metrics/' + name + '/')).json();
|
|
41
48
|
return data;
|
|
@@ -43,7 +50,7 @@ export const DataJunctionAPI = {
|
|
|
43
50
|
|
|
44
51
|
clientCode: async function (name) {
|
|
45
52
|
const data = await (
|
|
46
|
-
await fetch(DJ_URL + '/
|
|
53
|
+
await fetch(DJ_URL + '/datajunction-clients/python/new_node/' + name)
|
|
47
54
|
).json();
|
|
48
55
|
return data;
|
|
49
56
|
},
|
|
@@ -70,11 +77,9 @@ export const DataJunctionAPI = {
|
|
|
70
77
|
const data = await (
|
|
71
78
|
await fetch(
|
|
72
79
|
DJ_URL +
|
|
73
|
-
'/history
|
|
74
|
-
type +
|
|
75
|
-
'/' +
|
|
80
|
+
'/history?node=' +
|
|
76
81
|
name +
|
|
77
|
-
|
|
82
|
+
`&offset=${offset ? offset : 0}&limit=${limit ? limit : 100}`,
|
|
78
83
|
)
|
|
79
84
|
).json();
|
|
80
85
|
return data;
|
|
@@ -108,6 +113,13 @@ export const DataJunctionAPI = {
|
|
|
108
113
|
return data;
|
|
109
114
|
},
|
|
110
115
|
|
|
116
|
+
nodesWithDimension: async function (name) {
|
|
117
|
+
const data = await (
|
|
118
|
+
await fetch(DJ_URL + '/dimensions/' + name + '/nodes/')
|
|
119
|
+
).json();
|
|
120
|
+
return data;
|
|
121
|
+
},
|
|
122
|
+
|
|
111
123
|
materializations: async function (node) {
|
|
112
124
|
const data = await (
|
|
113
125
|
await fetch(DJ_URL + `/nodes/${node}/materializations/`)
|
|
@@ -118,7 +130,7 @@ export const DataJunctionAPI = {
|
|
|
118
130
|
materialization.clientCode = await (
|
|
119
131
|
await fetch(
|
|
120
132
|
DJ_URL +
|
|
121
|
-
`/
|
|
133
|
+
`/datajunction-clients/python/add_materialization/${node}/${materialization.name}`,
|
|
122
134
|
)
|
|
123
135
|
).json();
|
|
124
136
|
return materialization;
|
|
@@ -129,12 +141,14 @@ export const DataJunctionAPI = {
|
|
|
129
141
|
columns: async function (node) {
|
|
130
142
|
return await Promise.all(
|
|
131
143
|
node.columns.map(async col => {
|
|
132
|
-
col.
|
|
133
|
-
await
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
if (col.dimension !== null) {
|
|
145
|
+
col.clientCode = await (
|
|
146
|
+
await fetch(
|
|
147
|
+
DJ_URL +
|
|
148
|
+
`/datajunction-clients/python/link_dimension/${node.name}/${col.name}/${col.dimension?.name}`,
|
|
149
|
+
)
|
|
150
|
+
).json();
|
|
151
|
+
}
|
|
138
152
|
return col;
|
|
139
153
|
}),
|
|
140
154
|
);
|
|
@@ -153,11 +167,20 @@ export const DataJunctionAPI = {
|
|
|
153
167
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
154
168
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
155
169
|
const data = await (
|
|
156
|
-
await fetch(DJ_URL + '/data/?' + params + '&limit=
|
|
170
|
+
await fetch(DJ_URL + '/data/?' + params + '&limit=10000')
|
|
157
171
|
).json();
|
|
158
172
|
return data;
|
|
159
173
|
},
|
|
160
174
|
|
|
175
|
+
stream: async function (metricSelection, dimensionSelection) {
|
|
176
|
+
const params = new URLSearchParams();
|
|
177
|
+
metricSelection.map(metric => params.append('metrics', metric));
|
|
178
|
+
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
179
|
+
return new EventSource(
|
|
180
|
+
DJ_URL + '/stream/?' + params + '&limit=10000&async_=true',
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
|
|
161
184
|
lineage: async function (node) {},
|
|
162
185
|
|
|
163
186
|
compiledSql: async function (node) {
|
package/src/styles/index.css
CHANGED
|
@@ -265,15 +265,18 @@ tr {
|
|
|
265
265
|
left: 0;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
.table-responsive
|
|
268
|
+
.table-responsive,
|
|
269
|
+
.table-vertical {
|
|
269
270
|
overflow-x: auto;
|
|
270
271
|
-webkit-overflow-scrolling: touch;
|
|
271
272
|
display: grid;
|
|
272
|
-
|
|
273
273
|
grid-template-columns: auto auto auto;
|
|
274
274
|
grid-gap: 8px;
|
|
275
275
|
padding: 8px;
|
|
276
276
|
}
|
|
277
|
+
.table-vertical {
|
|
278
|
+
display: contents;
|
|
279
|
+
}
|
|
277
280
|
.table > thead {
|
|
278
281
|
vertical-align: bottom;
|
|
279
282
|
}
|
|
@@ -430,12 +433,12 @@ tbody th {
|
|
|
430
433
|
color: #00b368;
|
|
431
434
|
}
|
|
432
435
|
|
|
433
|
-
.
|
|
434
|
-
background-color: #
|
|
435
|
-
color: #
|
|
436
|
+
.history_type__delete {
|
|
437
|
+
background-color: #ffc8c8 !important;
|
|
438
|
+
color: #9b5a5a;
|
|
436
439
|
}
|
|
437
440
|
|
|
438
|
-
.
|
|
441
|
+
.history_type__restore {
|
|
439
442
|
background-color: #ccf7e5 !important;
|
|
440
443
|
color: #00b368;
|
|
441
444
|
}
|
|
@@ -456,8 +459,13 @@ tbody th {
|
|
|
456
459
|
}
|
|
457
460
|
|
|
458
461
|
.history_type__set_attribute {
|
|
459
|
-
background-color: #
|
|
460
|
-
color: #
|
|
462
|
+
background-color: #e6f5ff !important;
|
|
463
|
+
color: #487c8c;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.history_type__status_change {
|
|
467
|
+
background-color: #ffe9db !important;
|
|
468
|
+
color: #573d3d;
|
|
461
469
|
}
|
|
462
470
|
|
|
463
471
|
.partition_value {
|
package/webpack.config.js
CHANGED
|
@@ -99,6 +99,7 @@ module.exports = {
|
|
|
99
99
|
plugins: [
|
|
100
100
|
new HtmlWebpackPlugin({
|
|
101
101
|
template: path.resolve(__dirname, 'public', 'index.html'),
|
|
102
|
+
favicon: path.resolve(__dirname, 'public', 'favicon.ico'),
|
|
102
103
|
}),
|
|
103
104
|
new webpack.DefinePlugin({
|
|
104
105
|
'process.env': JSON.stringify(process.env),
|