@gen3/core 0.10.80 → 0.10.82
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 +679 -445
- 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 +662 -445
- 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
|
@@ -38,7 +38,7 @@ function _interopNamespaceDefault(e) {
|
|
|
38
38
|
|
|
39
39
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
40
40
|
|
|
41
|
-
const GEN3_COMMONS_NAME = process.env.
|
|
41
|
+
const GEN3_COMMONS_NAME = process.env.NEXT_PUBLIC_GEN3_COMMONS_NAME || 'gen3';
|
|
42
42
|
const GEN3_API = process.env.NEXT_PUBLIC_GEN3_API || '';
|
|
43
43
|
const GEN3_DOMAIN = process.env.NEXT_PUBLIC_GEN3_DOMAIN || '';
|
|
44
44
|
/**
|
|
@@ -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,231 @@ 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
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2353
|
+
const { [field]: _a, ...updated } = filters;
|
|
2354
|
+
return {
|
|
2355
|
+
cohort: {
|
|
2356
|
+
...state.cohort,
|
|
2357
|
+
filters: {
|
|
2358
|
+
...state.cohort.filters,
|
|
2359
|
+
[index]: {
|
|
2360
|
+
mode: state.cohort.filters[index].mode,
|
|
2361
|
+
root: updated
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
},
|
|
2367
|
+
// removes all filters from the cohort filter set at the given index
|
|
2368
|
+
clearCohortFilters: (state, action)=>{
|
|
2369
|
+
const { index } = action.payload;
|
|
2370
|
+
return {
|
|
2371
|
+
cohort: {
|
|
2372
|
+
...state.cohort,
|
|
2373
|
+
filters: {
|
|
2374
|
+
...state.cohort.filters,
|
|
2375
|
+
[index]: {
|
|
2376
|
+
// empty filter set
|
|
2377
|
+
mode: 'and',
|
|
2378
|
+
root: {}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
// Filter actions: addFilter, removeFilter, updateFilter
|
|
2387
|
+
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
2388
|
+
const selectCohortFilters = (state)=>state.cohorts.cohort.cohort.filters;
|
|
2389
|
+
const selectCurrentCohortId = (state)=>state.cohorts.cohort.cohort.id;
|
|
2390
|
+
const selectCurrentCohort = (state)=>state.cohorts.cohort.cohort;
|
|
2391
|
+
const selectCurrentCohortName = (state)=>state.cohorts.cohort.cohort.name;
|
|
2392
|
+
/**
|
|
2393
|
+
* Select a filter by its name from the current cohort. If the filter is not found
|
|
2394
|
+
* returns undefined.
|
|
2395
|
+
* @param state - Core
|
|
2396
|
+
* @param index which cohort index to select from
|
|
2397
|
+
* @param name name of the filter to select
|
|
2398
|
+
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
2399
|
+
return state.cohorts.cohort.cohort.filters[index]?.root[name];
|
|
2400
|
+
};
|
|
2401
|
+
const EmptyFilterSet = {
|
|
2402
|
+
mode: 'and',
|
|
2403
|
+
root: {}
|
|
2404
|
+
};
|
|
2405
|
+
/**
|
|
2406
|
+
* Select a filter from the index.
|
|
2407
|
+
* returns undefined.
|
|
2408
|
+
* @param state - Core
|
|
2409
|
+
* @param index which cohort index to select from
|
|
2410
|
+
*/ const selectIndexFilters = (state, index)=>{
|
|
2411
|
+
return state.cohorts.cohort.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
2412
|
+
};
|
|
2413
|
+
const cohortReducer = cohortSlice.reducer;
|
|
2414
|
+
|
|
2415
|
+
const initialState$2 = {};
|
|
2416
|
+
const expandSlice$1 = toolkit.createSlice({
|
|
2417
|
+
name: 'CohortBuilder/filterExpand',
|
|
2418
|
+
initialState: initialState$2,
|
|
2419
|
+
reducers: {
|
|
2420
|
+
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
2421
|
+
return {
|
|
2422
|
+
...state,
|
|
2423
|
+
[action.payload.index]: {
|
|
2424
|
+
...state[action.payload.index],
|
|
2425
|
+
[action.payload.field]: action.payload.expanded
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
},
|
|
2429
|
+
toggleCohortBuilderAllFilters: (state, action)=>{
|
|
2430
|
+
return {
|
|
2431
|
+
...state,
|
|
2432
|
+
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
2433
|
+
acc[k] = action.payload.expand;
|
|
2434
|
+
return acc;
|
|
2435
|
+
}, {})
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
2441
|
+
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
2442
|
+
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
2443
|
+
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
2444
|
+
|
|
2445
|
+
const initialState$1 = {};
|
|
2446
|
+
const expandSlice = toolkit.createSlice({
|
|
2447
|
+
name: 'CohortBuilder/filterCombineMode',
|
|
2448
|
+
initialState: initialState$1,
|
|
2449
|
+
reducers: {
|
|
2450
|
+
setCohortFilterCombineMode: (state, action)=>{
|
|
2451
|
+
return {
|
|
2452
|
+
...state,
|
|
2453
|
+
[action.payload.index]: {
|
|
2454
|
+
...state[action.payload.index],
|
|
2455
|
+
[action.payload.field]: action.payload.mode
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
|
|
2462
|
+
const { setCohortFilterCombineMode } = expandSlice.actions;
|
|
2463
|
+
const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
|
|
2464
|
+
|
|
2465
|
+
const initialState = {
|
|
2466
|
+
shouldShareFilters: false,
|
|
2467
|
+
sharedFiltersMap: {}
|
|
2468
|
+
};
|
|
2469
|
+
const cohortSharedFiltersSlice = toolkit.createSlice({
|
|
2470
|
+
name: 'cohortSharedFilters',
|
|
2471
|
+
initialState: initialState,
|
|
2472
|
+
reducers: {
|
|
2473
|
+
setShouldShareFilters: (state, action)=>{
|
|
2474
|
+
state.shouldShareFilters = action.payload;
|
|
2475
|
+
return state;
|
|
2476
|
+
},
|
|
2477
|
+
setSharedFilters: (state, action)=>{
|
|
2478
|
+
state.sharedFiltersMap = action.payload;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
});
|
|
2482
|
+
const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
|
|
2483
|
+
const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
|
|
2484
|
+
const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
|
|
2485
|
+
field
|
|
2486
|
+
];
|
|
2487
|
+
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
2488
|
+
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
2489
|
+
|
|
2490
|
+
const cohortReducers = toolkit.combineReducers({
|
|
2491
|
+
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
2492
|
+
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
2493
|
+
sharedFilters: cohortSharedFiltersReducer,
|
|
2494
|
+
cohort: cohortReducer
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2305
2497
|
const rootReducer = toolkit.combineReducers({
|
|
2306
2498
|
gen3Services: gen3ServicesReducer,
|
|
2307
2499
|
user: userReducer,
|
|
2308
2500
|
gen3Apps: gen3AppReducer,
|
|
2309
2501
|
drsHostnames: drsHostnamesReducer,
|
|
2310
2502
|
modals: modalReducer,
|
|
2311
|
-
cohorts:
|
|
2503
|
+
cohorts: cohortReducers,
|
|
2312
2504
|
activeWorkspace: activeWorkspaceReducer,
|
|
2313
2505
|
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2314
2506
|
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
@@ -2337,7 +2529,7 @@ const persistConfig = {
|
|
|
2337
2529
|
version: 1,
|
|
2338
2530
|
storage,
|
|
2339
2531
|
whitelist: [
|
|
2340
|
-
'
|
|
2532
|
+
'cohort',
|
|
2341
2533
|
'activeWorkspace'
|
|
2342
2534
|
]
|
|
2343
2535
|
};
|
|
@@ -2362,7 +2554,7 @@ const coreStore = setupCoreStore();
|
|
|
2362
2554
|
query.setupListeners(coreStore.dispatch);
|
|
2363
2555
|
|
|
2364
2556
|
const isNotDefined = (x)=>{
|
|
2365
|
-
return x === undefined || x === null || x ===
|
|
2557
|
+
return x === undefined || x === null || x === void 0;
|
|
2366
2558
|
};
|
|
2367
2559
|
const isObject = (x)=>{
|
|
2368
2560
|
return typeof x === 'object';
|
|
@@ -2381,51 +2573,14 @@ const isString = (x)=>{
|
|
|
2381
2573
|
* @returns {URL} - The prepared download URL as a URL object.
|
|
2382
2574
|
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2383
2575
|
|
|
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 {
|
|
2576
|
+
const DEFAULT_METHOD = 'GET';
|
|
2577
|
+
const CONTENT_TYPE_HEADER = 'Content-Type';
|
|
2578
|
+
const CONTENT_TYPE_JSON = 'application/json';
|
|
2579
|
+
/**
|
|
2580
|
+
* Represents an error that occurs during an HTTP request.
|
|
2581
|
+
* Extends the built-in `Error` class to provide additional information
|
|
2582
|
+
* about the HTTP status code and optional response data.
|
|
2583
|
+
*/ class HTTPError extends Error {
|
|
2429
2584
|
constructor(status, message, responseData){
|
|
2430
2585
|
super(message), this.status = status, this.responseData = responseData;
|
|
2431
2586
|
this.name = 'HTTPError';
|
|
@@ -2435,15 +2590,16 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2435
2590
|
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2436
2591
|
const headers = new Headers();
|
|
2437
2592
|
headers.set('Content-Type', 'application/json');
|
|
2438
|
-
let accessToken = undefined;
|
|
2439
2593
|
if (process.env.NODE_ENV === 'development') {
|
|
2440
2594
|
// NOTE: This cookie can only be accessed from the client side
|
|
2441
2595
|
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2442
|
-
accessToken = cookiesNext.getCookie('credentials_token');
|
|
2596
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
2597
|
+
if (accessToken) {
|
|
2598
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2599
|
+
}
|
|
2443
2600
|
}
|
|
2444
2601
|
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2445
|
-
|
|
2446
|
-
const url = `${GEN3_FENCE_API}/user/data/download/${guid}`;
|
|
2602
|
+
const url = `${GEN3_FENCE_API}/data/download/${guid}`;
|
|
2447
2603
|
try {
|
|
2448
2604
|
const response = await fetch(url, {
|
|
2449
2605
|
method: method,
|
|
@@ -2476,6 +2632,67 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2476
2632
|
throw error;
|
|
2477
2633
|
}
|
|
2478
2634
|
};
|
|
2635
|
+
/**
|
|
2636
|
+
* Retrieves a CSRF token from the server.
|
|
2637
|
+
*
|
|
2638
|
+
* This asynchronous function sends a GET request to the server's status endpoint
|
|
2639
|
+
* to fetch the CSRF token in the response. The token is expected to be included
|
|
2640
|
+
* in the JSON response under the `csrf_token` field.
|
|
2641
|
+
*
|
|
2642
|
+
* @returns {Promise<string | null>} A promise that resolves to the CSRF token as a string if successfully retrieved,
|
|
2643
|
+
* or null if the token is not present in the response.
|
|
2644
|
+
* @throws {HTTPError} Throws an HTTPError if the server response is not successful.
|
|
2645
|
+
*/ const getCSRFToken = async ()=>{
|
|
2646
|
+
const requestHeaders = new Headers({
|
|
2647
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2648
|
+
});
|
|
2649
|
+
const response = await fetch(`${GEN3_API}/_status`, {
|
|
2650
|
+
headers: requestHeaders
|
|
2651
|
+
});
|
|
2652
|
+
if (!response.ok) {
|
|
2653
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2654
|
+
}
|
|
2655
|
+
const { csrf_token: csrfToken } = await response.json();
|
|
2656
|
+
return csrfToken || null;
|
|
2657
|
+
};
|
|
2658
|
+
/**
|
|
2659
|
+
* Fetches JSON data from a specified URL using the Fetch API.
|
|
2660
|
+
*
|
|
2661
|
+
* @param {string} url - The URL to fetch the JSON data from.
|
|
2662
|
+
* @param {boolean} [requiresCSRF=false] - Indicates whether a CSRF token is required for the request.
|
|
2663
|
+
* If true, the CSRF token will be added to the request headers.
|
|
2664
|
+
* @param {string} [method=DEFAULT_METHOD] - The HTTP method to use for the request (e.g., 'GET', 'POST').
|
|
2665
|
+
* @param {unknown} [body=undefined] - The request body to send, applicable when using methods like 'POST'.
|
|
2666
|
+
*
|
|
2667
|
+
* @returns {Promise<any>} A promise that resolves to the parsed JSON data from the response.
|
|
2668
|
+
*
|
|
2669
|
+
* @throws {HTTPError} Throws an error if the HTTP response status indicates a failure.
|
|
2670
|
+
*/ const fetchJSONDataFromURL = async (url, requiresCSRF = false, method = DEFAULT_METHOD, body = undefined)=>{
|
|
2671
|
+
const requestHeaders = new Headers({
|
|
2672
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2673
|
+
});
|
|
2674
|
+
if (requiresCSRF) {
|
|
2675
|
+
const csrfToken = await getCSRFToken();
|
|
2676
|
+
if (csrfToken) {
|
|
2677
|
+
requestHeaders.set('X-CSRF-Token', csrfToken);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2681
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
2682
|
+
if (accessToken) {
|
|
2683
|
+
requestHeaders.set('Authorization', `Bearer ${accessToken}`);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
const response = await fetch(url, {
|
|
2687
|
+
method,
|
|
2688
|
+
headers: requestHeaders,
|
|
2689
|
+
body: method === 'POST' ? JSON.stringify(body) : null
|
|
2690
|
+
});
|
|
2691
|
+
if (!response.ok) {
|
|
2692
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2693
|
+
}
|
|
2694
|
+
return response.json();
|
|
2695
|
+
};
|
|
2479
2696
|
|
|
2480
2697
|
const persistor = reduxPersist.persistStore(coreStore);
|
|
2481
2698
|
const CoreProvider = ({ children })=>{
|
|
@@ -3302,7 +3519,6 @@ exports.GEN3_SOWER_API = GEN3_SOWER_API;
|
|
|
3302
3519
|
exports.GEN3_SUBMISSION_API = GEN3_SUBMISSION_API;
|
|
3303
3520
|
exports.GEN3_WORKSPACE_API = GEN3_WORKSPACE_API;
|
|
3304
3521
|
exports.HTTPError = HTTPError;
|
|
3305
|
-
exports.HTTPErrorMessages = HTTPErrorMessages;
|
|
3306
3522
|
exports.Modals = Modals;
|
|
3307
3523
|
exports.PodConditionType = PodConditionType;
|
|
3308
3524
|
exports.PodStatus = PodStatus;
|
|
@@ -3311,7 +3527,6 @@ exports.WorkspaceStatus = WorkspaceStatus;
|
|
|
3311
3527
|
exports.clearActiveWorkspaceId = clearActiveWorkspaceId;
|
|
3312
3528
|
exports.clearCohortFilters = clearCohortFilters;
|
|
3313
3529
|
exports.clearDataLibrarySelection = clearDataLibrarySelection;
|
|
3314
|
-
exports.cohortReducer = cohortReducer;
|
|
3315
3530
|
exports.convertFilterSetToGqlFilter = convertFilterSetToGqlFilter;
|
|
3316
3531
|
exports.coreStore = coreStore;
|
|
3317
3532
|
exports.createAppApiForRTKQ = createAppApiForRTKQ;
|
|
@@ -3330,6 +3545,7 @@ exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFrom
|
|
|
3330
3545
|
exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
|
|
3331
3546
|
exports.fetchFence = fetchFence;
|
|
3332
3547
|
exports.fetchFencePresignedURL = fetchFencePresignedURL;
|
|
3548
|
+
exports.fetchJSONDataFromURL = fetchJSONDataFromURL;
|
|
3333
3549
|
exports.fetchJson = fetchJson;
|
|
3334
3550
|
exports.fetchUserState = fetchUserState;
|
|
3335
3551
|
exports.fieldNameToTitle = fieldNameToTitle;
|
|
@@ -3340,6 +3556,7 @@ exports.getNumberOfItemsInDatalist = getNumberOfItemsInDatalist;
|
|
|
3340
3556
|
exports.getTimestamp = getTimestamp;
|
|
3341
3557
|
exports.graphQLAPI = graphQLAPI;
|
|
3342
3558
|
exports.graphQLWithTags = graphQLWithTags;
|
|
3559
|
+
exports.groupSharedFields = groupSharedFields;
|
|
3343
3560
|
exports.guppyAPISliceMiddleware = guppyAPISliceMiddleware;
|
|
3344
3561
|
exports.guppyApi = guppyApi;
|
|
3345
3562
|
exports.guppyApiReducer = guppyApiReducer;
|
|
@@ -3352,6 +3569,7 @@ exports.isAuthenticated = isAuthenticated;
|
|
|
3352
3569
|
exports.isCohortItem = isCohortItem;
|
|
3353
3570
|
exports.isErrorWithMessage = isErrorWithMessage;
|
|
3354
3571
|
exports.isFetchBaseQueryError = isFetchBaseQueryError;
|
|
3572
|
+
exports.isFetchError = isFetchError;
|
|
3355
3573
|
exports.isFetchParseError = isFetchParseError;
|
|
3356
3574
|
exports.isFileItem = isFileItem;
|
|
3357
3575
|
exports.isFilterEmpty = isFilterEmpty;
|
|
@@ -3366,17 +3584,20 @@ exports.isHistogramDataArrayAnEnum = isHistogramDataArrayAnEnum;
|
|
|
3366
3584
|
exports.isHistogramDataCollection = isHistogramDataCollection;
|
|
3367
3585
|
exports.isHistogramRangeData = isHistogramRangeData;
|
|
3368
3586
|
exports.isHttpStatusError = isHttpStatusError;
|
|
3587
|
+
exports.isIntersection = isIntersection;
|
|
3369
3588
|
exports.isJSONObject = isJSONObject;
|
|
3370
3589
|
exports.isJSONValue = isJSONValue;
|
|
3371
3590
|
exports.isJSONValueArray = isJSONValueArray;
|
|
3372
3591
|
exports.isNotDefined = isNotDefined;
|
|
3373
3592
|
exports.isObject = isObject;
|
|
3374
3593
|
exports.isOperationWithField = isOperationWithField;
|
|
3594
|
+
exports.isOperatorWithFieldAndArrayOfOperands = isOperatorWithFieldAndArrayOfOperands;
|
|
3375
3595
|
exports.isPending = isPending;
|
|
3376
3596
|
exports.isProgramUrl = isProgramUrl;
|
|
3377
3597
|
exports.isRootUrl = isRootUrl;
|
|
3378
3598
|
exports.isString = isString;
|
|
3379
3599
|
exports.isTimeGreaterThan = isTimeGreaterThan;
|
|
3600
|
+
exports.isUnion = isUnion;
|
|
3380
3601
|
exports.isWorkspaceActive = isWorkspaceActive;
|
|
3381
3602
|
exports.isWorkspaceRunningOrStopping = isWorkspaceRunningOrStopping;
|
|
3382
3603
|
exports.listifyMethodsFromMapping = listifyMethodsFromMapping;
|
|
@@ -3391,9 +3612,12 @@ exports.resetUserState = resetUserState;
|
|
|
3391
3612
|
exports.resourcePathFromProjectID = resourcePathFromProjectID;
|
|
3392
3613
|
exports.selectActiveWorkspaceId = selectActiveWorkspaceId;
|
|
3393
3614
|
exports.selectActiveWorkspaceStatus = selectActiveWorkspaceStatus;
|
|
3615
|
+
exports.selectAllCohortFiltersCollapsed = selectAllCohortFiltersCollapsed;
|
|
3394
3616
|
exports.selectAuthzMappingData = selectAuthzMappingData;
|
|
3395
3617
|
exports.selectCSRFToken = selectCSRFToken;
|
|
3396
3618
|
exports.selectCSRFTokenData = selectCSRFTokenData;
|
|
3619
|
+
exports.selectCohortFilterCombineMode = selectCohortFilterCombineMode;
|
|
3620
|
+
exports.selectCohortFilterExpanded = selectCohortFilterExpanded;
|
|
3397
3621
|
exports.selectCohortFilters = selectCohortFilters;
|
|
3398
3622
|
exports.selectCurrentCohort = selectCurrentCohort;
|
|
3399
3623
|
exports.selectCurrentCohortId = selectCurrentCohortId;
|
|
@@ -3408,6 +3632,9 @@ exports.selectIndexedFilterByName = selectIndexedFilterByName;
|
|
|
3408
3632
|
exports.selectPaymodelStatus = selectPaymodelStatus;
|
|
3409
3633
|
exports.selectRequestedWorkspaceStatus = selectRequestedWorkspaceStatus;
|
|
3410
3634
|
exports.selectRequestedWorkspaceStatusTimestamp = selectRequestedWorkspaceStatusTimestamp;
|
|
3635
|
+
exports.selectSharedFilters = selectSharedFilters;
|
|
3636
|
+
exports.selectSharedFiltersForFields = selectSharedFiltersForFields;
|
|
3637
|
+
exports.selectShouldShareFilters = selectShouldShareFilters;
|
|
3411
3638
|
exports.selectUser = selectUser;
|
|
3412
3639
|
exports.selectUserAuthStatus = selectUserAuthStatus;
|
|
3413
3640
|
exports.selectUserData = selectUserData;
|
|
@@ -3419,13 +3646,18 @@ exports.setActiveWorkspace = setActiveWorkspace;
|
|
|
3419
3646
|
exports.setActiveWorkspaceId = setActiveWorkspaceId;
|
|
3420
3647
|
exports.setActiveWorkspaceStatus = setActiveWorkspaceStatus;
|
|
3421
3648
|
exports.setCohortFilter = setCohortFilter;
|
|
3649
|
+
exports.setCohortFilterCombineMode = setCohortFilterCombineMode;
|
|
3422
3650
|
exports.setCohortIndexFilters = setCohortIndexFilters;
|
|
3423
3651
|
exports.setDRSHostnames = setDRSHostnames;
|
|
3424
3652
|
exports.setDataLibraryListSelection = setDataLibraryListSelection;
|
|
3425
3653
|
exports.setRequestedWorkspaceStatus = setRequestedWorkspaceStatus;
|
|
3654
|
+
exports.setSharedFilters = setSharedFilters;
|
|
3655
|
+
exports.setShouldShareFilters = setShouldShareFilters;
|
|
3426
3656
|
exports.setupCoreStore = setupCoreStore;
|
|
3427
3657
|
exports.showModal = showModal;
|
|
3428
3658
|
exports.submissionApi = submissionApi;
|
|
3659
|
+
exports.toggleCohortBuilderAllFilters = toggleCohortBuilderAllFilters;
|
|
3660
|
+
exports.toggleCohortBuilderCategoryFilter = toggleCohortBuilderCategoryFilter;
|
|
3429
3661
|
exports.trimFirstFieldNameToTitle = trimFirstFieldNameToTitle;
|
|
3430
3662
|
exports.updateCohortFilter = updateCohortFilter;
|
|
3431
3663
|
exports.useAddDataLibraryListMutation = useAddDataLibraryListMutation;
|
|
@@ -3444,6 +3676,7 @@ exports.useGetAISearchVersionQuery = useGetAISearchVersionQuery;
|
|
|
3444
3676
|
exports.useGetAccessibleDataQuery = useGetAccessibleDataQuery;
|
|
3445
3677
|
exports.useGetActivePayModelQuery = useGetActivePayModelQuery;
|
|
3446
3678
|
exports.useGetAggMDSQuery = useGetAggMDSQuery;
|
|
3679
|
+
exports.useGetAggsNoFilterSelfQuery = useGetAggsNoFilterSelfQuery;
|
|
3447
3680
|
exports.useGetAggsQuery = useGetAggsQuery;
|
|
3448
3681
|
exports.useGetAllFieldsForTypeQuery = useGetAllFieldsForTypeQuery;
|
|
3449
3682
|
exports.useGetArrayTypes = useGetArrayTypes;
|
|
@@ -3469,6 +3702,7 @@ exports.useGetMetadataByIdQuery = useGetMetadataByIdQuery;
|
|
|
3469
3702
|
exports.useGetProjectsDetailsQuery = useGetProjectsDetailsQuery;
|
|
3470
3703
|
exports.useGetProjectsQuery = useGetProjectsQuery;
|
|
3471
3704
|
exports.useGetRawDataAndTotalCountsQuery = useGetRawDataAndTotalCountsQuery;
|
|
3705
|
+
exports.useGetSharedFieldsForIndexQuery = useGetSharedFieldsForIndexQuery;
|
|
3472
3706
|
exports.useGetSowerJobListQuery = useGetSowerJobListQuery;
|
|
3473
3707
|
exports.useGetSowerJobStatusQuery = useGetSowerJobStatusQuery;
|
|
3474
3708
|
exports.useGetSowerOutputQuery = useGetSowerOutputQuery;
|