@gen3/core 0.10.54 → 0.10.56
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 +1866 -1057
- package/dist/cjs/index.js.map +1 -1
- package/dist/dts/constants.d.ts +1 -0
- package/dist/dts/features/aiSearch/aiSearchSlice.d.ts +10 -10
- package/dist/dts/features/authz/authzMappingSlice.d.ts +16 -16
- package/dist/dts/features/dataLibrary/dataLibraryApi.d.ts +1085 -0
- package/dist/dts/features/dataLibrary/dataLibraryIndexDB.d.ts +34 -0
- package/dist/dts/features/dataLibrary/dataLibrarySelectionSlice.d.ts +30 -0
- package/dist/dts/features/dataLibrary/index.d.ts +6 -0
- package/dist/dts/features/dataLibrary/types.d.ts +86 -0
- package/dist/dts/features/dataLibrary/useDataLibrary.d.ts +13 -0
- package/dist/dts/features/dataLibrary/utils.d.ts +13 -0
- package/dist/dts/features/download/downloadStatusApi.d.ts +11 -11
- package/dist/dts/features/fence/credentialsApi.d.ts +30 -30
- package/dist/dts/features/fence/fenceApi.d.ts +263 -1
- package/dist/dts/features/fence/index.d.ts +2 -2
- package/dist/dts/features/filters/types.d.ts +1 -0
- package/dist/dts/features/gen3Apps/Gen3AppRTKQ.d.ts +1 -1
- package/dist/dts/features/graphQL/graphQLSlice.d.ts +1 -1
- package/dist/dts/features/guppy/guppyDownloadSlice.d.ts +10 -10
- package/dist/dts/features/guppy/guppySlice.d.ts +1 -0
- package/dist/dts/features/guppy/tests/guppySlice.unit.test.d.ts +1 -0
- package/dist/dts/features/guppy/tests/jsonpath.unit.test.d.ts +1 -0
- package/dist/dts/features/sower/sowerApi.d.ts +10 -10
- package/dist/dts/features/submission/submissionApi.d.ts +1 -1
- package/dist/dts/features/user/hooks.d.ts +1 -1
- package/dist/dts/features/user/userSliceRTK.d.ts +47 -28
- package/dist/dts/features/user/utils.d.ts +9 -0
- package/dist/dts/features/workspace/workspaceApi.d.ts +58 -58
- package/dist/dts/hooks.d.ts +2 -0
- package/dist/dts/index.d.ts +2 -0
- package/dist/dts/reducers.d.ts +2 -0
- package/dist/dts/store.d.ts +2 -0
- package/dist/dts/utils/fetch.d.ts +17 -0
- package/dist/dts/utils/index.d.ts +3 -1
- package/dist/dts/utils/url.d.ts +7 -0
- package/dist/esm/index.js +1844 -1061
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +1325 -156
- package/package.json +2 -3
package/dist/esm/index.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { createSelector, createAsyncThunk, createSlice, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
1
|
+
import { createSelector, createAsyncThunk, createSlice, nanoid, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
2
|
import { createApi, fetchBaseQuery, buildCreateApi, coreModule, reactHooksModule } from '@reduxjs/toolkit/query/react';
|
|
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, { useRef, useEffect } from 'react';
|
|
6
|
+
import React__default, { useRef, useEffect, useState } from 'react';
|
|
7
7
|
import { useSelector, useDispatch, Provider, createSelectorHook, createDispatchHook, createStoreHook } from 'react-redux';
|
|
8
|
-
import { isEqual } from 'lodash';
|
|
9
|
-
import
|
|
8
|
+
import { isEqual, isArray as isArray$1 } from 'lodash';
|
|
9
|
+
import { openDB } from 'idb';
|
|
10
|
+
import useSWR from 'swr';
|
|
10
11
|
import { JSONPath } from 'jsonpath-plus';
|
|
12
|
+
import { flatten } from 'flat';
|
|
13
|
+
import Papa from 'papaparse';
|
|
14
|
+
import Queue from 'queue';
|
|
11
15
|
import { v5 } from 'uuid';
|
|
12
16
|
import { FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist';
|
|
13
17
|
import { CookiesProvider } from 'react-cookie';
|
|
14
|
-
import useSWR from 'swr';
|
|
15
|
-
import { flatten } from 'flat';
|
|
16
|
-
import Papa from 'papaparse';
|
|
17
18
|
|
|
18
19
|
const GEN3_COMMONS_NAME = process.env.GEN3_COMMONS_NAME || 'gen3';
|
|
19
20
|
const GEN3_API = process.env.NEXT_PUBLIC_GEN3_API || '';
|
|
@@ -30,6 +31,7 @@ const GEN3_REDIRECT_URL = process.env.NEXT_PUBLIC_GEN3_REDIRECT_URL || GEN3_API;
|
|
|
30
31
|
const GEN3_WORKSPACE_API = process.env.NEXT_PUBLIC_GEN3_WORKSPACE_STATUS_API || `${GEN3_API}/lw-workspace`;
|
|
31
32
|
const GEN3_SUBMISSION_API = process.env.NEXT_PUBLIC_GEN3_SUBMISSION_API || `${GEN3_API}/api/v0/submission`;
|
|
32
33
|
const GEN3_WTS_API = process.env.NEXT_PUBLIC_GEN3_WTS_API || `${GEN3_API}/wts`;
|
|
34
|
+
const GEN3_DATA_LIBRARY_API = process.env.NEXT_PUBLIC_GEN3_DATA_LIBRARY_API || `${GEN3_API}/library/lists`;
|
|
33
35
|
const GEN3_CROSSWALK_API = process.env.NEXT_PUBLIC_GEN3_CROSSWALK_API || `${GEN3_API}/mds`;
|
|
34
36
|
const GEN3_SOWER_API = process.env.NEXT_PUBLIC_GEN3_SOWER_API || `${GEN3_API}/jobs`;
|
|
35
37
|
var Accessibility;
|
|
@@ -43,6 +45,10 @@ const FILE_DELIMITERS = {
|
|
|
43
45
|
csv: ','
|
|
44
46
|
};
|
|
45
47
|
|
|
48
|
+
// import { selectCSRFToken } from '../user/userSliceRTK';
|
|
49
|
+
// import { coreStore } from '../../store';
|
|
50
|
+
// import { prepareUrl } from '../../utils';
|
|
51
|
+
// import { getCookie } from 'cookies-next';
|
|
46
52
|
/**
|
|
47
53
|
* Template for fence error response dict
|
|
48
54
|
* @returns: An error dict response from a RESTFUL API request
|
|
@@ -74,7 +80,59 @@ const FILE_DELIMITERS = {
|
|
|
74
80
|
headers,
|
|
75
81
|
body
|
|
76
82
|
});
|
|
83
|
+
}; /*
|
|
84
|
+
interface DownloadFromFenceParameters {
|
|
85
|
+
guid: string;
|
|
86
|
+
method?: 'GET' | 'POST';
|
|
87
|
+
onStart?: () => void; // function to call when the download starts
|
|
88
|
+
onDone?: (blob: Blob) => void; // function to call when the download is done
|
|
89
|
+
onError?: (error: Error) => void; // function to call when the download fails
|
|
90
|
+
onAbort?: () => void; // function to call when the download is aborted
|
|
91
|
+
signal?: AbortSignal; // AbortSignal to use for the request
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
export const fetchFencePresignedURL = async ({
|
|
96
|
+
guid,
|
|
97
|
+
method = 'GET',
|
|
98
|
+
onAbort = () => null,
|
|
99
|
+
signal = undefined,
|
|
100
|
+
}: DownloadFromFenceParameters) => {
|
|
101
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
102
|
+
|
|
103
|
+
const headers = new Headers();
|
|
104
|
+
headers.set('Content-Type', 'application/json');
|
|
105
|
+
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
106
|
+
let accessToken = undefined;
|
|
107
|
+
if (process.env.NODE_ENV === 'development') {
|
|
108
|
+
// NOTE: This cookie can only be accessed from the client side
|
|
109
|
+
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
110
|
+
accessToken = getCookie('credentials_token');
|
|
111
|
+
}
|
|
112
|
+
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
113
|
+
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
|
|
114
|
+
const url = prepareUrl(`${GEN3_FENCE_API}/user/download/${guid}`);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetch(url.toString(), {
|
|
118
|
+
method: method,
|
|
119
|
+
headers: headers,
|
|
120
|
+
...(signal ? { signal: signal } : {}),
|
|
121
|
+
} as RequestInit);
|
|
122
|
+
|
|
123
|
+
const jsonData = await response.json();
|
|
124
|
+
// convert the data to the specified format and return a Blob
|
|
125
|
+
return jsonData;
|
|
126
|
+
} catch (error: any) {
|
|
127
|
+
// Abort is handle as an exception
|
|
128
|
+
if (error.name == 'AbortError') {
|
|
129
|
+
// handle abort()
|
|
130
|
+
onAbort?.();
|
|
131
|
+
}
|
|
132
|
+
throw new Error(error);
|
|
133
|
+
}
|
|
77
134
|
};
|
|
135
|
+
*/
|
|
78
136
|
|
|
79
137
|
const userAuthApi = createApi({
|
|
80
138
|
reducerPath: 'userAuthApi',
|
|
@@ -204,10 +262,13 @@ const gen3ServicesReducerMiddleware = gen3Api.middleware;
|
|
|
204
262
|
endpoints: (builder)=>({
|
|
205
263
|
getLoginProviders: builder.query({
|
|
206
264
|
query: ()=>`${GEN3_FENCE_API}/user/login`
|
|
265
|
+
}),
|
|
266
|
+
getDownload: builder.query({
|
|
267
|
+
query: (guid)=>`${GEN3_FENCE_API}/user/data/download/${guid}`
|
|
207
268
|
})
|
|
208
269
|
})
|
|
209
270
|
});
|
|
210
|
-
const { useGetLoginProvidersQuery } = loginProvidersApi;
|
|
271
|
+
const { useGetLoginProvidersQuery, useGetDownloadQuery, useLazyGetDownloadQuery } = loginProvidersApi;
|
|
211
272
|
/**
|
|
212
273
|
* Logout from fence
|
|
213
274
|
*/ const logoutFence = async (redirect = '/')=>await fetchFence({
|
|
@@ -391,7 +452,7 @@ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
|
|
|
391
452
|
});
|
|
392
453
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
393
454
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
394
|
-
const initialState$
|
|
455
|
+
const initialState$5 = {
|
|
395
456
|
status: 'uninitialized',
|
|
396
457
|
loginStatus: 'unauthenticated',
|
|
397
458
|
error: undefined
|
|
@@ -402,9 +463,9 @@ const initialState$4 = {
|
|
|
402
463
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
403
464
|
*/ const slice$4 = createSlice({
|
|
404
465
|
name: 'fence/user',
|
|
405
|
-
initialState: initialState$
|
|
466
|
+
initialState: initialState$5,
|
|
406
467
|
reducers: {
|
|
407
|
-
resetUserState: ()=>initialState$
|
|
468
|
+
resetUserState: ()=>initialState$5
|
|
408
469
|
},
|
|
409
470
|
extraReducers: (builder)=>{
|
|
410
471
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -574,12 +635,12 @@ const lookupGen3App = (id)=>{
|
|
|
574
635
|
return REGISTRY[id];
|
|
575
636
|
};
|
|
576
637
|
|
|
577
|
-
const initialState$
|
|
638
|
+
const initialState$4 = {
|
|
578
639
|
gen3Apps: {}
|
|
579
640
|
};
|
|
580
641
|
const slice$3 = createSlice({
|
|
581
642
|
name: 'gen3Apps',
|
|
582
|
-
initialState: initialState$
|
|
643
|
+
initialState: initialState$4,
|
|
583
644
|
reducers: {
|
|
584
645
|
addGen3AppMetadata: (state, action)=>{
|
|
585
646
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -598,11 +659,11 @@ const { addGen3AppMetadata } = slice$3.actions;
|
|
|
598
659
|
const selectGen3AppMetadataByName = (state, appName)=>state.gen3Apps.gen3Apps[appName];
|
|
599
660
|
const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize this selector
|
|
600
661
|
|
|
601
|
-
const initialState$
|
|
662
|
+
const initialState$3 = {};
|
|
602
663
|
// TODO: document what this does
|
|
603
664
|
const slice$2 = createSlice({
|
|
604
665
|
name: 'drsResolver',
|
|
605
|
-
initialState: initialState$
|
|
666
|
+
initialState: initialState$3,
|
|
606
667
|
reducers: {
|
|
607
668
|
setDRSHostnames: (_state, action)=>{
|
|
608
669
|
return action.payload;
|
|
@@ -620,13 +681,13 @@ var Modals;
|
|
|
620
681
|
Modals["CreateCredentialsAPIKeyModal"] = "CreateCredentialsAPIKeyModal";
|
|
621
682
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
622
683
|
})(Modals || (Modals = {}));
|
|
623
|
-
const initialState$
|
|
684
|
+
const initialState$2 = {
|
|
624
685
|
currentModal: null
|
|
625
686
|
};
|
|
626
687
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
627
688
|
const slice$1 = createSlice({
|
|
628
689
|
name: 'modals',
|
|
629
|
-
initialState: initialState$
|
|
690
|
+
initialState: initialState$2,
|
|
630
691
|
reducers: {
|
|
631
692
|
showModal: (state, action)=>{
|
|
632
693
|
state.currentModal = action.payload.modal;
|
|
@@ -766,6 +827,587 @@ const EmptyFilterSet = {
|
|
|
766
827
|
};
|
|
767
828
|
const cohortReducer = cohortSlice.reducer;
|
|
768
829
|
|
|
830
|
+
const isFileItem = (item)=>{
|
|
831
|
+
return item && 'guid' in item;
|
|
832
|
+
};
|
|
833
|
+
const isAdditionalDataItem = (item)=>{
|
|
834
|
+
return item.itemType === 'AdditionalData'; // TODO resolve this with type from the api
|
|
835
|
+
};
|
|
836
|
+
// Type guard for CohortItem
|
|
837
|
+
const isCohortItem = (item)=>{
|
|
838
|
+
return item && 'data' in item && 'schemaVersion' in item && item.itemType === 'Gen3GraphQL';
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
const processItem = (id, data)=>{
|
|
842
|
+
if (data?.type === 'AdditionalData') {
|
|
843
|
+
return {
|
|
844
|
+
name: data.name,
|
|
845
|
+
itemType: 'AdditionalData',
|
|
846
|
+
description: data?.description,
|
|
847
|
+
documentationUrl: data?.documentationUrl,
|
|
848
|
+
url: data?.url
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
...data,
|
|
853
|
+
itemType: 'Data',
|
|
854
|
+
guid: data.id,
|
|
855
|
+
id: id
|
|
856
|
+
};
|
|
857
|
+
};
|
|
858
|
+
const BuildList = (listId, listData)=>{
|
|
859
|
+
if (!Object.keys(listData).includes('items')) return undefined;
|
|
860
|
+
const items = Object.entries(listData?.items).reduce((acc, [id, data])=>{
|
|
861
|
+
if (data?.type === 'Gen3GraphQL') {
|
|
862
|
+
acc.items[id] = {
|
|
863
|
+
itemType: 'Gen3GraphQL',
|
|
864
|
+
id: data.guid,
|
|
865
|
+
schemaVersion: data.schema_version,
|
|
866
|
+
data: data.data,
|
|
867
|
+
name: data.name,
|
|
868
|
+
index: data.index
|
|
869
|
+
};
|
|
870
|
+
} else {
|
|
871
|
+
if (!(data.dataset_guid in acc.items)) {
|
|
872
|
+
acc.items[data.dataset_guid] = {
|
|
873
|
+
id: data.dataset_guid,
|
|
874
|
+
name: '',
|
|
875
|
+
items: {
|
|
876
|
+
[id]: processItem(id, data)
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
} else {
|
|
880
|
+
acc.items[data.dataset_guid].items[id] = processItem(id, data);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return acc;
|
|
884
|
+
}, {
|
|
885
|
+
items: {},
|
|
886
|
+
version: listData?.version ?? 0,
|
|
887
|
+
createdTime: listData?.created_time,
|
|
888
|
+
updatedTime: listData?.updated_time,
|
|
889
|
+
name: listData?.name ?? listId,
|
|
890
|
+
id: listId,
|
|
891
|
+
authz: {
|
|
892
|
+
version: listData.authz.version,
|
|
893
|
+
authz: listData.authz
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
return items;
|
|
897
|
+
};
|
|
898
|
+
const BuildLists = (data)=>{
|
|
899
|
+
return Object.entries(data?.lists).reduce((acc, [listId, listData])=>{
|
|
900
|
+
const list = BuildList(listId, listData);
|
|
901
|
+
if (list) acc[listId] = list;
|
|
902
|
+
return acc;
|
|
903
|
+
}, {});
|
|
904
|
+
};
|
|
905
|
+
/**
|
|
906
|
+
* Calculates the total number of items within a DataList object.
|
|
907
|
+
*
|
|
908
|
+
* @param {DataList} dataList - The DataList object to count items from.
|
|
909
|
+
* @return {number} The total number of items in the DataList.
|
|
910
|
+
*/ const getNumberOfItemsInDatalist = (dataList)=>{
|
|
911
|
+
if (!dataList?.items) return 0;
|
|
912
|
+
return Object.values(dataList.items).reduce((count, item)=>{
|
|
913
|
+
if (isCohortItem(item)) {
|
|
914
|
+
return count + 1;
|
|
915
|
+
} else {
|
|
916
|
+
return count + Object.values(item?.items ?? {}).reduce((fileCount, x)=>{
|
|
917
|
+
if (isFileItem(x)) {
|
|
918
|
+
return fileCount + 1;
|
|
919
|
+
}
|
|
920
|
+
return fileCount;
|
|
921
|
+
}, 0);
|
|
922
|
+
}
|
|
923
|
+
}, 0);
|
|
924
|
+
};
|
|
925
|
+
const getTimestamp = ()=>{
|
|
926
|
+
return new Date(Date.now()).toLocaleString();
|
|
927
|
+
};
|
|
928
|
+
const flattenDataList = (dataList)=>{
|
|
929
|
+
// convert datalist into user-data-library for for updating.
|
|
930
|
+
const items = Object.entries(dataList.items).reduce((acc, [id, value])=>{
|
|
931
|
+
if (isCohortItem(value)) {
|
|
932
|
+
acc[id] = value;
|
|
933
|
+
} else {
|
|
934
|
+
return {
|
|
935
|
+
...acc,
|
|
936
|
+
...value.items
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
return acc;
|
|
940
|
+
}, {});
|
|
941
|
+
return {
|
|
942
|
+
name: dataList.name,
|
|
943
|
+
items: items
|
|
944
|
+
};
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
const TAGS = 'dataLibrary';
|
|
948
|
+
const dataLibraryTags = gen3Api.enhanceEndpoints({
|
|
949
|
+
addTagTypes: [
|
|
950
|
+
TAGS
|
|
951
|
+
]
|
|
952
|
+
});
|
|
953
|
+
/**
|
|
954
|
+
* RTKQuery hooks for data-library list CRUD operations.
|
|
955
|
+
* @param getLists returns all the list in the user's data library
|
|
956
|
+
* @param getList args: id returns the list for the given id
|
|
957
|
+
* @param addLists args: Record<string, ListItemDefinition> populates the whole datalibrary
|
|
958
|
+
* @param addList args: id ListItemDefinition creates a new list
|
|
959
|
+
* @param updateList args: id ListItemDefinition updates a list
|
|
960
|
+
* @param deleteList args: id deletes the list by id
|
|
961
|
+
*/ const dataLibraryApi = dataLibraryTags.injectEndpoints({
|
|
962
|
+
endpoints: (builder)=>({
|
|
963
|
+
getDataLibraryLists: builder.query({
|
|
964
|
+
query: ()=>`${GEN3_DATA_LIBRARY_API}`,
|
|
965
|
+
transformResponse: (res)=>{
|
|
966
|
+
return {
|
|
967
|
+
lists: BuildLists(res)
|
|
968
|
+
};
|
|
969
|
+
},
|
|
970
|
+
transformErrorResponse (response) {
|
|
971
|
+
if ('originalStatus' in response) return {
|
|
972
|
+
status: response.originalStatus,
|
|
973
|
+
data: response.data
|
|
974
|
+
};
|
|
975
|
+
return response;
|
|
976
|
+
}
|
|
977
|
+
}),
|
|
978
|
+
getDataLibraryList: builder.query({
|
|
979
|
+
query: (id)=>`${GEN3_DATA_LIBRARY_API}/${id}`,
|
|
980
|
+
transformResponse: (res)=>BuildLists(res)?.lists
|
|
981
|
+
}),
|
|
982
|
+
addAllDataLibraryLists: builder.mutation({
|
|
983
|
+
query: (lists)=>({
|
|
984
|
+
url: `${GEN3_DATA_LIBRARY_API}`,
|
|
985
|
+
method: 'POST',
|
|
986
|
+
body: lists
|
|
987
|
+
}),
|
|
988
|
+
invalidatesTags: [
|
|
989
|
+
TAGS
|
|
990
|
+
],
|
|
991
|
+
transformErrorResponse (response) {
|
|
992
|
+
if ('originalStatus' in response) return {
|
|
993
|
+
status: response.originalStatus,
|
|
994
|
+
data: response.data
|
|
995
|
+
};
|
|
996
|
+
return response;
|
|
997
|
+
}
|
|
998
|
+
}),
|
|
999
|
+
addDataLibraryList: builder.mutation({
|
|
1000
|
+
query: (list)=>({
|
|
1001
|
+
url: `${GEN3_DATA_LIBRARY_API}/${nanoid()}`,
|
|
1002
|
+
method: 'POST',
|
|
1003
|
+
body: list ?? {}
|
|
1004
|
+
}),
|
|
1005
|
+
transformErrorResponse (response) {
|
|
1006
|
+
if ('originalStatus' in response) return {
|
|
1007
|
+
status: response.originalStatus,
|
|
1008
|
+
data: response.data
|
|
1009
|
+
};
|
|
1010
|
+
return response;
|
|
1011
|
+
},
|
|
1012
|
+
invalidatesTags: [
|
|
1013
|
+
TAGS
|
|
1014
|
+
]
|
|
1015
|
+
}),
|
|
1016
|
+
updateDataLibraryList: builder.mutation({
|
|
1017
|
+
query: ({ id, list })=>({
|
|
1018
|
+
url: `${GEN3_DATA_LIBRARY_API}/${id}`,
|
|
1019
|
+
method: 'PUT',
|
|
1020
|
+
body: list
|
|
1021
|
+
}),
|
|
1022
|
+
transformErrorResponse (response) {
|
|
1023
|
+
if ('originalStatus' in response) return {
|
|
1024
|
+
status: response.originalStatus,
|
|
1025
|
+
data: response.data
|
|
1026
|
+
};
|
|
1027
|
+
return response;
|
|
1028
|
+
},
|
|
1029
|
+
invalidatesTags: [
|
|
1030
|
+
TAGS
|
|
1031
|
+
]
|
|
1032
|
+
}),
|
|
1033
|
+
deleteDataLibraryList: builder.mutation({
|
|
1034
|
+
query: (id)=>({
|
|
1035
|
+
url: `${GEN3_DATA_LIBRARY_API}/${id}`,
|
|
1036
|
+
method: 'DELETE'
|
|
1037
|
+
}),
|
|
1038
|
+
transformErrorResponse (response) {
|
|
1039
|
+
if ('originalStatus' in response) return {
|
|
1040
|
+
status: response.originalStatus,
|
|
1041
|
+
data: response.data
|
|
1042
|
+
};
|
|
1043
|
+
return response;
|
|
1044
|
+
},
|
|
1045
|
+
invalidatesTags: [
|
|
1046
|
+
TAGS
|
|
1047
|
+
]
|
|
1048
|
+
}),
|
|
1049
|
+
deleteAllDataLibrary: builder.mutation({
|
|
1050
|
+
query: ()=>({
|
|
1051
|
+
url: `${GEN3_DATA_LIBRARY_API}`,
|
|
1052
|
+
method: 'DELETE'
|
|
1053
|
+
}),
|
|
1054
|
+
transformErrorResponse (response) {
|
|
1055
|
+
if ('originalStatus' in response) return {
|
|
1056
|
+
status: response.originalStatus,
|
|
1057
|
+
data: response.data
|
|
1058
|
+
};
|
|
1059
|
+
return response;
|
|
1060
|
+
},
|
|
1061
|
+
invalidatesTags: [
|
|
1062
|
+
TAGS
|
|
1063
|
+
]
|
|
1064
|
+
})
|
|
1065
|
+
})
|
|
1066
|
+
});
|
|
1067
|
+
const { useGetDataLibraryListQuery, useGetDataLibraryListsQuery, useAddDataLibraryListMutation, useAddAllDataLibraryListsMutation, useDeleteDataLibraryListMutation, useDeleteAllDataLibraryMutation, useUpdateDataLibraryListMutation } = dataLibraryApi;
|
|
1068
|
+
|
|
1069
|
+
const DATABASE_NAME = 'Gen3DataLibrary';
|
|
1070
|
+
const STORE_NAME = 'DataLibraryLists';
|
|
1071
|
+
const getDb = async ()=>{
|
|
1072
|
+
return openDB(DATABASE_NAME, 1, {
|
|
1073
|
+
upgrade (db) {
|
|
1074
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
1075
|
+
db.createObjectStore(STORE_NAME, {
|
|
1076
|
+
keyPath: 'id'
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
};
|
|
1082
|
+
/**
|
|
1083
|
+
* Deletes a list from the database.
|
|
1084
|
+
*
|
|
1085
|
+
* @async
|
|
1086
|
+
* @param {string} id - The unique identifier of the list to delete.
|
|
1087
|
+
* @returns {Promise<ReturnStatus>} The status of the deletion operation.
|
|
1088
|
+
* @throws {Error} If the list with the provided id does not exist.
|
|
1089
|
+
*/ const deleteListIndexDB = async (id)=>{
|
|
1090
|
+
try {
|
|
1091
|
+
const db = await getDb();
|
|
1092
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
1093
|
+
const store = tx.objectStore(STORE_NAME);
|
|
1094
|
+
const item = await store.get(id);
|
|
1095
|
+
if (!item) {
|
|
1096
|
+
throw new Error(`List ${id} does not exist`);
|
|
1097
|
+
}
|
|
1098
|
+
store.delete(id);
|
|
1099
|
+
await tx.done;
|
|
1100
|
+
return {
|
|
1101
|
+
status: `${id} deleted`
|
|
1102
|
+
};
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
return {
|
|
1105
|
+
isError: true
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
const deleteAll = async ()=>{
|
|
1110
|
+
try {
|
|
1111
|
+
const db = await getDb();
|
|
1112
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
1113
|
+
tx.objectStore(STORE_NAME).clear();
|
|
1114
|
+
await tx.done;
|
|
1115
|
+
return {
|
|
1116
|
+
status: 'list added'
|
|
1117
|
+
};
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
return {
|
|
1120
|
+
isError: true,
|
|
1121
|
+
status: `unable to clear library`
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
const addListToDataLibraryIndexDB = async (body)=>{
|
|
1126
|
+
const timestamp = getTimestamp();
|
|
1127
|
+
try {
|
|
1128
|
+
const db = await getDb();
|
|
1129
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
1130
|
+
const id = nanoid(); // Create an id for the list
|
|
1131
|
+
tx.objectStore(STORE_NAME).put({
|
|
1132
|
+
id,
|
|
1133
|
+
version: 0,
|
|
1134
|
+
items: body?.items ?? {},
|
|
1135
|
+
creator: '{{subject_id}}',
|
|
1136
|
+
authz: {
|
|
1137
|
+
version: 0,
|
|
1138
|
+
authz: [
|
|
1139
|
+
`/users/{{subject_id}}/user-library/lists/${id}`
|
|
1140
|
+
]
|
|
1141
|
+
},
|
|
1142
|
+
name: body?.name ?? 'New List',
|
|
1143
|
+
created_time: timestamp,
|
|
1144
|
+
updated_time: timestamp
|
|
1145
|
+
});
|
|
1146
|
+
await tx.done;
|
|
1147
|
+
return {
|
|
1148
|
+
status: 'list added'
|
|
1149
|
+
};
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
return {
|
|
1152
|
+
isError: true,
|
|
1153
|
+
status: `unable to add list`
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
const updateListIndexDB = async (id, list)=>{
|
|
1158
|
+
try {
|
|
1159
|
+
const db = await getDb();
|
|
1160
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
1161
|
+
const store = tx.objectStore(STORE_NAME);
|
|
1162
|
+
const listData = await store.get(id);
|
|
1163
|
+
if (!listData) {
|
|
1164
|
+
throw new Error(`List ${id} does not exist`);
|
|
1165
|
+
}
|
|
1166
|
+
const timestamp = getTimestamp();
|
|
1167
|
+
const version = listData.version ? listData.version + 1 : 0;
|
|
1168
|
+
const updated = {
|
|
1169
|
+
...listData,
|
|
1170
|
+
...list,
|
|
1171
|
+
version: version,
|
|
1172
|
+
updated_time: timestamp,
|
|
1173
|
+
created_time: listData.created_time
|
|
1174
|
+
};
|
|
1175
|
+
store.put(updated);
|
|
1176
|
+
await tx.done;
|
|
1177
|
+
return {
|
|
1178
|
+
status: 'success'
|
|
1179
|
+
};
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
let errorMessage = 'An unknown error occurred';
|
|
1182
|
+
if (error instanceof Error) {
|
|
1183
|
+
errorMessage = error.message;
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
isError: true,
|
|
1187
|
+
status: `Unable to update list: ${id}. Error: ${errorMessage}`
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
const addAllListIndexDB = async (data)=>{
|
|
1192
|
+
if (!Object.keys(data).includes('lists') || !isArray$1(data['lists'])) {
|
|
1193
|
+
return {
|
|
1194
|
+
isError: true,
|
|
1195
|
+
status: 'lists not found in request'
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
const timestamp = getTimestamp();
|
|
1199
|
+
const allLists = data['lists'].reduce((acc, x)=>{
|
|
1200
|
+
if (!isJSONObject(x)) return acc;
|
|
1201
|
+
const id = nanoid(10);
|
|
1202
|
+
acc[id] = {
|
|
1203
|
+
...x,
|
|
1204
|
+
version: 0,
|
|
1205
|
+
created_time: timestamp,
|
|
1206
|
+
updated_time: timestamp,
|
|
1207
|
+
creator: '{{subject_id}}',
|
|
1208
|
+
authz: {
|
|
1209
|
+
version: 0,
|
|
1210
|
+
authz: [
|
|
1211
|
+
`/users/{{subject_id}}/user-library/lists/${id}`
|
|
1212
|
+
]
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
return acc;
|
|
1216
|
+
}, {});
|
|
1217
|
+
try {
|
|
1218
|
+
const db = await getDb();
|
|
1219
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
1220
|
+
for (const [id, list] of Object.entries(allLists)){
|
|
1221
|
+
tx.objectStore(STORE_NAME).put({
|
|
1222
|
+
id,
|
|
1223
|
+
...list
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
await tx.done;
|
|
1227
|
+
return {
|
|
1228
|
+
status: 'success'
|
|
1229
|
+
};
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
return {
|
|
1232
|
+
isError: true,
|
|
1233
|
+
status: 'unable to add lists'
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
const getDataLibraryListIndexDB = async (id)=>{
|
|
1238
|
+
try {
|
|
1239
|
+
const db = await getDb();
|
|
1240
|
+
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
1241
|
+
const store = tx.objectStore(STORE_NAME);
|
|
1242
|
+
if (id !== undefined) ; else {
|
|
1243
|
+
const lists = await store.getAll();
|
|
1244
|
+
const listMap = lists.reduce((acc, x)=>{
|
|
1245
|
+
acc[x.id] = {
|
|
1246
|
+
...x,
|
|
1247
|
+
items: x.items
|
|
1248
|
+
};
|
|
1249
|
+
return acc;
|
|
1250
|
+
}, {});
|
|
1251
|
+
const datalists = BuildLists({
|
|
1252
|
+
lists: listMap
|
|
1253
|
+
});
|
|
1254
|
+
return {
|
|
1255
|
+
status: 'success',
|
|
1256
|
+
lists: datalists
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
return {
|
|
1261
|
+
isError: true
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
const useDataLibrary = (useApi)=>{
|
|
1267
|
+
const [localLibrary, setLocalLibrary] = useState({});
|
|
1268
|
+
const { data: apiLibrary, refetch: refetchLibraryFromApi, error: apiError, isError: isAPIListError, isLoading: isAPIListLoading } = useGetDataLibraryListsQuery(undefined, {
|
|
1269
|
+
skip: !useApi
|
|
1270
|
+
});
|
|
1271
|
+
const [addItemToLibraryApi] = useAddDataLibraryListMutation();
|
|
1272
|
+
const [addAllItemsToLibraryApi] = useAddAllDataLibraryListsMutation();
|
|
1273
|
+
const [deleteItemInLibraryApi] = useDeleteDataLibraryListMutation();
|
|
1274
|
+
const [updateItemInLibraryApi, { isLoading: isUpdatingLoading }] = useUpdateDataLibraryListMutation();
|
|
1275
|
+
const [deleteAllApi] = useDeleteAllDataLibraryMutation();
|
|
1276
|
+
let hasError = false;
|
|
1277
|
+
let errorData = null;
|
|
1278
|
+
let isLoading = false;
|
|
1279
|
+
// TODO: Add error message from indexedDB
|
|
1280
|
+
if (useApi && isAPIListError) {
|
|
1281
|
+
hasError = true;
|
|
1282
|
+
errorData = apiError;
|
|
1283
|
+
}
|
|
1284
|
+
if (useApi && (isUpdatingLoading || isAPIListLoading)) {
|
|
1285
|
+
isLoading = true;
|
|
1286
|
+
}
|
|
1287
|
+
const generateUniqueName = (baseName = 'List')=>{
|
|
1288
|
+
let uniqueName = baseName;
|
|
1289
|
+
let counter = 1;
|
|
1290
|
+
const existingNames = dataLibrary ? Object.values(dataLibrary).map((x)=>x.name) : [];
|
|
1291
|
+
while(existingNames.includes(uniqueName)){
|
|
1292
|
+
uniqueName = `${baseName} ${counter}`;
|
|
1293
|
+
counter++;
|
|
1294
|
+
}
|
|
1295
|
+
return uniqueName;
|
|
1296
|
+
};
|
|
1297
|
+
const refetchLocalLists = async ()=>{
|
|
1298
|
+
const { isError, lists } = await getDataLibraryListIndexDB();
|
|
1299
|
+
setLocalLibrary(lists ?? {});
|
|
1300
|
+
hasError = isError === true;
|
|
1301
|
+
};
|
|
1302
|
+
useEffect(()=>{
|
|
1303
|
+
const fetchLibrary = async ()=>{
|
|
1304
|
+
if (!useApi) {
|
|
1305
|
+
const { isError, lists } = await getDataLibraryListIndexDB();
|
|
1306
|
+
if (!isError) {
|
|
1307
|
+
setLocalLibrary(lists ?? {});
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
fetchLibrary();
|
|
1312
|
+
}, [
|
|
1313
|
+
useApi
|
|
1314
|
+
]);
|
|
1315
|
+
const addListToDataLibrary = async (item)=>{
|
|
1316
|
+
const adjustedData = {
|
|
1317
|
+
...item ?? {},
|
|
1318
|
+
name: generateUniqueName(item?.name ?? 'List')
|
|
1319
|
+
};
|
|
1320
|
+
if (useApi) {
|
|
1321
|
+
await addItemToLibraryApi(adjustedData);
|
|
1322
|
+
refetchLibraryFromApi();
|
|
1323
|
+
} else {
|
|
1324
|
+
const { isError } = await addListToDataLibraryIndexDB(adjustedData);
|
|
1325
|
+
await refetchLocalLists();
|
|
1326
|
+
hasError = isError === true;
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
const deleteListFromDataLibrary = async (id)=>{
|
|
1330
|
+
if (useApi) {
|
|
1331
|
+
await deleteItemInLibraryApi(id);
|
|
1332
|
+
refetchLibraryFromApi();
|
|
1333
|
+
} else {
|
|
1334
|
+
const { isError } = await deleteListIndexDB(id);
|
|
1335
|
+
refetchLocalLists();
|
|
1336
|
+
hasError = isError === true;
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
const setAllListsInDataLibrary = async (data)=>{
|
|
1340
|
+
if (useApi) {
|
|
1341
|
+
await addAllItemsToLibraryApi(data);
|
|
1342
|
+
refetchLibraryFromApi();
|
|
1343
|
+
} else {
|
|
1344
|
+
const { isError } = await addAllListIndexDB(data);
|
|
1345
|
+
refetchLocalLists();
|
|
1346
|
+
hasError = isError === true;
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
const updateListInDataLibrary = async (id, data)=>{
|
|
1350
|
+
const flattend = flattenDataList(data);
|
|
1351
|
+
if (useApi) {
|
|
1352
|
+
await updateItemInLibraryApi({
|
|
1353
|
+
id,
|
|
1354
|
+
list: flattend
|
|
1355
|
+
});
|
|
1356
|
+
refetchLibraryFromApi();
|
|
1357
|
+
} else {
|
|
1358
|
+
const { isError } = await updateListIndexDB(id, data);
|
|
1359
|
+
refetchLocalLists();
|
|
1360
|
+
hasError = isError === true;
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
const clearLibrary = async ()=>{
|
|
1364
|
+
if (useApi) {
|
|
1365
|
+
await deleteAllApi();
|
|
1366
|
+
refetchLibraryFromApi();
|
|
1367
|
+
} else {
|
|
1368
|
+
const { isError } = await deleteAll();
|
|
1369
|
+
refetchLocalLists();
|
|
1370
|
+
hasError = isError === true;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
const dataLibrary = useApi ? apiLibrary ? apiLibrary.lists : {} : localLibrary;
|
|
1374
|
+
return {
|
|
1375
|
+
dataLibrary,
|
|
1376
|
+
isError: hasError,
|
|
1377
|
+
isLoading,
|
|
1378
|
+
error: errorData,
|
|
1379
|
+
addListToDataLibrary,
|
|
1380
|
+
deleteListFromDataLibrary,
|
|
1381
|
+
clearLibrary,
|
|
1382
|
+
setAllListsInDataLibrary,
|
|
1383
|
+
updateListInDataLibrary
|
|
1384
|
+
};
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
const initialState$1 = {};
|
|
1388
|
+
const dataLibrarySlice = createSlice({
|
|
1389
|
+
name: 'dataLibrary',
|
|
1390
|
+
initialState: initialState$1,
|
|
1391
|
+
reducers: {
|
|
1392
|
+
setDataLibraryListSelection: (state, action)=>{
|
|
1393
|
+
const { listId, itemIds } = action.payload;
|
|
1394
|
+
state[listId] = itemIds;
|
|
1395
|
+
},
|
|
1396
|
+
clearDataLibrarySelection: ()=>{
|
|
1397
|
+
return initialState$1;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
const { setDataLibraryListSelection, clearDataLibrarySelection } = dataLibrarySlice.actions;
|
|
1402
|
+
const dataLibrarySelectionReducer = dataLibrarySlice.reducer;
|
|
1403
|
+
// Selector
|
|
1404
|
+
const selectDataLibrary = (state)=>state.dataLibrarySelection;
|
|
1405
|
+
// Memoized selector for getting data library items for a specific root object
|
|
1406
|
+
createSelector([
|
|
1407
|
+
selectDataLibrary,
|
|
1408
|
+
(_, rootObjectId)=>rootObjectId
|
|
1409
|
+
], (dataLibrary, rootObjectId)=>dataLibrary[rootObjectId] || []);
|
|
1410
|
+
|
|
769
1411
|
var WorkspaceStatus;
|
|
770
1412
|
(function(WorkspaceStatus) {
|
|
771
1413
|
WorkspaceStatus["Launching"] = "Launching";
|
|
@@ -889,1127 +1531,1268 @@ const guppyAPISliceMiddleware = guppyApi.middleware;
|
|
|
889
1531
|
const guppyApiSliceReducerPath = guppyApi.reducerPath;
|
|
890
1532
|
const guppyApiReducer = guppyApi.reducer;
|
|
891
1533
|
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
user: userReducer,
|
|
895
|
-
gen3Apps: gen3AppReducer,
|
|
896
|
-
drsHostnames: drsHostnamesReducer,
|
|
897
|
-
modals: modalReducer,
|
|
898
|
-
cohorts: cohortReducer,
|
|
899
|
-
activeWorkspace: activeWorkspaceReducer,
|
|
900
|
-
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
901
|
-
[userAuthApiReducerPath]: userAuthApiReducer
|
|
902
|
-
});
|
|
903
|
-
|
|
904
|
-
const coreStore = configureStore({
|
|
905
|
-
reducer: rootReducer,
|
|
906
|
-
middleware: (getDefaultMiddleware)=>getDefaultMiddleware().concat(gen3ServicesReducerMiddleware, guppyAPISliceMiddleware, userAuthApiMiddleware)
|
|
907
|
-
});
|
|
908
|
-
setupListeners(coreStore.dispatch);
|
|
909
|
-
|
|
910
|
-
const CoreProvider = ({ children })=>{
|
|
911
|
-
return /*#__PURE__*/ React__default.createElement(Provider, {
|
|
912
|
-
store: coreStore
|
|
913
|
-
}, children);
|
|
1534
|
+
const isOperationWithField = (operation)=>{
|
|
1535
|
+
return operation?.field !== undefined;
|
|
914
1536
|
};
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1537
|
+
const extractFilterValue = (op)=>{
|
|
1538
|
+
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1539
|
+
return handleOperation(valueExtractorHandler, op);
|
|
1540
|
+
};
|
|
1541
|
+
const extractEnumFilterValue = (op)=>{
|
|
1542
|
+
const enumValueExtractorHandler = new EnumValueExtractorHandler();
|
|
1543
|
+
const results = handleOperation(enumValueExtractorHandler, op);
|
|
1544
|
+
return results ?? [];
|
|
1545
|
+
};
|
|
1546
|
+
const assertNever = (x)=>{
|
|
1547
|
+
throw Error(`Exhaustive comparison did not handle: ${x}`);
|
|
1548
|
+
};
|
|
1549
|
+
const handleOperation = (handler, op)=>{
|
|
1550
|
+
switch(op.operator){
|
|
1551
|
+
case '=':
|
|
1552
|
+
return handler.handleEquals(op);
|
|
1553
|
+
case '!=':
|
|
1554
|
+
return handler.handleNotEquals(op);
|
|
1555
|
+
case '<':
|
|
1556
|
+
return handler.handleLessThan(op);
|
|
1557
|
+
case '<=':
|
|
1558
|
+
return handler.handleLessThanOrEquals(op);
|
|
1559
|
+
case '>':
|
|
1560
|
+
return handler.handleGreaterThan(op);
|
|
1561
|
+
case '>=':
|
|
1562
|
+
return handler.handleGreaterThanOrEquals(op);
|
|
1563
|
+
case 'and':
|
|
1564
|
+
return handler.handleIntersection(op);
|
|
1565
|
+
case 'or':
|
|
1566
|
+
return handler.handleUnion(op);
|
|
1567
|
+
case 'nested':
|
|
1568
|
+
return handler.handleNestedFilter(op);
|
|
1569
|
+
case 'in':
|
|
1570
|
+
return handler.handleIncludes(op);
|
|
1571
|
+
case 'excludeifany':
|
|
1572
|
+
return handler.handleExcludeIfAny(op);
|
|
1573
|
+
case 'excludes':
|
|
1574
|
+
return handler.handleExcludes(op);
|
|
1575
|
+
default:
|
|
1576
|
+
return assertNever(op);
|
|
1577
|
+
}
|
|
937
1578
|
};
|
|
938
1579
|
/**
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
* @param getIndexAggMDS - queries the Aggregate Metadata service and returns all common passed in indexKeys
|
|
948
|
-
* @param getTags - Probably refering to Aggregate metadata service summary statistics query
|
|
949
|
-
* @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/metadata-service/master/docs/openapi.yaml#/Aggregate/get_aggregate_tags_aggregate_tags_get
|
|
950
|
-
* @param getData - Looks like a duplicate of getMDS handler. unused in ./frontend package
|
|
951
|
-
* @param getCrosswalkData - Maps ids from one source to another
|
|
952
|
-
* @returns: A guppy download API for fetching bulk metadata
|
|
953
|
-
*/ const metadataApi = gen3Api.injectEndpoints({
|
|
954
|
-
endpoints: (builder)=>({
|
|
955
|
-
getAggMDS: builder.query({
|
|
956
|
-
query: ({ offset, pageSize })=>{
|
|
957
|
-
return `${GEN3_MDS_API}/aggregate/metadata?flatten=true&pagination=true&offset=${offset}&limit=${pageSize}`;
|
|
958
|
-
},
|
|
959
|
-
transformResponse: (response, _meta, params)=>{
|
|
960
|
-
return {
|
|
961
|
-
data: response.results.map((x)=>{
|
|
962
|
-
const objValues = Object.values(x);
|
|
963
|
-
const firstValue = objValues ? objValues.at(0) : undefined;
|
|
964
|
-
return firstValue ? firstValue[params.studyField] : undefined;
|
|
965
|
-
}),
|
|
966
|
-
hits: response.pagination.hits
|
|
967
|
-
};
|
|
1580
|
+
* Return true if a FilterSet's root value is an empty object
|
|
1581
|
+
* @param fs - FilterSet to test
|
|
1582
|
+
*/ const isFilterEmpty = (fs)=>isEqual({}, fs);
|
|
1583
|
+
class ToGqlHandler {
|
|
1584
|
+
constructor(){
|
|
1585
|
+
this.handleEquals = (op)=>({
|
|
1586
|
+
'=': {
|
|
1587
|
+
[op.field]: op.operand
|
|
968
1588
|
}
|
|
969
|
-
})
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
},
|
|
974
|
-
transformResponse: (response, _meta, params)=>{
|
|
975
|
-
const dataFromIndexes = params.indexKeys.reduce((acc, key)=>{
|
|
976
|
-
if (response[key]) {
|
|
977
|
-
acc.push(...response[key]);
|
|
978
|
-
}
|
|
979
|
-
return acc;
|
|
980
|
-
}, []);
|
|
981
|
-
return {
|
|
982
|
-
data: dataFromIndexes.map((x)=>{
|
|
983
|
-
const objValues = Object.values(x);
|
|
984
|
-
const objIds = Object.keys(x);
|
|
985
|
-
let firstValue = objValues ? objValues.at(0) : undefined;
|
|
986
|
-
if (params?.filterEmpty) {
|
|
987
|
-
// remove any data that has < limit if defined
|
|
988
|
-
if (firstValue && !HasEnoughData(firstValue[params.studyField], params.filterEmpty.keys, params.filterEmpty.limit)) firstValue = undefined;
|
|
989
|
-
}
|
|
990
|
-
return firstValue ? {
|
|
991
|
-
gen3MDSGUID: objIds.at(0),
|
|
992
|
-
...firstValue[params.studyField]
|
|
993
|
-
} : undefined;
|
|
994
|
-
}).filter((x)=>x !== undefined),
|
|
995
|
-
hits: dataFromIndexes.length
|
|
996
|
-
};
|
|
1589
|
+
});
|
|
1590
|
+
this.handleNotEquals = (op)=>({
|
|
1591
|
+
'!=': {
|
|
1592
|
+
[op.field]: op.operand
|
|
997
1593
|
}
|
|
998
|
-
})
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
},
|
|
1003
|
-
transformResponse: (response, _meta)=>{
|
|
1004
|
-
return {
|
|
1005
|
-
data: Object.keys(response).map((guid)=>response[guid]),
|
|
1006
|
-
hits: Object.keys(response).length
|
|
1007
|
-
};
|
|
1594
|
+
});
|
|
1595
|
+
this.handleLessThan = (op)=>({
|
|
1596
|
+
'<': {
|
|
1597
|
+
[op.field]: op.operand
|
|
1008
1598
|
}
|
|
1009
|
-
})
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
getData: builder.query({
|
|
1014
|
-
query: (params)=>({
|
|
1015
|
-
url: `metadata?${params}`
|
|
1016
|
-
})
|
|
1017
|
-
}),
|
|
1018
|
-
// TODO: Move this to own slice
|
|
1019
|
-
getCrosswalkData: builder.query({
|
|
1020
|
-
queryFn: async (arg, _queryApi, _extraOptions, fetchWithBQ)=>{
|
|
1021
|
-
const queryMultiple = async ()=>{
|
|
1022
|
-
let result = [];
|
|
1023
|
-
const queue = Queue({
|
|
1024
|
-
concurrency: 15
|
|
1025
|
-
});
|
|
1026
|
-
for (const id of arg.ids){
|
|
1027
|
-
queue.push(async (callback)=>{
|
|
1028
|
-
const response = await fetchWithBQ({
|
|
1029
|
-
url: `${GEN3_CROSSWALK_API}/metadata/${id}`
|
|
1030
|
-
});
|
|
1031
|
-
if (response.error) {
|
|
1032
|
-
return {
|
|
1033
|
-
error: response.error
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
const toData = arg.toPaths.reduce((acc, path)=>{
|
|
1037
|
-
acc[path.id] = JSONPath({
|
|
1038
|
-
json: response.data,
|
|
1039
|
-
path: `$.[${path.dataPath}]`,
|
|
1040
|
-
resultType: 'value'
|
|
1041
|
-
})?.[0] ?? 'n/a';
|
|
1042
|
-
return acc;
|
|
1043
|
-
}, {});
|
|
1044
|
-
result = [
|
|
1045
|
-
...result,
|
|
1046
|
-
{
|
|
1047
|
-
from: id,
|
|
1048
|
-
to: toData
|
|
1049
|
-
}
|
|
1050
|
-
];
|
|
1051
|
-
if (callback) callback();
|
|
1052
|
-
return result;
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
return new Promise((resolve, reject)=>{
|
|
1056
|
-
queue.start((err)=>{
|
|
1057
|
-
if (err) {
|
|
1058
|
-
reject(err);
|
|
1059
|
-
} else {
|
|
1060
|
-
resolve(result);
|
|
1061
|
-
}
|
|
1062
|
-
});
|
|
1063
|
-
});
|
|
1064
|
-
};
|
|
1065
|
-
const result = await queryMultiple();
|
|
1066
|
-
return {
|
|
1067
|
-
data: result
|
|
1068
|
-
};
|
|
1599
|
+
});
|
|
1600
|
+
this.handleLessThanOrEquals = (op)=>({
|
|
1601
|
+
'<=': {
|
|
1602
|
+
[op.field]: op.operand
|
|
1069
1603
|
}
|
|
1070
|
-
})
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
// using a random uuid v4 as the namespace
|
|
1076
|
-
const GEN3_APP_NAMESPACE = '7bfaa818-c69c-457e-8d87-413cf60c25f0';
|
|
1077
|
-
|
|
1078
|
-
const getGen3AppId = (name, version)=>{
|
|
1079
|
-
const nameVersion = `${name}::${version}`;
|
|
1080
|
-
return v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
1081
|
-
};
|
|
1082
|
-
/**
|
|
1083
|
-
* Creates a Gen3App that is dynamically loaded
|
|
1084
|
-
*/ const createGen3App = ({ App, name, version, requiredEntityTypes })=>{
|
|
1085
|
-
// create a stable id for this app
|
|
1086
|
-
const nameVersion = `${name}::${version}`;
|
|
1087
|
-
const id = v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
1088
|
-
// need to create store and provider.
|
|
1089
|
-
// return a component representing this app
|
|
1090
|
-
// if component gets added to a list, then the list can be iterated in index.js and each provider component can be added
|
|
1091
|
-
// a route can be setup for the app
|
|
1092
|
-
// need to register its name, category, path, data requirements
|
|
1093
|
-
// this will be used to build page3
|
|
1094
|
-
// click app link
|
|
1095
|
-
// const store = configureStore({
|
|
1096
|
-
// // TODO allow user to pass in a reducer in CreateGen3AppOptions?
|
|
1097
|
-
// reducer: (state) => state,
|
|
1098
|
-
// devTools: {
|
|
1099
|
-
// name: `${nameVersion}::${id}`,
|
|
1100
|
-
// },
|
|
1101
|
-
// });
|
|
1102
|
-
const Gen3AppWrapper = (props)=>{
|
|
1103
|
-
useEffect(()=>{
|
|
1104
|
-
document.title = `GEN3 - ${name}`;
|
|
1105
|
-
});
|
|
1106
|
-
return /*#__PURE__*/ React__default.createElement(App, props);
|
|
1107
|
-
};
|
|
1108
|
-
// add the app to the store
|
|
1109
|
-
coreStore.dispatch(addGen3AppMetadata({
|
|
1110
|
-
id,
|
|
1111
|
-
name,
|
|
1112
|
-
version,
|
|
1113
|
-
requiredEntityTypes
|
|
1114
|
-
}));
|
|
1115
|
-
registerGen3App(name, Gen3AppWrapper);
|
|
1116
|
-
return Gen3AppWrapper;
|
|
1117
|
-
};
|
|
1118
|
-
// ----------------------------------------------------------------------------------------
|
|
1119
|
-
// Apps with Local Storage
|
|
1120
|
-
//
|
|
1121
|
-
const createAppStore = (options)=>{
|
|
1122
|
-
const { name, version, reducers, middleware } = options;
|
|
1123
|
-
const nameVersion = `${name}::${version}`;
|
|
1124
|
-
const id = v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
1125
|
-
const store = configureStore({
|
|
1126
|
-
reducer: reducers,
|
|
1127
|
-
devTools: {
|
|
1128
|
-
name: `${nameVersion}::${id}`
|
|
1129
|
-
},
|
|
1130
|
-
middleware: (getDefaultMiddleware)=>middleware ? getDefaultMiddleware({
|
|
1131
|
-
serializableCheck: {
|
|
1132
|
-
ignoredActions: [
|
|
1133
|
-
FLUSH,
|
|
1134
|
-
REHYDRATE,
|
|
1135
|
-
PAUSE,
|
|
1136
|
-
PERSIST,
|
|
1137
|
-
PURGE,
|
|
1138
|
-
REGISTER
|
|
1139
|
-
]
|
|
1604
|
+
});
|
|
1605
|
+
this.handleGreaterThan = (op)=>({
|
|
1606
|
+
'>': {
|
|
1607
|
+
[op.field]: op.operand
|
|
1140
1608
|
}
|
|
1141
|
-
})
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
REHYDRATE,
|
|
1146
|
-
PAUSE,
|
|
1147
|
-
PERSIST,
|
|
1148
|
-
PURGE,
|
|
1149
|
-
REGISTER
|
|
1150
|
-
]
|
|
1609
|
+
});
|
|
1610
|
+
this.handleGreaterThanOrEquals = (op)=>({
|
|
1611
|
+
'>=': {
|
|
1612
|
+
[op.field]: op.operand
|
|
1151
1613
|
}
|
|
1152
|
-
})
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
const useAppDispatch = createDispatchHook(context);
|
|
1157
|
-
const useAppStore = createStoreHook(context);
|
|
1158
|
-
return {
|
|
1159
|
-
id: id,
|
|
1160
|
-
AppStore: store,
|
|
1161
|
-
AppContext: context,
|
|
1162
|
-
useAppSelector: useAppSelector,
|
|
1163
|
-
useAppDispatch: useAppDispatch,
|
|
1164
|
-
useAppStore: useAppStore
|
|
1165
|
-
};
|
|
1166
|
-
};
|
|
1167
|
-
const createGen3AppWithOwnStore = (options)=>{
|
|
1168
|
-
const { App, id, name, version, requiredEntityTypes, store, context } = options;
|
|
1169
|
-
// need to create store and provider.
|
|
1170
|
-
// return a component representing this app
|
|
1171
|
-
// if component gets added to a list, then the list can be iterated in index.js and each provider component can be added
|
|
1172
|
-
// a route can be setup for the app
|
|
1173
|
-
// need to register its name, category, path, data requirements
|
|
1174
|
-
// this will be used to build page3
|
|
1175
|
-
// click app link
|
|
1176
|
-
const Gen3AppWrapper = (props)=>{
|
|
1177
|
-
useEffect(()=>{
|
|
1178
|
-
document.title = `GEN3 - ${name}`;
|
|
1179
|
-
});
|
|
1180
|
-
return /*#__PURE__*/ React__default.createElement(Provider, {
|
|
1181
|
-
store: store,
|
|
1182
|
-
context: context
|
|
1183
|
-
}, /*#__PURE__*/ React__default.createElement(CookiesProvider, null, /*#__PURE__*/ React__default.createElement(App, props)));
|
|
1184
|
-
};
|
|
1185
|
-
// add the app to the store
|
|
1186
|
-
coreStore.dispatch(addGen3AppMetadata({
|
|
1187
|
-
id,
|
|
1188
|
-
name,
|
|
1189
|
-
version,
|
|
1190
|
-
requiredEntityTypes
|
|
1191
|
-
}));
|
|
1192
|
-
registerGen3App(name, Gen3AppWrapper);
|
|
1193
|
-
return Gen3AppWrapper;
|
|
1194
|
-
};
|
|
1195
|
-
|
|
1196
|
-
const createAppApiForRTKQ = (reducerPath, baseQuery)=>{
|
|
1197
|
-
const appContext = React.createContext(null);
|
|
1198
|
-
const useAppSelector = useSelector.withTypes();
|
|
1199
|
-
const useAppDispatch = createDispatchHook(appContext);
|
|
1200
|
-
const useAppStore = createStoreHook(appContext);
|
|
1201
|
-
const appCreateApi = buildCreateApi(coreModule(), reactHooksModule({
|
|
1202
|
-
hooks: {
|
|
1203
|
-
useDispatch: useAppDispatch,
|
|
1204
|
-
useSelector: useAppSelector,
|
|
1205
|
-
useStore: useAppStore
|
|
1206
|
-
}
|
|
1207
|
-
}));
|
|
1208
|
-
const appRTKQApi = appCreateApi({
|
|
1209
|
-
reducerPath: reducerPath,
|
|
1210
|
-
baseQuery: baseQuery ?? fetchBaseQuery({
|
|
1211
|
-
baseUrl: `${GEN3_API}`,
|
|
1212
|
-
prepareHeaders: (headers)=>{
|
|
1213
|
-
headers.set('Content-Type', 'application/json');
|
|
1214
|
-
let accessToken = undefined;
|
|
1215
|
-
if (process.env.NODE_ENV === 'development') {
|
|
1216
|
-
// NOTE: This cookie can only be accessed from the client side
|
|
1217
|
-
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
1218
|
-
accessToken = getCookie('credentials_token');
|
|
1614
|
+
});
|
|
1615
|
+
this.handleIncludes = (op)=>({
|
|
1616
|
+
in: {
|
|
1617
|
+
[op.field]: op.operands
|
|
1219
1618
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
endpoints: ()=>({})
|
|
1225
|
-
});
|
|
1226
|
-
const appMiddleware = appRTKQApi.middleware;
|
|
1227
|
-
const appStore = configureStore({
|
|
1228
|
-
reducer: {
|
|
1229
|
-
[appRTKQApi.reducerPath]: appRTKQApi.reducer
|
|
1230
|
-
},
|
|
1231
|
-
middleware: (getDefaultMiddleware)=>getDefaultMiddleware({
|
|
1232
|
-
serializableCheck: {
|
|
1233
|
-
ignoredActions: [
|
|
1234
|
-
FLUSH,
|
|
1235
|
-
REHYDRATE,
|
|
1236
|
-
PAUSE,
|
|
1237
|
-
PERSIST,
|
|
1238
|
-
PURGE,
|
|
1239
|
-
REGISTER
|
|
1240
|
-
]
|
|
1619
|
+
});
|
|
1620
|
+
this.handleExcludes = (op)=>({
|
|
1621
|
+
exclude: {
|
|
1622
|
+
[op.field]: op.operands
|
|
1241
1623
|
}
|
|
1242
|
-
})
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1624
|
+
});
|
|
1625
|
+
this.handleExcludeIfAny = (op)=>({
|
|
1626
|
+
excludeifany: {
|
|
1627
|
+
[op.field]: op.operands
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
this.handleIntersection = (op)=>({
|
|
1631
|
+
and: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
1632
|
+
});
|
|
1633
|
+
this.handleUnion = (op)=>({
|
|
1634
|
+
or: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
1635
|
+
});
|
|
1636
|
+
this.handleNestedFilter = (op)=>{
|
|
1637
|
+
const child = convertFilterToGqlFilter(op.operand);
|
|
1638
|
+
return {
|
|
1639
|
+
nested: {
|
|
1640
|
+
path: op.path,
|
|
1641
|
+
...child
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
const convertFilterToGqlFilter = (filter)=>{
|
|
1648
|
+
const handler = new ToGqlHandler();
|
|
1649
|
+
return handleOperation(handler, filter);
|
|
1650
|
+
};
|
|
1651
|
+
const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
|
|
1652
|
+
const fsKeys = Object.keys(fs.root);
|
|
1653
|
+
// if no keys return undefined
|
|
1654
|
+
if (fsKeys.length === 0) return {
|
|
1655
|
+
and: []
|
|
1656
|
+
};
|
|
1657
|
+
return toplevelOp === 'and' ? {
|
|
1658
|
+
and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1659
|
+
} : {
|
|
1660
|
+
or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1252
1661
|
};
|
|
1253
1662
|
};
|
|
1254
|
-
|
|
1255
|
-
const graphQLWithTags = gen3Api.enhanceEndpoints({
|
|
1256
|
-
addTagTypes: [
|
|
1257
|
-
'graphQL'
|
|
1258
|
-
]
|
|
1259
|
-
});
|
|
1260
1663
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1664
|
+
* Extract the operand values, if operands themselves have values, otherwise undefined.
|
|
1665
|
+
*/ class ValueExtractorHandler {
|
|
1666
|
+
constructor(){
|
|
1667
|
+
this.handleEquals = (op)=>op.operand;
|
|
1668
|
+
this.handleNotEquals = (op)=>op.operand;
|
|
1669
|
+
this.handleIncludes = (op)=>op.operands;
|
|
1670
|
+
this.handleExcludes = (op)=>op.operands;
|
|
1671
|
+
this.handleExcludeIfAny = (op)=>op.operands;
|
|
1672
|
+
this.handleGreaterThanOrEquals = (op)=>op.operand;
|
|
1673
|
+
this.handleGreaterThan = (op)=>op.operand;
|
|
1674
|
+
this.handleLessThan = (op)=>op.operand;
|
|
1675
|
+
this.handleLessThanOrEquals = (op)=>op.operand;
|
|
1676
|
+
this.handleIntersection = (_arg)=>undefined;
|
|
1677
|
+
this.handleUnion = (_)=>undefined;
|
|
1678
|
+
this.handleNestedFilter = (_)=>undefined;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Extract the operand values, if operands themselves have values, otherwise undefined.
|
|
1683
|
+
*/ class EnumValueExtractorHandler {
|
|
1684
|
+
constructor(){
|
|
1685
|
+
this.handleEquals = (_)=>undefined;
|
|
1686
|
+
this.handleNotEquals = (_)=>undefined;
|
|
1687
|
+
this.handleIncludes = (op)=>op.operands;
|
|
1688
|
+
this.handleExcludes = (op)=>op.operands;
|
|
1689
|
+
this.handleExcludeIfAny = (op)=>op.operands;
|
|
1690
|
+
this.handleGreaterThanOrEquals = (_)=>undefined;
|
|
1691
|
+
this.handleGreaterThan = (_)=>undefined;
|
|
1692
|
+
this.handleLessThan = (_)=>undefined;
|
|
1693
|
+
this.handleLessThanOrEquals = (_)=>undefined;
|
|
1694
|
+
this.handleIntersection = (_)=>undefined;
|
|
1695
|
+
this.handleUnion = (_)=>undefined;
|
|
1696
|
+
this.handleNestedFilter = (op)=>{
|
|
1697
|
+
return extractEnumFilterValue(op.operand);
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1278
1701
|
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1702
|
+
const isFilterSet = (input)=>{
|
|
1703
|
+
if (typeof input !== 'object' || input === null) {
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
const { root, mode } = input;
|
|
1707
|
+
if (typeof root !== 'object' || root === null) {
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
if (![
|
|
1711
|
+
'and',
|
|
1712
|
+
'or'
|
|
1713
|
+
].includes(mode)) {
|
|
1714
|
+
return false;
|
|
1715
|
+
}
|
|
1716
|
+
return true;
|
|
1281
1717
|
};
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1718
|
+
|
|
1719
|
+
const FieldNameOverrides = {};
|
|
1720
|
+
const COMMON_PREPOSITIONS = [
|
|
1721
|
+
'a',
|
|
1722
|
+
'an',
|
|
1723
|
+
'and',
|
|
1724
|
+
'at',
|
|
1725
|
+
'but',
|
|
1726
|
+
'by',
|
|
1727
|
+
'for',
|
|
1728
|
+
'in',
|
|
1729
|
+
'is',
|
|
1730
|
+
'nor',
|
|
1731
|
+
'of',
|
|
1732
|
+
'on',
|
|
1733
|
+
'or',
|
|
1734
|
+
'out',
|
|
1735
|
+
'so',
|
|
1736
|
+
'the',
|
|
1737
|
+
'to',
|
|
1738
|
+
'up',
|
|
1739
|
+
'yet'
|
|
1740
|
+
];
|
|
1741
|
+
const capitalize = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
|
|
1742
|
+
const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
1743
|
+
if (trim) {
|
|
1744
|
+
const source = fieldName.slice(fieldName.indexOf('.') + 1);
|
|
1745
|
+
return fieldNameToTitle(source ? source : fieldName, 0);
|
|
1746
|
+
}
|
|
1747
|
+
return fieldNameToTitle(fieldName);
|
|
1748
|
+
};
|
|
1749
|
+
/**
|
|
1750
|
+
* Converts a filter name to a title,
|
|
1751
|
+
* For example files.input.experimental_strategy will get converted to Experimental Strategy
|
|
1752
|
+
* if sections == 2 then the output would be Input Experimental Strategy
|
|
1753
|
+
* @param fieldName input filter expected to be: string.firstpart_secondpart
|
|
1754
|
+
* @param sections number of "sections" string.string.string to got back from the end of the field
|
|
1755
|
+
*/ const fieldNameToTitle = (fieldName, sections = 1)=>{
|
|
1756
|
+
if (fieldName in FieldNameOverrides) {
|
|
1757
|
+
return FieldNameOverrides[fieldName];
|
|
1758
|
+
}
|
|
1759
|
+
if (fieldName === undefined) return 'No Title';
|
|
1760
|
+
return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize(word)).join(' ');
|
|
1761
|
+
};
|
|
1762
|
+
/**
|
|
1763
|
+
* Extracts the index name from the field name
|
|
1764
|
+
* @param fieldName
|
|
1765
|
+
*/ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
|
|
1766
|
+
/**
|
|
1767
|
+
* prepend the index name to the field name
|
|
1768
|
+
*/ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
|
|
1769
|
+
/**
|
|
1770
|
+
* extract the field name from the index.field name
|
|
1771
|
+
*/ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
|
|
1772
|
+
/**
|
|
1773
|
+
* extract the field name and the index from the index.field name returning as a tuple
|
|
1774
|
+
*/ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
|
|
1775
|
+
const [index, ...rest] = fieldName.split('.');
|
|
1776
|
+
return [
|
|
1777
|
+
index,
|
|
1778
|
+
rest.join('.')
|
|
1779
|
+
];
|
|
1780
|
+
};
|
|
1781
|
+
|
|
1782
|
+
const statusEndpoint = '/_status';
|
|
1783
|
+
const processHistogramResponse = (data)=>{
|
|
1784
|
+
const valueData = JSONPath({
|
|
1785
|
+
json: data,
|
|
1786
|
+
path: '$..histogram',
|
|
1787
|
+
resultType: 'value'
|
|
1788
|
+
});
|
|
1789
|
+
const pointerData = JSONPath({
|
|
1790
|
+
json: data,
|
|
1791
|
+
path: '$..histogram',
|
|
1792
|
+
resultType: 'pointer'
|
|
1793
|
+
});
|
|
1794
|
+
const results = pointerData.reduce((acc, element, idx)=>{
|
|
1795
|
+
const key = element.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1796
|
+
return {
|
|
1797
|
+
...acc,
|
|
1798
|
+
[key]: valueData[idx]
|
|
1799
|
+
};
|
|
1800
|
+
}, {});
|
|
1801
|
+
return results;
|
|
1802
|
+
};
|
|
1803
|
+
const fetchJson = async (url)=>{
|
|
1804
|
+
const res = await fetch(url, {
|
|
1805
|
+
method: 'GET',
|
|
1806
|
+
headers: {
|
|
1807
|
+
'Content-type': 'application/json'
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
if (!res.ok) throw new Error('An error occurred while fetching the data.');
|
|
1811
|
+
return await res.json();
|
|
1812
|
+
};
|
|
1813
|
+
const useGetStatus = ()=>{
|
|
1814
|
+
const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
|
|
1815
|
+
return useSWR('explorerStatus', fetcher);
|
|
1816
|
+
};
|
|
1817
|
+
/**
|
|
1818
|
+
* The main endpoint used in templating Exploration page queries.
|
|
1819
|
+
* Includes table, filter and aggregation query types and leverages guppyApi defined in ./gupplApi.ts
|
|
1820
|
+
* Query templates support filters where applicable
|
|
1821
|
+
*
|
|
1822
|
+
* @param endpoints - Defines endpoints used in Exploration page:
|
|
1823
|
+
* @param getAllFieldsForType - A mapping query that returns all property key names vertex types specified.
|
|
1824
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#mapping-query
|
|
1825
|
+
* @param getAccessibleData - An aggregation histogram counts query that filters based on access type
|
|
1826
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#accessibility-argument-for-regular-tier-access-level
|
|
1827
|
+
* @param getRawDataAndTotalCounts - Queries both _totalCount for selected vertex types and
|
|
1828
|
+
* tabular results containing the raw data in the rows of selected vertex types
|
|
1829
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#1-total-count-aggregation
|
|
1830
|
+
* @param getAggs - An aggregated histogram counts query which outputs vertex property frequencies
|
|
1831
|
+
* @param getSubAggs - TODO: not sure what this one does. Looks like nested aggregation
|
|
1832
|
+
* @param getCounts - Returns total counts of a vertex type
|
|
1833
|
+
* @returns: A guppy API endpoint for templating queriable data displayed on the exploration page
|
|
1834
|
+
*/ const explorerApi = guppyApi.injectEndpoints({
|
|
1835
|
+
endpoints: (builder)=>({
|
|
1836
|
+
getAllFieldsForType: builder.query({
|
|
1837
|
+
query: (type)=>({
|
|
1838
|
+
query: `{ _mapping ${type} } }`
|
|
1839
|
+
}),
|
|
1840
|
+
transformResponse: (response, _meta, params)=>{
|
|
1841
|
+
return response[params.type];
|
|
1842
|
+
}
|
|
1843
|
+
}),
|
|
1844
|
+
getAccessibleData: builder.query({
|
|
1845
|
+
query: ({ type, fields, accessType })=>{
|
|
1846
|
+
const fieldParts = fields.map((field)=>`${field} { histogram { key count } }`);
|
|
1847
|
+
return {
|
|
1848
|
+
query: `_aggregation {
|
|
1849
|
+
${type} (accessibility: ${accessType}) {
|
|
1850
|
+
${fieldParts.join(',')}
|
|
1851
|
+
}
|
|
1852
|
+
}`
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
}),
|
|
1856
|
+
getRawDataAndTotalCounts: builder.query({
|
|
1857
|
+
query: ({ type, fields, filters, sort, offset = 0, size = 20, accessibility = Accessibility.ALL, format = undefined })=>{
|
|
1858
|
+
const gqlFilter = convertFilterSetToGqlFilter(filters);
|
|
1859
|
+
const params = [
|
|
1860
|
+
...sort ? [
|
|
1861
|
+
'$sort: JSON'
|
|
1862
|
+
] : [],
|
|
1863
|
+
...gqlFilter ? [
|
|
1864
|
+
'$filter: JSON'
|
|
1865
|
+
] : [],
|
|
1866
|
+
...format ? [
|
|
1867
|
+
'$format: Format'
|
|
1868
|
+
] : []
|
|
1869
|
+
].join(',');
|
|
1870
|
+
const queryLine = `query getRawDataAndTotalCounts (${params}) {`;
|
|
1871
|
+
const dataParams = [
|
|
1872
|
+
...format ? [
|
|
1873
|
+
'format: $format'
|
|
1874
|
+
] : [],
|
|
1875
|
+
...sort ? [
|
|
1876
|
+
'sort: $sort'
|
|
1877
|
+
] : [],
|
|
1878
|
+
...gqlFilter ? [
|
|
1879
|
+
'filter: $filter'
|
|
1880
|
+
] : []
|
|
1881
|
+
].join(',');
|
|
1882
|
+
const dataTypeLine = `${type} (accessibility: ${accessibility}, offset: ${offset}, first: ${size},
|
|
1883
|
+
${dataParams}) {`;
|
|
1884
|
+
const typeAggsLine = `${type} (${gqlFilter && 'filter: $filter,'} accessibility: ${accessibility}) {`;
|
|
1885
|
+
const processedFields = fields.map((field)=>rawDataQueryStrForEachField(field));
|
|
1886
|
+
const query = `${queryLine}
|
|
1887
|
+
${dataTypeLine}
|
|
1888
|
+
${processedFields.join(' ')}
|
|
1889
|
+
}
|
|
1890
|
+
_aggregation {
|
|
1891
|
+
${typeAggsLine}
|
|
1892
|
+
_totalCount
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}`;
|
|
1896
|
+
const variables = {
|
|
1897
|
+
...sort && {
|
|
1898
|
+
sort
|
|
1899
|
+
},
|
|
1900
|
+
...gqlFilter && {
|
|
1901
|
+
filter: gqlFilter
|
|
1902
|
+
},
|
|
1903
|
+
...format && {
|
|
1904
|
+
format
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
return {
|
|
1908
|
+
query,
|
|
1909
|
+
variables
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
}),
|
|
1913
|
+
getAggs: builder.query({
|
|
1914
|
+
query: ({ type, fields, filters, accessibility = Accessibility.ALL })=>{
|
|
1915
|
+
const queryStart = isFilterEmpty(filters) ? `
|
|
1916
|
+
query getAggs {
|
|
1917
|
+
_aggregation {
|
|
1918
|
+
${type} (accessibility: ${accessibility}) {` : `query getAggs ($filter: JSON) {
|
|
1919
|
+
_aggregation {
|
|
1920
|
+
${type} (filter: $filter, filterSelf: false, accessibility: ${accessibility}) {`;
|
|
1921
|
+
const query = `${queryStart}
|
|
1922
|
+
${fields.map((field)=>histogramQueryStrForEachField(field))}
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
}`;
|
|
1926
|
+
const queryBody = {
|
|
1927
|
+
query: query,
|
|
1928
|
+
variables: {
|
|
1929
|
+
filter: convertFilterSetToGqlFilter(filters)
|
|
1930
|
+
}
|
|
1931
|
+
};
|
|
1932
|
+
return queryBody;
|
|
1933
|
+
},
|
|
1934
|
+
transformResponse: (response, _meta, args)=>{
|
|
1935
|
+
return processHistogramResponse(response.data._aggregation[args.type]);
|
|
1936
|
+
}
|
|
1937
|
+
}),
|
|
1938
|
+
getSubAggs: builder.query({
|
|
1939
|
+
query: ({ type, mainField, termsFields = undefined, missingFields = undefined, numericAggAsText = false, gqlFilter = undefined, accessibility = Accessibility.ALL })=>{
|
|
1940
|
+
const nestedAggFields = {
|
|
1941
|
+
termsFields: termsFields,
|
|
1942
|
+
missingFields: missingFields
|
|
1943
|
+
};
|
|
1944
|
+
const query = `query getSubAggs ( ${gqlFilter ?? '$filter: JSON,'} $nestedAggFields: JSON) {
|
|
1945
|
+
_aggregation {
|
|
1946
|
+
${type} ( ${gqlFilter ?? 'filter: $filter, filterSelf: false,'} nestedAggFields: $nestedAggFields, accessibility: ${accessibility}) {
|
|
1947
|
+
${nestedHistogramQueryStrForEachField(mainField, numericAggAsText)}
|
|
1948
|
+
}`;
|
|
1949
|
+
return {
|
|
1950
|
+
query: query,
|
|
1951
|
+
variables: {
|
|
1952
|
+
...gqlFilter && {
|
|
1953
|
+
filter: convertFilterSetToGqlFilter(gqlFilter)
|
|
1954
|
+
},
|
|
1955
|
+
nestedAggFields: nestedAggFields
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
},
|
|
1959
|
+
transformResponse: (response, _meta, args)=>{
|
|
1960
|
+
return processHistogramResponse(response.data._aggregation[args.type]);
|
|
1961
|
+
}
|
|
1962
|
+
}),
|
|
1963
|
+
getCounts: builder.query({
|
|
1964
|
+
query: ({ type, filters, accessibility = Accessibility.ALL })=>{
|
|
1965
|
+
const gqlFilters = convertFilterSetToGqlFilter(filters);
|
|
1966
|
+
const queryLine = `query totalCounts ${gqlFilters ? '($filter: JSON)' : ''}{`;
|
|
1967
|
+
const typeAggsLine = `${type} ${gqlFilters ? '(filter: $filter, ' : '('} accessibility: ${accessibility}) {`;
|
|
1968
|
+
const query = `${queryLine} _aggregation {
|
|
1969
|
+
${typeAggsLine}
|
|
1970
|
+
_totalCount
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}`;
|
|
1974
|
+
return {
|
|
1975
|
+
query: query,
|
|
1976
|
+
variables: {
|
|
1977
|
+
...gqlFilters && {
|
|
1978
|
+
filter: gqlFilters
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
},
|
|
1983
|
+
transformResponse: (response, _meta, args)=>{
|
|
1984
|
+
return response.data._aggregation[args.type]._totalCount;
|
|
1985
|
+
}
|
|
1986
|
+
}),
|
|
1987
|
+
getFieldCountSummary: builder.query({
|
|
1988
|
+
query: ({ type, field, filters, accessibility = Accessibility.ALL })=>{
|
|
1989
|
+
const gqlFilters = convertFilterSetToGqlFilter(filters);
|
|
1990
|
+
const query = `query summary ($filter: JSON) {
|
|
1991
|
+
_aggregation {
|
|
1992
|
+
${type} (filter: $filter, accessibility: ${accessibility}) {
|
|
1993
|
+
${field} {
|
|
1994
|
+
histogram {
|
|
1995
|
+
sum,
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}`;
|
|
2001
|
+
return {
|
|
2002
|
+
query: query,
|
|
2003
|
+
variables: {
|
|
2004
|
+
...gqlFilters && {
|
|
2005
|
+
filter: gqlFilters
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
}),
|
|
2011
|
+
getFieldsForIndex: builder.query({
|
|
2012
|
+
query: (index)=>{
|
|
2013
|
+
return {
|
|
2014
|
+
query: `{
|
|
2015
|
+
_mapping ${index}
|
|
2016
|
+
}`
|
|
2017
|
+
};
|
|
2018
|
+
},
|
|
2019
|
+
transformResponse: (response)=>{
|
|
2020
|
+
return response['_mapping'];
|
|
2021
|
+
}
|
|
2022
|
+
}),
|
|
2023
|
+
generalGQL: builder.query({
|
|
2024
|
+
query: ({ query, variables })=>{
|
|
2025
|
+
return {
|
|
2026
|
+
query: query,
|
|
2027
|
+
variables: variables
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
})
|
|
2031
|
+
})
|
|
2032
|
+
});
|
|
2033
|
+
// query for aggregate data
|
|
2034
|
+
// convert the function below to typescript
|
|
2035
|
+
const histogramQueryStrForEachField = (field)=>{
|
|
2036
|
+
const splittedFieldArray = field.split('.');
|
|
2037
|
+
const splittedField = splittedFieldArray.shift();
|
|
2038
|
+
if (splittedFieldArray.length === 0) {
|
|
2039
|
+
return `
|
|
2040
|
+
${splittedField} {
|
|
2041
|
+
histogram {
|
|
2042
|
+
key
|
|
2043
|
+
count
|
|
2044
|
+
}
|
|
2045
|
+
}`;
|
|
2046
|
+
}
|
|
2047
|
+
return `
|
|
2048
|
+
${splittedField} {
|
|
2049
|
+
${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
2050
|
+
}`;
|
|
1285
2051
|
};
|
|
1286
|
-
const
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
2052
|
+
const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
|
|
2053
|
+
${mainField} {
|
|
2054
|
+
${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
|
|
2055
|
+
key
|
|
2056
|
+
count
|
|
2057
|
+
missingFields {
|
|
2058
|
+
field
|
|
2059
|
+
count
|
|
2060
|
+
}
|
|
2061
|
+
termsFields {
|
|
2062
|
+
field
|
|
2063
|
+
count
|
|
2064
|
+
terms {
|
|
2065
|
+
key
|
|
2066
|
+
count
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}`;
|
|
2071
|
+
const rawDataQueryStrForEachField = (field)=>{
|
|
2072
|
+
const splitFieldArray = field.split('.');
|
|
2073
|
+
const splitField = splitFieldArray.shift();
|
|
2074
|
+
if (splitFieldArray.length === 0) {
|
|
2075
|
+
return `
|
|
2076
|
+
${splitField}
|
|
2077
|
+
`;
|
|
2078
|
+
}
|
|
2079
|
+
return `
|
|
2080
|
+
${splitField} {
|
|
2081
|
+
${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
|
|
2082
|
+
}`;
|
|
1290
2083
|
};
|
|
1291
|
-
const
|
|
1292
|
-
|
|
2084
|
+
const useGetArrayTypes = ()=>{
|
|
2085
|
+
{
|
|
2086
|
+
const { data, error } = useGetStatus();
|
|
2087
|
+
if (error) {
|
|
2088
|
+
return {};
|
|
2089
|
+
}
|
|
2090
|
+
return data ? data['indices'] : {};
|
|
2091
|
+
}
|
|
1293
2092
|
};
|
|
1294
|
-
const
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
2093
|
+
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2094
|
+
|
|
2095
|
+
/**
|
|
2096
|
+
* Flattens a deep nested JSON object skipping
|
|
2097
|
+
* the first level to avoid potentially flattening
|
|
2098
|
+
* non-nested data.
|
|
2099
|
+
* @param {JSON} json
|
|
2100
|
+
*/ function flattenJson(json) {
|
|
2101
|
+
const flattenedJson = [];
|
|
2102
|
+
Object.keys(json).forEach((key)=>{
|
|
2103
|
+
flattenedJson.push(flatten(json[key], {
|
|
2104
|
+
delimiter: '_'
|
|
2105
|
+
}));
|
|
2106
|
+
});
|
|
2107
|
+
return flattenedJson;
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Converts JSON based on a config.
|
|
2111
|
+
* @param {JSON} json
|
|
2112
|
+
* @param {Object} config
|
|
2113
|
+
*/ async function conversion(json, config) {
|
|
2114
|
+
return Papa.unparse(json, config);
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Converts JSON to a specified file format.
|
|
2118
|
+
* Defaults to JSON if file format is not supported.
|
|
2119
|
+
* @param {JSON} json
|
|
2120
|
+
* @param {string} format
|
|
2121
|
+
*/ async function jsonToFormat(json, format) {
|
|
2122
|
+
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
2123
|
+
const flatJson = await flattenJson(json);
|
|
2124
|
+
const data = await conversion(flatJson, {
|
|
2125
|
+
delimiter: FILE_DELIMITERS[format]
|
|
2126
|
+
});
|
|
2127
|
+
return data;
|
|
1322
2128
|
}
|
|
2129
|
+
return json;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
/**
|
|
2133
|
+
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2134
|
+
*
|
|
2135
|
+
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2136
|
+
* @returns {URL} - The prepared download URL as a URL object.
|
|
2137
|
+
*/ const prepareUrl$1 = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2138
|
+
/**
|
|
2139
|
+
* Prepares a fetch configuration object for downloading files from Guppy.
|
|
2140
|
+
*
|
|
2141
|
+
* @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
|
|
2142
|
+
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
2143
|
+
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
2144
|
+
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
2145
|
+
return {
|
|
2146
|
+
method: 'POST',
|
|
2147
|
+
headers: {
|
|
2148
|
+
'Content-Type': 'application/json',
|
|
2149
|
+
...csrfToken !== undefined && {
|
|
2150
|
+
'X-CSRF-Token': csrfToken
|
|
2151
|
+
}
|
|
2152
|
+
},
|
|
2153
|
+
body: JSON.stringify({
|
|
2154
|
+
type: parameters.type,
|
|
2155
|
+
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
2156
|
+
accessibility: parameters.accessibility,
|
|
2157
|
+
fields: parameters?.fields,
|
|
2158
|
+
sort: parameters?.sort
|
|
2159
|
+
})
|
|
2160
|
+
};
|
|
1323
2161
|
};
|
|
1324
2162
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
this.handleGreaterThanOrEquals = (op)=>({
|
|
1356
|
-
'>=': {
|
|
1357
|
-
[op.field]: op.operand
|
|
1358
|
-
}
|
|
1359
|
-
});
|
|
1360
|
-
this.handleIncludes = (op)=>({
|
|
1361
|
-
in: {
|
|
1362
|
-
[op.field]: op.operands
|
|
1363
|
-
}
|
|
1364
|
-
});
|
|
1365
|
-
this.handleExcludes = (op)=>({
|
|
1366
|
-
exclude: {
|
|
1367
|
-
[op.field]: op.operands
|
|
1368
|
-
}
|
|
1369
|
-
});
|
|
1370
|
-
this.handleExcludeIfAny = (op)=>({
|
|
1371
|
-
excludeifany: {
|
|
1372
|
-
[op.field]: op.operands
|
|
1373
|
-
}
|
|
1374
|
-
});
|
|
1375
|
-
this.handleIntersection = (op)=>({
|
|
1376
|
-
and: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
2163
|
+
* Downloads a file from Guppy using the provided parameters.
|
|
2164
|
+
* It will optionally convert the data to the specified format.
|
|
2165
|
+
*
|
|
2166
|
+
* @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
|
|
2167
|
+
* @param onStart - The function to call when the download starts.
|
|
2168
|
+
* @param onDone - The function to call when the download is done.
|
|
2169
|
+
* @param onError - The function to call when the download fails.
|
|
2170
|
+
* @param onAbort - The function to call when the download is aborted.
|
|
2171
|
+
* @param signal - AbortSignal to use for the request.
|
|
2172
|
+
*/ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
|
|
2173
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2174
|
+
onStart?.();
|
|
2175
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2176
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2177
|
+
fetch(url.toString(), {
|
|
2178
|
+
...fetchConfig,
|
|
2179
|
+
...signal ? {
|
|
2180
|
+
signal: signal
|
|
2181
|
+
} : {}
|
|
2182
|
+
}).then(async (response)=>{
|
|
2183
|
+
if (!response.ok) {
|
|
2184
|
+
throw new Error(response.statusText);
|
|
2185
|
+
}
|
|
2186
|
+
let jsonData = await response.json();
|
|
2187
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
2188
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
2189
|
+
jsonData = JSONPath({
|
|
2190
|
+
json: jsonData,
|
|
2191
|
+
path: `$.[${parameters.rootPath}]`,
|
|
2192
|
+
resultType: 'value'
|
|
1377
2193
|
});
|
|
1378
|
-
|
|
1379
|
-
|
|
2194
|
+
}
|
|
2195
|
+
// convert the data to the specified format and return a Blob
|
|
2196
|
+
let str = '';
|
|
2197
|
+
if (parameters.format === 'json') {
|
|
2198
|
+
str = JSON.stringify(jsonData);
|
|
2199
|
+
} else {
|
|
2200
|
+
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
2201
|
+
if (isJSONObject(convertedData)) {
|
|
2202
|
+
str = JSON.stringify(convertedData, null, 2);
|
|
2203
|
+
} else {
|
|
2204
|
+
str = convertedData;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return new Blob([
|
|
2208
|
+
str
|
|
2209
|
+
], {
|
|
2210
|
+
type: 'application/json'
|
|
2211
|
+
});
|
|
2212
|
+
}).then((blob)=>onDone?.(blob)).catch((error)=>{
|
|
2213
|
+
// Abort is handle as an exception
|
|
2214
|
+
if (error.name == 'AbortError') {
|
|
2215
|
+
// handle abort()
|
|
2216
|
+
onAbort?.();
|
|
2217
|
+
}
|
|
2218
|
+
onError?.(error);
|
|
2219
|
+
});
|
|
2220
|
+
};
|
|
2221
|
+
const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
|
|
2222
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2223
|
+
const url = prepareUrl$1(GEN3_GUPPY_API);
|
|
2224
|
+
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
2225
|
+
try {
|
|
2226
|
+
const response = await fetch(url.toString(), {
|
|
2227
|
+
...fetchConfig,
|
|
2228
|
+
...signal ? {
|
|
2229
|
+
signal: signal
|
|
2230
|
+
} : {}
|
|
2231
|
+
});
|
|
2232
|
+
let jsonData = await response.json();
|
|
2233
|
+
if (parameters?.rootPath && parameters.rootPath) {
|
|
2234
|
+
// if rootPath is provided, extract the data from the rootPath
|
|
2235
|
+
jsonData = JSONPath({
|
|
2236
|
+
json: jsonData,
|
|
2237
|
+
path: `$.[${parameters.rootPath}]`,
|
|
2238
|
+
resultType: 'value'
|
|
1380
2239
|
});
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
}
|
|
2240
|
+
}
|
|
2241
|
+
// convert the data to the specified format and return a Blob
|
|
2242
|
+
return jsonData;
|
|
2243
|
+
} catch (error) {
|
|
2244
|
+
// Abort is handle as an exception
|
|
2245
|
+
if (error.name == 'AbortError') {
|
|
2246
|
+
// handle abort()
|
|
2247
|
+
onAbort?.();
|
|
2248
|
+
}
|
|
2249
|
+
throw new Error(error);
|
|
1390
2250
|
}
|
|
1391
|
-
}
|
|
1392
|
-
const convertFilterToGqlFilter = (filter)=>{
|
|
1393
|
-
const handler = new ToGqlHandler();
|
|
1394
|
-
return handleOperation(handler, filter);
|
|
1395
2251
|
};
|
|
1396
|
-
const
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
if (fsKeys.length === 0) return {
|
|
1400
|
-
and: []
|
|
1401
|
-
};
|
|
1402
|
-
return toplevelOp === 'and' ? {
|
|
1403
|
-
and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1404
|
-
} : {
|
|
1405
|
-
or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1406
|
-
};
|
|
2252
|
+
const useGetIndexFields = (index)=>{
|
|
2253
|
+
const { data } = useGetFieldsForIndexQuery(index);
|
|
2254
|
+
return data ?? [];
|
|
1407
2255
|
};
|
|
2256
|
+
|
|
1408
2257
|
/**
|
|
1409
|
-
*
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
this.handleNestedFilter = (op)=>{
|
|
1442
|
-
return extractEnumFilterValue(op.operand);
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
2258
|
+
* Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
|
|
2259
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/download.md
|
|
2260
|
+
* @param endpoints - Resolver function which configures the query with
|
|
2261
|
+
* type, filter, accessibility, fields, and sort arguments
|
|
2262
|
+
* @returns: A guppy download API for fetching bulk metadata
|
|
2263
|
+
*/ const downloadRequestApi = gen3Api.injectEndpoints({
|
|
2264
|
+
endpoints: (builder)=>({
|
|
2265
|
+
downloadFromGuppy: builder.mutation({
|
|
2266
|
+
query: ({ type, filter, accessibility, fields, sort })=>{
|
|
2267
|
+
const queryBody = {
|
|
2268
|
+
filter: convertFilterSetToGqlFilter(filter),
|
|
2269
|
+
...{
|
|
2270
|
+
type,
|
|
2271
|
+
accessibility,
|
|
2272
|
+
fields,
|
|
2273
|
+
sort
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
return {
|
|
2277
|
+
url: `${GEN3_GUPPY_API}/download`,
|
|
2278
|
+
method: 'POST',
|
|
2279
|
+
queryBody,
|
|
2280
|
+
cache: 'no-cache'
|
|
2281
|
+
};
|
|
2282
|
+
},
|
|
2283
|
+
transformResponse: (response)=>{
|
|
2284
|
+
return response;
|
|
2285
|
+
}
|
|
2286
|
+
})
|
|
2287
|
+
})
|
|
2288
|
+
});
|
|
2289
|
+
const { useDownloadFromGuppyMutation } = downloadRequestApi;
|
|
1446
2290
|
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
];
|
|
1469
|
-
const capitalize = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
|
|
1470
|
-
const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
1471
|
-
if (trim) {
|
|
1472
|
-
const source = fieldName.slice(fieldName.indexOf('.') + 1);
|
|
1473
|
-
return fieldNameToTitle(source ? source : fieldName, 0);
|
|
1474
|
-
}
|
|
1475
|
-
return fieldNameToTitle(fieldName);
|
|
2291
|
+
const rootReducer = combineReducers({
|
|
2292
|
+
gen3Services: gen3ServicesReducer,
|
|
2293
|
+
user: userReducer,
|
|
2294
|
+
gen3Apps: gen3AppReducer,
|
|
2295
|
+
drsHostnames: drsHostnamesReducer,
|
|
2296
|
+
modals: modalReducer,
|
|
2297
|
+
cohorts: cohortReducer,
|
|
2298
|
+
activeWorkspace: activeWorkspaceReducer,
|
|
2299
|
+
dataLibrarySelection: dataLibrarySelectionReducer,
|
|
2300
|
+
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
2301
|
+
[userAuthApiReducerPath]: userAuthApiReducer
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
const coreStore = configureStore({
|
|
2305
|
+
reducer: rootReducer,
|
|
2306
|
+
middleware: (getDefaultMiddleware)=>getDefaultMiddleware().concat(gen3ServicesReducerMiddleware, guppyAPISliceMiddleware, userAuthApiMiddleware)
|
|
2307
|
+
});
|
|
2308
|
+
setupListeners(coreStore.dispatch);
|
|
2309
|
+
|
|
2310
|
+
const isNotDefined = (x)=>{
|
|
2311
|
+
return x === undefined || x === null || x === void 0;
|
|
1476
2312
|
};
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
* For example files.input.experimental_strategy will get converted to Experimental Strategy
|
|
1480
|
-
* if sections == 2 then the output would be Input Experimental Strategy
|
|
1481
|
-
* @param fieldName input filter expected to be: string.firstpart_secondpart
|
|
1482
|
-
* @param sections number of "sections" string.string.string to got back from the end of the field
|
|
1483
|
-
*/ const fieldNameToTitle = (fieldName, sections = 1)=>{
|
|
1484
|
-
if (fieldName in FieldNameOverrides) {
|
|
1485
|
-
return FieldNameOverrides[fieldName];
|
|
1486
|
-
}
|
|
1487
|
-
if (fieldName === undefined) return 'No Title';
|
|
1488
|
-
return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize(word)).join(' ');
|
|
2313
|
+
const isObject = (x)=>{
|
|
2314
|
+
return typeof x === 'object';
|
|
1489
2315
|
};
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
* @param fieldName
|
|
1493
|
-
*/ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
|
|
1494
|
-
/**
|
|
1495
|
-
* prepend the index name to the field name
|
|
1496
|
-
*/ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
|
|
1497
|
-
/**
|
|
1498
|
-
* extract the field name from the index.field name
|
|
1499
|
-
*/ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
|
|
1500
|
-
/**
|
|
1501
|
-
* extract the field name and the index from the index.field name returning as a tuple
|
|
1502
|
-
*/ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
|
|
1503
|
-
const [index, ...rest] = fieldName.split('.');
|
|
1504
|
-
return [
|
|
1505
|
-
index,
|
|
1506
|
-
rest.join('.')
|
|
1507
|
-
];
|
|
2316
|
+
const isArray = (x)=>{
|
|
2317
|
+
return Array.isArray(x);
|
|
1508
2318
|
};
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const processHistogramResponse = (data)=>{
|
|
1512
|
-
const pathData = JSONPath({
|
|
1513
|
-
json: data,
|
|
1514
|
-
path: '$..histogram',
|
|
1515
|
-
resultType: 'all'
|
|
1516
|
-
});
|
|
1517
|
-
const results = pathData.reduce((acc, element)=>{
|
|
1518
|
-
const key = element.pointer.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
|
|
1519
|
-
return {
|
|
1520
|
-
...acc,
|
|
1521
|
-
[key]: element.value
|
|
1522
|
-
};
|
|
1523
|
-
}, {});
|
|
1524
|
-
return results;
|
|
2319
|
+
const isString = (x)=>{
|
|
2320
|
+
return typeof x === 'string';
|
|
1525
2321
|
};
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
2322
|
+
|
|
2323
|
+
/**
|
|
2324
|
+
* Prepares a URL for downloading by appending '/download' to the provided apiUrl.
|
|
2325
|
+
*
|
|
2326
|
+
* @param {string} apiUrl - The base URL to be used for preparing the download URL.
|
|
2327
|
+
* @returns {URL} - The prepared download URL as a URL object.
|
|
2328
|
+
*/ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
|
|
2329
|
+
|
|
2330
|
+
const HTTPErrorMessages = {
|
|
2331
|
+
// 4xx Client Errors
|
|
2332
|
+
400: 'Bad Request',
|
|
2333
|
+
401: 'Unauthorized',
|
|
2334
|
+
402: 'Payment Required',
|
|
2335
|
+
403: 'Forbidden',
|
|
2336
|
+
404: 'Not Found',
|
|
2337
|
+
405: 'Method Not Allowed',
|
|
2338
|
+
406: 'Not Acceptable',
|
|
2339
|
+
407: 'Proxy Authentication Required',
|
|
2340
|
+
408: 'Request Timeout',
|
|
2341
|
+
409: 'Conflict',
|
|
2342
|
+
410: 'Gone',
|
|
2343
|
+
411: 'Length Required',
|
|
2344
|
+
412: 'Precondition Failed',
|
|
2345
|
+
413: 'Payload Too Large',
|
|
2346
|
+
414: 'URI Too Long',
|
|
2347
|
+
415: 'Unsupported Media Type',
|
|
2348
|
+
416: 'Range Not Satisfiable',
|
|
2349
|
+
417: 'Expectation Failed',
|
|
2350
|
+
418: "I'm a teapot",
|
|
2351
|
+
421: 'Misdirected Request',
|
|
2352
|
+
422: 'Unprocessable Entity',
|
|
2353
|
+
423: 'Locked',
|
|
2354
|
+
424: 'Failed Dependency',
|
|
2355
|
+
425: 'Too Early',
|
|
2356
|
+
426: 'Upgrade Required',
|
|
2357
|
+
428: 'Precondition Required',
|
|
2358
|
+
429: 'Too Many Requests',
|
|
2359
|
+
431: 'Request Header Fields Too Large',
|
|
2360
|
+
451: 'Unavailable For Legal Reasons',
|
|
2361
|
+
// 5xx Server Errors
|
|
2362
|
+
500: 'Internal Server Error',
|
|
2363
|
+
501: 'Not Implemented',
|
|
2364
|
+
502: 'Bad Gateway',
|
|
2365
|
+
503: 'Service Unavailable',
|
|
2366
|
+
504: 'Gateway Timeout',
|
|
2367
|
+
505: 'HTTP Version Not Supported',
|
|
2368
|
+
506: 'Variant Also Negotiates',
|
|
2369
|
+
507: 'Insufficient Storage',
|
|
2370
|
+
508: 'Loop Detected',
|
|
2371
|
+
510: 'Not Extended',
|
|
2372
|
+
511: 'Network Authentication Required'
|
|
2373
|
+
};
|
|
2374
|
+
const prepareDownloadUrl = (apiUrl, guid)=>new URL(`${apiUrl}/data/download/${guid}`);
|
|
2375
|
+
class HTTPError extends Error {
|
|
2376
|
+
constructor(status, message, responseData){
|
|
2377
|
+
super(message), this.status = status, this.responseData = responseData;
|
|
2378
|
+
this.name = 'HTTPError';
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null, signal = undefined })=>{
|
|
2382
|
+
const csrfToken = selectCSRFToken(coreStore.getState());
|
|
2383
|
+
const headers = new Headers();
|
|
2384
|
+
headers.set('Content-Type', 'application/json');
|
|
2385
|
+
let accessToken = undefined;
|
|
2386
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2387
|
+
// NOTE: This cookie can only be accessed from the client side
|
|
2388
|
+
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2389
|
+
accessToken = getCookie('credentials_token');
|
|
2390
|
+
}
|
|
2391
|
+
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
|
|
2392
|
+
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2393
|
+
const url = prepareDownloadUrl(`${GEN3_FENCE_API}/user`, guid);
|
|
2394
|
+
try {
|
|
2395
|
+
const response = await fetch(url.toString(), {
|
|
2396
|
+
method: method,
|
|
2397
|
+
headers: headers,
|
|
2398
|
+
...signal ? {
|
|
2399
|
+
signal: signal
|
|
2400
|
+
} : {}
|
|
2401
|
+
});
|
|
2402
|
+
// Check if the response is ok before proceeding
|
|
2403
|
+
if (!response.ok) {
|
|
2404
|
+
let errorMessage;
|
|
2405
|
+
let errorData;
|
|
2406
|
+
try {
|
|
2407
|
+
// Attempt to parse error response as JSON
|
|
2408
|
+
errorData = await response.json();
|
|
2409
|
+
errorMessage = errorData.message || response.statusText;
|
|
2410
|
+
} catch {
|
|
2411
|
+
// If JSON parsing fails, use status text
|
|
2412
|
+
errorMessage = response.statusText;
|
|
2413
|
+
}
|
|
2414
|
+
throw new HTTPError(response.status, errorMessage, errorData);
|
|
1531
2415
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2416
|
+
const jsonData = await response.json();
|
|
2417
|
+
return jsonData;
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
if (error instanceof Error) {
|
|
2420
|
+
if (error.name === 'AbortError') {
|
|
2421
|
+
onAbort?.();
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
throw error;
|
|
2425
|
+
}
|
|
1535
2426
|
};
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
return
|
|
2427
|
+
|
|
2428
|
+
const CoreProvider = ({ children })=>{
|
|
2429
|
+
return /*#__PURE__*/ React__default.createElement(Provider, {
|
|
2430
|
+
store: coreStore
|
|
2431
|
+
}, children);
|
|
1539
2432
|
};
|
|
2433
|
+
|
|
1540
2434
|
/**
|
|
1541
|
-
*
|
|
1542
|
-
*
|
|
1543
|
-
*
|
|
2435
|
+
* Creates the authzApi for checking arborist permissions for a selected user
|
|
2436
|
+
* @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/arborist/master/docs/openapi.yaml#/auth/get_auth_mapping
|
|
2437
|
+
* @see https://github.com/uc-cdis/arborist/blob/master/docs/relationships.simplified.png
|
|
2438
|
+
* @returns: An arborist response dict of user permissions {method, service} for each resource path.
|
|
2439
|
+
*/ const authzApi = gen3Api.injectEndpoints({
|
|
2440
|
+
endpoints: (builder)=>({
|
|
2441
|
+
getAuthzMappings: builder.query({
|
|
2442
|
+
query: ()=>`${GEN3_AUTHZ_API}/mapping`
|
|
2443
|
+
})
|
|
2444
|
+
})
|
|
2445
|
+
});
|
|
2446
|
+
const { useGetAuthzMappingsQuery } = authzApi;
|
|
2447
|
+
const selectAuthzMapping = authzApi.endpoints.getAuthzMappings.select();
|
|
2448
|
+
const selectAuthzMappingData = createSelector(selectAuthzMapping, (authzMapping)=>authzMapping?.data ?? {
|
|
2449
|
+
mappings: []
|
|
2450
|
+
});
|
|
2451
|
+
|
|
2452
|
+
const HasEnoughData = (data, keys, limit)=>{
|
|
2453
|
+
const numEmptyKeys = keys.filter((k)=>Object.hasOwn(data, k) && typeof data[k] === 'string' && data[k].trim() === '').length;
|
|
2454
|
+
return numEmptyKeys < limit;
|
|
2455
|
+
};
|
|
2456
|
+
/**
|
|
2457
|
+
* Defines metadataApi service using a base URL and expected endpoints. Derived from gen3Api core API.
|
|
1544
2458
|
*
|
|
1545
|
-
* @param endpoints - Defines endpoints used in
|
|
1546
|
-
*
|
|
1547
|
-
*
|
|
1548
|
-
*
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
* @returns: A guppy API
|
|
1557
|
-
*/ const
|
|
2459
|
+
* @param endpoints - Defines endpoints used in discovery page
|
|
2460
|
+
* @param getAggMDS - Queries aggregate metadata service
|
|
2461
|
+
* @see https://github.com/uc-cdis/metadata-service/blob/master/docs/agg_mds.md
|
|
2462
|
+
* @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/metadata-service/master/docs/openapi.yaml#/Aggregate/get_aggregate_metadata_aggregate_metadata_get
|
|
2463
|
+
* @param getMDS - Queries normal metadata service
|
|
2464
|
+
* @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/metadata-service/master/docs/openapi.yaml#/Query/search_metadata_metadata_get
|
|
2465
|
+
* @param getIndexAggMDS - queries the Aggregate Metadata service and returns all common passed in indexKeys
|
|
2466
|
+
* @param getTags - Probably refering to Aggregate metadata service summary statistics query
|
|
2467
|
+
* @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/metadata-service/master/docs/openapi.yaml#/Aggregate/get_aggregate_tags_aggregate_tags_get
|
|
2468
|
+
* @param getData - Looks like a duplicate of getMDS handler. unused in ./frontend package
|
|
2469
|
+
* @param getCrosswalkData - Maps ids from one source to another
|
|
2470
|
+
* @returns: A guppy download API for fetching bulk metadata
|
|
2471
|
+
*/ const metadataApi = gen3Api.injectEndpoints({
|
|
1558
2472
|
endpoints: (builder)=>({
|
|
1559
|
-
|
|
1560
|
-
query: (
|
|
1561
|
-
|
|
1562
|
-
|
|
2473
|
+
getAggMDS: builder.query({
|
|
2474
|
+
query: ({ offset, pageSize })=>{
|
|
2475
|
+
return `${GEN3_MDS_API}/aggregate/metadata?flatten=true&pagination=true&offset=${offset}&limit=${pageSize}`;
|
|
2476
|
+
},
|
|
1563
2477
|
transformResponse: (response, _meta, params)=>{
|
|
1564
|
-
return response[params.type];
|
|
1565
|
-
}
|
|
1566
|
-
}),
|
|
1567
|
-
getAccessibleData: builder.query({
|
|
1568
|
-
query: ({ type, fields, accessType })=>{
|
|
1569
|
-
const fieldParts = fields.map((field)=>`${field} { histogram { key count } }`);
|
|
1570
|
-
return {
|
|
1571
|
-
query: `_aggregation {
|
|
1572
|
-
${type} (accessibility: ${accessType}) {
|
|
1573
|
-
${fieldParts.join(',')}
|
|
1574
|
-
}
|
|
1575
|
-
}`
|
|
1576
|
-
};
|
|
1577
|
-
}
|
|
1578
|
-
}),
|
|
1579
|
-
getRawDataAndTotalCounts: builder.query({
|
|
1580
|
-
query: ({ type, fields, filters, sort, offset = 0, size = 20, accessibility = Accessibility.ALL, format = undefined })=>{
|
|
1581
|
-
const gqlFilter = convertFilterSetToGqlFilter(filters);
|
|
1582
|
-
const params = [
|
|
1583
|
-
...sort ? [
|
|
1584
|
-
'$sort: JSON'
|
|
1585
|
-
] : [],
|
|
1586
|
-
...gqlFilter ? [
|
|
1587
|
-
'$filter: JSON'
|
|
1588
|
-
] : [],
|
|
1589
|
-
...format ? [
|
|
1590
|
-
'$format: Format'
|
|
1591
|
-
] : []
|
|
1592
|
-
].join(',');
|
|
1593
|
-
const queryLine = `query getRawDataAndTotalCounts (${params}) {`;
|
|
1594
|
-
const dataParams = [
|
|
1595
|
-
...format ? [
|
|
1596
|
-
'format: $format'
|
|
1597
|
-
] : [],
|
|
1598
|
-
...sort ? [
|
|
1599
|
-
'sort: $sort'
|
|
1600
|
-
] : [],
|
|
1601
|
-
...gqlFilter ? [
|
|
1602
|
-
'filter: $filter'
|
|
1603
|
-
] : []
|
|
1604
|
-
].join(',');
|
|
1605
|
-
const dataTypeLine = `${type} (accessibility: ${accessibility}, offset: ${offset}, first: ${size},
|
|
1606
|
-
${dataParams}) {`;
|
|
1607
|
-
const typeAggsLine = `${type} (${gqlFilter && 'filter: $filter,'} accessibility: ${accessibility}) {`;
|
|
1608
|
-
const processedFields = fields.map((field)=>rawDataQueryStrForEachField(field));
|
|
1609
|
-
const query = `${queryLine}
|
|
1610
|
-
${dataTypeLine}
|
|
1611
|
-
${processedFields.join(' ')}
|
|
1612
|
-
}
|
|
1613
|
-
_aggregation {
|
|
1614
|
-
${typeAggsLine}
|
|
1615
|
-
_totalCount
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
}`;
|
|
1619
|
-
const variables = {
|
|
1620
|
-
...sort && {
|
|
1621
|
-
sort
|
|
1622
|
-
},
|
|
1623
|
-
...gqlFilter && {
|
|
1624
|
-
filter: gqlFilter
|
|
1625
|
-
},
|
|
1626
|
-
...format && {
|
|
1627
|
-
format
|
|
1628
|
-
}
|
|
1629
|
-
};
|
|
1630
2478
|
return {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
query: ({ type, fields, filters, accessibility = Accessibility.ALL })=>{
|
|
1638
|
-
const queryStart = isFilterEmpty(filters) ? `
|
|
1639
|
-
query getAggs {
|
|
1640
|
-
_aggregation {
|
|
1641
|
-
${type} (accessibility: ${accessibility}) {` : `query getAggs ($filter: JSON) {
|
|
1642
|
-
_aggregation {
|
|
1643
|
-
${type} (filter: $filter, filterSelf: false, accessibility: ${accessibility}) {`;
|
|
1644
|
-
const query = `${queryStart}
|
|
1645
|
-
${fields.map((field)=>histogramQueryStrForEachField(field))}
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
}`;
|
|
1649
|
-
const queryBody = {
|
|
1650
|
-
query: query,
|
|
1651
|
-
variables: {
|
|
1652
|
-
filter: convertFilterSetToGqlFilter(filters)
|
|
1653
|
-
}
|
|
2479
|
+
data: response.results.map((x)=>{
|
|
2480
|
+
const objValues = Object.values(x);
|
|
2481
|
+
const firstValue = objValues ? objValues.at(0) : undefined;
|
|
2482
|
+
return firstValue ? firstValue[params.studyField] : undefined;
|
|
2483
|
+
}),
|
|
2484
|
+
hits: response.pagination.hits
|
|
1654
2485
|
};
|
|
1655
|
-
return queryBody;
|
|
1656
|
-
},
|
|
1657
|
-
transformResponse: (response, _meta, args)=>{
|
|
1658
|
-
return processHistogramResponse(response.data._aggregation[args.type]);
|
|
1659
2486
|
}
|
|
1660
2487
|
}),
|
|
1661
|
-
|
|
1662
|
-
query: ({
|
|
1663
|
-
|
|
1664
|
-
termsFields: termsFields,
|
|
1665
|
-
missingFields: missingFields
|
|
1666
|
-
};
|
|
1667
|
-
const query = `query getSubAggs ( ${gqlFilter ?? '$filter: JSON,'} $nestedAggFields: JSON) {
|
|
1668
|
-
_aggregation {
|
|
1669
|
-
${type} ( ${gqlFilter ?? 'filter: $filter, filterSelf: false,'} nestedAggFields: $nestedAggFields, accessibility: ${accessibility}) {
|
|
1670
|
-
${nestedHistogramQueryStrForEachField(mainField, numericAggAsText)}
|
|
1671
|
-
}`;
|
|
1672
|
-
return {
|
|
1673
|
-
query: query,
|
|
1674
|
-
variables: {
|
|
1675
|
-
...gqlFilter && {
|
|
1676
|
-
filter: convertFilterSetToGqlFilter(gqlFilter)
|
|
1677
|
-
},
|
|
1678
|
-
nestedAggFields: nestedAggFields
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
2488
|
+
getIndexAggMDS: builder.query({
|
|
2489
|
+
query: ({ pageSize })=>{
|
|
2490
|
+
return `${GEN3_MDS_API}/aggregate/metadata?limit=${pageSize}`;
|
|
1681
2491
|
},
|
|
1682
|
-
transformResponse: (response, _meta,
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
getCounts: builder.query({
|
|
1687
|
-
query: ({ type, filters, accessibility = Accessibility.ALL })=>{
|
|
1688
|
-
const gqlFilters = convertFilterSetToGqlFilter(filters);
|
|
1689
|
-
const queryLine = `query totalCounts ${gqlFilters ? '($filter: JSON)' : ''}{`;
|
|
1690
|
-
const typeAggsLine = `${type} ${gqlFilters ? '(filter: $filter, ' : '('} accessibility: ${accessibility}) {`;
|
|
1691
|
-
const query = `${queryLine} _aggregation {
|
|
1692
|
-
${typeAggsLine}
|
|
1693
|
-
_totalCount
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
}`;
|
|
1697
|
-
return {
|
|
1698
|
-
query: query,
|
|
1699
|
-
variables: {
|
|
1700
|
-
...gqlFilters && {
|
|
1701
|
-
filter: gqlFilters
|
|
1702
|
-
}
|
|
2492
|
+
transformResponse: (response, _meta, params)=>{
|
|
2493
|
+
const dataFromIndexes = params.indexKeys.reduce((acc, key)=>{
|
|
2494
|
+
if (response[key]) {
|
|
2495
|
+
acc.push(...response[key]);
|
|
1703
2496
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
transformResponse: (response, _meta, args)=>{
|
|
1707
|
-
return response.data._aggregation[args.type]._totalCount;
|
|
1708
|
-
}
|
|
1709
|
-
}),
|
|
1710
|
-
getFieldCountSummary: builder.query({
|
|
1711
|
-
query: ({ type, field, filters, accessibility = Accessibility.ALL })=>{
|
|
1712
|
-
const gqlFilters = convertFilterSetToGqlFilter(filters);
|
|
1713
|
-
const query = `query summary ($filter: JSON) {
|
|
1714
|
-
_aggregation {
|
|
1715
|
-
${type} (filter: $filter, accessibility: ${accessibility}) {
|
|
1716
|
-
${field} {
|
|
1717
|
-
histogram {
|
|
1718
|
-
sum,
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
}`;
|
|
2497
|
+
return acc;
|
|
2498
|
+
}, []);
|
|
1724
2499
|
return {
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
2500
|
+
data: dataFromIndexes.map((x)=>{
|
|
2501
|
+
const objValues = Object.values(x);
|
|
2502
|
+
const objIds = Object.keys(x);
|
|
2503
|
+
let firstValue = objValues ? objValues.at(0) : undefined;
|
|
2504
|
+
if (params?.filterEmpty) {
|
|
2505
|
+
// remove any data that has < limit if defined
|
|
2506
|
+
if (firstValue && !HasEnoughData(firstValue[params.studyField], params.filterEmpty.keys, params.filterEmpty.limit)) firstValue = undefined;
|
|
1729
2507
|
}
|
|
1730
|
-
|
|
2508
|
+
return firstValue ? {
|
|
2509
|
+
gen3MDSGUID: objIds.at(0),
|
|
2510
|
+
...firstValue[params.studyField]
|
|
2511
|
+
} : undefined;
|
|
2512
|
+
}).filter((x)=>x !== undefined),
|
|
2513
|
+
hits: dataFromIndexes.length
|
|
1731
2514
|
};
|
|
1732
2515
|
}
|
|
1733
2516
|
}),
|
|
1734
|
-
|
|
1735
|
-
query: (
|
|
2517
|
+
getMDS: builder.query({
|
|
2518
|
+
query: ({ guidType, offset, pageSize })=>{
|
|
2519
|
+
return `${GEN3_MDS_API}/metadata?data=True&_guid_type=${guidType}&limit=${pageSize}&offset=${offset}`;
|
|
2520
|
+
},
|
|
2521
|
+
transformResponse: (response, _meta)=>{
|
|
1736
2522
|
return {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
}`
|
|
2523
|
+
data: Object.keys(response).map((guid)=>response[guid]),
|
|
2524
|
+
hits: Object.keys(response).length
|
|
1740
2525
|
};
|
|
1741
|
-
},
|
|
1742
|
-
transformResponse: (response)=>{
|
|
1743
|
-
return response['_mapping'];
|
|
1744
2526
|
}
|
|
1745
2527
|
}),
|
|
1746
|
-
|
|
1747
|
-
query: (
|
|
2528
|
+
getTags: builder.query({
|
|
2529
|
+
query: ()=>'tags'
|
|
2530
|
+
}),
|
|
2531
|
+
getData: builder.query({
|
|
2532
|
+
query: (params)=>({
|
|
2533
|
+
url: `metadata?${params}`
|
|
2534
|
+
})
|
|
2535
|
+
}),
|
|
2536
|
+
// TODO: Move this to own slice
|
|
2537
|
+
getCrosswalkData: builder.query({
|
|
2538
|
+
queryFn: async (arg, _queryApi, _extraOptions, fetchWithBQ)=>{
|
|
2539
|
+
const queryMultiple = async ()=>{
|
|
2540
|
+
let result = [];
|
|
2541
|
+
const queue = Queue({
|
|
2542
|
+
concurrency: 15
|
|
2543
|
+
});
|
|
2544
|
+
for (const id of arg.ids){
|
|
2545
|
+
queue.push(async (callback)=>{
|
|
2546
|
+
const response = await fetchWithBQ({
|
|
2547
|
+
url: `${GEN3_CROSSWALK_API}/metadata/${id}`
|
|
2548
|
+
});
|
|
2549
|
+
if (response.error) {
|
|
2550
|
+
return {
|
|
2551
|
+
error: response.error
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
const toData = arg.toPaths.reduce((acc, path)=>{
|
|
2555
|
+
acc[path.id] = JSONPath({
|
|
2556
|
+
json: response.data,
|
|
2557
|
+
path: `$.[${path.dataPath}]`,
|
|
2558
|
+
resultType: 'value'
|
|
2559
|
+
})?.[0] ?? 'n/a';
|
|
2560
|
+
return acc;
|
|
2561
|
+
}, {});
|
|
2562
|
+
result = [
|
|
2563
|
+
...result,
|
|
2564
|
+
{
|
|
2565
|
+
from: id,
|
|
2566
|
+
to: toData
|
|
2567
|
+
}
|
|
2568
|
+
];
|
|
2569
|
+
if (callback) callback();
|
|
2570
|
+
return result;
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
return new Promise((resolve, reject)=>{
|
|
2574
|
+
queue.start((err)=>{
|
|
2575
|
+
if (err) {
|
|
2576
|
+
reject(err);
|
|
2577
|
+
} else {
|
|
2578
|
+
resolve(result);
|
|
2579
|
+
}
|
|
2580
|
+
});
|
|
2581
|
+
});
|
|
2582
|
+
};
|
|
2583
|
+
const result = await queryMultiple();
|
|
1748
2584
|
return {
|
|
1749
|
-
|
|
1750
|
-
variables: variables
|
|
2585
|
+
data: result
|
|
1751
2586
|
};
|
|
1752
2587
|
}
|
|
1753
2588
|
})
|
|
1754
2589
|
})
|
|
1755
2590
|
});
|
|
1756
|
-
|
|
1757
|
-
// convert the function below to typescript
|
|
1758
|
-
const histogramQueryStrForEachField = (field)=>{
|
|
1759
|
-
const splittedFieldArray = field.split('.');
|
|
1760
|
-
const splittedField = splittedFieldArray.shift();
|
|
1761
|
-
if (splittedFieldArray.length === 0) {
|
|
1762
|
-
return `
|
|
1763
|
-
${splittedField} {
|
|
1764
|
-
histogram {
|
|
1765
|
-
key
|
|
1766
|
-
count
|
|
1767
|
-
}
|
|
1768
|
-
}`;
|
|
1769
|
-
}
|
|
1770
|
-
return `
|
|
1771
|
-
${splittedField} {
|
|
1772
|
-
${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
1773
|
-
}`;
|
|
1774
|
-
};
|
|
1775
|
-
const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
|
|
1776
|
-
${mainField} {
|
|
1777
|
-
${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
|
|
1778
|
-
key
|
|
1779
|
-
count
|
|
1780
|
-
missingFields {
|
|
1781
|
-
field
|
|
1782
|
-
count
|
|
1783
|
-
}
|
|
1784
|
-
termsFields {
|
|
1785
|
-
field
|
|
1786
|
-
count
|
|
1787
|
-
terms {
|
|
1788
|
-
key
|
|
1789
|
-
count
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
}`;
|
|
1794
|
-
const rawDataQueryStrForEachField = (field)=>{
|
|
1795
|
-
const splitFieldArray = field.split('.');
|
|
1796
|
-
const splitField = splitFieldArray.shift();
|
|
1797
|
-
if (splitFieldArray.length === 0) {
|
|
1798
|
-
return `
|
|
1799
|
-
${splitField}
|
|
1800
|
-
`;
|
|
1801
|
-
}
|
|
1802
|
-
return `
|
|
1803
|
-
${splitField} {
|
|
1804
|
-
${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
|
|
1805
|
-
}`;
|
|
1806
|
-
};
|
|
1807
|
-
const useGetArrayTypes = ()=>{
|
|
1808
|
-
{
|
|
1809
|
-
const { data, error } = useGetStatus();
|
|
1810
|
-
if (error) {
|
|
1811
|
-
return {};
|
|
1812
|
-
}
|
|
1813
|
-
return data ? data['indices'] : {};
|
|
1814
|
-
}
|
|
1815
|
-
};
|
|
1816
|
-
const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useLazyGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
|
|
2591
|
+
const { useGetAggMDSQuery, useGetMDSQuery, useGetTagsQuery, useGetDataQuery, useGetCrosswalkDataQuery, useLazyGetCrosswalkDataQuery, useGetIndexAggMDSQuery } = metadataApi;
|
|
1817
2592
|
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
* the first level to avoid potentially flattening
|
|
1821
|
-
* non-nested data.
|
|
1822
|
-
* @param {JSON} json
|
|
1823
|
-
*/ function flattenJson(json) {
|
|
1824
|
-
const flattenedJson = [];
|
|
1825
|
-
Object.keys(json).forEach((key)=>{
|
|
1826
|
-
flattenedJson.push(flatten(json[key], {
|
|
1827
|
-
delimiter: '_'
|
|
1828
|
-
}));
|
|
1829
|
-
});
|
|
1830
|
-
return flattenedJson;
|
|
1831
|
-
}
|
|
1832
|
-
/**
|
|
1833
|
-
* Converts JSON based on a config.
|
|
1834
|
-
* @param {JSON} json
|
|
1835
|
-
* @param {Object} config
|
|
1836
|
-
*/ async function conversion(json, config) {
|
|
1837
|
-
return Papa.unparse(json, config);
|
|
1838
|
-
}
|
|
1839
|
-
/**
|
|
1840
|
-
* Converts JSON to a specified file format.
|
|
1841
|
-
* Defaults to JSON if file format is not supported.
|
|
1842
|
-
* @param {JSON} json
|
|
1843
|
-
* @param {string} format
|
|
1844
|
-
*/ async function jsonToFormat(json, format) {
|
|
1845
|
-
if (Object.keys(FILE_DELIMITERS).includes(format)) {
|
|
1846
|
-
const flatJson = await flattenJson(json);
|
|
1847
|
-
const data = await conversion(flatJson, {
|
|
1848
|
-
delimiter: FILE_DELIMITERS[format]
|
|
1849
|
-
});
|
|
1850
|
-
return data;
|
|
1851
|
-
}
|
|
1852
|
-
return json;
|
|
1853
|
-
}
|
|
2593
|
+
// using a random uuid v4 as the namespace
|
|
2594
|
+
const GEN3_APP_NAMESPACE = '7bfaa818-c69c-457e-8d87-413cf60c25f0';
|
|
1854
2595
|
|
|
2596
|
+
const getGen3AppId = (name, version)=>{
|
|
2597
|
+
const nameVersion = `${name}::${version}`;
|
|
2598
|
+
return v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
2599
|
+
};
|
|
1855
2600
|
/**
|
|
1856
|
-
*
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2601
|
+
* Creates a Gen3App that is dynamically loaded
|
|
2602
|
+
*/ const createGen3App = ({ App, name, version, requiredEntityTypes })=>{
|
|
2603
|
+
// create a stable id for this app
|
|
2604
|
+
const nameVersion = `${name}::${version}`;
|
|
2605
|
+
const id = v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
2606
|
+
// need to create store and provider.
|
|
2607
|
+
// return a component representing this app
|
|
2608
|
+
// if component gets added to a list, then the list can be iterated in index.js and each provider component can be added
|
|
2609
|
+
// a route can be setup for the app
|
|
2610
|
+
// need to register its name, category, path, data requirements
|
|
2611
|
+
// this will be used to build page3
|
|
2612
|
+
// click app link
|
|
2613
|
+
// const store = configureStore({
|
|
2614
|
+
// // TODO allow user to pass in a reducer in CreateGen3AppOptions?
|
|
2615
|
+
// reducer: (state) => state,
|
|
2616
|
+
// devTools: {
|
|
2617
|
+
// name: `${nameVersion}::${id}`,
|
|
2618
|
+
// },
|
|
2619
|
+
// });
|
|
2620
|
+
const Gen3AppWrapper = (props)=>{
|
|
2621
|
+
useEffect(()=>{
|
|
2622
|
+
document.title = `GEN3 - ${name}`;
|
|
2623
|
+
});
|
|
2624
|
+
return /*#__PURE__*/ React__default.createElement(App, props);
|
|
2625
|
+
};
|
|
2626
|
+
// add the app to the store
|
|
2627
|
+
coreStore.dispatch(addGen3AppMetadata({
|
|
2628
|
+
id,
|
|
2629
|
+
name,
|
|
2630
|
+
version,
|
|
2631
|
+
requiredEntityTypes
|
|
2632
|
+
}));
|
|
2633
|
+
registerGen3App(name, Gen3AppWrapper);
|
|
2634
|
+
return Gen3AppWrapper;
|
|
2635
|
+
};
|
|
2636
|
+
// ----------------------------------------------------------------------------------------
|
|
2637
|
+
// Apps with Local Storage
|
|
2638
|
+
//
|
|
2639
|
+
const createAppStore = (options)=>{
|
|
2640
|
+
const { name, version, reducers, middleware } = options;
|
|
2641
|
+
const nameVersion = `${name}::${version}`;
|
|
2642
|
+
const id = v5(nameVersion, GEN3_APP_NAMESPACE);
|
|
2643
|
+
const store = configureStore({
|
|
2644
|
+
reducer: reducers,
|
|
2645
|
+
devTools: {
|
|
2646
|
+
name: `${nameVersion}::${id}`
|
|
2647
|
+
},
|
|
2648
|
+
middleware: (getDefaultMiddleware)=>middleware ? getDefaultMiddleware({
|
|
2649
|
+
serializableCheck: {
|
|
2650
|
+
ignoredActions: [
|
|
2651
|
+
FLUSH,
|
|
2652
|
+
REHYDRATE,
|
|
2653
|
+
PAUSE,
|
|
2654
|
+
PERSIST,
|
|
2655
|
+
PURGE,
|
|
2656
|
+
REGISTER
|
|
2657
|
+
]
|
|
2658
|
+
}
|
|
2659
|
+
}).concat(middleware) : getDefaultMiddleware({
|
|
2660
|
+
serializableCheck: {
|
|
2661
|
+
ignoredActions: [
|
|
2662
|
+
FLUSH,
|
|
2663
|
+
REHYDRATE,
|
|
2664
|
+
PAUSE,
|
|
2665
|
+
PERSIST,
|
|
2666
|
+
PURGE,
|
|
2667
|
+
REGISTER
|
|
2668
|
+
]
|
|
2669
|
+
}
|
|
2670
|
+
})
|
|
2671
|
+
});
|
|
2672
|
+
const context = /*#__PURE__*/ React__default.createContext(null);
|
|
2673
|
+
const useAppSelector = createSelectorHook(context);
|
|
2674
|
+
const useAppDispatch = createDispatchHook(context);
|
|
2675
|
+
const useAppStore = createStoreHook(context);
|
|
1868
2676
|
return {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
},
|
|
1876
|
-
body: JSON.stringify({
|
|
1877
|
-
type: parameters.type,
|
|
1878
|
-
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
1879
|
-
accessibility: parameters.accessibility,
|
|
1880
|
-
fields: parameters?.fields,
|
|
1881
|
-
sort: parameters?.sort
|
|
1882
|
-
})
|
|
2677
|
+
id: id,
|
|
2678
|
+
AppStore: store,
|
|
2679
|
+
AppContext: context,
|
|
2680
|
+
useAppSelector: useAppSelector,
|
|
2681
|
+
useAppDispatch: useAppDispatch,
|
|
2682
|
+
useAppStore: useAppStore
|
|
1883
2683
|
};
|
|
1884
2684
|
};
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
onStart?.();
|
|
1898
|
-
const url = prepareUrl(GEN3_GUPPY_API);
|
|
1899
|
-
const fetchConfig = prepareFetchConfig(parameters, csrfToken);
|
|
1900
|
-
fetch(url.toString(), {
|
|
1901
|
-
...fetchConfig,
|
|
1902
|
-
...signal ? {
|
|
1903
|
-
signal: signal
|
|
1904
|
-
} : {}
|
|
1905
|
-
}).then(async (response)=>{
|
|
1906
|
-
if (!response.ok) {
|
|
1907
|
-
throw new Error(response.statusText);
|
|
1908
|
-
}
|
|
1909
|
-
let jsonData = await response.json();
|
|
1910
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
1911
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
1912
|
-
jsonData = JSONPath({
|
|
1913
|
-
json: jsonData,
|
|
1914
|
-
path: `$.[${parameters.rootPath}]`,
|
|
1915
|
-
resultType: 'value'
|
|
1916
|
-
});
|
|
1917
|
-
}
|
|
1918
|
-
// convert the data to the specified format and return a Blob
|
|
1919
|
-
let str = '';
|
|
1920
|
-
if (parameters.format === 'json') {
|
|
1921
|
-
str = JSON.stringify(jsonData);
|
|
1922
|
-
} else {
|
|
1923
|
-
const convertedData = await jsonToFormat(jsonData, parameters.format);
|
|
1924
|
-
if (isJSONObject(convertedData)) {
|
|
1925
|
-
str = JSON.stringify(convertedData, null, 2);
|
|
1926
|
-
} else {
|
|
1927
|
-
str = convertedData;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
return new Blob([
|
|
1931
|
-
str
|
|
1932
|
-
], {
|
|
1933
|
-
type: 'application/json'
|
|
2685
|
+
const createGen3AppWithOwnStore = (options)=>{
|
|
2686
|
+
const { App, id, name, version, requiredEntityTypes, store, context } = options;
|
|
2687
|
+
// need to create store and provider.
|
|
2688
|
+
// return a component representing this app
|
|
2689
|
+
// if component gets added to a list, then the list can be iterated in index.js and each provider component can be added
|
|
2690
|
+
// a route can be setup for the app
|
|
2691
|
+
// need to register its name, category, path, data requirements
|
|
2692
|
+
// this will be used to build page3
|
|
2693
|
+
// click app link
|
|
2694
|
+
const Gen3AppWrapper = (props)=>{
|
|
2695
|
+
useEffect(()=>{
|
|
2696
|
+
document.title = `GEN3 - ${name}`;
|
|
1934
2697
|
});
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2698
|
+
return /*#__PURE__*/ React__default.createElement(Provider, {
|
|
2699
|
+
store: store,
|
|
2700
|
+
context: context
|
|
2701
|
+
}, /*#__PURE__*/ React__default.createElement(CookiesProvider, null, /*#__PURE__*/ React__default.createElement(App, props)));
|
|
2702
|
+
};
|
|
2703
|
+
// add the app to the store
|
|
2704
|
+
coreStore.dispatch(addGen3AppMetadata({
|
|
2705
|
+
id,
|
|
2706
|
+
name,
|
|
2707
|
+
version,
|
|
2708
|
+
requiredEntityTypes
|
|
2709
|
+
}));
|
|
2710
|
+
registerGen3App(name, Gen3AppWrapper);
|
|
2711
|
+
return Gen3AppWrapper;
|
|
1943
2712
|
};
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
const
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
let jsonData = await response.json();
|
|
1956
|
-
if (parameters?.rootPath && parameters.rootPath) {
|
|
1957
|
-
// if rootPath is provided, extract the data from the rootPath
|
|
1958
|
-
jsonData = JSONPath({
|
|
1959
|
-
json: jsonData,
|
|
1960
|
-
path: `$.[${parameters.rootPath}]`,
|
|
1961
|
-
resultType: 'value'
|
|
1962
|
-
});
|
|
1963
|
-
}
|
|
1964
|
-
// convert the data to the specified format and return a Blob
|
|
1965
|
-
return jsonData;
|
|
1966
|
-
} catch (error) {
|
|
1967
|
-
// Abort is handle as an exception
|
|
1968
|
-
if (error.name == 'AbortError') {
|
|
1969
|
-
// handle abort()
|
|
1970
|
-
onAbort?.();
|
|
2713
|
+
|
|
2714
|
+
const createAppApiForRTKQ = (reducerPath, baseQuery)=>{
|
|
2715
|
+
const appContext = React.createContext(null);
|
|
2716
|
+
const useAppSelector = useSelector.withTypes();
|
|
2717
|
+
const useAppDispatch = createDispatchHook(appContext);
|
|
2718
|
+
const useAppStore = createStoreHook(appContext);
|
|
2719
|
+
const appCreateApi = buildCreateApi(coreModule(), reactHooksModule({
|
|
2720
|
+
hooks: {
|
|
2721
|
+
useDispatch: useAppDispatch,
|
|
2722
|
+
useSelector: useAppSelector,
|
|
2723
|
+
useStore: useAppStore
|
|
1971
2724
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2725
|
+
}));
|
|
2726
|
+
const appRTKQApi = appCreateApi({
|
|
2727
|
+
reducerPath: reducerPath,
|
|
2728
|
+
baseQuery: baseQuery ?? fetchBaseQuery({
|
|
2729
|
+
baseUrl: `${GEN3_API}`,
|
|
2730
|
+
prepareHeaders: (headers)=>{
|
|
2731
|
+
headers.set('Content-Type', 'application/json');
|
|
2732
|
+
let accessToken = undefined;
|
|
2733
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2734
|
+
// NOTE: This cookie can only be accessed from the client side
|
|
2735
|
+
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
2736
|
+
accessToken = getCookie('credentials_token');
|
|
2737
|
+
}
|
|
2738
|
+
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
|
|
2739
|
+
return headers;
|
|
2740
|
+
}
|
|
2741
|
+
}),
|
|
2742
|
+
endpoints: ()=>({})
|
|
2743
|
+
});
|
|
2744
|
+
const appMiddleware = appRTKQApi.middleware;
|
|
2745
|
+
const appStore = configureStore({
|
|
2746
|
+
reducer: {
|
|
2747
|
+
[appRTKQApi.reducerPath]: appRTKQApi.reducer
|
|
2748
|
+
},
|
|
2749
|
+
middleware: (getDefaultMiddleware)=>getDefaultMiddleware({
|
|
2750
|
+
serializableCheck: {
|
|
2751
|
+
ignoredActions: [
|
|
2752
|
+
FLUSH,
|
|
2753
|
+
REHYDRATE,
|
|
2754
|
+
PAUSE,
|
|
2755
|
+
PERSIST,
|
|
2756
|
+
PURGE,
|
|
2757
|
+
REGISTER
|
|
2758
|
+
]
|
|
2759
|
+
}
|
|
2760
|
+
}).concat(appMiddleware)
|
|
2761
|
+
});
|
|
2762
|
+
return {
|
|
2763
|
+
useAppSelector: useAppSelector,
|
|
2764
|
+
useAppDispatch: useAppDispatch,
|
|
2765
|
+
useAppStore: useAppStore,
|
|
2766
|
+
AppContext: appContext,
|
|
2767
|
+
appApi: appRTKQApi,
|
|
2768
|
+
appContext: appContext,
|
|
2769
|
+
appStore: appStore
|
|
2770
|
+
};
|
|
1978
2771
|
};
|
|
1979
2772
|
|
|
2773
|
+
const graphQLWithTags = gen3Api.enhanceEndpoints({
|
|
2774
|
+
addTagTypes: [
|
|
2775
|
+
'graphQL'
|
|
2776
|
+
]
|
|
2777
|
+
});
|
|
1980
2778
|
/**
|
|
1981
|
-
* Creates a
|
|
1982
|
-
* @see https://github.com/uc-cdis/guppy/blob/master/doc/
|
|
1983
|
-
* @param
|
|
1984
|
-
*
|
|
1985
|
-
|
|
1986
|
-
*/ const downloadRequestApi = gen3Api.injectEndpoints({
|
|
2779
|
+
* Creates a graphQLAPI for graphql queries to elasticsearch indices via guppy
|
|
2780
|
+
* @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md
|
|
2781
|
+
* @param query - Resolver function which configures the graphql query with graphQLParams argument
|
|
2782
|
+
* @returns: A guppy search API for fetching metadata
|
|
2783
|
+
*/ const graphQLAPI = graphQLWithTags.injectEndpoints({
|
|
1987
2784
|
endpoints: (builder)=>({
|
|
1988
|
-
|
|
1989
|
-
query: (
|
|
1990
|
-
|
|
1991
|
-
filter: convertFilterSetToGqlFilter(filter),
|
|
1992
|
-
...{
|
|
1993
|
-
type,
|
|
1994
|
-
accessibility,
|
|
1995
|
-
fields,
|
|
1996
|
-
sort
|
|
1997
|
-
}
|
|
1998
|
-
};
|
|
1999
|
-
return {
|
|
2000
|
-
url: `${GEN3_GUPPY_API}/download`,
|
|
2785
|
+
graphQL: builder.query({
|
|
2786
|
+
query: (graphQLParams)=>({
|
|
2787
|
+
url: `${GEN3_GUPPY_API}/graphql`,
|
|
2001
2788
|
method: 'POST',
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
}
|
|
2005
|
-
},
|
|
2006
|
-
transformResponse: (response)=>{
|
|
2007
|
-
return response;
|
|
2008
|
-
}
|
|
2789
|
+
credentials: 'include',
|
|
2790
|
+
body: JSON.stringify(graphQLParams)
|
|
2791
|
+
})
|
|
2009
2792
|
})
|
|
2010
2793
|
})
|
|
2011
2794
|
});
|
|
2012
|
-
const {
|
|
2795
|
+
const { useGraphQLQuery } = graphQLAPI;
|
|
2013
2796
|
|
|
2014
2797
|
/**
|
|
2015
2798
|
* returns a response from the AI search service
|
|
@@ -2439,5 +3222,5 @@ const { useGetProjectsQuery, useGetSubmissionGraphQLQuery, useGetProjectsDetails
|
|
|
2439
3222
|
});
|
|
2440
3223
|
const { useGetSowerJobListQuery, useLazyGetSowerJobListQuery, useSubmitSowerJobMutation, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery } = loadingStatusApi;
|
|
2441
3224
|
|
|
2442
|
-
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, Modals, PodConditionType, PodStatus, WorkspaceStatus, clearActiveWorkspaceId, clearCohortFilters, cohortReducer, convertFilterSetToGqlFilter, coreStore, createAppApiForRTKQ, createAppStore, createGen3App, createGen3AppWithOwnStore, createUseCoreDataHook, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, drsHostnamesReducer, extractEnumFilterValue, extractFieldNameFromFullFieldName, extractFilterValue, extractIndexAndFieldNameFromFullFieldName, extractIndexFromFullFieldName, fetchFence, fetchJson, fetchUserState, fieldNameToTitle, gen3Api, getGen3AppId, graphQLAPI, graphQLWithTags, guppyAPISliceMiddleware, guppyApi, guppyApiReducer, guppyApiSliceReducerPath, handleOperation, hideModal, isAuthenticated, isErrorWithMessage, isFetchBaseQueryError, isFetchParseError, isFilterEmpty, isGuppyAggregationData, isHistogramData, isHistogramDataAArray, isHistogramDataAnEnum, isHistogramDataArray, isHistogramDataArrayARange, isHistogramDataArrayAnEnum, isHistogramDataCollection, isHistogramRangeData, isHttpStatusError, isJSONObject, isJSONValue, isJSONValueArray, isOperationWithField, isPending, isProgramUrl, isRootUrl, isWorkspaceActive, isWorkspaceRunningOrStopping, listifyMethodsFromMapping, logoutFence, prependIndexToFieldName, projectCodeFromResourcePath, rawDataQueryStrForEachField, removeCohortFilter, resetUserState, resourcePathFromProjectID, selectActiveWorkspaceId, selectActiveWorkspaceStatus, selectAuthzMappingData, selectCSRFToken, selectCSRFTokenData, selectCohortFilters, selectCurrentCohort, selectCurrentCohortId, selectCurrentCohortName, selectCurrentMessage, selectCurrentModal, selectGen3AppByName, selectGen3AppMetadataByName, selectHeadersWithCSRFToken, selectIndexFilters, selectIndexedFilterByName, selectRequestedWorkspaceStatus, selectUser, selectUserAuthStatus, selectUserData, selectUserDetails, selectUserLoginStatus, selectWorkspaceStatus, selectWorkspaceStatusFromService, setActiveWorkspace, setActiveWorkspaceId, setActiveWorkspaceStatus, setCohortFilter, setDRSHostnames, setRequestedWorkspaceStatus, showModal, submissionApi, trimFirstFieldNameToTitle, updateCohortFilter, useAddNewCredentialMutation, useAskQuestionMutation, useAuthorizeFromCredentialsMutation, useCoreDispatch, useCoreSelector, useDownloadFromGuppyMutation, useFetchUserDetailsQuery, useGeneralGQLQuery, useGetAISearchStatusQuery, useGetAISearchVersionQuery, useGetAccessibleDataQuery, useGetActivePayModelQuery, useGetAggMDSQuery, useGetAggsQuery, useGetAllFieldsForTypeQuery, useGetArrayTypes, useGetAuthzMappingsQuery, useGetCSRFQuery, useGetCountsQuery, useGetCredentialsQuery, useGetCrosswalkDataQuery, useGetDataQuery, useGetDictionaryQuery, useGetExternalLoginsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetIndexAggMDSQuery, useGetIndexFields, useGetJWKKeysQuery, useGetLoginProvidersQuery, useGetMDSQuery, useGetProjectsDetailsQuery, useGetProjectsQuery, useGetRawDataAndTotalCountsQuery, useGetSowerJobListQuery, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery, useGetStatus, useGetSubAggsQuery, useGetSubmissionGraphQLQuery, useGetSubmissionsQuery, useGetTagsQuery, useGetWorkspaceOptionsQuery, useGetWorkspacePayModelsQuery, useGetWorkspaceStatusQuery, useGraphQLQuery, useIsExternalConnectedQuery, useIsUserLoggedIn, useLaunchWorkspaceMutation, useLazyFetchUserDetailsQuery, useLazyGeneralGQLQuery, useLazyGetAggsQuery, useLazyGetCrosswalkDataQuery, useLazyGetExternalLoginsQuery, useLazyGetProjectsQuery, useLazyGetSowerJobListQuery, useLazyGetSubmissionGraphQLQuery, useLazyIsExternalConnectedQuery, usePrevious, useRemoveCredentialMutation, useSetCurrentPayModelMutation, useSubmitSowerJobMutation, useTerminateWorkspaceMutation, useUser, useUserAuth, userHasCreateOrUpdateOnAnyProject, userHasDataUpload, userHasMethodForServiceOnProject, userHasMethodForServiceOnResource, userHasMethodOnAnyProject, userHasSheepdogProgramAdmin, userHasSheepdogProjectAdmin };
|
|
3225
|
+
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, HTTPErrorMessages, Modals, PodConditionType, PodStatus, WorkspaceStatus, clearActiveWorkspaceId, clearCohortFilters, clearDataLibrarySelection, cohortReducer, convertFilterSetToGqlFilter, coreStore, createAppApiForRTKQ, createAppStore, createGen3App, createGen3AppWithOwnStore, createUseCoreDataHook, dataLibrarySelectionReducer, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, drsHostnamesReducer, extractEnumFilterValue, extractFieldNameFromFullFieldName, extractFilterValue, extractIndexAndFieldNameFromFullFieldName, extractIndexFromFullFieldName, fetchFence, fetchFencePresignedURL, fetchJson, fetchUserState, fieldNameToTitle, gen3Api, getGen3AppId, getNumberOfItemsInDatalist, getTimestamp, graphQLAPI, graphQLWithTags, guppyAPISliceMiddleware, guppyApi, guppyApiReducer, guppyApiSliceReducerPath, handleOperation, hideModal, isAdditionalDataItem, isArray, isAuthenticated, isCohortItem, isErrorWithMessage, isFetchBaseQueryError, isFetchParseError, isFileItem, isFilterEmpty, isFilterSet, isGuppyAggregationData, isHistogramData, isHistogramDataAArray, isHistogramDataAnEnum, isHistogramDataArray, isHistogramDataArrayARange, isHistogramDataArrayAnEnum, isHistogramDataCollection, isHistogramRangeData, isHttpStatusError, isJSONObject, isJSONValue, isJSONValueArray, isNotDefined, isObject, isOperationWithField, isPending, isProgramUrl, isRootUrl, isString, isWorkspaceActive, isWorkspaceRunningOrStopping, listifyMethodsFromMapping, logoutFence, prepareUrl, prependIndexToFieldName, processHistogramResponse, projectCodeFromResourcePath, rawDataQueryStrForEachField, removeCohortFilter, resetUserState, resourcePathFromProjectID, selectActiveWorkspaceId, selectActiveWorkspaceStatus, selectAuthzMappingData, selectCSRFToken, selectCSRFTokenData, selectCohortFilters, selectCurrentCohort, selectCurrentCohortId, selectCurrentCohortName, selectCurrentMessage, selectCurrentModal, selectGen3AppByName, selectGen3AppMetadataByName, selectHeadersWithCSRFToken, selectIndexFilters, selectIndexedFilterByName, selectRequestedWorkspaceStatus, selectUser, selectUserAuthStatus, selectUserData, selectUserDetails, selectUserLoginStatus, selectWorkspaceStatus, selectWorkspaceStatusFromService, setActiveWorkspace, setActiveWorkspaceId, setActiveWorkspaceStatus, setCohortFilter, setDRSHostnames, setDataLibraryListSelection, setRequestedWorkspaceStatus, showModal, submissionApi, trimFirstFieldNameToTitle, updateCohortFilter, useAddDataLibraryListMutation, useAddNewCredentialMutation, useAskQuestionMutation, useAuthorizeFromCredentialsMutation, useCoreDispatch, useCoreSelector, useDataLibrary, useDeleteDataLibraryListMutation, useDownloadFromGuppyMutation, useFetchUserDetailsQuery, useGeneralGQLQuery, useGetAISearchStatusQuery, useGetAISearchVersionQuery, useGetAccessibleDataQuery, useGetActivePayModelQuery, useGetAggMDSQuery, useGetAggsQuery, useGetAllFieldsForTypeQuery, useGetArrayTypes, useGetAuthzMappingsQuery, useGetCSRFQuery, useGetCountsQuery, useGetCredentialsQuery, useGetCrosswalkDataQuery, useGetDataLibraryListQuery, useGetDataLibraryListsQuery, useGetDataQuery, useGetDictionaryQuery, useGetDownloadQuery, useGetExternalLoginsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetIndexAggMDSQuery, useGetIndexFields, useGetJWKKeysQuery, useGetLoginProvidersQuery, useGetMDSQuery, useGetProjectsDetailsQuery, useGetProjectsQuery, useGetRawDataAndTotalCountsQuery, 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 };
|
|
2443
3226
|
//# sourceMappingURL=index.js.map
|