datajunction-ui 0.0.1-rc.17 → 0.0.1-rc.19
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 -1
- package/dj-logo.svg +10 -0
- package/package.json +28 -6
- package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -84
- package/src/app/components/djgraph/DJNode.jsx +1 -1
- package/src/app/components/djgraph/LayoutFlow.jsx +1 -1
- package/src/app/constants.js +2 -0
- package/src/app/icons/AlertIcon.jsx +32 -0
- package/src/app/icons/DJLogo.jsx +36 -0
- package/src/app/icons/DeleteIcon.jsx +21 -0
- package/src/app/icons/EditIcon.jsx +18 -0
- package/src/app/index.tsx +70 -36
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +33 -0
- package/src/app/pages/AddEditNodePage/FullNameField.jsx +36 -0
- package/src/app/pages/AddEditNodePage/Loadable.jsx +16 -0
- package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +89 -0
- package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +44 -0
- package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +29 -0
- package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +84 -0
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +353 -0
- package/src/app/pages/AddEditNodePage/index.jsx +349 -0
- package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
- package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
- package/src/app/pages/LoginPage/index.jsx +90 -0
- package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +50 -2
- package/src/app/pages/NamespacePage/index.jsx +38 -9
- package/src/app/pages/NodePage/NodeGraphTab.jsx +1 -2
- package/src/app/pages/NodePage/NodeHistory.jsx +0 -1
- package/src/app/pages/NodePage/index.jsx +43 -26
- package/src/app/pages/Root/index.tsx +20 -3
- package/src/app/pages/SQLBuilderPage/index.jsx +54 -8
- package/src/app/services/DJService.js +180 -32
- package/src/setupTests.ts +1 -1
- package/src/styles/index.css +82 -5
- package/src/styles/login.css +67 -0
- package/src/styles/node-creation.scss +190 -0
- package/src/styles/styles.scss +44 -0
- package/src/styles/styles.scss.d.ts +9 -0
- package/webpack.config.js +11 -1
|
@@ -13,6 +13,8 @@ import NodeMaterializationTab from './NodeMaterializationTab';
|
|
|
13
13
|
import ClientCodePopover from './ClientCodePopover';
|
|
14
14
|
import NodesWithDimension from './NodesWithDimension';
|
|
15
15
|
import NodeColumnLineage from './NodeLineageTab';
|
|
16
|
+
import EditIcon from '../../icons/EditIcon';
|
|
17
|
+
import AlertIcon from '../../icons/AlertIcon';
|
|
16
18
|
|
|
17
19
|
export function NodePage() {
|
|
18
20
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
@@ -109,7 +111,8 @@ export function NodePage() {
|
|
|
109
111
|
let tabToDisplay = null;
|
|
110
112
|
switch (state.selectedTab) {
|
|
111
113
|
case 0:
|
|
112
|
-
tabToDisplay =
|
|
114
|
+
tabToDisplay =
|
|
115
|
+
node && node.message === undefined ? <NodeInfoTab node={node} /> : '';
|
|
113
116
|
break;
|
|
114
117
|
case 1:
|
|
115
118
|
tabToDisplay = <NodeColumnTab node={node} djClient={djClient} />;
|
|
@@ -136,41 +139,55 @@ export function NodePage() {
|
|
|
136
139
|
default:
|
|
137
140
|
tabToDisplay = <NodeInfoTab node={node} />;
|
|
138
141
|
}
|
|
139
|
-
|
|
140
142
|
// @ts-ignore
|
|
141
143
|
return (
|
|
142
144
|
<div className="node__header">
|
|
143
145
|
<NamespaceHeader namespace={name.split('.').slice(0, -1).join('.')} />
|
|
144
146
|
<div className="card">
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
{node !== undefined && node.message === undefined ? (
|
|
148
|
+
<div className="card-header">
|
|
149
|
+
<h3
|
|
150
|
+
className="card-title align-items-start flex-column"
|
|
151
|
+
style={{ display: 'inline-block' }}
|
|
152
|
+
>
|
|
153
|
+
<span className="card-label fw-bold text-gray-800">
|
|
154
|
+
{node?.display_name}{' '}
|
|
155
|
+
<span
|
|
156
|
+
className={'node_type__' + node?.type + ' badge node_type'}
|
|
157
|
+
>
|
|
158
|
+
{node?.type}
|
|
159
|
+
</span>
|
|
154
160
|
</span>
|
|
155
|
-
</
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
<div>
|
|
159
|
-
<a href={'/nodes/' + node?.name} className="link-table">
|
|
160
|
-
{node?.name}
|
|
161
|
-
</a>
|
|
162
|
-
<span
|
|
163
|
-
className="rounded-pill badge bg-secondary-soft"
|
|
161
|
+
</h3>
|
|
162
|
+
<a
|
|
163
|
+
href={`/nodes/${node?.name}/edit`}
|
|
164
164
|
style={{ marginLeft: '0.5rem' }}
|
|
165
165
|
>
|
|
166
|
-
|
|
167
|
-
</
|
|
166
|
+
<EditIcon />
|
|
167
|
+
</a>
|
|
168
|
+
<ClientCodePopover code={node?.createNodeClientCode} />
|
|
169
|
+
<div>
|
|
170
|
+
<a href={'/nodes/' + node?.name} className="link-table">
|
|
171
|
+
{node?.name}
|
|
172
|
+
</a>
|
|
173
|
+
<span
|
|
174
|
+
className="rounded-pill badge bg-secondary-soft"
|
|
175
|
+
style={{ marginLeft: '0.5rem' }}
|
|
176
|
+
>
|
|
177
|
+
{node?.version}
|
|
178
|
+
</span>
|
|
179
|
+
</div>
|
|
180
|
+
<div className="align-items-center row">
|
|
181
|
+
{tabsList(node).map(buildTabs)}
|
|
182
|
+
</div>
|
|
183
|
+
{tabToDisplay}
|
|
168
184
|
</div>
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
) : (
|
|
186
|
+
<div className="message alert" style={{ margin: '20px' }}>
|
|
187
|
+
<AlertIcon />
|
|
188
|
+
Node `{name}` does not exist!
|
|
171
189
|
</div>
|
|
172
|
-
|
|
173
|
-
</div>
|
|
190
|
+
)}
|
|
174
191
|
</div>
|
|
175
192
|
</div>
|
|
176
193
|
);
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
1
2
|
import { Outlet } from 'react-router-dom';
|
|
2
|
-
import
|
|
3
|
+
import DJLogo from '../../icons/DJLogo';
|
|
3
4
|
import { Helmet } from 'react-helmet-async';
|
|
5
|
+
import DJClientContext from '../../providers/djclient';
|
|
4
6
|
|
|
5
7
|
export function Root() {
|
|
8
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
9
|
+
|
|
10
|
+
const handleLogout = async () => {
|
|
11
|
+
await djClient.logout();
|
|
12
|
+
window.location.reload();
|
|
13
|
+
};
|
|
6
14
|
return (
|
|
7
15
|
<>
|
|
8
16
|
<Helmet>
|
|
@@ -16,8 +24,8 @@ export function Root() {
|
|
|
16
24
|
<div className="header">
|
|
17
25
|
<div className="logo">
|
|
18
26
|
<h2>
|
|
19
|
-
<
|
|
20
|
-
|
|
27
|
+
<DJLogo />
|
|
28
|
+
Data<b>Junction</b>
|
|
21
29
|
</h2>
|
|
22
30
|
</div>
|
|
23
31
|
<div className="menu">
|
|
@@ -46,6 +54,15 @@ export function Root() {
|
|
|
46
54
|
</div>
|
|
47
55
|
</div>
|
|
48
56
|
</div>
|
|
57
|
+
{process.env.REACT_DISABLE_AUTH === 'true' ? (
|
|
58
|
+
''
|
|
59
|
+
) : (
|
|
60
|
+
<span className="menu-link">
|
|
61
|
+
<span className="menu-title">
|
|
62
|
+
<button onClick={handleLogout}>Logout</button>
|
|
63
|
+
</span>
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
49
66
|
</div>
|
|
50
67
|
<Outlet />
|
|
51
68
|
</>
|
|
@@ -6,10 +6,14 @@ import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
|
6
6
|
import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
|
|
7
7
|
import Select from 'react-select';
|
|
8
8
|
import QueryInfo from '../../components/QueryInfo';
|
|
9
|
+
import 'react-querybuilder/dist/query-builder.scss';
|
|
10
|
+
import QueryBuilder, { formatQuery } from 'react-querybuilder';
|
|
11
|
+
import 'styles/styles.scss';
|
|
9
12
|
|
|
10
13
|
export function SQLBuilderPage() {
|
|
11
14
|
const DEFAULT_NUM_ROWS = 100;
|
|
12
15
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
16
|
+
const validator = ruleType => !!ruleType.value;
|
|
13
17
|
const [stagedMetrics, setStagedMetrics] = useState([]);
|
|
14
18
|
const [metrics, setMetrics] = useState([]);
|
|
15
19
|
const [commonDimensionsList, setCommonDimensionsList] = useState([]);
|
|
@@ -17,6 +21,8 @@ export function SQLBuilderPage() {
|
|
|
17
21
|
const [stagedDimensions, setStagedDimensions] = useState([]);
|
|
18
22
|
const [selectedMetrics, setSelectedMetrics] = useState([]);
|
|
19
23
|
const [query, setQuery] = useState('');
|
|
24
|
+
const [fields, setFields] = useState([]);
|
|
25
|
+
const [filters, setFilters] = useState({ combinator: 'and', rules: [] });
|
|
20
26
|
const [queryInfo, setQueryInfo] = useState({});
|
|
21
27
|
const [data, setData] = useState(null);
|
|
22
28
|
const [loadingData, setLoadingData] = useState(false);
|
|
@@ -48,7 +54,11 @@ export function SQLBuilderPage() {
|
|
|
48
54
|
setQueryInfo({});
|
|
49
55
|
const fetchData = async () => {
|
|
50
56
|
if (process.env.REACT_USE_SSE) {
|
|
51
|
-
const sse = await djClient.stream(
|
|
57
|
+
const sse = await djClient.stream(
|
|
58
|
+
selectedMetrics,
|
|
59
|
+
selectedDimensions,
|
|
60
|
+
formatQuery(filters, { format: 'sql', parseNumbers: true }),
|
|
61
|
+
);
|
|
52
62
|
sse.onmessage = e => {
|
|
53
63
|
const messageData = JSON.parse(JSON.parse(e.data));
|
|
54
64
|
setQueryInfo(messageData);
|
|
@@ -99,6 +109,24 @@ export function SQLBuilderPage() {
|
|
|
99
109
|
fetchData().catch(console.error);
|
|
100
110
|
}, [djClient, djClient.metrics]);
|
|
101
111
|
|
|
112
|
+
const attributeToFormInput = dimension => {
|
|
113
|
+
const attribute = {
|
|
114
|
+
name: dimension.name,
|
|
115
|
+
label: `${dimension.name} (via ${dimension.path.join(' ▶ ')})`,
|
|
116
|
+
placeholder: `from ${dimension.path}`,
|
|
117
|
+
defaultOperator: '=',
|
|
118
|
+
validator,
|
|
119
|
+
};
|
|
120
|
+
if (dimension.type === 'bool') {
|
|
121
|
+
attribute.valueEditorType = 'checkbox';
|
|
122
|
+
}
|
|
123
|
+
if (dimension.type === 'timestamp') {
|
|
124
|
+
attribute.inputType = 'datetime-local';
|
|
125
|
+
attribute.defaultOperator = 'between';
|
|
126
|
+
}
|
|
127
|
+
return [dimension.name, attribute];
|
|
128
|
+
};
|
|
129
|
+
|
|
102
130
|
// Get common dimensions
|
|
103
131
|
useEffect(() => {
|
|
104
132
|
const fetchData = async () => {
|
|
@@ -113,8 +141,15 @@ export function SQLBuilderPage() {
|
|
|
113
141
|
path: d.path.join(' ▶ '),
|
|
114
142
|
})),
|
|
115
143
|
);
|
|
144
|
+
const uniqueFields = Object.fromEntries(
|
|
145
|
+
new Map(
|
|
146
|
+
commonDimensions.map(dimension => attributeToFormInput(dimension)),
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
setFields(Object.keys(uniqueFields).map(f => uniqueFields[f]));
|
|
116
150
|
} else {
|
|
117
151
|
setCommonDimensionsList([]);
|
|
152
|
+
setFields([]);
|
|
118
153
|
}
|
|
119
154
|
};
|
|
120
155
|
fetchData().catch(console.error);
|
|
@@ -123,15 +158,22 @@ export function SQLBuilderPage() {
|
|
|
123
158
|
// Get SQL
|
|
124
159
|
useEffect(() => {
|
|
125
160
|
const fetchData = async () => {
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
161
|
+
if (
|
|
162
|
+
selectedMetrics.length > 0 &&
|
|
163
|
+
(selectedDimensions.length > 0 || filters.rules.length > 0)
|
|
164
|
+
) {
|
|
165
|
+
const result = await djClient.sqls(
|
|
166
|
+
selectedMetrics,
|
|
167
|
+
selectedDimensions,
|
|
168
|
+
formatQuery(filters, { format: 'sql', parseNumbers: true }),
|
|
169
|
+
);
|
|
170
|
+
setQuery(result.sql);
|
|
129
171
|
} else {
|
|
130
172
|
resetView();
|
|
131
173
|
}
|
|
132
174
|
};
|
|
133
175
|
fetchData().catch(console.error);
|
|
134
|
-
}, [
|
|
176
|
+
}, [djClient, filters, selectedDimensions, selectedMetrics]);
|
|
135
177
|
|
|
136
178
|
// Set number of rows to display
|
|
137
179
|
useEffect(() => {
|
|
@@ -166,9 +208,7 @@ export function SQLBuilderPage() {
|
|
|
166
208
|
name="metrics"
|
|
167
209
|
options={metrics}
|
|
168
210
|
isDisabled={
|
|
169
|
-
selectedMetrics.length && selectedDimensions.length
|
|
170
|
-
? true
|
|
171
|
-
: false
|
|
211
|
+
!!(selectedMetrics.length && selectedDimensions.length)
|
|
172
212
|
}
|
|
173
213
|
noOptionsMessage={() => 'No metrics found.'}
|
|
174
214
|
placeholder={`${metrics.length} Available Metrics`}
|
|
@@ -206,6 +246,12 @@ export function SQLBuilderPage() {
|
|
|
206
246
|
setSelectedDimensions(stagedDimensions);
|
|
207
247
|
}}
|
|
208
248
|
/>
|
|
249
|
+
<h4>Filter By</h4>
|
|
250
|
+
<QueryBuilder
|
|
251
|
+
fields={fields}
|
|
252
|
+
query={filters}
|
|
253
|
+
onQueryChange={q => setFilters(q)}
|
|
254
|
+
/>
|
|
209
255
|
</div>
|
|
210
256
|
<div className="card-header">
|
|
211
257
|
{!viewData && !query ? (
|
|
@@ -5,8 +5,29 @@ const DJ_URL = process.env.REACT_APP_DJ_URL
|
|
|
5
5
|
: 'http://localhost:8000';
|
|
6
6
|
|
|
7
7
|
export const DataJunctionAPI = {
|
|
8
|
+
whoami: async function () {
|
|
9
|
+
const data = await (
|
|
10
|
+
await fetch(`${DJ_URL}/whoami/`, { credentials: 'include' })
|
|
11
|
+
).json();
|
|
12
|
+
return data;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
logout: async function () {
|
|
16
|
+
await await fetch(`${DJ_URL}/basic/logout/`, {
|
|
17
|
+
credentials: 'include',
|
|
18
|
+
method: 'POST',
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
|
|
8
22
|
node: async function (name) {
|
|
9
|
-
const data = await (
|
|
23
|
+
const data = await (
|
|
24
|
+
await fetch(`${DJ_URL}/nodes/${name}/`, {
|
|
25
|
+
credentials: 'include',
|
|
26
|
+
})
|
|
27
|
+
).json();
|
|
28
|
+
if (data.message !== undefined) {
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
10
31
|
data.primary_key = data.columns
|
|
11
32
|
.filter(col =>
|
|
12
33
|
col.attributes.some(attr => attr.attribute_type.name === 'primary_key'),
|
|
@@ -15,60 +36,150 @@ export const DataJunctionAPI = {
|
|
|
15
36
|
return data;
|
|
16
37
|
},
|
|
17
38
|
|
|
39
|
+
nodes: async function (prefix) {
|
|
40
|
+
return await (
|
|
41
|
+
await fetch(`${DJ_URL}/nodes/?prefix=${prefix}`, {
|
|
42
|
+
credentials: 'include',
|
|
43
|
+
})
|
|
44
|
+
).json();
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
createNode: async function (
|
|
48
|
+
nodeType,
|
|
49
|
+
name,
|
|
50
|
+
display_name,
|
|
51
|
+
description,
|
|
52
|
+
query,
|
|
53
|
+
mode,
|
|
54
|
+
namespace,
|
|
55
|
+
primary_key,
|
|
56
|
+
) {
|
|
57
|
+
const response = await fetch(`${DJ_URL}/nodes/${nodeType}`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
name: name,
|
|
64
|
+
display_name: display_name,
|
|
65
|
+
description: description,
|
|
66
|
+
query: query,
|
|
67
|
+
mode: mode,
|
|
68
|
+
namespace: namespace,
|
|
69
|
+
primary_key: primary_key,
|
|
70
|
+
}),
|
|
71
|
+
credentials: 'include',
|
|
72
|
+
});
|
|
73
|
+
return { status: response.status, json: await response.json() };
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
patchNode: async function (
|
|
77
|
+
name,
|
|
78
|
+
display_name,
|
|
79
|
+
description,
|
|
80
|
+
query,
|
|
81
|
+
mode,
|
|
82
|
+
primary_key,
|
|
83
|
+
) {
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(`${DJ_URL}/nodes/${name}`, {
|
|
86
|
+
method: 'PATCH',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify({
|
|
91
|
+
display_name: display_name,
|
|
92
|
+
description: description,
|
|
93
|
+
query: query,
|
|
94
|
+
mode: mode,
|
|
95
|
+
primary_key: primary_key,
|
|
96
|
+
}),
|
|
97
|
+
credentials: 'include',
|
|
98
|
+
});
|
|
99
|
+
return { status: response.status, json: await response.json() };
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return { status: 500, json: { message: 'Update failed' } };
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
18
105
|
upstreams: async function (name) {
|
|
19
106
|
const data = await (
|
|
20
|
-
await fetch(DJ_URL
|
|
107
|
+
await fetch(`${DJ_URL}/nodes/${name}/upstream/`, {
|
|
108
|
+
credentials: 'include',
|
|
109
|
+
})
|
|
21
110
|
).json();
|
|
22
111
|
return data;
|
|
23
112
|
},
|
|
24
113
|
|
|
25
114
|
downstreams: async function (name) {
|
|
26
115
|
const data = await (
|
|
27
|
-
await fetch(DJ_URL
|
|
116
|
+
await fetch(`${DJ_URL}/nodes/` + name + '/downstream/', {
|
|
117
|
+
credentials: 'include',
|
|
118
|
+
})
|
|
28
119
|
).json();
|
|
29
120
|
return data;
|
|
30
121
|
},
|
|
31
122
|
|
|
32
123
|
node_dag: async function (name) {
|
|
33
124
|
const data = await (
|
|
34
|
-
await fetch(DJ_URL
|
|
125
|
+
await fetch(`${DJ_URL}/nodes/` + name + '/dag/', {
|
|
126
|
+
credentials: 'include',
|
|
127
|
+
})
|
|
35
128
|
).json();
|
|
36
129
|
return data;
|
|
37
130
|
},
|
|
38
131
|
|
|
39
132
|
node_lineage: async function (name) {
|
|
40
133
|
const data = await (
|
|
41
|
-
await fetch(DJ_URL
|
|
134
|
+
await fetch(`${DJ_URL}/nodes/` + name + '/lineage/', {
|
|
135
|
+
credentials: 'include',
|
|
136
|
+
})
|
|
42
137
|
).json();
|
|
43
138
|
return data;
|
|
44
139
|
},
|
|
45
140
|
|
|
46
141
|
metric: async function (name) {
|
|
47
|
-
const data = await (
|
|
142
|
+
const data = await (
|
|
143
|
+
await fetch(`${DJ_URL}/metrics/` + name + '/', {
|
|
144
|
+
credentials: 'include',
|
|
145
|
+
})
|
|
146
|
+
).json();
|
|
48
147
|
return data;
|
|
49
148
|
},
|
|
50
149
|
|
|
51
150
|
clientCode: async function (name) {
|
|
52
151
|
const data = await (
|
|
53
|
-
await fetch(DJ_URL
|
|
152
|
+
await fetch(`${DJ_URL}/datajunction-clients/python/new_node/` + name, {
|
|
153
|
+
credentials: 'include',
|
|
154
|
+
})
|
|
54
155
|
).json();
|
|
55
156
|
return data;
|
|
56
157
|
},
|
|
57
158
|
|
|
58
159
|
cube: async function (name) {
|
|
59
|
-
const data = await (
|
|
160
|
+
const data = await (
|
|
161
|
+
await fetch(`${DJ_URL}/cubes/` + name + '/', {
|
|
162
|
+
credentials: 'include',
|
|
163
|
+
})
|
|
164
|
+
).json();
|
|
60
165
|
return data;
|
|
61
166
|
},
|
|
62
167
|
|
|
63
168
|
metrics: async function (name) {
|
|
64
|
-
const data = await (
|
|
169
|
+
const data = await (
|
|
170
|
+
await fetch(`${DJ_URL}/metrics/`, {
|
|
171
|
+
credentials: 'include',
|
|
172
|
+
})
|
|
173
|
+
).json();
|
|
65
174
|
return data;
|
|
66
175
|
},
|
|
67
176
|
|
|
68
177
|
commonDimensions: async function (metrics) {
|
|
69
178
|
const metricsQuery = '?' + metrics.map(m => `metric=${m}`).join('&');
|
|
70
179
|
const data = await (
|
|
71
|
-
await fetch(DJ_URL
|
|
180
|
+
await fetch(`${DJ_URL}/metrics/common/dimensions/` + metricsQuery, {
|
|
181
|
+
credentials: 'include',
|
|
182
|
+
})
|
|
72
183
|
).json();
|
|
73
184
|
return data;
|
|
74
185
|
},
|
|
@@ -76,10 +187,12 @@ export const DataJunctionAPI = {
|
|
|
76
187
|
history: async function (type, name, offset, limit) {
|
|
77
188
|
const data = await (
|
|
78
189
|
await fetch(
|
|
79
|
-
DJ_URL +
|
|
80
|
-
'/history?node=' +
|
|
190
|
+
`${DJ_URL}/history?node=` +
|
|
81
191
|
name +
|
|
82
192
|
`&offset=${offset ? offset : 0}&limit=${limit ? limit : 100}`,
|
|
193
|
+
{
|
|
194
|
+
credentials: 'include',
|
|
195
|
+
},
|
|
83
196
|
)
|
|
84
197
|
).json();
|
|
85
198
|
return data;
|
|
@@ -87,27 +200,38 @@ export const DataJunctionAPI = {
|
|
|
87
200
|
|
|
88
201
|
revisions: async function (name) {
|
|
89
202
|
const data = await (
|
|
90
|
-
await fetch(DJ_URL
|
|
203
|
+
await fetch(`${DJ_URL}/nodes/` + name + '/revisions/', {
|
|
204
|
+
credentials: 'include',
|
|
205
|
+
})
|
|
91
206
|
).json();
|
|
92
207
|
return data;
|
|
93
208
|
},
|
|
94
209
|
|
|
95
210
|
namespace: async function (nmspce) {
|
|
96
211
|
const data = await (
|
|
97
|
-
await fetch(DJ_URL
|
|
212
|
+
await fetch(`${DJ_URL}/namespaces/` + nmspce + '/', {
|
|
213
|
+
credentials: 'include',
|
|
214
|
+
})
|
|
98
215
|
).json();
|
|
99
216
|
return data;
|
|
100
217
|
},
|
|
101
218
|
|
|
102
219
|
namespaces: async function () {
|
|
103
|
-
const data = await (
|
|
220
|
+
const data = await (
|
|
221
|
+
await fetch(`${DJ_URL}/namespaces/`, {
|
|
222
|
+
credentials: 'include',
|
|
223
|
+
})
|
|
224
|
+
).json();
|
|
104
225
|
return data;
|
|
105
226
|
},
|
|
106
227
|
|
|
107
228
|
sql: async function (metric_name, selection) {
|
|
108
229
|
const data = await (
|
|
109
230
|
await fetch(
|
|
110
|
-
DJ_URL
|
|
231
|
+
`${DJ_URL}/sql/` + metric_name + '?' + new URLSearchParams(selection),
|
|
232
|
+
{
|
|
233
|
+
credentials: 'include',
|
|
234
|
+
},
|
|
111
235
|
)
|
|
112
236
|
).json();
|
|
113
237
|
return data;
|
|
@@ -115,22 +239,28 @@ export const DataJunctionAPI = {
|
|
|
115
239
|
|
|
116
240
|
nodesWithDimension: async function (name) {
|
|
117
241
|
const data = await (
|
|
118
|
-
await fetch(DJ_URL
|
|
242
|
+
await fetch(`${DJ_URL}/dimensions/` + name + '/nodes/', {
|
|
243
|
+
credentials: 'include',
|
|
244
|
+
})
|
|
119
245
|
).json();
|
|
120
246
|
return data;
|
|
121
247
|
},
|
|
122
248
|
|
|
123
249
|
materializations: async function (node) {
|
|
124
250
|
const data = await (
|
|
125
|
-
await fetch(DJ_URL
|
|
251
|
+
await fetch(`${DJ_URL}/nodes/${node}/materializations/`, {
|
|
252
|
+
credentials: 'include',
|
|
253
|
+
})
|
|
126
254
|
).json();
|
|
127
255
|
|
|
128
256
|
return await Promise.all(
|
|
129
257
|
data.map(async materialization => {
|
|
130
258
|
materialization.clientCode = await (
|
|
131
259
|
await fetch(
|
|
132
|
-
DJ_URL
|
|
133
|
-
|
|
260
|
+
`${DJ_URL}/datajunction-clients/python/add_materialization/${node}/${materialization.name}`,
|
|
261
|
+
{
|
|
262
|
+
credentials: 'include',
|
|
263
|
+
},
|
|
134
264
|
)
|
|
135
265
|
).json();
|
|
136
266
|
return materialization;
|
|
@@ -144,8 +274,10 @@ export const DataJunctionAPI = {
|
|
|
144
274
|
if (col.dimension) {
|
|
145
275
|
col.clientCode = await (
|
|
146
276
|
await fetch(
|
|
147
|
-
DJ_URL
|
|
148
|
-
|
|
277
|
+
`${DJ_URL}/datajunction-clients/python/link_dimension/${node.name}/${col.name}/${col.dimension?.name}`,
|
|
278
|
+
{
|
|
279
|
+
credentials: 'include',
|
|
280
|
+
},
|
|
149
281
|
)
|
|
150
282
|
).json();
|
|
151
283
|
}
|
|
@@ -154,12 +286,16 @@ export const DataJunctionAPI = {
|
|
|
154
286
|
);
|
|
155
287
|
},
|
|
156
288
|
|
|
157
|
-
sqls: async function (metricSelection, dimensionSelection) {
|
|
289
|
+
sqls: async function (metricSelection, dimensionSelection, filters) {
|
|
158
290
|
const params = new URLSearchParams();
|
|
159
291
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
160
292
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
161
|
-
|
|
162
|
-
return
|
|
293
|
+
params.append('filters', filters);
|
|
294
|
+
return await (
|
|
295
|
+
await fetch(`${DJ_URL}/sql/?${params}`, {
|
|
296
|
+
credentials: 'include',
|
|
297
|
+
})
|
|
298
|
+
).json();
|
|
163
299
|
},
|
|
164
300
|
|
|
165
301
|
data: async function (metricSelection, dimensionSelection) {
|
|
@@ -167,32 +303,44 @@ export const DataJunctionAPI = {
|
|
|
167
303
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
168
304
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
169
305
|
const data = await (
|
|
170
|
-
await fetch(DJ_URL
|
|
306
|
+
await fetch(`${DJ_URL}/data/?` + params + '&limit=10000', {
|
|
307
|
+
credentials: 'include',
|
|
308
|
+
})
|
|
171
309
|
).json();
|
|
172
310
|
return data;
|
|
173
311
|
},
|
|
174
312
|
|
|
175
|
-
stream: async function (metricSelection, dimensionSelection) {
|
|
313
|
+
stream: async function (metricSelection, dimensionSelection, filters) {
|
|
176
314
|
const params = new URLSearchParams();
|
|
177
315
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
178
316
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
317
|
+
params.append('filters', filters);
|
|
179
318
|
return new EventSource(
|
|
180
|
-
DJ_URL
|
|
319
|
+
`${DJ_URL}/stream/?${params}&limit=10000&async_=true`,
|
|
320
|
+
{
|
|
321
|
+
withCredentials: true,
|
|
322
|
+
},
|
|
181
323
|
);
|
|
182
324
|
},
|
|
183
325
|
|
|
184
326
|
lineage: async function (node) {},
|
|
185
327
|
|
|
186
328
|
compiledSql: async function (node) {
|
|
187
|
-
const data = await (
|
|
329
|
+
const data = await (
|
|
330
|
+
await fetch(`${DJ_URL}/sql/${node}/`, {
|
|
331
|
+
credentials: 'include',
|
|
332
|
+
})
|
|
333
|
+
).json();
|
|
188
334
|
return data;
|
|
189
335
|
},
|
|
190
336
|
|
|
191
337
|
dag: async function (namespace = 'default') {
|
|
192
338
|
const edges = [];
|
|
193
|
-
const data = await (
|
|
194
|
-
|
|
195
|
-
|
|
339
|
+
const data = await (
|
|
340
|
+
await fetch(`${DJ_URL}/nodes/`, {
|
|
341
|
+
credentials: 'include',
|
|
342
|
+
})
|
|
343
|
+
).json();
|
|
196
344
|
|
|
197
345
|
data.forEach(obj => {
|
|
198
346
|
obj.parents.forEach(parent => {
|
package/src/setupTests.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// react-testing-library renders your components to document.body,
|
|
2
2
|
// this adds jest-dom's custom assertions
|
|
3
|
-
import '@testing-library/jest-dom
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
4
|
|
|
5
5
|
import 'react-app-polyfill/ie11';
|
|
6
6
|
import 'react-app-polyfill/stable';
|