@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/esm/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createApi, fetchBaseQuery, buildCreateApi, coreModule, reactHooksModule
|
|
|
3
3
|
import { getCookie } from 'cookies-next';
|
|
4
4
|
import { QueryStatus, setupListeners } from '@reduxjs/toolkit/query';
|
|
5
5
|
import * as React from 'react';
|
|
6
|
-
import React__default, {
|
|
6
|
+
import React__default, { useEffect, useRef, useState } from 'react';
|
|
7
7
|
import { useSelector, useDispatch, Provider, createSelectorHook, createDispatchHook, createStoreHook } from 'react-redux';
|
|
8
8
|
import { isEqual, isArray as isArray$1 } from 'lodash';
|
|
9
9
|
import { openDB } from 'idb';
|
|
@@ -18,7 +18,7 @@ import Queue from 'queue';
|
|
|
18
18
|
import { v5 } from 'uuid';
|
|
19
19
|
import { CookiesProvider } from 'react-cookie';
|
|
20
20
|
|
|
21
|
-
const GEN3_COMMONS_NAME = process.env.
|
|
21
|
+
const GEN3_COMMONS_NAME = process.env.NEXT_PUBLIC_GEN3_COMMONS_NAME || 'gen3';
|
|
22
22
|
const GEN3_API = process.env.NEXT_PUBLIC_GEN3_API || '';
|
|
23
23
|
const GEN3_DOMAIN = process.env.NEXT_PUBLIC_GEN3_DOMAIN || '';
|
|
24
24
|
/**
|
|
@@ -26,7 +26,7 @@ const GEN3_DOMAIN = process.env.NEXT_PUBLIC_GEN3_DOMAIN || '';
|
|
|
26
26
|
*/ const GEN3_GUPPY_API = process.env.NEXT_PUBLIC_GEN3_GUPPY_API || `${GEN3_API}/guppy`;
|
|
27
27
|
const GEN3_MDS_API = process.env.NEXT_PUBLIC_GEN3_MDS_API || `${GEN3_API}/mds`;
|
|
28
28
|
const GEN3_DOWNLOADS_ENDPOINT = process.env.NEXT_PUBLIC_GEN3_DOWNLOADS_ENDPOINT || 'downloads';
|
|
29
|
-
const GEN3_FENCE_API = process.env.NEXT_PUBLIC_GEN3_FENCE_API || GEN3_API
|
|
29
|
+
const GEN3_FENCE_API = process.env.NEXT_PUBLIC_GEN3_FENCE_API || `${GEN3_API}/user`;
|
|
30
30
|
const GEN3_AI_SEARCH_API = process.env.NEXT_PUBLIC_GEN3_AI_SEARCH_API || `${GEN3_API}/ai-search`;
|
|
31
31
|
const GEN3_AUTHZ_API = process.env.NEXT_PUBLIC_GEN3_AUTHZ_API || `${GEN3_API}/authz`;
|
|
32
32
|
const GEN3_REDIRECT_URL = process.env.NEXT_PUBLIC_GEN3_REDIRECT_URL || GEN3_API;
|
|
@@ -47,6 +47,13 @@ const FILE_DELIMITERS = {
|
|
|
47
47
|
csv: ','
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
const isFetchError = (obj)=>{
|
|
51
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const { url, status, statusText, text } = obj;
|
|
55
|
+
return typeof url === 'string' && typeof status === 'number' && typeof statusText === 'string' && typeof text === 'string';
|
|
56
|
+
};
|
|
50
57
|
/**
|
|
51
58
|
* Template for fence error response dict
|
|
52
59
|
* @returns: An error dict response from a RESTFUL API request
|
|
@@ -123,7 +130,7 @@ const userAuthApi = createApi({
|
|
|
123
130
|
endpoints: (builder)=>({
|
|
124
131
|
fetchUserDetails: builder.query({
|
|
125
132
|
query: ()=>({
|
|
126
|
-
endpoint: '/user
|
|
133
|
+
endpoint: '/user'
|
|
127
134
|
}),
|
|
128
135
|
transformResponse (response) {
|
|
129
136
|
return {
|
|
@@ -134,12 +141,33 @@ const userAuthApi = createApi({
|
|
|
134
141
|
}
|
|
135
142
|
}),
|
|
136
143
|
getCSRF: builder.query({
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
queryFn: async ()=>{
|
|
145
|
+
const headers = {
|
|
146
|
+
Accept: 'application/json',
|
|
147
|
+
'Content-Type': 'application/json'
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
const res = await fetch(`${GEN3_API}/_status`, {
|
|
151
|
+
headers: headers
|
|
152
|
+
});
|
|
153
|
+
if (res.ok) {
|
|
154
|
+
const jsonData = await res.json();
|
|
155
|
+
const token = jsonData?.data?.csrf ?? '';
|
|
156
|
+
return {
|
|
157
|
+
data: {
|
|
158
|
+
csrfToken: token
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error instanceof Error) {
|
|
164
|
+
return {
|
|
165
|
+
error: error
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
141
169
|
return {
|
|
142
|
-
|
|
170
|
+
error: 'Unknown Error'
|
|
143
171
|
};
|
|
144
172
|
}
|
|
145
173
|
})
|
|
@@ -207,13 +235,13 @@ const gen3ServicesReducerMiddleware = gen3Api.middleware;
|
|
|
207
235
|
*/ const loginProvidersApi = gen3Api.injectEndpoints({
|
|
208
236
|
endpoints: (builder)=>({
|
|
209
237
|
getLoginProviders: builder.query({
|
|
210
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
238
|
+
query: ()=>`${GEN3_FENCE_API}/login`
|
|
211
239
|
}),
|
|
212
240
|
getDownload: builder.query({
|
|
213
|
-
query: (guid)=>`${GEN3_FENCE_API}/
|
|
241
|
+
query: (guid)=>`${GEN3_FENCE_API}/data/download/${guid}`
|
|
214
242
|
}),
|
|
215
243
|
getPresignedUrl: builder.query({
|
|
216
|
-
query: ({ guid, what })=>`${GEN3_FENCE_API}/
|
|
244
|
+
query: ({ guid, what })=>`${GEN3_FENCE_API}/data/${what}/${guid}`
|
|
217
245
|
})
|
|
218
246
|
})
|
|
219
247
|
});
|
|
@@ -221,7 +249,7 @@ const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery,
|
|
|
221
249
|
/**
|
|
222
250
|
* Logout from fence
|
|
223
251
|
*/ const logoutFence = async (redirect = '/')=>await fetchFence({
|
|
224
|
-
endpoint: `${GEN3_FENCE_API}/
|
|
252
|
+
endpoint: `${GEN3_FENCE_API}/logout?next=${GEN3_REDIRECT_URL}${redirect}`,
|
|
225
253
|
method: 'GET'
|
|
226
254
|
});
|
|
227
255
|
|
|
@@ -242,7 +270,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
242
270
|
*/ const credentialsApi = credentialsWithTags$1.injectEndpoints({
|
|
243
271
|
endpoints: (builder)=>({
|
|
244
272
|
getCredentials: builder.query({
|
|
245
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
273
|
+
query: ()=>`${GEN3_FENCE_API}/credentials/api`,
|
|
246
274
|
transformResponse: (response)=>response['jtis'],
|
|
247
275
|
// "jtis", which is an array of API keys
|
|
248
276
|
// no need to transform the response, since the API returns the correct format
|
|
@@ -252,7 +280,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
252
280
|
}),
|
|
253
281
|
addNewCredential: builder.mutation({
|
|
254
282
|
query: (csrfToken)=>({
|
|
255
|
-
url: `${GEN3_FENCE_API}/
|
|
283
|
+
url: `${GEN3_FENCE_API}/credentials/api`,
|
|
256
284
|
method: 'POST',
|
|
257
285
|
headers: {
|
|
258
286
|
'Content-Type': 'application/json',
|
|
@@ -271,7 +299,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
271
299
|
}),
|
|
272
300
|
removeCredential: builder.mutation({
|
|
273
301
|
query: ({ csrfToken, id })=>({
|
|
274
|
-
url: `${GEN3_FENCE_API}/
|
|
302
|
+
url: `${GEN3_FENCE_API}/credentials/api/${id}`,
|
|
275
303
|
method: 'DELETE',
|
|
276
304
|
headers: {
|
|
277
305
|
'Content-Type': 'application/json',
|
|
@@ -286,7 +314,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
286
314
|
}),
|
|
287
315
|
authorizeFromCredentials: builder.mutation({
|
|
288
316
|
query: (params)=>({
|
|
289
|
-
url: `${GEN3_FENCE_API}/
|
|
317
|
+
url: `${GEN3_FENCE_API}/credentials/api/access_token`,
|
|
290
318
|
method: 'POST',
|
|
291
319
|
headers: {
|
|
292
320
|
'Content-Type': 'application/json'
|
|
@@ -384,7 +412,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
384
412
|
accessToken = getCookie('credentials_token');
|
|
385
413
|
}
|
|
386
414
|
return await fetchFence({
|
|
387
|
-
endpoint: '/user
|
|
415
|
+
endpoint: '/user',
|
|
388
416
|
method: 'GET',
|
|
389
417
|
headers: {
|
|
390
418
|
Accept: 'application/json',
|
|
@@ -401,7 +429,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
401
429
|
});
|
|
402
430
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
403
431
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
404
|
-
const initialState$
|
|
432
|
+
const initialState$8 = {
|
|
405
433
|
status: 'uninitialized',
|
|
406
434
|
loginStatus: 'unauthenticated',
|
|
407
435
|
error: undefined
|
|
@@ -412,9 +440,9 @@ const initialState$5 = {
|
|
|
412
440
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
413
441
|
*/ const slice$4 = createSlice({
|
|
414
442
|
name: 'fence/user',
|
|
415
|
-
initialState: initialState$
|
|
443
|
+
initialState: initialState$8,
|
|
416
444
|
reducers: {
|
|
417
|
-
resetUserState: ()=>initialState$
|
|
445
|
+
resetUserState: ()=>initialState$8
|
|
418
446
|
},
|
|
419
447
|
extraReducers: (builder)=>{
|
|
420
448
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -584,12 +612,12 @@ const lookupGen3App = (id)=>{
|
|
|
584
612
|
return REGISTRY[id];
|
|
585
613
|
};
|
|
586
614
|
|
|
587
|
-
const initialState$
|
|
615
|
+
const initialState$7 = {
|
|
588
616
|
gen3Apps: {}
|
|
589
617
|
};
|
|
590
618
|
const slice$3 = createSlice({
|
|
591
619
|
name: 'gen3Apps',
|
|
592
|
-
initialState: initialState$
|
|
620
|
+
initialState: initialState$7,
|
|
593
621
|
reducers: {
|
|
594
622
|
addGen3AppMetadata: (state, action)=>{
|
|
595
623
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -608,11 +636,11 @@ const { addGen3AppMetadata } = slice$3.actions;
|
|
|
608
636
|
const selectGen3AppMetadataByName = (state, appName)=>state.gen3Apps.gen3Apps[appName];
|
|
609
637
|
const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize this selector
|
|
610
638
|
|
|
611
|
-
const initialState$
|
|
639
|
+
const initialState$6 = {};
|
|
612
640
|
// TODO: document what this does
|
|
613
641
|
const slice$2 = createSlice({
|
|
614
642
|
name: 'drsResolver',
|
|
615
|
-
initialState: initialState$
|
|
643
|
+
initialState: initialState$6,
|
|
616
644
|
reducers: {
|
|
617
645
|
setDRSHostnames: (_state, action)=>{
|
|
618
646
|
return action.payload;
|
|
@@ -634,13 +662,13 @@ const { setDRSHostnames } = slice$2.actions;
|
|
|
634
662
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
635
663
|
return Modals;
|
|
636
664
|
}({});
|
|
637
|
-
const initialState$
|
|
665
|
+
const initialState$5 = {
|
|
638
666
|
currentModal: null
|
|
639
667
|
};
|
|
640
668
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
641
669
|
const slice$1 = createSlice({
|
|
642
670
|
name: 'modals',
|
|
643
|
-
initialState: initialState$
|
|
671
|
+
initialState: initialState$5,
|
|
644
672
|
reducers: {
|
|
645
673
|
showModal: (state, action)=>{
|
|
646
674
|
state.currentModal = action.payload.modal;
|
|
@@ -658,141 +686,6 @@ const { showModal, hideModal } = slice$1.actions;
|
|
|
658
686
|
const selectCurrentModal = (state)=>state.modals.currentModal;
|
|
659
687
|
const selectCurrentMessage = (state)=>state.modals.message;
|
|
660
688
|
|
|
661
|
-
const EmptyCohort = {
|
|
662
|
-
id: 'default',
|
|
663
|
-
name: 'Filters',
|
|
664
|
-
filters: {},
|
|
665
|
-
modified_datetime: new Date().toISOString()
|
|
666
|
-
};
|
|
667
|
-
const initialCohortState = {
|
|
668
|
-
cohort: {
|
|
669
|
-
...EmptyCohort
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
// TODO: start using this adapter
|
|
673
|
-
/*
|
|
674
|
-
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
675
|
-
sortComparer: (a, b) => {
|
|
676
|
-
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
677
|
-
else return -1;
|
|
678
|
-
},
|
|
679
|
-
});
|
|
680
|
-
*/ /**
|
|
681
|
-
* Redux slice for cohort filters
|
|
682
|
-
*/ const cohortSlice = createSlice({
|
|
683
|
-
name: 'cohort',
|
|
684
|
-
initialState: initialCohortState,
|
|
685
|
-
reducers: {
|
|
686
|
-
// adds a filter to the cohort filter set at the given index
|
|
687
|
-
updateCohortFilter: (state, action)=>{
|
|
688
|
-
const { index, field, filter } = action.payload;
|
|
689
|
-
return {
|
|
690
|
-
cohort: {
|
|
691
|
-
...state.cohort,
|
|
692
|
-
filters: {
|
|
693
|
-
...state.cohort.filters,
|
|
694
|
-
[index]: {
|
|
695
|
-
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
696
|
-
root: {
|
|
697
|
-
...state.cohort.filters?.[index]?.root ?? {},
|
|
698
|
-
[field]: filter
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
},
|
|
705
|
-
setCohortFilter: (state, action)=>{
|
|
706
|
-
const { index, filters } = action.payload;
|
|
707
|
-
return {
|
|
708
|
-
cohort: {
|
|
709
|
-
...state.cohort,
|
|
710
|
-
filters: {
|
|
711
|
-
...state.cohort.filters,
|
|
712
|
-
[index]: filters
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
};
|
|
716
|
-
},
|
|
717
|
-
setCohortIndexFilters: (state, action)=>{
|
|
718
|
-
return {
|
|
719
|
-
cohort: {
|
|
720
|
-
...state.cohort,
|
|
721
|
-
filters: {
|
|
722
|
-
...action.payload.filters
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
},
|
|
727
|
-
// removes a filter to the cohort filter set at the given index
|
|
728
|
-
removeCohortFilter: (state, action)=>{
|
|
729
|
-
const { index, field } = action.payload;
|
|
730
|
-
const filters = state.cohort?.filters?.[index]?.root;
|
|
731
|
-
if (!filters) {
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
const { [field]: _a, ...updated } = filters;
|
|
735
|
-
return {
|
|
736
|
-
cohort: {
|
|
737
|
-
...state.cohort,
|
|
738
|
-
filters: {
|
|
739
|
-
...state.cohort.filters,
|
|
740
|
-
[index]: {
|
|
741
|
-
mode: state.cohort.filters[index].mode,
|
|
742
|
-
root: updated
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
},
|
|
748
|
-
// removes all filters from the cohort filter set at the given index
|
|
749
|
-
clearCohortFilters: (state, action)=>{
|
|
750
|
-
const { index } = action.payload;
|
|
751
|
-
return {
|
|
752
|
-
cohort: {
|
|
753
|
-
...state.cohort,
|
|
754
|
-
filters: {
|
|
755
|
-
...state.cohort.filters,
|
|
756
|
-
[index]: {
|
|
757
|
-
// empty filter set
|
|
758
|
-
mode: 'and',
|
|
759
|
-
root: {}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
});
|
|
767
|
-
// Filter actions: addFilter, removeFilter, updateFilter
|
|
768
|
-
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
769
|
-
const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
|
|
770
|
-
const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
|
|
771
|
-
const selectCurrentCohort = (state)=>state.cohorts.cohort;
|
|
772
|
-
const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
|
|
773
|
-
/**
|
|
774
|
-
* Select a filter by its name from the current cohort. If the filter is not found
|
|
775
|
-
* returns undefined.
|
|
776
|
-
* @param state - Core
|
|
777
|
-
* @param index which cohort index to select from
|
|
778
|
-
* @param name name of the filter to select
|
|
779
|
-
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
780
|
-
return state.cohorts.cohort.filters[index]?.root[name];
|
|
781
|
-
};
|
|
782
|
-
const EmptyFilterSet = {
|
|
783
|
-
mode: 'and',
|
|
784
|
-
root: {}
|
|
785
|
-
};
|
|
786
|
-
/**
|
|
787
|
-
* Select a filter from the index.
|
|
788
|
-
* returns undefined.
|
|
789
|
-
* @param state - Core
|
|
790
|
-
* @param index which cohort index to select from
|
|
791
|
-
*/ const selectIndexFilters = (state, index)=>{
|
|
792
|
-
return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
793
|
-
};
|
|
794
|
-
const cohortReducer = cohortSlice.reducer;
|
|
795
|
-
|
|
796
689
|
const isFileItem = (item)=>{
|
|
797
690
|
return item && 'guid' in item;
|
|
798
691
|
};
|
|
@@ -1350,17 +1243,17 @@ const useDataLibrary = (useApi)=>{
|
|
|
1350
1243
|
};
|
|
1351
1244
|
};
|
|
1352
1245
|
|
|
1353
|
-
const initialState$
|
|
1246
|
+
const initialState$4 = {};
|
|
1354
1247
|
const dataLibrarySlice = createSlice({
|
|
1355
1248
|
name: 'dataLibrary',
|
|
1356
|
-
initialState: initialState$
|
|
1249
|
+
initialState: initialState$4,
|
|
1357
1250
|
reducers: {
|
|
1358
1251
|
setDataLibraryListSelection: (state, action)=>{
|
|
1359
1252
|
const { listId, itemIds } = action.payload;
|
|
1360
1253
|
state[listId] = itemIds;
|
|
1361
1254
|
},
|
|
1362
1255
|
clearDataLibrarySelection: ()=>{
|
|
1363
|
-
return initialState$
|
|
1256
|
+
return initialState$4;
|
|
1364
1257
|
}
|
|
1365
1258
|
}
|
|
1366
1259
|
});
|
|
@@ -1426,7 +1319,7 @@ const isTimeGreaterThan = (startTime, minutes)=>{
|
|
|
1426
1319
|
};
|
|
1427
1320
|
|
|
1428
1321
|
const NO_WORKSPACE_ID = 'none';
|
|
1429
|
-
const initialState = {
|
|
1322
|
+
const initialState$3 = {
|
|
1430
1323
|
id: NO_WORKSPACE_ID,
|
|
1431
1324
|
status: WorkspaceStatus.NotFound,
|
|
1432
1325
|
requestedStatus: RequestedWorkspaceStatus.Unset,
|
|
@@ -1434,7 +1327,7 @@ const initialState = {
|
|
|
1434
1327
|
};
|
|
1435
1328
|
const slice = createSlice({
|
|
1436
1329
|
name: 'ActiveWorkspace',
|
|
1437
|
-
initialState,
|
|
1330
|
+
initialState: initialState$3,
|
|
1438
1331
|
reducers: {
|
|
1439
1332
|
setActiveWorkspaceId: (state, action)=>{
|
|
1440
1333
|
state = {
|
|
@@ -1528,6 +1421,14 @@ const guppyApiReducer = guppyApi.reducer;
|
|
|
1528
1421
|
const isOperationWithField = (operation)=>{
|
|
1529
1422
|
return operation?.field !== undefined;
|
|
1530
1423
|
};
|
|
1424
|
+
const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
|
|
1425
|
+
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
|
|
1426
|
+
) {
|
|
1427
|
+
const { operator } = operation.operator;
|
|
1428
|
+
return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
|
|
1429
|
+
}
|
|
1430
|
+
return false;
|
|
1431
|
+
};
|
|
1531
1432
|
const extractFilterValue = (op)=>{
|
|
1532
1433
|
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1533
1434
|
return handleOperation(valueExtractorHandler, op);
|
|
@@ -1709,6 +1610,12 @@ const isFilterSet = (input)=>{
|
|
|
1709
1610
|
}
|
|
1710
1611
|
return true;
|
|
1711
1612
|
};
|
|
1613
|
+
function isUnion(value) {
|
|
1614
|
+
return typeof value === 'object' && value !== null && value.operator === 'or' && Array.isArray(value.operands);
|
|
1615
|
+
}
|
|
1616
|
+
function isIntersection(value) {
|
|
1617
|
+
return typeof value === 'object' && value !== null && value.operator === 'and' && Array.isArray(value.operands);
|
|
1618
|
+
}
|
|
1712
1619
|
|
|
1713
1620
|
const FieldNameOverrides = {};
|
|
1714
1621
|
const COMMON_PREPOSITIONS = [
|
|
@@ -1773,67 +1680,250 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
|
1773
1680
|
];
|
|
1774
1681
|
};
|
|
1775
1682
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
json
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
});
|
|
1788
|
-
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1789
|
-
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1790
|
-
return {
|
|
1791
|
-
...acc,
|
|
1792
|
-
[key]: valueData[idx]
|
|
1793
|
-
};
|
|
1794
|
-
}, {});
|
|
1795
|
-
return results;
|
|
1796
|
-
};
|
|
1797
|
-
const fetchJson = async (url)=>{
|
|
1798
|
-
const res = await fetch(url, {
|
|
1799
|
-
method: 'GET',
|
|
1800
|
-
headers: {
|
|
1801
|
-
'Content-type': 'application/json'
|
|
1802
|
-
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Flattens a deep nested JSON object skipping
|
|
1685
|
+
* the first level to avoid potentially flattening
|
|
1686
|
+
* non-nested data.
|
|
1687
|
+
* @param {JSON} json
|
|
1688
|
+
*/ function flattenJson(json) {
|
|
1689
|
+
const flattenedJson = [];
|
|
1690
|
+
Object.keys(json).forEach((key)=>{
|
|
1691
|
+
flattenedJson.push(flatten(json[key], {
|
|
1692
|
+
delimiter: '_'
|
|
1693
|
+
}));
|
|
1803
1694
|
});
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
};
|
|
1807
|
-
const useGetStatus = ()=>{
|
|
1808
|
-
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1809
|
-
return useSWR('explorerStatus', fetcher);
|
|
1810
|
-
};
|
|
1695
|
+
return flattenedJson;
|
|
1696
|
+
}
|
|
1811
1697
|
/**
|
|
1812
|
-
*
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1698
|
+
* Converts JSON based on a config.
|
|
1699
|
+
* @param {JSON} json
|
|
1700
|
+
* @param {Object} config
|
|
1701
|
+
*/ async function conversion(json, config) {
|
|
1702
|
+
return Papa.unparse(json, config);
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Converts JSON to a specified file format.
|
|
1706
|
+
* Defaults to JSON if file format is not supported.
|
|
1707
|
+
* @param {JSON} json
|
|
1708
|
+
* @param {string} format
|
|
1709
|
+
*/ async function jsonToFormat(json, format) {
|
|
1710
|
+
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
1711
|
+
const flatJson = await flattenJson(json);
|
|
1712
|
+
const data = await conversion(flatJson, {
|
|
1713
|
+
delimiter: FILE_DELIMITERS[format]
|
|
1714
|
+
});
|
|
1715
|
+
return data;
|
|
1716
|
+
}
|
|
1717
|
+
return json;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
1815
1722
|
*
|
|
1816
|
-
* @param
|
|
1817
|
-
* @
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
* @
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1723
|
+
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
1724
|
+
* @returns {URL} - The prepared download URL as a URL object.
|
|
1725
|
+
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
1726
|
+
/**
|
|
1727
|
+
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
1728
|
+
*
|
|
1729
|
+
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
1730
|
+
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
1731
|
+
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
1732
|
+
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
1733
|
+
return {
|
|
1734
|
+
method: 'POST',
|
|
1735
|
+
headers: {
|
|
1736
|
+
'Content-Type': 'application/json',
|
|
1737
|
+
...csrfToken !== undefined && {
|
|
1738
|
+
'X-CSRF-Token': csrfToken
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
body: JSON.stringify({
|
|
1742
|
+
type: parameters.type,
|
|
1743
|
+
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
1744
|
+
accessibility: parameters.accessibility,
|
|
1745
|
+
fields: parameters?.fields,
|
|
1746
|
+
sort: parameters?.sort
|
|
1747
|
+
})
|
|
1748
|
+
};
|
|
1749
|
+
};
|
|
1750
|
+
/**
|
|
1751
|
+
* Downloads a file from Guppy using the provided parameters.
|
|
1752
|
+
* It will optionally convert the data to the specified format.
|
|
1753
|
+
*
|
|
1754
|
+
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
1755
|
+
* @param onStart - The function to call when the download starts.
|
|
1756
|
+
* @param onDone - The function to call when the download is done.
|
|
1757
|
+
* @param onError - The function to call when the download fails.
|
|
1758
|
+
* @param onAbort - The function to call when the download is aborted.
|
|
1759
|
+
* @param signal - AbortSignal to use for the request.
|
|
1760
|
+
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
1761
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
1762
|
+
onStart?.();
|
|
1763
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
1764
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
1765
|
+
fetch(url.toString(), {
|
|
1766
|
+
...fetchConfig,
|
|
1767
|
+
...signal ? {
|
|
1768
|
+
signal: signal
|
|
1769
|
+
} : {}
|
|
1770
|
+
}).then(async (response)=>{
|
|
1771
|
+
if (!response.ok) {
|
|
1772
|
+
throw new Error(response.statusText);
|
|
1773
|
+
}
|
|
1774
|
+
let jsonData = await response.json();
|
|
1775
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
1776
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
1777
|
+
jsonData = JSONPath({
|
|
1778
|
+
json: jsonData,
|
|
1779
|
+
path: `$.[${parameters.rootPath}]`,
|
|
1780
|
+
resultType: 'value'
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
// convert the data to the specified format and return a Blob
|
|
1784
|
+
let str = '';
|
|
1785
|
+
if (parameters.format === 'json') {
|
|
1786
|
+
str = JSON.stringify(jsonData);
|
|
1787
|
+
} else {
|
|
1788
|
+
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
1789
|
+
if (isJSONObject(convertedData)) {
|
|
1790
|
+
str = JSON.stringify(convertedData, null, 2);
|
|
1791
|
+
} else {
|
|
1792
|
+
str = convertedData;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return new Blob([
|
|
1796
|
+
str
|
|
1797
|
+
], {
|
|
1798
|
+
type: 'application/json'
|
|
1799
|
+
});
|
|
1800
|
+
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
1801
|
+
// Abort is handle as an exception
|
|
1802
|
+
if (error.name == 'AbortError') {
|
|
1803
|
+
// handle abort()
|
|
1804
|
+
onAbort?.();
|
|
1805
|
+
}
|
|
1806
|
+
onError?.(error);
|
|
1807
|
+
});
|
|
1808
|
+
};
|
|
1809
|
+
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
1810
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
1811
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
1812
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
1813
|
+
try {
|
|
1814
|
+
const response = await fetch(url.toString(), {
|
|
1815
|
+
...fetchConfig,
|
|
1816
|
+
...signal ? {
|
|
1817
|
+
signal: signal
|
|
1818
|
+
} : {}
|
|
1819
|
+
});
|
|
1820
|
+
let jsonData = await response.json();
|
|
1821
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
1822
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
1823
|
+
jsonData = JSONPath({
|
|
1824
|
+
json: jsonData,
|
|
1825
|
+
path: `$.[${parameters.rootPath}]`,
|
|
1826
|
+
resultType: 'value'
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
// convert the data to the specified format and return a Blob
|
|
1830
|
+
return jsonData;
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
// Abort is handle as an exception
|
|
1833
|
+
if (error.name == 'AbortError') {
|
|
1834
|
+
// handle abort()
|
|
1835
|
+
onAbort?.();
|
|
1836
|
+
}
|
|
1837
|
+
throw new Error(error);
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
const useGetIndexFields = (index)=>{
|
|
1841
|
+
const { data } = useGetFieldsForIndexQuery(index);
|
|
1842
|
+
return data ?? [];
|
|
1843
|
+
};
|
|
1844
|
+
const groupSharedFields = (data)=>{
|
|
1845
|
+
const reverseIndex = {};
|
|
1846
|
+
// Build reverse index: track which root keys contain each element
|
|
1847
|
+
for(const rootKey in data){
|
|
1848
|
+
data[rootKey].forEach((value)=>{
|
|
1849
|
+
if (!reverseIndex[value]) {
|
|
1850
|
+
reverseIndex[value] = new Set();
|
|
1851
|
+
}
|
|
1852
|
+
reverseIndex[value].add(rootKey);
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
return Object.entries(reverseIndex).reduce((acc, [field, indexSet])=>{
|
|
1856
|
+
if (indexSet.size > 1) {
|
|
1857
|
+
acc[field] = Array.from(indexSet).map((x)=>({
|
|
1858
|
+
index: x,
|
|
1859
|
+
field: field
|
|
1860
|
+
}));
|
|
1861
|
+
}
|
|
1862
|
+
return acc;
|
|
1863
|
+
}, {});
|
|
1864
|
+
};
|
|
1865
|
+
|
|
1866
|
+
const statusEndpoint = '/_status';
|
|
1867
|
+
const processHistogramResponse = (data)=>{
|
|
1868
|
+
const valueData = JSONPath({
|
|
1869
|
+
json: data,
|
|
1870
|
+
path: '$..histogram',
|
|
1871
|
+
resultType: 'value'
|
|
1872
|
+
});
|
|
1873
|
+
const pointerData = JSONPath({
|
|
1874
|
+
json: data,
|
|
1875
|
+
path: '$..histogram',
|
|
1876
|
+
resultType: 'pointer'
|
|
1877
|
+
});
|
|
1878
|
+
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1879
|
+
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1880
|
+
return {
|
|
1881
|
+
...acc,
|
|
1882
|
+
[key]: valueData[idx]
|
|
1883
|
+
};
|
|
1884
|
+
}, {});
|
|
1885
|
+
return results;
|
|
1886
|
+
};
|
|
1887
|
+
const fetchJson = async (url)=>{
|
|
1888
|
+
const res = await fetch(url, {
|
|
1889
|
+
method: 'GET',
|
|
1890
|
+
headers: {
|
|
1891
|
+
'Content-type': 'application/json'
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
if (!res.ok) throw new Error('An error occurred while fetching the data.');
|
|
1895
|
+
return await res.json();
|
|
1896
|
+
};
|
|
1897
|
+
const useGetStatus = ()=>{
|
|
1898
|
+
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1899
|
+
return useSWR('explorerStatus', fetcher);
|
|
1900
|
+
};
|
|
1901
|
+
/**
|
|
1902
|
+
* The main endpoint used in templating Exploration page queries.
|
|
1903
|
+
* Includes table, filter and aggregation query types and leverages guppyApi defined in ./gupplApi.ts
|
|
1904
|
+
* Query templates support filters where applicable
|
|
1905
|
+
*
|
|
1906
|
+
* @param endpoints - Defines endpoints used in Exploration page:
|
|
1907
|
+
* @param getAllFieldsForType - A mapping query that returns all property key names vertex types specified.
|
|
1908
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#mapping-query
|
|
1909
|
+
* @param getAccessibleData - An aggregation histogram counts query that filters based on access type
|
|
1910
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#accessibility-argument-for-regular-tier-access-level
|
|
1911
|
+
* @param getRawDataAndTotalCounts - Queries both _totalCount for selected vertex types and
|
|
1912
|
+
* tabular results containing the raw data in the rows of selected vertex types
|
|
1913
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#1-total-count-aggregation
|
|
1914
|
+
* @param getAggs - An aggregated histogram counts query which outputs vertex property frequencies
|
|
1915
|
+
* @param getSubAggs - TODO: not sure what this one does. Looks like nested aggregation
|
|
1916
|
+
* @param getCounts - Returns total counts of a vertex type
|
|
1917
|
+
* @returns: A guppy API endpoint for templating queryable data displayed on the exploration page
|
|
1918
|
+
*/ const explorerApi = guppyApi.injectEndpoints({
|
|
1919
|
+
endpoints: (builder)=>({
|
|
1920
|
+
getAllFieldsForType: builder.query({
|
|
1921
|
+
query: (type)=>({
|
|
1922
|
+
query: `{ _mapping ${type} } }`
|
|
1923
|
+
}),
|
|
1924
|
+
transformResponse: (response, _meta, params)=>{
|
|
1925
|
+
return response[params.type];
|
|
1926
|
+
}
|
|
1837
1927
|
}),
|
|
1838
1928
|
getAccessibleData: builder.query({
|
|
1839
1929
|
query: ({ type, fields, accessType })=>{
|
|
@@ -1926,7 +2016,32 @@ const useGetStatus = ()=>{
|
|
|
1926
2016
|
return queryBody;
|
|
1927
2017
|
},
|
|
1928
2018
|
transformResponse: (response, _meta, args)=>{
|
|
1929
|
-
return processHistogramResponse(response
|
|
2019
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
2020
|
+
}
|
|
2021
|
+
}),
|
|
2022
|
+
getAggsNoFilterSelf: builder.query({
|
|
2023
|
+
query: ({ type, fields, filters, accessibility = Accessibility.ALL })=>{
|
|
2024
|
+
const queryStart = isFilterEmpty(filters) ? `
|
|
2025
|
+
query getAggs {
|
|
2026
|
+
_aggregation {
|
|
2027
|
+
${type} (accessibility: ${accessibility}) {` : `query getAggs ($filter: JSON) {
|
|
2028
|
+
_aggregation {
|
|
2029
|
+
${type} (filter: $filter, filterSelf: true, accessibility: ${accessibility}) {`;
|
|
2030
|
+
const query = `${queryStart}
|
|
2031
|
+
${fields.map((field)=>histogramQueryStrForEachField(field))}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}`;
|
|
2035
|
+
const queryBody = {
|
|
2036
|
+
query: query,
|
|
2037
|
+
variables: {
|
|
2038
|
+
filter: convertFilterSetToGqlFilter(filters)
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
return queryBody;
|
|
2042
|
+
},
|
|
2043
|
+
transformResponse: (response, _meta, args)=>{
|
|
2044
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1930
2045
|
}
|
|
1931
2046
|
}),
|
|
1932
2047
|
getSubAggs: builder.query({
|
|
@@ -1951,7 +2066,7 @@ const useGetStatus = ()=>{
|
|
|
1951
2066
|
};
|
|
1952
2067
|
},
|
|
1953
2068
|
transformResponse: (response, _meta, args)=>{
|
|
1954
|
-
return processHistogramResponse(response
|
|
2069
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1955
2070
|
}
|
|
1956
2071
|
}),
|
|
1957
2072
|
getCounts: builder.query({
|
|
@@ -1975,7 +2090,13 @@ const useGetStatus = ()=>{
|
|
|
1975
2090
|
};
|
|
1976
2091
|
},
|
|
1977
2092
|
transformResponse: (response, _meta, args)=>{
|
|
1978
|
-
|
|
2093
|
+
if (!response.data || !response.data._aggregation) {
|
|
2094
|
+
throw new Error('Invalid response: Missing data or _aggregation field');
|
|
2095
|
+
}
|
|
2096
|
+
if (!(args.type in response.data._aggregation)) {
|
|
2097
|
+
throw new Error(`Invalid response: Missing expected key '${args.type}' in _aggregation`);
|
|
2098
|
+
}
|
|
2099
|
+
return response.data._aggregation[args.type]._totalCount ?? 0;
|
|
1979
2100
|
}
|
|
1980
2101
|
}),
|
|
1981
2102
|
getFieldCountSummary: builder.query({
|
|
@@ -2006,7 +2127,7 @@ const useGetStatus = ()=>{
|
|
|
2006
2127
|
query: (index)=>{
|
|
2007
2128
|
return {
|
|
2008
2129
|
query: `{
|
|
2009
|
-
_mapping ${index}
|
|
2130
|
+
_mapping { ${index} }
|
|
2010
2131
|
}`
|
|
2011
2132
|
};
|
|
2012
2133
|
},
|
|
@@ -2014,6 +2135,21 @@ const useGetStatus = ()=>{
|
|
|
2014
2135
|
return response['_mapping'];
|
|
2015
2136
|
}
|
|
2016
2137
|
}),
|
|
2138
|
+
getSharedFieldsForIndex: builder.query({
|
|
2139
|
+
query: (indices)=>{
|
|
2140
|
+
return {
|
|
2141
|
+
query: `{
|
|
2142
|
+
_mapping { ${indices.join(' ')} }
|
|
2143
|
+
}`
|
|
2144
|
+
};
|
|
2145
|
+
},
|
|
2146
|
+
transformResponse: (response)=>{
|
|
2147
|
+
if ('_mapping' in response.data) {
|
|
2148
|
+
return groupSharedFields(response.data['_mapping']);
|
|
2149
|
+
}
|
|
2150
|
+
return {};
|
|
2151
|
+
}
|
|
2152
|
+
}),
|
|
2017
2153
|
generalGQL: builder.query({
|
|
2018
2154
|
query: ({ query, variables })=>{
|
|
2019
2155
|
return {
|
|
@@ -2084,169 +2220,7 @@ const useGetArrayTypes = ()=>{
|
|
|
2084
2220
|
return data ? data['indices'] : {};
|
|
2085
2221
|
}
|
|
2086
2222
|
};
|
|
2087
|
-
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2088
|
-
|
|
2089
|
-
/**
|
|
2090
|
-
* Flattens a deep nested JSON object skipping
|
|
2091
|
-
* the first level to avoid potentially flattening
|
|
2092
|
-
* non-nested data.
|
|
2093
|
-
* @param {JSON} json
|
|
2094
|
-
*/ function flattenJson(json) {
|
|
2095
|
-
const flattenedJson = [];
|
|
2096
|
-
Object.keys(json).forEach((key)=>{
|
|
2097
|
-
flattenedJson.push(flatten(json[key], {
|
|
2098
|
-
delimiter: '_'
|
|
2099
|
-
}));
|
|
2100
|
-
});
|
|
2101
|
-
return flattenedJson;
|
|
2102
|
-
}
|
|
2103
|
-
/**
|
|
2104
|
-
* Converts JSON based on a config.
|
|
2105
|
-
* @param {JSON} json
|
|
2106
|
-
* @param {Object} config
|
|
2107
|
-
*/ async function conversion(json, config) {
|
|
2108
|
-
return Papa.unparse(json, config);
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* Converts JSON to a specified file format.
|
|
2112
|
-
* Defaults to JSON if file format is not supported.
|
|
2113
|
-
* @param {JSON} json
|
|
2114
|
-
* @param {string} format
|
|
2115
|
-
*/ async function jsonToFormat(json, format) {
|
|
2116
|
-
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
2117
|
-
const flatJson = await flattenJson(json);
|
|
2118
|
-
const data = await conversion(flatJson, {
|
|
2119
|
-
delimiter: FILE_DELIMITERS[format]
|
|
2120
|
-
});
|
|
2121
|
-
return data;
|
|
2122
|
-
}
|
|
2123
|
-
return json;
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
/**
|
|
2127
|
-
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2128
|
-
*
|
|
2129
|
-
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2130
|
-
* @returns {URL} - The prepared download URL as a URL object.
|
|
2131
|
-
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
2132
|
-
/**
|
|
2133
|
-
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
2134
|
-
*
|
|
2135
|
-
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
2136
|
-
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
2137
|
-
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
2138
|
-
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
2139
|
-
return {
|
|
2140
|
-
method: 'POST',
|
|
2141
|
-
headers: {
|
|
2142
|
-
'Content-Type': 'application/json',
|
|
2143
|
-
...csrfToken !== undefined && {
|
|
2144
|
-
'X-CSRF-Token': csrfToken
|
|
2145
|
-
}
|
|
2146
|
-
},
|
|
2147
|
-
body: JSON.stringify({
|
|
2148
|
-
type: parameters.type,
|
|
2149
|
-
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
2150
|
-
accessibility: parameters.accessibility,
|
|
2151
|
-
fields: parameters?.fields,
|
|
2152
|
-
sort: parameters?.sort
|
|
2153
|
-
})
|
|
2154
|
-
};
|
|
2155
|
-
};
|
|
2156
|
-
/**
|
|
2157
|
-
* Downloads a file from Guppy using the provided parameters.
|
|
2158
|
-
* It will optionally convert the data to the specified format.
|
|
2159
|
-
*
|
|
2160
|
-
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
2161
|
-
* @param onStart - The function to call when the download starts.
|
|
2162
|
-
* @param onDone - The function to call when the download is done.
|
|
2163
|
-
* @param onError - The function to call when the download fails.
|
|
2164
|
-
* @param onAbort - The function to call when the download is aborted.
|
|
2165
|
-
* @param signal - AbortSignal to use for the request.
|
|
2166
|
-
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
2167
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2168
|
-
onStart?.();
|
|
2169
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2170
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2171
|
-
fetch(url.toString(), {
|
|
2172
|
-
...fetchConfig,
|
|
2173
|
-
...signal ? {
|
|
2174
|
-
signal: signal
|
|
2175
|
-
} : {}
|
|
2176
|
-
}).then(async (response)=>{
|
|
2177
|
-
if (!response.ok) {
|
|
2178
|
-
throw new Error(response.statusText);
|
|
2179
|
-
}
|
|
2180
|
-
let jsonData = await response.json();
|
|
2181
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2182
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2183
|
-
jsonData = JSONPath({
|
|
2184
|
-
json: jsonData,
|
|
2185
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2186
|
-
resultType: 'value'
|
|
2187
|
-
});
|
|
2188
|
-
}
|
|
2189
|
-
// convert the data to the specified format and return a Blob
|
|
2190
|
-
let str = '';
|
|
2191
|
-
if (parameters.format === 'json') {
|
|
2192
|
-
str = JSON.stringify(jsonData);
|
|
2193
|
-
} else {
|
|
2194
|
-
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
2195
|
-
if (isJSONObject(convertedData)) {
|
|
2196
|
-
str = JSON.stringify(convertedData, null, 2);
|
|
2197
|
-
} else {
|
|
2198
|
-
str = convertedData;
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
return new Blob([
|
|
2202
|
-
str
|
|
2203
|
-
], {
|
|
2204
|
-
type: 'application/json'
|
|
2205
|
-
});
|
|
2206
|
-
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
2207
|
-
// Abort is handle as an exception
|
|
2208
|
-
if (error.name == 'AbortError') {
|
|
2209
|
-
// handle abort()
|
|
2210
|
-
onAbort?.();
|
|
2211
|
-
}
|
|
2212
|
-
onError?.(error);
|
|
2213
|
-
});
|
|
2214
|
-
};
|
|
2215
|
-
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
2216
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2217
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2218
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2219
|
-
try {
|
|
2220
|
-
const response = await fetch(url.toString(), {
|
|
2221
|
-
...fetchConfig,
|
|
2222
|
-
...signal ? {
|
|
2223
|
-
signal: signal
|
|
2224
|
-
} : {}
|
|
2225
|
-
});
|
|
2226
|
-
let jsonData = await response.json();
|
|
2227
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2228
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2229
|
-
jsonData = JSONPath({
|
|
2230
|
-
json: jsonData,
|
|
2231
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2232
|
-
resultType: 'value'
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
// convert the data to the specified format and return a Blob
|
|
2236
|
-
return jsonData;
|
|
2237
|
-
} catch (error) {
|
|
2238
|
-
// Abort is handle as an exception
|
|
2239
|
-
if (error.name == 'AbortError') {
|
|
2240
|
-
// handle abort()
|
|
2241
|
-
onAbort?.();
|
|
2242
|
-
}
|
|
2243
|
-
throw new Error(error);
|
|
2244
|
-
}
|
|
2245
|
-
};
|
|
2246
|
-
const useGetIndexFields = (index)=>{
|
|
2247
|
-
const { data } = useGetFieldsForIndexQuery(index);
|
|
2248
|
-
return data ?? [];
|
|
2249
|
-
};
|
|
2223
|
+
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useGetAggsNoFilterSelfQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetSharedFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2250
2224
|
|
|
2251
2225
|
/**
|
|
2252
2226
|
* Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
|
|
@@ -2282,13 +2256,231 @@ const useGetIndexFields = (index)=>{
|
|
|
2282
2256
|
});
|
|
2283
2257
|
const { useDownloadFromGuppyMutation } = downloadRequestApi;
|
|
2284
2258
|
|
|
2259
|
+
const EmptyCohort = {
|
|
2260
|
+
id: 'default',
|
|
2261
|
+
name: 'Filters',
|
|
2262
|
+
filters: {},
|
|
2263
|
+
modified_datetime: new Date().toISOString()
|
|
2264
|
+
};
|
|
2265
|
+
const initialCohortState = {
|
|
2266
|
+
cohort: {
|
|
2267
|
+
...EmptyCohort
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
// TODO: start using this adapter
|
|
2271
|
+
/*
|
|
2272
|
+
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
2273
|
+
sortComparer: (a, b) => {
|
|
2274
|
+
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
2275
|
+
else return -1;
|
|
2276
|
+
},
|
|
2277
|
+
});
|
|
2278
|
+
*/ /**
|
|
2279
|
+
* Redux slice for cohort filters
|
|
2280
|
+
*/ const cohortSlice = createSlice({
|
|
2281
|
+
name: 'cohort',
|
|
2282
|
+
initialState: initialCohortState,
|
|
2283
|
+
reducers: {
|
|
2284
|
+
// adds a filter to the cohort filter set at the given index
|
|
2285
|
+
updateCohortFilter: (state, action)=>{
|
|
2286
|
+
const { index, field, filter } = action.payload;
|
|
2287
|
+
return {
|
|
2288
|
+
cohort: {
|
|
2289
|
+
...state.cohort,
|
|
2290
|
+
filters: {
|
|
2291
|
+
...state.cohort.filters,
|
|
2292
|
+
[index]: {
|
|
2293
|
+
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
2294
|
+
root: {
|
|
2295
|
+
...state.cohort.filters?.[index]?.root ?? {},
|
|
2296
|
+
[field]: filter
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
},
|
|
2303
|
+
setCohortFilter: (state, action)=>{
|
|
2304
|
+
const { index, filters } = action.payload;
|
|
2305
|
+
return {
|
|
2306
|
+
cohort: {
|
|
2307
|
+
...state.cohort,
|
|
2308
|
+
filters: {
|
|
2309
|
+
...state.cohort.filters,
|
|
2310
|
+
[index]: filters
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
};
|
|
2314
|
+
},
|
|
2315
|
+
setCohortIndexFilters: (state, action)=>{
|
|
2316
|
+
return {
|
|
2317
|
+
cohort: {
|
|
2318
|
+
...state.cohort,
|
|
2319
|
+
filters: {
|
|
2320
|
+
...action.payload.filters
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
},
|
|
2325
|
+
// removes a filter to the cohort filter set at the given index
|
|
2326
|
+
removeCohortFilter: (state, action)=>{
|
|
2327
|
+
const { index, field } = action.payload;
|
|
2328
|
+
const filters = state.cohort?.filters?.[index]?.root;
|
|
2329
|
+
if (!filters) {
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2333
|
+
const { [field]: _a, ...updated } = filters;
|
|
2334
|
+
return {
|
|
2335
|
+
cohort: {
|
|
2336
|
+
...state.cohort,
|
|
2337
|
+
filters: {
|
|
2338
|
+
...state.cohort.filters,
|
|
2339
|
+
[index]: {
|
|
2340
|
+
mode: state.cohort.filters[index].mode,
|
|
2341
|
+
root: updated
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
};
|
|
2346
|
+
},
|
|
2347
|
+
// removes all filters from the cohort filter set at the given index
|
|
2348
|
+
clearCohortFilters: (state, action)=>{
|
|
2349
|
+
const { index } = action.payload;
|
|
2350
|
+
return {
|
|
2351
|
+
cohort: {
|
|
2352
|
+
...state.cohort,
|
|
2353
|
+
filters: {
|
|
2354
|
+
...state.cohort.filters,
|
|
2355
|
+
[index]: {
|
|
2356
|
+
// empty filter set
|
|
2357
|
+
mode: 'and',
|
|
2358
|
+
root: {}
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
// Filter actions: addFilter, removeFilter, updateFilter
|
|
2367
|
+
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
2368
|
+
const selectCohortFilters = (state)=>state.cohorts.cohort.cohort.filters;
|
|
2369
|
+
const selectCurrentCohortId = (state)=>state.cohorts.cohort.cohort.id;
|
|
2370
|
+
const selectCurrentCohort = (state)=>state.cohorts.cohort.cohort;
|
|
2371
|
+
const selectCurrentCohortName = (state)=>state.cohorts.cohort.cohort.name;
|
|
2372
|
+
/**
|
|
2373
|
+
* Select a filter by its name from the current cohort. If the filter is not found
|
|
2374
|
+
* returns undefined.
|
|
2375
|
+
* @param state - Core
|
|
2376
|
+
* @param index which cohort index to select from
|
|
2377
|
+
* @param name name of the filter to select
|
|
2378
|
+
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
2379
|
+
return state.cohorts.cohort.cohort.filters[index]?.root[name];
|
|
2380
|
+
};
|
|
2381
|
+
const EmptyFilterSet = {
|
|
2382
|
+
mode: 'and',
|
|
2383
|
+
root: {}
|
|
2384
|
+
};
|
|
2385
|
+
/**
|
|
2386
|
+
* Select a filter from the index.
|
|
2387
|
+
* returns undefined.
|
|
2388
|
+
* @param state - Core
|
|
2389
|
+
* @param index which cohort index to select from
|
|
2390
|
+
*/ const selectIndexFilters = (state, index)=>{
|
|
2391
|
+
return state.cohorts.cohort.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
2392
|
+
};
|
|
2393
|
+
const cohortReducer = cohortSlice.reducer;
|
|
2394
|
+
|
|
2395
|
+
const initialState$2 = {};
|
|
2396
|
+
const expandSlice$1 = createSlice({
|
|
2397
|
+
name: 'CohortBuilder/filterExpand',
|
|
2398
|
+
initialState: initialState$2,
|
|
2399
|
+
reducers: {
|
|
2400
|
+
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
2401
|
+
return {
|
|
2402
|
+
...state,
|
|
2403
|
+
[action.payload.index]: {
|
|
2404
|
+
...state[action.payload.index],
|
|
2405
|
+
[action.payload.field]: action.payload.expanded
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
},
|
|
2409
|
+
toggleCohortBuilderAllFilters: (state, action)=>{
|
|
2410
|
+
return {
|
|
2411
|
+
...state,
|
|
2412
|
+
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
2413
|
+
acc[k] = action.payload.expand;
|
|
2414
|
+
return acc;
|
|
2415
|
+
}, {})
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
2421
|
+
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
2422
|
+
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
2423
|
+
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
2424
|
+
|
|
2425
|
+
const initialState$1 = {};
|
|
2426
|
+
const expandSlice = createSlice({
|
|
2427
|
+
name: 'CohortBuilder/filterCombineMode',
|
|
2428
|
+
initialState: initialState$1,
|
|
2429
|
+
reducers: {
|
|
2430
|
+
setCohortFilterCombineMode: (state, action)=>{
|
|
2431
|
+
return {
|
|
2432
|
+
...state,
|
|
2433
|
+
[action.payload.index]: {
|
|
2434
|
+
...state[action.payload.index],
|
|
2435
|
+
[action.payload.field]: action.payload.mode
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
});
|
|
2441
|
+
const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
|
|
2442
|
+
const { setCohortFilterCombineMode } = expandSlice.actions;
|
|
2443
|
+
const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
|
|
2444
|
+
|
|
2445
|
+
const initialState = {
|
|
2446
|
+
shouldShareFilters: false,
|
|
2447
|
+
sharedFiltersMap: {}
|
|
2448
|
+
};
|
|
2449
|
+
const cohortSharedFiltersSlice = createSlice({
|
|
2450
|
+
name: 'cohortSharedFilters',
|
|
2451
|
+
initialState: initialState,
|
|
2452
|
+
reducers: {
|
|
2453
|
+
setShouldShareFilters: (state, action)=>{
|
|
2454
|
+
state.shouldShareFilters = action.payload;
|
|
2455
|
+
return state;
|
|
2456
|
+
},
|
|
2457
|
+
setSharedFilters: (state, action)=>{
|
|
2458
|
+
state.sharedFiltersMap = action.payload;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
|
|
2463
|
+
const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
|
|
2464
|
+
const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
|
|
2465
|
+
field
|
|
2466
|
+
];
|
|
2467
|
+
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
2468
|
+
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
2469
|
+
|
|
2470
|
+
const cohortReducers = combineReducers({
|
|
2471
|
+
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
2472
|
+
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
2473
|
+
sharedFilters: cohortSharedFiltersReducer,
|
|
2474
|
+
cohort: cohortReducer
|
|
2475
|
+
});
|
|
2476
|
+
|
|
2285
2477
|
const rootReducer = combineReducers({
|
|
2286
2478
|
gen3Services: gen3ServicesReducer,
|
|
2287
2479
|
user: userReducer,
|
|
2288
2480
|
gen3Apps: gen3AppReducer,
|
|
2289
2481
|
drsHostnames: drsHostnamesReducer,
|
|
2290
2482
|
modals: modalReducer,
|
|
2291
|
-
cohorts:
|
|
2483
|
+
cohorts: cohortReducers,
|
|
2292
2484
|
activeWorkspace: activeWorkspaceReducer,
|
|
2293
2485
|
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2294
2486
|
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
@@ -2317,7 +2509,7 @@ const persistConfig = {
|
|
|
2317
2509
|
version: 1,
|
|
2318
2510
|
storage,
|
|
2319
2511
|
whitelist: [
|
|
2320
|
-
'
|
|
2512
|
+
'cohort',
|
|
2321
2513
|
'activeWorkspace'
|
|
2322
2514
|
]
|
|
2323
2515
|
};
|
|
@@ -2342,7 +2534,7 @@ const coreStore = setupCoreStore();
|
|
|
2342
2534
|
setupListeners(coreStore.dispatch);
|
|
2343
2535
|
|
|
2344
2536
|
const isNotDefined = (x)=>{
|
|
2345
|
-
return x === undefined || x === null || x ===
|
|
2537
|
+
return x === undefined || x === null || x === void 0;
|
|
2346
2538
|
};
|
|
2347
2539
|
const isObject = (x)=>{
|
|
2348
2540
|
return typeof x === 'object';
|
|
@@ -2361,51 +2553,14 @@ const isString = (x)=>{
|
|
|
2361
2553
|
* @returns {URL} - The prepared download URL as a URL object.
|
|
2362
2554
|
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2363
2555
|
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
406: 'Not Acceptable',
|
|
2373
|
-
407: 'Proxy Authentication Required',
|
|
2374
|
-
408: 'Request Timeout',
|
|
2375
|
-
409: 'Conflict',
|
|
2376
|
-
410: 'Gone',
|
|
2377
|
-
411: 'Length Required',
|
|
2378
|
-
412: 'Precondition Failed',
|
|
2379
|
-
413: 'Payload Too Large',
|
|
2380
|
-
414: 'URI Too Long',
|
|
2381
|
-
415: 'Unsupported Media Type',
|
|
2382
|
-
416: 'Range Not Satisfiable',
|
|
2383
|
-
417: 'Expectation Failed',
|
|
2384
|
-
418: "I'm a teapot",
|
|
2385
|
-
421: 'Misdirected Request',
|
|
2386
|
-
422: 'Unprocessable Entity',
|
|
2387
|
-
423: 'Locked',
|
|
2388
|
-
424: 'Failed Dependency',
|
|
2389
|
-
425: 'Too Early',
|
|
2390
|
-
426: 'Upgrade Required',
|
|
2391
|
-
428: 'Precondition Required',
|
|
2392
|
-
429: 'Too Many Requests',
|
|
2393
|
-
431: 'Request Header Fields Too Large',
|
|
2394
|
-
451: 'Unavailable For Legal Reasons',
|
|
2395
|
-
// 5xx Server Errors
|
|
2396
|
-
500: 'Internal Server Error',
|
|
2397
|
-
501: 'Not Implemented',
|
|
2398
|
-
502: 'Bad Gateway',
|
|
2399
|
-
503: 'Service Unavailable',
|
|
2400
|
-
504: 'Gateway Timeout',
|
|
2401
|
-
505: 'HTTP Version Not Supported',
|
|
2402
|
-
506: 'Variant Also Negotiates',
|
|
2403
|
-
507: 'Insufficient Storage',
|
|
2404
|
-
508: 'Loop Detected',
|
|
2405
|
-
510: 'Not Extended',
|
|
2406
|
-
511: 'Network Authentication Required'
|
|
2407
|
-
};
|
|
2408
|
-
class HTTPError extends Error {
|
|
2556
|
+
const DEFAULT_METHOD = 'GET';
|
|
2557
|
+
const CONTENT_TYPE_HEADER = 'Content-Type';
|
|
2558
|
+
const CONTENT_TYPE_JSON = 'application/json';
|
|
2559
|
+
/**
|
|
2560
|
+
* Represents an error that occurs during an HTTP request.
|
|
2561
|
+
* Extends the built-in `Error` class to provide additional information
|
|
2562
|
+
* about the HTTP status code and optional response data.
|
|
2563
|
+
*/ class HTTPError extends Error {
|
|
2409
2564
|
constructor(status, message, responseData){
|
|
2410
2565
|
super(message), this.status = status, this.responseData = responseData;
|
|
2411
2566
|
this.name = 'HTTPError';
|
|
@@ -2415,15 +2570,16 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2415
2570
|
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2416
2571
|
const headers = new Headers();
|
|
2417
2572
|
headers.set('Content-Type', 'application/json');
|
|
2418
|
-
let accessToken = undefined;
|
|
2419
2573
|
if (process.env.NODE_ENV === 'development') {
|
|
2420
2574
|
// NOTE: This cookie can only be accessed from the client side
|
|
2421
2575
|
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2422
|
-
accessToken = getCookie('credentials_token');
|
|
2576
|
+
const accessToken = getCookie('credentials_token');
|
|
2577
|
+
if (accessToken) {
|
|
2578
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2579
|
+
}
|
|
2423
2580
|
}
|
|
2424
2581
|
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2425
|
-
|
|
2426
|
-
const url = `${GEN3_FENCE_API}/user/data/download/${guid}`;
|
|
2582
|
+
const url = `${GEN3_FENCE_API}/data/download/${guid}`;
|
|
2427
2583
|
try {
|
|
2428
2584
|
const response = await fetch(url, {
|
|
2429
2585
|
method: method,
|
|
@@ -2456,6 +2612,67 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2456
2612
|
throw error;
|
|
2457
2613
|
}
|
|
2458
2614
|
};
|
|
2615
|
+
/**
|
|
2616
|
+
* Retrieves a CSRF token from the server.
|
|
2617
|
+
*
|
|
2618
|
+
* This asynchronous function sends a GET request to the server's status endpoint
|
|
2619
|
+
* to fetch the CSRF token in the response. The token is expected to be included
|
|
2620
|
+
* in the JSON response under the `csrf_token` field.
|
|
2621
|
+
*
|
|
2622
|
+
* @returns {Promise<string | null>} A promise that resolves to the CSRF token as a string if successfully retrieved,
|
|
2623
|
+
* or null if the token is not present in the response.
|
|
2624
|
+
* @throws {HTTPError} Throws an HTTPError if the server response is not successful.
|
|
2625
|
+
*/ const getCSRFToken = async ()=>{
|
|
2626
|
+
const requestHeaders = new Headers({
|
|
2627
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2628
|
+
});
|
|
2629
|
+
const response = await fetch(`${GEN3_API}/_status`, {
|
|
2630
|
+
headers: requestHeaders
|
|
2631
|
+
});
|
|
2632
|
+
if (!response.ok) {
|
|
2633
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2634
|
+
}
|
|
2635
|
+
const { csrf_token: csrfToken } = await response.json();
|
|
2636
|
+
return csrfToken || null;
|
|
2637
|
+
};
|
|
2638
|
+
/**
|
|
2639
|
+
* Fetches JSON data from a specified URL using the Fetch API.
|
|
2640
|
+
*
|
|
2641
|
+
* @param {string} url - The URL to fetch the JSON data from.
|
|
2642
|
+
* @param {boolean} [requiresCSRF=false] - Indicates whether a CSRF token is required for the request.
|
|
2643
|
+
* If true, the CSRF token will be added to the request headers.
|
|
2644
|
+
* @param {string} [method=DEFAULT_METHOD] - The HTTP method to use for the request (e.g., 'GET', 'POST').
|
|
2645
|
+
* @param {unknown} [body=undefined] - The request body to send, applicable when using methods like 'POST'.
|
|
2646
|
+
*
|
|
2647
|
+
* @returns {Promise<any>} A promise that resolves to the parsed JSON data from the response.
|
|
2648
|
+
*
|
|
2649
|
+
* @throws {HTTPError} Throws an error if the HTTP response status indicates a failure.
|
|
2650
|
+
*/ const fetchJSONDataFromURL = async (url, requiresCSRF = false, method = DEFAULT_METHOD, body = undefined)=>{
|
|
2651
|
+
const requestHeaders = new Headers({
|
|
2652
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2653
|
+
});
|
|
2654
|
+
if (requiresCSRF) {
|
|
2655
|
+
const csrfToken = await getCSRFToken();
|
|
2656
|
+
if (csrfToken) {
|
|
2657
|
+
requestHeaders.set('X-CSRF-Token', csrfToken);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2661
|
+
const accessToken = getCookie('credentials_token');
|
|
2662
|
+
if (accessToken) {
|
|
2663
|
+
requestHeaders.set('Authorization', `Bearer ${accessToken}`);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
const response = await fetch(url, {
|
|
2667
|
+
method,
|
|
2668
|
+
headers: requestHeaders,
|
|
2669
|
+
body: method === 'POST' ? JSON.stringify(body) : null
|
|
2670
|
+
});
|
|
2671
|
+
if (!response.ok) {
|
|
2672
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2673
|
+
}
|
|
2674
|
+
return response.json();
|
|
2675
|
+
};
|
|
2459
2676
|
|
|
2460
2677
|
const persistor = persistStore(coreStore);
|
|
2461
2678
|
const CoreProvider = ({ children })=>{
|
|
@@ -3265,5 +3482,5 @@ const { useGetProjectsQuery, useGetSubmissionGraphQLQuery, useGetProjectsDetails
|
|
|
3265
3482
|
});
|
|
3266
3483
|
const { useGetSowerJobListQuery, useLazyGetSowerJobListQuery, useSubmitSowerJobMutation, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery } = loadingStatusApi;
|
|
3267
3484
|
|
|
3268
|
-
export { Accessibility, CoreProvider, EmptyWorkspaceStatusResponse, GEN3_API, GEN3_AUTHZ_API, GEN3_COMMONS_NAME, GEN3_CROSSWALK_API, GEN3_DOMAIN, GEN3_DOWNLOADS_ENDPOINT, GEN3_FENCE_API, GEN3_GUPPY_API, GEN3_MDS_API, GEN3_REDIRECT_URL, GEN3_SOWER_API, GEN3_SUBMISSION_API, GEN3_WORKSPACE_API, HTTPError,
|
|
3485
|
+
export { Accessibility, CoreProvider, EmptyWorkspaceStatusResponse, GEN3_API, GEN3_AUTHZ_API, GEN3_COMMONS_NAME, GEN3_CROSSWALK_API, GEN3_DOMAIN, GEN3_DOWNLOADS_ENDPOINT, GEN3_FENCE_API, GEN3_GUPPY_API, GEN3_MDS_API, GEN3_REDIRECT_URL, GEN3_SOWER_API, GEN3_SUBMISSION_API, GEN3_WORKSPACE_API, HTTPError, Modals, PodConditionType, PodStatus, RequestedWorkspaceStatus, WorkspaceStatus, clearActiveWorkspaceId, clearCohortFilters, clearDataLibrarySelection, convertFilterSetToGqlFilter, coreStore, createAppApiForRTKQ, createAppStore, createGen3App, createGen3AppWithOwnStore, createUseCoreDataHook, dataLibrarySelectionReducer, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, drsHostnamesReducer, extractEnumFilterValue, extractFieldNameFromFullFieldName, extractFilterValue, extractIndexAndFieldNameFromFullFieldName, extractIndexFromFullFieldName, fetchFence, fetchFencePresignedURL, fetchJSONDataFromURL, fetchJson, fetchUserState, fieldNameToTitle, gen3Api, getCurrentTimestamp, getGen3AppId, getNumberOfItemsInDatalist, getTimestamp, graphQLAPI, graphQLWithTags, groupSharedFields, guppyAPISliceMiddleware, guppyApi, guppyApiReducer, guppyApiSliceReducerPath, handleOperation, hideModal, isAdditionalDataItem, isArray, isAuthenticated, isCohortItem, isErrorWithMessage, isFetchBaseQueryError, isFetchError, isFetchParseError, isFileItem, isFilterEmpty, isFilterSet, isGuppyAggregationData, isHistogramData, isHistogramDataAArray, isHistogramDataAnEnum, isHistogramDataArray, isHistogramDataArrayARange, isHistogramDataArrayAnEnum, isHistogramDataCollection, isHistogramRangeData, isHttpStatusError, isIntersection, isJSONObject, isJSONValue, isJSONValueArray, isNotDefined, isObject, isOperationWithField, isOperatorWithFieldAndArrayOfOperands, isPending, isProgramUrl, isRootUrl, isString, isTimeGreaterThan, isUnion, isWorkspaceActive, isWorkspaceRunningOrStopping, listifyMethodsFromMapping, logoutFence, prepareUrl, prependIndexToFieldName, processHistogramResponse, projectCodeFromResourcePath, rawDataQueryStrForEachField, removeCohortFilter, resetUserState, resourcePathFromProjectID, selectActiveWorkspaceId, selectActiveWorkspaceStatus, selectAllCohortFiltersCollapsed, selectAuthzMappingData, selectCSRFToken, selectCSRFTokenData, selectCohortFilterCombineMode, selectCohortFilterExpanded, selectCohortFilters, selectCurrentCohort, selectCurrentCohortId, selectCurrentCohortName, selectCurrentMessage, selectCurrentModal, selectGen3AppByName, selectGen3AppMetadataByName, selectHeadersWithCSRFToken, selectIndexFilters, selectIndexedFilterByName, selectPaymodelStatus, selectRequestedWorkspaceStatus, selectRequestedWorkspaceStatusTimestamp, selectSharedFilters, selectSharedFiltersForFields, selectShouldShareFilters, selectUser, selectUserAuthStatus, selectUserData, selectUserDetails, selectUserLoginStatus, selectWorkspaceStatus, selectWorkspaceStatusFromService, setActiveWorkspace, setActiveWorkspaceId, setActiveWorkspaceStatus, setCohortFilter, setCohortFilterCombineMode, setCohortIndexFilters, setDRSHostnames, setDataLibraryListSelection, setRequestedWorkspaceStatus, setSharedFilters, setShouldShareFilters, setupCoreStore, showModal, submissionApi, toggleCohortBuilderAllFilters, toggleCohortBuilderCategoryFilter, trimFirstFieldNameToTitle, updateCohortFilter, useAddDataLibraryListMutation, useAddNewCredentialMutation, useAskQuestionMutation, useAuthorizeFromCredentialsMutation, useCoreDispatch, useCoreSelector, useDataLibrary, useDeleteDataLibraryListMutation, useDownloadFromGuppyMutation, useFetchUserDetailsQuery, useGeneralGQLQuery, useGetAISearchStatusQuery, useGetAISearchVersionQuery, useGetAccessibleDataQuery, useGetActivePayModelQuery, useGetAggMDSQuery, useGetAggsNoFilterSelfQuery, useGetAggsQuery, useGetAllFieldsForTypeQuery, useGetArrayTypes, useGetAuthzMappingsQuery, useGetCSRFQuery, useGetCountsQuery, useGetCredentialsQuery, useGetCrosswalkDataQuery, useGetDataLibraryListQuery, useGetDataLibraryListsQuery, useGetDataQuery, useGetDictionaryQuery, useGetDownloadQuery, useGetExternalLoginsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetIndexAggMDSQuery, useGetIndexFields, useGetJWKKeysQuery, useGetLoginProvidersQuery, useGetMDSQuery, useGetMetadataByIdQuery, useGetProjectsDetailsQuery, useGetProjectsQuery, useGetRawDataAndTotalCountsQuery, useGetSharedFieldsForIndexQuery, useGetSowerJobListQuery, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery, useGetStatus, useGetSubAggsQuery, useGetSubmissionGraphQLQuery, useGetSubmissionsQuery, useGetTagsQuery, useGetWorkspaceOptionsQuery, useGetWorkspacePayModelsQuery, useGetWorkspaceStatusQuery, useGraphQLQuery, useIsExternalConnectedQuery, useIsUserLoggedIn, useLaunchWorkspaceMutation, useLazyFetchUserDetailsQuery, useLazyGeneralGQLQuery, useLazyGetAggsQuery, useLazyGetCrosswalkDataQuery, useLazyGetDownloadQuery, useLazyGetExternalLoginsQuery, useLazyGetProjectsQuery, useLazyGetSowerJobListQuery, useLazyGetSubmissionGraphQLQuery, useLazyIsExternalConnectedQuery, usePrevious, useRemoveCredentialMutation, useSetCurrentPayModelMutation, useSubmitSowerJobMutation, useTerminateWorkspaceMutation, useUpdateDataLibraryListMutation, useUser, useUserAuth, userHasCreateOrUpdateOnAnyProject, userHasDataUpload, userHasMethodForServiceOnProject, userHasMethodForServiceOnResource, userHasMethodOnAnyProject, userHasSheepdogProgramAdmin, userHasSheepdogProjectAdmin };
|
|
3269
3486
|
//# sourceMappingURL=index.js.map
|