datajunction-ui 0.0.93 → 0.0.95
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/components/NodeComponents.jsx +4 -0
- package/src/app/components/Tab.jsx +11 -16
- package/src/app/components/__tests__/Tab.test.jsx +4 -2
- package/src/app/hooks/useWorkspaceData.js +226 -0
- package/src/app/index.tsx +17 -1
- package/src/app/pages/MyWorkspacePage/ActiveBranchesSection.jsx +38 -107
- package/src/app/pages/MyWorkspacePage/MyNodesSection.jsx +31 -6
- package/src/app/pages/MyWorkspacePage/MyWorkspacePage.css +5 -0
- package/src/app/pages/MyWorkspacePage/NeedsAttentionSection.jsx +86 -100
- package/src/app/pages/MyWorkspacePage/TypeGroupGrid.jsx +7 -11
- package/src/app/pages/MyWorkspacePage/__tests__/ActiveBranchesSection.test.jsx +79 -11
- package/src/app/pages/MyWorkspacePage/__tests__/CollectionsSection.test.jsx +22 -0
- package/src/app/pages/MyWorkspacePage/__tests__/MaterializationsSection.test.jsx +57 -0
- package/src/app/pages/MyWorkspacePage/__tests__/MyNodesSection.test.jsx +60 -18
- package/src/app/pages/MyWorkspacePage/__tests__/MyWorkspacePage.test.jsx +156 -162
- package/src/app/pages/MyWorkspacePage/__tests__/NeedsAttentionSection.test.jsx +17 -18
- package/src/app/pages/MyWorkspacePage/__tests__/NotificationsSection.test.jsx +179 -0
- package/src/app/pages/MyWorkspacePage/__tests__/TypeGroupGrid.test.jsx +169 -49
- package/src/app/pages/MyWorkspacePage/index.jsx +41 -73
- package/src/app/pages/NodePage/NodeDataFlowTab.jsx +464 -0
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +1 -1
- package/src/app/pages/NodePage/NodeDimensionsTab.jsx +362 -0
- package/src/app/pages/NodePage/NodeLineageTab.jsx +1 -0
- package/src/app/pages/NodePage/NodesWithDimension.jsx +3 -3
- package/src/app/pages/NodePage/__tests__/NodeDataFlowTab.test.jsx +428 -0
- package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +18 -1
- package/src/app/pages/NodePage/__tests__/NodeDimensionsTab.test.jsx +412 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +28 -3
- package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +2 -2
- package/src/app/pages/NodePage/index.jsx +15 -8
- package/src/app/pages/QueryPlannerPage/ResultsView.jsx +420 -86
- package/src/app/pages/QueryPlannerPage/SelectionPanel.jsx +32 -1
- package/src/app/pages/QueryPlannerPage/__tests__/ResultsView.test.jsx +322 -0
- package/src/app/pages/QueryPlannerPage/__tests__/SelectionPanel.test.jsx +431 -2
- package/src/app/pages/QueryPlannerPage/index.jsx +31 -5
- package/src/app/pages/QueryPlannerPage/styles.css +211 -2
- package/src/app/pages/Root/__tests__/index.test.jsx +2 -3
- package/src/app/pages/Root/index.tsx +1 -1
- package/src/app/services/DJService.js +133 -23
- package/src/app/services/__tests__/DJService.test.jsx +600 -11
- package/src/styles/index.css +32 -0
|
@@ -857,6 +857,13 @@
|
|
|
857
857
|
gap: 2px;
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
+
.dimension-full-name {
|
|
861
|
+
font-size: 10px;
|
|
862
|
+
color: var(--planner-text-muted);
|
|
863
|
+
font-family: var(--font-display);
|
|
864
|
+
word-break: break-all;
|
|
865
|
+
}
|
|
866
|
+
|
|
860
867
|
.dimension-path {
|
|
861
868
|
font-size: 10px;
|
|
862
869
|
color: var(--planner-text-dim);
|
|
@@ -3557,6 +3564,55 @@ a.action-btn {
|
|
|
3557
3564
|
cursor: not-allowed;
|
|
3558
3565
|
}
|
|
3559
3566
|
|
|
3567
|
+
/* =================================
|
|
3568
|
+
Engine Selection
|
|
3569
|
+
================================= */
|
|
3570
|
+
|
|
3571
|
+
.engine-section {
|
|
3572
|
+
display: flex;
|
|
3573
|
+
align-items: center;
|
|
3574
|
+
gap: 8px;
|
|
3575
|
+
padding: 10px 12px;
|
|
3576
|
+
border-top: 1px solid var(--planner-border);
|
|
3577
|
+
background: var(--planner-bg);
|
|
3578
|
+
flex-shrink: 0;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
.engine-label {
|
|
3582
|
+
font-size: 12px;
|
|
3583
|
+
font-weight: 500;
|
|
3584
|
+
color: var(--text-secondary);
|
|
3585
|
+
white-space: nowrap;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
.engine-pills {
|
|
3589
|
+
display: flex;
|
|
3590
|
+
gap: 4px;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
.engine-pill {
|
|
3594
|
+
padding: 4px 10px;
|
|
3595
|
+
border: 1px solid var(--planner-border);
|
|
3596
|
+
border-radius: 12px;
|
|
3597
|
+
background: transparent;
|
|
3598
|
+
font-size: 12px;
|
|
3599
|
+
color: var(--text-secondary);
|
|
3600
|
+
cursor: pointer;
|
|
3601
|
+
transition: all 0.12s ease;
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
.engine-pill:hover {
|
|
3605
|
+
border-color: #059669;
|
|
3606
|
+
color: #059669;
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
.engine-pill.active {
|
|
3610
|
+
background: #059669;
|
|
3611
|
+
border-color: #059669;
|
|
3612
|
+
color: white;
|
|
3613
|
+
font-weight: 500;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3560
3616
|
/* =================================
|
|
3561
3617
|
Run Query Section
|
|
3562
3618
|
================================= */
|
|
@@ -3950,8 +4006,9 @@ a.action-btn {
|
|
|
3950
4006
|
.results-table th {
|
|
3951
4007
|
position: sticky;
|
|
3952
4008
|
top: 0;
|
|
4009
|
+
z-index: 1;
|
|
3953
4010
|
padding: 10px 12px;
|
|
3954
|
-
background:
|
|
4011
|
+
background: #f8fafc;
|
|
3955
4012
|
color: var(--planner-text);
|
|
3956
4013
|
font-weight: 600;
|
|
3957
4014
|
text-align: left;
|
|
@@ -3962,7 +4019,7 @@ a.action-btn {
|
|
|
3962
4019
|
}
|
|
3963
4020
|
|
|
3964
4021
|
.results-table th:hover {
|
|
3965
|
-
background:
|
|
4022
|
+
background: #f1f5f9;
|
|
3966
4023
|
}
|
|
3967
4024
|
|
|
3968
4025
|
.results-table th.sorted {
|
|
@@ -4051,3 +4108,155 @@ a.action-btn {
|
|
|
4051
4108
|
.filters-list .filter-chip {
|
|
4052
4109
|
padding: 4px 10px;
|
|
4053
4110
|
}
|
|
4111
|
+
|
|
4112
|
+
/* =================================
|
|
4113
|
+
Results View - Tab bar & Chart
|
|
4114
|
+
================================= */
|
|
4115
|
+
|
|
4116
|
+
.results-tabs-bar {
|
|
4117
|
+
display: flex;
|
|
4118
|
+
align-items: center;
|
|
4119
|
+
gap: 12px;
|
|
4120
|
+
padding: 8px 16px 0;
|
|
4121
|
+
border-bottom: 1px solid var(--planner-border);
|
|
4122
|
+
background: var(--planner-surface);
|
|
4123
|
+
flex-shrink: 0;
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
.results-tabs {
|
|
4127
|
+
display: flex;
|
|
4128
|
+
gap: 2px;
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
.results-tab {
|
|
4132
|
+
padding: 6px 14px;
|
|
4133
|
+
font-size: 12px;
|
|
4134
|
+
font-weight: 500;
|
|
4135
|
+
font-family: var(--font-body);
|
|
4136
|
+
background: none;
|
|
4137
|
+
border: none;
|
|
4138
|
+
border-bottom: 2px solid transparent;
|
|
4139
|
+
color: var(--planner-text-muted);
|
|
4140
|
+
cursor: pointer;
|
|
4141
|
+
transition: color 0.15s, border-color 0.15s;
|
|
4142
|
+
margin-bottom: -1px;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
.results-tab:hover:not(.disabled) {
|
|
4146
|
+
color: var(--planner-text);
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
.results-tab.active {
|
|
4150
|
+
color: var(--accent-primary);
|
|
4151
|
+
border-bottom-color: var(--accent-primary);
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
.results-tab.disabled {
|
|
4155
|
+
opacity: 0.4;
|
|
4156
|
+
cursor: not-allowed;
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
.results-tabs-meta {
|
|
4160
|
+
display: flex;
|
|
4161
|
+
align-items: center;
|
|
4162
|
+
gap: 8px;
|
|
4163
|
+
margin-left: auto;
|
|
4164
|
+
padding-bottom: 6px;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
/* Chart wrapper - fills remaining height */
|
|
4168
|
+
.results-chart-wrapper {
|
|
4169
|
+
flex: 1;
|
|
4170
|
+
overflow: hidden;
|
|
4171
|
+
min-height: 0;
|
|
4172
|
+
padding: 16px 8px 8px;
|
|
4173
|
+
display: flex;
|
|
4174
|
+
align-items: stretch;
|
|
4175
|
+
}
|
|
4176
|
+
|
|
4177
|
+
/* KPI cards */
|
|
4178
|
+
.kpi-cards {
|
|
4179
|
+
display: flex;
|
|
4180
|
+
flex-wrap: wrap;
|
|
4181
|
+
gap: 16px;
|
|
4182
|
+
align-items: flex-start;
|
|
4183
|
+
padding: 8px;
|
|
4184
|
+
}
|
|
4185
|
+
|
|
4186
|
+
.kpi-card {
|
|
4187
|
+
background: var(--planner-surface);
|
|
4188
|
+
border: 1px solid var(--planner-border);
|
|
4189
|
+
border-radius: var(--radius-lg);
|
|
4190
|
+
padding: 20px 28px;
|
|
4191
|
+
min-width: 160px;
|
|
4192
|
+
text-align: center;
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4195
|
+
.kpi-label {
|
|
4196
|
+
font-size: 11px;
|
|
4197
|
+
font-weight: 600;
|
|
4198
|
+
color: var(--planner-text-muted);
|
|
4199
|
+
text-transform: uppercase;
|
|
4200
|
+
letter-spacing: 0.04em;
|
|
4201
|
+
margin-bottom: 8px;
|
|
4202
|
+
}
|
|
4203
|
+
|
|
4204
|
+
.kpi-value {
|
|
4205
|
+
font-size: 28px;
|
|
4206
|
+
font-weight: 700;
|
|
4207
|
+
color: var(--planner-text);
|
|
4208
|
+
font-family: var(--font-display);
|
|
4209
|
+
line-height: 1.1;
|
|
4210
|
+
}
|
|
4211
|
+
|
|
4212
|
+
.kpi-type {
|
|
4213
|
+
font-size: 10px;
|
|
4214
|
+
color: var(--planner-text-dim);
|
|
4215
|
+
margin-top: 6px;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
.chart-no-data {
|
|
4219
|
+
display: flex;
|
|
4220
|
+
align-items: center;
|
|
4221
|
+
justify-content: center;
|
|
4222
|
+
width: 100%;
|
|
4223
|
+
color: var(--planner-text-muted);
|
|
4224
|
+
font-size: 13px;
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
/* =================================
|
|
4228
|
+
Small Multiples
|
|
4229
|
+
================================= */
|
|
4230
|
+
|
|
4231
|
+
.small-multiples {
|
|
4232
|
+
display: flex;
|
|
4233
|
+
flex-direction: column;
|
|
4234
|
+
gap: 0;
|
|
4235
|
+
width: 100%;
|
|
4236
|
+
height: 100%;
|
|
4237
|
+
overflow-y: auto;
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
.small-multiple {
|
|
4241
|
+
flex-shrink: 0;
|
|
4242
|
+
height: 220px;
|
|
4243
|
+
padding: 0 0 8px 0;
|
|
4244
|
+
border-bottom: 1px solid var(--planner-border);
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
.small-multiple:last-child {
|
|
4248
|
+
border-bottom: none;
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
.small-multiple-label {
|
|
4252
|
+
font-size: 11px;
|
|
4253
|
+
font-weight: 600;
|
|
4254
|
+
color: var(--planner-text-muted);
|
|
4255
|
+
padding: 8px 0 0 8px;
|
|
4256
|
+
text-transform: uppercase;
|
|
4257
|
+
letter-spacing: 0.04em;
|
|
4258
|
+
}
|
|
4259
|
+
|
|
4260
|
+
.small-multiple-chart {
|
|
4261
|
+
height: 188px;
|
|
4262
|
+
}
|
|
@@ -41,9 +41,8 @@ describe('<Root />', () => {
|
|
|
41
41
|
expect(document.title).toEqual('DataJunction');
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
// Check navigation links exist
|
|
45
|
-
expect(screen.
|
|
46
|
-
expect(screen.getByText('Query Planner')).toBeInTheDocument();
|
|
44
|
+
// Check navigation links exist (two "Explore" links: catalog browser + planner)
|
|
45
|
+
expect(screen.getAllByText('Explore')).toHaveLength(2);
|
|
47
46
|
});
|
|
48
47
|
|
|
49
48
|
it('renders Docs dropdown', async () => {
|
|
@@ -13,6 +13,9 @@ const DJ_GQL = process.env.REACT_APP_DJ_GQL
|
|
|
13
13
|
// Export the base URL for components that need direct access
|
|
14
14
|
export const getDJUrl = () => DJ_URL;
|
|
15
15
|
|
|
16
|
+
const QUERY_END_STATES = ['FINISHED', 'CANCELED', 'FAILED'];
|
|
17
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
18
|
+
|
|
16
19
|
export const DataJunctionAPI = {
|
|
17
20
|
listNodesForLanding: async function (
|
|
18
21
|
namespace,
|
|
@@ -865,6 +868,11 @@ export const DataJunctionAPI = {
|
|
|
865
868
|
upstreamNodes(nodeNames: $nodeNames) {
|
|
866
869
|
name
|
|
867
870
|
type
|
|
871
|
+
current {
|
|
872
|
+
parents {
|
|
873
|
+
name
|
|
874
|
+
}
|
|
875
|
+
}
|
|
868
876
|
}
|
|
869
877
|
}
|
|
870
878
|
`;
|
|
@@ -886,6 +894,11 @@ export const DataJunctionAPI = {
|
|
|
886
894
|
downstreamNodes(nodeNames: $nodeNames) {
|
|
887
895
|
name
|
|
888
896
|
type
|
|
897
|
+
current {
|
|
898
|
+
parents {
|
|
899
|
+
name
|
|
900
|
+
}
|
|
901
|
+
}
|
|
889
902
|
}
|
|
890
903
|
}
|
|
891
904
|
`;
|
|
@@ -900,6 +913,39 @@ export const DataJunctionAPI = {
|
|
|
900
913
|
return results.data?.downstreamNodes || [];
|
|
901
914
|
},
|
|
902
915
|
|
|
916
|
+
// Batch-fetch cubes by name, returning each cube's name and its metric node names.
|
|
917
|
+
// Used to build the Sankey data flow graph without N individual cube fetches.
|
|
918
|
+
findCubesWithMetrics: async function (cubeNames) {
|
|
919
|
+
if (!cubeNames || cubeNames.length === 0) return [];
|
|
920
|
+
const query = `
|
|
921
|
+
query FindCubesWithMetrics($names: [String!]) {
|
|
922
|
+
findNodes(names: $names, nodeTypes: [CUBE]) {
|
|
923
|
+
name
|
|
924
|
+
current {
|
|
925
|
+
displayName
|
|
926
|
+
cubeMetrics {
|
|
927
|
+
name
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
`;
|
|
933
|
+
const results = await (
|
|
934
|
+
await fetch(DJ_GQL, {
|
|
935
|
+
method: 'POST',
|
|
936
|
+
headers: { 'Content-Type': 'application/json' },
|
|
937
|
+
credentials: 'include',
|
|
938
|
+
body: JSON.stringify({ query, variables: { names: cubeNames } }),
|
|
939
|
+
})
|
|
940
|
+
).json();
|
|
941
|
+
return (results.data?.findNodes || []).map(n => ({
|
|
942
|
+
name: n.name,
|
|
943
|
+
display_name: n.current?.displayName || n.name,
|
|
944
|
+
type: 'cube',
|
|
945
|
+
parents: (n.current?.cubeMetrics || []).map(m => ({ name: m.name })),
|
|
946
|
+
}));
|
|
947
|
+
},
|
|
948
|
+
|
|
903
949
|
node_dag: async function (name) {
|
|
904
950
|
return await (
|
|
905
951
|
await fetch(`${DJ_URL}/nodes/${name}/dag/`, {
|
|
@@ -1097,9 +1143,10 @@ export const DataJunctionAPI = {
|
|
|
1097
1143
|
).json();
|
|
1098
1144
|
},
|
|
1099
1145
|
|
|
1100
|
-
nodesWithDimension: async function (name) {
|
|
1146
|
+
nodesWithDimension: async function (name, nodeType = null) {
|
|
1147
|
+
const params = nodeType ? `?node_type=${nodeType}` : '';
|
|
1101
1148
|
return await (
|
|
1102
|
-
await fetch(`${DJ_URL}/dimensions/${name}/nodes
|
|
1149
|
+
await fetch(`${DJ_URL}/dimensions/${name}/nodes/${params}`, {
|
|
1103
1150
|
credentials: 'include',
|
|
1104
1151
|
})
|
|
1105
1152
|
).json();
|
|
@@ -1173,33 +1220,35 @@ export const DataJunctionAPI = {
|
|
|
1173
1220
|
metricsV3: async function (
|
|
1174
1221
|
metricSelection,
|
|
1175
1222
|
dimensionSelection,
|
|
1176
|
-
filters =
|
|
1223
|
+
filters = [],
|
|
1177
1224
|
useMaterialized = true,
|
|
1225
|
+
dialect = null,
|
|
1178
1226
|
) {
|
|
1179
1227
|
const params = new URLSearchParams();
|
|
1180
1228
|
metricSelection.forEach(metric => params.append('metrics', metric));
|
|
1181
1229
|
dimensionSelection.forEach(dimension =>
|
|
1182
1230
|
params.append('dimensions', dimension),
|
|
1183
1231
|
);
|
|
1184
|
-
if (filters) {
|
|
1185
|
-
params.append('filters',
|
|
1186
|
-
}
|
|
1187
|
-
if (useMaterialized) {
|
|
1188
|
-
params.append('use_materialized', 'true');
|
|
1189
|
-
params.append('dialect', 'druid');
|
|
1190
|
-
} else {
|
|
1191
|
-
params.append('use_materialized', 'false');
|
|
1192
|
-
params.append('dialect', 'spark');
|
|
1232
|
+
if (filters && filters.length > 0) {
|
|
1233
|
+
filters.forEach(f => params.append('filters', f));
|
|
1193
1234
|
}
|
|
1235
|
+
const resolvedDialect = dialect || (useMaterialized ? 'druid' : 'trino');
|
|
1236
|
+
params.append('use_materialized', useMaterialized ? 'true' : 'false');
|
|
1237
|
+
params.append('dialect', resolvedDialect);
|
|
1194
1238
|
return await (
|
|
1195
1239
|
await fetch(`${DJ_URL}/sql/metrics/v3/?${params}`, {
|
|
1196
1240
|
credentials: 'include',
|
|
1197
|
-
params: params,
|
|
1198
1241
|
})
|
|
1199
1242
|
).json();
|
|
1200
1243
|
},
|
|
1201
1244
|
|
|
1202
|
-
data: async function (
|
|
1245
|
+
data: async function (
|
|
1246
|
+
metricSelection,
|
|
1247
|
+
dimensionSelection,
|
|
1248
|
+
filters = [],
|
|
1249
|
+
dialect = null,
|
|
1250
|
+
onProgress = null,
|
|
1251
|
+
) {
|
|
1203
1252
|
const params = new URLSearchParams();
|
|
1204
1253
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
1205
1254
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
@@ -1207,18 +1256,56 @@ export const DataJunctionAPI = {
|
|
|
1207
1256
|
filters.forEach(f => params.append('filters', f));
|
|
1208
1257
|
}
|
|
1209
1258
|
params.append('limit', '10000');
|
|
1210
|
-
|
|
1259
|
+
params.append('async_', 'true');
|
|
1260
|
+
params.append('dialect', dialect || 'trino');
|
|
1261
|
+
|
|
1262
|
+
let pollInterval = 1000;
|
|
1263
|
+
|
|
1264
|
+
// Submit the query once
|
|
1265
|
+
const submitResponse = await fetch(`${DJ_URL}/data/?${params}`, {
|
|
1211
1266
|
credentials: 'include',
|
|
1212
1267
|
});
|
|
1213
|
-
if (!
|
|
1214
|
-
const errorData = await
|
|
1268
|
+
if (!submitResponse.ok) {
|
|
1269
|
+
const errorData = await submitResponse.json().catch(() => ({}));
|
|
1215
1270
|
throw new Error(
|
|
1216
1271
|
errorData.message ||
|
|
1217
1272
|
errorData.detail ||
|
|
1218
|
-
`Query failed: ${
|
|
1273
|
+
`Query failed: ${submitResponse.status}`,
|
|
1219
1274
|
);
|
|
1220
1275
|
}
|
|
1221
|
-
|
|
1276
|
+
let results = await submitResponse.json();
|
|
1277
|
+
|
|
1278
|
+
// Report links from the first response so they're visible during polling
|
|
1279
|
+
if (onProgress && results.links?.length > 0) {
|
|
1280
|
+
onProgress({ links: results.links });
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// Poll by query ID using GET /data/query/{id} to avoid re-submitting
|
|
1284
|
+
while (!QUERY_END_STATES.includes(results.state)) {
|
|
1285
|
+
await sleep(pollInterval);
|
|
1286
|
+
pollInterval = Math.min(pollInterval * 2, 10000);
|
|
1287
|
+
|
|
1288
|
+
const pollResponse = await fetch(`${DJ_URL}/data/query/${results.id}`, {
|
|
1289
|
+
credentials: 'include',
|
|
1290
|
+
});
|
|
1291
|
+
if (!pollResponse.ok) {
|
|
1292
|
+
const errorData = await pollResponse.json().catch(() => ({}));
|
|
1293
|
+
throw new Error(
|
|
1294
|
+
errorData.message ||
|
|
1295
|
+
errorData.detail ||
|
|
1296
|
+
`Query poll failed: ${pollResponse.status}`,
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
results = await pollResponse.json();
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (results.state === 'CANCELED') {
|
|
1303
|
+
throw new Error('Query execution was canceled');
|
|
1304
|
+
}
|
|
1305
|
+
if (results.state === 'FAILED') {
|
|
1306
|
+
throw new Error(results.errors?.[0] || 'Query execution failed');
|
|
1307
|
+
}
|
|
1308
|
+
return results;
|
|
1222
1309
|
},
|
|
1223
1310
|
|
|
1224
1311
|
nodeData: async function (nodeName, selection = null) {
|
|
@@ -1444,6 +1531,13 @@ export const DataJunctionAPI = {
|
|
|
1444
1531
|
})
|
|
1445
1532
|
).json();
|
|
1446
1533
|
},
|
|
1534
|
+
dimensionDag: async function (nodeName) {
|
|
1535
|
+
return await (
|
|
1536
|
+
await fetch(`${DJ_URL}/nodes/${nodeName}/dimension-dag/`, {
|
|
1537
|
+
credentials: 'include',
|
|
1538
|
+
})
|
|
1539
|
+
).json();
|
|
1540
|
+
},
|
|
1447
1541
|
linkDimension: async function (nodeName, columnName, dimensionName) {
|
|
1448
1542
|
const response = await fetch(
|
|
1449
1543
|
`${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
|
|
@@ -1928,11 +2022,18 @@ export const DataJunctionAPI = {
|
|
|
1928
2022
|
|
|
1929
2023
|
// ===== My Workspace GraphQL Queries =====
|
|
1930
2024
|
|
|
1931
|
-
getWorkspaceRecentlyEdited: async function (
|
|
2025
|
+
getWorkspaceRecentlyEdited: async function (
|
|
2026
|
+
username,
|
|
2027
|
+
limit = 10,
|
|
2028
|
+
nodeType = null,
|
|
2029
|
+
) {
|
|
1932
2030
|
// Nodes the user has edited, ordered by last updated (excluding source nodes)
|
|
1933
2031
|
const query = `
|
|
1934
2032
|
query RecentlyEdited($editedBy: String!, $limit: Int!, $nodeTypes: [NodeType!]) {
|
|
1935
2033
|
findNodesPaginated(editedBy: $editedBy, limit: $limit, nodeTypes: $nodeTypes, orderBy: UPDATED_AT, ascending: false) {
|
|
2034
|
+
pageInfo {
|
|
2035
|
+
hasNextPage
|
|
2036
|
+
}
|
|
1936
2037
|
edges {
|
|
1937
2038
|
node {
|
|
1938
2039
|
name
|
|
@@ -1957,6 +2058,7 @@ export const DataJunctionAPI = {
|
|
|
1957
2058
|
}
|
|
1958
2059
|
}
|
|
1959
2060
|
`;
|
|
2061
|
+
const allTypes = ['TRANSFORM', 'METRIC', 'DIMENSION', 'CUBE'];
|
|
1960
2062
|
return await (
|
|
1961
2063
|
await fetch(DJ_GQL, {
|
|
1962
2064
|
method: 'POST',
|
|
@@ -1967,18 +2069,25 @@ export const DataJunctionAPI = {
|
|
|
1967
2069
|
variables: {
|
|
1968
2070
|
editedBy: username,
|
|
1969
2071
|
limit,
|
|
1970
|
-
nodeTypes: [
|
|
2072
|
+
nodeTypes: nodeType ? [nodeType] : allTypes,
|
|
1971
2073
|
},
|
|
1972
2074
|
}),
|
|
1973
2075
|
})
|
|
1974
2076
|
).json();
|
|
1975
2077
|
},
|
|
1976
2078
|
|
|
1977
|
-
getWorkspaceOwnedNodes: async function (
|
|
2079
|
+
getWorkspaceOwnedNodes: async function (
|
|
2080
|
+
username,
|
|
2081
|
+
limit = 10,
|
|
2082
|
+
nodeType = null,
|
|
2083
|
+
) {
|
|
1978
2084
|
// Owned nodes ordered by UPDATED_AT (excluding source nodes)
|
|
1979
2085
|
const query = `
|
|
1980
2086
|
query OwnedNodes($ownedBy: String!, $limit: Int!, $nodeTypes: [NodeType!]) {
|
|
1981
2087
|
findNodesPaginated(ownedBy: $ownedBy, limit: $limit, nodeTypes: $nodeTypes, orderBy: UPDATED_AT, ascending: false) {
|
|
2088
|
+
pageInfo {
|
|
2089
|
+
hasNextPage
|
|
2090
|
+
}
|
|
1982
2091
|
edges {
|
|
1983
2092
|
node {
|
|
1984
2093
|
name
|
|
@@ -2003,6 +2112,7 @@ export const DataJunctionAPI = {
|
|
|
2003
2112
|
}
|
|
2004
2113
|
}
|
|
2005
2114
|
`;
|
|
2115
|
+
const allTypes = ['TRANSFORM', 'METRIC', 'DIMENSION', 'CUBE'];
|
|
2006
2116
|
return await (
|
|
2007
2117
|
await fetch(DJ_GQL, {
|
|
2008
2118
|
method: 'POST',
|
|
@@ -2013,7 +2123,7 @@ export const DataJunctionAPI = {
|
|
|
2013
2123
|
variables: {
|
|
2014
2124
|
ownedBy: username,
|
|
2015
2125
|
limit,
|
|
2016
|
-
nodeTypes: [
|
|
2126
|
+
nodeTypes: nodeType ? [nodeType] : allTypes,
|
|
2017
2127
|
},
|
|
2018
2128
|
}),
|
|
2019
2129
|
})
|