datajunction-ui 0.0.1-a87 → 0.0.1-a87.dev0
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 +1 -1
- package/public/index.html +1 -1
- package/src/app/components/forms/NodeNameField.jsx +1 -1
- package/src/app/icons/WrenchIcon.jsx +36 -0
- package/src/app/pages/AddEditNodePage/ColumnMetadata.jsx +61 -0
- package/src/app/pages/AddEditNodePage/ColumnsMetadataInput.jsx +72 -0
- package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +4 -4
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +1 -0
- package/src/app/pages/AddEditNodePage/NamespaceField.jsx +1 -1
- package/src/app/pages/AddEditNodePage/index.jsx +17 -10
- package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +1 -1
- package/src/app/pages/CubeBuilderPage/index.jsx +2 -2
- package/src/app/pages/NodePage/AddMaterializationPopover.jsx +2 -2
- package/src/app/pages/NodePage/LinkComplexDimensionPopover.jsx +141 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +8 -18
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +18 -12
- package/src/app/pages/NodePage/NodeGraphTab.jsx +4 -2
- package/src/app/pages/NodePage/NodeInfoTab.jsx +4 -1
- package/src/app/pages/NodePage/PartitionColumnPopover.jsx +3 -5
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" href="public/favicon.ico" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
8
|
<meta name="theme-color" content="#000000" />
|
|
9
|
-
<link rel="manifest" href="
|
|
9
|
+
<link rel="manifest" href="manifest.json" />
|
|
10
10
|
<link rel="manifest" href="src/styles/index.css" />
|
|
11
11
|
<meta property="og:title" content="DataJunction UI" />
|
|
12
12
|
<meta property="og:description" content="" />
|
|
@@ -33,7 +33,7 @@ export default function NodeNameField() {
|
|
|
33
33
|
<>
|
|
34
34
|
<div className="NamespaceInput">
|
|
35
35
|
<ErrorMessage name="namespace" component="span" />
|
|
36
|
-
<label htmlFor="
|
|
36
|
+
<label htmlFor="namespace">Namespace *</label>
|
|
37
37
|
<FormikSelect
|
|
38
38
|
selectOptions={namespaces}
|
|
39
39
|
formikFieldName="namespace"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const WrenchIcon = props => (
|
|
2
|
+
<svg
|
|
3
|
+
width="16"
|
|
4
|
+
height="17"
|
|
5
|
+
viewBox="0 0 16 17"
|
|
6
|
+
fill="none"
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
>
|
|
9
|
+
<g clip-path="url(#clip0_24_77)">
|
|
10
|
+
<rect
|
|
11
|
+
width="16"
|
|
12
|
+
height="16"
|
|
13
|
+
transform="translate(0 0.649803)"
|
|
14
|
+
fill="white"
|
|
15
|
+
/>
|
|
16
|
+
<path
|
|
17
|
+
fill-rule="evenodd"
|
|
18
|
+
clip-rule="evenodd"
|
|
19
|
+
d="M6.41665 5.44985C6.41665 2.8265 8.5433 0.699852 11.1667 0.699852C11.4613 0.699852 11.7504 0.726773 12.0313 0.778484C12.5762 0.878798 12.9364 1.28101 13.0406 1.74377C13.1406 2.18774 13.0066 2.67055 12.6619 3.01531L11.2273 4.44985L12.1667 5.38919L13.6012 3.95465C13.946 3.60989 14.4288 3.47589 14.8728 3.57591C15.3355 3.68017 15.7377 4.04033 15.838 4.58525C15.8898 4.86615 15.9167 5.15519 15.9167 5.44985C15.9167 8.0732 13.79 10.1999 11.1667 10.1999C10.658 10.1999 10.167 10.1196 9.70625 9.97091L3.50172 16.1754C2.94847 16.7287 2.05149 16.7287 1.49825 16.1754L0.441055 15.1183C-0.112189 14.565 -0.112186 13.668 0.441056 13.1148L6.64559 6.91025C6.49685 6.44951 6.41665 5.9585 6.41665 5.44985ZM11.1667 2.19985C9.37172 2.19985 7.91665 3.65492 7.91665 5.44985C7.91665 5.92812 8.01948 6.38034 8.20352 6.7873L8.41725 7.25991L8.05048 7.62667L1.56064 14.1165L2.49998 15.0559L8.98982 8.56601L9.35659 8.19925L9.8292 8.41298C10.2362 8.59702 10.6884 8.69985 11.1667 8.69985C12.9616 8.69985 14.4167 7.24477 14.4167 5.44985C14.4167 5.38796 14.415 5.32654 14.4116 5.26562L12.697 6.98018L12.1667 7.51051L11.6363 6.98018L9.63632 4.98018L9.10599 4.44985L9.63632 3.91952L11.3509 2.20496C11.29 2.20157 11.2286 2.19985 11.1667 2.19985Z"
|
|
20
|
+
fill="black"
|
|
21
|
+
fill-opacity="0.9"
|
|
22
|
+
/>
|
|
23
|
+
</g>
|
|
24
|
+
<defs>
|
|
25
|
+
<clipPath id="clip0_24_77">
|
|
26
|
+
<rect
|
|
27
|
+
width="16"
|
|
28
|
+
height="16"
|
|
29
|
+
fill="white"
|
|
30
|
+
transform="translate(0 0.649803)"
|
|
31
|
+
/>
|
|
32
|
+
</clipPath>
|
|
33
|
+
</defs>
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
export default WrenchIcon;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component for arbitrary column metadata
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage, useFormikContext } from 'formik';
|
|
5
|
+
import { useContext, useMemo, useState } from 'react';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import { FormikSelect } from './FormikSelect';
|
|
8
|
+
|
|
9
|
+
export const ColumnMetadata = ({ name, label, defaultValue }) => {
|
|
10
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
11
|
+
|
|
12
|
+
// Used to pull out current form values for node validation
|
|
13
|
+
const { values } = useFormikContext();
|
|
14
|
+
|
|
15
|
+
// The available columns, determined from validating the node query
|
|
16
|
+
const [availableColumns, setAvailableColumns] = useState([]);
|
|
17
|
+
const selectableOptions = useMemo(() => {
|
|
18
|
+
if (availableColumns && availableColumns.length > 0) {
|
|
19
|
+
return availableColumns;
|
|
20
|
+
}
|
|
21
|
+
}, [availableColumns]);
|
|
22
|
+
|
|
23
|
+
// When focus is on the input field, refresh the list of available columns for selection
|
|
24
|
+
const refreshColumns = event => {
|
|
25
|
+
async function fetchData() {
|
|
26
|
+
// eslint-disable-next-line no-unused-vars
|
|
27
|
+
const { status, json } = await djClient.validateNode(
|
|
28
|
+
values.type,
|
|
29
|
+
values.name,
|
|
30
|
+
values.display_name,
|
|
31
|
+
values.description,
|
|
32
|
+
values.query,
|
|
33
|
+
);
|
|
34
|
+
setAvailableColumns(
|
|
35
|
+
json.columns.map(col => {
|
|
36
|
+
return { value: col.name, label: col.name };
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
fetchData();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="NodeCreationInput" style={{ width: '25%' }}>
|
|
45
|
+
<ErrorMessage name={name} component="span" />
|
|
46
|
+
<label htmlFor="react-select-3-input">{label}</label>
|
|
47
|
+
<span data-testid={`select-${name}`}>
|
|
48
|
+
<FormikSelect
|
|
49
|
+
className="SelectInput"
|
|
50
|
+
style={{ width: '100px' }}
|
|
51
|
+
defaultValue={defaultValue}
|
|
52
|
+
selectOptions={selectableOptions}
|
|
53
|
+
formikFieldName={name}
|
|
54
|
+
// placeholder={`Choose ${label}`}
|
|
55
|
+
onFocus={event => refreshColumns(event)}
|
|
56
|
+
isMulti={false}
|
|
57
|
+
/>
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column metadata configuration component
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage, useFormikContext } from 'formik';
|
|
5
|
+
import { useContext, useMemo, useState, useEffect } from 'react';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import { FormikSelect } from './FormikSelect';
|
|
8
|
+
import WrenchIcon from 'app/icons/WrenchIcon';
|
|
9
|
+
|
|
10
|
+
export const ColumnsMetadataInput = ({ columns }) => {
|
|
11
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
12
|
+
|
|
13
|
+
// Used to pull out current form values for node validation
|
|
14
|
+
const { values } = useFormikContext();
|
|
15
|
+
|
|
16
|
+
// The available columns, determined from validating the node query
|
|
17
|
+
const [availableColumns, setAvailableColumns] = useState([]);
|
|
18
|
+
const selectableOptions = useMemo(() => {
|
|
19
|
+
if (availableColumns && availableColumns.length > 0) {
|
|
20
|
+
return availableColumns;
|
|
21
|
+
}
|
|
22
|
+
}, [availableColumns]);
|
|
23
|
+
|
|
24
|
+
// When focus is on the primary key field, refresh the list of available
|
|
25
|
+
// primary key columns for selection
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const fetchData = async () => {
|
|
28
|
+
// eslint-disable-next-line no-unused-vars
|
|
29
|
+
const { status, json } = await djClient.validateNode(
|
|
30
|
+
values.type,
|
|
31
|
+
values.name,
|
|
32
|
+
values.display_name,
|
|
33
|
+
values.description,
|
|
34
|
+
values.query,
|
|
35
|
+
);
|
|
36
|
+
setAvailableColumns(
|
|
37
|
+
json.columns.map(col => {
|
|
38
|
+
return { value: col.name, label: col.name };
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
fetchData().catch(console.error);
|
|
43
|
+
}, [djClient, name]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
className="ColumnsMetadataInput NodeCreationInput"
|
|
48
|
+
style={{ border: 'dashed 1px #ccc', padding: '20px' }}
|
|
49
|
+
>
|
|
50
|
+
<ErrorMessage name="mode" component="span" />
|
|
51
|
+
<label htmlFor="Mode">Columns Metadata</label>
|
|
52
|
+
<table>
|
|
53
|
+
<thead>
|
|
54
|
+
<tr>
|
|
55
|
+
<th>Column</th>
|
|
56
|
+
<th>Metadata</th>
|
|
57
|
+
</tr>
|
|
58
|
+
</thead>
|
|
59
|
+
<tbody>
|
|
60
|
+
{availableColumns.map(col => (
|
|
61
|
+
<tr key={col.value}>
|
|
62
|
+
<td>{col.value}</td>
|
|
63
|
+
<td>
|
|
64
|
+
<WrenchIcon />
|
|
65
|
+
</td>
|
|
66
|
+
</tr>
|
|
67
|
+
))}
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -50,12 +50,12 @@ export const ColumnsSelect = ({
|
|
|
50
50
|
fetchColumns();
|
|
51
51
|
}, [values.type, values.name, values.query]);
|
|
52
52
|
|
|
53
|
-
return
|
|
54
|
-
''
|
|
55
|
-
) : (
|
|
53
|
+
return (
|
|
56
54
|
<div className="CubeCreationInput">
|
|
57
55
|
<ErrorMessage name={fieldName} component="span" />
|
|
58
|
-
<label htmlFor={fieldName} style={labelStyle}>
|
|
56
|
+
<label htmlFor={fieldName} style={labelStyle}>
|
|
57
|
+
{label}
|
|
58
|
+
</label>
|
|
59
59
|
<span data-testid={`select-${fieldName}`}>
|
|
60
60
|
<FormikSelect
|
|
61
61
|
className={isMulti ? 'MultiSelectInput' : 'SelectInput'}
|
|
@@ -25,7 +25,7 @@ export const NamespaceField = ({ initialNamespace }) => {
|
|
|
25
25
|
return (
|
|
26
26
|
<div className="NamespaceInput">
|
|
27
27
|
<ErrorMessage name="namespace" component="span" />
|
|
28
|
-
<label htmlFor="
|
|
28
|
+
<label htmlFor="namespace">Namespace *</label>
|
|
29
29
|
<FormikSelect
|
|
30
30
|
selectOptions={namespaces}
|
|
31
31
|
formikFieldName="namespace"
|
|
@@ -93,10 +93,10 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
93
93
|
const staticFieldsInEdit = node => (
|
|
94
94
|
<>
|
|
95
95
|
<div className="NodeNameInput NodeCreationInput">
|
|
96
|
-
<label
|
|
96
|
+
<label>Name</label> {name}
|
|
97
97
|
</div>
|
|
98
98
|
<div className="NodeNameInput NodeCreationInput">
|
|
99
|
-
<label
|
|
99
|
+
<label>Type</label> {node.type}
|
|
100
100
|
</div>
|
|
101
101
|
</>
|
|
102
102
|
);
|
|
@@ -295,13 +295,15 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
295
295
|
isMulti={true}
|
|
296
296
|
/>,
|
|
297
297
|
);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
298
|
+
if (data.required_dimensions) {
|
|
299
|
+
setSelectRequiredDims(
|
|
300
|
+
<RequiredDimensionsSelect
|
|
301
|
+
defaultValue={data.required_dimensions.map(dim => {
|
|
302
|
+
return { value: dim, label: dim };
|
|
303
|
+
})}
|
|
304
|
+
/>,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
305
307
|
setSelectUpstreamNode(
|
|
306
308
|
<UpstreamNodeField
|
|
307
309
|
defaultValue={{
|
|
@@ -418,7 +420,12 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
418
420
|
action === Action.Edit ? (
|
|
419
421
|
selectPrimaryKey
|
|
420
422
|
) : (
|
|
421
|
-
<ColumnsSelect
|
|
423
|
+
<ColumnsSelect
|
|
424
|
+
defaultValue={[]}
|
|
425
|
+
fieldName="primary_key"
|
|
426
|
+
label="Primary Key"
|
|
427
|
+
isMulti={true}
|
|
428
|
+
/>
|
|
422
429
|
)
|
|
423
430
|
) : action === Action.Edit ? (
|
|
424
431
|
selectRequiredDims
|
|
@@ -23,7 +23,7 @@ export const MetricsSelect = ({ cube }) => {
|
|
|
23
23
|
// Get metrics
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
const fetchData = async () => {
|
|
26
|
-
if (cube
|
|
26
|
+
if (cube) {
|
|
27
27
|
const cubeMetrics = cube?.cube_elements
|
|
28
28
|
.filter(element => element.type === 'metric')
|
|
29
29
|
.map(metric => {
|
|
@@ -202,7 +202,7 @@ export function CubeBuilderPage() {
|
|
|
202
202
|
/>
|
|
203
203
|
</div>
|
|
204
204
|
<div className="CubeCreationInput">
|
|
205
|
-
<label
|
|
205
|
+
<label>Metrics *</label>
|
|
206
206
|
<p>Select metrics to include in the cube.</p>
|
|
207
207
|
<span
|
|
208
208
|
data-testid="select-metrics"
|
|
@@ -218,7 +218,7 @@ export function CubeBuilderPage() {
|
|
|
218
218
|
<br />
|
|
219
219
|
<br />
|
|
220
220
|
<div className="CubeCreationInput">
|
|
221
|
-
<label
|
|
221
|
+
<label>Dimensions *</label>
|
|
222
222
|
<p>
|
|
223
223
|
Select dimensions to include in the cube. As metrics are
|
|
224
224
|
selected above, the list of available dimensions will be
|
|
@@ -151,7 +151,7 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
|
|
|
151
151
|
/>
|
|
152
152
|
<span data-testid="edit-partition">
|
|
153
153
|
<label htmlFor="strategy">Strategy</label>
|
|
154
|
-
<Field as="select" name="strategy">
|
|
154
|
+
<Field as="select" name="strategy" id="strategy">
|
|
155
155
|
<>
|
|
156
156
|
<option key={'full'} value={'full'}>
|
|
157
157
|
Full
|
|
@@ -179,7 +179,7 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
|
|
|
179
179
|
<br />
|
|
180
180
|
<div className="DescriptionInput">
|
|
181
181
|
<ErrorMessage name="description" component="span" />
|
|
182
|
-
<label htmlFor="
|
|
182
|
+
<label htmlFor="lookback_window">Lookback Window</label>
|
|
183
183
|
<Field
|
|
184
184
|
type="text"
|
|
185
185
|
name="lookback_window"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import DJClientContext from '../../providers/djclient';
|
|
4
|
+
import { Field, Form, Formik } from 'formik';
|
|
5
|
+
import { FormikSelect } from '../AddEditNodePage/FormikSelect';
|
|
6
|
+
import EditIcon from '../../icons/EditIcon';
|
|
7
|
+
import { displayMessageAfterSubmit } from '../../../utils/form';
|
|
8
|
+
import LoadingIcon from '../../icons/LoadingIcon';
|
|
9
|
+
import { MetricQueryField } from '../AddEditNodePage/MetricQueryField';
|
|
10
|
+
|
|
11
|
+
export default function LinkComplexDimensionPopover({ link, onSubmit }) {
|
|
12
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
13
|
+
const [popoverAnchor, setPopoverAnchor] = useState(false);
|
|
14
|
+
const ref = useRef(null);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleClickOutside = event => {
|
|
18
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
19
|
+
setPopoverAnchor(false);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
document.addEventListener('click', handleClickOutside, true);
|
|
23
|
+
return () => {
|
|
24
|
+
document.removeEventListener('click', handleClickOutside, true);
|
|
25
|
+
};
|
|
26
|
+
}, [setPopoverAnchor]);
|
|
27
|
+
|
|
28
|
+
const handleSubmit = async (
|
|
29
|
+
{ node, column, dimension },
|
|
30
|
+
{ setSubmitting, setStatus },
|
|
31
|
+
) => {
|
|
32
|
+
if (referencedDimensionNode && dimension === 'Remove') {
|
|
33
|
+
await unlinkDimension(
|
|
34
|
+
node,
|
|
35
|
+
column,
|
|
36
|
+
referencedDimensionNode,
|
|
37
|
+
setStatus,
|
|
38
|
+
).then(_ => setSubmitting(false));
|
|
39
|
+
} else {
|
|
40
|
+
await linkDimension(node, column, dimension, setStatus).then(_ =>
|
|
41
|
+
setSubmitting(false),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
onSubmit();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const linkDimension = async (node, column, dimension, setStatus) => {
|
|
48
|
+
const response = await djClient.linkDimension(node, column, dimension);
|
|
49
|
+
if (response.status === 200 || response.status === 201) {
|
|
50
|
+
setStatus({ success: 'Saved!' });
|
|
51
|
+
} else {
|
|
52
|
+
setStatus({
|
|
53
|
+
failure: `${response.json.message}`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const unlinkDimension = async (node, column, currentDimension, setStatus) => {
|
|
59
|
+
const response = await djClient.unlinkDimension(
|
|
60
|
+
node,
|
|
61
|
+
column,
|
|
62
|
+
currentDimension,
|
|
63
|
+
);
|
|
64
|
+
if (response.status === 200 || response.status === 201) {
|
|
65
|
+
setStatus({ success: 'Removed dimension link!' });
|
|
66
|
+
} else {
|
|
67
|
+
setStatus({
|
|
68
|
+
failure: `${response.json.message}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<button
|
|
76
|
+
className="edit_button"
|
|
77
|
+
aria-label="LinkDimension"
|
|
78
|
+
tabIndex="0"
|
|
79
|
+
onClick={() => {
|
|
80
|
+
setPopoverAnchor(!popoverAnchor);
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<EditIcon />
|
|
84
|
+
</button>
|
|
85
|
+
<div
|
|
86
|
+
className="popover"
|
|
87
|
+
role="dialog"
|
|
88
|
+
aria-label="client-code"
|
|
89
|
+
style={{ display: popoverAnchor === false ? 'none' : 'block' }}
|
|
90
|
+
ref={ref}
|
|
91
|
+
>
|
|
92
|
+
<Formik
|
|
93
|
+
initialValues={
|
|
94
|
+
{
|
|
95
|
+
// column: column.name,
|
|
96
|
+
// node: node.name,
|
|
97
|
+
// dimension: '',
|
|
98
|
+
// currentDimension: referencedDimensionNode,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
onSubmit={handleSubmit}
|
|
102
|
+
>
|
|
103
|
+
{function Render({ isSubmitting, status, setFieldValue }) {
|
|
104
|
+
return (
|
|
105
|
+
<Form>
|
|
106
|
+
{displayMessageAfterSubmit(status)}
|
|
107
|
+
<span data-testid="link-dimension">
|
|
108
|
+
<label>Join Type</label>
|
|
109
|
+
<FormikSelect
|
|
110
|
+
selectOptions={[{ value: 'left', label: 'LEFT' }]}
|
|
111
|
+
formikFieldName="join_type"
|
|
112
|
+
placeholder="Select join type"
|
|
113
|
+
className="join_type"
|
|
114
|
+
defaultValue={link.join_type || ''}
|
|
115
|
+
isMulti={false}
|
|
116
|
+
/>
|
|
117
|
+
{console.log('link, ', link)}
|
|
118
|
+
|
|
119
|
+
<label>Join On</label>
|
|
120
|
+
<MetricQueryField
|
|
121
|
+
djClient={djClient}
|
|
122
|
+
value={link.join_sql ? link.join_sql : ''}
|
|
123
|
+
/>
|
|
124
|
+
</span>
|
|
125
|
+
<button
|
|
126
|
+
className="add_node"
|
|
127
|
+
type="submit"
|
|
128
|
+
aria-label="SaveComplexDimension"
|
|
129
|
+
aria-hidden="false"
|
|
130
|
+
disabled={isSubmitting}
|
|
131
|
+
>
|
|
132
|
+
{isSubmitting ? <LoadingIcon /> : 'Save'}
|
|
133
|
+
</button>
|
|
134
|
+
</Form>
|
|
135
|
+
);
|
|
136
|
+
}}
|
|
137
|
+
</Formik>
|
|
138
|
+
</div>
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -15,7 +15,9 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
15
15
|
|
|
16
16
|
useEffect(() => {
|
|
17
17
|
const fetchData = async () => {
|
|
18
|
-
|
|
18
|
+
if (node) {
|
|
19
|
+
setColumns(await djClient.columns(node));
|
|
20
|
+
}
|
|
19
21
|
};
|
|
20
22
|
fetchData().catch(console.error);
|
|
21
23
|
}, [djClient, node]);
|
|
@@ -60,28 +62,16 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
60
62
|
if (col.partition) {
|
|
61
63
|
return (
|
|
62
64
|
<>
|
|
63
|
-
<span
|
|
64
|
-
className="
|
|
65
|
-
key={`col-attr-partition-type`}
|
|
66
|
-
>
|
|
67
|
-
<span
|
|
68
|
-
className="partition_value badge"
|
|
69
|
-
key={`col-attr-partition-type`}
|
|
70
|
-
>
|
|
65
|
+
<span className="node_type badge node_type__blank">
|
|
66
|
+
<span className="partition_value badge">
|
|
71
67
|
<b>Type:</b> {col.partition.type_}
|
|
72
68
|
</span>
|
|
73
69
|
<br />
|
|
74
|
-
<span
|
|
75
|
-
className="partition_value badge"
|
|
76
|
-
key={`col-attr-partition-type`}
|
|
77
|
-
>
|
|
70
|
+
<span className="partition_value badge">
|
|
78
71
|
<b>Format:</b> <code>{col.partition.format}</code>
|
|
79
72
|
</span>
|
|
80
73
|
<br />
|
|
81
|
-
<span
|
|
82
|
-
className="partition_value badge"
|
|
83
|
-
key={`col-attr-partition-type`}
|
|
84
|
-
>
|
|
74
|
+
<span className="partition_value badge">
|
|
85
75
|
<b>Granularity:</b> <code>{col.partition.granularity}</code>
|
|
86
76
|
</span>
|
|
87
77
|
</span>
|
|
@@ -227,7 +217,7 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
227
217
|
<tbody>
|
|
228
218
|
{node?.dimension_links.map(link => {
|
|
229
219
|
return (
|
|
230
|
-
<tr>
|
|
220
|
+
<tr key={link.dimension.name}>
|
|
231
221
|
<td>
|
|
232
222
|
<a href={'/nodes/' + link.dimension.name}>
|
|
233
223
|
{link.dimension.name}
|
|
@@ -14,15 +14,17 @@ export default function NodeDependenciesTab({ node, djClient }) {
|
|
|
14
14
|
|
|
15
15
|
useEffect(() => {
|
|
16
16
|
const fetchData = async () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
if (node) {
|
|
18
|
+
let upstreams = await djClient.upstreams(node.name);
|
|
19
|
+
let downstreams = await djClient.downstreams(node.name);
|
|
20
|
+
let dimensions = await djClient.nodeDimensions(node.name);
|
|
21
|
+
setNodeDAG({
|
|
22
|
+
upstreams: upstreams,
|
|
23
|
+
downstreams: downstreams,
|
|
24
|
+
dimensions: dimensions,
|
|
25
|
+
});
|
|
26
|
+
setRetrieved(true);
|
|
27
|
+
}
|
|
26
28
|
};
|
|
27
29
|
fetchData().catch(console.error);
|
|
28
30
|
}, [djClient, node]);
|
|
@@ -102,12 +104,12 @@ export function NodeDimensionsList({ rawDimensions }) {
|
|
|
102
104
|
};
|
|
103
105
|
});
|
|
104
106
|
return (
|
|
105
|
-
<details>
|
|
107
|
+
<details key={grouping[0]}>
|
|
106
108
|
<summary style={{ marginBottom: '10px' }}>{groupHeader}</summary>
|
|
107
109
|
<div className="dimensionsList">
|
|
108
110
|
{dimensionGroupOptions.map(dimension => {
|
|
109
111
|
return (
|
|
110
|
-
<div>
|
|
112
|
+
<div key={dimension.value}>
|
|
111
113
|
{dimension.label.split('[').slice(0)[0]} ⇢{' '}
|
|
112
114
|
<code className="DimensionAttribute">
|
|
113
115
|
{dimension.value}
|
|
@@ -127,7 +129,11 @@ export function NodeList({ nodes }) {
|
|
|
127
129
|
return nodes && nodes.length > 0 ? (
|
|
128
130
|
<ul className="backfills">
|
|
129
131
|
{nodes?.map(node => (
|
|
130
|
-
<li
|
|
132
|
+
<li
|
|
133
|
+
className="backfill"
|
|
134
|
+
style={{ marginBottom: '5px' }}
|
|
135
|
+
key={node.name}
|
|
136
|
+
>
|
|
131
137
|
<span
|
|
132
138
|
className={`node_type__${node.type} badge node_type`}
|
|
133
139
|
style={{ marginRight: '5px' }}
|
|
@@ -110,8 +110,10 @@ const NodeGraphTab = djNode => {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
const dagFetch = async (getLayoutedElements, setNodes, setEdges) => {
|
|
113
|
-
let related_nodes =
|
|
114
|
-
|
|
113
|
+
let related_nodes = djNode.djNode
|
|
114
|
+
? await djClient.node_dag(djNode.djNode.name)
|
|
115
|
+
: [];
|
|
116
|
+
var djNodes = djNode.djNode ? [djNode.djNode] : [];
|
|
115
117
|
for (const iterable of [related_nodes]) {
|
|
116
118
|
for (const item of iterable) {
|
|
117
119
|
if (item.type !== 'cube') {
|
|
@@ -47,7 +47,10 @@ export default function NodeInfoTab({ node }) {
|
|
|
47
47
|
The following functions used in the metric definition may not be
|
|
48
48
|
compatible with Druid SQL:{' '}
|
|
49
49
|
{node?.incompatible_druid_functions.map(func => (
|
|
50
|
-
<li
|
|
50
|
+
<li
|
|
51
|
+
style={{ listStyleType: 'none', margin: '0.7rem 0.7rem' }}
|
|
52
|
+
key={func}
|
|
53
|
+
>
|
|
51
54
|
⇢{' '}
|
|
52
55
|
<span style={{ background: '#fff', padding: '0.3rem' }}>
|
|
53
56
|
{func}
|
|
@@ -80,7 +80,7 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
80
80
|
<Form>
|
|
81
81
|
{displayMessageAfterSubmit(status)}
|
|
82
82
|
<span data-testid="edit-partition">
|
|
83
|
-
<label htmlFor="
|
|
83
|
+
<label htmlFor="partitionType">Partition Type</label>
|
|
84
84
|
<Field
|
|
85
85
|
as="select"
|
|
86
86
|
name="partition_type"
|
|
@@ -108,9 +108,7 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
108
108
|
<br />
|
|
109
109
|
{values.partition_type === 'temporal' ? (
|
|
110
110
|
<>
|
|
111
|
-
<label htmlFor="
|
|
112
|
-
Partition Format
|
|
113
|
-
</label>
|
|
111
|
+
<label htmlFor="partitionFormat">Partition Format</label>
|
|
114
112
|
<Field
|
|
115
113
|
type="text"
|
|
116
114
|
name="format"
|
|
@@ -119,7 +117,7 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
119
117
|
/>
|
|
120
118
|
<br />
|
|
121
119
|
<br />
|
|
122
|
-
<label htmlFor="
|
|
120
|
+
<label htmlFor="partitionGranularity">
|
|
123
121
|
Partition Granularity
|
|
124
122
|
</label>
|
|
125
123
|
<Field
|