datajunction-ui 0.0.23 → 0.0.26
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/package.json +11 -4
- package/src/app/index.tsx +6 -0
- package/src/app/pages/NamespacePage/CompactSelect.jsx +100 -0
- package/src/app/pages/NamespacePage/NodeModeSelect.jsx +8 -5
- package/src/app/pages/NamespacePage/__tests__/CompactSelect.test.jsx +190 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +297 -8
- package/src/app/pages/NamespacePage/index.jsx +489 -62
- package/src/app/pages/QueryPlannerPage/Loadable.jsx +6 -0
- package/src/app/pages/QueryPlannerPage/MetricFlowGraph.jsx +311 -0
- package/src/app/pages/QueryPlannerPage/PreAggDetailsPanel.jsx +470 -0
- package/src/app/pages/QueryPlannerPage/SelectionPanel.jsx +384 -0
- package/src/app/pages/QueryPlannerPage/__tests__/MetricFlowGraph.test.jsx +239 -0
- package/src/app/pages/QueryPlannerPage/__tests__/PreAggDetailsPanel.test.jsx +638 -0
- package/src/app/pages/QueryPlannerPage/__tests__/SelectionPanel.test.jsx +429 -0
- package/src/app/pages/QueryPlannerPage/__tests__/index.test.jsx +317 -0
- package/src/app/pages/QueryPlannerPage/index.jsx +209 -0
- package/src/app/pages/QueryPlannerPage/styles.css +1251 -0
- package/src/app/pages/Root/index.tsx +5 -0
- package/src/app/services/DJService.js +61 -2
- package/src/styles/index.css +2 -2
- package/src/app/icons/FilterIcon.jsx +0 -7
- package/src/app/pages/NamespacePage/FieldControl.jsx +0 -21
- package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +0 -30
- package/src/app/pages/NamespacePage/TagSelect.jsx +0 -44
- package/src/app/pages/NamespacePage/UserSelect.jsx +0 -47
|
@@ -64,6 +64,11 @@ export function Root() {
|
|
|
64
64
|
<a href="/sql">SQL</a>
|
|
65
65
|
</span>
|
|
66
66
|
</span>
|
|
67
|
+
<span className="menu-link">
|
|
68
|
+
<span className="menu-title">
|
|
69
|
+
<a href="/materialization-planner">Planner</a>
|
|
70
|
+
</span>
|
|
71
|
+
</span>
|
|
67
72
|
<span className="menu-link">
|
|
68
73
|
<span className="menu-title">
|
|
69
74
|
<div className="dropdown">
|
|
@@ -19,9 +19,16 @@ export const DataJunctionAPI = {
|
|
|
19
19
|
limit,
|
|
20
20
|
sortConfig,
|
|
21
21
|
mode,
|
|
22
|
+
{
|
|
23
|
+
ownedBy = null,
|
|
24
|
+
statuses = null,
|
|
25
|
+
missingDescription = false,
|
|
26
|
+
hasMaterialization = false,
|
|
27
|
+
orphanedDimension = false,
|
|
28
|
+
} = {},
|
|
22
29
|
) {
|
|
23
30
|
const query = `
|
|
24
|
-
query ListNodes($namespace: String, $nodeTypes: [NodeType!], $tags: [String!], $editedBy: String, $mode: NodeMode, $before: String, $after: String, $limit: Int, $orderBy: NodeSortField, $ascending: Boolean) {
|
|
31
|
+
query ListNodes($namespace: String, $nodeTypes: [NodeType!], $tags: [String!], $editedBy: String, $mode: NodeMode, $before: String, $after: String, $limit: Int, $orderBy: NodeSortField, $ascending: Boolean, $ownedBy: String, $statuses: [NodeStatus!], $missingDescription: Boolean, $hasMaterialization: Boolean, $orphanedDimension: Boolean) {
|
|
25
32
|
findNodesPaginated(
|
|
26
33
|
namespace: $namespace
|
|
27
34
|
nodeTypes: $nodeTypes
|
|
@@ -31,8 +38,13 @@ export const DataJunctionAPI = {
|
|
|
31
38
|
limit: $limit
|
|
32
39
|
before: $before
|
|
33
40
|
after: $after
|
|
34
|
-
orderBy: $orderBy
|
|
41
|
+
orderBy: $orderBy
|
|
35
42
|
ascending: $ascending
|
|
43
|
+
ownedBy: $ownedBy
|
|
44
|
+
statuses: $statuses
|
|
45
|
+
missingDescription: $missingDescription
|
|
46
|
+
hasMaterialization: $hasMaterialization
|
|
47
|
+
orphanedDimension: $orphanedDimension
|
|
36
48
|
) {
|
|
37
49
|
pageInfo {
|
|
38
50
|
hasNextPage
|
|
@@ -92,6 +104,11 @@ export const DataJunctionAPI = {
|
|
|
92
104
|
limit: limit,
|
|
93
105
|
orderBy: sortOrderMapping[sortConfig.key],
|
|
94
106
|
ascending: sortConfig.direction === 'ascending',
|
|
107
|
+
ownedBy: ownedBy,
|
|
108
|
+
statuses: statuses,
|
|
109
|
+
missingDescription: missingDescription,
|
|
110
|
+
hasMaterialization: hasMaterialization,
|
|
111
|
+
orphanedDimension: orphanedDimension,
|
|
95
112
|
},
|
|
96
113
|
}),
|
|
97
114
|
})
|
|
@@ -897,6 +914,48 @@ export const DataJunctionAPI = {
|
|
|
897
914
|
).json();
|
|
898
915
|
},
|
|
899
916
|
|
|
917
|
+
// V3 Measures SQL - returns pre-aggregations with components for materialization planning
|
|
918
|
+
measuresV3: async function (
|
|
919
|
+
metricSelection,
|
|
920
|
+
dimensionSelection,
|
|
921
|
+
filters = '',
|
|
922
|
+
) {
|
|
923
|
+
const params = new URLSearchParams();
|
|
924
|
+
metricSelection.forEach(metric => params.append('metrics', metric));
|
|
925
|
+
dimensionSelection.forEach(dimension =>
|
|
926
|
+
params.append('dimensions', dimension),
|
|
927
|
+
);
|
|
928
|
+
if (filters) {
|
|
929
|
+
params.append('filters', filters);
|
|
930
|
+
}
|
|
931
|
+
return await (
|
|
932
|
+
await fetch(`${DJ_URL}/sql/measures/v3/?${params}`, {
|
|
933
|
+
credentials: 'include',
|
|
934
|
+
})
|
|
935
|
+
).json();
|
|
936
|
+
},
|
|
937
|
+
|
|
938
|
+
// V3 Metrics SQL - returns final combined SQL query
|
|
939
|
+
metricsV3: async function (
|
|
940
|
+
metricSelection,
|
|
941
|
+
dimensionSelection,
|
|
942
|
+
filters = '',
|
|
943
|
+
) {
|
|
944
|
+
const params = new URLSearchParams();
|
|
945
|
+
metricSelection.forEach(metric => params.append('metrics', metric));
|
|
946
|
+
dimensionSelection.forEach(dimension =>
|
|
947
|
+
params.append('dimensions', dimension),
|
|
948
|
+
);
|
|
949
|
+
if (filters) {
|
|
950
|
+
params.append('filters', filters);
|
|
951
|
+
}
|
|
952
|
+
return await (
|
|
953
|
+
await fetch(`${DJ_URL}/sql/metrics/v3/?${params}`, {
|
|
954
|
+
credentials: 'include',
|
|
955
|
+
})
|
|
956
|
+
).json();
|
|
957
|
+
},
|
|
958
|
+
|
|
900
959
|
data: async function (metricSelection, dimensionSelection) {
|
|
901
960
|
const params = new URLSearchParams();
|
|
902
961
|
metricSelection.map(metric => params.append('metrics', metric));
|
package/src/styles/index.css
CHANGED
|
@@ -775,9 +775,8 @@ pre {
|
|
|
775
775
|
|
|
776
776
|
.card-header {
|
|
777
777
|
align-items: center;
|
|
778
|
-
margin: 0.5rem;
|
|
779
778
|
margin-left: 0;
|
|
780
|
-
padding:
|
|
779
|
+
padding: 1.5rem 2.25rem 0 2.25rem;
|
|
781
780
|
}
|
|
782
781
|
|
|
783
782
|
.card-header h2 {
|
|
@@ -1000,6 +999,7 @@ pre {
|
|
|
1000
999
|
margin-top: 6px;
|
|
1001
1000
|
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
|
1002
1001
|
min-width: 115px;
|
|
1002
|
+
z-index: 1100;
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
1005
|
.dropdown:hover .dropdown-content {
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { components } from 'react-select';
|
|
2
|
-
|
|
3
|
-
const Control = ({ children, ...props }) => {
|
|
4
|
-
const { label, onLabelClick } = props.selectProps;
|
|
5
|
-
const style = {
|
|
6
|
-
cursor: 'pointer',
|
|
7
|
-
padding: '10px 5px 10px 12px',
|
|
8
|
-
color: 'rgb(112, 110, 115)',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<components.Control {...props}>
|
|
13
|
-
<span onMouseDown={onLabelClick} style={style}>
|
|
14
|
-
{label}
|
|
15
|
-
</span>
|
|
16
|
-
{children}
|
|
17
|
-
</components.Control>
|
|
18
|
-
);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default Control;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import Select from 'react-select';
|
|
2
|
-
import Control from './FieldControl';
|
|
3
|
-
|
|
4
|
-
export default function NodeTypeSelect({ onChange }) {
|
|
5
|
-
return (
|
|
6
|
-
<span
|
|
7
|
-
className="menu-link"
|
|
8
|
-
style={{ marginLeft: '30px', width: '300px' }}
|
|
9
|
-
data-testid="select-node-type"
|
|
10
|
-
>
|
|
11
|
-
<Select
|
|
12
|
-
name="node_type"
|
|
13
|
-
isClearable
|
|
14
|
-
label="Type"
|
|
15
|
-
components={{ Control }}
|
|
16
|
-
onChange={e => onChange(e)}
|
|
17
|
-
styles={{
|
|
18
|
-
control: styles => ({ ...styles, backgroundColor: 'white' }),
|
|
19
|
-
}}
|
|
20
|
-
options={[
|
|
21
|
-
{ value: 'source', label: 'Source' },
|
|
22
|
-
{ value: 'transform', label: 'Transform' },
|
|
23
|
-
{ value: 'dimension', label: 'Dimension' },
|
|
24
|
-
{ value: 'metric', label: 'Metric' },
|
|
25
|
-
{ value: 'cube', label: 'Cube' },
|
|
26
|
-
]}
|
|
27
|
-
/>
|
|
28
|
-
</span>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useContext, useEffect, useState } from 'react';
|
|
2
|
-
import DJClientContext from '../../providers/djclient';
|
|
3
|
-
import Control from './FieldControl';
|
|
4
|
-
|
|
5
|
-
import Select from 'react-select';
|
|
6
|
-
|
|
7
|
-
export default function TagSelect({ onChange }) {
|
|
8
|
-
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
9
|
-
|
|
10
|
-
const [retrieved, setRetrieved] = useState(false);
|
|
11
|
-
const [tags, setTags] = useState([]);
|
|
12
|
-
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
const fetchData = async () => {
|
|
15
|
-
const tags = await djClient.listTags();
|
|
16
|
-
setTags(tags);
|
|
17
|
-
setRetrieved(true);
|
|
18
|
-
};
|
|
19
|
-
fetchData().catch(console.error);
|
|
20
|
-
}, [djClient]);
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<span
|
|
24
|
-
className="menu-link"
|
|
25
|
-
style={{ marginLeft: '30px', width: '350px' }}
|
|
26
|
-
data-testid="select-tag"
|
|
27
|
-
>
|
|
28
|
-
<Select
|
|
29
|
-
name="tags"
|
|
30
|
-
isClearable
|
|
31
|
-
isMulti
|
|
32
|
-
label="Tags"
|
|
33
|
-
components={{ Control }}
|
|
34
|
-
onChange={e => onChange(e)}
|
|
35
|
-
options={tags?.map(tag => {
|
|
36
|
-
return {
|
|
37
|
-
value: tag.name,
|
|
38
|
-
label: tag.display_name,
|
|
39
|
-
};
|
|
40
|
-
})}
|
|
41
|
-
/>
|
|
42
|
-
</span>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { useContext, useEffect, useState } from 'react';
|
|
2
|
-
import DJClientContext from '../../providers/djclient';
|
|
3
|
-
import Control from './FieldControl';
|
|
4
|
-
|
|
5
|
-
import Select from 'react-select';
|
|
6
|
-
|
|
7
|
-
export default function UserSelect({ onChange, currentUser }) {
|
|
8
|
-
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
9
|
-
const [retrieved, setRetrieved] = useState(false);
|
|
10
|
-
const [users, setUsers] = useState([]);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
const fetchData = async () => {
|
|
14
|
-
const users = await djClient.users();
|
|
15
|
-
setUsers(users);
|
|
16
|
-
setRetrieved(true);
|
|
17
|
-
};
|
|
18
|
-
fetchData().catch(console.error);
|
|
19
|
-
}, [djClient]);
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<span
|
|
23
|
-
className="menu-link"
|
|
24
|
-
style={{ marginLeft: '30px', width: '400px' }}
|
|
25
|
-
data-testid="select-user"
|
|
26
|
-
>
|
|
27
|
-
{retrieved ? (
|
|
28
|
-
<Select
|
|
29
|
-
name="edited_by"
|
|
30
|
-
isClearable
|
|
31
|
-
label="Edited By"
|
|
32
|
-
components={{ Control }}
|
|
33
|
-
onChange={e => onChange(e)}
|
|
34
|
-
defaultValue={{
|
|
35
|
-
value: currentUser,
|
|
36
|
-
label: currentUser,
|
|
37
|
-
}}
|
|
38
|
-
options={users?.map(user => {
|
|
39
|
-
return { value: user.username, label: user.username };
|
|
40
|
-
})}
|
|
41
|
-
/>
|
|
42
|
-
) : (
|
|
43
|
-
''
|
|
44
|
-
)}
|
|
45
|
-
</span>
|
|
46
|
-
);
|
|
47
|
-
}
|