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