@gen3/core 0.10.44

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.
Files changed (86) hide show
  1. package/dist/cjs/index.js +2243 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/dts/api.d.ts +8 -0
  4. package/dist/dts/constants.d.ts +32 -0
  5. package/dist/dts/dataAccess.d.ts +35 -0
  6. package/dist/dts/features/aiSearch/aiSearchSlice.d.ts +38 -0
  7. package/dist/dts/features/aiSearch/index.d.ts +2 -0
  8. package/dist/dts/features/app/store.d.ts +6 -0
  9. package/dist/dts/features/authz/authzMappingSlice.d.ts +141 -0
  10. package/dist/dts/features/authz/index.d.ts +3 -0
  11. package/dist/dts/features/authz/types.d.ts +5 -0
  12. package/dist/dts/features/cohort/cohortBuilderConfigSlice.d.ts +0 -0
  13. package/dist/dts/features/cohort/cohortSlice.d.ts +95 -0
  14. package/dist/dts/features/cohort/index.d.ts +2 -0
  15. package/dist/dts/features/cohort/types.d.ts +0 -0
  16. package/dist/dts/features/download/constants.d.ts +14 -0
  17. package/dist/dts/features/download/downloadStatusApi.d.ts +19 -0
  18. package/dist/dts/features/download/index.d.ts +2 -0
  19. package/dist/dts/features/download/types.d.ts +20 -0
  20. package/dist/dts/features/drsResolver/drsHostnameSlice.d.ts +5 -0
  21. package/dist/dts/features/drsResolver/index.d.ts +2 -0
  22. package/dist/dts/features/drsResolver/resolvers/cachedDRSResolver.d.ts +1 -0
  23. package/dist/dts/features/drsResolver/resolvers/dataGUIDSDotOrg.d.ts +6 -0
  24. package/dist/dts/features/drsResolver/resolvers/tests/dataGUIDSDotOrg.unit.test.d.ts +1 -0
  25. package/dist/dts/features/drsResolver/types.d.ts +0 -0
  26. package/dist/dts/features/drsResolver/utils.d.ts +6 -0
  27. package/dist/dts/features/fence/credentialsApi.d.ts +35 -0
  28. package/dist/dts/features/fence/fenceApi.d.ts +37 -0
  29. package/dist/dts/features/fence/index.d.ts +6 -0
  30. package/dist/dts/features/fence/jwtApi.d.ts +12 -0
  31. package/dist/dts/features/fence/types.d.ts +12 -0
  32. package/dist/dts/features/fence/utils.d.ts +12 -0
  33. package/dist/dts/features/filters/filters.d.ts +122 -0
  34. package/dist/dts/features/filters/index.d.ts +4 -0
  35. package/dist/dts/features/filters/tests/utils.unit.test.d.ts +1 -0
  36. package/dist/dts/features/filters/types.d.ts +113 -0
  37. package/dist/dts/features/filters/utils.d.ts +26 -0
  38. package/dist/dts/features/gen3/gen3Api.d.ts +11 -0
  39. package/dist/dts/features/gen3/index.d.ts +2 -0
  40. package/dist/dts/features/gen3Apps/Gen3App.d.ts +48 -0
  41. package/dist/dts/features/gen3Apps/gen3AppRegistry.d.ts +7 -0
  42. package/dist/dts/features/gen3Apps/gen3AppsSlice.d.ts +19 -0
  43. package/dist/dts/features/gen3Apps/index.d.ts +3 -0
  44. package/dist/dts/features/graphQL/graphQLSlice.d.ts +12 -0
  45. package/dist/dts/features/graphQL/index.d.ts +1 -0
  46. package/dist/dts/features/guppy/conversion.d.ts +23 -0
  47. package/dist/dts/features/guppy/guppyApi.d.ts +41 -0
  48. package/dist/dts/features/guppy/guppyDownloadSlice.d.ts +21 -0
  49. package/dist/dts/features/guppy/guppySlice.d.ts +120 -0
  50. package/dist/dts/features/guppy/index.d.ts +6 -0
  51. package/dist/dts/features/guppy/tests/downloadFromGuppy.unit.test.d.ts +1 -0
  52. package/dist/dts/features/guppy/types.d.ts +33 -0
  53. package/dist/dts/features/guppy/utils.d.ts +28 -0
  54. package/dist/dts/features/metadata/index.d.ts +3 -0
  55. package/dist/dts/features/metadata/metadataSlice.d.ts +56 -0
  56. package/dist/dts/features/metadata/types.d.ts +12 -0
  57. package/dist/dts/features/modals/index.d.ts +2 -0
  58. package/dist/dts/features/modals/modalsSlice.d.ts +26 -0
  59. package/dist/dts/features/submission/authMappingUtils.d.ts +15 -0
  60. package/dist/dts/features/submission/index.d.ts +3 -0
  61. package/dist/dts/features/submission/submissionApi.d.ts +43 -0
  62. package/dist/dts/features/submission/types.d.ts +46 -0
  63. package/dist/dts/features/user/externalLoginsSlice.d.ts +14 -0
  64. package/dist/dts/features/user/hooks.d.ts +18 -0
  65. package/dist/dts/features/user/index.d.ts +5 -0
  66. package/dist/dts/features/user/test/useGetExternalLoginsQuery.unit.test.d.ts +1 -0
  67. package/dist/dts/features/user/types.d.ts +50 -0
  68. package/dist/dts/features/user/userSlice.d.ts +53 -0
  69. package/dist/dts/features/user/userSliceRTK.d.ts +484 -0
  70. package/dist/dts/features/workspace/index.d.ts +2 -0
  71. package/dist/dts/features/workspace/types.d.ts +0 -0
  72. package/dist/dts/features/workspace/workspacesSlice.d.ts +6 -0
  73. package/dist/dts/hooks.d.ts +36 -0
  74. package/dist/dts/index.d.ts +23 -0
  75. package/dist/dts/provider.d.ts +2 -0
  76. package/dist/dts/reducers.d.ts +22 -0
  77. package/dist/dts/store.d.ts +53 -0
  78. package/dist/dts/store.unit.test.d.ts +2 -0
  79. package/dist/dts/types/index.d.ts +51 -0
  80. package/dist/dts/utils/extractvalues.d.ts +9 -0
  81. package/dist/dts/utils/index.d.ts +2 -0
  82. package/dist/dts/utils/ts-utils.d.ts +4 -0
  83. package/dist/esm/index.js +2083 -0
  84. package/dist/esm/index.js.map +1 -0
  85. package/dist/index.d.ts +1604 -0
  86. package/package.json +70 -0
@@ -0,0 +1,2243 @@
1
+ 'use strict';
2
+
3
+ var toolkit = require('@reduxjs/toolkit');
4
+ var react = require('@reduxjs/toolkit/query/react');
5
+ var React = require('react');
6
+ var reactRedux = require('react-redux');
7
+ var react$1 = require('@reduxjs/toolkit/dist/query/react');
8
+ var cookiesNext = require('cookies-next');
9
+ var query = require('@reduxjs/toolkit/query');
10
+ var lodash = require('lodash');
11
+ var Queue = require('queue');
12
+ var jsonpathPlus = require('jsonpath-plus');
13
+ var uuid = require('uuid');
14
+ require('redux-persist');
15
+ var reactCookie = require('react-cookie');
16
+ var useSWR = require('swr');
17
+ var flat = require('flat');
18
+ var Papa = require('papaparse');
19
+
20
+ const GEN3_COMMONS_NAME = process.env.GEN3_COMMONS_NAME || 'gen3';
21
+ const GEN3_API = process.env.NEXT_PUBLIC_GEN3_API || '';
22
+ const GEN3_DOMAIN = process.env.NEXT_PUBLIC_GEN3_DOMAIN || '';
23
+ /**
24
+ * Service Specific Constants
25
+ */ const GEN3_GUPPY_API = process.env.NEXT_PUBLIC_GEN3_GUPPY_API || `${GEN3_API}/guppy`;
26
+ const GEN3_MDS_API = process.env.NEXT_PUBLIC_GEN3_MDS_API || `${GEN3_API}/mds`;
27
+ const GEN3_DOWNLOADS_ENDPOINT = process.env.NEXT_PUBLIC_GEN3_DOWNLOADS_ENDPOINT || 'downloads';
28
+ const GEN3_FENCE_API = process.env.NEXT_PUBLIC_GEN3_FENCE_API || GEN3_API;
29
+ const GEN3_AI_SEARCH_API = process.env.NEXT_PUBLIC_GEN3_AI_SEARCH_API || `${GEN3_API}/ai-search`;
30
+ const GEN3_AUTHZ_API = process.env.NEXT_PUBLIC_GEN3_AUTHZ_API || `${GEN3_API}/authz`;
31
+ const GEN3_REDIRECT_URL = process.env.NEXT_PUBLIC_GEN3_REDIRECT_URL || GEN3_API;
32
+ const GEN3_WORKSPACE_STATUS_API = process.env.NEXT_PUBLIC_GEN3_WORKSPACE_STATUS_API || `${GEN3_API}/lw-workspace`;
33
+ const GEN3_SUBMISSION_API = process.env.NEXT_PUBLIC_GEN3_SUBMISSION_API || `${GEN3_API}/api/v0/submission`;
34
+ const GEN3_WTS_API = process.env.NEXT_PUBLIC_GEN3_WTS_API || `${GEN3_API}/wts`;
35
+ const GEN3_CROSSWALK_API = process.env.NEXT_PUBLIC_GEN3_CROSSWALK_API || `${GEN3_API}/mds`;
36
+ exports.Accessibility = void 0;
37
+ (function(Accessibility) {
38
+ Accessibility["ACCESSIBLE"] = "accessible";
39
+ Accessibility["UNACCESSIBLE"] = "unaccessible";
40
+ Accessibility["ALL"] = "all";
41
+ })(exports.Accessibility || (exports.Accessibility = {}));
42
+ const FILE_DELIMITERS = {
43
+ tsv: '\t',
44
+ csv: ','
45
+ };
46
+
47
+ // From here down is react-related code. If we wanted to create a UI-agnotic core,
48
+ // we could need to move the following code and the provider into a new workspace,
49
+ // such as core-react.
50
+ /**
51
+ * The initial context is never used in practice. A little casting voodoo to satisfy TS.
52
+ *
53
+ * Note: Should the action type be AnyAction (from redux) or PayloadAction (from redux-toolkit)?
54
+ * If we are creating all of our actions through RTK, then PayloadAction might be the
55
+ * correct opinionated type.
56
+ */ const CoreContext = React.createContext(undefined);
57
+ const useCoreSelector = reactRedux.createSelectorHook(CoreContext);
58
+ const useCoreDispatch = reactRedux.createDispatchHook(CoreContext);
59
+ const useCoreStore = reactRedux.createStoreHook(CoreContext);
60
+
61
+ /**
62
+ * Creates a custom Redux Toolkit core API
63
+ * See: https://redux-toolkit.js.org/rtk-query/usage/customizing-create-api
64
+ * @returns: created core API.
65
+ */ const coreCreateApi = react.buildCreateApi(react.coreModule(), react.reactHooksModule({
66
+ useSelector: useCoreSelector,
67
+ useStore: useCoreStore,
68
+ useDispatch: useCoreDispatch
69
+ }));
70
+
71
+ /**
72
+ * Template for fence error response dict
73
+ * @returns: An error dict response from a RESTFUL API request
74
+ */ const buildFetchError = async (res, request)=>{
75
+ return {
76
+ url: res.url,
77
+ status: res.status,
78
+ statusText: res.statusText,
79
+ text: await res.text(),
80
+ request: request
81
+ };
82
+ };
83
+ /**
84
+ * Template for a standard fence request
85
+ * @returns: response data
86
+ */ const fetchFence = async ({ endpoint, headers, body = {}, method = 'GET', isJSON = true })=>{
87
+ const res = await fetch(`${GEN3_FENCE_API}${endpoint}`, {
88
+ method: method,
89
+ headers: headers,
90
+ body: 'POST' === method ? JSON.stringify(body) : null
91
+ });
92
+ if (res.ok) return {
93
+ data: isJSON ? await res.json() : await res.text(),
94
+ status: res.status
95
+ };
96
+ throw await buildFetchError(res, {
97
+ endpoint,
98
+ method,
99
+ headers,
100
+ body
101
+ });
102
+ };
103
+
104
+ const userAuthApi = coreCreateApi({
105
+ reducerPath: 'userAuthApi',
106
+ refetchOnMountOrArgChange: 1800,
107
+ refetchOnReconnect: true,
108
+ baseQuery: async ({ endpoint }, { getState })=>{
109
+ let results;
110
+ const csrfToken = selectCSRFToken(getState());
111
+ let accessToken = undefined;
112
+ if (process.env.NODE_ENV === 'development') {
113
+ accessToken = cookiesNext.getCookie('credentials_token');
114
+ }
115
+ const headers = {
116
+ Accept: 'application/json',
117
+ 'Content-Type': 'application/json',
118
+ ...csrfToken ? {
119
+ 'X-CSRF-Token': csrfToken
120
+ } : {},
121
+ ...accessToken ? {
122
+ Authorization: `Bearer ${accessToken}`
123
+ } : {},
124
+ credentials: 'include'
125
+ };
126
+ try {
127
+ results = await fetchFence({
128
+ endpoint,
129
+ headers
130
+ });
131
+ } catch (e) {
132
+ /*
133
+ Because an "error" response is valid for the auth requests we don't want to
134
+ put the request in an error state, or it will attempt the request over and over again
135
+ */ return {
136
+ data: {}
137
+ };
138
+ }
139
+ return {
140
+ data: results
141
+ };
142
+ },
143
+ endpoints: (builder)=>({
144
+ fetchUserDetails: builder.query({
145
+ query: ()=>({
146
+ endpoint: '/user/user'
147
+ }),
148
+ transformResponse (response) {
149
+ return {
150
+ data: response.data,
151
+ // TODO: check if this is the correct status code
152
+ loginStatus: response.status === 200 && response.data?.username ? 'authenticated' : 'unauthenticated'
153
+ };
154
+ }
155
+ }),
156
+ getCSRF: builder.query({
157
+ query: ()=>({
158
+ endpoint: '/_status'
159
+ }),
160
+ transformResponse: (response)=>{
161
+ return {
162
+ csrfToken: response?.data?.csrf ?? ''
163
+ };
164
+ }
165
+ })
166
+ })
167
+ });
168
+ const EMPTY_USER = {
169
+ username: undefined
170
+ };
171
+ const { useFetchUserDetailsQuery, useLazyFetchUserDetailsQuery, useGetCSRFQuery } = userAuthApi;
172
+ const userAuthApiMiddleware = userAuthApi.middleware;
173
+ const userAuthApiReducerPath = userAuthApi.reducerPath;
174
+ const userAuthApiReducer = userAuthApi.reducer;
175
+ const selectUserDetailsFromState = userAuthApi.endpoints.fetchUserDetails.select();
176
+ const selectUserDetails = toolkit.createSelector(selectUserDetailsFromState, (userDetails)=>userDetails?.data?.data ?? EMPTY_USER);
177
+ const selectUserAuthStatus = toolkit.createSelector(selectUserDetailsFromState, (userLoginState)=>userLoginState.status === query.QueryStatus.pending ? 'pending' : userLoginState.status === query.QueryStatus.uninitialized ? 'not present' : userLoginState?.data?.loginStatus ?? 'unauthenticated');
178
+ const selectCSRFTokenData = userAuthApi.endpoints.getCSRF.select();
179
+ const passThroughTheState = (state)=>state.gen3Services;
180
+ const selectCSRFToken = toolkit.createSelector([
181
+ selectCSRFTokenData,
182
+ passThroughTheState
183
+ ], (state)=>state?.data?.csrfToken);
184
+ const selectHeadersWithCSRFToken = toolkit.createSelector([
185
+ selectCSRFToken,
186
+ passThroughTheState
187
+ ], (csrfToken)=>({
188
+ Accept: 'application/json',
189
+ 'Content-Type': 'application/json',
190
+ ...csrfToken && {
191
+ 'X-CSRF-Token': csrfToken
192
+ }
193
+ }));
194
+
195
+ /**
196
+ * Creates a base class core API for building other API endpoints on top of.
197
+ * @param reducerPath - The root key name that the other slices will be derived from
198
+ * @param baseQuery: - The template query which the slices will addon to
199
+ * @param endpoints - Base API endpoints that should exist in every slice
200
+ * @returns: The generated base API
201
+ */ const gen3Api = coreCreateApi({
202
+ reducerPath: 'gen3Services',
203
+ baseQuery: react$1.fetchBaseQuery({
204
+ baseUrl: `${GEN3_API}`,
205
+ prepareHeaders: (headers, { getState })=>{
206
+ const csrfToken = selectCSRFToken(getState());
207
+ headers.set('Content-Type', 'application/json');
208
+ let accessToken = undefined;
209
+ if (process.env.NODE_ENV === 'development') {
210
+ // NOTE: This cookie can only be accessed from the client side
211
+ // in development mode. Otherwise, the cookie is set as httpOnly
212
+ accessToken = cookiesNext.getCookie('credentials_token');
213
+ }
214
+ if (csrfToken) headers.set('X-CSRF-Token', csrfToken);
215
+ if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
216
+ return headers;
217
+ }
218
+ }),
219
+ endpoints: ()=>({})
220
+ });
221
+ const gen3ServicesReducer = gen3Api.reducer;
222
+ const gen3ServicesReducerMiddleware = gen3Api.middleware;
223
+
224
+ /**
225
+ * Creates a fence API endpoint for handling login processes
226
+ * @param endpoints - defined endpoint query for logging in
227
+ * @returns: The generated fence login API slice
228
+ */ const loginProvidersApi = gen3Api.injectEndpoints({
229
+ endpoints: (builder)=>({
230
+ getLoginProviders: builder.query({
231
+ query: ()=>`${GEN3_FENCE_API}/user/login`
232
+ })
233
+ })
234
+ });
235
+ const { useGetLoginProvidersQuery } = loginProvidersApi;
236
+ /**
237
+ * Logout from fence
238
+ */ const logoutFence = async (redirect = '/')=>await fetchFence({
239
+ endpoint: `${GEN3_FENCE_API}/user/logout?next=${GEN3_REDIRECT_URL}${redirect}`,
240
+ method: 'GET'
241
+ });
242
+
243
+ // extending the gen3API to add a tag to the endpoints
244
+ const credentialsWithTags$1 = gen3Api.enhanceEndpoints({
245
+ addTagTypes: [
246
+ 'Credentials'
247
+ ]
248
+ });
249
+ /**
250
+ * Adds a credentialsApi into the base gen3Api
251
+ * @endpoints Includes get, add, and remove credential operations
252
+ * @see https://github.com/uc-cdis/fence/blob/master/openapis/swagger.yaml#L972-L1033
253
+ * @param getCredentials - List all the API keys for the current user
254
+ * @param addNewCredential - Get a new API key for the current user
255
+ * @param removeCredential - Delete API access key with given ID for current user
256
+ * @returns: A fence credential API for manipulating user credentials
257
+ */ const credentialsApi = credentialsWithTags$1.injectEndpoints({
258
+ endpoints: (builder)=>({
259
+ getCredentials: builder.query({
260
+ query: ()=>`${GEN3_FENCE_API}/user/credentials/api`,
261
+ transformResponse: (response)=>response['jtis'],
262
+ // "jtis", which is an array of API keys
263
+ // no need to transform the response, since the API returns the correct format
264
+ providesTags: [
265
+ 'Credentials'
266
+ ]
267
+ }),
268
+ addNewCredential: builder.mutation({
269
+ query: (csrfToken)=>({
270
+ url: `${GEN3_FENCE_API}/user/credentials/api`,
271
+ method: 'POST',
272
+ headers: {
273
+ 'Content-Type': 'application/json',
274
+ 'x-csrf-token': csrfToken
275
+ },
276
+ body: {
277
+ scope: [
278
+ 'user',
279
+ 'data'
280
+ ]
281
+ }
282
+ }),
283
+ invalidatesTags: [
284
+ 'Credentials'
285
+ ]
286
+ }),
287
+ removeCredential: builder.mutation({
288
+ query: ({ csrfToken, id })=>({
289
+ url: `${GEN3_FENCE_API}/user/credentials/api/${id}`,
290
+ method: 'DELETE',
291
+ headers: {
292
+ 'Content-Type': 'application/json',
293
+ ...csrfToken && {
294
+ 'x-csrf-token': csrfToken
295
+ }
296
+ }
297
+ }),
298
+ invalidatesTags: [
299
+ 'Credentials'
300
+ ]
301
+ }),
302
+ authorizeFromCredentials: builder.mutation({
303
+ query: (params)=>({
304
+ url: `${GEN3_FENCE_API}/user/credentials/api/access_token`,
305
+ method: 'POST',
306
+ headers: {
307
+ 'Content-Type': 'application/json'
308
+ },
309
+ body: {
310
+ api_key: params.api_key,
311
+ key_id: params.key_id
312
+ }
313
+ })
314
+ })
315
+ })
316
+ });
317
+ const { useGetCredentialsQuery, useAddNewCredentialMutation, useRemoveCredentialMutation, useAuthorizeFromCredentialsMutation } = credentialsApi;
318
+
319
+ // extending the gen3API to add a tag to the endpoints
320
+ const credentialsWithTags = gen3Api.enhanceEndpoints({
321
+ addTagTypes: [
322
+ 'fenceJWT'
323
+ ]
324
+ });
325
+ /**
326
+ * A fence API for getting the public keys which can be used to validate
327
+ * JWTs issued and signed by fence
328
+ * @returns: Fence public keys
329
+ */ const jwtApi = credentialsWithTags.injectEndpoints({
330
+ endpoints: (builder)=>({
331
+ getJWKKeys: builder.query({
332
+ query: ()=>'user/jwt/keys',
333
+ providesTags: [
334
+ 'fenceJWT'
335
+ ]
336
+ })
337
+ })
338
+ });
339
+ const { useGetJWKKeysQuery } = jwtApi;
340
+
341
+ const usePrevious = (value)=>{
342
+ const ref = React.useRef();
343
+ React.useEffect(()=>{
344
+ ref.current = value;
345
+ });
346
+ return ref.current;
347
+ };
348
+ const createUseCoreDataHook = (fetchDataActionCreator, dataSelector)=>{
349
+ return (...params)=>{
350
+ const coreDispatch = useCoreDispatch();
351
+ const { data, status, error } = useCoreSelector(dataSelector);
352
+ const action = fetchDataActionCreator(...params);
353
+ const prevParams = usePrevious(params);
354
+ React.useEffect(()=>{
355
+ if (status === 'uninitialized' || !lodash.isEqual(prevParams, params)) {
356
+ // createDispatchHook types forces the input to AnyAction, which is
357
+ // not compatible with thunk actions. hence, the `as any` cast. ;(
358
+ coreDispatch(action); // eslint-disable-line
359
+ }
360
+ }, [
361
+ status,
362
+ coreDispatch,
363
+ action,
364
+ params,
365
+ prevParams
366
+ ]);
367
+ return {
368
+ data,
369
+ error,
370
+ isUninitialized: status === 'uninitialized',
371
+ isFetching: status === 'pending',
372
+ isSuccess: status === 'fulfilled',
373
+ isError: status === 'rejected'
374
+ };
375
+ };
376
+ };
377
+
378
+ /**
379
+ * Creates an async thunk for fetching user permissions details from fence
380
+ * @see https://redux-toolkit.js.org/api/createAsyncThunk
381
+ * @returns: A fence response dict containing user details
382
+ */ const fetchUserState = toolkit.createAsyncThunk('fence/user/user', async (_, meta)=>{
383
+ // Get an access token from a cookie if in development mode
384
+ const csrfToken = selectCSRFToken(meta.getState());
385
+ let accessToken = undefined;
386
+ if (process.env.NODE_ENV === 'development') {
387
+ accessToken = cookiesNext.getCookie('credentials_token');
388
+ }
389
+ return await fetchFence({
390
+ endpoint: '/user/user',
391
+ method: 'GET',
392
+ headers: {
393
+ Accept: 'application/json',
394
+ 'Content-Type': 'application/json',
395
+ ...csrfToken ? {
396
+ 'X-CSRF-Token': csrfToken
397
+ } : {},
398
+ credentials: 'include',
399
+ ...accessToken ? {
400
+ Authorization: `Bearer ${accessToken}`
401
+ } : {}
402
+ }
403
+ });
404
+ });
405
+ const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
406
+ const isPending = (loginStatus)=>loginStatus === 'pending';
407
+ const initialState$3 = {
408
+ status: 'uninitialized',
409
+ loginStatus: 'unauthenticated',
410
+ error: undefined
411
+ };
412
+ /**
413
+ * Wraps a slice on top of fetchUserState async thunk to keep track of
414
+ * query state. authenticated/not-authenticated vs. ejected/fulfilled/pending
415
+ * @returns: status messages wrapped around fetchUserState response dict
416
+ */ const slice$3 = toolkit.createSlice({
417
+ name: 'fence/user',
418
+ initialState: initialState$3,
419
+ reducers: {
420
+ resetUserState: ()=>initialState$3
421
+ },
422
+ extraReducers: (builder)=>{
423
+ builder.addCase(fetchUserState.fulfilled, (_, action)=>{
424
+ const response = action.payload;
425
+ if (response.status !== 200) {
426
+ return {
427
+ status: 'rejected',
428
+ loginStatus: 'unauthenticated'
429
+ };
430
+ }
431
+ return {
432
+ data: {
433
+ ...response.data
434
+ },
435
+ status: 'fulfilled',
436
+ loginStatus: 'authenticated'
437
+ };
438
+ }).addCase(fetchUserState.pending, ()=>{
439
+ return {
440
+ status: 'pending',
441
+ loginStatus: 'unauthenticated'
442
+ };
443
+ }).addCase(fetchUserState.rejected, ()=>{
444
+ return {
445
+ status: 'rejected',
446
+ loginStatus: 'unauthenticated'
447
+ };
448
+ });
449
+ }
450
+ });
451
+ const userReducer = slice$3.reducer;
452
+ const { resetUserState } = slice$3.actions;
453
+ const selectUserData = (state)=>{
454
+ return state.user;
455
+ };
456
+ const selectUser = (state)=>state.user;
457
+ const selectUserLoginStatus = (state)=>state.user.loginStatus;
458
+ const useUser = createUseCoreDataHook(fetchUserState, selectUserData);
459
+ const useIsUserLoggedIn = ()=>{
460
+ return useCoreSelector((state)=>isAuthenticated(selectUserLoginStatus(state)));
461
+ };
462
+ /**
463
+ * Hook to return get the authenticated state of the user and if logged in,
464
+ * the user's profile and access api.
465
+ * Note that if fetchUserState gets called, the user's session is renewed.
466
+ */ const useUserAuth = (renew = false)=>{
467
+ const coreDispatch = useCoreDispatch();
468
+ const { data, status, loginStatus, error } = useCoreSelector(selectUserData);
469
+ React.useEffect(()=>{
470
+ if (status === 'uninitialized' || renew) {
471
+ // TODO: need to determine what other states require dispatch
472
+ coreDispatch(fetchUserState());
473
+ }
474
+ }, [
475
+ status,
476
+ coreDispatch,
477
+ renew
478
+ ]);
479
+ return {
480
+ data: data,
481
+ error,
482
+ loginStatus,
483
+ isUninitialized: status === 'uninitialized',
484
+ isFetching: status === 'pending',
485
+ isSuccess: status === 'fulfilled',
486
+ isError: status === 'rejected'
487
+ };
488
+ };
489
+
490
+ /**
491
+ * @description Creates a externalLoginApi for listing the configured identity providers
492
+ * in workspace token service. Includes user token expiration time.
493
+ * @see https://github.com/uc-cdis/workspace-token-service/tree/master
494
+ */ const externalLoginApi = gen3Api.injectEndpoints({
495
+ endpoints: (builder)=>({
496
+ getExternalLogins: builder.query({
497
+ query: ()=>({
498
+ url: `${GEN3_WTS_API}/external_oidc/`
499
+ })
500
+ }),
501
+ isExternalConnected: builder.query({
502
+ query: (idp)=>({
503
+ url: `${GEN3_WTS_API}/oauth2/connected?idp=${idp}`
504
+ }),
505
+ transformResponse: ()=>{
506
+ return true; // if success then connected is true
507
+ }
508
+ })
509
+ })
510
+ });
511
+ const { useGetExternalLoginsQuery, useLazyGetExternalLoginsQuery, useLazyIsExternalConnectedQuery, useIsExternalConnectedQuery } = externalLoginApi;
512
+
513
+ // type guard functions
514
+ const isHistogramRangeData = (key)=>{
515
+ return Array.isArray(key) && key.length === 2 && key.every((item)=>typeof item === 'number');
516
+ };
517
+ const isJSONObject = (data)=>{
518
+ return typeof data === 'object' && data !== null && !Array.isArray(data);
519
+ };
520
+ const isJSONValue = (data)=>{
521
+ return typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || Array.isArray(data) && data.every(isJSONValue) || isJSONObject(data);
522
+ };
523
+ const isJSONValueArray = (data)=>{
524
+ return Array.isArray(data) && data.every(isJSONValue);
525
+ };
526
+ const isValidObject = (input)=>typeof input === 'object' && input !== null;
527
+ const isHistogramData = (data)=>{
528
+ return isValidObject(data) && 'key' in data && 'count' in data;
529
+ };
530
+ const isHistogramDataArray = (input)=>{
531
+ if (!isValidObject(input) || !Array.isArray(input.histogram)) {
532
+ return false;
533
+ }
534
+ return input.histogram.every(isHistogramData);
535
+ };
536
+ const isHistogramDataCollection = (obj)=>{
537
+ return isValidObject(obj) && 'histogram' in obj && isHistogramData(obj.histogram);
538
+ };
539
+ // Type guard function for GuppyAggregationData interface
540
+ const isGuppyAggregationData = (obj)=>{
541
+ if (!isValidObject(obj)) return false;
542
+ for(const key in obj){
543
+ if (!isHistogramDataCollection(obj[key])) {
544
+ return false;
545
+ }
546
+ }
547
+ return true;
548
+ };
549
+ const isHistogramDataAnEnum = (data)=>{
550
+ return typeof data === 'object' && data !== null && 'key' in data && 'count' in data && typeof data.key === 'string' && typeof data.count === 'number';
551
+ };
552
+ const isHistogramDataAArray = (data)=>{
553
+ return Array.isArray(data) && data.every(isHistogramData);
554
+ };
555
+ const isHistogramDataArrayAnEnum = (data)=>{
556
+ return Array.isArray(data) && data.every(isHistogramDataAnEnum);
557
+ };
558
+ const isHistogramDataArrayARange = (data)=>{
559
+ return Array.isArray(data) && data.every((item)=>isHistogramRangeData(item.key));
560
+ };
561
+ /**
562
+ * Type predicate to narrow an unknown error to `FetchBaseQueryError`
563
+ */ function isFetchBaseQueryError(error) {
564
+ return typeof error === 'object' && error != null && 'status' in error;
565
+ }
566
+ /**
567
+ * Type predicate to narrow an unknown error to an object with a string 'message' property
568
+ */ function isErrorWithMessage(error) {
569
+ return typeof error === 'object' && error != null && 'message' in error && typeof error.message === 'string';
570
+ }
571
+ /**
572
+ * Type predicate to narrow an unknown error to an object with a string 'message' property
573
+ */ function isFetchParseError(error) {
574
+ return typeof error === 'object' && error != null && 'originalStatus' in error && 'status' in error && error['status'] === 'PARSING_ERROR';
575
+ }
576
+
577
+ /**
578
+ * A registry for the Gen3 Apps.
579
+ */ const REGISTRY = {};
580
+ const registerGen3App = (id, gen3App)=>{
581
+ REGISTRY[id] = gen3App;
582
+ };
583
+ const lookupGen3App = (id)=>{
584
+ return REGISTRY[id];
585
+ };
586
+
587
+ const initialState$2 = {
588
+ gen3Apps: {}
589
+ };
590
+ const slice$2 = toolkit.createSlice({
591
+ name: 'gen3Apps',
592
+ initialState: initialState$2,
593
+ reducers: {
594
+ addGen3AppMetadata: (state, action)=>{
595
+ const { id, requiredEntityTypes } = action.payload;
596
+ state.gen3Apps[id] = {
597
+ ...action.payload,
598
+ // need to turn a ReadonlyArray into a mutable array for immer's WritableDraft
599
+ requiredEntityTypes: [
600
+ ...requiredEntityTypes
601
+ ]
602
+ };
603
+ }
604
+ }
605
+ });
606
+ const gen3AppReducer = slice$2.reducer;
607
+ const { addGen3AppMetadata } = slice$2.actions;
608
+ const selectGen3AppMetadataById = (state, appId)=>state.gen3Apps.gen3Apps[appId];
609
+ const selectGen3AppById = (appId)=>lookupGen3App(appId); // TODO: memoize this selector
610
+
611
+ const initialState$1 = {};
612
+ // TODO: document what this does
613
+ const slice$1 = toolkit.createSlice({
614
+ name: 'drsResolver',
615
+ initialState: initialState$1,
616
+ reducers: {
617
+ setDRSHostnames: (_state, action)=>{
618
+ return action.payload;
619
+ }
620
+ }
621
+ });
622
+ const drsHostnamesReducer = slice$1.reducer;
623
+ const { setDRSHostnames } = slice$1.actions;
624
+
625
+ exports.Modals = void 0;
626
+ (function(Modals) {
627
+ Modals["FirstTimeModal"] = "FirstTimeModal";
628
+ Modals["SessionExpireModal"] = "SessionExpireModal";
629
+ Modals["NoAccessModal"] = "NoAccessModal";
630
+ Modals["CreateCredentialsAPIKeyModal"] = "CreateCredentialsAPIKeyModal";
631
+ Modals["GeneralErrorModal"] = "GeneralErrorModal";
632
+ })(exports.Modals || (exports.Modals = {}));
633
+ const initialState = {
634
+ currentModal: null
635
+ };
636
+ //Creates a modal slice for tracking showModal and hideModal state.
637
+ const slice = toolkit.createSlice({
638
+ name: 'modals',
639
+ initialState,
640
+ reducers: {
641
+ showModal: (state, action)=>{
642
+ state.currentModal = action.payload.modal;
643
+ state.message = action.payload.message;
644
+ return state;
645
+ },
646
+ hideModal: (state)=>{
647
+ state.currentModal = null;
648
+ return state;
649
+ }
650
+ }
651
+ });
652
+ const modalReducer = slice.reducer;
653
+ const { showModal, hideModal } = slice.actions;
654
+ const selectCurrentModal = (state)=>state.modals.currentModal;
655
+ const selectCurrentMessage = (state)=>state.modals.message;
656
+
657
+ const initialCohortState = {
658
+ cohort: {
659
+ id: 'default',
660
+ name: 'Filters',
661
+ filters: {},
662
+ modified_datetime: new Date().toISOString()
663
+ }
664
+ };
665
+ // TODO: start using this adapter
666
+ /*
667
+ const cohortsAdapter = createEntityAdapter<Cohort>({
668
+ sortComparer: (a, b) => {
669
+ if (a.modified_datetime <= b.modified_datetime) return 1;
670
+ else return -1;
671
+ },
672
+ });
673
+ */ /**
674
+ * Redux slice for cohort filters
675
+ */ const cohortSlice = toolkit.createSlice({
676
+ name: 'cohort',
677
+ initialState: initialCohortState,
678
+ reducers: {
679
+ // adds a filter to the cohort filter set at the given index
680
+ updateCohortFilter: (state, action)=>{
681
+ const { index, field, filter } = action.payload;
682
+ return {
683
+ cohort: {
684
+ ...state.cohort,
685
+ filters: {
686
+ ...state.cohort.filters,
687
+ [index]: {
688
+ mode: state.cohort.filters?.[index]?.mode ?? 'and',
689
+ root: {
690
+ ...state.cohort.filters?.[index]?.root ?? {},
691
+ [field]: filter
692
+ }
693
+ }
694
+ }
695
+ }
696
+ };
697
+ },
698
+ // removes a filter to the cohort filter set at the given index
699
+ removeCohortFilter: (state, action)=>{
700
+ const { index, field } = action.payload;
701
+ const filters = state.cohort?.filters?.[index]?.root;
702
+ if (!filters) {
703
+ return;
704
+ }
705
+ const { [field]: _a, ...updated } = filters;
706
+ return {
707
+ cohort: {
708
+ ...state.cohort,
709
+ filters: {
710
+ ...state.cohort.filters,
711
+ [index]: {
712
+ mode: state.cohort.filters[index].mode,
713
+ root: updated
714
+ }
715
+ }
716
+ }
717
+ };
718
+ },
719
+ // removes all filters from the cohort filter set at the given index
720
+ clearCohortFilters: (state, action)=>{
721
+ const { index } = action.payload;
722
+ return {
723
+ cohort: {
724
+ ...state.cohort,
725
+ filters: {
726
+ ...state.cohort.filters,
727
+ [index]: {
728
+ // empty filter set
729
+ mode: 'and',
730
+ root: {}
731
+ }
732
+ }
733
+ }
734
+ };
735
+ }
736
+ },
737
+ extraReducers: {}
738
+ });
739
+ // Filter actions: addFilter, removeFilter, updateFilter
740
+ const { updateCohortFilter, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
741
+ const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
742
+ const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
743
+ const selectCurrentCohort = (state)=>state.cohorts.cohort;
744
+ const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
745
+ /**
746
+ * Select a filter by its name from the current cohort. If the filter is not found
747
+ * returns undefined.
748
+ * @param state - Core
749
+ * @param index which cohort index to select from
750
+ * @param name name of the filter to select
751
+ */ const selectIndexedFilterByName = (state, index, name)=>{
752
+ return state.cohorts.cohort.filters[index]?.root[name];
753
+ };
754
+ const EmptyFilterSet = {
755
+ mode: 'and',
756
+ root: {}
757
+ };
758
+ /**
759
+ * Select a filter from the index.
760
+ * returns undefined.
761
+ * @param state - Core
762
+ * @param index which cohort index to select from
763
+ */ const selectIndexFilters = (state, index)=>{
764
+ return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
765
+ };
766
+ const cohortReducer = cohortSlice.reducer;
767
+
768
+ /**
769
+ * Creates a base class core API for guppy API calls.
770
+ * @returns: guppy core API with guppyAPIFetch base query
771
+ */ const guppyApi = coreCreateApi({
772
+ reducerPath: 'guppy',
773
+ // TODO: refactor to use fetchBaseQuery
774
+ baseQuery: async (query, api)=>{
775
+ const csrfToken = selectCSRFToken(api.getState());
776
+ let accessToken = undefined;
777
+ if (process.env.NODE_ENV === 'development') {
778
+ // NOTE: This cookie can only be accessed from the client side
779
+ // in development mode. Otherwise, the cookie is set as httpOnly
780
+ accessToken = cookiesNext.getCookie('credentials_token');
781
+ }
782
+ const headers = {
783
+ Accept: 'application/json',
784
+ 'Content-Type': 'application/json',
785
+ 'Access-Control-Allow-Origin': '*',
786
+ ...csrfToken && {
787
+ 'X-CSRF-Token': csrfToken
788
+ },
789
+ ...accessToken && {
790
+ Authorization: `Bearer ${accessToken}`
791
+ }
792
+ };
793
+ try {
794
+ const response = await fetch(`${GEN3_GUPPY_API}/graphql`, {
795
+ headers: headers,
796
+ method: 'POST',
797
+ body: JSON.stringify(query)
798
+ });
799
+ return {
800
+ data: await response.json()
801
+ };
802
+ } catch (e) {
803
+ if (e instanceof Error) return {
804
+ error: e.message
805
+ };
806
+ return {
807
+ error: e
808
+ };
809
+ }
810
+ },
811
+ endpoints: ()=>({})
812
+ });
813
+ const guppyAPISliceMiddleware = guppyApi.middleware;
814
+ const guppyApiSliceReducerPath = guppyApi.reducerPath;
815
+ const guppyApiReducer = guppyApi.reducer;
816
+
817
+ const rootReducer = toolkit.combineReducers({
818
+ gen3Services: gen3ServicesReducer,
819
+ user: userReducer,
820
+ gen3Apps: gen3AppReducer,
821
+ drsHostnames: drsHostnamesReducer,
822
+ modals: modalReducer,
823
+ cohorts: cohortReducer,
824
+ [guppyApiSliceReducerPath]: guppyApiReducer,
825
+ [userAuthApiReducerPath]: userAuthApiReducer
826
+ });
827
+
828
+ const coreStore = toolkit.configureStore({
829
+ reducer: rootReducer,
830
+ middleware: (getDefaultMiddleware)=>getDefaultMiddleware().concat(gen3ServicesReducerMiddleware, guppyAPISliceMiddleware, userAuthApiMiddleware)
831
+ });
832
+ query.setupListeners(coreStore.dispatch);
833
+
834
+ const CoreProvider = ({ children })=>{
835
+ return /*#__PURE__*/ React.createElement(reactRedux.Provider, {
836
+ store: coreStore,
837
+ context: CoreContext
838
+ }, children);
839
+ };
840
+
841
+ /**
842
+ * Creates the authzApi for checking arborist permissions for a selected user
843
+ * @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/arborist/master/docs/openapi.yaml#/auth/get_auth_mapping
844
+ * @see https://github.com/uc-cdis/arborist/blob/master/docs/relationships.simplified.png
845
+ * @returns: An arborist response dict of user permissions {method, service} for each resource path.
846
+ */ const authzApi = gen3Api.injectEndpoints({
847
+ endpoints: (builder)=>({
848
+ getAuthzMappings: builder.query({
849
+ query: ()=>`${GEN3_AUTHZ_API}/mapping`
850
+ })
851
+ })
852
+ });
853
+ const { useGetAuthzMappingsQuery } = authzApi;
854
+ const selectAuthzMapping = authzApi.endpoints.getAuthzMappings.select();
855
+ const selectAuthzMappingData = toolkit.createSelector(selectAuthzMapping, (authzMapping)=>authzMapping?.data ?? {
856
+ mappings: []
857
+ });
858
+
859
+ const HasEnoughData = (data, keys, limit)=>{
860
+ const numEmptyKeys = keys.filter((k)=>Object.hasOwn(data, k) && typeof data[k] === 'string' && data[k].trim() === '').length;
861
+ return numEmptyKeys < limit;
862
+ };
863
+ /**
864
+ * Defines metadataApi service using a base URL and expected endpoints. Derived from gen3Api core API.
865
+ *
866
+ * @param endpoints - Defines endpoints used in discovery page
867
+ * @param getAggMDS - Queries aggregate metadata service
868
+ * @see https://github.com/uc-cdis/metadata-service/blob/master/docs/agg_mds.md
869
+ * @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
870
+ * @param getMDS - Queries normal metadata service
871
+ * @see https://petstore.swagger.io/?url=https://raw.githubusercontent.com/uc-cdis/metadata-service/master/docs/openapi.yaml#/Query/search_metadata_metadata_get
872
+ * @param getIndexAggMDS - queries the Aggregate Metadata service and returns all common passed in indexKeys
873
+ * @param getTags - Probably refering to Aggregate metadata service summary statistics query
874
+ * @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
875
+ * @param getData - Looks like a duplicate of getMDS handler. unused in ./frontend package
876
+ * @param getCrosswalkData - Maps ids from one source to another
877
+ * @returns: A guppy download API for fetching bulk metadata
878
+ */ const metadataApi = gen3Api.injectEndpoints({
879
+ endpoints: (builder)=>({
880
+ getAggMDS: builder.query({
881
+ query: ({ offset, pageSize })=>{
882
+ return `${GEN3_MDS_API}/aggregate/metadata?flatten=true&pagination=true&offset=${offset}&limit=${pageSize}`;
883
+ },
884
+ transformResponse: (response, _meta, params)=>{
885
+ return {
886
+ data: response.results.map((x)=>{
887
+ const objValues = Object.values(x);
888
+ const firstValue = objValues ? objValues.at(0) : undefined;
889
+ return firstValue ? firstValue[params.studyField] : undefined;
890
+ }),
891
+ hits: response.pagination.hits
892
+ };
893
+ }
894
+ }),
895
+ getIndexAggMDS: builder.query({
896
+ query: ({ pageSize })=>{
897
+ return `${GEN3_MDS_API}/aggregate/metadata?limit=${pageSize}`;
898
+ },
899
+ transformResponse: (response, _meta, params)=>{
900
+ const dataFromIndexes = params.indexKeys.reduce((acc, key)=>{
901
+ if (response[key]) {
902
+ acc.push(...response[key]);
903
+ }
904
+ return acc;
905
+ }, []);
906
+ return {
907
+ data: dataFromIndexes.map((x)=>{
908
+ const objValues = Object.values(x);
909
+ const objIds = Object.keys(x);
910
+ let firstValue = objValues ? objValues.at(0) : undefined;
911
+ if (params?.filterEmpty) {
912
+ // remove any data that has < limit if defined
913
+ if (firstValue && !HasEnoughData(firstValue[params.studyField], params.filterEmpty.keys, params.filterEmpty.limit)) firstValue = undefined;
914
+ }
915
+ return firstValue ? {
916
+ gen3MDSGUID: objIds.at(0),
917
+ ...firstValue[params.studyField]
918
+ } : undefined;
919
+ }).filter((x)=>x !== undefined),
920
+ hits: dataFromIndexes.length
921
+ };
922
+ }
923
+ }),
924
+ getMDS: builder.query({
925
+ query: ({ guidType, offset, pageSize })=>{
926
+ return `${GEN3_MDS_API}/metadata?data=True&_guid_type=${guidType}&limit=${pageSize}&offset=${offset}`;
927
+ },
928
+ transformResponse: (response, _meta)=>{
929
+ return {
930
+ data: Object.keys(response).map((guid)=>response[guid]),
931
+ hits: Object.keys(response).length
932
+ };
933
+ }
934
+ }),
935
+ getTags: builder.query({
936
+ query: ()=>'tags'
937
+ }),
938
+ getData: builder.query({
939
+ query: (params)=>({
940
+ url: `metadata?${params}`
941
+ })
942
+ }),
943
+ // TODO: Move this to own slice
944
+ getCrosswalkData: builder.query({
945
+ queryFn: async (arg, _queryApi, _extraOptions, fetchWithBQ)=>{
946
+ const queryMultiple = async ()=>{
947
+ let result = [];
948
+ const queue = Queue({
949
+ concurrency: 15
950
+ });
951
+ for (const id of arg.ids){
952
+ queue.push(async (callback)=>{
953
+ const response = await fetchWithBQ({
954
+ url: `${GEN3_CROSSWALK_API}/metadata/${id}`
955
+ });
956
+ if (response.error) {
957
+ return {
958
+ error: response.error
959
+ };
960
+ }
961
+ const toData = arg.toPaths.reduce((acc, path)=>{
962
+ acc[path.id] = jsonpathPlus.JSONPath({
963
+ json: response.data,
964
+ path: `$.[${path.dataPath}]`,
965
+ resultType: 'value'
966
+ })?.[0] ?? 'n/a';
967
+ return acc;
968
+ }, {});
969
+ result = [
970
+ ...result,
971
+ {
972
+ from: id,
973
+ to: toData
974
+ }
975
+ ];
976
+ callback && callback();
977
+ return result;
978
+ });
979
+ }
980
+ return new Promise((resolve, reject)=>{
981
+ queue.start((err)=>{
982
+ if (err) {
983
+ reject(err);
984
+ } else {
985
+ resolve(result);
986
+ }
987
+ });
988
+ });
989
+ };
990
+ const result = await queryMultiple();
991
+ return {
992
+ data: result
993
+ };
994
+ }
995
+ })
996
+ })
997
+ });
998
+ const { useGetAggMDSQuery, useGetMDSQuery, useGetTagsQuery, useGetDataQuery, useGetCrosswalkDataQuery, useLazyGetCrosswalkDataQuery, useGetIndexAggMDSQuery } = metadataApi;
999
+
1000
+ // using a random uuid v4 as the namespace
1001
+ const GEN3_APP_NAMESPACE = '7bfaa818-c69c-457e-8d87-413cf60c25f0';
1002
+ const getGen3AppId = (name, version)=>{
1003
+ const nameVersion = `${name}::${version}`;
1004
+ return uuid.v5(nameVersion, GEN3_APP_NAMESPACE);
1005
+ };
1006
+ /**
1007
+ * TODO: can't tell what anything in this directory is doing.
1008
+ */ const createGen3App = ({ App, name, version, requiredEntityTypes })=>{
1009
+ // create a stable id for this app
1010
+ const nameVersion = `${name}::${version}`;
1011
+ const id = uuid.v5(nameVersion, GEN3_APP_NAMESPACE);
1012
+ // need to create store and provider.
1013
+ // return a component representing this app
1014
+ // if component gets added to a list, then the list can be iterated in index.js and each provider component can be added
1015
+ // a route can be setup for the app
1016
+ // need to register its name, category, path, data requirements
1017
+ // this will be used to build page3
1018
+ // click app link
1019
+ const store = toolkit.configureStore({
1020
+ // TODO allow user to pass in a reducer in CreateGen3AppOptions?
1021
+ reducer: (state)=>state,
1022
+ devTools: {
1023
+ name: `${nameVersion}::${id}`
1024
+ }
1025
+ });
1026
+ const Gen3AppWrapper = ()=>{
1027
+ React.useEffect(()=>{
1028
+ document.title = `GEN3 - ${name}`;
1029
+ });
1030
+ return /*#__PURE__*/ React.createElement(reactRedux.Provider, {
1031
+ store: store
1032
+ }, /*#__PURE__*/ React.createElement(reactCookie.CookiesProvider, null, /*#__PURE__*/ React.createElement(App, null)));
1033
+ };
1034
+ // add the app to the store
1035
+ coreStore.dispatch(addGen3AppMetadata({
1036
+ id,
1037
+ name,
1038
+ version,
1039
+ requiredEntityTypes
1040
+ }));
1041
+ registerGen3App(id, Gen3AppWrapper);
1042
+ return Gen3AppWrapper;
1043
+ };
1044
+
1045
+ const graphQLWithTags = gen3Api.enhanceEndpoints({
1046
+ addTagTypes: [
1047
+ 'graphQL'
1048
+ ]
1049
+ });
1050
+ /**
1051
+ * Creates a graphQLAPI for graphql queries to elasticsearch indices via guppy
1052
+ * @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md
1053
+ * @param query - Resolver function which configures the graphql query with graphQLParams argument
1054
+ * @returns: A guppy search API for fetching metadata
1055
+ */ const graphQLAPI = graphQLWithTags.injectEndpoints({
1056
+ endpoints: (builder)=>({
1057
+ graphQL: builder.query({
1058
+ query: (graphQLParams)=>({
1059
+ url: `${GEN3_GUPPY_API}/graphql`,
1060
+ method: 'POST',
1061
+ credentials: 'include',
1062
+ body: JSON.stringify(graphQLParams)
1063
+ })
1064
+ })
1065
+ })
1066
+ });
1067
+ const { useGraphQLQuery } = graphQLAPI;
1068
+
1069
+ const isOperationWithField = (operation)=>{
1070
+ return operation?.field !== undefined;
1071
+ };
1072
+ const extractFilterValue = (op)=>{
1073
+ const valueExtractorHandler = new ValueExtractorHandler();
1074
+ return handleOperation(valueExtractorHandler, op);
1075
+ };
1076
+ const extractEnumFilterValue = (op)=>{
1077
+ const enumValueExtractorHandler = new EnumValueExtractorHandler();
1078
+ const results = handleOperation(enumValueExtractorHandler, op);
1079
+ return results ?? [];
1080
+ };
1081
+ const assertNever = (x)=>{
1082
+ throw Error(`Exhaustive comparison did not handle: ${x}`);
1083
+ };
1084
+ const handleOperation = (handler, op)=>{
1085
+ switch(op.operator){
1086
+ case '=':
1087
+ return handler.handleEquals(op);
1088
+ case '!=':
1089
+ return handler.handleNotEquals(op);
1090
+ case '<':
1091
+ return handler.handleLessThan(op);
1092
+ case '<=':
1093
+ return handler.handleLessThanOrEquals(op);
1094
+ case '>':
1095
+ return handler.handleGreaterThan(op);
1096
+ case '>=':
1097
+ return handler.handleGreaterThanOrEquals(op);
1098
+ case 'and':
1099
+ return handler.handleIntersection(op);
1100
+ case 'or':
1101
+ return handler.handleUnion(op);
1102
+ case 'nested':
1103
+ return handler.handleNestedFilter(op);
1104
+ case 'in':
1105
+ return handler.handleIncludes(op);
1106
+ case 'excludeifany':
1107
+ return handler.handleExcludeIfAny(op);
1108
+ case 'excludes':
1109
+ return handler.handleExcludes(op);
1110
+ default:
1111
+ return assertNever(op);
1112
+ }
1113
+ };
1114
+ /**
1115
+ * Return true if a FilterSet's root value is an empty object
1116
+ * @param fs - FilterSet to test
1117
+ */ const isFilterEmpty = (fs)=>lodash.isEqual({}, fs);
1118
+ class ToGqlHandler {
1119
+ constructor(){
1120
+ this.handleEquals = (op)=>({
1121
+ '=': {
1122
+ [op.field]: op.operand
1123
+ }
1124
+ });
1125
+ this.handleNotEquals = (op)=>({
1126
+ '!=': {
1127
+ [op.field]: op.operand
1128
+ }
1129
+ });
1130
+ this.handleLessThan = (op)=>({
1131
+ '<': {
1132
+ [op.field]: op.operand
1133
+ }
1134
+ });
1135
+ this.handleLessThanOrEquals = (op)=>({
1136
+ '<=': {
1137
+ [op.field]: op.operand
1138
+ }
1139
+ });
1140
+ this.handleGreaterThan = (op)=>({
1141
+ '>': {
1142
+ [op.field]: op.operand
1143
+ }
1144
+ });
1145
+ this.handleGreaterThanOrEquals = (op)=>({
1146
+ '>=': {
1147
+ [op.field]: op.operand
1148
+ }
1149
+ });
1150
+ this.handleIncludes = (op)=>({
1151
+ in: {
1152
+ [op.field]: op.operands
1153
+ }
1154
+ });
1155
+ this.handleExcludes = (op)=>({
1156
+ exclude: {
1157
+ [op.field]: op.operands
1158
+ }
1159
+ });
1160
+ this.handleExcludeIfAny = (op)=>({
1161
+ excludeifany: {
1162
+ [op.field]: op.operands
1163
+ }
1164
+ });
1165
+ this.handleIntersection = (op)=>({
1166
+ and: op.operands.map((x)=>convertFilterToGqlFilter(x))
1167
+ });
1168
+ this.handleUnion = (op)=>({
1169
+ or: op.operands.map((x)=>convertFilterToGqlFilter(x))
1170
+ });
1171
+ this.handleNestedFilter = (op)=>{
1172
+ const child = convertFilterToGqlFilter(op.operand);
1173
+ return {
1174
+ nested: {
1175
+ path: op.path,
1176
+ ...child
1177
+ }
1178
+ };
1179
+ };
1180
+ }
1181
+ }
1182
+ const convertFilterToGqlFilter = (filter)=>{
1183
+ const handler = new ToGqlHandler();
1184
+ return handleOperation(handler, filter);
1185
+ };
1186
+ const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
1187
+ const fsKeys = Object.keys(fs.root);
1188
+ // if no keys return undefined
1189
+ if (fsKeys.length === 0) return {
1190
+ and: []
1191
+ };
1192
+ return toplevelOp === 'and' ? {
1193
+ and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1194
+ } : {
1195
+ or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1196
+ };
1197
+ };
1198
+ /**
1199
+ * Extract the operand values, if operands themselves have values, otherwise undefined.
1200
+ */ class ValueExtractorHandler {
1201
+ constructor(){
1202
+ this.handleEquals = (op)=>op.operand;
1203
+ this.handleNotEquals = (op)=>op.operand;
1204
+ this.handleIncludes = (op)=>op.operands;
1205
+ this.handleExcludes = (op)=>op.operands;
1206
+ this.handleExcludeIfAny = (op)=>op.operands;
1207
+ this.handleGreaterThanOrEquals = (op)=>op.operand;
1208
+ this.handleGreaterThan = (op)=>op.operand;
1209
+ this.handleLessThan = (op)=>op.operand;
1210
+ this.handleLessThanOrEquals = (op)=>op.operand;
1211
+ this.handleIntersection = (_)=>undefined;
1212
+ this.handleUnion = (_)=>undefined;
1213
+ this.handleNestedFilter = (_)=>undefined;
1214
+ }
1215
+ }
1216
+ /**
1217
+ * Extract the operand values, if operands themselves have values, otherwise undefined.
1218
+ */ class EnumValueExtractorHandler {
1219
+ constructor(){
1220
+ this.handleEquals = (_)=>undefined;
1221
+ this.handleNotEquals = (_)=>undefined;
1222
+ this.handleIncludes = (op)=>op.operands;
1223
+ this.handleExcludes = (op)=>op.operands;
1224
+ this.handleExcludeIfAny = (op)=>op.operands;
1225
+ this.handleGreaterThanOrEquals = (_)=>undefined;
1226
+ this.handleGreaterThan = (_)=>undefined;
1227
+ this.handleLessThan = (_)=>undefined;
1228
+ this.handleLessThanOrEquals = (_)=>undefined;
1229
+ this.handleIntersection = (_)=>undefined;
1230
+ this.handleUnion = (_)=>undefined;
1231
+ this.handleNestedFilter = (op)=>{
1232
+ return extractEnumFilterValue(op.operand);
1233
+ };
1234
+ }
1235
+ }
1236
+
1237
+ const FieldNameOverrides = {};
1238
+ const COMMON_PREPOSITIONS = [
1239
+ 'a',
1240
+ 'an',
1241
+ 'and',
1242
+ 'at',
1243
+ 'but',
1244
+ 'by',
1245
+ 'for',
1246
+ 'in',
1247
+ 'is',
1248
+ 'nor',
1249
+ 'of',
1250
+ 'on',
1251
+ 'or',
1252
+ 'out',
1253
+ 'so',
1254
+ 'the',
1255
+ 'to',
1256
+ 'up',
1257
+ 'yet'
1258
+ ];
1259
+ const capitalize = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
1260
+ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
1261
+ if (trim) {
1262
+ const source = fieldName.slice(fieldName.indexOf('.') + 1);
1263
+ return fieldNameToTitle(source ? source : fieldName, 0);
1264
+ }
1265
+ return fieldNameToTitle(fieldName);
1266
+ };
1267
+ /**
1268
+ * Converts a filter name to a title,
1269
+ * For example files.input.experimental_strategy will get converted to Experimental Strategy
1270
+ * if sections == 2 then the output would be Input Experimental Strategy
1271
+ * @param fieldName input filter expected to be: string.firstpart_secondpart
1272
+ * @param sections number of "sections" string.string.string to got back from the end of the field
1273
+ */ const fieldNameToTitle = (fieldName, sections = 1)=>{
1274
+ if (fieldName in FieldNameOverrides) {
1275
+ return FieldNameOverrides[fieldName];
1276
+ }
1277
+ if (fieldName === undefined) return 'No Title';
1278
+ return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize(word)).join(' ');
1279
+ };
1280
+ /**
1281
+ * Extracts the index name from the field name
1282
+ * @param fieldName
1283
+ */ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
1284
+ /**
1285
+ * prepend the index name to the field name
1286
+ */ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
1287
+ /**
1288
+ * extract the field name from the index.field name
1289
+ */ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
1290
+ /**
1291
+ * extract the field name and the index from the index.field name returning as a tuple
1292
+ */ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
1293
+ const [index, ...rest] = fieldName.split('.');
1294
+ return [
1295
+ index,
1296
+ rest.join('.')
1297
+ ];
1298
+ };
1299
+
1300
+ const statusEndpoint = '/_status';
1301
+ const processHistogramResponse = (data)=>{
1302
+ const pathData = jsonpathPlus.JSONPath({
1303
+ json: data,
1304
+ path: '$..histogram',
1305
+ resultType: 'all'
1306
+ });
1307
+ const results = pathData.reduce((acc, element)=>{
1308
+ const key = element.pointer.slice(1).replace(/\/histogram/g, '').replace(/\//g, '.');
1309
+ return {
1310
+ ...acc,
1311
+ [key]: element.value
1312
+ };
1313
+ }, {});
1314
+ return results;
1315
+ };
1316
+ const fetchJson = async (url)=>{
1317
+ const res = await fetch(url, {
1318
+ method: 'GET',
1319
+ headers: {
1320
+ 'Content-type': 'application/json'
1321
+ }
1322
+ });
1323
+ if (!res.ok) throw new Error('An error occurred while fetching the data.');
1324
+ return await res.json();
1325
+ };
1326
+ const useGetStatus = ()=>{
1327
+ const fetcher = ()=>fetchJson(`${GEN3_GUPPY_API}${statusEndpoint}`);
1328
+ return useSWR('explorerStatus', fetcher);
1329
+ };
1330
+ /**
1331
+ * The main endpoint used in templating Exploration page queries.
1332
+ * Includes table, filter and aggregation query types and leverages guppyApi defined in ./gupplApi.ts
1333
+ * Query templates support filters where applicable
1334
+ *
1335
+ * @param endpoints - Defines endpoints used in Exploration page:
1336
+ * @param getAllFieldsForType - A mapping query that returns all property key names vertex types specified.
1337
+ * @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#mapping-query
1338
+ * @param getAccessibleData - An aggregation histogram counts query that filters based on access type
1339
+ * @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#accessibility-argument-for-regular-tier-access-level
1340
+ * @param getRawDataAndTotalCounts - Queries both _totalCount for selected vertex types and
1341
+ * tabular results containing the raw data in the rows of selected vertex types
1342
+ * @see https://github.com/uc-cdis/guppy/blob/master/doc/queries.md#1-total-count-aggregation
1343
+ * @param getAggs - An aggregated histogram counts query which outputs vertex property frequencies
1344
+ * @param getSubAggs - TODO: not sure what this one does. Looks like nested aggregation
1345
+ * @param getCounts - Returns total counts of a vertex type
1346
+ * @returns: A guppy API endpoint for templating queriable data displayed on the exploration page
1347
+ */ const explorerApi = guppyApi.injectEndpoints({
1348
+ endpoints: (builder)=>({
1349
+ getAllFieldsForType: builder.query({
1350
+ query: (type)=>({
1351
+ query: `{ _mapping ${type} } }`
1352
+ }),
1353
+ transformResponse: (response, _meta, params)=>{
1354
+ return response[params.type];
1355
+ }
1356
+ }),
1357
+ getAccessibleData: builder.query({
1358
+ query: ({ type, fields, accessType })=>{
1359
+ const fieldParts = fields.map((field)=>`${field} { histogram { key count } }`);
1360
+ return {
1361
+ query: `_aggregation {
1362
+ ${type} (accessibility: ${accessType}) {
1363
+ ${fieldParts.join(',')}
1364
+ }
1365
+ }`
1366
+ };
1367
+ }
1368
+ }),
1369
+ getRawDataAndTotalCounts: builder.query({
1370
+ query: ({ type, fields, filters, sort, offset = 0, size = 20, accessibility = exports.Accessibility.ALL, format = undefined })=>{
1371
+ const gqlFilter = convertFilterSetToGqlFilter(filters);
1372
+ const params = [
1373
+ ...sort ? [
1374
+ '$sort: JSON'
1375
+ ] : [],
1376
+ ...gqlFilter ? [
1377
+ '$filter: JSON'
1378
+ ] : [],
1379
+ ...format ? [
1380
+ '$format: Format'
1381
+ ] : []
1382
+ ].join(',');
1383
+ const queryLine = `query getRawDataAndTotalCounts (${params}) {`;
1384
+ const dataParams = [
1385
+ ...format ? [
1386
+ 'format: $format'
1387
+ ] : [],
1388
+ ...sort ? [
1389
+ 'sort: $sort'
1390
+ ] : [],
1391
+ ...gqlFilter ? [
1392
+ 'filter: $filter'
1393
+ ] : []
1394
+ ].join(',');
1395
+ const dataTypeLine = `${type} (accessibility: ${accessibility}, offset: ${offset}, first: ${size},
1396
+ ${dataParams}) {`;
1397
+ const typeAggsLine = `${type} (${gqlFilter && 'filter: $filter,'} accessibility: ${accessibility}) {`;
1398
+ const processedFields = fields.map((field)=>rawDataQueryStrForEachField(field));
1399
+ const query = `${queryLine}
1400
+ ${dataTypeLine}
1401
+ ${processedFields.join(' ')}
1402
+ }
1403
+ _aggregation {
1404
+ ${typeAggsLine}
1405
+ _totalCount
1406
+ }
1407
+ }
1408
+ }`;
1409
+ const variables = {
1410
+ ...sort && {
1411
+ sort
1412
+ },
1413
+ ...gqlFilter && {
1414
+ filter: gqlFilter
1415
+ },
1416
+ ...format && {
1417
+ format
1418
+ }
1419
+ };
1420
+ return {
1421
+ query,
1422
+ variables
1423
+ };
1424
+ }
1425
+ }),
1426
+ getAggs: builder.query({
1427
+ query: ({ type, fields, filters, accessibility = exports.Accessibility.ALL })=>{
1428
+ const queryStart = isFilterEmpty(filters) ? `
1429
+ query getAggs {
1430
+ _aggregation {
1431
+ ${type} (accessibility: ${accessibility}) {` : `query getAggs ($filter: JSON) {
1432
+ _aggregation {
1433
+ ${type} (filter: $filter, filterSelf: false, accessibility: ${accessibility}) {`;
1434
+ const query = `${queryStart}
1435
+ ${fields.map((field)=>histogramQueryStrForEachField(field))}
1436
+ }
1437
+ }
1438
+ }`;
1439
+ const queryBody = {
1440
+ query: query,
1441
+ variables: {
1442
+ filter: convertFilterSetToGqlFilter(filters)
1443
+ }
1444
+ };
1445
+ return queryBody;
1446
+ },
1447
+ transformResponse: (response, _meta, args)=>{
1448
+ return processHistogramResponse(response.data._aggregation[args.type]);
1449
+ }
1450
+ }),
1451
+ getSubAggs: builder.query({
1452
+ query: ({ type, mainField, termsFields = undefined, missingFields = undefined, numericAggAsText = false, gqlFilter = undefined, accessibility = exports.Accessibility.ALL })=>{
1453
+ const nestedAggFields = {
1454
+ termsFields: termsFields,
1455
+ missingFields: missingFields
1456
+ };
1457
+ const query = `query getSubAggs ( ${gqlFilter ?? '$filter: JSON,'} $nestedAggFields: JSON) {
1458
+ _aggregation {
1459
+ ${type} ( ${gqlFilter ?? 'filter: $filter, filterSelf: false,'} nestedAggFields: $nestedAggFields, accessibility: ${accessibility}) {
1460
+ ${nestedHistogramQueryStrForEachField(mainField, numericAggAsText)}
1461
+ }`;
1462
+ return {
1463
+ query: query,
1464
+ variables: {
1465
+ ...gqlFilter && {
1466
+ filter: convertFilterSetToGqlFilter(gqlFilter)
1467
+ },
1468
+ nestedAggFields: nestedAggFields
1469
+ }
1470
+ };
1471
+ },
1472
+ transformResponse: (response, _meta, args)=>{
1473
+ return processHistogramResponse(response.data._aggregation[args.type]);
1474
+ }
1475
+ }),
1476
+ getCounts: builder.query({
1477
+ query: ({ type, filters, accessibility = exports.Accessibility.ALL })=>{
1478
+ const gqlFilters = convertFilterSetToGqlFilter(filters);
1479
+ const queryLine = `query totalCounts ${gqlFilters ? '($filter: JSON)' : ''}{`;
1480
+ const typeAggsLine = `${type} ${gqlFilters ? '(filter: $filter, ' : '('} accessibility: ${accessibility}) {`;
1481
+ const query = `${queryLine} _aggregation {
1482
+ ${typeAggsLine}
1483
+ _totalCount
1484
+ }
1485
+ }
1486
+ }`;
1487
+ return {
1488
+ query: query,
1489
+ variables: {
1490
+ ...gqlFilters && {
1491
+ filter: gqlFilters
1492
+ }
1493
+ }
1494
+ };
1495
+ },
1496
+ transformResponse: (response, _meta, args)=>{
1497
+ return response.data._aggregation[args.type]._totalCount;
1498
+ }
1499
+ }),
1500
+ getFieldCountSummary: builder.query({
1501
+ query: ({ type, field, filters })=>{
1502
+ const gqlFilters = convertFilterSetToGqlFilter(filters);
1503
+ const query = `query ($filter: JSON) {
1504
+ _aggregation {
1505
+ ${type} (filter: $filter) {
1506
+ ${field} {
1507
+ histogram {
1508
+ sum
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+ }`;
1514
+ return {
1515
+ query: query,
1516
+ variables: {
1517
+ ...gqlFilters && {
1518
+ filter: gqlFilters
1519
+ }
1520
+ }
1521
+ };
1522
+ }
1523
+ }),
1524
+ getFieldsForIndex: builder.query({
1525
+ query: (index)=>{
1526
+ return {
1527
+ query: `{
1528
+ _mapping ${index}
1529
+ }`
1530
+ };
1531
+ },
1532
+ transformResponse: (response)=>{
1533
+ return response['_mapping'];
1534
+ }
1535
+ }),
1536
+ generalGQL: builder.query({
1537
+ query: ({ query, variables })=>{
1538
+ return {
1539
+ query: query,
1540
+ variables: variables
1541
+ };
1542
+ }
1543
+ })
1544
+ })
1545
+ });
1546
+ // query for aggregate data
1547
+ // convert the function below to typescript
1548
+ const histogramQueryStrForEachField = (field)=>{
1549
+ const splittedFieldArray = field.split('.');
1550
+ const splittedField = splittedFieldArray.shift();
1551
+ if (splittedFieldArray.length === 0) {
1552
+ return `
1553
+ ${splittedField} {
1554
+ histogram {
1555
+ key
1556
+ count
1557
+ }
1558
+ }`;
1559
+ }
1560
+ return `
1561
+ ${splittedField} {
1562
+ ${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
1563
+ }`;
1564
+ };
1565
+ const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
1566
+ ${mainField} {
1567
+ ${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
1568
+ key
1569
+ count
1570
+ missingFields {
1571
+ field
1572
+ count
1573
+ }
1574
+ termsFields {
1575
+ field
1576
+ count
1577
+ terms {
1578
+ key
1579
+ count
1580
+ }
1581
+ }
1582
+ }
1583
+ }`;
1584
+ const rawDataQueryStrForEachField = (field)=>{
1585
+ const splitFieldArray = field.split('.');
1586
+ const splitField = splitFieldArray.shift();
1587
+ if (splitFieldArray.length === 0) {
1588
+ return `
1589
+ ${splitField}
1590
+ `;
1591
+ }
1592
+ return `
1593
+ ${splitField} {
1594
+ ${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
1595
+ }`;
1596
+ };
1597
+ const useGetArrayTypes = ()=>{
1598
+ {
1599
+ const { data, error } = useGetStatus();
1600
+ if (error) {
1601
+ return {};
1602
+ }
1603
+ return data ? data['indices'] : {};
1604
+ }
1605
+ };
1606
+ const { useGetRawDataAndTotalCountsQuery, useGetAccessibleDataQuery, useGetAllFieldsForTypeQuery, useGetAggsQuery, useGetSubAggsQuery, useGetCountsQuery, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGeneralGQLQuery, useLazyGeneralGQLQuery } = explorerApi;
1607
+ const EmptyAggData = {};
1608
+ const selectAggDataForIndex = (state, index, field)=>{
1609
+ const data = state.guppyApi.aggs[index]?.[field]?.histogram;
1610
+ return data ?? EmptyAggData;
1611
+ };
1612
+
1613
+ /**
1614
+ * Flattens a deep nested JSON object skipping
1615
+ * the first level to avoid potentially flattening
1616
+ * non-nested data.
1617
+ * @param {JSON} json
1618
+ */ function flattenJson(json) {
1619
+ const flattenedJson = [];
1620
+ Object.keys(json).forEach((key)=>{
1621
+ flattenedJson.push(flat.flatten(json[key], {
1622
+ delimiter: '_'
1623
+ }));
1624
+ });
1625
+ return flattenedJson;
1626
+ }
1627
+ /**
1628
+ * Converts JSON based on a config.
1629
+ * @param {JSON} json
1630
+ * @param {Object} config
1631
+ */ async function conversion(json, config) {
1632
+ return Papa.unparse(json, config);
1633
+ }
1634
+ /**
1635
+ * Converts JSON to a specified file format.
1636
+ * Defaults to JSON if file format is not supported.
1637
+ * @param {JSON} json
1638
+ * @param {string} format
1639
+ */ async function jsonToFormat(json, format) {
1640
+ if (Object.keys(FILE_DELIMITERS).includes(format)) {
1641
+ const flatJson = await flattenJson(json);
1642
+ const data = await conversion(flatJson, {
1643
+ delimiter: FILE_DELIMITERS[format]
1644
+ });
1645
+ return data;
1646
+ }
1647
+ return json;
1648
+ }
1649
+
1650
+ /**
1651
+ * Prepares a URL for downloading by appending '/download' to the provided apiUrl.
1652
+ *
1653
+ * @param {string} apiUrl - The base URL to be used for preparing the download URL.
1654
+ * @returns {URL} - The prepared download URL as a URL object.
1655
+ */ const prepareUrl = (apiUrl)=>new URL(apiUrl + '/download');
1656
+ /**
1657
+ * Prepares a fetch configuration object for downloading files from Guppy.
1658
+ *
1659
+ * @param {GuppyFileDownloadRequestParams} parameters - The parameters to include in the request body.
1660
+ * @param {string} csrfToken - The CSRF token to include in the request headers.
1661
+ * @returns {FetchConfig} - The prepared fetch configuration object.
1662
+ */ const prepareFetchConfig = (parameters, csrfToken)=>{
1663
+ return {
1664
+ method: 'POST',
1665
+ headers: {
1666
+ 'Content-Type': 'application/json',
1667
+ ...csrfToken !== undefined && {
1668
+ 'X-CSRF-Token': csrfToken
1669
+ }
1670
+ },
1671
+ body: JSON.stringify({
1672
+ type: parameters.type,
1673
+ filter: convertFilterSetToGqlFilter(parameters.filter),
1674
+ accessibility: parameters.accessibility,
1675
+ fields: parameters?.fields,
1676
+ sort: parameters?.sort
1677
+ })
1678
+ };
1679
+ };
1680
+ /**
1681
+ * Downloads a file from Guppy using the provided parameters.
1682
+ * It will optionally convert the data to the specified format.
1683
+ *
1684
+ * @param {DownloadFromGuppyParams} parameters - The parameters to use for the download request.
1685
+ * @param onStart - The function to call when the download starts.
1686
+ * @param onDone - The function to call when the download is done.
1687
+ * @param onError - The function to call when the download fails.
1688
+ * @param onAbort - The function to call when the download is aborted.
1689
+ * @param signal - AbortSignal to use for the request.
1690
+ */ const downloadFromGuppyToBlob = async ({ parameters, onStart = ()=>null, onDone = (_)=>null, onError = (_)=>null, onAbort = ()=>null, signal = undefined })=>{
1691
+ const csrfToken = selectCSRFToken(coreStore.getState());
1692
+ onStart?.();
1693
+ const url = prepareUrl(GEN3_GUPPY_API);
1694
+ const fetchConfig = prepareFetchConfig(parameters, csrfToken);
1695
+ fetch(url.toString(), {
1696
+ ...fetchConfig,
1697
+ ...signal ? {
1698
+ signal: signal
1699
+ } : {}
1700
+ }).then(async (response)=>{
1701
+ if (!response.ok) {
1702
+ throw new Error(response.statusText);
1703
+ }
1704
+ let jsonData = await response.json();
1705
+ if (parameters?.rootPath && parameters.rootPath) {
1706
+ // if rootPath is provided, extract the data from the rootPath
1707
+ jsonData = jsonpathPlus.JSONPath({
1708
+ json: jsonData,
1709
+ path: `$.[${parameters.rootPath}]`,
1710
+ resultType: 'value'
1711
+ });
1712
+ }
1713
+ // convert the data to the specified format and return a Blob
1714
+ let str = '';
1715
+ if (parameters.format === 'json') {
1716
+ str = JSON.stringify(jsonData);
1717
+ } else {
1718
+ const convertedData = await jsonToFormat(jsonData, parameters.format);
1719
+ if (isJSONObject(convertedData)) {
1720
+ str = JSON.stringify(convertedData, null, 2);
1721
+ } else {
1722
+ str = convertedData;
1723
+ }
1724
+ }
1725
+ return new Blob([
1726
+ str
1727
+ ], {
1728
+ type: 'application/json'
1729
+ });
1730
+ }).then((blob)=>onDone?.(blob)).catch((error)=>{
1731
+ // Abort is handle as an exception
1732
+ if (error.name == 'AbortError') {
1733
+ // handle abort()
1734
+ onAbort?.();
1735
+ }
1736
+ onError?.(error);
1737
+ });
1738
+ };
1739
+ const downloadJSONDataFromGuppy = async ({ parameters, onAbort = ()=>null, signal = undefined })=>{
1740
+ const csrfToken = selectCSRFToken(coreStore.getState());
1741
+ const url = prepareUrl(GEN3_GUPPY_API);
1742
+ const fetchConfig = prepareFetchConfig(parameters, csrfToken);
1743
+ try {
1744
+ const response = await fetch(url.toString(), {
1745
+ ...fetchConfig,
1746
+ ...signal ? {
1747
+ signal: signal
1748
+ } : {}
1749
+ });
1750
+ let jsonData = await response.json();
1751
+ if (parameters?.rootPath && parameters.rootPath) {
1752
+ // if rootPath is provided, extract the data from the rootPath
1753
+ jsonData = jsonpathPlus.JSONPath({
1754
+ json: jsonData,
1755
+ path: `$.[${parameters.rootPath}]`,
1756
+ resultType: 'value'
1757
+ });
1758
+ }
1759
+ // convert the data to the specified format and return a Blob
1760
+ return jsonData;
1761
+ } catch (error) {
1762
+ // Abort is handle as an exception
1763
+ if (error.name == 'AbortError') {
1764
+ // handle abort()
1765
+ onAbort?.();
1766
+ }
1767
+ throw new Error(error);
1768
+ }
1769
+ };
1770
+ const useGetIndexFields = (index)=>{
1771
+ const { data } = useGetFieldsForIndexQuery(index);
1772
+ return data ?? [];
1773
+ };
1774
+
1775
+ /**
1776
+ * Creates a Guppy API for fetching bulk (> 10K rows) elasticsearch data
1777
+ * @see https://github.com/uc-cdis/guppy/blob/master/doc/download.md
1778
+ * @param endpoints - Resolver function which configures the query with
1779
+ * type, filter, accessibility, fields, and sort arguments
1780
+ * @returns: A guppy download API for fetching bulk metadata
1781
+ */ const downloadRequestApi = gen3Api.injectEndpoints({
1782
+ endpoints: (builder)=>({
1783
+ downloadFromGuppy: builder.mutation({
1784
+ query: ({ type, filter, accessibility, fields, sort })=>{
1785
+ const queryBody = {
1786
+ filter: convertFilterSetToGqlFilter(filter),
1787
+ ...{
1788
+ type,
1789
+ accessibility,
1790
+ fields,
1791
+ sort
1792
+ }
1793
+ };
1794
+ return {
1795
+ url: `${GEN3_GUPPY_API}/download`,
1796
+ method: 'POST',
1797
+ queryBody,
1798
+ cache: 'no-cache'
1799
+ };
1800
+ },
1801
+ transformResponse: (response)=>{
1802
+ return response;
1803
+ }
1804
+ })
1805
+ })
1806
+ });
1807
+ const { useDownloadFromGuppyMutation } = downloadRequestApi;
1808
+
1809
+ /**
1810
+ * returns a response from the AI search service
1811
+ * @param searchParams - the parameters for the AI search service
1812
+ * @returns the response from the AI search service
1813
+ */ const aiSearchApi = gen3Api.injectEndpoints({
1814
+ endpoints: (builder)=>({
1815
+ askQuestion: builder.mutation({
1816
+ query: (searchParams)=>({
1817
+ url: `${GEN3_AI_SEARCH_API}/ask`,
1818
+ method: 'POST',
1819
+ credentials: 'include',
1820
+ headers: {
1821
+ Accept: 'application/json',
1822
+ 'Content-Type': 'application/json'
1823
+ },
1824
+ body: JSON.stringify({
1825
+ query: searchParams.query,
1826
+ ...searchParams.topic ? {
1827
+ topic: searchParams.topic
1828
+ } : {},
1829
+ ...searchParams.conversationId ? {
1830
+ conversation_id: searchParams.conversationId
1831
+ } : {}
1832
+ })
1833
+ }),
1834
+ transformResponse: (data, _, arg)=>{
1835
+ return {
1836
+ query: arg.query,
1837
+ response: data.response,
1838
+ topic: data.topic,
1839
+ conversationId: data.conversation_id,
1840
+ documents: data.documents
1841
+ };
1842
+ }
1843
+ }),
1844
+ /**
1845
+ * returns the status of the AI search service
1846
+ * @returns {
1847
+ * status: string
1848
+ * timestamp: string
1849
+ * }
1850
+ */ getAISearchStatus: builder.query({
1851
+ query: ()=>`${GEN3_AI_SEARCH_API}/_status`
1852
+ }),
1853
+ getAISearchVersion: builder.query({
1854
+ query: ()=>`${GEN3_AI_SEARCH_API}/_version`
1855
+ })
1856
+ })
1857
+ });
1858
+ // Add more endpoints here
1859
+ const { useAskQuestionMutation, useGetAISearchStatusQuery, useGetAISearchVersionQuery } = aiSearchApi;
1860
+
1861
+ const workspacesApi = gen3Api.injectEndpoints({
1862
+ endpoints: (builder)=>({
1863
+ getWorkspaceOptions: builder.query({
1864
+ query: ()=>`${GEN3_WORKSPACE_STATUS_API}/options`
1865
+ }),
1866
+ getWorkspacePayModels: builder.query({
1867
+ query: ()=>`${GEN3_WORKSPACE_STATUS_API}/allpaymodels`
1868
+ }),
1869
+ getWorkspaceStatus: builder.query({
1870
+ query: ()=>`${GEN3_WORKSPACE_STATUS_API}/status`
1871
+ })
1872
+ })
1873
+ });
1874
+ const { useGetWorkspaceOptionsQuery, useGetWorkspacePayModelsQuery, useGetWorkspaceStatusQuery } = workspacesApi;
1875
+
1876
+ const resourcePathFromProjectID = (projectID)=>{
1877
+ const split = projectID.split('-');
1878
+ const program = split[0];
1879
+ const project = split.slice(1).join('-');
1880
+ const resourcePath = [
1881
+ '/programs',
1882
+ program,
1883
+ 'projects',
1884
+ project
1885
+ ].join('/');
1886
+ return resourcePath;
1887
+ };
1888
+ const isRootUrl = (urlFragment)=>urlFragment === '_root';
1889
+ const isProgramUrl = (urlFragment)=>urlFragment !== '_root' && !urlFragment.includes('-');
1890
+ const userHasSheepdogProgramAdmin = (userAuthMapping = {})=>userAuthMapping['/services/sheepdog/submission/program'] !== undefined;
1891
+ const userHasSheepdogProjectAdmin = (userAuthMapping = {})=>userAuthMapping['/services/sheepdog/submission/project'] !== undefined;
1892
+ const projectCodeFromResourcePath = (resourcePath)=>{
1893
+ const split = resourcePath.split('/');
1894
+ return split.length < 5 || split[1] !== 'programs' || split[3] !== 'projects' ? '' : split[4];
1895
+ };
1896
+ const listifyMethodsFromMapping = (actions)=>{
1897
+ const reducer = (accumulator, currval)=>accumulator.concat([
1898
+ currval.method
1899
+ ]);
1900
+ return actions.reduce(reducer, []);
1901
+ };
1902
+ const userHasDataUpload = (userAuthMapping = {})=>{
1903
+ const actionIsFileUpload = (x)=>x.method === 'file_upload';
1904
+ const resource = userAuthMapping['/data_file'];
1905
+ return resource !== undefined && resource.some(actionIsFileUpload);
1906
+ };
1907
+ const userHasMethodForServiceOnResource = (method, service, resourcePath, userAuthMapping = {})=>{
1908
+ const actions = userAuthMapping[resourcePath];
1909
+ return actions !== undefined && actions.some((x)=>(x.service === service || x.service === '*') && (x.method === method || x.method === '*'));
1910
+ };
1911
+ const userHasMethodForServiceOnProject = (method, service, projectID, userAuthMapping = {})=>{
1912
+ const resourcePath = resourcePathFromProjectID(projectID);
1913
+ return userHasMethodForServiceOnResource(method, service, resourcePath, userAuthMapping);
1914
+ };
1915
+ const userHasMethodOnAnyProject = (method, userAuthMapping = {})=>{
1916
+ const actionHasMethod = (x)=>x.method === method;
1917
+ const actionArrays = Object.values(userAuthMapping);
1918
+ const hasMethod = actionArrays.some((x)=>x.some(actionHasMethod));
1919
+ return hasMethod;
1920
+ };
1921
+ const userHasCreateOrUpdateOnAnyProject = (userAuthMapping = {})=>userHasMethodOnAnyProject('create', userAuthMapping) || userHasMethodOnAnyProject('update', userAuthMapping);
1922
+
1923
+ const extractValuesFromObject = (jsonPathMappings, obj)=>{
1924
+ const result = {};
1925
+ const extractObjectValue = (jsonPath, obj)=>{
1926
+ const extractedValues = jsonpathPlus.JSONPath({
1927
+ path: jsonPath,
1928
+ json: obj
1929
+ });
1930
+ return extractedValues.length > 0 ? extractedValues[0] : undefined;
1931
+ };
1932
+ for(const key in jsonPathMappings){
1933
+ if (key in Object.keys(jsonPathMappings)) {
1934
+ // Extract value from object and store it in the result.
1935
+ result[key] = extractObjectValue(jsonPathMappings[key], obj);
1936
+ }
1937
+ }
1938
+ return result;
1939
+ };
1940
+
1941
+ const SubmissionGraphqlQuery = `query transactionList {
1942
+ transactionList: transaction_log(last: 20) {
1943
+ id
1944
+ submitter
1945
+ project_id
1946
+ created_datetime
1947
+ documents {
1948
+ doc_size
1949
+ doc
1950
+ id
1951
+ }
1952
+ state
1953
+ }
1954
+ }`;
1955
+ /**
1956
+ * Defines submissionApi service using a base URL and expected endpoints. Derived from gen3Api core API.
1957
+ * @param endpoints - Defines endpoints used in submission page
1958
+ * @param getProjects - Queries the list of projects
1959
+ * @param getProjectsDetails - Queries the list of projects and their details
1960
+ * @param getSubmissionGraphQL - Queries the submission graphql with a query and variables
1961
+ * @returns: A submission API for fetching project details
1962
+ */ const submissionApi = gen3Api.injectEndpoints({
1963
+ endpoints: (builder)=>({
1964
+ getProjects: builder.query({
1965
+ query: (graphQLParams)=>({
1966
+ url: `${GEN3_SUBMISSION_API}/graphql`,
1967
+ method: 'POST',
1968
+ credentials: 'include',
1969
+ body: JSON.stringify(graphQLParams)
1970
+ }),
1971
+ transformResponse: (response, _meta)=>{
1972
+ return {
1973
+ projects: response.data.project
1974
+ };
1975
+ }
1976
+ }),
1977
+ getSubmissions: builder.query({
1978
+ query: ()=>({
1979
+ url: `${GEN3_SUBMISSION_API}/graphql`,
1980
+ method: 'POST',
1981
+ credentials: 'include',
1982
+ body: JSON.stringify({
1983
+ query: SubmissionGraphqlQuery
1984
+ })
1985
+ }),
1986
+ transformResponse: (response, _meta)=>{
1987
+ return {
1988
+ transactionList: response.data.transactionList
1989
+ };
1990
+ }
1991
+ }),
1992
+ getProjectsDetails: builder.query({
1993
+ async queryFn (args, _queryApi, _extraOptions, fetchWithBQ) {
1994
+ // get the list of projects
1995
+ const projectsResponse = await fetchWithBQ({
1996
+ url: `${GEN3_SUBMISSION_API}/graphql`,
1997
+ method: 'POST',
1998
+ credentials: 'include',
1999
+ body: JSON.stringify(args.projectQuery)
2000
+ });
2001
+ if (projectsResponse.error) return {
2002
+ error: projectsResponse.error
2003
+ };
2004
+ const projects = projectsResponse?.data.data.project;
2005
+ const projectIds = projects.map((p)=>p.project_id);
2006
+ // given the list of projects, get all of them by executing the projectDetailsQuery for each project
2007
+ const projectDetails = await Promise.all(projectIds.map(async (projectId)=>{
2008
+ const result = await fetchWithBQ({
2009
+ url: `${GEN3_SUBMISSION_API}/graphql`,
2010
+ method: 'POST',
2011
+ credentials: 'include',
2012
+ body: JSON.stringify({
2013
+ ...args.projectDetailsQuery,
2014
+ variables: {
2015
+ name: projectId
2016
+ }
2017
+ })
2018
+ });
2019
+ const { project, ...values } = result.data.data;
2020
+ return result.data ? {
2021
+ data: args.mapping ? {
2022
+ ...extractValuesFromObject(args.mapping, values),
2023
+ project: project[0]
2024
+ } : {
2025
+ ...values,
2026
+ project: project[0]
2027
+ }
2028
+ } : {
2029
+ error: result.error
2030
+ };
2031
+ }));
2032
+ // if any of the projectDetails has an error, return the error
2033
+ if (projectDetails.some((detail)=>'error' in detail)) {
2034
+ return {
2035
+ error: projectDetails.find((detail)=>'error' in detail)
2036
+ };
2037
+ } else return {
2038
+ data: {
2039
+ details: projectDetails.reduce((acc, detail)=>{
2040
+ acc.push(detail.data ?? {
2041
+ project: {
2042
+ name: '',
2043
+ id: '',
2044
+ code: ''
2045
+ }
2046
+ });
2047
+ return acc;
2048
+ }, []),
2049
+ hits: projectIds.length
2050
+ }
2051
+ };
2052
+ }
2053
+ }),
2054
+ getSubmissionGraphQL: builder.query({
2055
+ query: (graphQLParams)=>({
2056
+ url: `${GEN3_SUBMISSION_API}/graphql`,
2057
+ method: 'POST',
2058
+ credentials: 'include',
2059
+ body: JSON.stringify({
2060
+ query: graphQLParams.query,
2061
+ ...graphQLParams.variables ? {
2062
+ variables: graphQLParams.variables
2063
+ } : {}
2064
+ })
2065
+ }),
2066
+ transformResponse: (response, _meta, params)=>{
2067
+ if (params.mapping) {
2068
+ return {
2069
+ data: extractValuesFromObject(params.mapping, response.data)
2070
+ };
2071
+ }
2072
+ return response.data;
2073
+ }
2074
+ }),
2075
+ getDictionary: builder.query({
2076
+ query: ()=>({
2077
+ url: `${GEN3_SUBMISSION_API}/_dictionary/_all/`
2078
+ })
2079
+ })
2080
+ })
2081
+ });
2082
+ const { useGetProjectsQuery, useGetSubmissionGraphQLQuery, useGetProjectsDetailsQuery, useLazyGetProjectsQuery, useLazyGetSubmissionGraphQLQuery, useGetSubmissionsQuery, useGetDictionaryQuery } = submissionApi;
2083
+
2084
+ exports.CoreContext = CoreContext;
2085
+ exports.CoreProvider = CoreProvider;
2086
+ exports.GEN3_API = GEN3_API;
2087
+ exports.GEN3_AUTHZ_API = GEN3_AUTHZ_API;
2088
+ exports.GEN3_COMMONS_NAME = GEN3_COMMONS_NAME;
2089
+ exports.GEN3_CROSSWALK_API = GEN3_CROSSWALK_API;
2090
+ exports.GEN3_DOMAIN = GEN3_DOMAIN;
2091
+ exports.GEN3_DOWNLOADS_ENDPOINT = GEN3_DOWNLOADS_ENDPOINT;
2092
+ exports.GEN3_FENCE_API = GEN3_FENCE_API;
2093
+ exports.GEN3_GUPPY_API = GEN3_GUPPY_API;
2094
+ exports.GEN3_MDS_API = GEN3_MDS_API;
2095
+ exports.GEN3_REDIRECT_URL = GEN3_REDIRECT_URL;
2096
+ exports.GEN3_SUBMISSION_API = GEN3_SUBMISSION_API;
2097
+ exports.GEN3_WORKSPACE_STATUS_API = GEN3_WORKSPACE_STATUS_API;
2098
+ exports.clearCohortFilters = clearCohortFilters;
2099
+ exports.cohortReducer = cohortReducer;
2100
+ exports.convertFilterSetToGqlFilter = convertFilterSetToGqlFilter;
2101
+ exports.coreStore = coreStore;
2102
+ exports.createGen3App = createGen3App;
2103
+ exports.createUseCoreDataHook = createUseCoreDataHook;
2104
+ exports.downloadFromGuppyToBlob = downloadFromGuppyToBlob;
2105
+ exports.downloadJSONDataFromGuppy = downloadJSONDataFromGuppy;
2106
+ exports.drsHostnamesReducer = drsHostnamesReducer;
2107
+ exports.extractEnumFilterValue = extractEnumFilterValue;
2108
+ exports.extractFieldNameFromFullFieldName = extractFieldNameFromFullFieldName;
2109
+ exports.extractFilterValue = extractFilterValue;
2110
+ exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFromFullFieldName;
2111
+ exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
2112
+ exports.fetchFence = fetchFence;
2113
+ exports.fetchJson = fetchJson;
2114
+ exports.fetchUserState = fetchUserState;
2115
+ exports.fieldNameToTitle = fieldNameToTitle;
2116
+ exports.gen3Api = gen3Api;
2117
+ exports.getGen3AppId = getGen3AppId;
2118
+ exports.graphQLAPI = graphQLAPI;
2119
+ exports.graphQLWithTags = graphQLWithTags;
2120
+ exports.guppyAPISliceMiddleware = guppyAPISliceMiddleware;
2121
+ exports.guppyApi = guppyApi;
2122
+ exports.guppyApiReducer = guppyApiReducer;
2123
+ exports.guppyApiSliceReducerPath = guppyApiSliceReducerPath;
2124
+ exports.handleOperation = handleOperation;
2125
+ exports.hideModal = hideModal;
2126
+ exports.isAuthenticated = isAuthenticated;
2127
+ exports.isErrorWithMessage = isErrorWithMessage;
2128
+ exports.isFetchBaseQueryError = isFetchBaseQueryError;
2129
+ exports.isFetchParseError = isFetchParseError;
2130
+ exports.isFilterEmpty = isFilterEmpty;
2131
+ exports.isGuppyAggregationData = isGuppyAggregationData;
2132
+ exports.isHistogramData = isHistogramData;
2133
+ exports.isHistogramDataAArray = isHistogramDataAArray;
2134
+ exports.isHistogramDataAnEnum = isHistogramDataAnEnum;
2135
+ exports.isHistogramDataArray = isHistogramDataArray;
2136
+ exports.isHistogramDataArrayARange = isHistogramDataArrayARange;
2137
+ exports.isHistogramDataArrayAnEnum = isHistogramDataArrayAnEnum;
2138
+ exports.isHistogramDataCollection = isHistogramDataCollection;
2139
+ exports.isHistogramRangeData = isHistogramRangeData;
2140
+ exports.isJSONObject = isJSONObject;
2141
+ exports.isJSONValue = isJSONValue;
2142
+ exports.isJSONValueArray = isJSONValueArray;
2143
+ exports.isOperationWithField = isOperationWithField;
2144
+ exports.isPending = isPending;
2145
+ exports.isProgramUrl = isProgramUrl;
2146
+ exports.isRootUrl = isRootUrl;
2147
+ exports.listifyMethodsFromMapping = listifyMethodsFromMapping;
2148
+ exports.logoutFence = logoutFence;
2149
+ exports.prependIndexToFieldName = prependIndexToFieldName;
2150
+ exports.projectCodeFromResourcePath = projectCodeFromResourcePath;
2151
+ exports.rawDataQueryStrForEachField = rawDataQueryStrForEachField;
2152
+ exports.removeCohortFilter = removeCohortFilter;
2153
+ exports.resetUserState = resetUserState;
2154
+ exports.resourcePathFromProjectID = resourcePathFromProjectID;
2155
+ exports.selectAggDataForIndex = selectAggDataForIndex;
2156
+ exports.selectAuthzMappingData = selectAuthzMappingData;
2157
+ exports.selectCSRFToken = selectCSRFToken;
2158
+ exports.selectCSRFTokenData = selectCSRFTokenData;
2159
+ exports.selectCohortFilters = selectCohortFilters;
2160
+ exports.selectCurrentCohort = selectCurrentCohort;
2161
+ exports.selectCurrentCohortId = selectCurrentCohortId;
2162
+ exports.selectCurrentCohortName = selectCurrentCohortName;
2163
+ exports.selectCurrentMessage = selectCurrentMessage;
2164
+ exports.selectCurrentModal = selectCurrentModal;
2165
+ exports.selectGen3AppById = selectGen3AppById;
2166
+ exports.selectGen3AppMetadataById = selectGen3AppMetadataById;
2167
+ exports.selectHeadersWithCSRFToken = selectHeadersWithCSRFToken;
2168
+ exports.selectIndexFilters = selectIndexFilters;
2169
+ exports.selectIndexedFilterByName = selectIndexedFilterByName;
2170
+ exports.selectUser = selectUser;
2171
+ exports.selectUserAuthStatus = selectUserAuthStatus;
2172
+ exports.selectUserData = selectUserData;
2173
+ exports.selectUserDetails = selectUserDetails;
2174
+ exports.selectUserLoginStatus = selectUserLoginStatus;
2175
+ exports.setDRSHostnames = setDRSHostnames;
2176
+ exports.showModal = showModal;
2177
+ exports.submissionApi = submissionApi;
2178
+ exports.trimFirstFieldNameToTitle = trimFirstFieldNameToTitle;
2179
+ exports.updateCohortFilter = updateCohortFilter;
2180
+ exports.useAddNewCredentialMutation = useAddNewCredentialMutation;
2181
+ exports.useAskQuestionMutation = useAskQuestionMutation;
2182
+ exports.useAuthorizeFromCredentialsMutation = useAuthorizeFromCredentialsMutation;
2183
+ exports.useCoreDispatch = useCoreDispatch;
2184
+ exports.useCoreSelector = useCoreSelector;
2185
+ exports.useCoreStore = useCoreStore;
2186
+ exports.useDownloadFromGuppyMutation = useDownloadFromGuppyMutation;
2187
+ exports.useFetchUserDetailsQuery = useFetchUserDetailsQuery;
2188
+ exports.useGeneralGQLQuery = useGeneralGQLQuery;
2189
+ exports.useGetAISearchStatusQuery = useGetAISearchStatusQuery;
2190
+ exports.useGetAISearchVersionQuery = useGetAISearchVersionQuery;
2191
+ exports.useGetAccessibleDataQuery = useGetAccessibleDataQuery;
2192
+ exports.useGetAggMDSQuery = useGetAggMDSQuery;
2193
+ exports.useGetAggsQuery = useGetAggsQuery;
2194
+ exports.useGetAllFieldsForTypeQuery = useGetAllFieldsForTypeQuery;
2195
+ exports.useGetArrayTypes = useGetArrayTypes;
2196
+ exports.useGetAuthzMappingsQuery = useGetAuthzMappingsQuery;
2197
+ exports.useGetCSRFQuery = useGetCSRFQuery;
2198
+ exports.useGetCountsQuery = useGetCountsQuery;
2199
+ exports.useGetCredentialsQuery = useGetCredentialsQuery;
2200
+ exports.useGetCrosswalkDataQuery = useGetCrosswalkDataQuery;
2201
+ exports.useGetDataQuery = useGetDataQuery;
2202
+ exports.useGetDictionaryQuery = useGetDictionaryQuery;
2203
+ exports.useGetExternalLoginsQuery = useGetExternalLoginsQuery;
2204
+ exports.useGetFieldCountSummaryQuery = useGetFieldCountSummaryQuery;
2205
+ exports.useGetFieldsForIndexQuery = useGetFieldsForIndexQuery;
2206
+ exports.useGetIndexAggMDSQuery = useGetIndexAggMDSQuery;
2207
+ exports.useGetIndexFields = useGetIndexFields;
2208
+ exports.useGetJWKKeysQuery = useGetJWKKeysQuery;
2209
+ exports.useGetLoginProvidersQuery = useGetLoginProvidersQuery;
2210
+ exports.useGetMDSQuery = useGetMDSQuery;
2211
+ exports.useGetProjectsDetailsQuery = useGetProjectsDetailsQuery;
2212
+ exports.useGetProjectsQuery = useGetProjectsQuery;
2213
+ exports.useGetRawDataAndTotalCountsQuery = useGetRawDataAndTotalCountsQuery;
2214
+ exports.useGetStatus = useGetStatus;
2215
+ exports.useGetSubAggsQuery = useGetSubAggsQuery;
2216
+ exports.useGetSubmissionGraphQLQuery = useGetSubmissionGraphQLQuery;
2217
+ exports.useGetSubmissionsQuery = useGetSubmissionsQuery;
2218
+ exports.useGetTagsQuery = useGetTagsQuery;
2219
+ exports.useGetWorkspaceOptionsQuery = useGetWorkspaceOptionsQuery;
2220
+ exports.useGetWorkspacePayModelsQuery = useGetWorkspacePayModelsQuery;
2221
+ exports.useGetWorkspaceStatusQuery = useGetWorkspaceStatusQuery;
2222
+ exports.useGraphQLQuery = useGraphQLQuery;
2223
+ exports.useIsExternalConnectedQuery = useIsExternalConnectedQuery;
2224
+ exports.useIsUserLoggedIn = useIsUserLoggedIn;
2225
+ exports.useLazyFetchUserDetailsQuery = useLazyFetchUserDetailsQuery;
2226
+ exports.useLazyGeneralGQLQuery = useLazyGeneralGQLQuery;
2227
+ exports.useLazyGetCrosswalkDataQuery = useLazyGetCrosswalkDataQuery;
2228
+ exports.useLazyGetExternalLoginsQuery = useLazyGetExternalLoginsQuery;
2229
+ exports.useLazyGetProjectsQuery = useLazyGetProjectsQuery;
2230
+ exports.useLazyGetSubmissionGraphQLQuery = useLazyGetSubmissionGraphQLQuery;
2231
+ exports.useLazyIsExternalConnectedQuery = useLazyIsExternalConnectedQuery;
2232
+ exports.usePrevious = usePrevious;
2233
+ exports.useRemoveCredentialMutation = useRemoveCredentialMutation;
2234
+ exports.useUser = useUser;
2235
+ exports.useUserAuth = useUserAuth;
2236
+ exports.userHasCreateOrUpdateOnAnyProject = userHasCreateOrUpdateOnAnyProject;
2237
+ exports.userHasDataUpload = userHasDataUpload;
2238
+ exports.userHasMethodForServiceOnProject = userHasMethodForServiceOnProject;
2239
+ exports.userHasMethodForServiceOnResource = userHasMethodForServiceOnResource;
2240
+ exports.userHasMethodOnAnyProject = userHasMethodOnAnyProject;
2241
+ exports.userHasSheepdogProgramAdmin = userHasSheepdogProgramAdmin;
2242
+ exports.userHasSheepdogProjectAdmin = userHasSheepdogProjectAdmin;
2243
+ //# sourceMappingURL=index.js.map