@gen3/core 0.10.80 → 0.10.81
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/dist/cjs/index.js +677 -444
- package/dist/cjs/index.js.map +1 -1
- package/dist/dts/constants.d.ts.map +1 -1
- package/dist/dts/dataAccess.d.ts.map +1 -1
- package/dist/dts/features/cohort/cohortSlice.d.ts.map +1 -1
- package/dist/dts/features/cohort/filterCombineModeSlice.d.ts +12 -0
- package/dist/dts/features/cohort/filterCombineModeSlice.d.ts.map +1 -0
- package/dist/dts/features/cohort/filterExpandSlice.d.ts +15 -0
- package/dist/dts/features/cohort/filterExpandSlice.d.ts.map +1 -0
- package/dist/dts/features/cohort/index.d.ts +6 -2
- package/dist/dts/features/cohort/index.d.ts.map +1 -1
- package/dist/dts/features/cohort/reducers.d.ts +20 -0
- package/dist/dts/features/cohort/reducers.d.ts.map +1 -0
- package/dist/dts/features/cohort/sharedFiltersSlice.d.ts +17 -0
- package/dist/dts/features/cohort/sharedFiltersSlice.d.ts.map +1 -0
- package/dist/dts/features/cohort/types.d.ts +1 -0
- package/dist/dts/features/cohort/types.d.ts.map +1 -1
- package/dist/dts/features/dataLibrary/dataLibraryIndexDB.d.ts.map +1 -1
- package/dist/dts/features/dataLibrary/dataLibrarySelectionSlice.d.ts.map +1 -1
- package/dist/dts/features/dataLibrary/types.d.ts.map +1 -1
- package/dist/dts/features/dataLibrary/useDataLibrary.d.ts.map +1 -1
- package/dist/dts/features/dataLibrary/utils.d.ts.map +1 -1
- package/dist/dts/features/drsResolver/drsHostnameSlice.d.ts.map +1 -1
- package/dist/dts/features/drsResolver/resolvers/cachedDRSResolver.d.ts.map +1 -1
- package/dist/dts/features/drsResolver/resolvers/dataGUIDSDotOrg.d.ts.map +1 -1
- package/dist/dts/features/drsResolver/utils.d.ts.map +1 -1
- package/dist/dts/features/fence/credentialsApi.d.ts.map +1 -1
- package/dist/dts/features/fence/fenceApi.d.ts.map +1 -1
- package/dist/dts/features/fence/index.d.ts +2 -2
- package/dist/dts/features/fence/index.d.ts.map +1 -1
- package/dist/dts/features/fence/utils.d.ts +1 -0
- package/dist/dts/features/fence/utils.d.ts.map +1 -1
- package/dist/dts/features/filters/filters.d.ts +2 -0
- package/dist/dts/features/filters/filters.d.ts.map +1 -1
- package/dist/dts/features/filters/index.d.ts +2 -2
- package/dist/dts/features/filters/index.d.ts.map +1 -1
- package/dist/dts/features/filters/types.d.ts +4 -0
- package/dist/dts/features/filters/types.d.ts.map +1 -1
- package/dist/dts/features/filters/utils.d.ts.map +1 -1
- package/dist/dts/features/gen3/index.d.ts.map +1 -1
- package/dist/dts/features/gen3Apps/Gen3App.d.ts.map +1 -1
- package/dist/dts/features/gen3Apps/Gen3AppRTKQ.d.ts.map +1 -1
- package/dist/dts/features/gen3Apps/gen3AppRegistry.d.ts.map +1 -1
- package/dist/dts/features/gen3Apps/gen3AppsSlice.d.ts.map +1 -1
- package/dist/dts/features/guppy/guppySlice.d.ts +373 -0
- package/dist/dts/features/guppy/guppySlice.d.ts.map +1 -1
- package/dist/dts/features/guppy/index.d.ts +2 -2
- package/dist/dts/features/guppy/index.d.ts.map +1 -1
- package/dist/dts/features/guppy/tests/grouping.unit.test.d.ts +2 -0
- package/dist/dts/features/guppy/tests/grouping.unit.test.d.ts.map +1 -0
- package/dist/dts/features/guppy/types.d.ts +6 -0
- package/dist/dts/features/guppy/types.d.ts.map +1 -1
- package/dist/dts/features/guppy/utils.d.ts +2 -1
- package/dist/dts/features/guppy/utils.d.ts.map +1 -1
- package/dist/dts/features/modals/modalsSlice.d.ts.map +1 -1
- package/dist/dts/features/submission/authMappingUtils.d.ts.map +1 -1
- package/dist/dts/features/user/hooks.d.ts.map +1 -1
- package/dist/dts/features/user/userSlice.d.ts.map +1 -1
- package/dist/dts/features/user/userSliceRTK.d.ts +30 -3
- package/dist/dts/features/user/userSliceRTK.d.ts.map +1 -1
- package/dist/dts/features/user/utils.d.ts.map +1 -1
- package/dist/dts/features/workspace/utils.d.ts.map +1 -1
- package/dist/dts/features/workspace/workspaceSlice.d.ts.map +1 -1
- package/dist/dts/hooks.d.ts +20 -2
- package/dist/dts/hooks.d.ts.map +1 -1
- package/dist/dts/reducers.d.ts +29 -2
- package/dist/dts/reducers.d.ts.map +1 -1
- package/dist/dts/store.d.ts +40 -4
- package/dist/dts/store.d.ts.map +1 -1
- package/dist/dts/types/index.d.ts.map +1 -1
- package/dist/dts/utils/extractvalues.d.ts.map +1 -1
- package/dist/dts/utils/fetch.d.ts +21 -2
- package/dist/dts/utils/fetch.d.ts.map +1 -1
- package/dist/dts/utils/index.d.ts +2 -2
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/dts/utils/time.d.ts.map +1 -1
- package/dist/dts/utils/ts-utils.d.ts.map +1 -1
- package/dist/dts/utils/url.d.ts.map +1 -1
- package/dist/esm/index.js +660 -444
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +5200 -4656
- package/package.json +2 -3
- package/LICENSE +0 -201
- package/dist/dts/features/cohort/cohortBuilderConfigSlice.d.ts +0 -1
- package/dist/dts/features/cohort/cohortBuilderConfigSlice.d.ts.map +0 -1
package/dist/cjs/index.js
CHANGED
|
@@ -46,7 +46,7 @@ const GEN3_DOMAIN = process.env.NEXT_PUBLIC_GEN3_DOMAIN || '';
|
|
|
46
46
|
*/ const GEN3_GUPPY_API = process.env.NEXT_PUBLIC_GEN3_GUPPY_API || `${GEN3_API}/guppy`;
|
|
47
47
|
const GEN3_MDS_API = process.env.NEXT_PUBLIC_GEN3_MDS_API || `${GEN3_API}/mds`;
|
|
48
48
|
const GEN3_DOWNLOADS_ENDPOINT = process.env.NEXT_PUBLIC_GEN3_DOWNLOADS_ENDPOINT || 'downloads';
|
|
49
|
-
const GEN3_FENCE_API = process.env.NEXT_PUBLIC_GEN3_FENCE_API || GEN3_API
|
|
49
|
+
const GEN3_FENCE_API = process.env.NEXT_PUBLIC_GEN3_FENCE_API || `${GEN3_API}/user`;
|
|
50
50
|
const GEN3_AI_SEARCH_API = process.env.NEXT_PUBLIC_GEN3_AI_SEARCH_API || `${GEN3_API}/ai-search`;
|
|
51
51
|
const GEN3_AUTHZ_API = process.env.NEXT_PUBLIC_GEN3_AUTHZ_API || `${GEN3_API}/authz`;
|
|
52
52
|
const GEN3_REDIRECT_URL = process.env.NEXT_PUBLIC_GEN3_REDIRECT_URL || GEN3_API;
|
|
@@ -67,6 +67,13 @@ const FILE_DELIMITERS = {
|
|
|
67
67
|
csv: ','
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
const isFetchError = (obj)=>{
|
|
71
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const { url, status, statusText, text } = obj;
|
|
75
|
+
return typeof url === 'string' && typeof status === 'number' && typeof statusText === 'string' && typeof text === 'string';
|
|
76
|
+
};
|
|
70
77
|
/**
|
|
71
78
|
* Template for fence error response dict
|
|
72
79
|
* @returns: An error dict response from a RESTFUL API request
|
|
@@ -143,7 +150,7 @@ const userAuthApi = react.createApi({
|
|
|
143
150
|
endpoints: (builder)=>({
|
|
144
151
|
fetchUserDetails: builder.query({
|
|
145
152
|
query: ()=>({
|
|
146
|
-
endpoint: '/user
|
|
153
|
+
endpoint: '/user'
|
|
147
154
|
}),
|
|
148
155
|
transformResponse (response) {
|
|
149
156
|
return {
|
|
@@ -154,12 +161,33 @@ const userAuthApi = react.createApi({
|
|
|
154
161
|
}
|
|
155
162
|
}),
|
|
156
163
|
getCSRF: builder.query({
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
queryFn: async ()=>{
|
|
165
|
+
const headers = {
|
|
166
|
+
Accept: 'application/json',
|
|
167
|
+
'Content-Type': 'application/json'
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
const res = await fetch(`${GEN3_API}/_status`, {
|
|
171
|
+
headers: headers
|
|
172
|
+
});
|
|
173
|
+
if (res.ok) {
|
|
174
|
+
const jsonData = await res.json();
|
|
175
|
+
const token = jsonData?.data?.csrf ?? '';
|
|
176
|
+
return {
|
|
177
|
+
data: {
|
|
178
|
+
csrfToken: token
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (error instanceof Error) {
|
|
184
|
+
return {
|
|
185
|
+
error: error
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
161
189
|
return {
|
|
162
|
-
|
|
190
|
+
error: 'Unknown Error'
|
|
163
191
|
};
|
|
164
192
|
}
|
|
165
193
|
})
|
|
@@ -227,13 +255,13 @@ const gen3ServicesReducerMiddleware = gen3Api.middleware;
|
|
|
227
255
|
*/ const loginProvidersApi = gen3Api.injectEndpoints({
|
|
228
256
|
endpoints: (builder)=>({
|
|
229
257
|
getLoginProviders: builder.query({
|
|
230
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
258
|
+
query: ()=>`${GEN3_FENCE_API}/login`
|
|
231
259
|
}),
|
|
232
260
|
getDownload: builder.query({
|
|
233
|
-
query: (guid)=>`${GEN3_FENCE_API}/
|
|
261
|
+
query: (guid)=>`${GEN3_FENCE_API}/data/download/${guid}`
|
|
234
262
|
}),
|
|
235
263
|
getPresignedUrl: builder.query({
|
|
236
|
-
query: ({ guid, what })=>`${GEN3_FENCE_API}/
|
|
264
|
+
query: ({ guid, what })=>`${GEN3_FENCE_API}/data/${what}/${guid}`
|
|
237
265
|
})
|
|
238
266
|
})
|
|
239
267
|
});
|
|
@@ -241,7 +269,7 @@ const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery,
|
|
|
241
269
|
/**
|
|
242
270
|
* Logout from fence
|
|
243
271
|
*/ const logoutFence = async (redirect = '/')=>await fetchFence({
|
|
244
|
-
endpoint: `${GEN3_FENCE_API}/
|
|
272
|
+
endpoint: `${GEN3_FENCE_API}/logout?next=${GEN3_REDIRECT_URL}${redirect}`,
|
|
245
273
|
method: 'GET'
|
|
246
274
|
});
|
|
247
275
|
|
|
@@ -262,7 +290,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
262
290
|
*/ const credentialsApi = credentialsWithTags$1.injectEndpoints({
|
|
263
291
|
endpoints: (builder)=>({
|
|
264
292
|
getCredentials: builder.query({
|
|
265
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
293
|
+
query: ()=>`${GEN3_FENCE_API}/credentials/api`,
|
|
266
294
|
transformResponse: (response)=>response['jtis'],
|
|
267
295
|
// "jtis", which is an array of API keys
|
|
268
296
|
// no need to transform the response, since the API returns the correct format
|
|
@@ -272,7 +300,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
272
300
|
}),
|
|
273
301
|
addNewCredential: builder.mutation({
|
|
274
302
|
query: (csrfToken)=>({
|
|
275
|
-
url: `${GEN3_FENCE_API}/
|
|
303
|
+
url: `${GEN3_FENCE_API}/credentials/api`,
|
|
276
304
|
method: 'POST',
|
|
277
305
|
headers: {
|
|
278
306
|
'Content-Type': 'application/json',
|
|
@@ -291,7 +319,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
291
319
|
}),
|
|
292
320
|
removeCredential: builder.mutation({
|
|
293
321
|
query: ({ csrfToken, id })=>({
|
|
294
|
-
url: `${GEN3_FENCE_API}/
|
|
322
|
+
url: `${GEN3_FENCE_API}/credentials/api/${id}`,
|
|
295
323
|
method: 'DELETE',
|
|
296
324
|
headers: {
|
|
297
325
|
'Content-Type': 'application/json',
|
|
@@ -306,7 +334,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
306
334
|
}),
|
|
307
335
|
authorizeFromCredentials: builder.mutation({
|
|
308
336
|
query: (params)=>({
|
|
309
|
-
url: `${GEN3_FENCE_API}/
|
|
337
|
+
url: `${GEN3_FENCE_API}/credentials/api/access_token`,
|
|
310
338
|
method: 'POST',
|
|
311
339
|
headers: {
|
|
312
340
|
'Content-Type': 'application/json'
|
|
@@ -404,7 +432,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
404
432
|
accessToken = cookiesNext.getCookie('credentials_token');
|
|
405
433
|
}
|
|
406
434
|
return await fetchFence({
|
|
407
|
-
endpoint: '/user
|
|
435
|
+
endpoint: '/user',
|
|
408
436
|
method: 'GET',
|
|
409
437
|
headers: {
|
|
410
438
|
Accept: 'application/json',
|
|
@@ -421,7 +449,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
421
449
|
});
|
|
422
450
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
423
451
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
424
|
-
const initialState$
|
|
452
|
+
const initialState$8 = {
|
|
425
453
|
status: 'uninitialized',
|
|
426
454
|
loginStatus: 'unauthenticated',
|
|
427
455
|
error: undefined
|
|
@@ -432,9 +460,9 @@ const initialState$5 = {
|
|
|
432
460
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
433
461
|
*/ const slice$4 = toolkit.createSlice({
|
|
434
462
|
name: 'fence/user',
|
|
435
|
-
initialState: initialState$
|
|
463
|
+
initialState: initialState$8,
|
|
436
464
|
reducers: {
|
|
437
|
-
resetUserState: ()=>initialState$
|
|
465
|
+
resetUserState: ()=>initialState$8
|
|
438
466
|
},
|
|
439
467
|
extraReducers: (builder)=>{
|
|
440
468
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -604,12 +632,12 @@ const lookupGen3App = (id)=>{
|
|
|
604
632
|
return REGISTRY[id];
|
|
605
633
|
};
|
|
606
634
|
|
|
607
|
-
const initialState$
|
|
635
|
+
const initialState$7 = {
|
|
608
636
|
gen3Apps: {}
|
|
609
637
|
};
|
|
610
638
|
const slice$3 = toolkit.createSlice({
|
|
611
639
|
name: 'gen3Apps',
|
|
612
|
-
initialState: initialState$
|
|
640
|
+
initialState: initialState$7,
|
|
613
641
|
reducers: {
|
|
614
642
|
addGen3AppMetadata: (state, action)=>{
|
|
615
643
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -628,11 +656,11 @@ const { addGen3AppMetadata } = slice$3.actions;
|
|
|
628
656
|
const selectGen3AppMetadataByName = (state, appName)=>state.gen3Apps.gen3Apps[appName];
|
|
629
657
|
const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize this selector
|
|
630
658
|
|
|
631
|
-
const initialState$
|
|
659
|
+
const initialState$6 = {};
|
|
632
660
|
// TODO: document what this does
|
|
633
661
|
const slice$2 = toolkit.createSlice({
|
|
634
662
|
name: 'drsResolver',
|
|
635
|
-
initialState: initialState$
|
|
663
|
+
initialState: initialState$6,
|
|
636
664
|
reducers: {
|
|
637
665
|
setDRSHostnames: (_state, action)=>{
|
|
638
666
|
return action.payload;
|
|
@@ -654,13 +682,13 @@ const { setDRSHostnames } = slice$2.actions;
|
|
|
654
682
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
655
683
|
return Modals;
|
|
656
684
|
}({});
|
|
657
|
-
const initialState$
|
|
685
|
+
const initialState$5 = {
|
|
658
686
|
currentModal: null
|
|
659
687
|
};
|
|
660
688
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
661
689
|
const slice$1 = toolkit.createSlice({
|
|
662
690
|
name: 'modals',
|
|
663
|
-
initialState: initialState$
|
|
691
|
+
initialState: initialState$5,
|
|
664
692
|
reducers: {
|
|
665
693
|
showModal: (state, action)=>{
|
|
666
694
|
state.currentModal = action.payload.modal;
|
|
@@ -678,141 +706,6 @@ const { showModal, hideModal } = slice$1.actions;
|
|
|
678
706
|
const selectCurrentModal = (state)=>state.modals.currentModal;
|
|
679
707
|
const selectCurrentMessage = (state)=>state.modals.message;
|
|
680
708
|
|
|
681
|
-
const EmptyCohort = {
|
|
682
|
-
id: 'default',
|
|
683
|
-
name: 'Filters',
|
|
684
|
-
filters: {},
|
|
685
|
-
modified_datetime: new Date().toISOString()
|
|
686
|
-
};
|
|
687
|
-
const initialCohortState = {
|
|
688
|
-
cohort: {
|
|
689
|
-
...EmptyCohort
|
|
690
|
-
}
|
|
691
|
-
};
|
|
692
|
-
// TODO: start using this adapter
|
|
693
|
-
/*
|
|
694
|
-
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
695
|
-
sortComparer: (a, b) => {
|
|
696
|
-
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
697
|
-
else return -1;
|
|
698
|
-
},
|
|
699
|
-
});
|
|
700
|
-
*/ /**
|
|
701
|
-
* Redux slice for cohort filters
|
|
702
|
-
*/ const cohortSlice = toolkit.createSlice({
|
|
703
|
-
name: 'cohort',
|
|
704
|
-
initialState: initialCohortState,
|
|
705
|
-
reducers: {
|
|
706
|
-
// adds a filter to the cohort filter set at the given index
|
|
707
|
-
updateCohortFilter: (state, action)=>{
|
|
708
|
-
const { index, field, filter } = action.payload;
|
|
709
|
-
return {
|
|
710
|
-
cohort: {
|
|
711
|
-
...state.cohort,
|
|
712
|
-
filters: {
|
|
713
|
-
...state.cohort.filters,
|
|
714
|
-
[index]: {
|
|
715
|
-
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
716
|
-
root: {
|
|
717
|
-
...state.cohort.filters?.[index]?.root ?? {},
|
|
718
|
-
[field]: filter
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
};
|
|
724
|
-
},
|
|
725
|
-
setCohortFilter: (state, action)=>{
|
|
726
|
-
const { index, filters } = action.payload;
|
|
727
|
-
return {
|
|
728
|
-
cohort: {
|
|
729
|
-
...state.cohort,
|
|
730
|
-
filters: {
|
|
731
|
-
...state.cohort.filters,
|
|
732
|
-
[index]: filters
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
};
|
|
736
|
-
},
|
|
737
|
-
setCohortIndexFilters: (state, action)=>{
|
|
738
|
-
return {
|
|
739
|
-
cohort: {
|
|
740
|
-
...state.cohort,
|
|
741
|
-
filters: {
|
|
742
|
-
...action.payload.filters
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
},
|
|
747
|
-
// removes a filter to the cohort filter set at the given index
|
|
748
|
-
removeCohortFilter: (state, action)=>{
|
|
749
|
-
const { index, field } = action.payload;
|
|
750
|
-
const filters = state.cohort?.filters?.[index]?.root;
|
|
751
|
-
if (!filters) {
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
const { [field]: _a, ...updated } = filters;
|
|
755
|
-
return {
|
|
756
|
-
cohort: {
|
|
757
|
-
...state.cohort,
|
|
758
|
-
filters: {
|
|
759
|
-
...state.cohort.filters,
|
|
760
|
-
[index]: {
|
|
761
|
-
mode: state.cohort.filters[index].mode,
|
|
762
|
-
root: updated
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
};
|
|
767
|
-
},
|
|
768
|
-
// removes all filters from the cohort filter set at the given index
|
|
769
|
-
clearCohortFilters: (state, action)=>{
|
|
770
|
-
const { index } = action.payload;
|
|
771
|
-
return {
|
|
772
|
-
cohort: {
|
|
773
|
-
...state.cohort,
|
|
774
|
-
filters: {
|
|
775
|
-
...state.cohort.filters,
|
|
776
|
-
[index]: {
|
|
777
|
-
// empty filter set
|
|
778
|
-
mode: 'and',
|
|
779
|
-
root: {}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
});
|
|
787
|
-
// Filter actions: addFilter, removeFilter, updateFilter
|
|
788
|
-
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
789
|
-
const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
|
|
790
|
-
const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
|
|
791
|
-
const selectCurrentCohort = (state)=>state.cohorts.cohort;
|
|
792
|
-
const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
|
|
793
|
-
/**
|
|
794
|
-
* Select a filter by its name from the current cohort. If the filter is not found
|
|
795
|
-
* returns undefined.
|
|
796
|
-
* @param state - Core
|
|
797
|
-
* @param index which cohort index to select from
|
|
798
|
-
* @param name name of the filter to select
|
|
799
|
-
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
800
|
-
return state.cohorts.cohort.filters[index]?.root[name];
|
|
801
|
-
};
|
|
802
|
-
const EmptyFilterSet = {
|
|
803
|
-
mode: 'and',
|
|
804
|
-
root: {}
|
|
805
|
-
};
|
|
806
|
-
/**
|
|
807
|
-
* Select a filter from the index.
|
|
808
|
-
* returns undefined.
|
|
809
|
-
* @param state - Core
|
|
810
|
-
* @param index which cohort index to select from
|
|
811
|
-
*/ const selectIndexFilters = (state, index)=>{
|
|
812
|
-
return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
813
|
-
};
|
|
814
|
-
const cohortReducer = cohortSlice.reducer;
|
|
815
|
-
|
|
816
709
|
const isFileItem = (item)=>{
|
|
817
710
|
return item && 'guid' in item;
|
|
818
711
|
};
|
|
@@ -1370,17 +1263,17 @@ const useDataLibrary = (useApi)=>{
|
|
|
1370
1263
|
};
|
|
1371
1264
|
};
|
|
1372
1265
|
|
|
1373
|
-
const initialState$
|
|
1266
|
+
const initialState$4 = {};
|
|
1374
1267
|
const dataLibrarySlice = toolkit.createSlice({
|
|
1375
1268
|
name: 'dataLibrary',
|
|
1376
|
-
initialState: initialState$
|
|
1269
|
+
initialState: initialState$4,
|
|
1377
1270
|
reducers: {
|
|
1378
1271
|
setDataLibraryListSelection: (state, action)=>{
|
|
1379
1272
|
const { listId, itemIds } = action.payload;
|
|
1380
1273
|
state[listId] = itemIds;
|
|
1381
1274
|
},
|
|
1382
1275
|
clearDataLibrarySelection: ()=>{
|
|
1383
|
-
return initialState$
|
|
1276
|
+
return initialState$4;
|
|
1384
1277
|
}
|
|
1385
1278
|
}
|
|
1386
1279
|
});
|
|
@@ -1446,7 +1339,7 @@ const isTimeGreaterThan = (startTime, minutes)=>{
|
|
|
1446
1339
|
};
|
|
1447
1340
|
|
|
1448
1341
|
const NO_WORKSPACE_ID = 'none';
|
|
1449
|
-
const initialState = {
|
|
1342
|
+
const initialState$3 = {
|
|
1450
1343
|
id: NO_WORKSPACE_ID,
|
|
1451
1344
|
status: WorkspaceStatus.NotFound,
|
|
1452
1345
|
requestedStatus: RequestedWorkspaceStatus.Unset,
|
|
@@ -1454,7 +1347,7 @@ const initialState = {
|
|
|
1454
1347
|
};
|
|
1455
1348
|
const slice = toolkit.createSlice({
|
|
1456
1349
|
name: 'ActiveWorkspace',
|
|
1457
|
-
initialState,
|
|
1350
|
+
initialState: initialState$3,
|
|
1458
1351
|
reducers: {
|
|
1459
1352
|
setActiveWorkspaceId: (state, action)=>{
|
|
1460
1353
|
state = {
|
|
@@ -1548,6 +1441,14 @@ const guppyApiReducer = guppyApi.reducer;
|
|
|
1548
1441
|
const isOperationWithField = (operation)=>{
|
|
1549
1442
|
return operation?.field !== undefined;
|
|
1550
1443
|
};
|
|
1444
|
+
const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
|
|
1445
|
+
if (typeof operation === 'object' && operation !== null && 'operands' in operation && Array.isArray(operation.operands) && 'field' in operation && typeof operation.field === 'string' // Assuming `field` should be a string
|
|
1446
|
+
) {
|
|
1447
|
+
const { operator } = operation.operator;
|
|
1448
|
+
return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
|
|
1449
|
+
}
|
|
1450
|
+
return false;
|
|
1451
|
+
};
|
|
1551
1452
|
const extractFilterValue = (op)=>{
|
|
1552
1453
|
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1553
1454
|
return handleOperation(valueExtractorHandler, op);
|
|
@@ -1729,6 +1630,12 @@ const isFilterSet = (input)=>{
|
|
|
1729
1630
|
}
|
|
1730
1631
|
return true;
|
|
1731
1632
|
};
|
|
1633
|
+
function isUnion(value) {
|
|
1634
|
+
return typeof value === 'object' && value !== null && value.operator === 'or' && Array.isArray(value.operands);
|
|
1635
|
+
}
|
|
1636
|
+
function isIntersection(value) {
|
|
1637
|
+
return typeof value === 'object' && value !== null && value.operator === 'and' && Array.isArray(value.operands);
|
|
1638
|
+
}
|
|
1732
1639
|
|
|
1733
1640
|
const FieldNameOverrides = {};
|
|
1734
1641
|
const COMMON_PREPOSITIONS = [
|
|
@@ -1793,67 +1700,250 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
|
1793
1700
|
];
|
|
1794
1701
|
};
|
|
1795
1702
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
json
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
});
|
|
1808
|
-
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1809
|
-
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1810
|
-
return {
|
|
1811
|
-
...acc,
|
|
1812
|
-
[key]: valueData[idx]
|
|
1813
|
-
};
|
|
1814
|
-
}, {});
|
|
1815
|
-
return results;
|
|
1816
|
-
};
|
|
1817
|
-
const fetchJson = async (url)=>{
|
|
1818
|
-
const res = await fetch(url, {
|
|
1819
|
-
method: 'GET',
|
|
1820
|
-
headers: {
|
|
1821
|
-
'Content-type': 'application/json'
|
|
1822
|
-
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Flattens a deep nested JSON object skipping
|
|
1705
|
+
* the first level to avoid potentially flattening
|
|
1706
|
+
* non-nested data.
|
|
1707
|
+
* @param {JSON} json
|
|
1708
|
+
*/ function flattenJson(json) {
|
|
1709
|
+
const flattenedJson = [];
|
|
1710
|
+
Object.keys(json).forEach((key)=>{
|
|
1711
|
+
flattenedJson.push(flat.flatten(json[key], {
|
|
1712
|
+
delimiter: '_'
|
|
1713
|
+
}));
|
|
1823
1714
|
});
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
};
|
|
1827
|
-
const useGetStatus = ()=>{
|
|
1828
|
-
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1829
|
-
return useSWR('explorerStatus', fetcher);
|
|
1830
|
-
};
|
|
1715
|
+
return flattenedJson;
|
|
1716
|
+
}
|
|
1831
1717
|
/**
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1718
|
+
* Converts JSON based on a config.
|
|
1719
|
+
* @param {JSON} json
|
|
1720
|
+
* @param {Object} config
|
|
1721
|
+
*/ async function conversion(json, config) {
|
|
1722
|
+
return Papa.unparse(json, config);
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Converts JSON to a specified file format.
|
|
1726
|
+
* Defaults to JSON if file format is not supported.
|
|
1727
|
+
* @param {JSON} json
|
|
1728
|
+
* @param {string} format
|
|
1729
|
+
*/ async function jsonToFormat(json, format) {
|
|
1730
|
+
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
1731
|
+
const flatJson = await flattenJson(json);
|
|
1732
|
+
const data = await conversion(flatJson, {
|
|
1733
|
+
delimiter: FILE_DELIMITERS[format]
|
|
1734
|
+
});
|
|
1735
|
+
return data;
|
|
1736
|
+
}
|
|
1737
|
+
return json;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
1835
1742
|
*
|
|
1836
|
-
* @param
|
|
1837
|
-
* @
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
-
* @
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1743
|
+
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
1744
|
+
* @returns {URL} - The prepared download URL as a URL object.
|
|
1745
|
+
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
1746
|
+
/**
|
|
1747
|
+
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
1748
|
+
*
|
|
1749
|
+
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
1750
|
+
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
1751
|
+
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
1752
|
+
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
1753
|
+
return {
|
|
1754
|
+
method: 'POST',
|
|
1755
|
+
headers: {
|
|
1756
|
+
'Content-Type': 'application/json',
|
|
1757
|
+
...csrfToken !== undefined && {
|
|
1758
|
+
'X-CSRF-Token': csrfToken
|
|
1759
|
+
}
|
|
1760
|
+
},
|
|
1761
|
+
body: JSON.stringify({
|
|
1762
|
+
type: parameters.type,
|
|
1763
|
+
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
1764
|
+
accessibility: parameters.accessibility,
|
|
1765
|
+
fields: parameters?.fields,
|
|
1766
|
+
sort: parameters?.sort
|
|
1767
|
+
})
|
|
1768
|
+
};
|
|
1769
|
+
};
|
|
1770
|
+
/**
|
|
1771
|
+
* Downloads a file from Guppy using the provided parameters.
|
|
1772
|
+
* It will optionally convert the data to the specified format.
|
|
1773
|
+
*
|
|
1774
|
+
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
1775
|
+
* @param onStart - The function to call when the download starts.
|
|
1776
|
+
* @param onDone - The function to call when the download is done.
|
|
1777
|
+
* @param onError - The function to call when the download fails.
|
|
1778
|
+
* @param onAbort - The function to call when the download is aborted.
|
|
1779
|
+
* @param signal - AbortSignal to use for the request.
|
|
1780
|
+
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
1781
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
1782
|
+
onStart?.();
|
|
1783
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
1784
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
1785
|
+
fetch(url.toString(), {
|
|
1786
|
+
...fetchConfig,
|
|
1787
|
+
...signal ? {
|
|
1788
|
+
signal: signal
|
|
1789
|
+
} : {}
|
|
1790
|
+
}).then(async (response)=>{
|
|
1791
|
+
if (!response.ok) {
|
|
1792
|
+
throw new Error(response.statusText);
|
|
1793
|
+
}
|
|
1794
|
+
let jsonData = await response.json();
|
|
1795
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
1796
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
1797
|
+
jsonData = jsonpathPlus.JSONPath({
|
|
1798
|
+
json: jsonData,
|
|
1799
|
+
path: `$.[${parameters.rootPath}]`,
|
|
1800
|
+
resultType: 'value'
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
// convert the data to the specified format and return a Blob
|
|
1804
|
+
let str = '';
|
|
1805
|
+
if (parameters.format === 'json') {
|
|
1806
|
+
str = JSON.stringify(jsonData);
|
|
1807
|
+
} else {
|
|
1808
|
+
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
1809
|
+
if (isJSONObject(convertedData)) {
|
|
1810
|
+
str = JSON.stringify(convertedData, null, 2);
|
|
1811
|
+
} else {
|
|
1812
|
+
str = convertedData;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
return new Blob([
|
|
1816
|
+
str
|
|
1817
|
+
], {
|
|
1818
|
+
type: 'application/json'
|
|
1819
|
+
});
|
|
1820
|
+
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
1821
|
+
// Abort is handle as an exception
|
|
1822
|
+
if (error.name == 'AbortError') {
|
|
1823
|
+
// handle abort()
|
|
1824
|
+
onAbort?.();
|
|
1825
|
+
}
|
|
1826
|
+
onError?.(error);
|
|
1827
|
+
});
|
|
1828
|
+
};
|
|
1829
|
+
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
1830
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
1831
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
1832
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
1833
|
+
try {
|
|
1834
|
+
const response = await fetch(url.toString(), {
|
|
1835
|
+
...fetchConfig,
|
|
1836
|
+
...signal ? {
|
|
1837
|
+
signal: signal
|
|
1838
|
+
} : {}
|
|
1839
|
+
});
|
|
1840
|
+
let jsonData = await response.json();
|
|
1841
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
1842
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
1843
|
+
jsonData = jsonpathPlus.JSONPath({
|
|
1844
|
+
json: jsonData,
|
|
1845
|
+
path: `$.[${parameters.rootPath}]`,
|
|
1846
|
+
resultType: 'value'
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
// convert the data to the specified format and return a Blob
|
|
1850
|
+
return jsonData;
|
|
1851
|
+
} catch (error) {
|
|
1852
|
+
// Abort is handle as an exception
|
|
1853
|
+
if (error.name == 'AbortError') {
|
|
1854
|
+
// handle abort()
|
|
1855
|
+
onAbort?.();
|
|
1856
|
+
}
|
|
1857
|
+
throw new Error(error);
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
const useGetIndexFields = (index)=>{
|
|
1861
|
+
const { data } = useGetFieldsForIndexQuery(index);
|
|
1862
|
+
return data ?? [];
|
|
1863
|
+
};
|
|
1864
|
+
const groupSharedFields = (data)=>{
|
|
1865
|
+
const reverseIndex = {};
|
|
1866
|
+
// Build reverse index: track which root keys contain each element
|
|
1867
|
+
for(const rootKey in data){
|
|
1868
|
+
data[rootKey].forEach((value)=>{
|
|
1869
|
+
if (!reverseIndex[value]) {
|
|
1870
|
+
reverseIndex[value] = new Set();
|
|
1871
|
+
}
|
|
1872
|
+
reverseIndex[value].add(rootKey);
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
return Object.entries(reverseIndex).reduce((acc, [field, indexSet])=>{
|
|
1876
|
+
if (indexSet.size > 1) {
|
|
1877
|
+
acc[field] = Array.from(indexSet).map((x)=>({
|
|
1878
|
+
index: x,
|
|
1879
|
+
field: field
|
|
1880
|
+
}));
|
|
1881
|
+
}
|
|
1882
|
+
return acc;
|
|
1883
|
+
}, {});
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
const statusEndpoint = '/_status';
|
|
1887
|
+
const processHistogramResponse = (data)=>{
|
|
1888
|
+
const valueData = jsonpathPlus.JSONPath({
|
|
1889
|
+
json: data,
|
|
1890
|
+
path: '$..histogram',
|
|
1891
|
+
resultType: 'value'
|
|
1892
|
+
});
|
|
1893
|
+
const pointerData = jsonpathPlus.JSONPath({
|
|
1894
|
+
json: data,
|
|
1895
|
+
path: '$..histogram',
|
|
1896
|
+
resultType: 'pointer'
|
|
1897
|
+
});
|
|
1898
|
+
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1899
|
+
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1900
|
+
return {
|
|
1901
|
+
...acc,
|
|
1902
|
+
[key]: valueData[idx]
|
|
1903
|
+
};
|
|
1904
|
+
}, {});
|
|
1905
|
+
return results;
|
|
1906
|
+
};
|
|
1907
|
+
const fetchJson = async (url)=>{
|
|
1908
|
+
const res = await fetch(url, {
|
|
1909
|
+
method: 'GET',
|
|
1910
|
+
headers: {
|
|
1911
|
+
'Content-type': 'application/json'
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1914
|
+
if (!res.ok) throw new Error('An error occurred while fetching the data.');
|
|
1915
|
+
return await res.json();
|
|
1916
|
+
};
|
|
1917
|
+
const useGetStatus = ()=>{
|
|
1918
|
+
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1919
|
+
return useSWR('explorerStatus', fetcher);
|
|
1920
|
+
};
|
|
1921
|
+
/**
|
|
1922
|
+
* The main endpoint used in templating Exploration page queries.
|
|
1923
|
+
* Includes table, filter and aggregation query types and leverages guppyApi defined in ./gupplApi.ts
|
|
1924
|
+
* Query templates support filters where applicable
|
|
1925
|
+
*
|
|
1926
|
+
* @param endpoints - Defines endpoints used in Exploration page:
|
|
1927
|
+
* @param getAllFieldsForType - A mapping query that returns all property key names vertex types specified.
|
|
1928
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#mapping-query
|
|
1929
|
+
* @param getAccessibleData - An aggregation histogram counts query that filters based on access type
|
|
1930
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#accessibility-argument-for-regular-tier-access-level
|
|
1931
|
+
* @param getRawDataAndTotalCounts - Queries both _totalCount for selected vertex types and
|
|
1932
|
+
* tabular results containing the raw data in the rows of selected vertex types
|
|
1933
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#1-total-count-aggregation
|
|
1934
|
+
* @param getAggs - An aggregated histogram counts query which outputs vertex property frequencies
|
|
1935
|
+
* @param getSubAggs - TODO: not sure what this one does. Looks like nested aggregation
|
|
1936
|
+
* @param getCounts - Returns total counts of a vertex type
|
|
1937
|
+
* @returns: A guppy API endpoint for templating queryable data displayed on the exploration page
|
|
1938
|
+
*/ const explorerApi = guppyApi.injectEndpoints({
|
|
1939
|
+
endpoints: (builder)=>({
|
|
1940
|
+
getAllFieldsForType: builder.query({
|
|
1941
|
+
query: (type)=>({
|
|
1942
|
+
query: `{ _mapping ${type} } }`
|
|
1943
|
+
}),
|
|
1944
|
+
transformResponse: (response, _meta, params)=>{
|
|
1945
|
+
return response[params.type];
|
|
1946
|
+
}
|
|
1857
1947
|
}),
|
|
1858
1948
|
getAccessibleData: builder.query({
|
|
1859
1949
|
query: ({ type, fields, accessType })=>{
|
|
@@ -1946,7 +2036,32 @@ const useGetStatus = ()=>{
|
|
|
1946
2036
|
return queryBody;
|
|
1947
2037
|
},
|
|
1948
2038
|
transformResponse: (response, _meta, args)=>{
|
|
1949
|
-
return processHistogramResponse(response
|
|
2039
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
2040
|
+
}
|
|
2041
|
+
}),
|
|
2042
|
+
getAggsNoFilterSelf: builder.query({
|
|
2043
|
+
query: ({ type, fields, filters, accessibility = Accessibility.ALL })=>{
|
|
2044
|
+
const queryStart = isFilterEmpty(filters) ? `
|
|
2045
|
+
query getAggs {
|
|
2046
|
+
_aggregation {
|
|
2047
|
+
${type} (accessibility: ${accessibility}) {` : `query getAggs ($filter: JSON) {
|
|
2048
|
+
_aggregation {
|
|
2049
|
+
${type} (filter: $filter, filterSelf: true, accessibility: ${accessibility}) {`;
|
|
2050
|
+
const query = `${queryStart}
|
|
2051
|
+
${fields.map((field)=>histogramQueryStrForEachField(field))}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}`;
|
|
2055
|
+
const queryBody = {
|
|
2056
|
+
query: query,
|
|
2057
|
+
variables: {
|
|
2058
|
+
filter: convertFilterSetToGqlFilter(filters)
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
return queryBody;
|
|
2062
|
+
},
|
|
2063
|
+
transformResponse: (response, _meta, args)=>{
|
|
2064
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1950
2065
|
}
|
|
1951
2066
|
}),
|
|
1952
2067
|
getSubAggs: builder.query({
|
|
@@ -1971,7 +2086,7 @@ const useGetStatus = ()=>{
|
|
|
1971
2086
|
};
|
|
1972
2087
|
},
|
|
1973
2088
|
transformResponse: (response, _meta, args)=>{
|
|
1974
|
-
return processHistogramResponse(response
|
|
2089
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1975
2090
|
}
|
|
1976
2091
|
}),
|
|
1977
2092
|
getCounts: builder.query({
|
|
@@ -1995,7 +2110,13 @@ const useGetStatus = ()=>{
|
|
|
1995
2110
|
};
|
|
1996
2111
|
},
|
|
1997
2112
|
transformResponse: (response, _meta, args)=>{
|
|
1998
|
-
|
|
2113
|
+
if (!response.data || !response.data._aggregation) {
|
|
2114
|
+
throw new Error('Invalid response: Missing data or _aggregation field');
|
|
2115
|
+
}
|
|
2116
|
+
if (!(args.type in response.data._aggregation)) {
|
|
2117
|
+
throw new Error(`Invalid response: Missing expected key '${args.type}' in _aggregation`);
|
|
2118
|
+
}
|
|
2119
|
+
return response.data._aggregation[args.type]._totalCount ?? 0;
|
|
1999
2120
|
}
|
|
2000
2121
|
}),
|
|
2001
2122
|
getFieldCountSummary: builder.query({
|
|
@@ -2026,7 +2147,7 @@ const useGetStatus = ()=>{
|
|
|
2026
2147
|
query: (index)=>{
|
|
2027
2148
|
return {
|
|
2028
2149
|
query: `{
|
|
2029
|
-
_mapping ${index}
|
|
2150
|
+
_mapping { ${index} }
|
|
2030
2151
|
}`
|
|
2031
2152
|
};
|
|
2032
2153
|
},
|
|
@@ -2034,6 +2155,21 @@ const useGetStatus = ()=>{
|
|
|
2034
2155
|
return response['_mapping'];
|
|
2035
2156
|
}
|
|
2036
2157
|
}),
|
|
2158
|
+
getSharedFieldsForIndex: builder.query({
|
|
2159
|
+
query: (indices)=>{
|
|
2160
|
+
return {
|
|
2161
|
+
query: `{
|
|
2162
|
+
_mapping { ${indices.join(' ')} }
|
|
2163
|
+
}`
|
|
2164
|
+
};
|
|
2165
|
+
},
|
|
2166
|
+
transformResponse: (response)=>{
|
|
2167
|
+
if ('_mapping' in response.data) {
|
|
2168
|
+
return groupSharedFields(response.data['_mapping']);
|
|
2169
|
+
}
|
|
2170
|
+
return {};
|
|
2171
|
+
}
|
|
2172
|
+
}),
|
|
2037
2173
|
generalGQL: builder.query({
|
|
2038
2174
|
query: ({ query, variables })=>{
|
|
2039
2175
|
return {
|
|
@@ -2104,169 +2240,7 @@ const useGetArrayTypes = ()=>{
|
|
|
2104
2240
|
return data ? data['indices'] : {};
|
|
2105
2241
|
}
|
|
2106
2242
|
};
|
|
2107
|
-
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2108
|
-
|
|
2109
|
-
/**
|
|
2110
|
-
* Flattens a deep nested JSON object skipping
|
|
2111
|
-
* the first level to avoid potentially flattening
|
|
2112
|
-
* non-nested data.
|
|
2113
|
-
* @param {JSON} json
|
|
2114
|
-
*/ function flattenJson(json) {
|
|
2115
|
-
const flattenedJson = [];
|
|
2116
|
-
Object.keys(json).forEach((key)=>{
|
|
2117
|
-
flattenedJson.push(flat.flatten(json[key], {
|
|
2118
|
-
delimiter: '_'
|
|
2119
|
-
}));
|
|
2120
|
-
});
|
|
2121
|
-
return flattenedJson;
|
|
2122
|
-
}
|
|
2123
|
-
/**
|
|
2124
|
-
* Converts JSON based on a config.
|
|
2125
|
-
* @param {JSON} json
|
|
2126
|
-
* @param {Object} config
|
|
2127
|
-
*/ async function conversion(json, config) {
|
|
2128
|
-
return Papa.unparse(json, config);
|
|
2129
|
-
}
|
|
2130
|
-
/**
|
|
2131
|
-
* Converts JSON to a specified file format.
|
|
2132
|
-
* Defaults to JSON if file format is not supported.
|
|
2133
|
-
* @param {JSON} json
|
|
2134
|
-
* @param {string} format
|
|
2135
|
-
*/ async function jsonToFormat(json, format) {
|
|
2136
|
-
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
2137
|
-
const flatJson = await flattenJson(json);
|
|
2138
|
-
const data = await conversion(flatJson, {
|
|
2139
|
-
delimiter: FILE_DELIMITERS[format]
|
|
2140
|
-
});
|
|
2141
|
-
return data;
|
|
2142
|
-
}
|
|
2143
|
-
return json;
|
|
2144
|
-
}
|
|
2145
|
-
|
|
2146
|
-
/**
|
|
2147
|
-
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2148
|
-
*
|
|
2149
|
-
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2150
|
-
* @returns {URL} - The prepared download URL as a URL object.
|
|
2151
|
-
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
2152
|
-
/**
|
|
2153
|
-
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
2154
|
-
*
|
|
2155
|
-
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
2156
|
-
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
2157
|
-
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
2158
|
-
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
2159
|
-
return {
|
|
2160
|
-
method: 'POST',
|
|
2161
|
-
headers: {
|
|
2162
|
-
'Content-Type': 'application/json',
|
|
2163
|
-
...csrfToken !== undefined && {
|
|
2164
|
-
'X-CSRF-Token': csrfToken
|
|
2165
|
-
}
|
|
2166
|
-
},
|
|
2167
|
-
body: JSON.stringify({
|
|
2168
|
-
type: parameters.type,
|
|
2169
|
-
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
2170
|
-
accessibility: parameters.accessibility,
|
|
2171
|
-
fields: parameters?.fields,
|
|
2172
|
-
sort: parameters?.sort
|
|
2173
|
-
})
|
|
2174
|
-
};
|
|
2175
|
-
};
|
|
2176
|
-
/**
|
|
2177
|
-
* Downloads a file from Guppy using the provided parameters.
|
|
2178
|
-
* It will optionally convert the data to the specified format.
|
|
2179
|
-
*
|
|
2180
|
-
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
2181
|
-
* @param onStart - The function to call when the download starts.
|
|
2182
|
-
* @param onDone - The function to call when the download is done.
|
|
2183
|
-
* @param onError - The function to call when the download fails.
|
|
2184
|
-
* @param onAbort - The function to call when the download is aborted.
|
|
2185
|
-
* @param signal - AbortSignal to use for the request.
|
|
2186
|
-
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
2187
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2188
|
-
onStart?.();
|
|
2189
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2190
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2191
|
-
fetch(url.toString(), {
|
|
2192
|
-
...fetchConfig,
|
|
2193
|
-
...signal ? {
|
|
2194
|
-
signal: signal
|
|
2195
|
-
} : {}
|
|
2196
|
-
}).then(async (response)=>{
|
|
2197
|
-
if (!response.ok) {
|
|
2198
|
-
throw new Error(response.statusText);
|
|
2199
|
-
}
|
|
2200
|
-
let jsonData = await response.json();
|
|
2201
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2202
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2203
|
-
jsonData = jsonpathPlus.JSONPath({
|
|
2204
|
-
json: jsonData,
|
|
2205
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2206
|
-
resultType: 'value'
|
|
2207
|
-
});
|
|
2208
|
-
}
|
|
2209
|
-
// convert the data to the specified format and return a Blob
|
|
2210
|
-
let str = '';
|
|
2211
|
-
if (parameters.format === 'json') {
|
|
2212
|
-
str = JSON.stringify(jsonData);
|
|
2213
|
-
} else {
|
|
2214
|
-
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
2215
|
-
if (isJSONObject(convertedData)) {
|
|
2216
|
-
str = JSON.stringify(convertedData, null, 2);
|
|
2217
|
-
} else {
|
|
2218
|
-
str = convertedData;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
return new Blob([
|
|
2222
|
-
str
|
|
2223
|
-
], {
|
|
2224
|
-
type: 'application/json'
|
|
2225
|
-
});
|
|
2226
|
-
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
2227
|
-
// Abort is handle as an exception
|
|
2228
|
-
if (error.name == 'AbortError') {
|
|
2229
|
-
// handle abort()
|
|
2230
|
-
onAbort?.();
|
|
2231
|
-
}
|
|
2232
|
-
onError?.(error);
|
|
2233
|
-
});
|
|
2234
|
-
};
|
|
2235
|
-
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
2236
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2237
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2238
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2239
|
-
try {
|
|
2240
|
-
const response = await fetch(url.toString(), {
|
|
2241
|
-
...fetchConfig,
|
|
2242
|
-
...signal ? {
|
|
2243
|
-
signal: signal
|
|
2244
|
-
} : {}
|
|
2245
|
-
});
|
|
2246
|
-
let jsonData = await response.json();
|
|
2247
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2248
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2249
|
-
jsonData = jsonpathPlus.JSONPath({
|
|
2250
|
-
json: jsonData,
|
|
2251
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2252
|
-
resultType: 'value'
|
|
2253
|
-
});
|
|
2254
|
-
}
|
|
2255
|
-
// convert the data to the specified format and return a Blob
|
|
2256
|
-
return jsonData;
|
|
2257
|
-
} catch (error) {
|
|
2258
|
-
// Abort is handle as an exception
|
|
2259
|
-
if (error.name == 'AbortError') {
|
|
2260
|
-
// handle abort()
|
|
2261
|
-
onAbort?.();
|
|
2262
|
-
}
|
|
2263
|
-
throw new Error(error);
|
|
2264
|
-
}
|
|
2265
|
-
};
|
|
2266
|
-
const useGetIndexFields = (index)=>{
|
|
2267
|
-
const { data } = useGetFieldsForIndexQuery(index);
|
|
2268
|
-
return data ?? [];
|
|
2269
|
-
};
|
|
2243
|
+
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useGetAggsNoFilterSelfQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetSharedFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2270
2244
|
|
|
2271
2245
|
/**
|
|
2272
2246
|
* Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
|
|
@@ -2302,13 +2276,230 @@ const useGetIndexFields = (index)=>{
|
|
|
2302
2276
|
});
|
|
2303
2277
|
const { useDownloadFromGuppyMutation } = downloadRequestApi;
|
|
2304
2278
|
|
|
2279
|
+
const EmptyCohort = {
|
|
2280
|
+
id: 'default',
|
|
2281
|
+
name: 'Filters',
|
|
2282
|
+
filters: {},
|
|
2283
|
+
modified_datetime: new Date().toISOString()
|
|
2284
|
+
};
|
|
2285
|
+
const initialCohortState = {
|
|
2286
|
+
cohort: {
|
|
2287
|
+
...EmptyCohort
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
2290
|
+
// TODO: start using this adapter
|
|
2291
|
+
/*
|
|
2292
|
+
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
2293
|
+
sortComparer: (a, b) => {
|
|
2294
|
+
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
2295
|
+
else return -1;
|
|
2296
|
+
},
|
|
2297
|
+
});
|
|
2298
|
+
*/ /**
|
|
2299
|
+
* Redux slice for cohort filters
|
|
2300
|
+
*/ const cohortSlice = toolkit.createSlice({
|
|
2301
|
+
name: 'cohort',
|
|
2302
|
+
initialState: initialCohortState,
|
|
2303
|
+
reducers: {
|
|
2304
|
+
// adds a filter to the cohort filter set at the given index
|
|
2305
|
+
updateCohortFilter: (state, action)=>{
|
|
2306
|
+
const { index, field, filter } = action.payload;
|
|
2307
|
+
return {
|
|
2308
|
+
cohort: {
|
|
2309
|
+
...state.cohort,
|
|
2310
|
+
filters: {
|
|
2311
|
+
...state.cohort.filters,
|
|
2312
|
+
[index]: {
|
|
2313
|
+
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
2314
|
+
root: {
|
|
2315
|
+
...state.cohort.filters?.[index]?.root ?? {},
|
|
2316
|
+
[field]: filter
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
},
|
|
2323
|
+
setCohortFilter: (state, action)=>{
|
|
2324
|
+
const { index, filters } = action.payload;
|
|
2325
|
+
return {
|
|
2326
|
+
cohort: {
|
|
2327
|
+
...state.cohort,
|
|
2328
|
+
filters: {
|
|
2329
|
+
...state.cohort.filters,
|
|
2330
|
+
[index]: filters
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
};
|
|
2334
|
+
},
|
|
2335
|
+
setCohortIndexFilters: (state, action)=>{
|
|
2336
|
+
return {
|
|
2337
|
+
cohort: {
|
|
2338
|
+
...state.cohort,
|
|
2339
|
+
filters: {
|
|
2340
|
+
...action.payload.filters
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
},
|
|
2345
|
+
// removes a filter to the cohort filter set at the given index
|
|
2346
|
+
removeCohortFilter: (state, action)=>{
|
|
2347
|
+
const { index, field } = action.payload;
|
|
2348
|
+
const filters = state.cohort?.filters?.[index]?.root;
|
|
2349
|
+
if (!filters) {
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
const { [field]: _a, ...updated } = filters;
|
|
2353
|
+
return {
|
|
2354
|
+
cohort: {
|
|
2355
|
+
...state.cohort,
|
|
2356
|
+
filters: {
|
|
2357
|
+
...state.cohort.filters,
|
|
2358
|
+
[index]: {
|
|
2359
|
+
mode: state.cohort.filters[index].mode,
|
|
2360
|
+
root: updated
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
};
|
|
2365
|
+
},
|
|
2366
|
+
// removes all filters from the cohort filter set at the given index
|
|
2367
|
+
clearCohortFilters: (state, action)=>{
|
|
2368
|
+
const { index } = action.payload;
|
|
2369
|
+
return {
|
|
2370
|
+
cohort: {
|
|
2371
|
+
...state.cohort,
|
|
2372
|
+
filters: {
|
|
2373
|
+
...state.cohort.filters,
|
|
2374
|
+
[index]: {
|
|
2375
|
+
// empty filter set
|
|
2376
|
+
mode: 'and',
|
|
2377
|
+
root: {}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2385
|
+
// Filter actions: addFilter, removeFilter, updateFilter
|
|
2386
|
+
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
2387
|
+
const selectCohortFilters = (state)=>state.cohorts.cohort.cohort.filters;
|
|
2388
|
+
const selectCurrentCohortId = (state)=>state.cohorts.cohort.cohort.id;
|
|
2389
|
+
const selectCurrentCohort = (state)=>state.cohorts.cohort.cohort;
|
|
2390
|
+
const selectCurrentCohortName = (state)=>state.cohorts.cohort.cohort.name;
|
|
2391
|
+
/**
|
|
2392
|
+
* Select a filter by its name from the current cohort. If the filter is not found
|
|
2393
|
+
* returns undefined.
|
|
2394
|
+
* @param state - Core
|
|
2395
|
+
* @param index which cohort index to select from
|
|
2396
|
+
* @param name name of the filter to select
|
|
2397
|
+
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
2398
|
+
return state.cohorts.cohort.cohort.filters[index]?.root[name];
|
|
2399
|
+
};
|
|
2400
|
+
const EmptyFilterSet = {
|
|
2401
|
+
mode: 'and',
|
|
2402
|
+
root: {}
|
|
2403
|
+
};
|
|
2404
|
+
/**
|
|
2405
|
+
* Select a filter from the index.
|
|
2406
|
+
* returns undefined.
|
|
2407
|
+
* @param state - Core
|
|
2408
|
+
* @param index which cohort index to select from
|
|
2409
|
+
*/ const selectIndexFilters = (state, index)=>{
|
|
2410
|
+
return state.cohorts.cohort.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
2411
|
+
};
|
|
2412
|
+
const cohortReducer = cohortSlice.reducer;
|
|
2413
|
+
|
|
2414
|
+
const initialState$2 = {};
|
|
2415
|
+
const expandSlice$1 = toolkit.createSlice({
|
|
2416
|
+
name: 'CohortBuilder/filterExpand',
|
|
2417
|
+
initialState: initialState$2,
|
|
2418
|
+
reducers: {
|
|
2419
|
+
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
2420
|
+
return {
|
|
2421
|
+
...state,
|
|
2422
|
+
[action.payload.index]: {
|
|
2423
|
+
...state[action.payload.index],
|
|
2424
|
+
[action.payload.field]: action.payload.expanded
|
|
2425
|
+
}
|
|
2426
|
+
};
|
|
2427
|
+
},
|
|
2428
|
+
toggleCohortBuilderAllFilters: (state, action)=>{
|
|
2429
|
+
return {
|
|
2430
|
+
...state,
|
|
2431
|
+
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
2432
|
+
acc[k] = action.payload.expand;
|
|
2433
|
+
return acc;
|
|
2434
|
+
}, {})
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
2440
|
+
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
2441
|
+
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
2442
|
+
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
2443
|
+
|
|
2444
|
+
const initialState$1 = {};
|
|
2445
|
+
const expandSlice = toolkit.createSlice({
|
|
2446
|
+
name: 'CohortBuilder/filterCombineMode',
|
|
2447
|
+
initialState: initialState$1,
|
|
2448
|
+
reducers: {
|
|
2449
|
+
setCohortFilterCombineMode: (state, action)=>{
|
|
2450
|
+
return {
|
|
2451
|
+
...state,
|
|
2452
|
+
[action.payload.index]: {
|
|
2453
|
+
...state[action.payload.index],
|
|
2454
|
+
[action.payload.field]: action.payload.mode
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
});
|
|
2460
|
+
const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
|
|
2461
|
+
const { setCohortFilterCombineMode } = expandSlice.actions;
|
|
2462
|
+
const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
|
|
2463
|
+
|
|
2464
|
+
const initialState = {
|
|
2465
|
+
shouldShareFilters: false,
|
|
2466
|
+
sharedFiltersMap: {}
|
|
2467
|
+
};
|
|
2468
|
+
const cohortSharedFiltersSlice = toolkit.createSlice({
|
|
2469
|
+
name: 'cohortSharedFilters',
|
|
2470
|
+
initialState: initialState,
|
|
2471
|
+
reducers: {
|
|
2472
|
+
setShouldShareFilters: (state, action)=>{
|
|
2473
|
+
state.shouldShareFilters = action.payload;
|
|
2474
|
+
return state;
|
|
2475
|
+
},
|
|
2476
|
+
setSharedFilters: (state, action)=>{
|
|
2477
|
+
state.sharedFiltersMap = action.payload;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
|
|
2482
|
+
const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
|
|
2483
|
+
const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
|
|
2484
|
+
field
|
|
2485
|
+
];
|
|
2486
|
+
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
2487
|
+
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
2488
|
+
|
|
2489
|
+
const cohortReducers = toolkit.combineReducers({
|
|
2490
|
+
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
2491
|
+
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
2492
|
+
sharedFilters: cohortSharedFiltersReducer,
|
|
2493
|
+
cohort: cohortReducer
|
|
2494
|
+
});
|
|
2495
|
+
|
|
2305
2496
|
const rootReducer = toolkit.combineReducers({
|
|
2306
2497
|
gen3Services: gen3ServicesReducer,
|
|
2307
2498
|
user: userReducer,
|
|
2308
2499
|
gen3Apps: gen3AppReducer,
|
|
2309
2500
|
drsHostnames: drsHostnamesReducer,
|
|
2310
2501
|
modals: modalReducer,
|
|
2311
|
-
cohorts:
|
|
2502
|
+
cohorts: cohortReducers,
|
|
2312
2503
|
activeWorkspace: activeWorkspaceReducer,
|
|
2313
2504
|
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2314
2505
|
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
@@ -2337,7 +2528,7 @@ const persistConfig = {
|
|
|
2337
2528
|
version: 1,
|
|
2338
2529
|
storage,
|
|
2339
2530
|
whitelist: [
|
|
2340
|
-
'
|
|
2531
|
+
'cohort',
|
|
2341
2532
|
'activeWorkspace'
|
|
2342
2533
|
]
|
|
2343
2534
|
};
|
|
@@ -2362,7 +2553,7 @@ const coreStore = setupCoreStore();
|
|
|
2362
2553
|
query.setupListeners(coreStore.dispatch);
|
|
2363
2554
|
|
|
2364
2555
|
const isNotDefined = (x)=>{
|
|
2365
|
-
return x === undefined || x === null || x ===
|
|
2556
|
+
return x === undefined || x === null || x === void 0;
|
|
2366
2557
|
};
|
|
2367
2558
|
const isObject = (x)=>{
|
|
2368
2559
|
return typeof x === 'object';
|
|
@@ -2381,51 +2572,14 @@ const isString = (x)=>{
|
|
|
2381
2572
|
* @returns {URL} - The prepared download URL as a URL object.
|
|
2382
2573
|
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2383
2574
|
|
|
2384
|
-
const
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
406: 'Not Acceptable',
|
|
2393
|
-
407: 'Proxy Authentication Required',
|
|
2394
|
-
408: 'Request Timeout',
|
|
2395
|
-
409: 'Conflict',
|
|
2396
|
-
410: 'Gone',
|
|
2397
|
-
411: 'Length Required',
|
|
2398
|
-
412: 'Precondition Failed',
|
|
2399
|
-
413: 'Payload Too Large',
|
|
2400
|
-
414: 'URI Too Long',
|
|
2401
|
-
415: 'Unsupported Media Type',
|
|
2402
|
-
416: 'Range Not Satisfiable',
|
|
2403
|
-
417: 'Expectation Failed',
|
|
2404
|
-
418: "I'm a teapot",
|
|
2405
|
-
421: 'Misdirected Request',
|
|
2406
|
-
422: 'Unprocessable Entity',
|
|
2407
|
-
423: 'Locked',
|
|
2408
|
-
424: 'Failed Dependency',
|
|
2409
|
-
425: 'Too Early',
|
|
2410
|
-
426: 'Upgrade Required',
|
|
2411
|
-
428: 'Precondition Required',
|
|
2412
|
-
429: 'Too Many Requests',
|
|
2413
|
-
431: 'Request Header Fields Too Large',
|
|
2414
|
-
451: 'Unavailable For Legal Reasons',
|
|
2415
|
-
// 5xx Server Errors
|
|
2416
|
-
500: 'Internal Server Error',
|
|
2417
|
-
501: 'Not Implemented',
|
|
2418
|
-
502: 'Bad Gateway',
|
|
2419
|
-
503: 'Service Unavailable',
|
|
2420
|
-
504: 'Gateway Timeout',
|
|
2421
|
-
505: 'HTTP Version Not Supported',
|
|
2422
|
-
506: 'Variant Also Negotiates',
|
|
2423
|
-
507: 'Insufficient Storage',
|
|
2424
|
-
508: 'Loop Detected',
|
|
2425
|
-
510: 'Not Extended',
|
|
2426
|
-
511: 'Network Authentication Required'
|
|
2427
|
-
};
|
|
2428
|
-
class HTTPError extends Error {
|
|
2575
|
+
const DEFAULT_METHOD = 'GET';
|
|
2576
|
+
const CONTENT_TYPE_HEADER = 'Content-Type';
|
|
2577
|
+
const CONTENT_TYPE_JSON = 'application/json';
|
|
2578
|
+
/**
|
|
2579
|
+
* Represents an error that occurs during an HTTP request.
|
|
2580
|
+
* Extends the built-in `Error` class to provide additional information
|
|
2581
|
+
* about the HTTP status code and optional response data.
|
|
2582
|
+
*/ class HTTPError extends Error {
|
|
2429
2583
|
constructor(status, message, responseData){
|
|
2430
2584
|
super(message), this.status = status, this.responseData = responseData;
|
|
2431
2585
|
this.name = 'HTTPError';
|
|
@@ -2435,15 +2589,16 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2435
2589
|
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2436
2590
|
const headers = new Headers();
|
|
2437
2591
|
headers.set('Content-Type', 'application/json');
|
|
2438
|
-
let accessToken = undefined;
|
|
2439
2592
|
if (process.env.NODE_ENV === 'development') {
|
|
2440
2593
|
// NOTE: This cookie can only be accessed from the client side
|
|
2441
2594
|
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2442
|
-
accessToken = cookiesNext.getCookie('credentials_token');
|
|
2595
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
2596
|
+
if (accessToken) {
|
|
2597
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2598
|
+
}
|
|
2443
2599
|
}
|
|
2444
2600
|
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2445
|
-
|
|
2446
|
-
const url = `${GEN3_FENCE_API}/user/data/download/${guid}`;
|
|
2601
|
+
const url = `${GEN3_FENCE_API}/data/download/${guid}`;
|
|
2447
2602
|
try {
|
|
2448
2603
|
const response = await fetch(url, {
|
|
2449
2604
|
method: method,
|
|
@@ -2476,6 +2631,67 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2476
2631
|
throw error;
|
|
2477
2632
|
}
|
|
2478
2633
|
};
|
|
2634
|
+
/**
|
|
2635
|
+
* Retrieves a CSRF token from the server.
|
|
2636
|
+
*
|
|
2637
|
+
* This asynchronous function sends a GET request to the server's status endpoint
|
|
2638
|
+
* to fetch the CSRF token in the response. The token is expected to be included
|
|
2639
|
+
* in the JSON response under the `csrf_token` field.
|
|
2640
|
+
*
|
|
2641
|
+
* @returns {Promise<string | null>} A promise that resolves to the CSRF token as a string if successfully retrieved,
|
|
2642
|
+
* or null if the token is not present in the response.
|
|
2643
|
+
* @throws {HTTPError} Throws an HTTPError if the server response is not successful.
|
|
2644
|
+
*/ const getCSRFToken = async ()=>{
|
|
2645
|
+
const requestHeaders = new Headers({
|
|
2646
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2647
|
+
});
|
|
2648
|
+
const response = await fetch(`${GEN3_API}/_status`, {
|
|
2649
|
+
headers: requestHeaders
|
|
2650
|
+
});
|
|
2651
|
+
if (!response.ok) {
|
|
2652
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2653
|
+
}
|
|
2654
|
+
const { csrf_token: csrfToken } = await response.json();
|
|
2655
|
+
return csrfToken || null;
|
|
2656
|
+
};
|
|
2657
|
+
/**
|
|
2658
|
+
* Fetches JSON data from a specified URL using the Fetch API.
|
|
2659
|
+
*
|
|
2660
|
+
* @param {string} url - The URL to fetch the JSON data from.
|
|
2661
|
+
* @param {boolean} [requiresCSRF=false] - Indicates whether a CSRF token is required for the request.
|
|
2662
|
+
* If true, the CSRF token will be added to the request headers.
|
|
2663
|
+
* @param {string} [method=DEFAULT_METHOD] - The HTTP method to use for the request (e.g., 'GET', 'POST').
|
|
2664
|
+
* @param {unknown} [body=undefined] - The request body to send, applicable when using methods like 'POST'.
|
|
2665
|
+
*
|
|
2666
|
+
* @returns {Promise<any>} A promise that resolves to the parsed JSON data from the response.
|
|
2667
|
+
*
|
|
2668
|
+
* @throws {HTTPError} Throws an error if the HTTP response status indicates a failure.
|
|
2669
|
+
*/ const fetchJSONDataFromURL = async (url, requiresCSRF = false, method = DEFAULT_METHOD, body = undefined)=>{
|
|
2670
|
+
const requestHeaders = new Headers({
|
|
2671
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2672
|
+
});
|
|
2673
|
+
if (requiresCSRF) {
|
|
2674
|
+
const csrfToken = await getCSRFToken();
|
|
2675
|
+
if (csrfToken) {
|
|
2676
|
+
requestHeaders.set('X-CSRF-Token', csrfToken);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2680
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
2681
|
+
if (accessToken) {
|
|
2682
|
+
requestHeaders.set('Authorization', `Bearer ${accessToken}`);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
const response = await fetch(url, {
|
|
2686
|
+
method,
|
|
2687
|
+
headers: requestHeaders,
|
|
2688
|
+
body: method === 'POST' ? JSON.stringify(body) : null
|
|
2689
|
+
});
|
|
2690
|
+
if (!response.ok) {
|
|
2691
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2692
|
+
}
|
|
2693
|
+
return response.json();
|
|
2694
|
+
};
|
|
2479
2695
|
|
|
2480
2696
|
const persistor = reduxPersist.persistStore(coreStore);
|
|
2481
2697
|
const CoreProvider = ({ children })=>{
|
|
@@ -3302,7 +3518,6 @@ exports.GEN3_SOWER_API = GEN3_SOWER_API;
|
|
|
3302
3518
|
exports.GEN3_SUBMISSION_API = GEN3_SUBMISSION_API;
|
|
3303
3519
|
exports.GEN3_WORKSPACE_API = GEN3_WORKSPACE_API;
|
|
3304
3520
|
exports.HTTPError = HTTPError;
|
|
3305
|
-
exports.HTTPErrorMessages = HTTPErrorMessages;
|
|
3306
3521
|
exports.Modals = Modals;
|
|
3307
3522
|
exports.PodConditionType = PodConditionType;
|
|
3308
3523
|
exports.PodStatus = PodStatus;
|
|
@@ -3311,7 +3526,6 @@ exports.WorkspaceStatus = WorkspaceStatus;
|
|
|
3311
3526
|
exports.clearActiveWorkspaceId = clearActiveWorkspaceId;
|
|
3312
3527
|
exports.clearCohortFilters = clearCohortFilters;
|
|
3313
3528
|
exports.clearDataLibrarySelection = clearDataLibrarySelection;
|
|
3314
|
-
exports.cohortReducer = cohortReducer;
|
|
3315
3529
|
exports.convertFilterSetToGqlFilter = convertFilterSetToGqlFilter;
|
|
3316
3530
|
exports.coreStore = coreStore;
|
|
3317
3531
|
exports.createAppApiForRTKQ = createAppApiForRTKQ;
|
|
@@ -3330,6 +3544,7 @@ exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFrom
|
|
|
3330
3544
|
exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
|
|
3331
3545
|
exports.fetchFence = fetchFence;
|
|
3332
3546
|
exports.fetchFencePresignedURL = fetchFencePresignedURL;
|
|
3547
|
+
exports.fetchJSONDataFromURL = fetchJSONDataFromURL;
|
|
3333
3548
|
exports.fetchJson = fetchJson;
|
|
3334
3549
|
exports.fetchUserState = fetchUserState;
|
|
3335
3550
|
exports.fieldNameToTitle = fieldNameToTitle;
|
|
@@ -3340,6 +3555,7 @@ exports.getNumberOfItemsInDatalist = getNumberOfItemsInDatalist;
|
|
|
3340
3555
|
exports.getTimestamp = getTimestamp;
|
|
3341
3556
|
exports.graphQLAPI = graphQLAPI;
|
|
3342
3557
|
exports.graphQLWithTags = graphQLWithTags;
|
|
3558
|
+
exports.groupSharedFields = groupSharedFields;
|
|
3343
3559
|
exports.guppyAPISliceMiddleware = guppyAPISliceMiddleware;
|
|
3344
3560
|
exports.guppyApi = guppyApi;
|
|
3345
3561
|
exports.guppyApiReducer = guppyApiReducer;
|
|
@@ -3352,6 +3568,7 @@ exports.isAuthenticated = isAuthenticated;
|
|
|
3352
3568
|
exports.isCohortItem = isCohortItem;
|
|
3353
3569
|
exports.isErrorWithMessage = isErrorWithMessage;
|
|
3354
3570
|
exports.isFetchBaseQueryError = isFetchBaseQueryError;
|
|
3571
|
+
exports.isFetchError = isFetchError;
|
|
3355
3572
|
exports.isFetchParseError = isFetchParseError;
|
|
3356
3573
|
exports.isFileItem = isFileItem;
|
|
3357
3574
|
exports.isFilterEmpty = isFilterEmpty;
|
|
@@ -3366,17 +3583,20 @@ exports.isHistogramDataArrayAnEnum = isHistogramDataArrayAnEnum;
|
|
|
3366
3583
|
exports.isHistogramDataCollection = isHistogramDataCollection;
|
|
3367
3584
|
exports.isHistogramRangeData = isHistogramRangeData;
|
|
3368
3585
|
exports.isHttpStatusError = isHttpStatusError;
|
|
3586
|
+
exports.isIntersection = isIntersection;
|
|
3369
3587
|
exports.isJSONObject = isJSONObject;
|
|
3370
3588
|
exports.isJSONValue = isJSONValue;
|
|
3371
3589
|
exports.isJSONValueArray = isJSONValueArray;
|
|
3372
3590
|
exports.isNotDefined = isNotDefined;
|
|
3373
3591
|
exports.isObject = isObject;
|
|
3374
3592
|
exports.isOperationWithField = isOperationWithField;
|
|
3593
|
+
exports.isOperatorWithFieldAndArrayOfOperands = isOperatorWithFieldAndArrayOfOperands;
|
|
3375
3594
|
exports.isPending = isPending;
|
|
3376
3595
|
exports.isProgramUrl = isProgramUrl;
|
|
3377
3596
|
exports.isRootUrl = isRootUrl;
|
|
3378
3597
|
exports.isString = isString;
|
|
3379
3598
|
exports.isTimeGreaterThan = isTimeGreaterThan;
|
|
3599
|
+
exports.isUnion = isUnion;
|
|
3380
3600
|
exports.isWorkspaceActive = isWorkspaceActive;
|
|
3381
3601
|
exports.isWorkspaceRunningOrStopping = isWorkspaceRunningOrStopping;
|
|
3382
3602
|
exports.listifyMethodsFromMapping = listifyMethodsFromMapping;
|
|
@@ -3391,9 +3611,12 @@ exports.resetUserState = resetUserState;
|
|
|
3391
3611
|
exports.resourcePathFromProjectID = resourcePathFromProjectID;
|
|
3392
3612
|
exports.selectActiveWorkspaceId = selectActiveWorkspaceId;
|
|
3393
3613
|
exports.selectActiveWorkspaceStatus = selectActiveWorkspaceStatus;
|
|
3614
|
+
exports.selectAllCohortFiltersCollapsed = selectAllCohortFiltersCollapsed;
|
|
3394
3615
|
exports.selectAuthzMappingData = selectAuthzMappingData;
|
|
3395
3616
|
exports.selectCSRFToken = selectCSRFToken;
|
|
3396
3617
|
exports.selectCSRFTokenData = selectCSRFTokenData;
|
|
3618
|
+
exports.selectCohortFilterCombineMode = selectCohortFilterCombineMode;
|
|
3619
|
+
exports.selectCohortFilterExpanded = selectCohortFilterExpanded;
|
|
3397
3620
|
exports.selectCohortFilters = selectCohortFilters;
|
|
3398
3621
|
exports.selectCurrentCohort = selectCurrentCohort;
|
|
3399
3622
|
exports.selectCurrentCohortId = selectCurrentCohortId;
|
|
@@ -3408,6 +3631,9 @@ exports.selectIndexedFilterByName = selectIndexedFilterByName;
|
|
|
3408
3631
|
exports.selectPaymodelStatus = selectPaymodelStatus;
|
|
3409
3632
|
exports.selectRequestedWorkspaceStatus = selectRequestedWorkspaceStatus;
|
|
3410
3633
|
exports.selectRequestedWorkspaceStatusTimestamp = selectRequestedWorkspaceStatusTimestamp;
|
|
3634
|
+
exports.selectSharedFilters = selectSharedFilters;
|
|
3635
|
+
exports.selectSharedFiltersForFields = selectSharedFiltersForFields;
|
|
3636
|
+
exports.selectShouldShareFilters = selectShouldShareFilters;
|
|
3411
3637
|
exports.selectUser = selectUser;
|
|
3412
3638
|
exports.selectUserAuthStatus = selectUserAuthStatus;
|
|
3413
3639
|
exports.selectUserData = selectUserData;
|
|
@@ -3419,13 +3645,18 @@ exports.setActiveWorkspace = setActiveWorkspace;
|
|
|
3419
3645
|
exports.setActiveWorkspaceId = setActiveWorkspaceId;
|
|
3420
3646
|
exports.setActiveWorkspaceStatus = setActiveWorkspaceStatus;
|
|
3421
3647
|
exports.setCohortFilter = setCohortFilter;
|
|
3648
|
+
exports.setCohortFilterCombineMode = setCohortFilterCombineMode;
|
|
3422
3649
|
exports.setCohortIndexFilters = setCohortIndexFilters;
|
|
3423
3650
|
exports.setDRSHostnames = setDRSHostnames;
|
|
3424
3651
|
exports.setDataLibraryListSelection = setDataLibraryListSelection;
|
|
3425
3652
|
exports.setRequestedWorkspaceStatus = setRequestedWorkspaceStatus;
|
|
3653
|
+
exports.setSharedFilters = setSharedFilters;
|
|
3654
|
+
exports.setShouldShareFilters = setShouldShareFilters;
|
|
3426
3655
|
exports.setupCoreStore = setupCoreStore;
|
|
3427
3656
|
exports.showModal = showModal;
|
|
3428
3657
|
exports.submissionApi = submissionApi;
|
|
3658
|
+
exports.toggleCohortBuilderAllFilters = toggleCohortBuilderAllFilters;
|
|
3659
|
+
exports.toggleCohortBuilderCategoryFilter = toggleCohortBuilderCategoryFilter;
|
|
3429
3660
|
exports.trimFirstFieldNameToTitle = trimFirstFieldNameToTitle;
|
|
3430
3661
|
exports.updateCohortFilter = updateCohortFilter;
|
|
3431
3662
|
exports.useAddDataLibraryListMutation = useAddDataLibraryListMutation;
|
|
@@ -3444,6 +3675,7 @@ exports.useGetAISearchVersionQuery = useGetAISearchVersionQuery;
|
|
|
3444
3675
|
exports.useGetAccessibleDataQuery = useGetAccessibleDataQuery;
|
|
3445
3676
|
exports.useGetActivePayModelQuery = useGetActivePayModelQuery;
|
|
3446
3677
|
exports.useGetAggMDSQuery = useGetAggMDSQuery;
|
|
3678
|
+
exports.useGetAggsNoFilterSelfQuery = useGetAggsNoFilterSelfQuery;
|
|
3447
3679
|
exports.useGetAggsQuery = useGetAggsQuery;
|
|
3448
3680
|
exports.useGetAllFieldsForTypeQuery = useGetAllFieldsForTypeQuery;
|
|
3449
3681
|
exports.useGetArrayTypes = useGetArrayTypes;
|
|
@@ -3469,6 +3701,7 @@ exports.useGetMetadataByIdQuery = useGetMetadataByIdQuery;
|
|
|
3469
3701
|
exports.useGetProjectsDetailsQuery = useGetProjectsDetailsQuery;
|
|
3470
3702
|
exports.useGetProjectsQuery = useGetProjectsQuery;
|
|
3471
3703
|
exports.useGetRawDataAndTotalCountsQuery = useGetRawDataAndTotalCountsQuery;
|
|
3704
|
+
exports.useGetSharedFieldsForIndexQuery = useGetSharedFieldsForIndexQuery;
|
|
3472
3705
|
exports.useGetSowerJobListQuery = useGetSowerJobListQuery;
|
|
3473
3706
|
exports.useGetSowerJobStatusQuery = useGetSowerJobStatusQuery;
|
|
3474
3707
|
exports.useGetSowerOutputQuery = useGetSowerOutputQuery;
|