@gen3/core 0.10.79 → 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 +681 -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 +270 -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 +10 -14
- 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 +664 -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
|
|
@@ -127,7 +134,8 @@ const userAuthApi = react.createApi({
|
|
|
127
134
|
endpoint,
|
|
128
135
|
headers
|
|
129
136
|
});
|
|
130
|
-
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
138
|
+
} catch (_e) {
|
|
131
139
|
/*
|
|
132
140
|
Because an "error" response is valid for the auth requests we don't want to
|
|
133
141
|
put the request in an error state, or it will attempt the request over and over again
|
|
@@ -142,7 +150,7 @@ const userAuthApi = react.createApi({
|
|
|
142
150
|
endpoints: (builder)=>({
|
|
143
151
|
fetchUserDetails: builder.query({
|
|
144
152
|
query: ()=>({
|
|
145
|
-
endpoint: '/user
|
|
153
|
+
endpoint: '/user'
|
|
146
154
|
}),
|
|
147
155
|
transformResponse (response) {
|
|
148
156
|
return {
|
|
@@ -153,12 +161,33 @@ const userAuthApi = react.createApi({
|
|
|
153
161
|
}
|
|
154
162
|
}),
|
|
155
163
|
getCSRF: builder.query({
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
}
|
|
160
189
|
return {
|
|
161
|
-
|
|
190
|
+
error: 'Unknown Error'
|
|
162
191
|
};
|
|
163
192
|
}
|
|
164
193
|
})
|
|
@@ -220,24 +249,27 @@ const gen3ServicesReducer = gen3Api.reducer;
|
|
|
220
249
|
const gen3ServicesReducerMiddleware = gen3Api.middleware;
|
|
221
250
|
|
|
222
251
|
/**
|
|
223
|
-
* Creates a fence API endpoint for handling login processes
|
|
252
|
+
* Creates a fence API endpoint for handling login/data processes
|
|
224
253
|
* @param endpoints - defined endpoint query for logging in
|
|
225
254
|
* @returns: The generated fence login API slice
|
|
226
255
|
*/ const loginProvidersApi = gen3Api.injectEndpoints({
|
|
227
256
|
endpoints: (builder)=>({
|
|
228
257
|
getLoginProviders: builder.query({
|
|
229
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
258
|
+
query: ()=>`${GEN3_FENCE_API}/login`
|
|
230
259
|
}),
|
|
231
260
|
getDownload: builder.query({
|
|
232
|
-
query: (guid)=>`${GEN3_FENCE_API}/
|
|
261
|
+
query: (guid)=>`${GEN3_FENCE_API}/data/download/${guid}`
|
|
262
|
+
}),
|
|
263
|
+
getPresignedUrl: builder.query({
|
|
264
|
+
query: ({ guid, what })=>`${GEN3_FENCE_API}/data/${what}/${guid}`
|
|
233
265
|
})
|
|
234
266
|
})
|
|
235
267
|
});
|
|
236
|
-
const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery } = loginProvidersApi;
|
|
268
|
+
const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery, useGetPresignedUrlQuery, useLazyGetPresignedUrlQuery } = loginProvidersApi;
|
|
237
269
|
/**
|
|
238
270
|
* Logout from fence
|
|
239
271
|
*/ const logoutFence = async (redirect = '/')=>await fetchFence({
|
|
240
|
-
endpoint: `${GEN3_FENCE_API}/
|
|
272
|
+
endpoint: `${GEN3_FENCE_API}/logout?next=${GEN3_REDIRECT_URL}${redirect}`,
|
|
241
273
|
method: 'GET'
|
|
242
274
|
});
|
|
243
275
|
|
|
@@ -258,7 +290,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
258
290
|
*/ const credentialsApi = credentialsWithTags$1.injectEndpoints({
|
|
259
291
|
endpoints: (builder)=>({
|
|
260
292
|
getCredentials: builder.query({
|
|
261
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
293
|
+
query: ()=>`${GEN3_FENCE_API}/credentials/api`,
|
|
262
294
|
transformResponse: (response)=>response['jtis'],
|
|
263
295
|
// "jtis", which is an array of API keys
|
|
264
296
|
// no need to transform the response, since the API returns the correct format
|
|
@@ -268,7 +300,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
268
300
|
}),
|
|
269
301
|
addNewCredential: builder.mutation({
|
|
270
302
|
query: (csrfToken)=>({
|
|
271
|
-
url: `${GEN3_FENCE_API}/
|
|
303
|
+
url: `${GEN3_FENCE_API}/credentials/api`,
|
|
272
304
|
method: 'POST',
|
|
273
305
|
headers: {
|
|
274
306
|
'Content-Type': 'application/json',
|
|
@@ -287,7 +319,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
287
319
|
}),
|
|
288
320
|
removeCredential: builder.mutation({
|
|
289
321
|
query: ({ csrfToken, id })=>({
|
|
290
|
-
url: `${GEN3_FENCE_API}/
|
|
322
|
+
url: `${GEN3_FENCE_API}/credentials/api/${id}`,
|
|
291
323
|
method: 'DELETE',
|
|
292
324
|
headers: {
|
|
293
325
|
'Content-Type': 'application/json',
|
|
@@ -302,7 +334,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
302
334
|
}),
|
|
303
335
|
authorizeFromCredentials: builder.mutation({
|
|
304
336
|
query: (params)=>({
|
|
305
|
-
url: `${GEN3_FENCE_API}/
|
|
337
|
+
url: `${GEN3_FENCE_API}/credentials/api/access_token`,
|
|
306
338
|
method: 'POST',
|
|
307
339
|
headers: {
|
|
308
340
|
'Content-Type': 'application/json'
|
|
@@ -400,7 +432,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
400
432
|
accessToken = cookiesNext.getCookie('credentials_token');
|
|
401
433
|
}
|
|
402
434
|
return await fetchFence({
|
|
403
|
-
endpoint: '/user
|
|
435
|
+
endpoint: '/user',
|
|
404
436
|
method: 'GET',
|
|
405
437
|
headers: {
|
|
406
438
|
Accept: 'application/json',
|
|
@@ -417,7 +449,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
417
449
|
});
|
|
418
450
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
419
451
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
420
|
-
const initialState$
|
|
452
|
+
const initialState$8 = {
|
|
421
453
|
status: 'uninitialized',
|
|
422
454
|
loginStatus: 'unauthenticated',
|
|
423
455
|
error: undefined
|
|
@@ -428,9 +460,9 @@ const initialState$5 = {
|
|
|
428
460
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
429
461
|
*/ const slice$4 = toolkit.createSlice({
|
|
430
462
|
name: 'fence/user',
|
|
431
|
-
initialState: initialState$
|
|
463
|
+
initialState: initialState$8,
|
|
432
464
|
reducers: {
|
|
433
|
-
resetUserState: ()=>initialState$
|
|
465
|
+
resetUserState: ()=>initialState$8
|
|
434
466
|
},
|
|
435
467
|
extraReducers: (builder)=>{
|
|
436
468
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -600,12 +632,12 @@ const lookupGen3App = (id)=>{
|
|
|
600
632
|
return REGISTRY[id];
|
|
601
633
|
};
|
|
602
634
|
|
|
603
|
-
const initialState$
|
|
635
|
+
const initialState$7 = {
|
|
604
636
|
gen3Apps: {}
|
|
605
637
|
};
|
|
606
638
|
const slice$3 = toolkit.createSlice({
|
|
607
639
|
name: 'gen3Apps',
|
|
608
|
-
initialState: initialState$
|
|
640
|
+
initialState: initialState$7,
|
|
609
641
|
reducers: {
|
|
610
642
|
addGen3AppMetadata: (state, action)=>{
|
|
611
643
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -624,11 +656,11 @@ const { addGen3AppMetadata } = slice$3.actions;
|
|
|
624
656
|
const selectGen3AppMetadataByName = (state, appName)=>state.gen3Apps.gen3Apps[appName];
|
|
625
657
|
const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize this selector
|
|
626
658
|
|
|
627
|
-
const initialState$
|
|
659
|
+
const initialState$6 = {};
|
|
628
660
|
// TODO: document what this does
|
|
629
661
|
const slice$2 = toolkit.createSlice({
|
|
630
662
|
name: 'drsResolver',
|
|
631
|
-
initialState: initialState$
|
|
663
|
+
initialState: initialState$6,
|
|
632
664
|
reducers: {
|
|
633
665
|
setDRSHostnames: (_state, action)=>{
|
|
634
666
|
return action.payload;
|
|
@@ -650,13 +682,13 @@ const { setDRSHostnames } = slice$2.actions;
|
|
|
650
682
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
651
683
|
return Modals;
|
|
652
684
|
}({});
|
|
653
|
-
const initialState$
|
|
685
|
+
const initialState$5 = {
|
|
654
686
|
currentModal: null
|
|
655
687
|
};
|
|
656
688
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
657
689
|
const slice$1 = toolkit.createSlice({
|
|
658
690
|
name: 'modals',
|
|
659
|
-
initialState: initialState$
|
|
691
|
+
initialState: initialState$5,
|
|
660
692
|
reducers: {
|
|
661
693
|
showModal: (state, action)=>{
|
|
662
694
|
state.currentModal = action.payload.modal;
|
|
@@ -674,141 +706,6 @@ const { showModal, hideModal } = slice$1.actions;
|
|
|
674
706
|
const selectCurrentModal = (state)=>state.modals.currentModal;
|
|
675
707
|
const selectCurrentMessage = (state)=>state.modals.message;
|
|
676
708
|
|
|
677
|
-
const EmptyCohort = {
|
|
678
|
-
id: 'default',
|
|
679
|
-
name: 'Filters',
|
|
680
|
-
filters: {},
|
|
681
|
-
modified_datetime: new Date().toISOString()
|
|
682
|
-
};
|
|
683
|
-
const initialCohortState = {
|
|
684
|
-
cohort: {
|
|
685
|
-
...EmptyCohort
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
// TODO: start using this adapter
|
|
689
|
-
/*
|
|
690
|
-
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
691
|
-
sortComparer: (a, b) => {
|
|
692
|
-
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
693
|
-
else return -1;
|
|
694
|
-
},
|
|
695
|
-
});
|
|
696
|
-
*/ /**
|
|
697
|
-
* Redux slice for cohort filters
|
|
698
|
-
*/ const cohortSlice = toolkit.createSlice({
|
|
699
|
-
name: 'cohort',
|
|
700
|
-
initialState: initialCohortState,
|
|
701
|
-
reducers: {
|
|
702
|
-
// adds a filter to the cohort filter set at the given index
|
|
703
|
-
updateCohortFilter: (state, action)=>{
|
|
704
|
-
const { index, field, filter } = action.payload;
|
|
705
|
-
return {
|
|
706
|
-
cohort: {
|
|
707
|
-
...state.cohort,
|
|
708
|
-
filters: {
|
|
709
|
-
...state.cohort.filters,
|
|
710
|
-
[index]: {
|
|
711
|
-
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
712
|
-
root: {
|
|
713
|
-
...state.cohort.filters?.[index]?.root ?? {},
|
|
714
|
-
[field]: filter
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
};
|
|
720
|
-
},
|
|
721
|
-
setCohortFilter: (state, action)=>{
|
|
722
|
-
const { index, filters } = action.payload;
|
|
723
|
-
return {
|
|
724
|
-
cohort: {
|
|
725
|
-
...state.cohort,
|
|
726
|
-
filters: {
|
|
727
|
-
...state.cohort.filters,
|
|
728
|
-
[index]: filters
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
},
|
|
733
|
-
setCohortIndexFilters: (state, action)=>{
|
|
734
|
-
return {
|
|
735
|
-
cohort: {
|
|
736
|
-
...state.cohort,
|
|
737
|
-
filters: {
|
|
738
|
-
...action.payload.filters
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
},
|
|
743
|
-
// removes a filter to the cohort filter set at the given index
|
|
744
|
-
removeCohortFilter: (state, action)=>{
|
|
745
|
-
const { index, field } = action.payload;
|
|
746
|
-
const filters = state.cohort?.filters?.[index]?.root;
|
|
747
|
-
if (!filters) {
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
const { [field]: _a, ...updated } = filters;
|
|
751
|
-
return {
|
|
752
|
-
cohort: {
|
|
753
|
-
...state.cohort,
|
|
754
|
-
filters: {
|
|
755
|
-
...state.cohort.filters,
|
|
756
|
-
[index]: {
|
|
757
|
-
mode: state.cohort.filters[index].mode,
|
|
758
|
-
root: updated
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
},
|
|
764
|
-
// removes all filters from the cohort filter set at the given index
|
|
765
|
-
clearCohortFilters: (state, action)=>{
|
|
766
|
-
const { index } = action.payload;
|
|
767
|
-
return {
|
|
768
|
-
cohort: {
|
|
769
|
-
...state.cohort,
|
|
770
|
-
filters: {
|
|
771
|
-
...state.cohort.filters,
|
|
772
|
-
[index]: {
|
|
773
|
-
// empty filter set
|
|
774
|
-
mode: 'and',
|
|
775
|
-
root: {}
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
// Filter actions: addFilter, removeFilter, updateFilter
|
|
784
|
-
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
785
|
-
const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
|
|
786
|
-
const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
|
|
787
|
-
const selectCurrentCohort = (state)=>state.cohorts.cohort;
|
|
788
|
-
const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
|
|
789
|
-
/**
|
|
790
|
-
* Select a filter by its name from the current cohort. If the filter is not found
|
|
791
|
-
* returns undefined.
|
|
792
|
-
* @param state - Core
|
|
793
|
-
* @param index which cohort index to select from
|
|
794
|
-
* @param name name of the filter to select
|
|
795
|
-
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
796
|
-
return state.cohorts.cohort.filters[index]?.root[name];
|
|
797
|
-
};
|
|
798
|
-
const EmptyFilterSet = {
|
|
799
|
-
mode: 'and',
|
|
800
|
-
root: {}
|
|
801
|
-
};
|
|
802
|
-
/**
|
|
803
|
-
* Select a filter from the index.
|
|
804
|
-
* returns undefined.
|
|
805
|
-
* @param state - Core
|
|
806
|
-
* @param index which cohort index to select from
|
|
807
|
-
*/ const selectIndexFilters = (state, index)=>{
|
|
808
|
-
return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
809
|
-
};
|
|
810
|
-
const cohortReducer = cohortSlice.reducer;
|
|
811
|
-
|
|
812
709
|
const isFileItem = (item)=>{
|
|
813
710
|
return item && 'guid' in item;
|
|
814
711
|
};
|
|
@@ -1366,17 +1263,17 @@ const useDataLibrary = (useApi)=>{
|
|
|
1366
1263
|
};
|
|
1367
1264
|
};
|
|
1368
1265
|
|
|
1369
|
-
const initialState$
|
|
1266
|
+
const initialState$4 = {};
|
|
1370
1267
|
const dataLibrarySlice = toolkit.createSlice({
|
|
1371
1268
|
name: 'dataLibrary',
|
|
1372
|
-
initialState: initialState$
|
|
1269
|
+
initialState: initialState$4,
|
|
1373
1270
|
reducers: {
|
|
1374
1271
|
setDataLibraryListSelection: (state, action)=>{
|
|
1375
1272
|
const { listId, itemIds } = action.payload;
|
|
1376
1273
|
state[listId] = itemIds;
|
|
1377
1274
|
},
|
|
1378
1275
|
clearDataLibrarySelection: ()=>{
|
|
1379
|
-
return initialState$
|
|
1276
|
+
return initialState$4;
|
|
1380
1277
|
}
|
|
1381
1278
|
}
|
|
1382
1279
|
});
|
|
@@ -1442,7 +1339,7 @@ const isTimeGreaterThan = (startTime, minutes)=>{
|
|
|
1442
1339
|
};
|
|
1443
1340
|
|
|
1444
1341
|
const NO_WORKSPACE_ID = 'none';
|
|
1445
|
-
const initialState = {
|
|
1342
|
+
const initialState$3 = {
|
|
1446
1343
|
id: NO_WORKSPACE_ID,
|
|
1447
1344
|
status: WorkspaceStatus.NotFound,
|
|
1448
1345
|
requestedStatus: RequestedWorkspaceStatus.Unset,
|
|
@@ -1450,7 +1347,7 @@ const initialState = {
|
|
|
1450
1347
|
};
|
|
1451
1348
|
const slice = toolkit.createSlice({
|
|
1452
1349
|
name: 'ActiveWorkspace',
|
|
1453
|
-
initialState,
|
|
1350
|
+
initialState: initialState$3,
|
|
1454
1351
|
reducers: {
|
|
1455
1352
|
setActiveWorkspaceId: (state, action)=>{
|
|
1456
1353
|
state = {
|
|
@@ -1544,6 +1441,14 @@ const guppyApiReducer = guppyApi.reducer;
|
|
|
1544
1441
|
const isOperationWithField = (operation)=>{
|
|
1545
1442
|
return operation?.field !== undefined;
|
|
1546
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
|
+
};
|
|
1547
1452
|
const extractFilterValue = (op)=>{
|
|
1548
1453
|
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1549
1454
|
return handleOperation(valueExtractorHandler, op);
|
|
@@ -1725,6 +1630,12 @@ const isFilterSet = (input)=>{
|
|
|
1725
1630
|
}
|
|
1726
1631
|
return true;
|
|
1727
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
|
+
}
|
|
1728
1639
|
|
|
1729
1640
|
const FieldNameOverrides = {};
|
|
1730
1641
|
const COMMON_PREPOSITIONS = [
|
|
@@ -1789,65 +1700,248 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
|
1789
1700
|
];
|
|
1790
1701
|
};
|
|
1791
1702
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
json
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
});
|
|
1804
|
-
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1805
|
-
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1806
|
-
return {
|
|
1807
|
-
...acc,
|
|
1808
|
-
[key]: valueData[idx]
|
|
1809
|
-
};
|
|
1810
|
-
}, {});
|
|
1811
|
-
return results;
|
|
1812
|
-
};
|
|
1813
|
-
const fetchJson = async (url)=>{
|
|
1814
|
-
const res = await fetch(url, {
|
|
1815
|
-
method: 'GET',
|
|
1816
|
-
headers: {
|
|
1817
|
-
'Content-type': 'application/json'
|
|
1818
|
-
}
|
|
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
|
+
}));
|
|
1819
1714
|
});
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
};
|
|
1823
|
-
const useGetStatus = ()=>{
|
|
1824
|
-
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1825
|
-
return useSWR('explorerStatus', fetcher);
|
|
1826
|
-
};
|
|
1715
|
+
return flattenedJson;
|
|
1716
|
+
}
|
|
1827
1717
|
/**
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
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.
|
|
1831
1742
|
*
|
|
1832
|
-
* @param
|
|
1833
|
-
* @
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
* @
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
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)=>{
|
|
1851
1945
|
return response[params.type];
|
|
1852
1946
|
}
|
|
1853
1947
|
}),
|
|
@@ -1942,7 +2036,32 @@ const useGetStatus = ()=>{
|
|
|
1942
2036
|
return queryBody;
|
|
1943
2037
|
},
|
|
1944
2038
|
transformResponse: (response, _meta, args)=>{
|
|
1945
|
-
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] ?? {});
|
|
1946
2065
|
}
|
|
1947
2066
|
}),
|
|
1948
2067
|
getSubAggs: builder.query({
|
|
@@ -1967,7 +2086,7 @@ const useGetStatus = ()=>{
|
|
|
1967
2086
|
};
|
|
1968
2087
|
},
|
|
1969
2088
|
transformResponse: (response, _meta, args)=>{
|
|
1970
|
-
return processHistogramResponse(response
|
|
2089
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1971
2090
|
}
|
|
1972
2091
|
}),
|
|
1973
2092
|
getCounts: builder.query({
|
|
@@ -1991,7 +2110,13 @@ const useGetStatus = ()=>{
|
|
|
1991
2110
|
};
|
|
1992
2111
|
},
|
|
1993
2112
|
transformResponse: (response, _meta, args)=>{
|
|
1994
|
-
|
|
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;
|
|
1995
2120
|
}
|
|
1996
2121
|
}),
|
|
1997
2122
|
getFieldCountSummary: builder.query({
|
|
@@ -2022,7 +2147,7 @@ const useGetStatus = ()=>{
|
|
|
2022
2147
|
query: (index)=>{
|
|
2023
2148
|
return {
|
|
2024
2149
|
query: `{
|
|
2025
|
-
_mapping ${index}
|
|
2150
|
+
_mapping { ${index} }
|
|
2026
2151
|
}`
|
|
2027
2152
|
};
|
|
2028
2153
|
},
|
|
@@ -2030,6 +2155,21 @@ const useGetStatus = ()=>{
|
|
|
2030
2155
|
return response['_mapping'];
|
|
2031
2156
|
}
|
|
2032
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
|
+
}),
|
|
2033
2173
|
generalGQL: builder.query({
|
|
2034
2174
|
query: ({ query, variables })=>{
|
|
2035
2175
|
return {
|
|
@@ -2100,169 +2240,7 @@ const useGetArrayTypes = ()=>{
|
|
|
2100
2240
|
return data ? data['indices'] : {};
|
|
2101
2241
|
}
|
|
2102
2242
|
};
|
|
2103
|
-
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2104
|
-
|
|
2105
|
-
/**
|
|
2106
|
-
* Flattens a deep nested JSON object skipping
|
|
2107
|
-
* the first level to avoid potentially flattening
|
|
2108
|
-
* non-nested data.
|
|
2109
|
-
* @param {JSON} json
|
|
2110
|
-
*/ function flattenJson(json) {
|
|
2111
|
-
const flattenedJson = [];
|
|
2112
|
-
Object.keys(json).forEach((key)=>{
|
|
2113
|
-
flattenedJson.push(flat.flatten(json[key], {
|
|
2114
|
-
delimiter: '_'
|
|
2115
|
-
}));
|
|
2116
|
-
});
|
|
2117
|
-
return flattenedJson;
|
|
2118
|
-
}
|
|
2119
|
-
/**
|
|
2120
|
-
* Converts JSON based on a config.
|
|
2121
|
-
* @param {JSON} json
|
|
2122
|
-
* @param {Object} config
|
|
2123
|
-
*/ async function conversion(json, config) {
|
|
2124
|
-
return Papa.unparse(json, config);
|
|
2125
|
-
}
|
|
2126
|
-
/**
|
|
2127
|
-
* Converts JSON to a specified file format.
|
|
2128
|
-
* Defaults to JSON if file format is not supported.
|
|
2129
|
-
* @param {JSON} json
|
|
2130
|
-
* @param {string} format
|
|
2131
|
-
*/ async function jsonToFormat(json, format) {
|
|
2132
|
-
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
2133
|
-
const flatJson = await flattenJson(json);
|
|
2134
|
-
const data = await conversion(flatJson, {
|
|
2135
|
-
delimiter: FILE_DELIMITERS[format]
|
|
2136
|
-
});
|
|
2137
|
-
return data;
|
|
2138
|
-
}
|
|
2139
|
-
return json;
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
/**
|
|
2143
|
-
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2144
|
-
*
|
|
2145
|
-
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2146
|
-
* @returns {URL} - The prepared download URL as a URL object.
|
|
2147
|
-
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
2148
|
-
/**
|
|
2149
|
-
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
2150
|
-
*
|
|
2151
|
-
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
2152
|
-
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
2153
|
-
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
2154
|
-
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
2155
|
-
return {
|
|
2156
|
-
method: 'POST',
|
|
2157
|
-
headers: {
|
|
2158
|
-
'Content-Type': 'application/json',
|
|
2159
|
-
...csrfToken !== undefined && {
|
|
2160
|
-
'X-CSRF-Token': csrfToken
|
|
2161
|
-
}
|
|
2162
|
-
},
|
|
2163
|
-
body: JSON.stringify({
|
|
2164
|
-
type: parameters.type,
|
|
2165
|
-
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
2166
|
-
accessibility: parameters.accessibility,
|
|
2167
|
-
fields: parameters?.fields,
|
|
2168
|
-
sort: parameters?.sort
|
|
2169
|
-
})
|
|
2170
|
-
};
|
|
2171
|
-
};
|
|
2172
|
-
/**
|
|
2173
|
-
* Downloads a file from Guppy using the provided parameters.
|
|
2174
|
-
* It will optionally convert the data to the specified format.
|
|
2175
|
-
*
|
|
2176
|
-
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
2177
|
-
* @param onStart - The function to call when the download starts.
|
|
2178
|
-
* @param onDone - The function to call when the download is done.
|
|
2179
|
-
* @param onError - The function to call when the download fails.
|
|
2180
|
-
* @param onAbort - The function to call when the download is aborted.
|
|
2181
|
-
* @param signal - AbortSignal to use for the request.
|
|
2182
|
-
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
2183
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2184
|
-
onStart?.();
|
|
2185
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2186
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2187
|
-
fetch(url.toString(), {
|
|
2188
|
-
...fetchConfig,
|
|
2189
|
-
...signal ? {
|
|
2190
|
-
signal: signal
|
|
2191
|
-
} : {}
|
|
2192
|
-
}).then(async (response)=>{
|
|
2193
|
-
if (!response.ok) {
|
|
2194
|
-
throw new Error(response.statusText);
|
|
2195
|
-
}
|
|
2196
|
-
let jsonData = await response.json();
|
|
2197
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2198
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2199
|
-
jsonData = jsonpathPlus.JSONPath({
|
|
2200
|
-
json: jsonData,
|
|
2201
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2202
|
-
resultType: 'value'
|
|
2203
|
-
});
|
|
2204
|
-
}
|
|
2205
|
-
// convert the data to the specified format and return a Blob
|
|
2206
|
-
let str = '';
|
|
2207
|
-
if (parameters.format === 'json') {
|
|
2208
|
-
str = JSON.stringify(jsonData);
|
|
2209
|
-
} else {
|
|
2210
|
-
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
2211
|
-
if (isJSONObject(convertedData)) {
|
|
2212
|
-
str = JSON.stringify(convertedData, null, 2);
|
|
2213
|
-
} else {
|
|
2214
|
-
str = convertedData;
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
return new Blob([
|
|
2218
|
-
str
|
|
2219
|
-
], {
|
|
2220
|
-
type: 'application/json'
|
|
2221
|
-
});
|
|
2222
|
-
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
2223
|
-
// Abort is handle as an exception
|
|
2224
|
-
if (error.name == 'AbortError') {
|
|
2225
|
-
// handle abort()
|
|
2226
|
-
onAbort?.();
|
|
2227
|
-
}
|
|
2228
|
-
onError?.(error);
|
|
2229
|
-
});
|
|
2230
|
-
};
|
|
2231
|
-
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
2232
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2233
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2234
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2235
|
-
try {
|
|
2236
|
-
const response = await fetch(url.toString(), {
|
|
2237
|
-
...fetchConfig,
|
|
2238
|
-
...signal ? {
|
|
2239
|
-
signal: signal
|
|
2240
|
-
} : {}
|
|
2241
|
-
});
|
|
2242
|
-
let jsonData = await response.json();
|
|
2243
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2244
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2245
|
-
jsonData = jsonpathPlus.JSONPath({
|
|
2246
|
-
json: jsonData,
|
|
2247
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2248
|
-
resultType: 'value'
|
|
2249
|
-
});
|
|
2250
|
-
}
|
|
2251
|
-
// convert the data to the specified format and return a Blob
|
|
2252
|
-
return jsonData;
|
|
2253
|
-
} catch (error) {
|
|
2254
|
-
// Abort is handle as an exception
|
|
2255
|
-
if (error.name == 'AbortError') {
|
|
2256
|
-
// handle abort()
|
|
2257
|
-
onAbort?.();
|
|
2258
|
-
}
|
|
2259
|
-
throw new Error(error);
|
|
2260
|
-
}
|
|
2261
|
-
};
|
|
2262
|
-
const useGetIndexFields = (index)=>{
|
|
2263
|
-
const { data } = useGetFieldsForIndexQuery(index);
|
|
2264
|
-
return data ?? [];
|
|
2265
|
-
};
|
|
2243
|
+
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useGetAggsNoFilterSelfQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetSharedFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2266
2244
|
|
|
2267
2245
|
/**
|
|
2268
2246
|
* Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
|
|
@@ -2298,13 +2276,230 @@ const useGetIndexFields = (index)=>{
|
|
|
2298
2276
|
});
|
|
2299
2277
|
const { useDownloadFromGuppyMutation } = downloadRequestApi;
|
|
2300
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
|
+
|
|
2301
2496
|
const rootReducer = toolkit.combineReducers({
|
|
2302
2497
|
gen3Services: gen3ServicesReducer,
|
|
2303
2498
|
user: userReducer,
|
|
2304
2499
|
gen3Apps: gen3AppReducer,
|
|
2305
2500
|
drsHostnames: drsHostnamesReducer,
|
|
2306
2501
|
modals: modalReducer,
|
|
2307
|
-
cohorts:
|
|
2502
|
+
cohorts: cohortReducers,
|
|
2308
2503
|
activeWorkspace: activeWorkspaceReducer,
|
|
2309
2504
|
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2310
2505
|
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
@@ -2333,7 +2528,7 @@ const persistConfig = {
|
|
|
2333
2528
|
version: 1,
|
|
2334
2529
|
storage,
|
|
2335
2530
|
whitelist: [
|
|
2336
|
-
'
|
|
2531
|
+
'cohort',
|
|
2337
2532
|
'activeWorkspace'
|
|
2338
2533
|
]
|
|
2339
2534
|
};
|
|
@@ -2358,7 +2553,7 @@ const coreStore = setupCoreStore();
|
|
|
2358
2553
|
query.setupListeners(coreStore.dispatch);
|
|
2359
2554
|
|
|
2360
2555
|
const isNotDefined = (x)=>{
|
|
2361
|
-
return x === undefined || x === null || x ===
|
|
2556
|
+
return x === undefined || x === null || x === void 0;
|
|
2362
2557
|
};
|
|
2363
2558
|
const isObject = (x)=>{
|
|
2364
2559
|
return typeof x === 'object';
|
|
@@ -2377,51 +2572,14 @@ const isString = (x)=>{
|
|
|
2377
2572
|
* @returns {URL} - The prepared download URL as a URL object.
|
|
2378
2573
|
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2379
2574
|
|
|
2380
|
-
const
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
406: 'Not Acceptable',
|
|
2389
|
-
407: 'Proxy Authentication Required',
|
|
2390
|
-
408: 'Request Timeout',
|
|
2391
|
-
409: 'Conflict',
|
|
2392
|
-
410: 'Gone',
|
|
2393
|
-
411: 'Length Required',
|
|
2394
|
-
412: 'Precondition Failed',
|
|
2395
|
-
413: 'Payload Too Large',
|
|
2396
|
-
414: 'URI Too Long',
|
|
2397
|
-
415: 'Unsupported Media Type',
|
|
2398
|
-
416: 'Range Not Satisfiable',
|
|
2399
|
-
417: 'Expectation Failed',
|
|
2400
|
-
418: "I'm a teapot",
|
|
2401
|
-
421: 'Misdirected Request',
|
|
2402
|
-
422: 'Unprocessable Entity',
|
|
2403
|
-
423: 'Locked',
|
|
2404
|
-
424: 'Failed Dependency',
|
|
2405
|
-
425: 'Too Early',
|
|
2406
|
-
426: 'Upgrade Required',
|
|
2407
|
-
428: 'Precondition Required',
|
|
2408
|
-
429: 'Too Many Requests',
|
|
2409
|
-
431: 'Request Header Fields Too Large',
|
|
2410
|
-
451: 'Unavailable For Legal Reasons',
|
|
2411
|
-
// 5xx Server Errors
|
|
2412
|
-
500: 'Internal Server Error',
|
|
2413
|
-
501: 'Not Implemented',
|
|
2414
|
-
502: 'Bad Gateway',
|
|
2415
|
-
503: 'Service Unavailable',
|
|
2416
|
-
504: 'Gateway Timeout',
|
|
2417
|
-
505: 'HTTP Version Not Supported',
|
|
2418
|
-
506: 'Variant Also Negotiates',
|
|
2419
|
-
507: 'Insufficient Storage',
|
|
2420
|
-
508: 'Loop Detected',
|
|
2421
|
-
510: 'Not Extended',
|
|
2422
|
-
511: 'Network Authentication Required'
|
|
2423
|
-
};
|
|
2424
|
-
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 {
|
|
2425
2583
|
constructor(status, message, responseData){
|
|
2426
2584
|
super(message), this.status = status, this.responseData = responseData;
|
|
2427
2585
|
this.name = 'HTTPError';
|
|
@@ -2431,15 +2589,16 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2431
2589
|
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2432
2590
|
const headers = new Headers();
|
|
2433
2591
|
headers.set('Content-Type', 'application/json');
|
|
2434
|
-
let accessToken = undefined;
|
|
2435
2592
|
if (process.env.NODE_ENV === 'development') {
|
|
2436
2593
|
// NOTE: This cookie can only be accessed from the client side
|
|
2437
2594
|
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2438
|
-
accessToken = cookiesNext.getCookie('credentials_token');
|
|
2595
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
2596
|
+
if (accessToken) {
|
|
2597
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2598
|
+
}
|
|
2439
2599
|
}
|
|
2440
2600
|
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2441
|
-
|
|
2442
|
-
const url = `${GEN3_FENCE_API}/user/data/download/${guid}`;
|
|
2601
|
+
const url = `${GEN3_FENCE_API}/data/download/${guid}`;
|
|
2443
2602
|
try {
|
|
2444
2603
|
const response = await fetch(url, {
|
|
2445
2604
|
method: method,
|
|
@@ -2472,6 +2631,67 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2472
2631
|
throw error;
|
|
2473
2632
|
}
|
|
2474
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
|
+
};
|
|
2475
2695
|
|
|
2476
2696
|
const persistor = reduxPersist.persistStore(coreStore);
|
|
2477
2697
|
const CoreProvider = ({ children })=>{
|
|
@@ -3298,7 +3518,6 @@ exports.GEN3_SOWER_API = GEN3_SOWER_API;
|
|
|
3298
3518
|
exports.GEN3_SUBMISSION_API = GEN3_SUBMISSION_API;
|
|
3299
3519
|
exports.GEN3_WORKSPACE_API = GEN3_WORKSPACE_API;
|
|
3300
3520
|
exports.HTTPError = HTTPError;
|
|
3301
|
-
exports.HTTPErrorMessages = HTTPErrorMessages;
|
|
3302
3521
|
exports.Modals = Modals;
|
|
3303
3522
|
exports.PodConditionType = PodConditionType;
|
|
3304
3523
|
exports.PodStatus = PodStatus;
|
|
@@ -3307,7 +3526,6 @@ exports.WorkspaceStatus = WorkspaceStatus;
|
|
|
3307
3526
|
exports.clearActiveWorkspaceId = clearActiveWorkspaceId;
|
|
3308
3527
|
exports.clearCohortFilters = clearCohortFilters;
|
|
3309
3528
|
exports.clearDataLibrarySelection = clearDataLibrarySelection;
|
|
3310
|
-
exports.cohortReducer = cohortReducer;
|
|
3311
3529
|
exports.convertFilterSetToGqlFilter = convertFilterSetToGqlFilter;
|
|
3312
3530
|
exports.coreStore = coreStore;
|
|
3313
3531
|
exports.createAppApiForRTKQ = createAppApiForRTKQ;
|
|
@@ -3326,6 +3544,7 @@ exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFrom
|
|
|
3326
3544
|
exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
|
|
3327
3545
|
exports.fetchFence = fetchFence;
|
|
3328
3546
|
exports.fetchFencePresignedURL = fetchFencePresignedURL;
|
|
3547
|
+
exports.fetchJSONDataFromURL = fetchJSONDataFromURL;
|
|
3329
3548
|
exports.fetchJson = fetchJson;
|
|
3330
3549
|
exports.fetchUserState = fetchUserState;
|
|
3331
3550
|
exports.fieldNameToTitle = fieldNameToTitle;
|
|
@@ -3336,6 +3555,7 @@ exports.getNumberOfItemsInDatalist = getNumberOfItemsInDatalist;
|
|
|
3336
3555
|
exports.getTimestamp = getTimestamp;
|
|
3337
3556
|
exports.graphQLAPI = graphQLAPI;
|
|
3338
3557
|
exports.graphQLWithTags = graphQLWithTags;
|
|
3558
|
+
exports.groupSharedFields = groupSharedFields;
|
|
3339
3559
|
exports.guppyAPISliceMiddleware = guppyAPISliceMiddleware;
|
|
3340
3560
|
exports.guppyApi = guppyApi;
|
|
3341
3561
|
exports.guppyApiReducer = guppyApiReducer;
|
|
@@ -3348,6 +3568,7 @@ exports.isAuthenticated = isAuthenticated;
|
|
|
3348
3568
|
exports.isCohortItem = isCohortItem;
|
|
3349
3569
|
exports.isErrorWithMessage = isErrorWithMessage;
|
|
3350
3570
|
exports.isFetchBaseQueryError = isFetchBaseQueryError;
|
|
3571
|
+
exports.isFetchError = isFetchError;
|
|
3351
3572
|
exports.isFetchParseError = isFetchParseError;
|
|
3352
3573
|
exports.isFileItem = isFileItem;
|
|
3353
3574
|
exports.isFilterEmpty = isFilterEmpty;
|
|
@@ -3362,17 +3583,20 @@ exports.isHistogramDataArrayAnEnum = isHistogramDataArrayAnEnum;
|
|
|
3362
3583
|
exports.isHistogramDataCollection = isHistogramDataCollection;
|
|
3363
3584
|
exports.isHistogramRangeData = isHistogramRangeData;
|
|
3364
3585
|
exports.isHttpStatusError = isHttpStatusError;
|
|
3586
|
+
exports.isIntersection = isIntersection;
|
|
3365
3587
|
exports.isJSONObject = isJSONObject;
|
|
3366
3588
|
exports.isJSONValue = isJSONValue;
|
|
3367
3589
|
exports.isJSONValueArray = isJSONValueArray;
|
|
3368
3590
|
exports.isNotDefined = isNotDefined;
|
|
3369
3591
|
exports.isObject = isObject;
|
|
3370
3592
|
exports.isOperationWithField = isOperationWithField;
|
|
3593
|
+
exports.isOperatorWithFieldAndArrayOfOperands = isOperatorWithFieldAndArrayOfOperands;
|
|
3371
3594
|
exports.isPending = isPending;
|
|
3372
3595
|
exports.isProgramUrl = isProgramUrl;
|
|
3373
3596
|
exports.isRootUrl = isRootUrl;
|
|
3374
3597
|
exports.isString = isString;
|
|
3375
3598
|
exports.isTimeGreaterThan = isTimeGreaterThan;
|
|
3599
|
+
exports.isUnion = isUnion;
|
|
3376
3600
|
exports.isWorkspaceActive = isWorkspaceActive;
|
|
3377
3601
|
exports.isWorkspaceRunningOrStopping = isWorkspaceRunningOrStopping;
|
|
3378
3602
|
exports.listifyMethodsFromMapping = listifyMethodsFromMapping;
|
|
@@ -3387,9 +3611,12 @@ exports.resetUserState = resetUserState;
|
|
|
3387
3611
|
exports.resourcePathFromProjectID = resourcePathFromProjectID;
|
|
3388
3612
|
exports.selectActiveWorkspaceId = selectActiveWorkspaceId;
|
|
3389
3613
|
exports.selectActiveWorkspaceStatus = selectActiveWorkspaceStatus;
|
|
3614
|
+
exports.selectAllCohortFiltersCollapsed = selectAllCohortFiltersCollapsed;
|
|
3390
3615
|
exports.selectAuthzMappingData = selectAuthzMappingData;
|
|
3391
3616
|
exports.selectCSRFToken = selectCSRFToken;
|
|
3392
3617
|
exports.selectCSRFTokenData = selectCSRFTokenData;
|
|
3618
|
+
exports.selectCohortFilterCombineMode = selectCohortFilterCombineMode;
|
|
3619
|
+
exports.selectCohortFilterExpanded = selectCohortFilterExpanded;
|
|
3393
3620
|
exports.selectCohortFilters = selectCohortFilters;
|
|
3394
3621
|
exports.selectCurrentCohort = selectCurrentCohort;
|
|
3395
3622
|
exports.selectCurrentCohortId = selectCurrentCohortId;
|
|
@@ -3404,6 +3631,9 @@ exports.selectIndexedFilterByName = selectIndexedFilterByName;
|
|
|
3404
3631
|
exports.selectPaymodelStatus = selectPaymodelStatus;
|
|
3405
3632
|
exports.selectRequestedWorkspaceStatus = selectRequestedWorkspaceStatus;
|
|
3406
3633
|
exports.selectRequestedWorkspaceStatusTimestamp = selectRequestedWorkspaceStatusTimestamp;
|
|
3634
|
+
exports.selectSharedFilters = selectSharedFilters;
|
|
3635
|
+
exports.selectSharedFiltersForFields = selectSharedFiltersForFields;
|
|
3636
|
+
exports.selectShouldShareFilters = selectShouldShareFilters;
|
|
3407
3637
|
exports.selectUser = selectUser;
|
|
3408
3638
|
exports.selectUserAuthStatus = selectUserAuthStatus;
|
|
3409
3639
|
exports.selectUserData = selectUserData;
|
|
@@ -3415,13 +3645,18 @@ exports.setActiveWorkspace = setActiveWorkspace;
|
|
|
3415
3645
|
exports.setActiveWorkspaceId = setActiveWorkspaceId;
|
|
3416
3646
|
exports.setActiveWorkspaceStatus = setActiveWorkspaceStatus;
|
|
3417
3647
|
exports.setCohortFilter = setCohortFilter;
|
|
3648
|
+
exports.setCohortFilterCombineMode = setCohortFilterCombineMode;
|
|
3418
3649
|
exports.setCohortIndexFilters = setCohortIndexFilters;
|
|
3419
3650
|
exports.setDRSHostnames = setDRSHostnames;
|
|
3420
3651
|
exports.setDataLibraryListSelection = setDataLibraryListSelection;
|
|
3421
3652
|
exports.setRequestedWorkspaceStatus = setRequestedWorkspaceStatus;
|
|
3653
|
+
exports.setSharedFilters = setSharedFilters;
|
|
3654
|
+
exports.setShouldShareFilters = setShouldShareFilters;
|
|
3422
3655
|
exports.setupCoreStore = setupCoreStore;
|
|
3423
3656
|
exports.showModal = showModal;
|
|
3424
3657
|
exports.submissionApi = submissionApi;
|
|
3658
|
+
exports.toggleCohortBuilderAllFilters = toggleCohortBuilderAllFilters;
|
|
3659
|
+
exports.toggleCohortBuilderCategoryFilter = toggleCohortBuilderCategoryFilter;
|
|
3425
3660
|
exports.trimFirstFieldNameToTitle = trimFirstFieldNameToTitle;
|
|
3426
3661
|
exports.updateCohortFilter = updateCohortFilter;
|
|
3427
3662
|
exports.useAddDataLibraryListMutation = useAddDataLibraryListMutation;
|
|
@@ -3440,6 +3675,7 @@ exports.useGetAISearchVersionQuery = useGetAISearchVersionQuery;
|
|
|
3440
3675
|
exports.useGetAccessibleDataQuery = useGetAccessibleDataQuery;
|
|
3441
3676
|
exports.useGetActivePayModelQuery = useGetActivePayModelQuery;
|
|
3442
3677
|
exports.useGetAggMDSQuery = useGetAggMDSQuery;
|
|
3678
|
+
exports.useGetAggsNoFilterSelfQuery = useGetAggsNoFilterSelfQuery;
|
|
3443
3679
|
exports.useGetAggsQuery = useGetAggsQuery;
|
|
3444
3680
|
exports.useGetAllFieldsForTypeQuery = useGetAllFieldsForTypeQuery;
|
|
3445
3681
|
exports.useGetArrayTypes = useGetArrayTypes;
|
|
@@ -3465,6 +3701,7 @@ exports.useGetMetadataByIdQuery = useGetMetadataByIdQuery;
|
|
|
3465
3701
|
exports.useGetProjectsDetailsQuery = useGetProjectsDetailsQuery;
|
|
3466
3702
|
exports.useGetProjectsQuery = useGetProjectsQuery;
|
|
3467
3703
|
exports.useGetRawDataAndTotalCountsQuery = useGetRawDataAndTotalCountsQuery;
|
|
3704
|
+
exports.useGetSharedFieldsForIndexQuery = useGetSharedFieldsForIndexQuery;
|
|
3468
3705
|
exports.useGetSowerJobListQuery = useGetSowerJobListQuery;
|
|
3469
3706
|
exports.useGetSowerJobStatusQuery = useGetSowerJobStatusQuery;
|
|
3470
3707
|
exports.useGetSowerOutputQuery = useGetSowerOutputQuery;
|