datajunction-ui 0.0.1-a93 → 0.0.1-a95
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/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +25 -10
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +3 -1
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +11 -6
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +10 -16
- package/src/app/pages/AddEditNodePage/index.jsx +40 -42
- package/src/app/pages/NodePage/NodeInfoTab.jsx +11 -0
- package/src/app/services/DJService.js +56 -0
- package/src/app/services/__tests__/DJService.test.jsx +1 -0
- package/src/mocks/mockNodes.jsx +70 -0
package/package.json
CHANGED
|
@@ -24,11 +24,17 @@ export const MetricMetadataFields = () => {
|
|
|
24
24
|
}, [djClient]);
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
<div
|
|
28
|
+
style={{
|
|
29
|
+
borderRadius: '8px',
|
|
30
|
+
padding: '10px 10px 20px 10px',
|
|
31
|
+
margin: '32px 0',
|
|
32
|
+
background: '#f9f9f9',
|
|
33
|
+
width: 'max-content',
|
|
34
|
+
display: 'flex',
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<div style={{ margin: '15px 25px' }}>
|
|
32
38
|
<ErrorMessage name="metric_direction" component="span" />
|
|
33
39
|
<label htmlFor="MetricDirection">Metric Direction</label>
|
|
34
40
|
<Field as="select" name="metric_direction" id="MetricDirection">
|
|
@@ -40,10 +46,7 @@ export const MetricMetadataFields = () => {
|
|
|
40
46
|
))}
|
|
41
47
|
</Field>
|
|
42
48
|
</div>
|
|
43
|
-
<div
|
|
44
|
-
className="MetricUnitInput NodeCreationInput"
|
|
45
|
-
style={{ width: '25%' }}
|
|
46
|
-
>
|
|
49
|
+
<div style={{ margin: '15px 25px' }}>
|
|
47
50
|
<ErrorMessage name="metric_unit" component="span" />
|
|
48
51
|
<label htmlFor="MetricUnit">Metric Unit</label>
|
|
49
52
|
<Field as="select" name="metric_unit" id="MetricUnit">
|
|
@@ -55,6 +58,18 @@ export const MetricMetadataFields = () => {
|
|
|
55
58
|
))}
|
|
56
59
|
</Field>
|
|
57
60
|
</div>
|
|
58
|
-
|
|
61
|
+
<div style={{ margin: '15px 25px' }}>
|
|
62
|
+
<ErrorMessage name="significant_digits" component="span" />
|
|
63
|
+
<label htmlFor="SignificantDigits">Significant Digits</label>
|
|
64
|
+
<Field as="select" name="significant_digits" id="SignificantDigits">
|
|
65
|
+
<option value=""></option>
|
|
66
|
+
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(val => (
|
|
67
|
+
<option value={val} key={val}>
|
|
68
|
+
{val}
|
|
69
|
+
</option>
|
|
70
|
+
))}
|
|
71
|
+
</Field>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
59
74
|
);
|
|
60
75
|
};
|
|
@@ -67,7 +67,9 @@ describe('AddEditNodePage submission failed', () => {
|
|
|
67
67
|
|
|
68
68
|
it('for editing a node', async () => {
|
|
69
69
|
const mockDjClient = initializeMockDJClient();
|
|
70
|
-
mockDjClient.DataJunctionAPI.
|
|
70
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(
|
|
71
|
+
mocks.mockGetMetricNode,
|
|
72
|
+
);
|
|
71
73
|
mockDjClient.DataJunctionAPI.patchNode.mockReturnValue({
|
|
72
74
|
status: 500,
|
|
73
75
|
json: { message: 'Update failed' },
|
|
@@ -152,7 +152,9 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
152
152
|
it('for editing a transform or dimension node', async () => {
|
|
153
153
|
const mockDjClient = initializeMockDJClient();
|
|
154
154
|
|
|
155
|
-
mockDjClient.DataJunctionAPI.
|
|
155
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(
|
|
156
|
+
mocks.mockGetTransformNode,
|
|
157
|
+
);
|
|
156
158
|
mockDjClient.DataJunctionAPI.patchNode = jest.fn();
|
|
157
159
|
mockDjClient.DataJunctionAPI.patchNode.mockReturnValue({
|
|
158
160
|
status: 201,
|
|
@@ -189,8 +191,9 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
189
191
|
'SELECT repair_order_id, municipality_id, hard_hat_id, dispatcher_id FROM default.repair_orders',
|
|
190
192
|
'published',
|
|
191
193
|
[],
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
'',
|
|
195
|
+
'',
|
|
196
|
+
'',
|
|
194
197
|
undefined,
|
|
195
198
|
);
|
|
196
199
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
@@ -211,8 +214,9 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
211
214
|
it('for editing a metric node', async () => {
|
|
212
215
|
const mockDjClient = initializeMockDJClient();
|
|
213
216
|
|
|
214
|
-
mockDjClient.DataJunctionAPI.
|
|
215
|
-
|
|
217
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(
|
|
218
|
+
mocks.mockGetMetricNode,
|
|
219
|
+
);
|
|
216
220
|
mockDjClient.DataJunctionAPI.patchNode = jest.fn();
|
|
217
221
|
mockDjClient.DataJunctionAPI.patchNode.mockReturnValue({
|
|
218
222
|
status: 201,
|
|
@@ -248,9 +252,10 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
248
252
|
'Number of repair orders!!!',
|
|
249
253
|
'SELECT count(repair_order_id) FROM default.repair_orders',
|
|
250
254
|
'published',
|
|
251
|
-
[],
|
|
255
|
+
['repair_order_id', 'country'],
|
|
252
256
|
'neutral',
|
|
253
257
|
'unitless',
|
|
258
|
+
5,
|
|
254
259
|
undefined,
|
|
255
260
|
);
|
|
256
261
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
@@ -35,6 +35,7 @@ export const initializeMockDJClient = () => {
|
|
|
35
35
|
];
|
|
36
36
|
},
|
|
37
37
|
metrics: {},
|
|
38
|
+
getNodeForEditing: jest.fn(),
|
|
38
39
|
namespaces: () => {
|
|
39
40
|
return [
|
|
40
41
|
{
|
|
@@ -146,7 +147,9 @@ describe('AddEditNodePage', () => {
|
|
|
146
147
|
|
|
147
148
|
it('Edit node page renders with the selected node', async () => {
|
|
148
149
|
const mockDjClient = initializeMockDJClient();
|
|
149
|
-
mockDjClient.DataJunctionAPI.
|
|
150
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(
|
|
151
|
+
mocks.mockGetMetricNode,
|
|
152
|
+
);
|
|
150
153
|
|
|
151
154
|
const element = testElement(mockDjClient);
|
|
152
155
|
renderEditNode(element);
|
|
@@ -175,17 +178,12 @@ describe('AddEditNodePage', () => {
|
|
|
175
178
|
|
|
176
179
|
it('Verify edit page node not found', async () => {
|
|
177
180
|
const mockDjClient = initializeMockDJClient();
|
|
178
|
-
mockDjClient.DataJunctionAPI.
|
|
179
|
-
mockDjClient.DataJunctionAPI.node.mockReturnValue({
|
|
180
|
-
message: 'A node with name `default.num_repair_orders` does not exist.',
|
|
181
|
-
errors: [],
|
|
182
|
-
warnings: [],
|
|
183
|
-
});
|
|
181
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(null);
|
|
184
182
|
const element = testElement(mockDjClient);
|
|
185
183
|
renderEditNode(element);
|
|
186
184
|
|
|
187
185
|
await waitFor(() => {
|
|
188
|
-
expect(mockDjClient.DataJunctionAPI.
|
|
186
|
+
expect(mockDjClient.DataJunctionAPI.getNodeForEditing).toBeCalledTimes(1);
|
|
189
187
|
expect(
|
|
190
188
|
screen.getByText('Node default.num_repair_orders does not exist!'),
|
|
191
189
|
).toBeInTheDocument();
|
|
@@ -194,18 +192,14 @@ describe('AddEditNodePage', () => {
|
|
|
194
192
|
|
|
195
193
|
it('Verify only transforms, metrics, and dimensions can be edited', async () => {
|
|
196
194
|
const mockDjClient = initializeMockDJClient();
|
|
197
|
-
mockDjClient.DataJunctionAPI.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
type: 'source',
|
|
201
|
-
name: 'default.repair_orders',
|
|
202
|
-
display_name: 'Default: Repair Orders',
|
|
203
|
-
});
|
|
195
|
+
mockDjClient.DataJunctionAPI.getNodeForEditing.mockReturnValue(
|
|
196
|
+
mocks.mockGetSourceNode,
|
|
197
|
+
);
|
|
204
198
|
const element = testElement(mockDjClient);
|
|
205
199
|
renderEditNode(element);
|
|
206
200
|
|
|
207
201
|
await waitFor(() => {
|
|
208
|
-
expect(mockDjClient.DataJunctionAPI.
|
|
202
|
+
expect(mockDjClient.DataJunctionAPI.getNodeForEditing).toBeCalledTimes(1);
|
|
209
203
|
expect(
|
|
210
204
|
screen.getByText(
|
|
211
205
|
'Node default.num_repair_orders is of type source and cannot be edited',
|
|
@@ -152,6 +152,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
152
152
|
values.primary_key ? primaryKeyToList(values.primary_key) : null,
|
|
153
153
|
values.metric_direction,
|
|
154
154
|
values.metric_unit,
|
|
155
|
+
values.significant_digits,
|
|
155
156
|
values.required_dimensions,
|
|
156
157
|
);
|
|
157
158
|
const tagsResponse = await djClient.tagsNode(
|
|
@@ -187,26 +188,35 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
187
188
|
};
|
|
188
189
|
|
|
189
190
|
const getExistingNodeData = async name => {
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
data.upstream_node = metric.upstream_node;
|
|
194
|
-
data.expression = metric.expression;
|
|
195
|
-
data.required_dimensions = metric.required_dimensions;
|
|
191
|
+
const node = await djClient.getNodeForEditing(name);
|
|
192
|
+
if (node === null) {
|
|
193
|
+
return { message: `Node ${name} does not exist` };
|
|
196
194
|
}
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
const baseData = {
|
|
196
|
+
name: node.name,
|
|
197
|
+
type: node.type.toLowerCase(),
|
|
198
|
+
display_name: node.current.displayName,
|
|
199
|
+
description: node.current.description,
|
|
200
|
+
primary_key: node.current.primaryKey,
|
|
201
|
+
query: node.current.query,
|
|
202
|
+
tags: node.tags,
|
|
203
|
+
mode: node.current.mode.toLowerCase(),
|
|
204
|
+
};
|
|
199
205
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
if (node.type === 'METRIC') {
|
|
207
|
+
return {
|
|
208
|
+
...baseData,
|
|
209
|
+
metric_direction: node.current.metricMetadata?.direction?.toLowerCase(),
|
|
210
|
+
metric_unit: node.current.metricMetadata?.unit?.name?.toLowerCase(),
|
|
211
|
+
significant_digits: node.current.metricMetadata?.significantDigits,
|
|
212
|
+
required_dimensions: node.current.requiredDimensions.map(
|
|
213
|
+
dim => dim.name,
|
|
214
|
+
),
|
|
215
|
+
upstream_node: node.current.parents[0]?.name,
|
|
216
|
+
aggregate_expression: node.current.metricMetadata?.expression,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return baseData;
|
|
210
220
|
};
|
|
211
221
|
|
|
212
222
|
const runValidityChecks = (data, setNode, setMessage) => {
|
|
@@ -216,7 +226,6 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
216
226
|
setMessage(`Node ${name} does not exist!`);
|
|
217
227
|
return;
|
|
218
228
|
}
|
|
219
|
-
|
|
220
229
|
// Check if node type can be edited
|
|
221
230
|
if (!nodeCanBeEdited(data.type)) {
|
|
222
231
|
setNode(null);
|
|
@@ -245,14 +254,14 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
245
254
|
'primary_key',
|
|
246
255
|
'mode',
|
|
247
256
|
'tags',
|
|
248
|
-
'
|
|
257
|
+
'aggregate_expression',
|
|
249
258
|
'upstream_node',
|
|
259
|
+
'metric_unit',
|
|
260
|
+
'metric_direction',
|
|
261
|
+
'significant_digits',
|
|
250
262
|
];
|
|
251
|
-
const primaryKey = primaryKeyFromNode(data);
|
|
252
263
|
fields.forEach(field => {
|
|
253
|
-
if (field === '
|
|
254
|
-
setFieldValue(field, primaryKey);
|
|
255
|
-
} else if (field === 'tags') {
|
|
264
|
+
if (field === 'tags') {
|
|
256
265
|
setFieldValue(
|
|
257
266
|
field,
|
|
258
267
|
data[field].map(tag => tag.name),
|
|
@@ -261,21 +270,6 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
261
270
|
setFieldValue(field, data[field] || '', false);
|
|
262
271
|
}
|
|
263
272
|
});
|
|
264
|
-
if (data.metric_metadata?.direction) {
|
|
265
|
-
setFieldValue('metric_direction', data.metric_metadata.direction);
|
|
266
|
-
}
|
|
267
|
-
if (data.metric_metadata?.unit) {
|
|
268
|
-
setFieldValue(
|
|
269
|
-
'metric_unit',
|
|
270
|
-
data.metric_metadata.unit.name.toLowerCase(),
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
if (data.expression) {
|
|
274
|
-
setFieldValue('aggregate_expression', data.expression);
|
|
275
|
-
}
|
|
276
|
-
if (data.upstream_node) {
|
|
277
|
-
setFieldValue('upstream_node', data.upstream_node);
|
|
278
|
-
}
|
|
279
273
|
setNode(data);
|
|
280
274
|
|
|
281
275
|
// For react-select fields, we have to explicitly set the entire
|
|
@@ -283,13 +277,13 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
283
277
|
setSelectTags(
|
|
284
278
|
<TagsField
|
|
285
279
|
defaultValue={data.tags.map(t => {
|
|
286
|
-
return { value: t.name, label: t.
|
|
280
|
+
return { value: t.name, label: t.displayName };
|
|
287
281
|
})}
|
|
288
282
|
/>,
|
|
289
283
|
);
|
|
290
284
|
setSelectPrimaryKey(
|
|
291
285
|
<ColumnsSelect
|
|
292
|
-
defaultValue={
|
|
286
|
+
defaultValue={data.primary_key}
|
|
293
287
|
fieldName="primary_key"
|
|
294
288
|
label="Primary Key"
|
|
295
289
|
isMulti={true}
|
|
@@ -402,7 +396,11 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
402
396
|
{nodeType === 'metric' || node.type === 'metric' ? (
|
|
403
397
|
<MetricQueryField
|
|
404
398
|
djClient={djClient}
|
|
405
|
-
value={
|
|
399
|
+
value={
|
|
400
|
+
node.aggregate_expression
|
|
401
|
+
? node.aggregate_expression
|
|
402
|
+
: ''
|
|
403
|
+
}
|
|
406
404
|
/>
|
|
407
405
|
) : (
|
|
408
406
|
<NodeQueryField
|
|
@@ -181,6 +181,17 @@ export default function NodeInfoTab({ node }) {
|
|
|
181
181
|
: 'None'}
|
|
182
182
|
</p>
|
|
183
183
|
</div>
|
|
184
|
+
<div style={{ marginRight: '2rem' }}>
|
|
185
|
+
<h6 className="mb-0 w-100">Significant Digits</h6>
|
|
186
|
+
<p
|
|
187
|
+
className="mb-0 opacity-75"
|
|
188
|
+
role="dialog"
|
|
189
|
+
aria-hidden="false"
|
|
190
|
+
aria-label="SignificantDigits"
|
|
191
|
+
>
|
|
192
|
+
{node?.metric_metadata?.significantDigits || 'None'}
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
184
195
|
</div>
|
|
185
196
|
</div>
|
|
186
197
|
) : (
|
|
@@ -128,6 +128,59 @@ export const DataJunctionAPI = {
|
|
|
128
128
|
return data;
|
|
129
129
|
},
|
|
130
130
|
|
|
131
|
+
getNodeForEditing: async function (name) {
|
|
132
|
+
const query = `
|
|
133
|
+
query GetNodeForEditing($name: String!) {
|
|
134
|
+
findNodes (names: [$name]) {
|
|
135
|
+
name
|
|
136
|
+
type
|
|
137
|
+
current {
|
|
138
|
+
displayName
|
|
139
|
+
description
|
|
140
|
+
primaryKey
|
|
141
|
+
query
|
|
142
|
+
parents { name }
|
|
143
|
+
metricMetadata {
|
|
144
|
+
direction
|
|
145
|
+
unit { name }
|
|
146
|
+
expression
|
|
147
|
+
significantDigits
|
|
148
|
+
incompatibleDruidFunctions
|
|
149
|
+
}
|
|
150
|
+
requiredDimensions {
|
|
151
|
+
name
|
|
152
|
+
}
|
|
153
|
+
mode
|
|
154
|
+
}
|
|
155
|
+
tags {
|
|
156
|
+
name
|
|
157
|
+
displayName
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
const results = await (
|
|
164
|
+
await fetch(DJ_GQL, {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
headers: {
|
|
167
|
+
'Content-Type': 'application/json',
|
|
168
|
+
},
|
|
169
|
+
credentials: 'include',
|
|
170
|
+
body: JSON.stringify({
|
|
171
|
+
query,
|
|
172
|
+
variables: {
|
|
173
|
+
name: name,
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
})
|
|
177
|
+
).json();
|
|
178
|
+
if (results.data.findNodes.length === 0) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return results.data.findNodes[0];
|
|
182
|
+
},
|
|
183
|
+
|
|
131
184
|
getMetric: async function (name) {
|
|
132
185
|
const query = `
|
|
133
186
|
query GetMetric($name: String!) {
|
|
@@ -139,6 +192,7 @@ export const DataJunctionAPI = {
|
|
|
139
192
|
direction
|
|
140
193
|
unit { name }
|
|
141
194
|
expression
|
|
195
|
+
significantDigits
|
|
142
196
|
incompatibleDruidFunctions
|
|
143
197
|
}
|
|
144
198
|
requiredDimensions {
|
|
@@ -267,6 +321,7 @@ export const DataJunctionAPI = {
|
|
|
267
321
|
primary_key,
|
|
268
322
|
metric_direction,
|
|
269
323
|
metric_unit,
|
|
324
|
+
significant_digits,
|
|
270
325
|
required_dimensions,
|
|
271
326
|
) {
|
|
272
327
|
try {
|
|
@@ -275,6 +330,7 @@ export const DataJunctionAPI = {
|
|
|
275
330
|
? {
|
|
276
331
|
direction: metric_direction,
|
|
277
332
|
unit: metric_unit,
|
|
333
|
+
significant_digits: significant_digits || null,
|
|
278
334
|
}
|
|
279
335
|
: null;
|
|
280
336
|
const response = await fetch(`${DJ_URL}/nodes/${name}`, {
|
package/src/mocks/mockNodes.jsx
CHANGED
|
@@ -278,12 +278,82 @@ export const mocks = {
|
|
|
278
278
|
label: 'Unitless',
|
|
279
279
|
},
|
|
280
280
|
direction: 'neutral',
|
|
281
|
+
max_decimal_exponent: null,
|
|
282
|
+
min_decimal_exponent: null,
|
|
283
|
+
significant_digits: 4,
|
|
281
284
|
},
|
|
282
285
|
upstream_node: 'default.repair_orders',
|
|
283
286
|
expression: 'count(repair_order_id)',
|
|
284
287
|
aggregate_expression: 'count(repair_order_id)',
|
|
285
288
|
required_dimensions: [],
|
|
286
289
|
},
|
|
290
|
+
|
|
291
|
+
mockGetSourceNode: {
|
|
292
|
+
name: 'default.num_repair_orders',
|
|
293
|
+
type: 'SOURCE',
|
|
294
|
+
current: {
|
|
295
|
+
displayName: 'source.prodhive.dse.playback_f',
|
|
296
|
+
description:
|
|
297
|
+
'This source node was automatically created as a registered table.',
|
|
298
|
+
primaryKey: [],
|
|
299
|
+
parents: [],
|
|
300
|
+
metricMetadata: null,
|
|
301
|
+
requiredDimensions: [],
|
|
302
|
+
mode: 'PUBLISHED',
|
|
303
|
+
},
|
|
304
|
+
tags: [],
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
mockGetMetricNode: {
|
|
308
|
+
name: 'default.num_repair_orders',
|
|
309
|
+
type: 'METRIC',
|
|
310
|
+
current: {
|
|
311
|
+
displayName: 'Default: Num Repair Orders',
|
|
312
|
+
description: 'Number of repair orders',
|
|
313
|
+
primaryKey: ['repair_order_id', 'country'],
|
|
314
|
+
query:
|
|
315
|
+
'SELECT count(repair_order_id) default_DOT_num_repair_orders FROM default.repair_orders',
|
|
316
|
+
parents: [
|
|
317
|
+
{
|
|
318
|
+
name: 'default.repair_orders',
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
metricMetadata: {
|
|
322
|
+
direction: 'NEUTRAL',
|
|
323
|
+
unit: {
|
|
324
|
+
name: 'UNITLESS',
|
|
325
|
+
},
|
|
326
|
+
expression: 'count(repair_order_id)',
|
|
327
|
+
significantDigits: 5,
|
|
328
|
+
incompatibleDruidFunctions: ['IF'],
|
|
329
|
+
},
|
|
330
|
+
requiredDimensions: [],
|
|
331
|
+
mode: 'PUBLISHED',
|
|
332
|
+
},
|
|
333
|
+
tags: [{ name: 'purpose', displayName: 'Purpose' }],
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
mockGetTransformNode: {
|
|
337
|
+
name: 'default.repair_order_transform',
|
|
338
|
+
type: 'TRANSFORM',
|
|
339
|
+
current: {
|
|
340
|
+
displayName: 'Default: Repair Order Transform',
|
|
341
|
+
description: 'Repair order dimension',
|
|
342
|
+
primaryKey: [],
|
|
343
|
+
query:
|
|
344
|
+
'SELECT repair_order_id, municipality_id, hard_hat_id, dispatcher_id FROM default.repair_orders',
|
|
345
|
+
parents: [
|
|
346
|
+
{
|
|
347
|
+
name: 'default.repair_orders',
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
metricMetadata: null,
|
|
351
|
+
requiredDimensions: [],
|
|
352
|
+
mode: 'PUBLISHED',
|
|
353
|
+
},
|
|
354
|
+
tags: [],
|
|
355
|
+
},
|
|
356
|
+
|
|
287
357
|
attributes: [
|
|
288
358
|
{
|
|
289
359
|
uniqueness_scope: [],
|