@gen3/core 0.12.5 → 0.12.8

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 CHANGED
@@ -914,7 +914,7 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
914
914
  } : {}
915
915
  });
916
916
  if (!response.ok) {
917
- throw new HTTPError(response.status, response.statusText, response.text());
917
+ throw new HTTPError(response.status, response.statusText);
918
918
  }
919
919
  if (response.status === 204) {
920
920
  // no content so returns null
@@ -1227,7 +1227,8 @@ const getFederatedLoginStatus = async (selectedFiles)=>{
1227
1227
  return {
1228
1228
  error: {
1229
1229
  message: e.message,
1230
- locations: e.locations
1230
+ locations: e.locations,
1231
+ path: e.path
1231
1232
  }
1232
1233
  };
1233
1234
  }
@@ -3298,17 +3299,21 @@ const explorerTags = guppyApi.enhanceEndpoints({
3298
3299
  ...gqlFilters && {
3299
3300
  [filterName]: gqlFilters
3300
3301
  }
3302
+ },
3303
+ validateStatus: (response)=>{
3304
+ if (response?.errors && response.errors?.length > 0) return false;
3305
+ if (!response.data || !response.data[`${indexPrefix ?? ''}_aggregation`]) {
3306
+ return false;
3307
+ }
3308
+ if (!(type in response.data[`${indexPrefix ?? ''}_aggregation`])) {
3309
+ return false;
3310
+ }
3311
+ return true;
3301
3312
  }
3302
3313
  };
3303
3314
  },
3304
3315
  transformResponse: (response, _meta, args)=>{
3305
- if (!response.data || !response.data[`${args?.indexPrefix ?? ''}_aggregation`]) {
3306
- throw new Error('Invalid response: Missing data or _aggregation field');
3307
- }
3308
- if (!(args.type in response.data[`${args?.indexPrefix ?? ''}_aggregation`])) {
3309
- throw new Error(`Invalid response: Missing expected key '${args.type}' in _aggregation`);
3310
- }
3311
- return response.data[`${args?.indexPrefix ?? ''}_aggregation`][args.type]._totalCount ?? 0;
3316
+ return response?.data[`${args?.indexPrefix ?? ''}_aggregation`][args.type]?._totalCount ?? 0;
3312
3317
  },
3313
3318
  providesTags: [
3314
3319
  'COUNTS'
@@ -3490,8 +3495,32 @@ const createNoopStorage = ()=>{
3490
3495
  const storage = typeof window !== 'undefined' ? createWebStorage('local') : createNoopStorage();
3491
3496
  typeof window !== 'undefined' ? createWebStorage('session') : createNoopStorage();
3492
3497
 
3498
+ /**
3499
+ * Low-level helper to fetch Arborist resources for the current user.
3500
+ * Adds an Authorization header when a token is provided and normalizes the response
3501
+ * to a simple string[] of resource paths.
3502
+ *
3503
+ * token { string | null } - access token to use for authorization
3504
+ * useService { boolean } - use the arborist service endpoint instead of the public arborist API
3505
+ */ async function fetchArboristResources(token, useService = false) {
3506
+ const headers = {};
3507
+ if (token) {
3508
+ headers.Authorization = `Bearer ${token}`;
3509
+ }
3510
+ const url = useService ? `${GEN3_AUTHZ_SERVICE}/resource` : `${GEN3_AUTHZ_API}/resources`;
3511
+ const res = await fetch(url, {
3512
+ headers
3513
+ });
3514
+ if (!res.ok) {
3515
+ console.error('@gen3/core:fetchArboristResources /resource failed:', res.status, await res.text());
3516
+ return [];
3517
+ }
3518
+ const data = await res.json();
3519
+ return data.resources ?? [];
3520
+ }
3521
+
3493
3522
  const persistConfig = {
3494
- key: 'root',
3523
+ key: `${GEN3_COMMONS_NAME}-root`,
3495
3524
  version: 1,
3496
3525
  storage,
3497
3526
  whitelist: [
@@ -3659,30 +3688,6 @@ const selectAuthzMappingData = toolkit.createSelector(selectAuthzMapping, (authz
3659
3688
  mappings: []
3660
3689
  });
3661
3690
 
3662
- /**
3663
- * Low-level helper to fetch Arborist resources for the current user.
3664
- * Adds an Authorization header when a token is provided and normalizes the response
3665
- * to a simple string[] of resource paths.
3666
- *
3667
- * token { string | null } - access token to use for authorization
3668
- * useService { boolean } - use the arborist service endpoint instead of the public arborist API
3669
- */ async function fetchArboristResources(token, useService = false) {
3670
- const headers = {};
3671
- if (token) {
3672
- headers.Authorization = `Bearer ${token}`;
3673
- }
3674
- const url = useService ? `${GEN3_AUTHZ_SERVICE}/resource` : `${GEN3_AUTHZ_API}/resources`;
3675
- const res = await fetch(url, {
3676
- headers
3677
- });
3678
- if (!res.ok) {
3679
- console.error('@gen3/core:fetchArboristResources /resource failed:', res.status, await res.text());
3680
- return [];
3681
- }
3682
- const data = await res.json();
3683
- return data.resources ?? [];
3684
- }
3685
-
3686
3691
  class CohortStorage {
3687
3692
  constructor(config){
3688
3693
  this.databaseName = config.databaseName;
@@ -4265,7 +4270,7 @@ const extractIndexFromDataLibraryCohort = (query)=>{
4265
4270
  if (!dataObjects || !Array.isArray(dataObjects)) {
4266
4271
  return acc;
4267
4272
  }
4268
- const datasetId = resource[dataFieldMapping.datasetIdField]; // Note: typo still preserved
4273
+ const datasetId = resource[dataFieldMapping.datasetIdField];
4269
4274
  if (datasetId === undefined) {
4270
4275
  return acc; // Skip if dataset ID is missing
4271
4276
  }
@@ -4924,137 +4929,124 @@ const useDataLibrary = (options = {
4924
4929
  storageMode: DataLibraryStoreMode.ApiOnly
4925
4930
  })=>{
4926
4931
  // State management
4927
- const [isLoggedIn, setIsLoggedIn] = React.useState(false);
4928
4932
  const [isLoading, setIsLoading] = React.useState(false);
4929
4933
  const [isUpdating, setIsUpdating] = React.useState(null);
4930
4934
  const [error, setError] = React.useState(null);
4931
4935
  const [lists, setLists] = React.useState({});
4932
4936
  // Refs
4933
- const initialLoadRef = React.useRef(false);
4937
+ const hasInitializedRef = React.useRef(false);
4934
4938
  // Services
4935
- const dataLibraryStoreAPI = React.useRef(new DataLibraryStorageService(options.storageMode)).current;
4936
- const handleErrorOrSetLists = React.useCallback(async (error)=>{
4937
- if (error.isError) {
4938
- setError(error);
4939
- } else {
4940
- const getListResults = await dataLibraryStoreAPI.getLists();
4941
- if (getListResults.isError) {
4942
- setError(getListResults);
4943
- } else {
4944
- setLists(getListResults.lists ?? {});
4945
- setError(null);
4946
- }
4939
+ const storage = React.useRef(new DataLibraryStorageService(options.storageMode)).current;
4940
+ const refreshLists = React.useCallback(async ()=>{
4941
+ const results = await storage.getLists();
4942
+ if (results.isError) {
4943
+ setError(results);
4944
+ return results;
4947
4945
  }
4946
+ setLists(results.lists ?? {});
4947
+ setError(null);
4948
+ return results;
4948
4949
  }, [
4949
- dataLibraryStoreAPI
4950
+ storage
4950
4951
  ]);
4951
- const generateUniqueName = React.useCallback((baseName = DEFAULT_LIST_NAME)=>{
4952
- let uniqueName = baseName;
4953
- let counter = 1;
4954
- const existingNames = Object.values(lists).map((x)=>x.name);
4955
- while(existingNames.includes(uniqueName)){
4956
- uniqueName = `${baseName} ${counter}`;
4957
- counter++;
4952
+ const applyOperationResult = React.useCallback(async (result)=>{
4953
+ if (result.isError) {
4954
+ setError(result);
4955
+ return;
4958
4956
  }
4959
- return uniqueName;
4957
+ await refreshLists();
4960
4958
  }, [
4961
- lists
4959
+ refreshLists
4962
4960
  ]);
4963
- const performLibraryOperation = React.useCallback(async (operation, updateId)=>{
4961
+ const runOperation = React.useCallback(async (operation, updatingId)=>{
4964
4962
  setError(null);
4965
- if (updateId) {
4966
- setIsUpdating(updateId);
4967
- } else setIsLoading(true);
4968
- const operationResults = await operation();
4969
- await handleErrorOrSetLists(operationResults);
4970
- if (updateId) setIsUpdating(null);
4963
+ if (updatingId) setIsUpdating(updatingId);
4964
+ else setIsLoading(true);
4965
+ const result = await operation();
4966
+ await applyOperationResult(result);
4967
+ if (updatingId) setIsUpdating(null);
4971
4968
  else setIsLoading(false);
4972
- return operationResults;
4969
+ return result;
4973
4970
  }, [
4974
- handleErrorOrSetLists
4971
+ applyOperationResult
4975
4972
  ]);
4976
- // Lifecycle effects
4977
- React.useEffect(()=>{
4978
- const initializeData = async ()=>{
4979
- if (!initialLoadRef.current) {
4980
- setError(null);
4981
- setIsLoading(true);
4982
- const results = await dataLibraryStoreAPI.getLists(); // get the initial lists
4983
- if (results.isError) setError(results);
4984
- else setLists(results.lists ?? {});
4985
- setIsLoading(false);
4986
- initialLoadRef.current = true;
4987
- }
4988
- };
4989
- initializeData();
4973
+ const createUniqueListName = React.useCallback((preferredName = DEFAULT_LIST_NAME)=>{
4974
+ const existingNames = new Set(Object.values(lists).map((x)=>x.name));
4975
+ if (!existingNames.has(preferredName)) return preferredName;
4976
+ let counter = 1;
4977
+ let candidate = `${preferredName} ${counter}`;
4978
+ while(existingNames.has(candidate)){
4979
+ counter += 1;
4980
+ candidate = `${preferredName} ${counter}`;
4981
+ }
4982
+ return candidate;
4990
4983
  }, [
4991
- dataLibraryStoreAPI
4984
+ lists
4992
4985
  ]);
4986
+ // Lifecycle effects
4993
4987
  React.useEffect(()=>{
4994
- const handleLogin = async ()=>{
4995
- // setIsLoading(true);
4996
- // await dataLibraryStoreAPI.setUseAPI(options.requiresAPI && isLoggedIn);
4997
- // setIsLoading(false);
4988
+ const initialize = async ()=>{
4989
+ if (hasInitializedRef.current) return;
4990
+ setError(null);
4991
+ setIsLoading(true);
4992
+ await refreshLists();
4993
+ setIsLoading(false);
4994
+ hasInitializedRef.current = true;
4998
4995
  };
4999
- handleLogin();
4996
+ void initialize();
5000
4997
  }, [
5001
- dataLibraryStoreAPI,
5002
- isLoggedIn
4998
+ refreshLists
5003
4999
  ]);
5004
5000
  // CRUD operations
5005
5001
  const addListToDataLibrary = React.useCallback(async (items, name)=>{
5006
5002
  const apiItems = convertDatasetOrCohortToLibraryListItemsAPI(items);
5007
- const namedItems = {
5003
+ const payload = {
5008
5004
  items: apiItems,
5009
- name: generateUniqueName(name ?? DEFAULT_LIST_NAME)
5005
+ name: createUniqueListName(name ?? DEFAULT_LIST_NAME)
5010
5006
  };
5011
- return await performLibraryOperation(()=>dataLibraryStoreAPI.addList(namedItems));
5007
+ return runOperation(()=>storage.addList(payload));
5012
5008
  }, [
5013
- dataLibraryStoreAPI,
5014
- generateUniqueName,
5015
- performLibraryOperation
5009
+ createUniqueListName,
5010
+ runOperation,
5011
+ storage
5016
5012
  ]);
5017
5013
  const updateListInDataLibrary = React.useCallback(async (payload)=>{
5018
5014
  const flattened = flattenDataList(payload);
5019
- return await performLibraryOperation(()=>dataLibraryStoreAPI.updateList(payload.id, {
5015
+ return runOperation(()=>storage.updateList(payload.id, {
5020
5016
  name: payload.name,
5021
5017
  items: flattened.items
5022
5018
  }), payload.id);
5023
5019
  }, [
5024
- dataLibraryStoreAPI,
5025
- performLibraryOperation
5020
+ runOperation,
5021
+ storage
5026
5022
  ]);
5027
- const deleteListFromDataLibrary = React.useCallback(async (id)=>{
5028
- return await performLibraryOperation(()=>dataLibraryStoreAPI.deleteList(id));
5029
- }, [
5030
- dataLibraryStoreAPI,
5031
- performLibraryOperation
5023
+ const deleteListFromDataLibrary = React.useCallback(async (id)=>runOperation(()=>storage.deleteList(id)), [
5024
+ runOperation,
5025
+ storage
5032
5026
  ]);
5033
- const clearLibrary = React.useCallback(async ()=>{
5034
- return await performLibraryOperation(()=>dataLibraryStoreAPI.clearLists());
5035
- }, [
5036
- dataLibraryStoreAPI,
5037
- performLibraryOperation
5027
+ const clearLibrary = React.useCallback(async ()=>runOperation(()=>storage.clearLists()), [
5028
+ runOperation,
5029
+ storage
5038
5030
  ]);
5039
5031
  const setAllListsInDataLibrary = React.useCallback(async (data)=>{
5040
- const flattenedLists = data.map((x)=>flattenDataList(x));
5041
- return await performLibraryOperation(()=>dataLibraryStoreAPI.setAllLists(flattenedLists));
5032
+ const flattenedLists = data.map(flattenDataList);
5033
+ return runOperation(()=>storage.setAllLists(flattenedLists));
5042
5034
  }, [
5043
- dataLibraryStoreAPI,
5044
- performLibraryOperation
5035
+ runOperation,
5036
+ storage
5045
5037
  ]);
5046
5038
  const getDatalist = React.useCallback((id)=>{
5047
- if (id in lists) return lists[id];
5039
+ const list = lists[id];
5040
+ if (list) return list;
5048
5041
  setError({
5049
5042
  isError: true,
5050
5043
  status: 404,
5051
- message: `List not found. Returning empty list.`
5044
+ message: 'List not found. Returning empty list.'
5052
5045
  });
5053
5046
  return EMPTY_LIST;
5054
5047
  }, [
5055
5048
  lists
5056
5049
  ]);
5057
- const setLoginState = React.useCallback((loggedIn)=>setIsLoggedIn(loggedIn), []);
5058
5050
  const results = useDeepCompare.useDeepCompareMemo(()=>({
5059
5051
  dataLibrary: lists,
5060
5052
  isLoading,
@@ -5065,7 +5057,6 @@ const useDataLibrary = (options = {
5065
5057
  deleteListFromDataLibrary,
5066
5058
  clearLibrary,
5067
5059
  setAllListsInDataLibrary,
5068
- setLoginState,
5069
5060
  getDatalist
5070
5061
  }), [
5071
5062
  addListToDataLibrary,
@@ -5077,7 +5068,6 @@ const useDataLibrary = (options = {
5077
5068
  isUpdating,
5078
5069
  lists,
5079
5070
  setAllListsInDataLibrary,
5080
- setLoginState,
5081
5071
  updateListInDataLibrary
5082
5072
  ]);
5083
5073
  return results;