datajunction-ui 0.0.2-0.dev1 → 0.0.2-3.dev1
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 +4 -4
- package/src/app/components/NotificationBell.tsx +11 -5
- package/src/app/components/UserMenu.tsx +3 -11
- package/src/app/components/__tests__/NotificationBell.test.tsx +17 -6
- package/src/app/components/__tests__/UserMenu.test.tsx +10 -3
- package/src/app/index.tsx +92 -85
- package/src/app/pages/AddEditNodePage/OwnersField.jsx +2 -3
- package/src/app/pages/NamespacePage/index.jsx +2 -4
- package/src/app/pages/NodePage/ClientCodePopover.jsx +27 -5
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +47 -45
- package/src/app/pages/NodePage/NodeInfoTab.jsx +73 -15
- package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +13 -7
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +23 -13
- package/src/app/pages/NodePage/index.jsx +14 -19
- package/src/app/pages/SettingsPage/__tests__/index.test.jsx +4 -1
- package/src/app/pages/SettingsPage/index.jsx +6 -6
- package/src/app/providers/UserProvider.tsx +78 -0
- package/src/app/services/DJService.js +43 -0
- package/src/app/services/__tests__/DJService.test.jsx +76 -0
- package/src/styles/nav-bar.css +5 -5
- package/webpack.config.js +3 -1
- package/cleanup-deps.sh +0 -70
- package/runit.sh +0 -30
- package/runit2.sh +0 -30
- package/src/app/icons/WrenchIcon.jsx +0 -36
- package/src/app/pages/AddEditNodePage/ColumnMetadata.jsx +0 -61
- package/src/app/pages/AddEditNodePage/ColumnsMetadataInput.jsx +0 -72
- package/src/app/pages/AddEditNodePage/ExperimentationExtension.jsx +0 -338
|
@@ -690,6 +690,49 @@ export const DataJunctionAPI = {
|
|
|
690
690
|
).json();
|
|
691
691
|
},
|
|
692
692
|
|
|
693
|
+
// GraphQL-based upstream/downstream queries - more efficient as they only fetch needed fields
|
|
694
|
+
upstreamsGQL: async function (nodeNames) {
|
|
695
|
+
const names = Array.isArray(nodeNames) ? nodeNames : [nodeNames];
|
|
696
|
+
const query = `
|
|
697
|
+
query GetUpstreamNodes($nodeNames: [String!]!) {
|
|
698
|
+
upstreamNodes(nodeNames: $nodeNames) {
|
|
699
|
+
name
|
|
700
|
+
type
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
`;
|
|
704
|
+
const results = await (
|
|
705
|
+
await fetch(DJ_GQL, {
|
|
706
|
+
method: 'POST',
|
|
707
|
+
headers: { 'Content-Type': 'application/json' },
|
|
708
|
+
credentials: 'include',
|
|
709
|
+
body: JSON.stringify({ query, variables: { nodeNames: names } }),
|
|
710
|
+
})
|
|
711
|
+
).json();
|
|
712
|
+
return results.data?.upstreamNodes || [];
|
|
713
|
+
},
|
|
714
|
+
|
|
715
|
+
downstreamsGQL: async function (nodeNames) {
|
|
716
|
+
const names = Array.isArray(nodeNames) ? nodeNames : [nodeNames];
|
|
717
|
+
const query = `
|
|
718
|
+
query GetDownstreamNodes($nodeNames: [String!]!) {
|
|
719
|
+
downstreamNodes(nodeNames: $nodeNames) {
|
|
720
|
+
name
|
|
721
|
+
type
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
`;
|
|
725
|
+
const results = await (
|
|
726
|
+
await fetch(DJ_GQL, {
|
|
727
|
+
method: 'POST',
|
|
728
|
+
headers: { 'Content-Type': 'application/json' },
|
|
729
|
+
credentials: 'include',
|
|
730
|
+
body: JSON.stringify({ query, variables: { nodeNames: names } }),
|
|
731
|
+
})
|
|
732
|
+
).json();
|
|
733
|
+
return results.data?.downstreamNodes || [];
|
|
734
|
+
},
|
|
735
|
+
|
|
693
736
|
node_dag: async function (name) {
|
|
694
737
|
return await (
|
|
695
738
|
await fetch(`${DJ_URL}/nodes/${name}/dag/`, {
|
|
@@ -324,6 +324,82 @@ describe('DataJunctionAPI', () => {
|
|
|
324
324
|
);
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
it('calls upstreamsGQL correctly with single node', async () => {
|
|
328
|
+
const nodeName = 'sampleNode';
|
|
329
|
+
fetch.mockResponseOnce(
|
|
330
|
+
JSON.stringify({
|
|
331
|
+
data: { upstreamNodes: [{ name: 'upstream1', type: 'SOURCE' }] },
|
|
332
|
+
}),
|
|
333
|
+
);
|
|
334
|
+
const result = await DataJunctionAPI.upstreamsGQL(nodeName);
|
|
335
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
336
|
+
`${DJ_URL}/graphql`,
|
|
337
|
+
expect.objectContaining({
|
|
338
|
+
method: 'POST',
|
|
339
|
+
credentials: 'include',
|
|
340
|
+
headers: { 'Content-Type': 'application/json' },
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
expect(result).toEqual([{ name: 'upstream1', type: 'SOURCE' }]);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('calls upstreamsGQL correctly with multiple nodes', async () => {
|
|
347
|
+
const nodeNames = ['node1', 'node2'];
|
|
348
|
+
fetch.mockResponseOnce(
|
|
349
|
+
JSON.stringify({
|
|
350
|
+
data: {
|
|
351
|
+
upstreamNodes: [
|
|
352
|
+
{ name: 'upstream1', type: 'SOURCE' },
|
|
353
|
+
{ name: 'upstream2', type: 'TRANSFORM' },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
}),
|
|
357
|
+
);
|
|
358
|
+
const result = await DataJunctionAPI.upstreamsGQL(nodeNames);
|
|
359
|
+
expect(result).toEqual([
|
|
360
|
+
{ name: 'upstream1', type: 'SOURCE' },
|
|
361
|
+
{ name: 'upstream2', type: 'TRANSFORM' },
|
|
362
|
+
]);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('calls downstreamsGQL correctly with single node', async () => {
|
|
366
|
+
const nodeName = 'sampleNode';
|
|
367
|
+
fetch.mockResponseOnce(
|
|
368
|
+
JSON.stringify({
|
|
369
|
+
data: { downstreamNodes: [{ name: 'downstream1', type: 'METRIC' }] },
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
const result = await DataJunctionAPI.downstreamsGQL(nodeName);
|
|
373
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
374
|
+
`${DJ_URL}/graphql`,
|
|
375
|
+
expect.objectContaining({
|
|
376
|
+
method: 'POST',
|
|
377
|
+
credentials: 'include',
|
|
378
|
+
headers: { 'Content-Type': 'application/json' },
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
381
|
+
expect(result).toEqual([{ name: 'downstream1', type: 'METRIC' }]);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('calls downstreamsGQL correctly with multiple nodes', async () => {
|
|
385
|
+
const nodeNames = ['node1', 'node2'];
|
|
386
|
+
fetch.mockResponseOnce(
|
|
387
|
+
JSON.stringify({
|
|
388
|
+
data: {
|
|
389
|
+
downstreamNodes: [
|
|
390
|
+
{ name: 'downstream1', type: 'METRIC' },
|
|
391
|
+
{ name: 'downstream2', type: 'CUBE' },
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
}),
|
|
395
|
+
);
|
|
396
|
+
const result = await DataJunctionAPI.downstreamsGQL(nodeNames);
|
|
397
|
+
expect(result).toEqual([
|
|
398
|
+
{ name: 'downstream1', type: 'METRIC' },
|
|
399
|
+
{ name: 'downstream2', type: 'CUBE' },
|
|
400
|
+
]);
|
|
401
|
+
});
|
|
402
|
+
|
|
327
403
|
it('calls node_dag correctly', async () => {
|
|
328
404
|
const nodeName = 'sampleNode';
|
|
329
405
|
fetch.mockResponseOnce(JSON.stringify({}));
|
package/src/styles/nav-bar.css
CHANGED
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
|
|
136
136
|
/* Notifications menu */
|
|
137
137
|
.notifications-menu {
|
|
138
|
-
min-width:
|
|
138
|
+
min-width: 340px;
|
|
139
139
|
max-width: 600px;
|
|
140
140
|
padding: 0;
|
|
141
141
|
}
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
.notification-title {
|
|
224
|
-
font-size:
|
|
224
|
+
font-size: 15px;
|
|
225
225
|
color: #333;
|
|
226
226
|
font-weight: 500;
|
|
227
227
|
display: flex;
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
.notification-entity {
|
|
233
|
-
font-size:
|
|
233
|
+
font-size: 13px;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
.notification-title .badge.version {
|
|
@@ -243,9 +243,9 @@
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
.notification-meta {
|
|
246
|
-
font-size:
|
|
246
|
+
font-size: 13px;
|
|
247
247
|
color: #999;
|
|
248
|
-
display:
|
|
248
|
+
display: block;
|
|
249
249
|
align-items: center;
|
|
250
250
|
gap: 0.35rem;
|
|
251
251
|
}
|
package/webpack.config.js
CHANGED
|
@@ -10,6 +10,8 @@ var babelOptions = {
|
|
|
10
10
|
presets: ['@babel/preset-react'],
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
14
|
+
|
|
13
15
|
module.exports = {
|
|
14
16
|
cache: true,
|
|
15
17
|
entry: {
|
|
@@ -17,7 +19,7 @@ module.exports = {
|
|
|
17
19
|
vendor: ['events', 'react', 'react-dom'],
|
|
18
20
|
},
|
|
19
21
|
target: 'web',
|
|
20
|
-
mode: 'development',
|
|
22
|
+
mode: isProduction ? 'production' : 'development',
|
|
21
23
|
stats: 'minimal',
|
|
22
24
|
output: {
|
|
23
25
|
path: path.resolve(__dirname, './dist'),
|
package/cleanup-deps.sh
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Cleanup script for removing unused dependencies from DJ UI
|
|
3
|
-
# Run this from datajunction-ui directory
|
|
4
|
-
# NOTE: Preserves webpack build setup for library distribution
|
|
5
|
-
|
|
6
|
-
set -e # Exit on error
|
|
7
|
-
|
|
8
|
-
echo "🧹 Cleaning up unused dependencies..."
|
|
9
|
-
echo "⚠️ Preserving webpack setup for internal library builds"
|
|
10
|
-
echo ""
|
|
11
|
-
|
|
12
|
-
# Backup package.json
|
|
13
|
-
echo "📦 Creating backup..."
|
|
14
|
-
cp package.json package.json.backup
|
|
15
|
-
echo "✅ Backup created: package.json.backup"
|
|
16
|
-
echo ""
|
|
17
|
-
|
|
18
|
-
# Remove Redux stack (completely unused)
|
|
19
|
-
echo "🗑️ Removing Redux stack (unused)..."
|
|
20
|
-
yarn remove \
|
|
21
|
-
@reduxjs/toolkit \
|
|
22
|
-
redux-saga \
|
|
23
|
-
redux-injectors \
|
|
24
|
-
react-redux \
|
|
25
|
-
@types/react-redux
|
|
26
|
-
|
|
27
|
-
# Remove i18next stack (no internationalization)
|
|
28
|
-
echo "🗑️ Removing i18next stack (unused)..."
|
|
29
|
-
yarn remove \
|
|
30
|
-
i18next \
|
|
31
|
-
i18next-browser-languagedetector \
|
|
32
|
-
i18next-scanner \
|
|
33
|
-
react-i18next
|
|
34
|
-
|
|
35
|
-
# Remove code generation tools (no generators directory)
|
|
36
|
-
echo "🗑️ Removing code generators (unused)..."
|
|
37
|
-
yarn remove \
|
|
38
|
-
plop \
|
|
39
|
-
node-plop \
|
|
40
|
-
inquirer \
|
|
41
|
-
inquirer-directory
|
|
42
|
-
|
|
43
|
-
# Remove unused utilities
|
|
44
|
-
echo "🗑️ Removing unused utilities..."
|
|
45
|
-
yarn remove \
|
|
46
|
-
shelljs \
|
|
47
|
-
@types/shelljs \
|
|
48
|
-
rimraf \
|
|
49
|
-
@types/rimraf \
|
|
50
|
-
chalk \
|
|
51
|
-
husky \
|
|
52
|
-
lint-staged
|
|
53
|
-
|
|
54
|
-
echo ""
|
|
55
|
-
echo "✅ Cleanup complete!"
|
|
56
|
-
echo ""
|
|
57
|
-
echo "📊 Summary:"
|
|
58
|
-
echo " - Removed ~19 unused packages"
|
|
59
|
-
echo " - Kept webpack & datajunction for internal builds"
|
|
60
|
-
echo " - Cleaned up broken npm scripts"
|
|
61
|
-
echo " - Updated Node.js requirement to >=18.x"
|
|
62
|
-
echo ""
|
|
63
|
-
echo "🧪 Next steps:"
|
|
64
|
-
echo " 1. Test local dev: yarn start (react-scripts)"
|
|
65
|
-
echo " 2. Test library build: yarn webpack-build (for internal consumption)"
|
|
66
|
-
echo " 3. Run tests: yarn test"
|
|
67
|
-
echo " 4. If everything works, commit changes"
|
|
68
|
-
echo " 5. If issues arise, restore: mv package.json.backup package.json && yarn install"
|
|
69
|
-
echo ""
|
|
70
|
-
|
package/runit.sh
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
total=0
|
|
2
|
-
durations=()
|
|
3
|
-
count=100
|
|
4
|
-
|
|
5
|
-
for i in $(seq 1 $count); do
|
|
6
|
-
echo "Run $i"
|
|
7
|
-
start=$(gdate +%s%3N)
|
|
8
|
-
# metatron curl -provideE2eToken -a mpdatajunction -X GET 'https://mpdatajunction-test.cluster.us-east-1.test.cloud.netflix.net:7004/sql/measures/v2?metrics=users.yshang.member.total_missing_covariates&metrics=users.yshang.member.overall_streaming_lift&metrics=users.yshang.member.engagement_quality_score&metrics=users.yshang.member.qpd_normalization__35&metrics=users.yshang.member.qpd&dimensions=common.dimensions.xp.allocation_day.account_days_since_allocation&dimensions=common.dimensions.hardware_category.hw_category&dimensions=common.dimensions.xp.allocation_day.account_observation_day&dimensions=common.dimensions.account.account_id&dimensions=common.dimensions.xp.allocation_day.alloc_country_iso_code&dimensions=common.dimensions.xp.ab_test_plan.group_id&dimensions=common.dimensions.xp.ab_test_cell.cell_id&dimensions=member.is_jfk_profile.is_jfk_profile&dimensions=common.dimensions.client_category.client_name&filters=common.dimensions.xp.max_observation_end.max_observation_end+IN+%2835%29&filters=common.dimensions.xp.allocation_snapshot_date.dateint+%3D+20250603&filters=common.dimensions.xp.measure_date.dateint+BETWEEN+20250414+AND+20250605&filters=common.dimensions.xp.ab_test.test_id+IN+%2867486%29&preaggregate=true' > /dev/null 2>&1
|
|
9
|
-
metatron curl -provideE2eToken -a mpdatajunction -X GET 'https://mpdatajunction-test.cluster.us-east-1.test.cloud.netflix.net:7004/metrics/common/dimensions?metric=member.completed_download_cnt&metric=member.pct_users_who_completed_download' > /dev/null 2>&1
|
|
10
|
-
end=$(gdate +%s%3N)
|
|
11
|
-
duration=$((end - start))
|
|
12
|
-
durations+=($duration)
|
|
13
|
-
total=$((total + duration))
|
|
14
|
-
done
|
|
15
|
-
|
|
16
|
-
average=$(echo "scale=2; $total / $count" | bc)
|
|
17
|
-
echo "Average duration: $average ms"
|
|
18
|
-
|
|
19
|
-
# Compute standard deviation
|
|
20
|
-
sum_squared_diff=0
|
|
21
|
-
for dur in "${durations[@]}"; do
|
|
22
|
-
diff=$(echo "$dur - $average" | bc)
|
|
23
|
-
squared_diff=$(echo "$diff * $diff" | bc)
|
|
24
|
-
sum_squared_diff=$(echo "$sum_squared_diff + $squared_diff" | bc)
|
|
25
|
-
done
|
|
26
|
-
|
|
27
|
-
variance=$(echo "scale=2; $sum_squared_diff / $count" | bc)
|
|
28
|
-
stddev=$(echo "scale=2; sqrt($variance)" | bc -l)
|
|
29
|
-
echo "Standard deviation: $stddev ms"
|
|
30
|
-
|
package/runit2.sh
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
total=0
|
|
2
|
-
durations=()
|
|
3
|
-
count=100
|
|
4
|
-
|
|
5
|
-
for i in $(seq 1 $count); do
|
|
6
|
-
echo "Run $i"
|
|
7
|
-
start=$(gdate +%s%3N)
|
|
8
|
-
# metatron curl -provideE2eToken -a mpdatajunction -X GET 'https://mpdatajunction-production.cluster.us-east-1.prod.cloud.netflix.net:7004/sql/measures/v2?metrics=users.yshang.member.total_missing_covariates&metrics=users.yshang.member.overall_streaming_lift&metrics=users.yshang.member.engagement_quality_score&metrics=users.yshang.member.qpd_normalization__35&metrics=users.yshang.member.qpd&dimensions=common.dimensions.xp.allocation_day.account_days_since_allocation&dimensions=common.dimensions.hardware_category.hw_category&dimensions=common.dimensions.xp.allocation_day.account_observation_day&dimensions=common.dimensions.account.account_id&dimensions=common.dimensions.xp.allocation_day.alloc_country_iso_code&dimensions=common.dimensions.xp.ab_test_plan.group_id&dimensions=common.dimensions.xp.ab_test_cell.cell_id&dimensions=member.is_jfk_profile.is_jfk_profile&dimensions=common.dimensions.client_category.client_name&filters=common.dimensions.xp.max_observation_end.max_observation_end+IN+%2835%29&filters=common.dimensions.xp.allocation_snapshot_date.dateint+%3D+20250603&filters=common.dimensions.xp.measure_date.dateint+BETWEEN+20250414+AND+20250605&filters=common.dimensions.xp.ab_test.test_id+IN+%2867486%29&preaggregate=true' > /dev/null 2>&1
|
|
9
|
-
metatron curl -provideE2eToken -a mpdatajunction -X GET 'https://mpdatajunction-production.cluster.us-east-1.prod.cloud.netflix.net:7004/metrics/common/dimensions?metric=member.completed_download_cnt&metric=member.pct_users_who_completed_download' > /dev/null 2>&1
|
|
10
|
-
end=$(gdate +%s%3N)
|
|
11
|
-
duration=$((end - start))
|
|
12
|
-
durations+=($duration)
|
|
13
|
-
total=$((total + duration))
|
|
14
|
-
done
|
|
15
|
-
|
|
16
|
-
average=$(echo "scale=2; $total / $count" | bc)
|
|
17
|
-
echo "Average duration: $average ms"
|
|
18
|
-
|
|
19
|
-
# Compute standard deviation
|
|
20
|
-
sum_squared_diff=0
|
|
21
|
-
for dur in "${durations[@]}"; do
|
|
22
|
-
diff=$(echo "$dur - $average" | bc)
|
|
23
|
-
squared_diff=$(echo "$diff * $diff" | bc)
|
|
24
|
-
sum_squared_diff=$(echo "$sum_squared_diff + $squared_diff" | bc)
|
|
25
|
-
done
|
|
26
|
-
|
|
27
|
-
variance=$(echo "scale=2; $sum_squared_diff / $count" | bc)
|
|
28
|
-
stddev=$(echo "scale=2; sqrt($variance)" | bc -l)
|
|
29
|
-
echo "Standard deviation: $stddev ms"
|
|
30
|
-
|
|
@@ -1,36 +0,0 @@
|
|
|
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;
|
|
@@ -1,61 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
};
|