@gen3/core 0.11.32 → 0.11.34

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 (60) hide show
  1. package/dist/cjs/index.js +887 -672
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/server.js +45 -0
  4. package/dist/cjs/server.js.map +1 -0
  5. package/dist/dts/constants.d.ts +1 -0
  6. package/dist/dts/constants.d.ts.map +1 -1
  7. package/dist/dts/features/cart/cartSelectors.d.ts +129 -0
  8. package/dist/dts/features/cart/cartSelectors.d.ts.map +1 -0
  9. package/dist/dts/features/cart/cartSlice.d.ts +24 -0
  10. package/dist/dts/features/cart/cartSlice.d.ts.map +1 -0
  11. package/dist/dts/features/cart/index.d.ts +5 -0
  12. package/dist/dts/features/cart/index.d.ts.map +1 -0
  13. package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts +2 -0
  14. package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts.map +1 -0
  15. package/dist/dts/features/cohort/cohortManagerSelector.d.ts +4 -0
  16. package/dist/dts/features/cohort/cohortManagerSelector.d.ts.map +1 -1
  17. package/dist/dts/features/cohort/cohortManagerSlice.d.ts +1 -0
  18. package/dist/dts/features/cohort/cohortManagerSlice.d.ts.map +1 -1
  19. package/dist/dts/features/cohort/utils.d.ts +13 -0
  20. package/dist/dts/features/cohort/utils.d.ts.map +1 -1
  21. package/dist/dts/features/filters/filters.d.ts +14 -0
  22. package/dist/dts/features/filters/filters.d.ts.map +1 -1
  23. package/dist/dts/features/filters/types.d.ts +7 -0
  24. package/dist/dts/features/filters/types.d.ts.map +1 -1
  25. package/dist/dts/features/guppy/guppyApi.d.ts.map +1 -1
  26. package/dist/dts/features/guppy/guppySlice.d.ts +0 -4
  27. package/dist/dts/features/guppy/guppySlice.d.ts.map +1 -1
  28. package/dist/dts/features/guppy/index.d.ts +6 -0
  29. package/dist/dts/features/guppy/index.d.ts.map +1 -1
  30. package/dist/dts/features/guppy/queryGenerators.d.ts +6 -0
  31. package/dist/dts/features/guppy/queryGenerators.d.ts.map +1 -0
  32. package/dist/dts/features/guppy/tests/queryGenerators.unit.test.d.ts +2 -0
  33. package/dist/dts/features/guppy/tests/queryGenerators.unit.test.d.ts.map +1 -0
  34. package/dist/dts/features/guppy/utils.d.ts +1 -1
  35. package/dist/dts/features/guppy/utils.d.ts.map +1 -1
  36. package/dist/dts/features/user/userSliceRTK.d.ts +3 -0
  37. package/dist/dts/features/user/userSliceRTK.d.ts.map +1 -1
  38. package/dist/dts/hooks.d.ts +2 -0
  39. package/dist/dts/hooks.d.ts.map +1 -1
  40. package/dist/dts/index.d.ts +3 -2
  41. package/dist/dts/index.d.ts.map +1 -1
  42. package/dist/dts/reducers.d.ts +2 -0
  43. package/dist/dts/reducers.d.ts.map +1 -1
  44. package/dist/dts/server.d.ts +4 -0
  45. package/dist/dts/server.d.ts.map +1 -0
  46. package/dist/dts/store.d.ts +4 -0
  47. package/dist/dts/store.d.ts.map +1 -1
  48. package/dist/dts/utils/conversions.d.ts +7 -0
  49. package/dist/dts/utils/conversions.d.ts.map +1 -1
  50. package/dist/dts/utils/extractvalues.d.ts +1 -0
  51. package/dist/dts/utils/extractvalues.d.ts.map +1 -1
  52. package/dist/dts/utils/index.d.ts +3 -2
  53. package/dist/dts/utils/index.d.ts.map +1 -1
  54. package/dist/esm/index.js +868 -673
  55. package/dist/esm/index.js.map +1 -1
  56. package/dist/esm/server.js +29 -0
  57. package/dist/esm/server.js.map +1 -0
  58. package/dist/index.d.ts +420 -199
  59. package/dist/server.d.ts +31 -0
  60. package/package.json +7 -2
package/dist/esm/index.js CHANGED
@@ -5,18 +5,18 @@ import { QueryStatus, setupListeners } from '@reduxjs/toolkit/query';
5
5
  import { useSelector, useDispatch, Provider, createSelectorHook, createDispatchHook, createStoreHook } from 'react-redux';
6
6
  import * as React from 'react';
7
7
  import React__default, { useEffect, useState, useRef, useCallback } from 'react';
8
+ import { GraphQLError, parse } from 'graphql';
9
+ import { JSONPath } from 'jsonpath-plus';
10
+ import { isEqual } from 'lodash';
8
11
  import { customAlphabet } from 'nanoid';
9
12
  import useSWR from 'swr';
10
- import { isEqual } from 'lodash';
11
13
  import { flatten } from 'flat';
12
14
  import Papa from 'papaparse';
13
- import { JSONPath } from 'jsonpath-plus';
14
15
  import { persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, persistStore } from 'redux-persist';
15
16
  import createWebStorage from 'redux-persist/lib/storage/createWebStorage';
16
17
  import { PersistGate } from 'redux-persist/integration/react';
17
18
  import { openDB } from 'idb';
18
19
  import { useDeepCompareMemo } from 'use-deep-compare';
19
- import { parse } from 'graphql';
20
20
  import { v5 } from 'uuid';
21
21
  import { CookiesProvider } from 'react-cookie';
22
22
  import Queue from 'queue';
@@ -52,6 +52,7 @@ const FILE_DELIMITERS = {
52
52
  tsv: '\t',
53
53
  csv: ','
54
54
  };
55
+ const CART_LIMIT = 10000;
55
56
 
56
57
  const isFetchError = (obj)=>{
57
58
  if (typeof obj !== 'object' || obj === null) {
@@ -406,7 +407,7 @@ const useCoreDispatch = useDispatch.withTypes();
406
407
  });
407
408
  const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
408
409
  const isPending = (loginStatus)=>loginStatus === 'pending';
409
- const initialState$8 = {
410
+ const initialState$9 = {
410
411
  status: 'uninitialized',
411
412
  loginStatus: 'unauthenticated',
412
413
  error: undefined
@@ -417,9 +418,9 @@ const initialState$8 = {
417
418
  * @returns: status messages wrapped around fetchUserState response dict
418
419
  */ const slice$4 = createSlice({
419
420
  name: 'fence/user',
420
- initialState: initialState$8,
421
+ initialState: initialState$9,
421
422
  reducers: {
422
- resetUserState: ()=>initialState$8
423
+ resetUserState: ()=>initialState$9
423
424
  },
424
425
  extraReducers: (builder)=>{
425
426
  builder.addCase(fetchUserState.fulfilled, (_, action)=>{
@@ -561,11 +562,11 @@ const { useGetExternalLoginsQuery, useLazyGetExternalLoginsQuery, useLazyIsExter
561
562
  }
562
563
  };
563
564
 
564
- const initialState$7 = {};
565
+ const initialState$8 = {};
565
566
  // TODO: document what this does
566
567
  const slice$3 = createSlice({
567
568
  name: 'drsResolver',
568
- initialState: initialState$7,
569
+ initialState: initialState$8,
569
570
  reducers: {
570
571
  setDRSHostnames: (_state, action)=>{
571
572
  return action.payload;
@@ -587,12 +588,12 @@ const lookupGen3App = (id)=>{
587
588
  else return null;
588
589
  };
589
590
 
590
- const initialState$6 = {
591
+ const initialState$7 = {
591
592
  gen3Apps: {}
592
593
  };
593
594
  const slice$2 = createSlice({
594
595
  name: 'gen3Apps',
595
- initialState: initialState$6,
596
+ initialState: initialState$7,
596
597
  reducers: {
597
598
  addGen3AppMetadata: (state, action)=>{
598
599
  const { name, requiredEntityTypes } = action.payload;
@@ -623,13 +624,13 @@ const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize
623
624
  Modals["GeneralErrorModal"] = "GeneralErrorModal";
624
625
  return Modals;
625
626
  }({});
626
- const initialState$5 = {
627
+ const initialState$6 = {
627
628
  currentModal: null
628
629
  };
629
630
  //Creates a modal slice for tracking showModal and hideModal state.
630
631
  const slice$1 = createSlice({
631
632
  name: 'modals',
632
- initialState: initialState$5,
633
+ initialState: initialState$6,
633
634
  reducers: {
634
635
  showModal: (state, action)=>{
635
636
  state.currentModal = action.payload.modal;
@@ -702,7 +703,7 @@ const getTimestamp = ()=>{
702
703
  };
703
704
 
704
705
  const NO_WORKSPACE_ID = 'none';
705
- const initialState$4 = {
706
+ const initialState$5 = {
706
707
  id: NO_WORKSPACE_ID,
707
708
  status: WorkspaceStatus.NotFound,
708
709
  requestedStatus: RequestedWorkspaceStatus.Unset,
@@ -710,7 +711,7 @@ const initialState$4 = {
710
711
  };
711
712
  const slice = createSlice({
712
713
  name: 'ActiveWorkspace',
713
- initialState: initialState$4,
714
+ initialState: initialState$5,
714
715
  reducers: {
715
716
  setActiveWorkspaceId: (state, action)=>{
716
717
  state = {
@@ -752,6 +753,25 @@ const selectActiveWorkspaceStatus = (state)=>state.activeWorkspace.status;
752
753
  const selectRequestedWorkspaceStatus = (state)=>state.activeWorkspace.requestedStatus;
753
754
  const selectRequestedWorkspaceStatusTimestamp = (state)=>state.activeWorkspace.requestedStatusTimestamp;
754
755
 
756
+ const cartAdapter = createEntityAdapter({
757
+ selectId: (item)=>item.id
758
+ });
759
+ const initialState$4 = cartAdapter.getInitialState({});
760
+ const cartSlice = createSlice({
761
+ name: 'cart',
762
+ initialState: initialState$4,
763
+ reducers: {
764
+ addItemsToCart: cartAdapter.addMany,
765
+ removeItemsFromCart: cartAdapter.removeMany
766
+ }
767
+ });
768
+ const cartReducer = cartSlice.reducer;
769
+ const { addItemsToCart, removeItemsFromCart } = cartSlice.actions;
770
+
771
+ const { selectById: selectCartItem, selectIds: selectCartItems, selectAll: selectCart, selectTotal: selectCartCount } = cartAdapter.getSelectors((state)=>state.cart);
772
+
773
+ const cartReducerPath = 'cart';
774
+
755
775
  /**
756
776
  * Creates a base class core API for guppy API calls.
757
777
  * @returns: guppy core API with guppyAPIFetch base query
@@ -786,6 +806,15 @@ const selectRequestedWorkspaceStatusTimestamp = (state)=>state.activeWorkspace.r
786
806
  data: await response.json()
787
807
  };
788
808
  } catch (e) {
809
+ if (e instanceof GraphQLError) {
810
+ return {
811
+ error: {
812
+ message: e.message,
813
+ locations: e.locations,
814
+ path: e.path
815
+ }
816
+ };
817
+ }
789
818
  if (e instanceof Error) return {
790
819
  error: e.message
791
820
  };
@@ -800,578 +829,176 @@ const guppyAPISliceMiddleware = guppyApi.middleware;
800
829
  const guppyApiSliceReducerPath = guppyApi.reducerPath;
801
830
  const guppyApiReducer = guppyApi.reducer;
802
831
 
803
- const defaultCohortNameGenerator = ()=>`Custom cohort ${new Date().toLocaleString('en-CA', {
804
- timeZone: 'America/Chicago',
805
- hour12: false
806
- }).replace(',', '')}`;
807
- const isNameUnique = (entities, name, excludeId)=>{
808
- const trimmedName = name.trim();
809
- if (!trimmedName) return false;
810
- return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
832
+ const isOperationWithField = (operation)=>{
833
+ return operation?.field !== undefined;
811
834
  };
812
- const generateUniqueName = (entities, baseName)=>{
813
- const trimmedBaseName = baseName.trim();
814
- // If base name is unique, use it
815
- if (isNameUnique(entities, trimmedBaseName)) {
816
- return trimmedBaseName;
835
+ const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
836
+ if (typeof operation === 'object' && operation !== null && 'operands' in operation && Array.isArray(operation.operands) && 'field' in operation && typeof operation.field === 'string' // Assuming `field` should be a string
837
+ ) {
838
+ const { operator } = operation.operator;
839
+ return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
840
+ }
841
+ return false;
842
+ };
843
+ const extractFilterValue = (op)=>{
844
+ const valueExtractorHandler = new ValueExtractorHandler();
845
+ return handleOperation(valueExtractorHandler, op);
846
+ };
847
+ const extractEnumFilterValue = (op)=>{
848
+ const enumValueExtractorHandler = new EnumValueExtractorHandler();
849
+ const results = handleOperation(enumValueExtractorHandler, op);
850
+ return results ?? [];
851
+ };
852
+ const assertNever = (x)=>{
853
+ throw Error(`Exhaustive comparison did not handle: ${x}`);
854
+ };
855
+ const handleOperation = (handler, op)=>{
856
+ switch(op.operator){
857
+ case '=':
858
+ return handler.handleEquals(op);
859
+ case '!=':
860
+ return handler.handleNotEquals(op);
861
+ case '<':
862
+ return handler.handleLessThan(op);
863
+ case '<=':
864
+ return handler.handleLessThanOrEquals(op);
865
+ case '>':
866
+ return handler.handleGreaterThan(op);
867
+ case '>=':
868
+ return handler.handleGreaterThanOrEquals(op);
869
+ case 'and':
870
+ return handler.handleIntersection(op);
871
+ case 'or':
872
+ return handler.handleUnion(op);
873
+ case 'nested':
874
+ return handler.handleNestedFilter(op);
875
+ case 'in':
876
+ case 'includes':
877
+ return handler.handleIncludes(op);
878
+ case 'excludeifany':
879
+ return handler.handleExcludeIfAny(op);
880
+ case 'excludes':
881
+ return handler.handleExcludes(op);
882
+ case 'exists':
883
+ return handler.handleExists(op);
884
+ case 'missing':
885
+ return handler.handleMissing(op);
886
+ default:
887
+ return assertNever(op);
817
888
  }
818
- // Find a unique name by appending numbers
819
- let counter = 1;
820
- let uniqueName;
821
- do {
822
- uniqueName = `${trimmedBaseName} (${counter})`;
823
- counter++;
824
- }while (!isNameUnique(entities, uniqueName))
825
- return uniqueName;
826
889
  };
827
-
828
890
  /**
829
- * Cohorts in Gen3 are defined as a set of filters for each index in the data.
830
- * This means one cohort id defined for all "tabs" in CohortBuilder (explorer)
831
- * Switching a cohort id means all the cohorts for the index are changed.
832
- */ const DEFAULT_COHORT_NAME = 'Cohort';
833
- const newCohort = ({ filters = {}, customName })=>{
834
- const ts = new Date().toISOString();
835
- const newName = customName ?? defaultCohortNameGenerator();
836
- const newId = createCohortId();
837
- return {
838
- name: newName,
839
- id: newId,
840
- filters: filters ?? {},
841
- modified: false,
842
- saved: false,
843
- createdDatetime: ts,
844
- modifiedDatetime: ts,
845
- counts: {}
846
- };
891
+ * Return true if a FilterSet's root value is an empty object
892
+ * @param fs - FilterSet to test
893
+ */ const isFilterEmpty = (fs)=>isEqual({}, fs);
894
+ /**
895
+ * Type guard to check if an object is a GQLIntersection
896
+ * @param value - The value to check
897
+ * @returns True if the value is a GQLIntersection
898
+ */ const isGQLIntersection = (value)=>{
899
+ return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
847
900
  };
848
- const nanoid = customAlphabet('1234567890abcdef', 16);
849
- const createCohortId = ()=>nanoid();
850
- const cohortsAdapter = createEntityAdapter({
851
- sortComparer: (a, b)=>{
852
- if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
853
- else return -1;
854
- },
855
- selectId: (cohort)=>cohort.id
856
- });
857
- // Create an initial unsaved cohort
858
- const initialCohort = newCohort({
859
- customName: DEFAULT_COHORT_NAME
860
- });
861
- const emptyInitialState = cohortsAdapter.getInitialState({
862
- currentCohortId: initialCohort.id,
863
- message: undefined
864
- });
865
- // Set the initial cohort in the adapter state
866
- const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
867
- const getCurrentCohortId = (state)=>state.currentCohortId;
868
901
  /**
869
- * Redux slice for cohort filters
870
- */ const cohortManagerSlice = createSlice({
871
- name: 'cohort',
872
- initialState: initialState$3,
873
- reducers: {
874
- createNewCohort: (state, action)=>{
875
- const baseName = action.payload.name || `Cohort`;
876
- const uniqueName = generateUniqueName(Object.values(state.entities), baseName);
877
- const cohort = newCohort({
878
- filters: action.payload.filters,
879
- customName: uniqueName
902
+ * Type guard to check if an object is a GQLIntersection
903
+ * @param value - The value to check
904
+ * @returns True if the value is a GQLIntersection
905
+ */ const isGQLUnion = (value)=>{
906
+ return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
907
+ };
908
+ class ToGqlHandler {
909
+ constructor(){
910
+ this.handleEquals = (op)=>({
911
+ '=': {
912
+ [op.field]: op.operand
913
+ }
880
914
  });
881
- cohortsAdapter.addOne(state, cohort);
882
- state.currentCohortId = cohort.id;
883
- },
884
- updateCohortName: (state, action)=>{
885
- const { id, name } = action.payload;
886
- cohortsAdapter.updateOne(state, {
887
- id: id,
888
- changes: {
889
- name: name,
890
- modified: true,
891
- modifiedDatetime: new Date().toISOString()
915
+ this.handleNotEquals = (op)=>({
916
+ '!=': {
917
+ [op.field]: op.operand
892
918
  }
893
919
  });
894
- },
895
- removeCohort: (state, action)=>{
896
- const { id: cohortId } = action.payload;
897
- const removedCohortName = state.entities[cohortId].name;
898
- const totalCohorts = Object.keys(state.entities).length;
899
- if (totalCohorts <= 1) {
900
- cohortsAdapter.removeAll(state);
901
- const defaultCohort = newCohort({
902
- filters: {},
903
- customName: DEFAULT_COHORT_NAME
904
- });
905
- cohortsAdapter.addOne(state, defaultCohort);
906
- state.currentCohortId = defaultCohort.id;
907
- if (action?.payload.shouldShowMessage) {
908
- state.message = [
909
- `deleteCohort|${removedCohortName}|${state.currentCohortId}`
910
- ];
920
+ this.handleLessThan = (op)=>({
921
+ '<': {
922
+ [op.field]: op.operand
911
923
  }
912
- return;
913
- }
914
- cohortsAdapter.removeOne(state, cohortId);
915
- // deleted the current cohort so set to the most recent cohort
916
- if (state.currentCohortId === cohortId) {
917
- const remainingIds = Object.keys(state.entities);
918
- state.currentCohortId = remainingIds[0];
919
- }
920
- if (action?.payload.shouldShowMessage) {
921
- state.message = [
922
- `deleteCohort|${removedCohortName}|${state.currentCohortId}`
923
- ];
924
- }
925
- },
926
- // adds a filter to the cohort filter set at the given index
927
- updateCohortFilter: (state, action)=>{
928
- const { index, field, filter } = action.payload;
929
- const currentCohortId = getCurrentCohortId(state);
930
- if (!state.entities[currentCohortId]) {
931
- return;
932
- }
933
- cohortsAdapter.updateOne(state, {
934
- id: currentCohortId,
935
- changes: {
936
- filters: {
937
- ...state.entities[currentCohortId].filters,
938
- [index]: {
939
- mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
940
- root: {
941
- ...state.entities[currentCohortId]?.filters[index]?.root ?? {},
942
- [field]: filter
943
- }
944
- }
945
- },
946
- modified: true,
947
- modifiedDatetime: new Date().toISOString()
924
+ });
925
+ this.handleLessThanOrEquals = (op)=>({
926
+ '<=': {
927
+ [op.field]: op.operand
948
928
  }
949
929
  });
950
- },
951
- setCohortFilter: (state, action)=>{
952
- const { index, filters } = action.payload;
953
- const currentCohortId = getCurrentCohortId(state);
954
- if (!state.entities[currentCohortId]) {
955
- console.error(`no cohort with id=${currentCohortId} defined`);
956
- return;
957
- }
958
- cohortsAdapter.updateOne(state, {
959
- id: currentCohortId,
960
- changes: {
961
- filters: {
962
- ...state.entities[currentCohortId].filters,
963
- [index]: filters
964
- },
965
- modified: true,
966
- modifiedDatetime: new Date().toISOString()
930
+ this.handleGreaterThan = (op)=>({
931
+ '>': {
932
+ [op.field]: op.operand
967
933
  }
968
934
  });
969
- },
970
- setCohortIndexFilters: (state, action)=>{
971
- const currentCohortId = getCurrentCohortId(state);
972
- if (!state.entities[currentCohortId]) {
973
- console.error(`no cohort with id=${currentCohortId} defined`);
974
- return;
975
- }
976
- cohortsAdapter.updateOne(state, {
977
- id: currentCohortId,
978
- changes: {
979
- filters: action.payload.filters,
980
- modified: true,
981
- modifiedDatetime: new Date().toISOString()
935
+ this.handleGreaterThanOrEquals = (op)=>({
936
+ '>=': {
937
+ [op.field]: op.operand
982
938
  }
983
939
  });
984
- },
985
- // removes a filter to the cohort filter set at the given index
986
- removeCohortFilter: (state, action)=>{
987
- const { index, field } = action.payload;
988
- const currentCohortId = getCurrentCohortId(state);
989
- if (!state.entities[currentCohortId]) {
990
- console.error(`no cohort with id=${currentCohortId} defined`);
991
- return;
992
- }
993
- const filters = state.entities[currentCohortId]?.filters[index]?.root;
994
- if (!filters) {
995
- return;
996
- }
997
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
998
- const { [field]: _a, ...updated } = filters;
999
- cohortsAdapter.updateOne(state, {
1000
- id: currentCohortId,
1001
- changes: {
1002
- filters: {
1003
- ...state.entities[currentCohortId]?.filters,
1004
- [index]: {
1005
- mode: state.entities[currentCohortId].filters[index].mode,
1006
- root: updated
1007
- }
1008
- },
1009
- modified: true,
1010
- modifiedDatetime: new Date().toISOString()
940
+ this.handleIncludes = (op)=>({
941
+ in: {
942
+ [op.field]: op.operands
1011
943
  }
1012
944
  });
1013
- },
1014
- duplicateCohort: (state)=>{
1015
- const currentCohortId = getCurrentCohortId(state);
1016
- const currentCohort = state.entities[currentCohortId];
1017
- const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
1018
- const duplicatedCohort = newCohort({
1019
- filters: {
1020
- ...currentCohort.filters
1021
- },
1022
- customName: newName
1023
- });
1024
- cohortsAdapter.addOne(state, {
1025
- ...duplicatedCohort,
1026
- counts: {
1027
- ...currentCohort.counts
945
+ this.handleExcludes = (op)=>({
946
+ exclude: {
947
+ [op.field]: op.operands
1028
948
  }
1029
949
  });
1030
- state.currentCohortId = duplicatedCohort.id;
1031
- },
1032
- // removes all filters from the cohort filter set at the given index
1033
- clearCohortFilters: (state, action)=>{
1034
- const { index } = action.payload;
1035
- const currentCohortId = getCurrentCohortId(state);
1036
- if (!state.entities[currentCohortId]) {
1037
- console.error(`no cohort with id=${currentCohortId} defined`);
1038
- return;
1039
- }
1040
- const filters = state.entities[currentCohortId]?.filters[index]?.root;
1041
- if (!filters) {
1042
- return;
1043
- }
1044
- cohortsAdapter.updateOne(state, {
1045
- id: currentCohortId,
1046
- changes: {
1047
- filters: {
1048
- ...state.entities[currentCohortId]?.filters,
1049
- [index]: {
1050
- mode: 'and',
1051
- root: {}
1052
- }
1053
- },
1054
- modified: true,
1055
- modifiedDatetime: new Date().toISOString()
950
+ this.handleExcludeIfAny = (op)=>({
951
+ excludeifany: {
952
+ [op.field]: op.operands
1056
953
  }
1057
954
  });
1058
- },
1059
- updateCohortCounts: (state, action)=>{
1060
- const currentCohortId = getCurrentCohortId(state);
1061
- const currentCohort = state.entities[currentCohortId];
1062
- cohortsAdapter.updateOne(state, {
1063
- id: currentCohortId,
1064
- changes: {
1065
- counts: {
1066
- ...currentCohort.counts,
1067
- ...action.payload
1068
- }
1069
- }
955
+ this.handleIntersection = (op)=>({
956
+ and: op.operands.map((x)=>convertFilterToGqlFilter(x))
1070
957
  });
1071
- },
1072
- updateCohortIndexCountById: (state, action)=>{
1073
- const { index, cohortId, counts } = action.payload;
1074
- const cohort = state.entities[cohortId];
1075
- cohortsAdapter.updateOne(state, {
1076
- id: cohortId,
1077
- changes: {
1078
- counts: {
1079
- ...cohort.counts,
1080
- ...{
1081
- [index]: counts
1082
- }
1083
- }
958
+ this.handleUnion = (op)=>({
959
+ or: op.operands.map((x)=>convertFilterToGqlFilter(x))
960
+ });
961
+ this.handleMissing = (op)=>({
962
+ is: {
963
+ [op.field]: 'MISSING'
1084
964
  }
1085
965
  });
1086
- },
1087
- setCurrentCohortId: (state, action)=>{
1088
- state.currentCohortId = action.payload;
1089
- },
1090
- /** @hidden */ setCohortList: (state, action)=>{
1091
- if (!action.payload) {
1092
- cohortsAdapter.removeMany(state, state.ids);
1093
- } else {
1094
- cohortsAdapter.upsertMany(state, [
1095
- ...action.payload
1096
- ]);
1097
- }
1098
- }
1099
- }
1100
- });
1101
- /**
1102
- * Returns the selectors for the cohorts EntityAdapter
1103
- * @param state - the CoreState
1104
- *
1105
- * @hidden
1106
- */ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
1107
- // Filter actions: addFilter, removeFilter, updateFilter
1108
- const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
1109
- const cohortReducer = cohortManagerSlice.reducer;
1110
-
1111
- const initialState$2 = {};
1112
- const expandSlice$1 = createSlice({
1113
- name: 'CohortBuilder/filterExpand',
1114
- initialState: initialState$2,
1115
- reducers: {
1116
- toggleCohortBuilderCategoryFilter: (state, action)=>{
1117
- return {
1118
- ...state,
1119
- [action.payload.index]: {
1120
- ...state[action.payload.index],
1121
- [action.payload.field]: action.payload.expanded
966
+ this.handleExists = (op)=>({
967
+ not: {
968
+ [op.field]: op?.operand ?? null
1122
969
  }
1123
- };
1124
- },
1125
- toggleCohortBuilderAllFilters: (state, action)=>{
1126
- return {
1127
- ...state,
1128
- [action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
1129
- acc[k] = action.payload.expand;
1130
- return acc;
1131
- }, {})
1132
- };
1133
- }
1134
- }
1135
- });
1136
- const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
1137
- const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
1138
- const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
1139
- const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
1140
-
1141
- const initialState$1 = {};
1142
- const expandSlice = createSlice({
1143
- name: 'CohortBuilder/filterCombineMode',
1144
- initialState: initialState$1,
1145
- reducers: {
1146
- setCohortFilterCombineMode: (state, action)=>{
970
+ });
971
+ this.handleNestedFilter = (op)=>{
972
+ const child = convertFilterToGqlFilter(op.operand);
1147
973
  return {
1148
- ...state,
1149
- [action.payload.index]: {
1150
- ...state[action.payload.index],
1151
- [action.payload.field]: action.payload.mode
974
+ nested: {
975
+ path: op.path,
976
+ ...child
1152
977
  }
1153
978
  };
1154
- }
979
+ };
1155
980
  }
1156
- });
1157
- const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
1158
- const { setCohortFilterCombineMode } = expandSlice.actions;
1159
- const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
1160
-
1161
- const initialState = {
1162
- shouldShareFilters: false,
1163
- sharedFiltersMap: {}
981
+ }
982
+ const convertFilterToGqlFilter = (filter)=>{
983
+ const handler = new ToGqlHandler();
984
+ return handleOperation(handler, filter);
1164
985
  };
1165
- const cohortSharedFiltersSlice = createSlice({
1166
- name: 'cohortSharedFilters',
1167
- initialState: initialState,
1168
- reducers: {
1169
- setShouldShareFilters: (state, action)=>{
1170
- state.shouldShareFilters = action.payload;
1171
- return state;
1172
- },
1173
- setSharedFilters: (state, action)=>{
1174
- state.sharedFiltersMap = action.payload;
1175
- }
1176
- }
1177
- });
1178
- const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
1179
- const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
1180
- const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
1181
- field
1182
- ];
1183
- const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
1184
- const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
1185
-
1186
- const cohortReducers = combineReducers({
1187
- filtersExpanded: cohortBuilderFiltersExpandedReducer,
1188
- filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
1189
- sharedFilters: cohortSharedFiltersReducer,
1190
- cohortManager: cohortReducer
1191
- });
1192
-
1193
- const rootReducer = combineReducers({
1194
- gen3Services: gen3ServicesReducer,
1195
- user: userReducer,
1196
- gen3Apps: gen3AppReducer,
1197
- drsHostnames: drsHostnamesReducer,
1198
- modals: modalReducer,
1199
- cohorts: cohortReducers,
1200
- activeWorkspace: activeWorkspaceReducer,
1201
- [guppyApiSliceReducerPath]: guppyApiReducer,
1202
- [userAuthApiReducerPath]: userAuthApiReducer
1203
- });
1204
-
1205
- const isOperationWithField = (operation)=>{
1206
- return operation?.field !== undefined;
1207
- };
1208
- const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
1209
- if (typeof operation === 'object' && operation !== null && 'operands' in operation && Array.isArray(operation.operands) && 'field' in operation && typeof operation.field === 'string' // Assuming `field` should be a string
1210
- ) {
1211
- const { operator } = operation.operator;
1212
- return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
1213
- }
1214
- return false;
1215
- };
1216
- const extractFilterValue = (op)=>{
1217
- const valueExtractorHandler = new ValueExtractorHandler();
1218
- return handleOperation(valueExtractorHandler, op);
1219
- };
1220
- const extractEnumFilterValue = (op)=>{
1221
- const enumValueExtractorHandler = new EnumValueExtractorHandler();
1222
- const results = handleOperation(enumValueExtractorHandler, op);
1223
- return results ?? [];
1224
- };
1225
- const assertNever = (x)=>{
1226
- throw Error(`Exhaustive comparison did not handle: ${x}`);
1227
- };
1228
- const handleOperation = (handler, op)=>{
1229
- switch(op.operator){
1230
- case '=':
1231
- return handler.handleEquals(op);
1232
- case '!=':
1233
- return handler.handleNotEquals(op);
1234
- case '<':
1235
- return handler.handleLessThan(op);
1236
- case '<=':
1237
- return handler.handleLessThanOrEquals(op);
1238
- case '>':
1239
- return handler.handleGreaterThan(op);
1240
- case '>=':
1241
- return handler.handleGreaterThanOrEquals(op);
1242
- case 'and':
1243
- return handler.handleIntersection(op);
1244
- case 'or':
1245
- return handler.handleUnion(op);
1246
- case 'nested':
1247
- return handler.handleNestedFilter(op);
1248
- case 'in':
1249
- case 'includes':
1250
- return handler.handleIncludes(op);
1251
- case 'excludeifany':
1252
- return handler.handleExcludeIfAny(op);
1253
- case 'excludes':
1254
- return handler.handleExcludes(op);
1255
- case 'exists':
1256
- return handler.handleExists(op);
1257
- case 'missing':
1258
- return handler.handleMissing(op);
1259
- default:
1260
- return assertNever(op);
1261
- }
1262
- };
1263
- /**
1264
- * Return true if a FilterSet's root value is an empty object
1265
- * @param fs - FilterSet to test
1266
- */ const isFilterEmpty = (fs)=>isEqual({}, fs);
1267
- /**
1268
- * Type guard to check if an object is a GQLIntersection
1269
- * @param value - The value to check
1270
- * @returns True if the value is a GQLIntersection
1271
- */ const isGQLIntersection = (value)=>{
1272
- return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
1273
- };
1274
- /**
1275
- * Type guard to check if an object is a GQLIntersection
1276
- * @param value - The value to check
1277
- * @returns True if the value is a GQLIntersection
1278
- */ const isGQLUnion = (value)=>{
1279
- return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
1280
- };
1281
- class ToGqlHandler {
1282
- constructor(){
1283
- this.handleEquals = (op)=>({
1284
- '=': {
1285
- [op.field]: op.operand
1286
- }
1287
- });
1288
- this.handleNotEquals = (op)=>({
1289
- '!=': {
1290
- [op.field]: op.operand
1291
- }
1292
- });
1293
- this.handleLessThan = (op)=>({
1294
- '<': {
1295
- [op.field]: op.operand
1296
- }
1297
- });
1298
- this.handleLessThanOrEquals = (op)=>({
1299
- '<=': {
1300
- [op.field]: op.operand
1301
- }
1302
- });
1303
- this.handleGreaterThan = (op)=>({
1304
- '>': {
1305
- [op.field]: op.operand
1306
- }
1307
- });
1308
- this.handleGreaterThanOrEquals = (op)=>({
1309
- '>=': {
1310
- [op.field]: op.operand
1311
- }
1312
- });
1313
- this.handleIncludes = (op)=>({
1314
- in: {
1315
- [op.field]: op.operands
1316
- }
1317
- });
1318
- this.handleExcludes = (op)=>({
1319
- exclude: {
1320
- [op.field]: op.operands
1321
- }
1322
- });
1323
- this.handleExcludeIfAny = (op)=>({
1324
- excludeifany: {
1325
- [op.field]: op.operands
1326
- }
1327
- });
1328
- this.handleIntersection = (op)=>({
1329
- and: op.operands.map((x)=>convertFilterToGqlFilter(x))
1330
- });
1331
- this.handleUnion = (op)=>({
1332
- or: op.operands.map((x)=>convertFilterToGqlFilter(x))
1333
- });
1334
- this.handleMissing = (op)=>({
1335
- is: {
1336
- [op.field]: 'MISSING'
1337
- }
1338
- });
1339
- this.handleExists = (op)=>({
1340
- not: {
1341
- [op.field]: op?.operand ?? null
1342
- }
1343
- });
1344
- this.handleNestedFilter = (op)=>{
1345
- const child = convertFilterToGqlFilter(op.operand);
1346
- return {
1347
- nested: {
1348
- path: op.path,
1349
- ...child
1350
- }
1351
- };
1352
- };
1353
- }
1354
- }
1355
- const convertFilterToGqlFilter = (filter)=>{
1356
- const handler = new ToGqlHandler();
1357
- return handleOperation(handler, filter);
1358
- };
1359
- const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
1360
- const fsKeys = Object.keys(fs.root);
1361
- // if no keys return undefined
1362
- if (fsKeys.length === 0) return {
1363
- and: []
1364
- };
1365
- return toplevelOp === 'and' ? {
1366
- and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1367
- } : {
1368
- or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1369
- };
1370
- };
1371
- const handleGqlOperation = (handler, op)=>{
1372
- const operationKeys = Object.keys(op);
1373
- if (operationKeys.includes('=')) {
1374
- return handler.handleEquals(op);
986
+ const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
987
+ const fsKeys = Object.keys(fs.root);
988
+ // if no keys return undefined
989
+ if (fsKeys.length === 0) return {
990
+ and: []
991
+ };
992
+ return toplevelOp === 'and' ? {
993
+ and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
994
+ } : {
995
+ or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
996
+ };
997
+ };
998
+ const handleGqlOperation = (handler, op)=>{
999
+ const operationKeys = Object.keys(op);
1000
+ if (operationKeys.includes('=')) {
1001
+ return handler.handleEquals(op);
1375
1002
  }
1376
1003
  if (operationKeys.includes('!=')) {
1377
1004
  return handler.handleNotEquals(op);
@@ -1595,6 +1222,36 @@ const filterSetToOperation = (fs)=>{
1595
1222
  }
1596
1223
  return undefined;
1597
1224
  };
1225
+ /**
1226
+ * Constructs a nested operation object based on the provided field and leaf operand.
1227
+ * If the field does not contain a dot '.', it either assigns the field to the leaf operand (if applicable)
1228
+ * or returns the leaf operand as is. When the field contains dots, it splits the field into parts,
1229
+ * creates a "nested" operation for the root field, and recursively constructs the nested structure
1230
+ * for the remaining portion of the field.
1231
+ *
1232
+ * @param {string} field - The hierarchical field path, with segments separated by dots (e.g., "root.child").
1233
+ * @param {Operation} leafOperand - The operation to be nested within the specified path.
1234
+ * @param parentPath - The parent path of the current field. Guppy nested filters require a parent path.
1235
+ * @param depth
1236
+ * @returns {Operation} A nested operation object that represents the structured path and operand.
1237
+ */ const buildNestedGQLFilter = (field, leafOperand, parentPath = undefined)=>{
1238
+ if (!field.includes('.')) {
1239
+ return leafOperand;
1240
+ }
1241
+ const splitFieldArray = field.split('.');
1242
+ const nextField = splitFieldArray.shift();
1243
+ if (!nextField) {
1244
+ console.warn('Invalid field path:', field);
1245
+ return leafOperand;
1246
+ }
1247
+ const currentPath = parentPath ? `${parentPath}.${nextField}` : nextField;
1248
+ return {
1249
+ nested: {
1250
+ path: currentPath,
1251
+ ...buildNestedGQLFilter(splitFieldArray.join('.'), leafOperand, currentPath)
1252
+ }
1253
+ };
1254
+ };
1598
1255
 
1599
1256
  const isFilterSet = (input)=>{
1600
1257
  if (typeof input !== 'object' || input === null) {
@@ -1618,9 +1275,17 @@ const isUnion = (value)=>{
1618
1275
  const isIntersection = (value)=>{
1619
1276
  return typeof value === 'object' && value !== null && value.operator === 'and' && Array.isArray(value.operands);
1620
1277
  };
1278
+ /**
1279
+ * Type guard for Union or Intersection
1280
+ * @param o - operator to check
1281
+ * @category Filters
1282
+ */ const isIntersectionOrUnion = (o)=>o.operator === 'and' || o.operator === 'or';
1621
1283
  const isOperandsType = (operation)=>{
1622
1284
  return operation?.operands !== undefined;
1623
1285
  };
1286
+ const isNestedFilter = (operation)=>{
1287
+ return operation.operator === 'nested';
1288
+ };
1624
1289
  const isIndexedFilterSetEmpty = (filters)=>Object.values(filters).every((filterSet)=>Object.keys(filterSet).length === 0);
1625
1290
  const EmptyFilterSet = {
1626
1291
  mode: 'and',
@@ -1649,7 +1314,7 @@ const COMMON_PREPOSITIONS = [
1649
1314
  'up',
1650
1315
  'yet'
1651
1316
  ];
1652
- const capitalize = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
1317
+ const capitalize$1 = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
1653
1318
  const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
1654
1319
  if (trim) {
1655
1320
  const source = fieldName.slice(fieldName.indexOf('.') + 1);
@@ -1667,28 +1332,459 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
1667
1332
  if (fieldName in FieldNameOverrides) {
1668
1333
  return FieldNameOverrides[fieldName];
1669
1334
  }
1670
- if (fieldName === undefined) return 'No Title';
1671
- return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize(word)).join(' ');
1672
- };
1673
- /**
1674
- * Extracts the index name from the field name
1675
- * @param fieldName
1676
- */ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
1677
- /**
1678
- * prepend the index name to the field name
1679
- */ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
1680
- /**
1681
- * extract the field name from the index.field name
1682
- */ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
1683
- /**
1684
- * extract the field name and the index from the index.field name returning as a tuple
1685
- */ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
1686
- const [index, ...rest] = fieldName.split('.');
1687
- return [
1688
- index,
1689
- rest.join('.')
1335
+ if (fieldName === undefined) return 'No Title';
1336
+ return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize$1(word)).join(' ');
1337
+ };
1338
+ /**
1339
+ * Extracts the index name from the field name
1340
+ * @param fieldName
1341
+ */ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
1342
+ /**
1343
+ * prepend the index name to the field name
1344
+ */ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
1345
+ /**
1346
+ * extract the field name from the index.field name
1347
+ */ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
1348
+ /**
1349
+ * extract the field name and the index from the index.field name returning as a tuple
1350
+ */ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
1351
+ const [index, ...rest] = fieldName.split('.');
1352
+ return [
1353
+ index,
1354
+ rest.join('.')
1355
+ ];
1356
+ };
1357
+
1358
+ const defaultCohortNameGenerator = ()=>`Custom cohort ${new Date().toLocaleString('en-CA', {
1359
+ timeZone: 'America/Chicago',
1360
+ hour12: false
1361
+ }).replace(',', '')}`;
1362
+ const isNameUnique = (entities, name, excludeId)=>{
1363
+ const trimmedName = name.trim();
1364
+ if (!trimmedName) return false;
1365
+ return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
1366
+ };
1367
+ const generateUniqueName = (entities, baseName)=>{
1368
+ const trimmedBaseName = baseName.trim();
1369
+ // If base name is unique, use it
1370
+ if (isNameUnique(entities, trimmedBaseName)) {
1371
+ return trimmedBaseName;
1372
+ }
1373
+ // Find a unique name by appending numbers
1374
+ let counter = 1;
1375
+ let uniqueName;
1376
+ do {
1377
+ uniqueName = `${trimmedBaseName} (${counter})`;
1378
+ counter++;
1379
+ }while (!isNameUnique(entities, uniqueName))
1380
+ return uniqueName;
1381
+ };
1382
+ /**
1383
+ * This function takes a FilterSet object and a prefix string as input.
1384
+ * It filters the root property of the FilterSet object and returns a
1385
+ * new FilterSet object that only contains filters with field names
1386
+ * that start with the specified prefix.
1387
+ *
1388
+ * @param fs - The FilterSet object to filter
1389
+ * @param prefix - The prefix to filter by
1390
+ * @returns - A new FilterSet object that only contains filters with field names that start with the specified prefix
1391
+ * @category Filters
1392
+ */ const extractFiltersWithPrefixFromFilterSet = (fs, prefix)=>{
1393
+ if (fs === undefined || fs.root === undefined) {
1394
+ return {
1395
+ mode: 'and',
1396
+ root: {}
1397
+ };
1398
+ }
1399
+ return Object.values(fs.root).reduce((acc, filter)=>{
1400
+ if (isIntersectionOrUnion(filter) || isNestedFilter(filter)) return acc;
1401
+ if (filter.field.startsWith(prefix)) {
1402
+ acc.root[filter.field] = filter;
1403
+ }
1404
+ return acc;
1405
+ }, {
1406
+ mode: 'and',
1407
+ root: {}
1408
+ });
1409
+ };
1410
+
1411
+ /**
1412
+ * Cohorts in Gen3 are defined as a set of filters for each index in the data.
1413
+ * This means one cohort id defined for all "tabs" in CohortBuilder (explorer)
1414
+ * Switching a cohort id means all the cohorts for the index are changed.
1415
+ */ const DEFAULT_COHORT_NAME = 'Cohort';
1416
+ const newCohort = ({ filters = {}, customName })=>{
1417
+ const ts = new Date().toISOString();
1418
+ const newName = customName ?? defaultCohortNameGenerator();
1419
+ const newId = createCohortId();
1420
+ return {
1421
+ name: newName,
1422
+ id: newId,
1423
+ filters: filters ?? {},
1424
+ modified: false,
1425
+ saved: false,
1426
+ createdDatetime: ts,
1427
+ modifiedDatetime: ts,
1428
+ counts: {}
1429
+ };
1430
+ };
1431
+ const nanoid = customAlphabet('1234567890abcdef', 16);
1432
+ const createCohortId = ()=>nanoid();
1433
+ const cohortsAdapter = createEntityAdapter({
1434
+ sortComparer: (a, b)=>{
1435
+ if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
1436
+ else return -1;
1437
+ },
1438
+ selectId: (cohort)=>cohort.id
1439
+ });
1440
+ // Create an initial unsaved cohort
1441
+ const initialCohort = newCohort({
1442
+ customName: DEFAULT_COHORT_NAME
1443
+ });
1444
+ const emptyInitialState = cohortsAdapter.getInitialState({
1445
+ currentCohortId: initialCohort.id,
1446
+ message: undefined
1447
+ });
1448
+ // Set the initial cohort in the adapter state
1449
+ const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
1450
+ const getCurrentCohortId = (state)=>state.currentCohortId;
1451
+ /**
1452
+ * Redux slice for cohort filters
1453
+ */ const cohortManagerSlice = createSlice({
1454
+ name: 'cohort',
1455
+ initialState: initialState$3,
1456
+ reducers: {
1457
+ createNewCohort: (state, action)=>{
1458
+ const baseName = action.payload.name || `Cohort`;
1459
+ const uniqueName = generateUniqueName(Object.values(state.entities), baseName);
1460
+ const cohort = newCohort({
1461
+ filters: action.payload.filters,
1462
+ customName: uniqueName
1463
+ });
1464
+ cohortsAdapter.addOne(state, cohort);
1465
+ state.currentCohortId = cohort.id;
1466
+ },
1467
+ updateCohortName: (state, action)=>{
1468
+ const { id, name } = action.payload;
1469
+ cohortsAdapter.updateOne(state, {
1470
+ id: id,
1471
+ changes: {
1472
+ name: name,
1473
+ modified: true,
1474
+ modifiedDatetime: new Date().toISOString()
1475
+ }
1476
+ });
1477
+ },
1478
+ removeCohort: (state, action)=>{
1479
+ const { id: cohortId } = action.payload;
1480
+ const removedCohortName = state.entities[cohortId].name;
1481
+ const totalCohorts = Object.keys(state.entities).length;
1482
+ if (totalCohorts <= 1) {
1483
+ cohortsAdapter.removeAll(state);
1484
+ const defaultCohort = newCohort({
1485
+ filters: {},
1486
+ customName: DEFAULT_COHORT_NAME
1487
+ });
1488
+ cohortsAdapter.addOne(state, defaultCohort);
1489
+ state.currentCohortId = defaultCohort.id;
1490
+ if (action?.payload.shouldShowMessage) {
1491
+ state.message = [
1492
+ `deleteCohort|${removedCohortName}|${state.currentCohortId}`
1493
+ ];
1494
+ }
1495
+ return;
1496
+ }
1497
+ cohortsAdapter.removeOne(state, cohortId);
1498
+ // deleted the current cohort so set to the most recent cohort
1499
+ if (state.currentCohortId === cohortId) {
1500
+ const remainingIds = Object.keys(state.entities);
1501
+ state.currentCohortId = remainingIds[0];
1502
+ }
1503
+ if (action?.payload.shouldShowMessage) {
1504
+ state.message = [
1505
+ `deleteCohort|${removedCohortName}|${state.currentCohortId}`
1506
+ ];
1507
+ }
1508
+ },
1509
+ // adds a filter to the cohort filter set at the given index
1510
+ updateCohortFilter: (state, action)=>{
1511
+ const { index, field, filter } = action.payload;
1512
+ const currentCohortId = getCurrentCohortId(state);
1513
+ if (!state.entities[currentCohortId]) {
1514
+ return;
1515
+ }
1516
+ cohortsAdapter.updateOne(state, {
1517
+ id: currentCohortId,
1518
+ changes: {
1519
+ filters: {
1520
+ ...state.entities[currentCohortId].filters,
1521
+ [index]: {
1522
+ mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
1523
+ root: {
1524
+ ...state.entities[currentCohortId]?.filters[index]?.root ?? {},
1525
+ [field]: filter
1526
+ }
1527
+ }
1528
+ },
1529
+ modified: true,
1530
+ modifiedDatetime: new Date().toISOString()
1531
+ }
1532
+ });
1533
+ },
1534
+ setCohortFilter: (state, action)=>{
1535
+ const { index, filters } = action.payload;
1536
+ const currentCohortId = getCurrentCohortId(state);
1537
+ if (!state.entities[currentCohortId]) {
1538
+ console.error(`no cohort with id=${currentCohortId} defined`);
1539
+ return;
1540
+ }
1541
+ cohortsAdapter.updateOne(state, {
1542
+ id: currentCohortId,
1543
+ changes: {
1544
+ filters: {
1545
+ ...state.entities[currentCohortId].filters,
1546
+ [index]: filters
1547
+ },
1548
+ modified: true,
1549
+ modifiedDatetime: new Date().toISOString()
1550
+ }
1551
+ });
1552
+ },
1553
+ setCohortIndexFilters: (state, action)=>{
1554
+ const currentCohortId = getCurrentCohortId(state);
1555
+ if (!state.entities[currentCohortId]) {
1556
+ console.error(`no cohort with id=${currentCohortId} defined`);
1557
+ return;
1558
+ }
1559
+ cohortsAdapter.updateOne(state, {
1560
+ id: currentCohortId,
1561
+ changes: {
1562
+ filters: action.payload.filters,
1563
+ modified: true,
1564
+ modifiedDatetime: new Date().toISOString()
1565
+ }
1566
+ });
1567
+ },
1568
+ // removes a filter to the cohort filter set at the given index
1569
+ removeCohortFilter: (state, action)=>{
1570
+ const { index, field } = action.payload;
1571
+ const currentCohortId = getCurrentCohortId(state);
1572
+ if (!state.entities[currentCohortId]) {
1573
+ console.error(`no cohort with id=${currentCohortId} defined`);
1574
+ return;
1575
+ }
1576
+ const filters = state.entities[currentCohortId]?.filters[index]?.root;
1577
+ if (!filters) {
1578
+ return;
1579
+ }
1580
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1581
+ const { [field]: _a, ...updated } = filters;
1582
+ cohortsAdapter.updateOne(state, {
1583
+ id: currentCohortId,
1584
+ changes: {
1585
+ filters: {
1586
+ ...state.entities[currentCohortId]?.filters,
1587
+ [index]: {
1588
+ mode: state.entities[currentCohortId].filters[index].mode,
1589
+ root: updated
1590
+ }
1591
+ },
1592
+ modified: true,
1593
+ modifiedDatetime: new Date().toISOString()
1594
+ }
1595
+ });
1596
+ },
1597
+ duplicateCohort: (state)=>{
1598
+ const currentCohortId = getCurrentCohortId(state);
1599
+ const currentCohort = state.entities[currentCohortId];
1600
+ const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
1601
+ const duplicatedCohort = newCohort({
1602
+ filters: {
1603
+ ...currentCohort.filters
1604
+ },
1605
+ customName: newName
1606
+ });
1607
+ cohortsAdapter.addOne(state, {
1608
+ ...duplicatedCohort,
1609
+ counts: {
1610
+ ...currentCohort.counts
1611
+ }
1612
+ });
1613
+ state.currentCohortId = duplicatedCohort.id;
1614
+ },
1615
+ // removes all filters from the cohort filter set at the given index
1616
+ clearCohortFilters: (state, action)=>{
1617
+ const { index } = action.payload;
1618
+ const currentCohortId = getCurrentCohortId(state);
1619
+ if (!state.entities[currentCohortId]) {
1620
+ console.error(`no cohort with id=${currentCohortId} defined`);
1621
+ return;
1622
+ }
1623
+ const filters = state.entities[currentCohortId]?.filters[index]?.root;
1624
+ if (!filters) {
1625
+ return;
1626
+ }
1627
+ cohortsAdapter.updateOne(state, {
1628
+ id: currentCohortId,
1629
+ changes: {
1630
+ filters: {
1631
+ ...state.entities[currentCohortId]?.filters,
1632
+ [index]: {
1633
+ mode: 'and',
1634
+ root: {}
1635
+ }
1636
+ },
1637
+ modified: true,
1638
+ modifiedDatetime: new Date().toISOString()
1639
+ }
1640
+ });
1641
+ },
1642
+ updateCohortCounts: (state, action)=>{
1643
+ const currentCohortId = getCurrentCohortId(state);
1644
+ const currentCohort = state.entities[currentCohortId];
1645
+ cohortsAdapter.updateOne(state, {
1646
+ id: currentCohortId,
1647
+ changes: {
1648
+ counts: {
1649
+ ...currentCohort.counts,
1650
+ ...action.payload
1651
+ }
1652
+ }
1653
+ });
1654
+ },
1655
+ updateCohortIndexCountById: (state, action)=>{
1656
+ const { index, cohortId, counts } = action.payload;
1657
+ const cohort = state.entities[cohortId];
1658
+ cohortsAdapter.updateOne(state, {
1659
+ id: cohortId,
1660
+ changes: {
1661
+ counts: {
1662
+ ...cohort.counts,
1663
+ ...{
1664
+ [index]: counts
1665
+ }
1666
+ }
1667
+ }
1668
+ });
1669
+ },
1670
+ setCurrentCohortId: (state, action)=>{
1671
+ state.currentCohortId = action.payload;
1672
+ },
1673
+ /** @hidden */ setCohortList: (state, action)=>{
1674
+ if (!action.payload) {
1675
+ cohortsAdapter.removeMany(state, state.ids);
1676
+ } else {
1677
+ cohortsAdapter.upsertMany(state, [
1678
+ ...action.payload
1679
+ ]);
1680
+ }
1681
+ }
1682
+ }
1683
+ });
1684
+ /**
1685
+ * Returns the selectors for the cohorts EntityAdapter
1686
+ * @param state - the CoreState
1687
+ *
1688
+ * @hidden
1689
+ */ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
1690
+ // Filter actions: addFilter, removeFilter, updateFilter
1691
+ const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
1692
+ const cohortReducer = cohortManagerSlice.reducer;
1693
+
1694
+ const initialState$2 = {};
1695
+ const expandSlice$1 = createSlice({
1696
+ name: 'CohortBuilder/filterExpand',
1697
+ initialState: initialState$2,
1698
+ reducers: {
1699
+ toggleCohortBuilderCategoryFilter: (state, action)=>{
1700
+ return {
1701
+ ...state,
1702
+ [action.payload.index]: {
1703
+ ...state[action.payload.index],
1704
+ [action.payload.field]: action.payload.expanded
1705
+ }
1706
+ };
1707
+ },
1708
+ toggleCohortBuilderAllFilters: (state, action)=>{
1709
+ return {
1710
+ ...state,
1711
+ [action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
1712
+ acc[k] = action.payload.expand;
1713
+ return acc;
1714
+ }, {})
1715
+ };
1716
+ }
1717
+ }
1718
+ });
1719
+ const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
1720
+ const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
1721
+ const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
1722
+ const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
1723
+
1724
+ const initialState$1 = {};
1725
+ const expandSlice = createSlice({
1726
+ name: 'CohortBuilder/filterCombineMode',
1727
+ initialState: initialState$1,
1728
+ reducers: {
1729
+ setCohortFilterCombineMode: (state, action)=>{
1730
+ return {
1731
+ ...state,
1732
+ [action.payload.index]: {
1733
+ ...state[action.payload.index],
1734
+ [action.payload.field]: action.payload.mode
1735
+ }
1736
+ };
1737
+ }
1738
+ }
1739
+ });
1740
+ const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
1741
+ const { setCohortFilterCombineMode } = expandSlice.actions;
1742
+ const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
1743
+
1744
+ const initialState = {
1745
+ shouldShareFilters: false,
1746
+ sharedFiltersMap: {}
1747
+ };
1748
+ const cohortSharedFiltersSlice = createSlice({
1749
+ name: 'cohortSharedFilters',
1750
+ initialState: initialState,
1751
+ reducers: {
1752
+ setShouldShareFilters: (state, action)=>{
1753
+ state.shouldShareFilters = action.payload;
1754
+ return state;
1755
+ },
1756
+ setSharedFilters: (state, action)=>{
1757
+ state.sharedFiltersMap = action.payload;
1758
+ }
1759
+ }
1760
+ });
1761
+ const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
1762
+ const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
1763
+ const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
1764
+ field
1690
1765
  ];
1691
- };
1766
+ const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
1767
+ const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
1768
+
1769
+ const cohortReducers = combineReducers({
1770
+ filtersExpanded: cohortBuilderFiltersExpandedReducer,
1771
+ filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
1772
+ sharedFilters: cohortSharedFiltersReducer,
1773
+ cohortManager: cohortReducer
1774
+ });
1775
+
1776
+ const rootReducer = combineReducers({
1777
+ gen3Services: gen3ServicesReducer,
1778
+ user: userReducer,
1779
+ gen3Apps: gen3AppReducer,
1780
+ drsHostnames: drsHostnamesReducer,
1781
+ modals: modalReducer,
1782
+ cohorts: cohortReducers,
1783
+ activeWorkspace: activeWorkspaceReducer,
1784
+ [guppyApiSliceReducerPath]: guppyApiReducer,
1785
+ [userAuthApiReducerPath]: userAuthApiReducer,
1786
+ [cartReducerPath]: cartReducer
1787
+ });
1692
1788
 
1693
1789
  /**
1694
1790
  * Flattens a deep nested JSON object skipping
@@ -1850,14 +1946,22 @@ function isHttpStatusError(error) {
1850
1946
  * @param {string} csrfToken - The CSRF token to include in the request headers.
1851
1947
  * @returns {FetchConfig} - The prepared fetch configuration object.
1852
1948
  */ const prepareFetchConfig = (parameters, csrfToken)=>{
1949
+ const headers = new Headers({
1950
+ Accept: 'application/json',
1951
+ 'Content-Type': 'application/json',
1952
+ ...csrfToken !== undefined && {
1953
+ 'X-CSRF-Token': csrfToken
1954
+ }
1955
+ });
1956
+ if (process.env.NODE_ENV === 'development') {
1957
+ // NOTE: This cookie can only be accessed from the client side
1958
+ // in development mode. Otherwise, the cookie is set as httpOnly
1959
+ const accessToken = getCookie('credentials_token');
1960
+ if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
1961
+ }
1853
1962
  return {
1854
1963
  method: 'POST',
1855
- headers: {
1856
- 'Content-Type': 'application/json',
1857
- ...csrfToken !== undefined && {
1858
- 'X-CSRF-Token': csrfToken
1859
- }
1860
- },
1964
+ headers: headers,
1861
1965
  body: JSON.stringify({
1862
1966
  type: parameters.type,
1863
1967
  filter: convertFilterSetToGqlFilter(parameters.filter),
@@ -2042,6 +2146,88 @@ const groupSharedFields = (data)=>{
2042
2146
  return data;
2043
2147
  };
2044
2148
 
2149
+ const customQueryStrForField = (field, query, depth = 0)=>{
2150
+ const indent = ' '.repeat(depth);
2151
+ const splittedFieldArray = field.split('.');
2152
+ const splittedField = splittedFieldArray.shift();
2153
+ if (splittedFieldArray.length === 0) {
2154
+ return `${indent}${splittedField} ${query}`;
2155
+ }
2156
+ return `${indent}${splittedField} {
2157
+ ${customQueryStrForField(splittedFieldArray.join('.'), query, depth + 1)}
2158
+ ${indent}}`;
2159
+ };
2160
+ // TODO: refactor the function below using customQueryStrForEachField and a wrapper function that passes the query
2161
+ const histogramQueryStrForEachField = (field)=>{
2162
+ const splittedFieldArray = field.split('.');
2163
+ const splittedField = splittedFieldArray.shift();
2164
+ if (splittedFieldArray.length === 0) {
2165
+ return `
2166
+ ${splittedField} {
2167
+ histogram {
2168
+ key
2169
+ count
2170
+ }
2171
+ }`;
2172
+ }
2173
+ return `
2174
+ ${splittedField} {
2175
+ ${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
2176
+ }`;
2177
+ };
2178
+ const statsQueryStrForEachField = (field)=>{
2179
+ const splittedFieldArray = field.split('.');
2180
+ const splittedField = splittedFieldArray.shift();
2181
+ if (splittedFieldArray.length === 0) {
2182
+ return `
2183
+ ${splittedField} {
2184
+ histogram {
2185
+ count
2186
+ min
2187
+ max
2188
+ avg
2189
+ sum
2190
+ }
2191
+ }`;
2192
+ }
2193
+ return `
2194
+ ${splittedField} {
2195
+ ${statsQueryStrForEachField(splittedFieldArray.join('.'))}
2196
+ }`;
2197
+ };
2198
+ const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
2199
+ ${mainField} {
2200
+ ${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
2201
+ key
2202
+ count
2203
+ missingFields {
2204
+ field
2205
+ count
2206
+ }
2207
+ termsFields {
2208
+ field
2209
+ count
2210
+ terms {
2211
+ key
2212
+ count
2213
+ }
2214
+ }
2215
+ }
2216
+ }`;
2217
+ const rawDataQueryStrForEachField = (field)=>{
2218
+ const splitFieldArray = field.split('.');
2219
+ const splitField = splitFieldArray.shift();
2220
+ if (splitFieldArray.length === 0) {
2221
+ return `
2222
+ ${splitField}
2223
+ `;
2224
+ }
2225
+ return `
2226
+ ${splitField} {
2227
+ ${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
2228
+ }`;
2229
+ };
2230
+
2045
2231
  const statusEndpoint = '/_status';
2046
2232
  const fetchJson = async (url)=>{
2047
2233
  const res = await fetch(url, {
@@ -2322,75 +2508,6 @@ const explorerTags = guppyApi.enhanceEndpoints({
2322
2508
  })
2323
2509
  })
2324
2510
  });
2325
- const histogramQueryStrForEachField = (field)=>{
2326
- const splittedFieldArray = field.split('.');
2327
- const splittedField = splittedFieldArray.shift();
2328
- if (splittedFieldArray.length === 0) {
2329
- return `
2330
- ${splittedField} {
2331
- histogram {
2332
- key
2333
- count
2334
- }
2335
- }`;
2336
- }
2337
- return `
2338
- ${splittedField} {
2339
- ${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
2340
- }`;
2341
- };
2342
- const statsQueryStrForEachField = (field)=>{
2343
- const splittedFieldArray = field.split('.');
2344
- const splittedField = splittedFieldArray.shift();
2345
- if (splittedFieldArray.length === 0) {
2346
- return `
2347
- ${splittedField} {
2348
- histogram {
2349
- count
2350
- min
2351
- max
2352
- avg
2353
- sum
2354
- }
2355
- }`;
2356
- }
2357
- return `
2358
- ${splittedField} {
2359
- ${statsQueryStrForEachField(splittedFieldArray.join('.'))}
2360
- }`;
2361
- };
2362
- const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
2363
- ${mainField} {
2364
- ${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
2365
- key
2366
- count
2367
- missingFields {
2368
- field
2369
- count
2370
- }
2371
- termsFields {
2372
- field
2373
- count
2374
- terms {
2375
- key
2376
- count
2377
- }
2378
- }
2379
- }
2380
- }`;
2381
- const rawDataQueryStrForEachField = (field)=>{
2382
- const splitFieldArray = field.split('.');
2383
- const splitField = splitFieldArray.shift();
2384
- if (splitFieldArray.length === 0) {
2385
- return `
2386
- ${splitField}
2387
- `;
2388
- }
2389
- return `
2390
- ${splitField} {
2391
- ${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
2392
- }`;
2393
- };
2394
2511
  const useGetArrayTypes = ()=>{
2395
2512
  {
2396
2513
  const { data, error } = useGetStatus();
@@ -2494,7 +2611,8 @@ const persistConfig = {
2494
2611
  storage,
2495
2612
  whitelist: [
2496
2613
  'cohorts',
2497
- 'activeWorkspace'
2614
+ 'activeWorkspace',
2615
+ 'cart'
2498
2616
  ]
2499
2617
  };
2500
2618
  const persistedReducer = persistReducer(persistConfig, rootReducer);
@@ -2742,6 +2860,28 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
2742
2860
  return await response.json();
2743
2861
  };
2744
2862
 
2863
+ const extractValuesFromObject = (jsonPathMappings, obj)=>{
2864
+ const result = {};
2865
+ const extractObjectValue = (jsonPath, obj)=>{
2866
+ const extractedValues = JSONPath({
2867
+ path: jsonPath,
2868
+ json: obj
2869
+ });
2870
+ return extractedValues.length > 0 ? extractedValues[0] : undefined;
2871
+ };
2872
+ for(const key in jsonPathMappings){
2873
+ if (key in Object.keys(jsonPathMappings)) {
2874
+ // Extract value from an object and store it in the result.
2875
+ result[key] = extractObjectValue(jsonPathMappings[key], obj);
2876
+ }
2877
+ }
2878
+ return result;
2879
+ };
2880
+ const ExtractValueFromObject = (obj, key, valueIfNotFound)=>{
2881
+ return obj?.[key] ?? valueIfNotFound;
2882
+ };
2883
+
2884
+ const DAYS_IN_YEAR = 365.25;
2745
2885
  /**
2746
2886
  * Converts HistogramData to HistogramDataAsStringKey by ensuring the key is a string.
2747
2887
  * If the key is already a string, it's used as is.
@@ -2757,6 +2897,79 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
2757
2897
  };
2758
2898
  const calculatePercentageAsNumber = (count, total)=>count ? count / total * 100 : 0;
2759
2899
  const calculatePercentageAsString = (count, total)=>`${(count / total * 100).toFixed(2)}%`;
2900
+ const capitalize = (original)=>{
2901
+ const customCapitalizations = {
2902
+ id: 'ID',
2903
+ uuid: 'UUID',
2904
+ dna: 'DNA',
2905
+ dbsnp: 'dbSNP',
2906
+ cosmic: 'COSMIC',
2907
+ civic: 'CIViC',
2908
+ dbgap: 'dbGaP',
2909
+ ecog: 'ECOG',
2910
+ bmi: 'BMI',
2911
+ gdc: 'GDC',
2912
+ cnv: 'CNV',
2913
+ ssm: 'SSM',
2914
+ aa: 'AA'
2915
+ };
2916
+ return original.split(' ').map((word)=>customCapitalizations[word.toLowerCase()] || `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' ');
2917
+ };
2918
+ const humanify = ({ term = '', capitalize: cap = true, facetTerm = false })=>{
2919
+ let original;
2920
+ let humanified;
2921
+ if (facetTerm) {
2922
+ // Splits on capital letters followed by lowercase letters to find
2923
+ // words squished together in a string.
2924
+ original = term?.split(/(?=[A-Z][a-z])/).join(' ');
2925
+ humanified = term?.replace(/\./g, ' ').replace(/_/g, ' ').trim();
2926
+ } else {
2927
+ const split = (original || term)?.split('.');
2928
+ humanified = split[split.length - 1]?.replace(/_/g, ' ').trim();
2929
+ // Special case 'name' to include any parent nested for sake of
2930
+ // specificity in the UI
2931
+ if (humanified === 'name' && split?.length > 1) {
2932
+ humanified = `${split[split?.length - 2]} ${humanified}`;
2933
+ }
2934
+ }
2935
+ return cap ? capitalize(humanified) : humanified;
2936
+ };
2937
+ /*https://github.com/NCI-GDC/portal-ui/blob/develop/src/packages/%40ncigdc/utils/ageDisplay.js*/ /**
2938
+ * Converts age in days into a human-readable format.
2939
+ *
2940
+ * @param ageInDays - The age in days.
2941
+ * @param yearsOnly - If true, only display years.
2942
+ * @defaultValue false
2943
+ * @param defaultValue - The default value to return if ageInDays is falsy.
2944
+ * @defaultValue "--"
2945
+ * @returns The formatted age string.
2946
+ */ const ageDisplay = (ageInDays, yearsOnly = false, defaultValue = '--')=>{
2947
+ if (ageInDays !== 0 && !ageInDays) {
2948
+ return defaultValue;
2949
+ }
2950
+ const calculateYearsAndDays = (years, days)=>days === 365 ? [
2951
+ years + 1,
2952
+ 0
2953
+ ] : [
2954
+ years,
2955
+ days
2956
+ ];
2957
+ const ABS_AGE_DAYS = Math.abs(ageInDays);
2958
+ const [years, remainingDays] = calculateYearsAndDays(Math.floor(ABS_AGE_DAYS / DAYS_IN_YEAR), Math.ceil(ABS_AGE_DAYS % DAYS_IN_YEAR));
2959
+ const formattedYears = years === 0 ? '' : `${years} ${years === 1 ? 'year' : 'years'}`;
2960
+ const formattedDays = !yearsOnly && remainingDays > 0 ? `${remainingDays} ${remainingDays === 1 ? 'day' : 'days'}` : years === 0 && remainingDays === 0 ? '0 days' : '';
2961
+ const ageString = [
2962
+ formattedYears,
2963
+ formattedDays
2964
+ ].filter(Boolean).join(' ');
2965
+ return ageInDays >= 0 ? ageString : `-${ageString}`;
2966
+ };
2967
+ /**
2968
+ * Given an object of JSON, stringify it into a string.
2969
+ * @param obj - the object to stringify
2970
+ * @param defaults - the default value to return if the object is undefined
2971
+ * @category Utility
2972
+ */ const stringifyJSONParam = (obj, defaults = '{}')=>obj ? JSON.stringify(obj) : defaults;
2760
2973
 
2761
2974
  const queryWTSFederatedLoginStatus = async (signal)=>{
2762
2975
  try {
@@ -4748,7 +4961,7 @@ const { useGraphQLQuery } = graphQLAPI;
4748
4961
  return {
4749
4962
  url: `${GEN3_GUPPY_API}/download`,
4750
4963
  method: 'POST',
4751
- body: JSON.stringify(queryBody),
4964
+ body: queryBody,
4752
4965
  cache: 'no-cache'
4753
4966
  };
4754
4967
  },
@@ -5301,24 +5514,6 @@ const userHasMethodOnAnyProject = (method, userAuthMapping = {})=>{
5301
5514
  };
5302
5515
  const userHasCreateOrUpdateOnAnyProject = (userAuthMapping = {})=>userHasMethodOnAnyProject('create', userAuthMapping) || userHasMethodOnAnyProject('update', userAuthMapping);
5303
5516
 
5304
- const extractValuesFromObject = (jsonPathMappings, obj)=>{
5305
- const result = {};
5306
- const extractObjectValue = (jsonPath, obj)=>{
5307
- const extractedValues = JSONPath({
5308
- path: jsonPath,
5309
- json: obj
5310
- });
5311
- return extractedValues.length > 0 ? extractedValues[0] : undefined;
5312
- };
5313
- for(const key in jsonPathMappings){
5314
- if (key in Object.keys(jsonPathMappings)) {
5315
- // Extract value from an object and store it in the result.
5316
- result[key] = extractObjectValue(jsonPathMappings[key], obj);
5317
- }
5318
- }
5319
- return result;
5320
- };
5321
-
5322
5517
  const SubmissionGraphqlQuery = `query transactionList {
5323
5518
  transactionList: transaction_log(last: 20) {
5324
5519
  id
@@ -5601,5 +5796,5 @@ const selectPaymodelStatus = createSelector(paymodelStatusSelector, (status)=>st
5601
5796
  const isWorkspaceActive = (status)=>status === WorkspaceStatus.Running || status === WorkspaceStatus.Launching || status === WorkspaceStatus.Terminating;
5602
5797
  const isWorkspaceRunningOrStopping = (status)=>status === WorkspaceStatus.Running || status === WorkspaceStatus.Terminating;
5603
5798
 
5604
- export { Accessibility, CohortStorage, CoreProvider, DataLibraryStoreMode, EmptyFilterSet, EmptyWorkspaceStatusResponse, EnumValueExtractorHandler, GEN3_API, GEN3_AUTHZ_API, GEN3_COMMONS_NAME, GEN3_CROSSWALK_API, GEN3_DOMAIN, GEN3_DOWNLOADS_ENDPOINT, GEN3_FENCE_API, GEN3_GUPPY_API, GEN3_MANIFEST_API, GEN3_MDS_API, GEN3_REDIRECT_URL, GEN3_SOWER_API, GEN3_SUBMISSION_API, GEN3_WORKSPACE_API, HTTPError, HTTPErrorMessages, HttpMethod, MissingServiceConfigurationError, Modals, PodConditionType, PodStatus, RequestedWorkspaceStatus, ToGqlHandler, ValueExtractorHandler, WorkspaceStatus, appendFilterToOperation, buildGetAggregationQuery, buildGetStatsAggregationQuery, buildListItemsGroupedByDataset, calculatePercentageAsNumber, calculatePercentageAsString, clearActiveWorkspaceId, clearCohortFilters, cohortReducer, convertFilterSetToGqlFilter, convertFilterToGqlFilter, convertGqlFilterToFilter, convertToHistogramDataAsStringKey, convertToQueryString, coreStore, createAppApiForRTKQ, createAppStore, createGen3App, createGen3AppWithOwnStore, createNewCohort, createUseCoreDataHook, defaultCohortNameGenerator, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, drsHostnamesReducer, duplicateCohort, explorerApi, explorerTags, extractEnumFilterValue, extractFieldNameFromFullFieldName, extractFileDatasetsInRecords, extractFilterValue, extractIndexAndFieldNameFromFullFieldName, extractIndexFromDataLibraryCohort, extractIndexFromFullFieldName, fetchFence, fetchFencePresignedURL, fetchJSONDataFromURL, fetchJson, fetchUserState, fieldNameToTitle, filterSetToOperation, gen3Api, generateUniqueName, getCurrentTimestamp, getFederatedLoginStatus, getGen3AppId, getNumberOfItemsInDatalist, getRemoteSupportServiceRegistry, getTimestamp, graphQLAPI, graphQLWithTags, groupSharedFields, guppyAPISliceMiddleware, guppyApi, guppyApiReducer, guppyApiSliceReducerPath, handleGqlOperation, handleOperation, hideModal, histogramQueryStrForEachField, isAdditionalDataItem, isArray, isAuthenticated, isCohortItem, isDataLibraryAPIResponse, isDatalistAPI, isErrorWithMessage, isFetchBaseQueryError, isFetchError, isFetchParseError, isFileItem, isFilterEmpty, isFilterSet, isGQLIntersection, isGQLUnion, isGuppyAggregationData, isHistogramData, isHistogramDataAArray, isHistogramDataAnEnum, isHistogramDataArray, isHistogramDataArrayARange, isHistogramDataArrayAnEnum, isHistogramDataCollection, isHistogramRangeData, isHttpStatusError, isIndexedFilterSetEmpty, isIntersection, isJSONObject, isJSONValue, isJSONValueArray, isNameUnique, isNotDefined, isObject, isOperandsType, isOperationWithField, isOperatorWithFieldAndArrayOfOperands, isPending, isProgramUrl, isRootUrl, isStatsValue, isStatsValuesArray, isString, isTimeGreaterThan, isUnion, isWorkspaceActive, isWorkspaceRunningOrStopping, listifyMethodsFromMapping, logoutFence, manifestApi, manifestTags, nestedHistogramQueryStrForEachField, prepareUrl, prependIndexToFieldName, processHistogramResponse, projectCodeFromResourcePath, queryMultipleMDSRecords, rawDataQueryStrForEachField, registerDefaultRemoteSupport, removeCohort, removeCohortFilter, requestorApi, resetUserState, resourcePathFromProjectID, roundHistogramResponse, selectActiveWorkspaceId, selectActiveWorkspaceStatus, selectAllCohortFiltersCollapsed, selectAllCohorts, selectAuthzMappingData, selectAvailableCohortByName, selectAvailableCohorts, selectCSRFToken, selectCSRFTokenData, selectCohortById, selectCohortFilterCombineMode, selectCohortFilterExpanded, selectCohortFilters, selectCohortIds, selectCurrentCohort, selectCurrentCohortFilters, selectCurrentCohortId, selectCurrentCohortModified, selectCurrentCohortName, selectCurrentCohortSaved, selectCurrentMessage, selectCurrentModal, selectGen3AppByName, selectGen3AppMetadataByName, selectHeadersWithCSRFToken, selectIndexFilters, selectIndexedFilterByName, selectPaymodelStatus, selectRequestedWorkspaceStatus, selectRequestedWorkspaceStatusTimestamp, selectSharedFilters, selectSharedFiltersForFields, selectShouldShareFilters, selectTotalCohorts, selectUser, selectUserAuthStatus, selectUserData, selectUserDetails, selectUserLoginStatus, selectWorkspaceStatus, selectWorkspaceStatusFromService, setActiveWorkspace, setActiveWorkspaceId, setActiveWorkspaceStatus, setCohortFilter, setCohortFilterCombineMode, setCohortIndexFilters, setCohortList, setCurrentCohortId, setDRSHostnames, setRequestedWorkspaceStatus, setSharedFilters, setShouldShareFilters, setupCoreStore, showModal, statsQueryStrForEachField, submissionApi, toggleCohortBuilderAllFilters, toggleCohortBuilderCategoryFilter, trimFirstFieldNameToTitle, updateCohortFilter, updateCohortName, useAddCohortManifestMutation, useAddFileManifestMutation, useAddMetadataManifestMutation, useAddNewCredentialMutation, useAskQuestionMutation, useAuthorizeFromCredentialsMutation, useCoreDispatch, useCoreSelector, useCreateAuthzResourceMutation, useCreateRequestMutation, useDataLibrary, useDownloadFromGuppyMutation, useFetchUserDetailsQuery, useGeneralGQLQuery, useGetAISearchStatusQuery, useGetAISearchVersionQuery, useGetAccessibleDataQuery, useGetActivePayModelQuery, useGetAggMDSQuery, useGetAggsQuery, useGetAllFieldsForTypeQuery, useGetArrayTypes, useGetAuthzMappingsQuery, useGetAuthzResourcesQuery, useGetCSRFQuery, useGetCohortManifestQuery, useGetCountsQuery, useGetCredentialsQuery, useGetCrosswalkDataQuery, useGetDataQuery, useGetDictionaryQuery, useGetDownloadQuery, useGetExternalLoginsQuery, useGetFederatedLoginStatus, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetFileFromManifestQuery, useGetFileManifestQuery, useGetIndexAggMDSQuery, useGetIndexFields, useGetJWKKeysQuery, useGetLoginProvidersQuery, useGetMDSQuery, useGetManifestServiceStatusQuery, useGetMetadataByIdQuery, useGetMetadataFromManifestQuery, useGetMetadataManifestQuery, useGetProjectsDetailsQuery, useGetProjectsQuery, useGetRawDataAndTotalCountsQuery, useGetSharedFieldsForIndexQuery, useGetSowerJobListQuery, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery, useGetStatsAggregationsQuery, useGetStatus, useGetSubAggsQuery, useGetSubmissionGraphQLQuery, useGetSubmissionsQuery, useGetTagsQuery, useGetWorkspaceOptionsQuery, useGetWorkspacePayModelsQuery, useGetWorkspaceStatusQuery, useGraphQLQuery, useIsExternalConnectedQuery, useIsUserLoggedIn, useLaunchWorkspaceMutation, useLazyFetchUserDetailsQuery, useLazyGeneralGQLQuery, useLazyGetAggsQuery, useLazyGetAuthzMappingsQuery, useLazyGetAuthzResourcesQuery, useLazyGetCSRFQuery, useLazyGetCountsQuery, useLazyGetCrosswalkDataQuery, useLazyGetDownloadQuery, useLazyGetExternalLoginsQuery, useLazyGetManifestServiceStatusQuery, useLazyGetProjectsQuery, useLazyGetSowerJobListQuery, useLazyGetStatsAggregationsQuery, useLazyGetSubmissionGraphQLQuery, useLazyIsExternalConnectedQuery, useLazyRequestQuery, usePrevious, useRemoveCredentialMutation, useRequestByIdQuery, useRequestQuery, useRequestorStatusQuery, useSetCurrentPayModelMutation, useSubmitSowerJobMutation, useTerminateWorkspaceMutation, useUserAuth, useUserRequestQuery, userHasCreateOrUpdateOnAnyProject, userHasDataUpload, userHasMethodForServiceOnProject, userHasMethodForServiceOnResource, userHasMethodOnAnyProject, userHasSheepdogProgramAdmin, userHasSheepdogProjectAdmin };
5799
+ export { Accessibility, CART_LIMIT, CohortStorage, CoreProvider, DAYS_IN_YEAR, DataLibraryStoreMode, EmptyFilterSet, EmptyWorkspaceStatusResponse, EnumValueExtractorHandler, ExtractValueFromObject, GEN3_API, GEN3_AUTHZ_API, GEN3_COMMONS_NAME, GEN3_CROSSWALK_API, GEN3_DOMAIN, GEN3_DOWNLOADS_ENDPOINT, GEN3_FENCE_API, GEN3_GUPPY_API, GEN3_MANIFEST_API, GEN3_MDS_API, GEN3_REDIRECT_URL, GEN3_SOWER_API, GEN3_SUBMISSION_API, GEN3_WORKSPACE_API, HTTPError, HTTPErrorMessages, HttpMethod, MissingServiceConfigurationError, Modals, PodConditionType, PodStatus, RequestedWorkspaceStatus, ToGqlHandler, ValueExtractorHandler, WorkspaceStatus, addItemsToCart, ageDisplay, appendFilterToOperation, buildGetAggregationQuery, buildGetStatsAggregationQuery, buildListItemsGroupedByDataset, buildNestedGQLFilter, calculatePercentageAsNumber, calculatePercentageAsString, capitalize, cartReducer, cartReducerPath, clearActiveWorkspaceId, clearCohortFilters, cohortReducer, convertFilterSetToGqlFilter, convertFilterToGqlFilter, convertGqlFilterToFilter, convertToHistogramDataAsStringKey, convertToQueryString, coreStore, createAppApiForRTKQ, createAppStore, createGen3App, createGen3AppWithOwnStore, createNewCohort, createUseCoreDataHook, customQueryStrForField, defaultCohortNameGenerator, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, drsHostnamesReducer, duplicateCohort, explorerApi, explorerTags, extractEnumFilterValue, extractFieldNameFromFullFieldName, extractFileDatasetsInRecords, extractFilterValue, extractFiltersWithPrefixFromFilterSet, extractIndexAndFieldNameFromFullFieldName, extractIndexFromDataLibraryCohort, extractIndexFromFullFieldName, fetchFence, fetchFencePresignedURL, fetchJSONDataFromURL, fetchJson, fetchUserState, fieldNameToTitle, filterSetToOperation, gen3Api, generateUniqueName, getCurrentTimestamp, getFederatedLoginStatus, getGen3AppId, getNumberOfItemsInDatalist, getRemoteSupportServiceRegistry, getTimestamp, graphQLAPI, graphQLWithTags, groupSharedFields, guppyAPISliceMiddleware, guppyApi, guppyApiReducer, guppyApiSliceReducerPath, handleGqlOperation, handleOperation, hideModal, histogramQueryStrForEachField, humanify, isAdditionalDataItem, isArray, isAuthenticated, isCohortItem, isDataLibraryAPIResponse, isDatalistAPI, isErrorWithMessage, isFetchBaseQueryError, isFetchError, isFetchParseError, isFileItem, isFilterEmpty, isFilterSet, isGQLIntersection, isGQLUnion, isGuppyAggregationData, isHistogramData, isHistogramDataAArray, isHistogramDataAnEnum, isHistogramDataArray, isHistogramDataArrayARange, isHistogramDataArrayAnEnum, isHistogramDataCollection, isHistogramRangeData, isHttpStatusError, isIndexedFilterSetEmpty, isIntersection, isIntersectionOrUnion, isJSONObject, isJSONValue, isJSONValueArray, isNameUnique, isNestedFilter, isNotDefined, isObject, isOperandsType, isOperationWithField, isOperatorWithFieldAndArrayOfOperands, isPending, isProgramUrl, isRootUrl, isStatsValue, isStatsValuesArray, isString, isTimeGreaterThan, isUnion, isWorkspaceActive, isWorkspaceRunningOrStopping, listifyMethodsFromMapping, logoutFence, manifestApi, manifestTags, nestedHistogramQueryStrForEachField, prepareUrl, prependIndexToFieldName, processHistogramResponse, projectCodeFromResourcePath, queryMultipleMDSRecords, rawDataQueryStrForEachField, registerDefaultRemoteSupport, removeCohort, removeCohortFilter, removeItemsFromCart, requestorApi, resetUserState, resourcePathFromProjectID, roundHistogramResponse, selectActiveWorkspaceId, selectActiveWorkspaceStatus, selectAllCohortFiltersCollapsed, selectAllCohorts, selectAuthzMappingData, selectAvailableCohortByName, selectAvailableCohorts, selectCSRFToken, selectCSRFTokenData, selectCart, selectCartCount, selectCartItem, selectCartItems, selectCohortById, selectCohortFilterCombineMode, selectCohortFilterExpanded, selectCohortFilters, selectCohortIds, selectCurrentCohort, selectCurrentCohortFilters, selectCurrentCohortId, selectCurrentCohortModified, selectCurrentCohortName, selectCurrentCohortSaved, selectCurrentMessage, selectCurrentModal, selectGen3AppByName, selectGen3AppMetadataByName, selectHeadersWithCSRFToken, selectIndexFilters, selectIndexedFilterByName, selectPaymodelStatus, selectRequestedWorkspaceStatus, selectRequestedWorkspaceStatusTimestamp, selectSharedFilters, selectSharedFiltersForFields, selectShouldShareFilters, selectTotalCohorts, selectUser, selectUserAuthStatus, selectUserData, selectUserDetails, selectUserLoginStatus, selectWorkspaceStatus, selectWorkspaceStatusFromService, setActiveWorkspace, setActiveWorkspaceId, setActiveWorkspaceStatus, setCohortFilter, setCohortFilterCombineMode, setCohortIndexFilters, setCohortList, setCurrentCohortId, setDRSHostnames, setRequestedWorkspaceStatus, setSharedFilters, setShouldShareFilters, setupCoreStore, showModal, statsQueryStrForEachField, stringifyJSONParam, submissionApi, toggleCohortBuilderAllFilters, toggleCohortBuilderCategoryFilter, trimFirstFieldNameToTitle, updateCohortFilter, updateCohortName, useAddCohortManifestMutation, useAddFileManifestMutation, useAddMetadataManifestMutation, useAddNewCredentialMutation, useAskQuestionMutation, useAuthorizeFromCredentialsMutation, useCoreDispatch, useCoreSelector, useCreateAuthzResourceMutation, useCreateRequestMutation, useDataLibrary, useDownloadFromGuppyMutation, useFetchUserDetailsQuery, useGeneralGQLQuery, useGetAISearchStatusQuery, useGetAISearchVersionQuery, useGetAccessibleDataQuery, useGetActivePayModelQuery, useGetAggMDSQuery, useGetAggsQuery, useGetAllFieldsForTypeQuery, useGetArrayTypes, useGetAuthzMappingsQuery, useGetAuthzResourcesQuery, useGetCSRFQuery, useGetCohortManifestQuery, useGetCountsQuery, useGetCredentialsQuery, useGetCrosswalkDataQuery, useGetDataQuery, useGetDictionaryQuery, useGetDownloadQuery, useGetExternalLoginsQuery, useGetFederatedLoginStatus, useGetFieldCountSummaryQuery, useGetFieldsForIndexQuery, useGetFileFromManifestQuery, useGetFileManifestQuery, useGetIndexAggMDSQuery, useGetIndexFields, useGetJWKKeysQuery, useGetLoginProvidersQuery, useGetMDSQuery, useGetManifestServiceStatusQuery, useGetMetadataByIdQuery, useGetMetadataFromManifestQuery, useGetMetadataManifestQuery, useGetProjectsDetailsQuery, useGetProjectsQuery, useGetRawDataAndTotalCountsQuery, useGetSharedFieldsForIndexQuery, useGetSowerJobListQuery, useGetSowerJobStatusQuery, useGetSowerOutputQuery, useGetSowerServiceStatusQuery, useGetStatsAggregationsQuery, useGetStatus, useGetSubAggsQuery, useGetSubmissionGraphQLQuery, useGetSubmissionsQuery, useGetTagsQuery, useGetWorkspaceOptionsQuery, useGetWorkspacePayModelsQuery, useGetWorkspaceStatusQuery, useGraphQLQuery, useIsExternalConnectedQuery, useIsUserLoggedIn, useLaunchWorkspaceMutation, useLazyFetchUserDetailsQuery, useLazyGeneralGQLQuery, useLazyGetAggsQuery, useLazyGetAuthzMappingsQuery, useLazyGetAuthzResourcesQuery, useLazyGetCSRFQuery, useLazyGetCountsQuery, useLazyGetCrosswalkDataQuery, useLazyGetDownloadQuery, useLazyGetExternalLoginsQuery, useLazyGetManifestServiceStatusQuery, useLazyGetProjectsQuery, useLazyGetSowerJobListQuery, useLazyGetStatsAggregationsQuery, useLazyGetSubmissionGraphQLQuery, useLazyIsExternalConnectedQuery, useLazyRequestQuery, usePrevious, useRemoveCredentialMutation, useRequestByIdQuery, useRequestQuery, useRequestorStatusQuery, useSetCurrentPayModelMutation, useSubmitSowerJobMutation, useTerminateWorkspaceMutation, useUserAuth, useUserRequestQuery, userHasCreateOrUpdateOnAnyProject, userHasDataUpload, userHasMethodForServiceOnProject, userHasMethodForServiceOnResource, userHasMethodOnAnyProject, userHasSheepdogProgramAdmin, userHasSheepdogProjectAdmin };
5605
5800
  //# sourceMappingURL=index.js.map