@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/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';
|
|
@@ -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
|
|
@@ -107,7 +114,8 @@ const userAuthApi = createApi({
|
|
|
107
114
|
endpoint,
|
|
108
115
|
headers
|
|
109
116
|
});
|
|
110
|
-
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
118
|
+
} catch (_e) {
|
|
111
119
|
/*
|
|
112
120
|
Because an "error" response is valid for the auth requests we don't want to
|
|
113
121
|
put the request in an error state, or it will attempt the request over and over again
|
|
@@ -122,7 +130,7 @@ const userAuthApi = createApi({
|
|
|
122
130
|
endpoints: (builder)=>({
|
|
123
131
|
fetchUserDetails: builder.query({
|
|
124
132
|
query: ()=>({
|
|
125
|
-
endpoint: '/user
|
|
133
|
+
endpoint: '/user'
|
|
126
134
|
}),
|
|
127
135
|
transformResponse (response) {
|
|
128
136
|
return {
|
|
@@ -133,12 +141,33 @@ const userAuthApi = createApi({
|
|
|
133
141
|
}
|
|
134
142
|
}),
|
|
135
143
|
getCSRF: builder.query({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
}
|
|
140
169
|
return {
|
|
141
|
-
|
|
170
|
+
error: 'Unknown Error'
|
|
142
171
|
};
|
|
143
172
|
}
|
|
144
173
|
})
|
|
@@ -200,24 +229,27 @@ const gen3ServicesReducer = gen3Api.reducer;
|
|
|
200
229
|
const gen3ServicesReducerMiddleware = gen3Api.middleware;
|
|
201
230
|
|
|
202
231
|
/**
|
|
203
|
-
* Creates a fence API endpoint for handling login processes
|
|
232
|
+
* Creates a fence API endpoint for handling login/data processes
|
|
204
233
|
* @param endpoints - defined endpoint query for logging in
|
|
205
234
|
* @returns: The generated fence login API slice
|
|
206
235
|
*/ const loginProvidersApi = gen3Api.injectEndpoints({
|
|
207
236
|
endpoints: (builder)=>({
|
|
208
237
|
getLoginProviders: builder.query({
|
|
209
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
238
|
+
query: ()=>`${GEN3_FENCE_API}/login`
|
|
210
239
|
}),
|
|
211
240
|
getDownload: builder.query({
|
|
212
|
-
query: (guid)=>`${GEN3_FENCE_API}/
|
|
241
|
+
query: (guid)=>`${GEN3_FENCE_API}/data/download/${guid}`
|
|
242
|
+
}),
|
|
243
|
+
getPresignedUrl: builder.query({
|
|
244
|
+
query: ({ guid, what })=>`${GEN3_FENCE_API}/data/${what}/${guid}`
|
|
213
245
|
})
|
|
214
246
|
})
|
|
215
247
|
});
|
|
216
|
-
const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery } = loginProvidersApi;
|
|
248
|
+
const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery, useGetPresignedUrlQuery, useLazyGetPresignedUrlQuery } = loginProvidersApi;
|
|
217
249
|
/**
|
|
218
250
|
* Logout from fence
|
|
219
251
|
*/ const logoutFence = async (redirect = '/')=>await fetchFence({
|
|
220
|
-
endpoint: `${GEN3_FENCE_API}/
|
|
252
|
+
endpoint: `${GEN3_FENCE_API}/logout?next=${GEN3_REDIRECT_URL}${redirect}`,
|
|
221
253
|
method: 'GET'
|
|
222
254
|
});
|
|
223
255
|
|
|
@@ -238,7 +270,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
238
270
|
*/ const credentialsApi = credentialsWithTags$1.injectEndpoints({
|
|
239
271
|
endpoints: (builder)=>({
|
|
240
272
|
getCredentials: builder.query({
|
|
241
|
-
query: ()=>`${GEN3_FENCE_API}/
|
|
273
|
+
query: ()=>`${GEN3_FENCE_API}/credentials/api`,
|
|
242
274
|
transformResponse: (response)=>response['jtis'],
|
|
243
275
|
// "jtis", which is an array of API keys
|
|
244
276
|
// no need to transform the response, since the API returns the correct format
|
|
@@ -248,7 +280,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
248
280
|
}),
|
|
249
281
|
addNewCredential: builder.mutation({
|
|
250
282
|
query: (csrfToken)=>({
|
|
251
|
-
url: `${GEN3_FENCE_API}/
|
|
283
|
+
url: `${GEN3_FENCE_API}/credentials/api`,
|
|
252
284
|
method: 'POST',
|
|
253
285
|
headers: {
|
|
254
286
|
'Content-Type': 'application/json',
|
|
@@ -267,7 +299,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
267
299
|
}),
|
|
268
300
|
removeCredential: builder.mutation({
|
|
269
301
|
query: ({ csrfToken, id })=>({
|
|
270
|
-
url: `${GEN3_FENCE_API}/
|
|
302
|
+
url: `${GEN3_FENCE_API}/credentials/api/${id}`,
|
|
271
303
|
method: 'DELETE',
|
|
272
304
|
headers: {
|
|
273
305
|
'Content-Type': 'application/json',
|
|
@@ -282,7 +314,7 @@ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
|
|
|
282
314
|
}),
|
|
283
315
|
authorizeFromCredentials: builder.mutation({
|
|
284
316
|
query: (params)=>({
|
|
285
|
-
url: `${GEN3_FENCE_API}/
|
|
317
|
+
url: `${GEN3_FENCE_API}/credentials/api/access_token`,
|
|
286
318
|
method: 'POST',
|
|
287
319
|
headers: {
|
|
288
320
|
'Content-Type': 'application/json'
|
|
@@ -380,7 +412,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
380
412
|
accessToken = getCookie('credentials_token');
|
|
381
413
|
}
|
|
382
414
|
return await fetchFence({
|
|
383
|
-
endpoint: '/user
|
|
415
|
+
endpoint: '/user',
|
|
384
416
|
method: 'GET',
|
|
385
417
|
headers: {
|
|
386
418
|
Accept: 'application/json',
|
|
@@ -397,7 +429,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
397
429
|
});
|
|
398
430
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
399
431
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
400
|
-
const initialState$
|
|
432
|
+
const initialState$8 = {
|
|
401
433
|
status: 'uninitialized',
|
|
402
434
|
loginStatus: 'unauthenticated',
|
|
403
435
|
error: undefined
|
|
@@ -408,9 +440,9 @@ const initialState$5 = {
|
|
|
408
440
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
409
441
|
*/ const slice$4 = createSlice({
|
|
410
442
|
name: 'fence/user',
|
|
411
|
-
initialState: initialState$
|
|
443
|
+
initialState: initialState$8,
|
|
412
444
|
reducers: {
|
|
413
|
-
resetUserState: ()=>initialState$
|
|
445
|
+
resetUserState: ()=>initialState$8
|
|
414
446
|
},
|
|
415
447
|
extraReducers: (builder)=>{
|
|
416
448
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -580,12 +612,12 @@ const lookupGen3App = (id)=>{
|
|
|
580
612
|
return REGISTRY[id];
|
|
581
613
|
};
|
|
582
614
|
|
|
583
|
-
const initialState$
|
|
615
|
+
const initialState$7 = {
|
|
584
616
|
gen3Apps: {}
|
|
585
617
|
};
|
|
586
618
|
const slice$3 = createSlice({
|
|
587
619
|
name: 'gen3Apps',
|
|
588
|
-
initialState: initialState$
|
|
620
|
+
initialState: initialState$7,
|
|
589
621
|
reducers: {
|
|
590
622
|
addGen3AppMetadata: (state, action)=>{
|
|
591
623
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -604,11 +636,11 @@ const { addGen3AppMetadata } = slice$3.actions;
|
|
|
604
636
|
const selectGen3AppMetadataByName = (state, appName)=>state.gen3Apps.gen3Apps[appName];
|
|
605
637
|
const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize this selector
|
|
606
638
|
|
|
607
|
-
const initialState$
|
|
639
|
+
const initialState$6 = {};
|
|
608
640
|
// TODO: document what this does
|
|
609
641
|
const slice$2 = createSlice({
|
|
610
642
|
name: 'drsResolver',
|
|
611
|
-
initialState: initialState$
|
|
643
|
+
initialState: initialState$6,
|
|
612
644
|
reducers: {
|
|
613
645
|
setDRSHostnames: (_state, action)=>{
|
|
614
646
|
return action.payload;
|
|
@@ -630,13 +662,13 @@ const { setDRSHostnames } = slice$2.actions;
|
|
|
630
662
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
631
663
|
return Modals;
|
|
632
664
|
}({});
|
|
633
|
-
const initialState$
|
|
665
|
+
const initialState$5 = {
|
|
634
666
|
currentModal: null
|
|
635
667
|
};
|
|
636
668
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
637
669
|
const slice$1 = createSlice({
|
|
638
670
|
name: 'modals',
|
|
639
|
-
initialState: initialState$
|
|
671
|
+
initialState: initialState$5,
|
|
640
672
|
reducers: {
|
|
641
673
|
showModal: (state, action)=>{
|
|
642
674
|
state.currentModal = action.payload.modal;
|
|
@@ -654,141 +686,6 @@ const { showModal, hideModal } = slice$1.actions;
|
|
|
654
686
|
const selectCurrentModal = (state)=>state.modals.currentModal;
|
|
655
687
|
const selectCurrentMessage = (state)=>state.modals.message;
|
|
656
688
|
|
|
657
|
-
const EmptyCohort = {
|
|
658
|
-
id: 'default',
|
|
659
|
-
name: 'Filters',
|
|
660
|
-
filters: {},
|
|
661
|
-
modified_datetime: new Date().toISOString()
|
|
662
|
-
};
|
|
663
|
-
const initialCohortState = {
|
|
664
|
-
cohort: {
|
|
665
|
-
...EmptyCohort
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
// TODO: start using this adapter
|
|
669
|
-
/*
|
|
670
|
-
const cohortsAdapter = createEntityAdapter<Cohort>({
|
|
671
|
-
sortComparer: (a, b) => {
|
|
672
|
-
if (a.modified_datetime <= b.modified_datetime) return 1;
|
|
673
|
-
else return -1;
|
|
674
|
-
},
|
|
675
|
-
});
|
|
676
|
-
*/ /**
|
|
677
|
-
* Redux slice for cohort filters
|
|
678
|
-
*/ const cohortSlice = createSlice({
|
|
679
|
-
name: 'cohort',
|
|
680
|
-
initialState: initialCohortState,
|
|
681
|
-
reducers: {
|
|
682
|
-
// adds a filter to the cohort filter set at the given index
|
|
683
|
-
updateCohortFilter: (state, action)=>{
|
|
684
|
-
const { index, field, filter } = action.payload;
|
|
685
|
-
return {
|
|
686
|
-
cohort: {
|
|
687
|
-
...state.cohort,
|
|
688
|
-
filters: {
|
|
689
|
-
...state.cohort.filters,
|
|
690
|
-
[index]: {
|
|
691
|
-
mode: state.cohort.filters?.[index]?.mode ?? 'and',
|
|
692
|
-
root: {
|
|
693
|
-
...state.cohort.filters?.[index]?.root ?? {},
|
|
694
|
-
[field]: filter
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
},
|
|
701
|
-
setCohortFilter: (state, action)=>{
|
|
702
|
-
const { index, filters } = action.payload;
|
|
703
|
-
return {
|
|
704
|
-
cohort: {
|
|
705
|
-
...state.cohort,
|
|
706
|
-
filters: {
|
|
707
|
-
...state.cohort.filters,
|
|
708
|
-
[index]: filters
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
},
|
|
713
|
-
setCohortIndexFilters: (state, action)=>{
|
|
714
|
-
return {
|
|
715
|
-
cohort: {
|
|
716
|
-
...state.cohort,
|
|
717
|
-
filters: {
|
|
718
|
-
...action.payload.filters
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
},
|
|
723
|
-
// removes a filter to the cohort filter set at the given index
|
|
724
|
-
removeCohortFilter: (state, action)=>{
|
|
725
|
-
const { index, field } = action.payload;
|
|
726
|
-
const filters = state.cohort?.filters?.[index]?.root;
|
|
727
|
-
if (!filters) {
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
const { [field]: _a, ...updated } = filters;
|
|
731
|
-
return {
|
|
732
|
-
cohort: {
|
|
733
|
-
...state.cohort,
|
|
734
|
-
filters: {
|
|
735
|
-
...state.cohort.filters,
|
|
736
|
-
[index]: {
|
|
737
|
-
mode: state.cohort.filters[index].mode,
|
|
738
|
-
root: updated
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
},
|
|
744
|
-
// removes all filters from the cohort filter set at the given index
|
|
745
|
-
clearCohortFilters: (state, action)=>{
|
|
746
|
-
const { index } = action.payload;
|
|
747
|
-
return {
|
|
748
|
-
cohort: {
|
|
749
|
-
...state.cohort,
|
|
750
|
-
filters: {
|
|
751
|
-
...state.cohort.filters,
|
|
752
|
-
[index]: {
|
|
753
|
-
// empty filter set
|
|
754
|
-
mode: 'and',
|
|
755
|
-
root: {}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
});
|
|
763
|
-
// Filter actions: addFilter, removeFilter, updateFilter
|
|
764
|
-
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
765
|
-
const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
|
|
766
|
-
const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
|
|
767
|
-
const selectCurrentCohort = (state)=>state.cohorts.cohort;
|
|
768
|
-
const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
|
|
769
|
-
/**
|
|
770
|
-
* Select a filter by its name from the current cohort. If the filter is not found
|
|
771
|
-
* returns undefined.
|
|
772
|
-
* @param state - Core
|
|
773
|
-
* @param index which cohort index to select from
|
|
774
|
-
* @param name name of the filter to select
|
|
775
|
-
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
776
|
-
return state.cohorts.cohort.filters[index]?.root[name];
|
|
777
|
-
};
|
|
778
|
-
const EmptyFilterSet = {
|
|
779
|
-
mode: 'and',
|
|
780
|
-
root: {}
|
|
781
|
-
};
|
|
782
|
-
/**
|
|
783
|
-
* Select a filter from the index.
|
|
784
|
-
* returns undefined.
|
|
785
|
-
* @param state - Core
|
|
786
|
-
* @param index which cohort index to select from
|
|
787
|
-
*/ const selectIndexFilters = (state, index)=>{
|
|
788
|
-
return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
789
|
-
};
|
|
790
|
-
const cohortReducer = cohortSlice.reducer;
|
|
791
|
-
|
|
792
689
|
const isFileItem = (item)=>{
|
|
793
690
|
return item && 'guid' in item;
|
|
794
691
|
};
|
|
@@ -1346,17 +1243,17 @@ const useDataLibrary = (useApi)=>{
|
|
|
1346
1243
|
};
|
|
1347
1244
|
};
|
|
1348
1245
|
|
|
1349
|
-
const initialState$
|
|
1246
|
+
const initialState$4 = {};
|
|
1350
1247
|
const dataLibrarySlice = createSlice({
|
|
1351
1248
|
name: 'dataLibrary',
|
|
1352
|
-
initialState: initialState$
|
|
1249
|
+
initialState: initialState$4,
|
|
1353
1250
|
reducers: {
|
|
1354
1251
|
setDataLibraryListSelection: (state, action)=>{
|
|
1355
1252
|
const { listId, itemIds } = action.payload;
|
|
1356
1253
|
state[listId] = itemIds;
|
|
1357
1254
|
},
|
|
1358
1255
|
clearDataLibrarySelection: ()=>{
|
|
1359
|
-
return initialState$
|
|
1256
|
+
return initialState$4;
|
|
1360
1257
|
}
|
|
1361
1258
|
}
|
|
1362
1259
|
});
|
|
@@ -1422,7 +1319,7 @@ const isTimeGreaterThan = (startTime, minutes)=>{
|
|
|
1422
1319
|
};
|
|
1423
1320
|
|
|
1424
1321
|
const NO_WORKSPACE_ID = 'none';
|
|
1425
|
-
const initialState = {
|
|
1322
|
+
const initialState$3 = {
|
|
1426
1323
|
id: NO_WORKSPACE_ID,
|
|
1427
1324
|
status: WorkspaceStatus.NotFound,
|
|
1428
1325
|
requestedStatus: RequestedWorkspaceStatus.Unset,
|
|
@@ -1430,7 +1327,7 @@ const initialState = {
|
|
|
1430
1327
|
};
|
|
1431
1328
|
const slice = createSlice({
|
|
1432
1329
|
name: 'ActiveWorkspace',
|
|
1433
|
-
initialState,
|
|
1330
|
+
initialState: initialState$3,
|
|
1434
1331
|
reducers: {
|
|
1435
1332
|
setActiveWorkspaceId: (state, action)=>{
|
|
1436
1333
|
state = {
|
|
@@ -1524,6 +1421,14 @@ const guppyApiReducer = guppyApi.reducer;
|
|
|
1524
1421
|
const isOperationWithField = (operation)=>{
|
|
1525
1422
|
return operation?.field !== undefined;
|
|
1526
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
|
+
};
|
|
1527
1432
|
const extractFilterValue = (op)=>{
|
|
1528
1433
|
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1529
1434
|
return handleOperation(valueExtractorHandler, op);
|
|
@@ -1705,6 +1610,12 @@ const isFilterSet = (input)=>{
|
|
|
1705
1610
|
}
|
|
1706
1611
|
return true;
|
|
1707
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
|
+
}
|
|
1708
1619
|
|
|
1709
1620
|
const FieldNameOverrides = {};
|
|
1710
1621
|
const COMMON_PREPOSITIONS = [
|
|
@@ -1769,65 +1680,248 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
|
1769
1680
|
];
|
|
1770
1681
|
};
|
|
1771
1682
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
json
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
});
|
|
1784
|
-
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1785
|
-
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1786
|
-
return {
|
|
1787
|
-
...acc,
|
|
1788
|
-
[key]: valueData[idx]
|
|
1789
|
-
};
|
|
1790
|
-
}, {});
|
|
1791
|
-
return results;
|
|
1792
|
-
};
|
|
1793
|
-
const fetchJson = async (url)=>{
|
|
1794
|
-
const res = await fetch(url, {
|
|
1795
|
-
method: 'GET',
|
|
1796
|
-
headers: {
|
|
1797
|
-
'Content-type': 'application/json'
|
|
1798
|
-
}
|
|
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
|
+
}));
|
|
1799
1694
|
});
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
};
|
|
1803
|
-
const useGetStatus = ()=>{
|
|
1804
|
-
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1805
|
-
return useSWR('explorerStatus', fetcher);
|
|
1806
|
-
};
|
|
1695
|
+
return flattenedJson;
|
|
1696
|
+
}
|
|
1807
1697
|
/**
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1810
|
-
*
|
|
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.
|
|
1811
1722
|
*
|
|
1812
|
-
* @param
|
|
1813
|
-
* @
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1820
|
-
* @
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
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)=>{
|
|
1831
1925
|
return response[params.type];
|
|
1832
1926
|
}
|
|
1833
1927
|
}),
|
|
@@ -1922,7 +2016,32 @@ const useGetStatus = ()=>{
|
|
|
1922
2016
|
return queryBody;
|
|
1923
2017
|
},
|
|
1924
2018
|
transformResponse: (response, _meta, args)=>{
|
|
1925
|
-
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] ?? {});
|
|
1926
2045
|
}
|
|
1927
2046
|
}),
|
|
1928
2047
|
getSubAggs: builder.query({
|
|
@@ -1947,7 +2066,7 @@ const useGetStatus = ()=>{
|
|
|
1947
2066
|
};
|
|
1948
2067
|
},
|
|
1949
2068
|
transformResponse: (response, _meta, args)=>{
|
|
1950
|
-
return processHistogramResponse(response
|
|
2069
|
+
return processHistogramResponse(response?.data?._aggregation[args.type] ?? {});
|
|
1951
2070
|
}
|
|
1952
2071
|
}),
|
|
1953
2072
|
getCounts: builder.query({
|
|
@@ -1971,7 +2090,13 @@ const useGetStatus = ()=>{
|
|
|
1971
2090
|
};
|
|
1972
2091
|
},
|
|
1973
2092
|
transformResponse: (response, _meta, args)=>{
|
|
1974
|
-
|
|
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;
|
|
1975
2100
|
}
|
|
1976
2101
|
}),
|
|
1977
2102
|
getFieldCountSummary: builder.query({
|
|
@@ -2002,7 +2127,7 @@ const useGetStatus = ()=>{
|
|
|
2002
2127
|
query: (index)=>{
|
|
2003
2128
|
return {
|
|
2004
2129
|
query: `{
|
|
2005
|
-
_mapping ${index}
|
|
2130
|
+
_mapping { ${index} }
|
|
2006
2131
|
}`
|
|
2007
2132
|
};
|
|
2008
2133
|
},
|
|
@@ -2010,6 +2135,21 @@ const useGetStatus = ()=>{
|
|
|
2010
2135
|
return response['_mapping'];
|
|
2011
2136
|
}
|
|
2012
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
|
+
}),
|
|
2013
2153
|
generalGQL: builder.query({
|
|
2014
2154
|
query: ({ query, variables })=>{
|
|
2015
2155
|
return {
|
|
@@ -2080,169 +2220,7 @@ const useGetArrayTypes = ()=>{
|
|
|
2080
2220
|
return data ? data['indices'] : {};
|
|
2081
2221
|
}
|
|
2082
2222
|
};
|
|
2083
|
-
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2084
|
-
|
|
2085
|
-
/**
|
|
2086
|
-
* Flattens a deep nested JSON object skipping
|
|
2087
|
-
* the first level to avoid potentially flattening
|
|
2088
|
-
* non-nested data.
|
|
2089
|
-
* @param {JSON} json
|
|
2090
|
-
*/ function flattenJson(json) {
|
|
2091
|
-
const flattenedJson = [];
|
|
2092
|
-
Object.keys(json).forEach((key)=>{
|
|
2093
|
-
flattenedJson.push(flatten(json[key], {
|
|
2094
|
-
delimiter: '_'
|
|
2095
|
-
}));
|
|
2096
|
-
});
|
|
2097
|
-
return flattenedJson;
|
|
2098
|
-
}
|
|
2099
|
-
/**
|
|
2100
|
-
* Converts JSON based on a config.
|
|
2101
|
-
* @param {JSON} json
|
|
2102
|
-
* @param {Object} config
|
|
2103
|
-
*/ async function conversion(json, config) {
|
|
2104
|
-
return Papa.unparse(json, config);
|
|
2105
|
-
}
|
|
2106
|
-
/**
|
|
2107
|
-
* Converts JSON to a specified file format.
|
|
2108
|
-
* Defaults to JSON if file format is not supported.
|
|
2109
|
-
* @param {JSON} json
|
|
2110
|
-
* @param {string} format
|
|
2111
|
-
*/ async function jsonToFormat(json, format) {
|
|
2112
|
-
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
2113
|
-
const flatJson = await flattenJson(json);
|
|
2114
|
-
const data = await conversion(flatJson, {
|
|
2115
|
-
delimiter: FILE_DELIMITERS[format]
|
|
2116
|
-
});
|
|
2117
|
-
return data;
|
|
2118
|
-
}
|
|
2119
|
-
return json;
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
/**
|
|
2123
|
-
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2124
|
-
*
|
|
2125
|
-
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2126
|
-
* @returns {URL} - The prepared download URL as a URL object.
|
|
2127
|
-
*/ const prepareUrl$1 = (apiUrl)=>`${apiUrl}/download`;
|
|
2128
|
-
/**
|
|
2129
|
-
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
2130
|
-
*
|
|
2131
|
-
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
2132
|
-
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
2133
|
-
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
2134
|
-
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
2135
|
-
return {
|
|
2136
|
-
method: 'POST',
|
|
2137
|
-
headers: {
|
|
2138
|
-
'Content-Type': 'application/json',
|
|
2139
|
-
...csrfToken !== undefined && {
|
|
2140
|
-
'X-CSRF-Token': csrfToken
|
|
2141
|
-
}
|
|
2142
|
-
},
|
|
2143
|
-
body: JSON.stringify({
|
|
2144
|
-
type: parameters.type,
|
|
2145
|
-
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
2146
|
-
accessibility: parameters.accessibility,
|
|
2147
|
-
fields: parameters?.fields,
|
|
2148
|
-
sort: parameters?.sort
|
|
2149
|
-
})
|
|
2150
|
-
};
|
|
2151
|
-
};
|
|
2152
|
-
/**
|
|
2153
|
-
* Downloads a file from Guppy using the provided parameters.
|
|
2154
|
-
* It will optionally convert the data to the specified format.
|
|
2155
|
-
*
|
|
2156
|
-
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
2157
|
-
* @param onStart - The function to call when the download starts.
|
|
2158
|
-
* @param onDone - The function to call when the download is done.
|
|
2159
|
-
* @param onError - The function to call when the download fails.
|
|
2160
|
-
* @param onAbort - The function to call when the download is aborted.
|
|
2161
|
-
* @param signal - AbortSignal to use for the request.
|
|
2162
|
-
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
2163
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2164
|
-
onStart?.();
|
|
2165
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2166
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2167
|
-
fetch(url.toString(), {
|
|
2168
|
-
...fetchConfig,
|
|
2169
|
-
...signal ? {
|
|
2170
|
-
signal: signal
|
|
2171
|
-
} : {}
|
|
2172
|
-
}).then(async (response)=>{
|
|
2173
|
-
if (!response.ok) {
|
|
2174
|
-
throw new Error(response.statusText);
|
|
2175
|
-
}
|
|
2176
|
-
let jsonData = await response.json();
|
|
2177
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2178
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2179
|
-
jsonData = JSONPath({
|
|
2180
|
-
json: jsonData,
|
|
2181
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2182
|
-
resultType: 'value'
|
|
2183
|
-
});
|
|
2184
|
-
}
|
|
2185
|
-
// convert the data to the specified format and return a Blob
|
|
2186
|
-
let str = '';
|
|
2187
|
-
if (parameters.format === 'json') {
|
|
2188
|
-
str = JSON.stringify(jsonData);
|
|
2189
|
-
} else {
|
|
2190
|
-
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
2191
|
-
if (isJSONObject(convertedData)) {
|
|
2192
|
-
str = JSON.stringify(convertedData, null, 2);
|
|
2193
|
-
} else {
|
|
2194
|
-
str = convertedData;
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2197
|
-
return new Blob([
|
|
2198
|
-
str
|
|
2199
|
-
], {
|
|
2200
|
-
type: 'application/json'
|
|
2201
|
-
});
|
|
2202
|
-
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
2203
|
-
// Abort is handle as an exception
|
|
2204
|
-
if (error.name == 'AbortError') {
|
|
2205
|
-
// handle abort()
|
|
2206
|
-
onAbort?.();
|
|
2207
|
-
}
|
|
2208
|
-
onError?.(error);
|
|
2209
|
-
});
|
|
2210
|
-
};
|
|
2211
|
-
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
2212
|
-
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2213
|
-
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2214
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2215
|
-
try {
|
|
2216
|
-
const response = await fetch(url.toString(), {
|
|
2217
|
-
...fetchConfig,
|
|
2218
|
-
...signal ? {
|
|
2219
|
-
signal: signal
|
|
2220
|
-
} : {}
|
|
2221
|
-
});
|
|
2222
|
-
let jsonData = await response.json();
|
|
2223
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
2224
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
2225
|
-
jsonData = JSONPath({
|
|
2226
|
-
json: jsonData,
|
|
2227
|
-
path: `$.[${parameters.rootPath}]`,
|
|
2228
|
-
resultType: 'value'
|
|
2229
|
-
});
|
|
2230
|
-
}
|
|
2231
|
-
// convert the data to the specified format and return a Blob
|
|
2232
|
-
return jsonData;
|
|
2233
|
-
} catch (error) {
|
|
2234
|
-
// Abort is handle as an exception
|
|
2235
|
-
if (error.name == 'AbortError') {
|
|
2236
|
-
// handle abort()
|
|
2237
|
-
onAbort?.();
|
|
2238
|
-
}
|
|
2239
|
-
throw new Error(error);
|
|
2240
|
-
}
|
|
2241
|
-
};
|
|
2242
|
-
const useGetIndexFields = (index)=>{
|
|
2243
|
-
const { data } = useGetFieldsForIndexQuery(index);
|
|
2244
|
-
return data ?? [];
|
|
2245
|
-
};
|
|
2223
|
+
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useGetAggsNoFilterSelfQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetSharedFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2246
2224
|
|
|
2247
2225
|
/**
|
|
2248
2226
|
* Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
|
|
@@ -2278,13 +2256,230 @@ const useGetIndexFields = (index)=>{
|
|
|
2278
2256
|
});
|
|
2279
2257
|
const { useDownloadFromGuppyMutation } = downloadRequestApi;
|
|
2280
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
|
+
const { [field]: _a, ...updated } = filters;
|
|
2333
|
+
return {
|
|
2334
|
+
cohort: {
|
|
2335
|
+
...state.cohort,
|
|
2336
|
+
filters: {
|
|
2337
|
+
...state.cohort.filters,
|
|
2338
|
+
[index]: {
|
|
2339
|
+
mode: state.cohort.filters[index].mode,
|
|
2340
|
+
root: updated
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
};
|
|
2345
|
+
},
|
|
2346
|
+
// removes all filters from the cohort filter set at the given index
|
|
2347
|
+
clearCohortFilters: (state, action)=>{
|
|
2348
|
+
const { index } = action.payload;
|
|
2349
|
+
return {
|
|
2350
|
+
cohort: {
|
|
2351
|
+
...state.cohort,
|
|
2352
|
+
filters: {
|
|
2353
|
+
...state.cohort.filters,
|
|
2354
|
+
[index]: {
|
|
2355
|
+
// empty filter set
|
|
2356
|
+
mode: 'and',
|
|
2357
|
+
root: {}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
// Filter actions: addFilter, removeFilter, updateFilter
|
|
2366
|
+
const { updateCohortFilter, setCohortFilter, setCohortIndexFilters, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
|
|
2367
|
+
const selectCohortFilters = (state)=>state.cohorts.cohort.cohort.filters;
|
|
2368
|
+
const selectCurrentCohortId = (state)=>state.cohorts.cohort.cohort.id;
|
|
2369
|
+
const selectCurrentCohort = (state)=>state.cohorts.cohort.cohort;
|
|
2370
|
+
const selectCurrentCohortName = (state)=>state.cohorts.cohort.cohort.name;
|
|
2371
|
+
/**
|
|
2372
|
+
* Select a filter by its name from the current cohort. If the filter is not found
|
|
2373
|
+
* returns undefined.
|
|
2374
|
+
* @param state - Core
|
|
2375
|
+
* @param index which cohort index to select from
|
|
2376
|
+
* @param name name of the filter to select
|
|
2377
|
+
*/ const selectIndexedFilterByName = (state, index, name)=>{
|
|
2378
|
+
return state.cohorts.cohort.cohort.filters[index]?.root[name];
|
|
2379
|
+
};
|
|
2380
|
+
const EmptyFilterSet = {
|
|
2381
|
+
mode: 'and',
|
|
2382
|
+
root: {}
|
|
2383
|
+
};
|
|
2384
|
+
/**
|
|
2385
|
+
* Select a filter from the index.
|
|
2386
|
+
* returns undefined.
|
|
2387
|
+
* @param state - Core
|
|
2388
|
+
* @param index which cohort index to select from
|
|
2389
|
+
*/ const selectIndexFilters = (state, index)=>{
|
|
2390
|
+
return state.cohorts.cohort.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
|
|
2391
|
+
};
|
|
2392
|
+
const cohortReducer = cohortSlice.reducer;
|
|
2393
|
+
|
|
2394
|
+
const initialState$2 = {};
|
|
2395
|
+
const expandSlice$1 = createSlice({
|
|
2396
|
+
name: 'CohortBuilder/filterExpand',
|
|
2397
|
+
initialState: initialState$2,
|
|
2398
|
+
reducers: {
|
|
2399
|
+
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
2400
|
+
return {
|
|
2401
|
+
...state,
|
|
2402
|
+
[action.payload.index]: {
|
|
2403
|
+
...state[action.payload.index],
|
|
2404
|
+
[action.payload.field]: action.payload.expanded
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
},
|
|
2408
|
+
toggleCohortBuilderAllFilters: (state, action)=>{
|
|
2409
|
+
return {
|
|
2410
|
+
...state,
|
|
2411
|
+
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
2412
|
+
acc[k] = action.payload.expand;
|
|
2413
|
+
return acc;
|
|
2414
|
+
}, {})
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
2420
|
+
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
2421
|
+
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
2422
|
+
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
2423
|
+
|
|
2424
|
+
const initialState$1 = {};
|
|
2425
|
+
const expandSlice = createSlice({
|
|
2426
|
+
name: 'CohortBuilder/filterCombineMode',
|
|
2427
|
+
initialState: initialState$1,
|
|
2428
|
+
reducers: {
|
|
2429
|
+
setCohortFilterCombineMode: (state, action)=>{
|
|
2430
|
+
return {
|
|
2431
|
+
...state,
|
|
2432
|
+
[action.payload.index]: {
|
|
2433
|
+
...state[action.payload.index],
|
|
2434
|
+
[action.payload.field]: action.payload.mode
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
|
|
2441
|
+
const { setCohortFilterCombineMode } = expandSlice.actions;
|
|
2442
|
+
const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
|
|
2443
|
+
|
|
2444
|
+
const initialState = {
|
|
2445
|
+
shouldShareFilters: false,
|
|
2446
|
+
sharedFiltersMap: {}
|
|
2447
|
+
};
|
|
2448
|
+
const cohortSharedFiltersSlice = createSlice({
|
|
2449
|
+
name: 'cohortSharedFilters',
|
|
2450
|
+
initialState: initialState,
|
|
2451
|
+
reducers: {
|
|
2452
|
+
setShouldShareFilters: (state, action)=>{
|
|
2453
|
+
state.shouldShareFilters = action.payload;
|
|
2454
|
+
return state;
|
|
2455
|
+
},
|
|
2456
|
+
setSharedFilters: (state, action)=>{
|
|
2457
|
+
state.sharedFiltersMap = action.payload;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
|
|
2462
|
+
const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
|
|
2463
|
+
const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
|
|
2464
|
+
field
|
|
2465
|
+
];
|
|
2466
|
+
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
2467
|
+
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
2468
|
+
|
|
2469
|
+
const cohortReducers = combineReducers({
|
|
2470
|
+
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
2471
|
+
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
2472
|
+
sharedFilters: cohortSharedFiltersReducer,
|
|
2473
|
+
cohort: cohortReducer
|
|
2474
|
+
});
|
|
2475
|
+
|
|
2281
2476
|
const rootReducer = combineReducers({
|
|
2282
2477
|
gen3Services: gen3ServicesReducer,
|
|
2283
2478
|
user: userReducer,
|
|
2284
2479
|
gen3Apps: gen3AppReducer,
|
|
2285
2480
|
drsHostnames: drsHostnamesReducer,
|
|
2286
2481
|
modals: modalReducer,
|
|
2287
|
-
cohorts:
|
|
2482
|
+
cohorts: cohortReducers,
|
|
2288
2483
|
activeWorkspace: activeWorkspaceReducer,
|
|
2289
2484
|
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2290
2485
|
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
@@ -2313,7 +2508,7 @@ const persistConfig = {
|
|
|
2313
2508
|
version: 1,
|
|
2314
2509
|
storage,
|
|
2315
2510
|
whitelist: [
|
|
2316
|
-
'
|
|
2511
|
+
'cohort',
|
|
2317
2512
|
'activeWorkspace'
|
|
2318
2513
|
]
|
|
2319
2514
|
};
|
|
@@ -2338,7 +2533,7 @@ const coreStore = setupCoreStore();
|
|
|
2338
2533
|
setupListeners(coreStore.dispatch);
|
|
2339
2534
|
|
|
2340
2535
|
const isNotDefined = (x)=>{
|
|
2341
|
-
return x === undefined || x === null || x ===
|
|
2536
|
+
return x === undefined || x === null || x === void 0;
|
|
2342
2537
|
};
|
|
2343
2538
|
const isObject = (x)=>{
|
|
2344
2539
|
return typeof x === 'object';
|
|
@@ -2357,51 +2552,14 @@ const isString = (x)=>{
|
|
|
2357
2552
|
* @returns {URL} - The prepared download URL as a URL object.
|
|
2358
2553
|
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2359
2554
|
|
|
2360
|
-
const
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
406: 'Not Acceptable',
|
|
2369
|
-
407: 'Proxy Authentication Required',
|
|
2370
|
-
408: 'Request Timeout',
|
|
2371
|
-
409: 'Conflict',
|
|
2372
|
-
410: 'Gone',
|
|
2373
|
-
411: 'Length Required',
|
|
2374
|
-
412: 'Precondition Failed',
|
|
2375
|
-
413: 'Payload Too Large',
|
|
2376
|
-
414: 'URI Too Long',
|
|
2377
|
-
415: 'Unsupported Media Type',
|
|
2378
|
-
416: 'Range Not Satisfiable',
|
|
2379
|
-
417: 'Expectation Failed',
|
|
2380
|
-
418: "I'm a teapot",
|
|
2381
|
-
421: 'Misdirected Request',
|
|
2382
|
-
422: 'Unprocessable Entity',
|
|
2383
|
-
423: 'Locked',
|
|
2384
|
-
424: 'Failed Dependency',
|
|
2385
|
-
425: 'Too Early',
|
|
2386
|
-
426: 'Upgrade Required',
|
|
2387
|
-
428: 'Precondition Required',
|
|
2388
|
-
429: 'Too Many Requests',
|
|
2389
|
-
431: 'Request Header Fields Too Large',
|
|
2390
|
-
451: 'Unavailable For Legal Reasons',
|
|
2391
|
-
// 5xx Server Errors
|
|
2392
|
-
500: 'Internal Server Error',
|
|
2393
|
-
501: 'Not Implemented',
|
|
2394
|
-
502: 'Bad Gateway',
|
|
2395
|
-
503: 'Service Unavailable',
|
|
2396
|
-
504: 'Gateway Timeout',
|
|
2397
|
-
505: 'HTTP Version Not Supported',
|
|
2398
|
-
506: 'Variant Also Negotiates',
|
|
2399
|
-
507: 'Insufficient Storage',
|
|
2400
|
-
508: 'Loop Detected',
|
|
2401
|
-
510: 'Not Extended',
|
|
2402
|
-
511: 'Network Authentication Required'
|
|
2403
|
-
};
|
|
2404
|
-
class HTTPError extends Error {
|
|
2555
|
+
const DEFAULT_METHOD = 'GET';
|
|
2556
|
+
const CONTENT_TYPE_HEADER = 'Content-Type';
|
|
2557
|
+
const CONTENT_TYPE_JSON = 'application/json';
|
|
2558
|
+
/**
|
|
2559
|
+
* Represents an error that occurs during an HTTP request.
|
|
2560
|
+
* Extends the built-in `Error` class to provide additional information
|
|
2561
|
+
* about the HTTP status code and optional response data.
|
|
2562
|
+
*/ class HTTPError extends Error {
|
|
2405
2563
|
constructor(status, message, responseData){
|
|
2406
2564
|
super(message), this.status = status, this.responseData = responseData;
|
|
2407
2565
|
this.name = 'HTTPError';
|
|
@@ -2411,15 +2569,16 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2411
2569
|
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2412
2570
|
const headers = new Headers();
|
|
2413
2571
|
headers.set('Content-Type', 'application/json');
|
|
2414
|
-
let accessToken = undefined;
|
|
2415
2572
|
if (process.env.NODE_ENV === 'development') {
|
|
2416
2573
|
// NOTE: This cookie can only be accessed from the client side
|
|
2417
2574
|
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2418
|
-
accessToken = getCookie('credentials_token');
|
|
2575
|
+
const accessToken = getCookie('credentials_token');
|
|
2576
|
+
if (accessToken) {
|
|
2577
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2578
|
+
}
|
|
2419
2579
|
}
|
|
2420
2580
|
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2421
|
-
|
|
2422
|
-
const url = `${GEN3_FENCE_API}/user/data/download/${guid}`;
|
|
2581
|
+
const url = `${GEN3_FENCE_API}/data/download/${guid}`;
|
|
2423
2582
|
try {
|
|
2424
2583
|
const response = await fetch(url, {
|
|
2425
2584
|
method: method,
|
|
@@ -2452,6 +2611,67 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2452
2611
|
throw error;
|
|
2453
2612
|
}
|
|
2454
2613
|
};
|
|
2614
|
+
/**
|
|
2615
|
+
* Retrieves a CSRF token from the server.
|
|
2616
|
+
*
|
|
2617
|
+
* This asynchronous function sends a GET request to the server's status endpoint
|
|
2618
|
+
* to fetch the CSRF token in the response. The token is expected to be included
|
|
2619
|
+
* in the JSON response under the `csrf_token` field.
|
|
2620
|
+
*
|
|
2621
|
+
* @returns {Promise<string | null>} A promise that resolves to the CSRF token as a string if successfully retrieved,
|
|
2622
|
+
* or null if the token is not present in the response.
|
|
2623
|
+
* @throws {HTTPError} Throws an HTTPError if the server response is not successful.
|
|
2624
|
+
*/ const getCSRFToken = async ()=>{
|
|
2625
|
+
const requestHeaders = new Headers({
|
|
2626
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2627
|
+
});
|
|
2628
|
+
const response = await fetch(`${GEN3_API}/_status`, {
|
|
2629
|
+
headers: requestHeaders
|
|
2630
|
+
});
|
|
2631
|
+
if (!response.ok) {
|
|
2632
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2633
|
+
}
|
|
2634
|
+
const { csrf_token: csrfToken } = await response.json();
|
|
2635
|
+
return csrfToken || null;
|
|
2636
|
+
};
|
|
2637
|
+
/**
|
|
2638
|
+
* Fetches JSON data from a specified URL using the Fetch API.
|
|
2639
|
+
*
|
|
2640
|
+
* @param {string} url - The URL to fetch the JSON data from.
|
|
2641
|
+
* @param {boolean} [requiresCSRF=false] - Indicates whether a CSRF token is required for the request.
|
|
2642
|
+
* If true, the CSRF token will be added to the request headers.
|
|
2643
|
+
* @param {string} [method=DEFAULT_METHOD] - The HTTP method to use for the request (e.g., 'GET', 'POST').
|
|
2644
|
+
* @param {unknown} [body=undefined] - The request body to send, applicable when using methods like 'POST'.
|
|
2645
|
+
*
|
|
2646
|
+
* @returns {Promise<any>} A promise that resolves to the parsed JSON data from the response.
|
|
2647
|
+
*
|
|
2648
|
+
* @throws {HTTPError} Throws an error if the HTTP response status indicates a failure.
|
|
2649
|
+
*/ const fetchJSONDataFromURL = async (url, requiresCSRF = false, method = DEFAULT_METHOD, body = undefined)=>{
|
|
2650
|
+
const requestHeaders = new Headers({
|
|
2651
|
+
[CONTENT_TYPE_HEADER]: CONTENT_TYPE_JSON
|
|
2652
|
+
});
|
|
2653
|
+
if (requiresCSRF) {
|
|
2654
|
+
const csrfToken = await getCSRFToken();
|
|
2655
|
+
if (csrfToken) {
|
|
2656
|
+
requestHeaders.set('X-CSRF-Token', csrfToken);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2660
|
+
const accessToken = getCookie('credentials_token');
|
|
2661
|
+
if (accessToken) {
|
|
2662
|
+
requestHeaders.set('Authorization', `Bearer ${accessToken}`);
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
const response = await fetch(url, {
|
|
2666
|
+
method,
|
|
2667
|
+
headers: requestHeaders,
|
|
2668
|
+
body: method === 'POST' ? JSON.stringify(body) : null
|
|
2669
|
+
});
|
|
2670
|
+
if (!response.ok) {
|
|
2671
|
+
throw new HTTPError(response.status, response.statusText);
|
|
2672
|
+
}
|
|
2673
|
+
return response.json();
|
|
2674
|
+
};
|
|
2455
2675
|
|
|
2456
2676
|
const persistor = persistStore(coreStore);
|
|
2457
2677
|
const CoreProvider = ({ children })=>{
|
|
@@ -3261,5 +3481,5 @@ const { useGetProjectsQuery, useGetSubmissionGraphQLQuery, useGetProjectsDetails
|
|
|
3261
3481
|
});
|
|
3262
3482
|
const { useGetSowerJobListQuery, useLazyGetSowerJobListQuery, useSubmitSowerJobMutation, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery } = loadingStatusApi;
|
|
3263
3483
|
|
|
3264
|
-
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,
|
|
3484
|
+
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 };
|
|
3265
3485
|
//# sourceMappingURL=index.js.map
|