datajunction-ui 0.0.1-rc.12 → 0.0.1-rc.13
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/src/app/__tests__/__snapshots__/index.test.tsx.snap +2 -0
- package/src/app/components/QueryInfo.jsx +2 -34
- package/src/app/pages/NamespacePage/index.jsx +1 -1
- package/src/app/pages/NodePage/NodeHistory.jsx +104 -3
- package/src/app/pages/NodePage/NodeInfoTab.jsx +1 -18
- package/src/app/pages/NodePage/NodeSQLTab.jsx +1 -8
- package/src/app/pages/NodePage/NodesWithDimension.jsx +40 -0
- package/src/app/pages/NodePage/index.jsx +51 -30
- package/src/app/pages/SQLBuilderPage/index.jsx +64 -54
- package/src/app/services/DJService.js +29 -13
- package/src/styles/index.css +16 -8
package/.env
CHANGED
package/package.json
CHANGED
|
@@ -35,9 +35,11 @@ exports[`<App /> should render and match the snapshot 1`] = `
|
|
|
35
35
|
"namespaces": [Function],
|
|
36
36
|
"node": [Function],
|
|
37
37
|
"node_dag": [Function],
|
|
38
|
+
"nodesWithDimension": [Function],
|
|
38
39
|
"revisions": [Function],
|
|
39
40
|
"sql": [Function],
|
|
40
41
|
"sqls": [Function],
|
|
42
|
+
"stream": [Function],
|
|
41
43
|
"upstreams": [Function],
|
|
42
44
|
},
|
|
43
45
|
}
|
|
@@ -10,38 +10,6 @@ export default function QueryInfo({
|
|
|
10
10
|
started,
|
|
11
11
|
numRows,
|
|
12
12
|
}) {
|
|
13
|
-
const stateIcon =
|
|
14
|
-
state === 'FINISHED' ? (
|
|
15
|
-
<span className="status__valid status" style={{ alignContent: 'center' }}>
|
|
16
|
-
<svg
|
|
17
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
18
|
-
width="25"
|
|
19
|
-
height="25"
|
|
20
|
-
fill="currentColor"
|
|
21
|
-
className="bi bi-check-circle-fill"
|
|
22
|
-
viewBox="0 0 16 16"
|
|
23
|
-
>
|
|
24
|
-
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
|
|
25
|
-
</svg>
|
|
26
|
-
</span>
|
|
27
|
-
) : (
|
|
28
|
-
<span
|
|
29
|
-
className="status__invalid status"
|
|
30
|
-
style={{ alignContent: 'center' }}
|
|
31
|
-
>
|
|
32
|
-
<svg
|
|
33
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
34
|
-
width="16"
|
|
35
|
-
height="16"
|
|
36
|
-
fill="currentColor"
|
|
37
|
-
className="bi bi-x-circle-fill"
|
|
38
|
-
viewBox="0 0 16 16"
|
|
39
|
-
>
|
|
40
|
-
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
|
|
41
|
-
</svg>
|
|
42
|
-
</span>
|
|
43
|
-
);
|
|
44
|
-
|
|
45
13
|
return (
|
|
46
14
|
<div className="table-responsive">
|
|
47
15
|
<table className="card-inner-table table">
|
|
@@ -70,11 +38,11 @@ export default function QueryInfo({
|
|
|
70
38
|
{engine_version}
|
|
71
39
|
</span>
|
|
72
40
|
</td>
|
|
73
|
-
<td>{
|
|
41
|
+
<td>{state}</td>
|
|
74
42
|
<td>{scheduled}</td>
|
|
75
43
|
<td>{started}</td>
|
|
76
44
|
<td>
|
|
77
|
-
{errors
|
|
45
|
+
{errors?.length ? (
|
|
78
46
|
errors.map(e => (
|
|
79
47
|
<p>
|
|
80
48
|
<span className="rounded-pill badge bg-secondary-error">
|
|
@@ -13,6 +13,107 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
13
13
|
};
|
|
14
14
|
fetchData().catch(console.error);
|
|
15
15
|
}, [djClient, node]);
|
|
16
|
+
|
|
17
|
+
const eventData = event => {
|
|
18
|
+
console.log('event', event);
|
|
19
|
+
if (
|
|
20
|
+
event.activity_type === 'set_attribute' &&
|
|
21
|
+
event.entity_type === 'column_attribute'
|
|
22
|
+
) {
|
|
23
|
+
return event.details.attributes
|
|
24
|
+
.map(attr => (
|
|
25
|
+
<div>
|
|
26
|
+
Set{' '}
|
|
27
|
+
<span className={`badge partition_value`}>{attr.column_name}</span>{' '}
|
|
28
|
+
as{' '}
|
|
29
|
+
<span className={`badge partition_value_highlight`}>
|
|
30
|
+
{attr.attribute_type_name}
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
))
|
|
34
|
+
.reduce((prev, curr) => [prev, <br />, curr]);
|
|
35
|
+
}
|
|
36
|
+
if (event.activity_type === 'create' && event.entity_type === 'link') {
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
Linked{' '}
|
|
40
|
+
<span className={`badge partition_value`}>
|
|
41
|
+
{event.details.column}
|
|
42
|
+
</span>{' '}
|
|
43
|
+
to
|
|
44
|
+
<span className={`badge partition_value_highlight`}>
|
|
45
|
+
{event.details.dimension}
|
|
46
|
+
</span>{' '}
|
|
47
|
+
via
|
|
48
|
+
<span className={`badge partition_value`}>
|
|
49
|
+
{event.details.dimension_column}
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
event.activity_type === 'create' &&
|
|
56
|
+
event.entity_type === 'materialization'
|
|
57
|
+
) {
|
|
58
|
+
return (
|
|
59
|
+
<div>
|
|
60
|
+
Initialized materialization{' '}
|
|
61
|
+
<span className={`badge partition_value`}>
|
|
62
|
+
{event.details.materialization}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
event.activity_type === 'create' &&
|
|
69
|
+
event.entity_type === 'availability'
|
|
70
|
+
) {
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
Materialized at{' '}
|
|
74
|
+
<span className={`badge partition_value_highlight`}>
|
|
75
|
+
{event.post.catalog}.{event.post.schema_}.{event.post.table}
|
|
76
|
+
</span>
|
|
77
|
+
from{' '}
|
|
78
|
+
<span className={`badge partition_value`}>
|
|
79
|
+
{event.post.min_temporal_partition}
|
|
80
|
+
</span>{' '}
|
|
81
|
+
to
|
|
82
|
+
<span className={`badge partition_value`}>
|
|
83
|
+
{event.post.max_temporal_partition}
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
event.activity_type === 'status_change' &&
|
|
90
|
+
event.entity_type === 'node'
|
|
91
|
+
) {
|
|
92
|
+
const expr = (
|
|
93
|
+
<div>
|
|
94
|
+
Caused by a change in upstream{' '}
|
|
95
|
+
<a href={`/nodes/${event.details['upstream_node']}`}>
|
|
96
|
+
{event.details['upstream_node']}
|
|
97
|
+
</a>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
return (
|
|
101
|
+
<div>
|
|
102
|
+
Status changed from{' '}
|
|
103
|
+
<span className={`status__${event.pre['status']}`}>
|
|
104
|
+
{event.pre['status']}
|
|
105
|
+
</span>{' '}
|
|
106
|
+
to{' '}
|
|
107
|
+
<span className={`status__${event.post['status']}`}>
|
|
108
|
+
{event.post['status']}
|
|
109
|
+
</span>{' '}
|
|
110
|
+
{event.details['upstream_node'] !== undefined ? expr : ''}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return '';
|
|
115
|
+
};
|
|
116
|
+
|
|
16
117
|
const tableData = history => {
|
|
17
118
|
return history.map(event => (
|
|
18
119
|
<tr>
|
|
@@ -24,9 +125,9 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
24
125
|
</span>
|
|
25
126
|
</td>
|
|
26
127
|
<td>{event.entity_type}</td>
|
|
27
|
-
<td>{event.entity_name}</td>
|
|
28
128
|
<td>{event.user ? event.user : 'unknown'}</td>
|
|
29
129
|
<td>{event.created_at}</td>
|
|
130
|
+
<td>{eventData(event)}</td>
|
|
30
131
|
</tr>
|
|
31
132
|
));
|
|
32
133
|
};
|
|
@@ -45,7 +146,7 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
45
146
|
));
|
|
46
147
|
};
|
|
47
148
|
return (
|
|
48
|
-
<div className="table-
|
|
149
|
+
<div className="table-vertical">
|
|
49
150
|
<table className="card-inner-table table">
|
|
50
151
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
51
152
|
<th className="text-start">Version</th>
|
|
@@ -60,9 +161,9 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
60
161
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
61
162
|
<th className="text-start">Activity</th>
|
|
62
163
|
<th>Type</th>
|
|
63
|
-
<th>Name</th>
|
|
64
164
|
<th>User</th>
|
|
65
165
|
<th>Timestamp</th>
|
|
166
|
+
<th>Details</th>
|
|
66
167
|
</thead>
|
|
67
168
|
{tableData(history)}
|
|
68
169
|
</table>
|
|
@@ -2,7 +2,6 @@ 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
5
|
import NodeStatus from './NodeStatus';
|
|
7
6
|
import ListGroupItem from '../../components/ListGroupItem';
|
|
8
7
|
import ToggleSwitch from '../../components/ToggleSwitch';
|
|
@@ -50,23 +49,7 @@ export default function NodeInfoTab({ node }) {
|
|
|
50
49
|
<></>
|
|
51
50
|
)}
|
|
52
51
|
<SyntaxHighlighter language="sql" style={foundation}>
|
|
53
|
-
{checked
|
|
54
|
-
? format(compiledSQL, {
|
|
55
|
-
language: 'spark',
|
|
56
|
-
tabWidth: 2,
|
|
57
|
-
keywordCase: 'upper',
|
|
58
|
-
denseOperators: true,
|
|
59
|
-
logicalOperatorNewline: 'before',
|
|
60
|
-
expressionWidth: 10,
|
|
61
|
-
})
|
|
62
|
-
: format(node?.query, {
|
|
63
|
-
language: 'spark',
|
|
64
|
-
tabWidth: 2,
|
|
65
|
-
keywordCase: 'upper',
|
|
66
|
-
denseOperators: true,
|
|
67
|
-
logicalOperatorNewline: 'before',
|
|
68
|
-
expressionWidth: 10,
|
|
69
|
-
})}
|
|
52
|
+
{checked ? compiledSQL : node?.query}
|
|
70
53
|
</SyntaxHighlighter>
|
|
71
54
|
</div>
|
|
72
55
|
</div>
|
|
@@ -82,14 +82,7 @@ const NodeSQLTab = djNode => {
|
|
|
82
82
|
>
|
|
83
83
|
<h6 className="mb-0 w-100">Query</h6>
|
|
84
84
|
<SyntaxHighlighter language="sql" style={foundation}>
|
|
85
|
-
{
|
|
86
|
-
language: 'spark',
|
|
87
|
-
tabWidth: 2,
|
|
88
|
-
keywordCase: 'upper',
|
|
89
|
-
denseOperators: true,
|
|
90
|
-
logicalOperatorNewline: 'before',
|
|
91
|
-
expressionWidth: 10,
|
|
92
|
-
})}
|
|
85
|
+
{query}
|
|
93
86
|
</SyntaxHighlighter>
|
|
94
87
|
</div>
|
|
95
88
|
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
export default function NodesWithDimension({ node, djClient }) {
|
|
5
|
+
const [availableNodes, setAvailableNodes] = useState([]);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const fetchData = async () => {
|
|
9
|
+
const data = await djClient.nodesWithDimension(node.name);
|
|
10
|
+
setAvailableNodes(data);
|
|
11
|
+
};
|
|
12
|
+
fetchData().catch(console.error);
|
|
13
|
+
}, [djClient, node]);
|
|
14
|
+
return (
|
|
15
|
+
<div className="table-responsive">
|
|
16
|
+
<table className="card-inner-table table">
|
|
17
|
+
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
18
|
+
<th className="text-start">Name</th>
|
|
19
|
+
<th>Type</th>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
{availableNodes.map(node => (
|
|
23
|
+
<tr>
|
|
24
|
+
<td>
|
|
25
|
+
<a href={`/nodes/${node.name}`}>{node.display_name}</a>
|
|
26
|
+
</td>
|
|
27
|
+
<td>
|
|
28
|
+
<span
|
|
29
|
+
className={'node_type__' + node.type + ' badge node_type'}
|
|
30
|
+
>
|
|
31
|
+
{node.type}
|
|
32
|
+
</span>
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
))}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -11,6 +11,7 @@ import DJClientContext from '../../providers/djclient';
|
|
|
11
11
|
import NodeSQLTab from './NodeSQLTab';
|
|
12
12
|
import NodeMaterializationTab from './NodeMaterializationTab';
|
|
13
13
|
import ClientCodePopover from './ClientCodePopover';
|
|
14
|
+
import NodesWithDimension from './NodesWithDimension';
|
|
14
15
|
|
|
15
16
|
export function NodePage() {
|
|
16
17
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
@@ -25,7 +26,7 @@ export function NodePage() {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const buildTabs = tab => {
|
|
28
|
-
return (
|
|
29
|
+
return tab.display ? (
|
|
29
30
|
<Tab
|
|
30
31
|
key={tab.id}
|
|
31
32
|
id={tab.id}
|
|
@@ -33,7 +34,7 @@ export function NodePage() {
|
|
|
33
34
|
onClick={onClickTab(tab.id)}
|
|
34
35
|
selectedTab={state.selectedTab}
|
|
35
36
|
/>
|
|
36
|
-
);
|
|
37
|
+
) : null;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
const { name } = useParams();
|
|
@@ -57,32 +58,46 @@ export function NodePage() {
|
|
|
57
58
|
fetchData().catch(console.error);
|
|
58
59
|
}, [djClient, name]);
|
|
59
60
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
61
|
+
const tabsList = node => {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
id: 0,
|
|
65
|
+
name: 'Info',
|
|
66
|
+
display: true,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 1,
|
|
70
|
+
name: 'Columns',
|
|
71
|
+
display: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 2,
|
|
75
|
+
name: 'Graph',
|
|
76
|
+
display: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 3,
|
|
80
|
+
name: 'History',
|
|
81
|
+
display: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 4,
|
|
85
|
+
name: 'SQL',
|
|
86
|
+
display: node?.type !== 'dimension' && node?.type !== 'source',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 5,
|
|
90
|
+
name: 'Materializations',
|
|
91
|
+
display: node?.type !== 'source',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 6,
|
|
95
|
+
name: 'Linked Nodes',
|
|
96
|
+
display: node?.type === 'dimension',
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
};
|
|
100
|
+
|
|
86
101
|
//
|
|
87
102
|
//
|
|
88
103
|
let tabToDisplay = null;
|
|
@@ -106,6 +121,9 @@ export function NodePage() {
|
|
|
106
121
|
case 5:
|
|
107
122
|
tabToDisplay = <NodeMaterializationTab node={node} djClient={djClient} />;
|
|
108
123
|
break;
|
|
124
|
+
case 6:
|
|
125
|
+
tabToDisplay = <NodesWithDimension node={node} djClient={djClient} />;
|
|
126
|
+
break;
|
|
109
127
|
default:
|
|
110
128
|
tabToDisplay = <NodeInfoTab node={node} />;
|
|
111
129
|
}
|
|
@@ -121,12 +139,15 @@ export function NodePage() {
|
|
|
121
139
|
style={{ display: 'inline-block' }}
|
|
122
140
|
>
|
|
123
141
|
<span className="card-label fw-bold text-gray-800">
|
|
124
|
-
{node?.display_name}
|
|
142
|
+
{node?.display_name}{' '}
|
|
143
|
+
<span className={'node_type__' + node?.type + ' badge node_type'}>
|
|
144
|
+
{node?.type}
|
|
145
|
+
</span>
|
|
125
146
|
</span>
|
|
126
147
|
</h3>
|
|
127
148
|
<ClientCodePopover code={node?.createNodeClientCode} />
|
|
128
149
|
<div className="align-items-center row">
|
|
129
|
-
{
|
|
150
|
+
{tabsList(node).map(buildTabs)}
|
|
130
151
|
</div>
|
|
131
152
|
{tabToDisplay}
|
|
132
153
|
</div>
|
|
@@ -9,6 +9,7 @@ import Select from 'react-select';
|
|
|
9
9
|
import QueryInfo from '../../components/QueryInfo';
|
|
10
10
|
|
|
11
11
|
export function SQLBuilderPage() {
|
|
12
|
+
const DEFAULT_NUM_ROWS = 100;
|
|
12
13
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
13
14
|
const [stagedMetrics, setStagedMetrics] = useState([]);
|
|
14
15
|
const [metrics, setMetrics] = useState([]);
|
|
@@ -17,12 +18,11 @@ export function SQLBuilderPage() {
|
|
|
17
18
|
const [stagedDimensions, setStagedDimensions] = useState([]);
|
|
18
19
|
const [selectedMetrics, setSelectedMetrics] = useState([]);
|
|
19
20
|
const [query, setQuery] = useState('');
|
|
20
|
-
const [
|
|
21
|
+
const [queryInfo, setQueryInfo] = useState({});
|
|
21
22
|
const [data, setData] = useState(null);
|
|
22
23
|
const [loadingData, setLoadingData] = useState(false);
|
|
23
24
|
const [viewData, setViewData] = useState(false);
|
|
24
|
-
const [
|
|
25
|
-
const [showNumRows, setShowNumRows] = useState(100);
|
|
25
|
+
const [showNumRows, setShowNumRows] = useState(DEFAULT_NUM_ROWS);
|
|
26
26
|
const [displayedRows, setDisplayedRows] = useState(<></>);
|
|
27
27
|
const numRowsOptions = [
|
|
28
28
|
{
|
|
@@ -46,20 +46,39 @@ export function SQLBuilderPage() {
|
|
|
46
46
|
// Get data for the current selection of metrics and dimensions
|
|
47
47
|
const getData = () => {
|
|
48
48
|
setLoadingData(true);
|
|
49
|
+
setQueryInfo({});
|
|
49
50
|
const fetchData = async () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
if (process.env.REACT_USE_SSE) {
|
|
52
|
+
const sse = await djClient.stream(selectedMetrics, selectedDimensions);
|
|
53
|
+
sse.onmessage = e => {
|
|
54
|
+
const messageData = JSON.parse(JSON.parse(e.data));
|
|
55
|
+
setQueryInfo(messageData);
|
|
56
|
+
if (messageData.results) {
|
|
57
|
+
setLoadingData(false);
|
|
58
|
+
setData(messageData.results);
|
|
59
|
+
messageData.numRows = messageData.results?.length
|
|
60
|
+
? messageData.results[0].rows.length
|
|
61
|
+
: [];
|
|
62
|
+
setViewData(true);
|
|
63
|
+
setShowNumRows(DEFAULT_NUM_ROWS);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
sse.onerror = () => sse.close();
|
|
67
|
+
} else {
|
|
68
|
+
const response = await djClient.data(
|
|
69
|
+
selectedMetrics,
|
|
70
|
+
selectedDimensions,
|
|
71
|
+
);
|
|
72
|
+
setQueryInfo(response);
|
|
73
|
+
if (response.results) {
|
|
74
|
+
setLoadingData(false);
|
|
75
|
+
setData(response.results);
|
|
76
|
+
response.numRows = response.results?.length
|
|
77
|
+
? response.results[0].rows.length
|
|
78
|
+
: [];
|
|
79
|
+
setViewData(true);
|
|
80
|
+
setShowNumRows(DEFAULT_NUM_ROWS);
|
|
81
|
+
}
|
|
63
82
|
}
|
|
64
83
|
};
|
|
65
84
|
fetchData().catch(console.error);
|
|
@@ -69,25 +88,7 @@ export function SQLBuilderPage() {
|
|
|
69
88
|
setQuery('');
|
|
70
89
|
setData(null);
|
|
71
90
|
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);
|
|
91
|
+
setQueryInfo({});
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
// Get metrics
|
|
@@ -125,11 +126,9 @@ export function SQLBuilderPage() {
|
|
|
125
126
|
const fetchData = async () => {
|
|
126
127
|
if (selectedMetrics.length && selectedDimensions.length) {
|
|
127
128
|
const query = await djClient.sqls(selectedMetrics, selectedDimensions);
|
|
128
|
-
setShowHelp(false);
|
|
129
129
|
setQuery(query.sql);
|
|
130
130
|
} else {
|
|
131
131
|
resetView();
|
|
132
|
-
setShowHelp(true);
|
|
133
132
|
}
|
|
134
133
|
};
|
|
135
134
|
fetchData().catch(console.error);
|
|
@@ -139,7 +138,7 @@ export function SQLBuilderPage() {
|
|
|
139
138
|
useEffect(() => {
|
|
140
139
|
if (data) {
|
|
141
140
|
setDisplayedRows(
|
|
142
|
-
data[0]
|
|
141
|
+
data[0]?.rows.slice(0, showNumRows).map((rowData, index) => (
|
|
143
142
|
<tr key={`data-row:${index}`}>
|
|
144
143
|
{rowData.map(rowValue => (
|
|
145
144
|
<td key={rowValue}>{rowValue}</td>
|
|
@@ -167,15 +166,28 @@ export function SQLBuilderPage() {
|
|
|
167
166
|
<Select
|
|
168
167
|
name="metrics"
|
|
169
168
|
options={metrics}
|
|
169
|
+
isDisabled={
|
|
170
|
+
selectedMetrics.length && selectedDimensions.length
|
|
171
|
+
? true
|
|
172
|
+
: false
|
|
173
|
+
}
|
|
170
174
|
noOptionsMessage={() => 'No metrics found.'}
|
|
171
175
|
placeholder={`${metrics.length} Available Metrics`}
|
|
172
176
|
isMulti
|
|
173
177
|
isClearable
|
|
174
178
|
closeMenuOnSelect={false}
|
|
175
|
-
onChange={
|
|
176
|
-
|
|
179
|
+
onChange={e => {
|
|
180
|
+
setSelectedDimensions([]);
|
|
181
|
+
resetView();
|
|
182
|
+
setStagedMetrics(e.map(m => m.value));
|
|
183
|
+
}}
|
|
184
|
+
onMenuClose={() => {
|
|
185
|
+
resetView();
|
|
186
|
+
setSelectedDimensions([]);
|
|
187
|
+
setSelectedMetrics(stagedMetrics);
|
|
188
|
+
}}
|
|
177
189
|
/>
|
|
178
|
-
<h4>
|
|
190
|
+
<h4>Group By</h4>
|
|
179
191
|
<Select
|
|
180
192
|
name="dimensions"
|
|
181
193
|
formatOptionLabel={formatOptionLabel}
|
|
@@ -187,12 +199,17 @@ export function SQLBuilderPage() {
|
|
|
187
199
|
isMulti
|
|
188
200
|
isClearable
|
|
189
201
|
closeMenuOnSelect={false}
|
|
190
|
-
onChange={
|
|
191
|
-
|
|
202
|
+
onChange={e => {
|
|
203
|
+
resetView();
|
|
204
|
+
setStagedDimensions(e.map(d => d.value));
|
|
205
|
+
}}
|
|
206
|
+
onMenuClose={() => {
|
|
207
|
+
setSelectedDimensions(stagedDimensions);
|
|
208
|
+
}}
|
|
192
209
|
/>
|
|
193
210
|
</div>
|
|
194
211
|
<div className="card-header">
|
|
195
|
-
{
|
|
212
|
+
{!viewData && !query ? (
|
|
196
213
|
<div className="card-light-shadow">
|
|
197
214
|
<h6>Using the SQL Builder</h6>
|
|
198
215
|
<p>
|
|
@@ -272,18 +289,11 @@ export function SQLBuilderPage() {
|
|
|
272
289
|
) : (
|
|
273
290
|
<></>
|
|
274
291
|
)}
|
|
275
|
-
{
|
|
292
|
+
{queryInfo && queryInfo.id ? <QueryInfo {...queryInfo} /> : <></>}
|
|
276
293
|
<div>
|
|
277
294
|
{query && !viewData ? (
|
|
278
295
|
<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
|
-
})}
|
|
296
|
+
{query}
|
|
287
297
|
</SyntaxHighlighter>
|
|
288
298
|
) : (
|
|
289
299
|
''
|
|
@@ -294,7 +304,7 @@ export function SQLBuilderPage() {
|
|
|
294
304
|
<table className="card-inner-table table">
|
|
295
305
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
296
306
|
<tr>
|
|
297
|
-
{data[0]
|
|
307
|
+
{data[0]?.columns.map(columnName => (
|
|
298
308
|
<th key={columnName.name}>{columnName.name}</th>
|
|
299
309
|
))}
|
|
300
310
|
</tr>
|
|
@@ -43,7 +43,7 @@ export const DataJunctionAPI = {
|
|
|
43
43
|
|
|
44
44
|
clientCode: async function (name) {
|
|
45
45
|
const data = await (
|
|
46
|
-
await fetch(DJ_URL + '/
|
|
46
|
+
await fetch(DJ_URL + '/datajunction-clients/python/new_node/' + name)
|
|
47
47
|
).json();
|
|
48
48
|
return data;
|
|
49
49
|
},
|
|
@@ -70,11 +70,9 @@ export const DataJunctionAPI = {
|
|
|
70
70
|
const data = await (
|
|
71
71
|
await fetch(
|
|
72
72
|
DJ_URL +
|
|
73
|
-
'/history
|
|
74
|
-
type +
|
|
75
|
-
'/' +
|
|
73
|
+
'/history?node=' +
|
|
76
74
|
name +
|
|
77
|
-
|
|
75
|
+
`&offset=${offset ? offset : 0}&limit=${limit ? limit : 100}`,
|
|
78
76
|
)
|
|
79
77
|
).json();
|
|
80
78
|
return data;
|
|
@@ -108,6 +106,13 @@ export const DataJunctionAPI = {
|
|
|
108
106
|
return data;
|
|
109
107
|
},
|
|
110
108
|
|
|
109
|
+
nodesWithDimension: async function (name) {
|
|
110
|
+
const data = await (
|
|
111
|
+
await fetch(DJ_URL + '/dimensions/' + name + '/nodes/')
|
|
112
|
+
).json();
|
|
113
|
+
return data;
|
|
114
|
+
},
|
|
115
|
+
|
|
111
116
|
materializations: async function (node) {
|
|
112
117
|
const data = await (
|
|
113
118
|
await fetch(DJ_URL + `/nodes/${node}/materializations/`)
|
|
@@ -118,7 +123,7 @@ export const DataJunctionAPI = {
|
|
|
118
123
|
materialization.clientCode = await (
|
|
119
124
|
await fetch(
|
|
120
125
|
DJ_URL +
|
|
121
|
-
`/
|
|
126
|
+
`/datajunction-clients/python/add_materialization/${node}/${materialization.name}`,
|
|
122
127
|
)
|
|
123
128
|
).json();
|
|
124
129
|
return materialization;
|
|
@@ -129,12 +134,14 @@ export const DataJunctionAPI = {
|
|
|
129
134
|
columns: async function (node) {
|
|
130
135
|
return await Promise.all(
|
|
131
136
|
node.columns.map(async col => {
|
|
132
|
-
col.
|
|
133
|
-
await
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
if (col.dimension !== null) {
|
|
138
|
+
col.clientCode = await (
|
|
139
|
+
await fetch(
|
|
140
|
+
DJ_URL +
|
|
141
|
+
`/datajunction-clients/python/link_dimension/${node.name}/${col.name}/${col.dimension?.name}`,
|
|
142
|
+
)
|
|
143
|
+
).json();
|
|
144
|
+
}
|
|
138
145
|
return col;
|
|
139
146
|
}),
|
|
140
147
|
);
|
|
@@ -153,11 +160,20 @@ export const DataJunctionAPI = {
|
|
|
153
160
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
154
161
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
155
162
|
const data = await (
|
|
156
|
-
await fetch(DJ_URL + '/data/?' + params + '&limit=
|
|
163
|
+
await fetch(DJ_URL + '/data/?' + params + '&limit=10000')
|
|
157
164
|
).json();
|
|
158
165
|
return data;
|
|
159
166
|
},
|
|
160
167
|
|
|
168
|
+
stream: async function (metricSelection, dimensionSelection) {
|
|
169
|
+
const params = new URLSearchParams();
|
|
170
|
+
metricSelection.map(metric => params.append('metrics', metric));
|
|
171
|
+
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
172
|
+
return new EventSource(
|
|
173
|
+
DJ_URL + '/stream/?' + params + '&limit=10000&async_=true',
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
|
|
161
177
|
lineage: async function (node) {},
|
|
162
178
|
|
|
163
179
|
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 {
|