datajunction-ui 0.0.43 → 0.0.45
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/__tests__/NamespaceHeader.test.jsx +349 -1
- package/src/app/pages/NodePage/NodeInfoTab.jsx +38 -40
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +0 -133
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +9 -7
- package/src/app/pages/NodePage/index.jsx +12 -11
- package/src/app/pages/QueryPlannerPage/PreAggDetailsPanel.jsx +46 -1
- package/src/app/pages/QueryPlannerPage/ResultsView.jsx +281 -0
- package/src/app/pages/QueryPlannerPage/SelectionPanel.jsx +225 -100
- package/src/app/pages/QueryPlannerPage/__tests__/MetricFlowGraph.test.jsx +193 -0
- package/src/app/pages/QueryPlannerPage/__tests__/ResultsView.test.jsx +388 -0
- package/src/app/pages/QueryPlannerPage/__tests__/SelectionPanel.test.jsx +31 -51
- package/src/app/pages/QueryPlannerPage/__tests__/index.test.jsx +720 -34
- package/src/app/pages/QueryPlannerPage/index.jsx +237 -117
- package/src/app/pages/QueryPlannerPage/styles.css +765 -15
- package/src/app/services/DJService.js +29 -6
- package/src/app/services/__tests__/DJService.test.jsx +163 -0
|
@@ -172,6 +172,12 @@ export const DataJunctionAPI = {
|
|
|
172
172
|
cubeDimensions {
|
|
173
173
|
name
|
|
174
174
|
}
|
|
175
|
+
availability {
|
|
176
|
+
catalog
|
|
177
|
+
schema_
|
|
178
|
+
table
|
|
179
|
+
validThroughTs
|
|
180
|
+
}
|
|
175
181
|
materializations {
|
|
176
182
|
name
|
|
177
183
|
config
|
|
@@ -195,6 +201,11 @@ export const DataJunctionAPI = {
|
|
|
195
201
|
})
|
|
196
202
|
).json();
|
|
197
203
|
|
|
204
|
+
if (result.errors) {
|
|
205
|
+
console.error('GraphQL errors:', result.errors);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
198
209
|
const node = result?.data?.findNodes?.[0];
|
|
199
210
|
if (!node) {
|
|
200
211
|
return null;
|
|
@@ -228,6 +239,7 @@ export const DataJunctionAPI = {
|
|
|
228
239
|
cube_node_metrics: cubeMetrics,
|
|
229
240
|
cube_node_dimensions: cubeDimensions,
|
|
230
241
|
cubeMaterialization, // Included so we don't need a second fetch
|
|
242
|
+
availability: current.availability || null,
|
|
231
243
|
};
|
|
232
244
|
} catch (err) {
|
|
233
245
|
console.error('Failed to fetch cube via GraphQL:', err);
|
|
@@ -1169,15 +1181,26 @@ export const DataJunctionAPI = {
|
|
|
1169
1181
|
).json();
|
|
1170
1182
|
},
|
|
1171
1183
|
|
|
1172
|
-
data: async function (metricSelection, dimensionSelection) {
|
|
1184
|
+
data: async function (metricSelection, dimensionSelection, filters = []) {
|
|
1173
1185
|
const params = new URLSearchParams();
|
|
1174
1186
|
metricSelection.map(metric => params.append('metrics', metric));
|
|
1175
1187
|
dimensionSelection.map(dimension => params.append('dimensions', dimension));
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1188
|
+
if (filters && filters.length > 0) {
|
|
1189
|
+
filters.forEach(f => params.append('filters', f));
|
|
1190
|
+
}
|
|
1191
|
+
params.append('limit', '10000');
|
|
1192
|
+
const response = await fetch(`${DJ_URL}/data/?${params}`, {
|
|
1193
|
+
credentials: 'include',
|
|
1194
|
+
});
|
|
1195
|
+
if (!response.ok) {
|
|
1196
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1197
|
+
throw new Error(
|
|
1198
|
+
errorData.message ||
|
|
1199
|
+
errorData.detail ||
|
|
1200
|
+
`Query failed: ${response.status}`,
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
return await response.json();
|
|
1181
1204
|
},
|
|
1182
1205
|
|
|
1183
1206
|
nodeData: async function (nodeName, selection = null) {
|
|
@@ -2278,6 +2278,7 @@ describe('DataJunctionAPI', () => {
|
|
|
2278
2278
|
timestampColumn: 'ts',
|
|
2279
2279
|
timestampFormat: 'yyyy-MM-dd',
|
|
2280
2280
|
},
|
|
2281
|
+
availability: null,
|
|
2281
2282
|
});
|
|
2282
2283
|
});
|
|
2283
2284
|
|
|
@@ -2773,4 +2774,166 @@ describe('DataJunctionAPI', () => {
|
|
|
2773
2774
|
expect(result._error).toBe(true);
|
|
2774
2775
|
expect(result.message).toBeDefined();
|
|
2775
2776
|
});
|
|
2777
|
+
|
|
2778
|
+
// Namespace sources tests
|
|
2779
|
+
it('calls namespaceSources correctly', async () => {
|
|
2780
|
+
fetch.mockResponseOnce(
|
|
2781
|
+
JSON.stringify({ primary_source: { type: 'git', repository: 'repo' } }),
|
|
2782
|
+
);
|
|
2783
|
+
const result = await DataJunctionAPI.namespaceSources('default');
|
|
2784
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2785
|
+
'http://localhost:8000/namespaces/default/sources',
|
|
2786
|
+
{ credentials: 'include' },
|
|
2787
|
+
);
|
|
2788
|
+
expect(result.primary_source.type).toBe('git');
|
|
2789
|
+
});
|
|
2790
|
+
|
|
2791
|
+
it('calls namespaceSourcesBulk correctly', async () => {
|
|
2792
|
+
fetch.mockResponseOnce(
|
|
2793
|
+
JSON.stringify({
|
|
2794
|
+
default: { primary_source: { type: 'git' } },
|
|
2795
|
+
other: { primary_source: { type: 'local' } },
|
|
2796
|
+
}),
|
|
2797
|
+
);
|
|
2798
|
+
const result = await DataJunctionAPI.namespaceSourcesBulk([
|
|
2799
|
+
'default',
|
|
2800
|
+
'other',
|
|
2801
|
+
]);
|
|
2802
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2803
|
+
'http://localhost:8000/namespaces/sources/bulk',
|
|
2804
|
+
{
|
|
2805
|
+
method: 'POST',
|
|
2806
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2807
|
+
body: JSON.stringify({ namespaces: ['default', 'other'] }),
|
|
2808
|
+
credentials: 'include',
|
|
2809
|
+
},
|
|
2810
|
+
);
|
|
2811
|
+
});
|
|
2812
|
+
|
|
2813
|
+
it('calls listDeployments correctly', async () => {
|
|
2814
|
+
fetch.mockResponseOnce(
|
|
2815
|
+
JSON.stringify([{ uuid: '123', status: 'success' }]),
|
|
2816
|
+
);
|
|
2817
|
+
const result = await DataJunctionAPI.listDeployments('default', 10);
|
|
2818
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2819
|
+
'http://localhost:8000/deployments?namespace=default&limit=10',
|
|
2820
|
+
{ credentials: 'include' },
|
|
2821
|
+
);
|
|
2822
|
+
});
|
|
2823
|
+
|
|
2824
|
+
it('calls listDeployments without namespace', async () => {
|
|
2825
|
+
fetch.mockResponseOnce(JSON.stringify([]));
|
|
2826
|
+
await DataJunctionAPI.listDeployments(null, 5);
|
|
2827
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2828
|
+
'http://localhost:8000/deployments?limit=5',
|
|
2829
|
+
{ credentials: 'include' },
|
|
2830
|
+
);
|
|
2831
|
+
});
|
|
2832
|
+
|
|
2833
|
+
// Notification/history tests
|
|
2834
|
+
it('calls getSubscribedHistory correctly', async () => {
|
|
2835
|
+
fetch.mockResponseOnce(
|
|
2836
|
+
JSON.stringify([{ id: 1, activity_type: 'create' }]),
|
|
2837
|
+
);
|
|
2838
|
+
const result = await DataJunctionAPI.getSubscribedHistory(20);
|
|
2839
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2840
|
+
'http://localhost:8000/history/?only_subscribed=true&limit=20',
|
|
2841
|
+
{ credentials: 'include' },
|
|
2842
|
+
);
|
|
2843
|
+
});
|
|
2844
|
+
|
|
2845
|
+
it('calls getSubscribedHistory with default limit', async () => {
|
|
2846
|
+
fetch.mockResponseOnce(JSON.stringify([]));
|
|
2847
|
+
await DataJunctionAPI.getSubscribedHistory();
|
|
2848
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2849
|
+
'http://localhost:8000/history/?only_subscribed=true&limit=10',
|
|
2850
|
+
{ credentials: 'include' },
|
|
2851
|
+
);
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2854
|
+
it('calls markNotificationsRead correctly', async () => {
|
|
2855
|
+
fetch.mockResponseOnce(JSON.stringify({ marked: 5 }));
|
|
2856
|
+
const result = await DataJunctionAPI.markNotificationsRead();
|
|
2857
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2858
|
+
'http://localhost:8000/notifications/mark-read',
|
|
2859
|
+
{ method: 'POST', credentials: 'include' },
|
|
2860
|
+
);
|
|
2861
|
+
expect(result.marked).toBe(5);
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
// Service account tests
|
|
2865
|
+
it('calls listServiceAccounts correctly', async () => {
|
|
2866
|
+
fetch.mockResponseOnce(
|
|
2867
|
+
JSON.stringify([{ client_id: 'abc', name: 'test-account' }]),
|
|
2868
|
+
);
|
|
2869
|
+
const result = await DataJunctionAPI.listServiceAccounts();
|
|
2870
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2871
|
+
'http://localhost:8000/service-accounts',
|
|
2872
|
+
{ credentials: 'include' },
|
|
2873
|
+
);
|
|
2874
|
+
expect(result[0].name).toBe('test-account');
|
|
2875
|
+
});
|
|
2876
|
+
|
|
2877
|
+
it('calls createServiceAccount correctly', async () => {
|
|
2878
|
+
fetch.mockResponseOnce(
|
|
2879
|
+
JSON.stringify({ client_id: 'new-id', name: 'new-account' }),
|
|
2880
|
+
);
|
|
2881
|
+
const result = await DataJunctionAPI.createServiceAccount('new-account');
|
|
2882
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2883
|
+
'http://localhost:8000/service-accounts',
|
|
2884
|
+
{
|
|
2885
|
+
method: 'POST',
|
|
2886
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2887
|
+
credentials: 'include',
|
|
2888
|
+
body: JSON.stringify({ name: 'new-account' }),
|
|
2889
|
+
},
|
|
2890
|
+
);
|
|
2891
|
+
expect(result.client_id).toBe('new-id');
|
|
2892
|
+
});
|
|
2893
|
+
|
|
2894
|
+
it('calls deleteServiceAccount correctly', async () => {
|
|
2895
|
+
fetch.mockResponseOnce(JSON.stringify({ deleted: true }));
|
|
2896
|
+
const result = await DataJunctionAPI.deleteServiceAccount('client-123');
|
|
2897
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2898
|
+
'http://localhost:8000/service-accounts/client-123',
|
|
2899
|
+
{ method: 'DELETE', credentials: 'include' },
|
|
2900
|
+
);
|
|
2901
|
+
expect(result.deleted).toBe(true);
|
|
2902
|
+
});
|
|
2903
|
+
|
|
2904
|
+
// Bulk deactivate preagg workflows tests
|
|
2905
|
+
it('calls bulkDeactivatePreaggWorkflows correctly', async () => {
|
|
2906
|
+
fetch.mockResponseOnce(JSON.stringify({ deactivated: 3 }));
|
|
2907
|
+
const result = await DataJunctionAPI.bulkDeactivatePreaggWorkflows(
|
|
2908
|
+
'default.metric1',
|
|
2909
|
+
);
|
|
2910
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2911
|
+
'http://localhost:8000/preaggs/workflows?node_name=default.metric1',
|
|
2912
|
+
{ method: 'DELETE', credentials: 'include' },
|
|
2913
|
+
);
|
|
2914
|
+
expect(result.deactivated).toBe(3);
|
|
2915
|
+
});
|
|
2916
|
+
|
|
2917
|
+
it('calls bulkDeactivatePreaggWorkflows with staleOnly', async () => {
|
|
2918
|
+
fetch.mockResponseOnce(JSON.stringify({ deactivated: 1 }));
|
|
2919
|
+
const result = await DataJunctionAPI.bulkDeactivatePreaggWorkflows(
|
|
2920
|
+
'default.metric1',
|
|
2921
|
+
true,
|
|
2922
|
+
);
|
|
2923
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
2924
|
+
'http://localhost:8000/preaggs/workflows?node_name=default.metric1&stale_only=true',
|
|
2925
|
+
{ method: 'DELETE', credentials: 'include' },
|
|
2926
|
+
);
|
|
2927
|
+
});
|
|
2928
|
+
|
|
2929
|
+
it('handles bulkDeactivatePreaggWorkflows error', async () => {
|
|
2930
|
+
fetch.mockResponseOnce(JSON.stringify({ detail: 'Not found' }), {
|
|
2931
|
+
status: 404,
|
|
2932
|
+
});
|
|
2933
|
+
const result = await DataJunctionAPI.bulkDeactivatePreaggWorkflows(
|
|
2934
|
+
'default.metric1',
|
|
2935
|
+
);
|
|
2936
|
+
expect(result._error).toBe(true);
|
|
2937
|
+
expect(result._status).toBe(404);
|
|
2938
|
+
});
|
|
2776
2939
|
});
|