@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.
- package/dist/cjs/index.js +887 -672
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/server.js +45 -0
- package/dist/cjs/server.js.map +1 -0
- package/dist/dts/constants.d.ts +1 -0
- package/dist/dts/constants.d.ts.map +1 -1
- package/dist/dts/features/cart/cartSelectors.d.ts +129 -0
- package/dist/dts/features/cart/cartSelectors.d.ts.map +1 -0
- package/dist/dts/features/cart/cartSlice.d.ts +24 -0
- package/dist/dts/features/cart/cartSlice.d.ts.map +1 -0
- package/dist/dts/features/cart/index.d.ts +5 -0
- package/dist/dts/features/cart/index.d.ts.map +1 -0
- package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts +2 -0
- package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts.map +1 -0
- package/dist/dts/features/cohort/cohortManagerSelector.d.ts +4 -0
- package/dist/dts/features/cohort/cohortManagerSelector.d.ts.map +1 -1
- package/dist/dts/features/cohort/cohortManagerSlice.d.ts +1 -0
- package/dist/dts/features/cohort/cohortManagerSlice.d.ts.map +1 -1
- package/dist/dts/features/cohort/utils.d.ts +13 -0
- package/dist/dts/features/cohort/utils.d.ts.map +1 -1
- package/dist/dts/features/filters/filters.d.ts +14 -0
- package/dist/dts/features/filters/filters.d.ts.map +1 -1
- package/dist/dts/features/filters/types.d.ts +7 -0
- package/dist/dts/features/filters/types.d.ts.map +1 -1
- package/dist/dts/features/guppy/guppyApi.d.ts.map +1 -1
- package/dist/dts/features/guppy/guppySlice.d.ts +0 -4
- package/dist/dts/features/guppy/guppySlice.d.ts.map +1 -1
- package/dist/dts/features/guppy/index.d.ts +6 -0
- package/dist/dts/features/guppy/index.d.ts.map +1 -1
- package/dist/dts/features/guppy/queryGenerators.d.ts +6 -0
- package/dist/dts/features/guppy/queryGenerators.d.ts.map +1 -0
- package/dist/dts/features/guppy/tests/queryGenerators.unit.test.d.ts +2 -0
- package/dist/dts/features/guppy/tests/queryGenerators.unit.test.d.ts.map +1 -0
- package/dist/dts/features/guppy/utils.d.ts +1 -1
- package/dist/dts/features/guppy/utils.d.ts.map +1 -1
- package/dist/dts/features/user/userSliceRTK.d.ts +3 -0
- package/dist/dts/features/user/userSliceRTK.d.ts.map +1 -1
- package/dist/dts/hooks.d.ts +2 -0
- package/dist/dts/hooks.d.ts.map +1 -1
- package/dist/dts/index.d.ts +3 -2
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/reducers.d.ts +2 -0
- package/dist/dts/reducers.d.ts.map +1 -1
- package/dist/dts/server.d.ts +4 -0
- package/dist/dts/server.d.ts.map +1 -0
- package/dist/dts/store.d.ts +4 -0
- package/dist/dts/store.d.ts.map +1 -1
- package/dist/dts/utils/conversions.d.ts +7 -0
- package/dist/dts/utils/conversions.d.ts.map +1 -1
- package/dist/dts/utils/extractvalues.d.ts +1 -0
- package/dist/dts/utils/extractvalues.d.ts.map +1 -1
- package/dist/dts/utils/index.d.ts +3 -2
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/esm/index.js +868 -673
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server.js +29 -0
- package/dist/esm/server.js.map +1 -0
- package/dist/index.d.ts +420 -199
- package/dist/server.d.ts +31 -0
- package/package.json +7 -2
package/dist/cjs/index.js
CHANGED
|
@@ -6,18 +6,18 @@ var cookiesNext = require('cookies-next');
|
|
|
6
6
|
var query = require('@reduxjs/toolkit/query');
|
|
7
7
|
var reactRedux = require('react-redux');
|
|
8
8
|
var React = require('react');
|
|
9
|
+
var graphql = require('graphql');
|
|
10
|
+
var jsonpathPlus = require('jsonpath-plus');
|
|
11
|
+
var lodash = require('lodash');
|
|
9
12
|
var nanoid$1 = require('nanoid');
|
|
10
13
|
var useSWR = require('swr');
|
|
11
|
-
var lodash = require('lodash');
|
|
12
14
|
var flat = require('flat');
|
|
13
15
|
var Papa = require('papaparse');
|
|
14
|
-
var jsonpathPlus = require('jsonpath-plus');
|
|
15
16
|
var reduxPersist = require('redux-persist');
|
|
16
17
|
var createWebStorage = require('redux-persist/lib/storage/createWebStorage');
|
|
17
18
|
var react$1 = require('redux-persist/integration/react');
|
|
18
19
|
var idb = require('idb');
|
|
19
20
|
var useDeepCompare = require('use-deep-compare');
|
|
20
|
-
var graphql = require('graphql');
|
|
21
21
|
var uuid = require('uuid');
|
|
22
22
|
var reactCookie = require('react-cookie');
|
|
23
23
|
var Queue = require('queue');
|
|
@@ -72,6 +72,7 @@ const FILE_DELIMITERS = {
|
|
|
72
72
|
tsv: '\t',
|
|
73
73
|
csv: ','
|
|
74
74
|
};
|
|
75
|
+
const CART_LIMIT = 10000;
|
|
75
76
|
|
|
76
77
|
const isFetchError = (obj)=>{
|
|
77
78
|
if (typeof obj !== 'object' || obj === null) {
|
|
@@ -426,7 +427,7 @@ const useCoreDispatch = reactRedux.useDispatch.withTypes();
|
|
|
426
427
|
});
|
|
427
428
|
const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
|
|
428
429
|
const isPending = (loginStatus)=>loginStatus === 'pending';
|
|
429
|
-
const initialState$
|
|
430
|
+
const initialState$9 = {
|
|
430
431
|
status: 'uninitialized',
|
|
431
432
|
loginStatus: 'unauthenticated',
|
|
432
433
|
error: undefined
|
|
@@ -437,9 +438,9 @@ const initialState$8 = {
|
|
|
437
438
|
* @returns: status messages wrapped around fetchUserState response dict
|
|
438
439
|
*/ const slice$4 = toolkit.createSlice({
|
|
439
440
|
name: 'fence/user',
|
|
440
|
-
initialState: initialState$
|
|
441
|
+
initialState: initialState$9,
|
|
441
442
|
reducers: {
|
|
442
|
-
resetUserState: ()=>initialState$
|
|
443
|
+
resetUserState: ()=>initialState$9
|
|
443
444
|
},
|
|
444
445
|
extraReducers: (builder)=>{
|
|
445
446
|
builder.addCase(fetchUserState.fulfilled, (_, action)=>{
|
|
@@ -581,11 +582,11 @@ const { useGetExternalLoginsQuery, useLazyGetExternalLoginsQuery, useLazyIsExter
|
|
|
581
582
|
}
|
|
582
583
|
};
|
|
583
584
|
|
|
584
|
-
const initialState$
|
|
585
|
+
const initialState$8 = {};
|
|
585
586
|
// TODO: document what this does
|
|
586
587
|
const slice$3 = toolkit.createSlice({
|
|
587
588
|
name: 'drsResolver',
|
|
588
|
-
initialState: initialState$
|
|
589
|
+
initialState: initialState$8,
|
|
589
590
|
reducers: {
|
|
590
591
|
setDRSHostnames: (_state, action)=>{
|
|
591
592
|
return action.payload;
|
|
@@ -607,12 +608,12 @@ const lookupGen3App = (id)=>{
|
|
|
607
608
|
else return null;
|
|
608
609
|
};
|
|
609
610
|
|
|
610
|
-
const initialState$
|
|
611
|
+
const initialState$7 = {
|
|
611
612
|
gen3Apps: {}
|
|
612
613
|
};
|
|
613
614
|
const slice$2 = toolkit.createSlice({
|
|
614
615
|
name: 'gen3Apps',
|
|
615
|
-
initialState: initialState$
|
|
616
|
+
initialState: initialState$7,
|
|
616
617
|
reducers: {
|
|
617
618
|
addGen3AppMetadata: (state, action)=>{
|
|
618
619
|
const { name, requiredEntityTypes } = action.payload;
|
|
@@ -643,13 +644,13 @@ const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize
|
|
|
643
644
|
Modals["GeneralErrorModal"] = "GeneralErrorModal";
|
|
644
645
|
return Modals;
|
|
645
646
|
}({});
|
|
646
|
-
const initialState$
|
|
647
|
+
const initialState$6 = {
|
|
647
648
|
currentModal: null
|
|
648
649
|
};
|
|
649
650
|
//Creates a modal slice for tracking showModal and hideModal state.
|
|
650
651
|
const slice$1 = toolkit.createSlice({
|
|
651
652
|
name: 'modals',
|
|
652
|
-
initialState: initialState$
|
|
653
|
+
initialState: initialState$6,
|
|
653
654
|
reducers: {
|
|
654
655
|
showModal: (state, action)=>{
|
|
655
656
|
state.currentModal = action.payload.modal;
|
|
@@ -722,7 +723,7 @@ const getTimestamp = ()=>{
|
|
|
722
723
|
};
|
|
723
724
|
|
|
724
725
|
const NO_WORKSPACE_ID = 'none';
|
|
725
|
-
const initialState$
|
|
726
|
+
const initialState$5 = {
|
|
726
727
|
id: NO_WORKSPACE_ID,
|
|
727
728
|
status: WorkspaceStatus.NotFound,
|
|
728
729
|
requestedStatus: RequestedWorkspaceStatus.Unset,
|
|
@@ -730,7 +731,7 @@ const initialState$4 = {
|
|
|
730
731
|
};
|
|
731
732
|
const slice = toolkit.createSlice({
|
|
732
733
|
name: 'ActiveWorkspace',
|
|
733
|
-
initialState: initialState$
|
|
734
|
+
initialState: initialState$5,
|
|
734
735
|
reducers: {
|
|
735
736
|
setActiveWorkspaceId: (state, action)=>{
|
|
736
737
|
state = {
|
|
@@ -772,6 +773,25 @@ const selectActiveWorkspaceStatus = (state)=>state.activeWorkspace.status;
|
|
|
772
773
|
const selectRequestedWorkspaceStatus = (state)=>state.activeWorkspace.requestedStatus;
|
|
773
774
|
const selectRequestedWorkspaceStatusTimestamp = (state)=>state.activeWorkspace.requestedStatusTimestamp;
|
|
774
775
|
|
|
776
|
+
const cartAdapter = toolkit.createEntityAdapter({
|
|
777
|
+
selectId: (item)=>item.id
|
|
778
|
+
});
|
|
779
|
+
const initialState$4 = cartAdapter.getInitialState({});
|
|
780
|
+
const cartSlice = toolkit.createSlice({
|
|
781
|
+
name: 'cart',
|
|
782
|
+
initialState: initialState$4,
|
|
783
|
+
reducers: {
|
|
784
|
+
addItemsToCart: cartAdapter.addMany,
|
|
785
|
+
removeItemsFromCart: cartAdapter.removeMany
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
const cartReducer = cartSlice.reducer;
|
|
789
|
+
const { addItemsToCart, removeItemsFromCart } = cartSlice.actions;
|
|
790
|
+
|
|
791
|
+
const { selectById: selectCartItem, selectIds: selectCartItems, selectAll: selectCart, selectTotal: selectCartCount } = cartAdapter.getSelectors((state)=>state.cart);
|
|
792
|
+
|
|
793
|
+
const cartReducerPath = 'cart';
|
|
794
|
+
|
|
775
795
|
/**
|
|
776
796
|
* Creates a base class core API for guppy API calls.
|
|
777
797
|
* @returns: guppy core API with guppyAPIFetch base query
|
|
@@ -806,6 +826,15 @@ const selectRequestedWorkspaceStatusTimestamp = (state)=>state.activeWorkspace.r
|
|
|
806
826
|
data: await response.json()
|
|
807
827
|
};
|
|
808
828
|
} catch (e) {
|
|
829
|
+
if (e instanceof graphql.GraphQLError) {
|
|
830
|
+
return {
|
|
831
|
+
error: {
|
|
832
|
+
message: e.message,
|
|
833
|
+
locations: e.locations,
|
|
834
|
+
path: e.path
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
}
|
|
809
838
|
if (e instanceof Error) return {
|
|
810
839
|
error: e.message
|
|
811
840
|
};
|
|
@@ -820,578 +849,176 @@ const guppyAPISliceMiddleware = guppyApi.middleware;
|
|
|
820
849
|
const guppyApiSliceReducerPath = guppyApi.reducerPath;
|
|
821
850
|
const guppyApiReducer = guppyApi.reducer;
|
|
822
851
|
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
hour12: false
|
|
826
|
-
}).replace(',', '')}`;
|
|
827
|
-
const isNameUnique = (entities, name, excludeId)=>{
|
|
828
|
-
const trimmedName = name.trim();
|
|
829
|
-
if (!trimmedName) return false;
|
|
830
|
-
return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
|
|
852
|
+
const isOperationWithField = (operation)=>{
|
|
853
|
+
return operation?.field !== undefined;
|
|
831
854
|
};
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
return
|
|
855
|
+
const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
|
|
856
|
+
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
|
|
857
|
+
) {
|
|
858
|
+
const { operator } = operation.operator;
|
|
859
|
+
return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
|
|
860
|
+
}
|
|
861
|
+
return false;
|
|
862
|
+
};
|
|
863
|
+
const extractFilterValue = (op)=>{
|
|
864
|
+
const valueExtractorHandler = new ValueExtractorHandler();
|
|
865
|
+
return handleOperation(valueExtractorHandler, op);
|
|
866
|
+
};
|
|
867
|
+
const extractEnumFilterValue = (op)=>{
|
|
868
|
+
const enumValueExtractorHandler = new EnumValueExtractorHandler();
|
|
869
|
+
const results = handleOperation(enumValueExtractorHandler, op);
|
|
870
|
+
return results ?? [];
|
|
871
|
+
};
|
|
872
|
+
const assertNever = (x)=>{
|
|
873
|
+
throw Error(`Exhaustive comparison did not handle: ${x}`);
|
|
874
|
+
};
|
|
875
|
+
const handleOperation = (handler, op)=>{
|
|
876
|
+
switch(op.operator){
|
|
877
|
+
case '=':
|
|
878
|
+
return handler.handleEquals(op);
|
|
879
|
+
case '!=':
|
|
880
|
+
return handler.handleNotEquals(op);
|
|
881
|
+
case '<':
|
|
882
|
+
return handler.handleLessThan(op);
|
|
883
|
+
case '<=':
|
|
884
|
+
return handler.handleLessThanOrEquals(op);
|
|
885
|
+
case '>':
|
|
886
|
+
return handler.handleGreaterThan(op);
|
|
887
|
+
case '>=':
|
|
888
|
+
return handler.handleGreaterThanOrEquals(op);
|
|
889
|
+
case 'and':
|
|
890
|
+
return handler.handleIntersection(op);
|
|
891
|
+
case 'or':
|
|
892
|
+
return handler.handleUnion(op);
|
|
893
|
+
case 'nested':
|
|
894
|
+
return handler.handleNestedFilter(op);
|
|
895
|
+
case 'in':
|
|
896
|
+
case 'includes':
|
|
897
|
+
return handler.handleIncludes(op);
|
|
898
|
+
case 'excludeifany':
|
|
899
|
+
return handler.handleExcludeIfAny(op);
|
|
900
|
+
case 'excludes':
|
|
901
|
+
return handler.handleExcludes(op);
|
|
902
|
+
case 'exists':
|
|
903
|
+
return handler.handleExists(op);
|
|
904
|
+
case 'missing':
|
|
905
|
+
return handler.handleMissing(op);
|
|
906
|
+
default:
|
|
907
|
+
return assertNever(op);
|
|
837
908
|
}
|
|
838
|
-
// Find a unique name by appending numbers
|
|
839
|
-
let counter = 1;
|
|
840
|
-
let uniqueName;
|
|
841
|
-
do {
|
|
842
|
-
uniqueName = `${trimmedBaseName} (${counter})`;
|
|
843
|
-
counter++;
|
|
844
|
-
}while (!isNameUnique(entities, uniqueName))
|
|
845
|
-
return uniqueName;
|
|
846
909
|
};
|
|
847
|
-
|
|
848
910
|
/**
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
return
|
|
858
|
-
name: newName,
|
|
859
|
-
id: newId,
|
|
860
|
-
filters: filters ?? {},
|
|
861
|
-
modified: false,
|
|
862
|
-
saved: false,
|
|
863
|
-
createdDatetime: ts,
|
|
864
|
-
modifiedDatetime: ts,
|
|
865
|
-
counts: {}
|
|
866
|
-
};
|
|
911
|
+
* Return true if a FilterSet's root value is an empty object
|
|
912
|
+
* @param fs - FilterSet to test
|
|
913
|
+
*/ const isFilterEmpty = (fs)=>lodash.isEqual({}, fs);
|
|
914
|
+
/**
|
|
915
|
+
* Type guard to check if an object is a GQLIntersection
|
|
916
|
+
* @param value - The value to check
|
|
917
|
+
* @returns True if the value is a GQLIntersection
|
|
918
|
+
*/ const isGQLIntersection = (value)=>{
|
|
919
|
+
return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
|
|
867
920
|
};
|
|
868
|
-
const nanoid = nanoid$1.customAlphabet('1234567890abcdef', 16);
|
|
869
|
-
const createCohortId = ()=>nanoid();
|
|
870
|
-
const cohortsAdapter = toolkit.createEntityAdapter({
|
|
871
|
-
sortComparer: (a, b)=>{
|
|
872
|
-
if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
|
|
873
|
-
else return -1;
|
|
874
|
-
},
|
|
875
|
-
selectId: (cohort)=>cohort.id
|
|
876
|
-
});
|
|
877
|
-
// Create an initial unsaved cohort
|
|
878
|
-
const initialCohort = newCohort({
|
|
879
|
-
customName: DEFAULT_COHORT_NAME
|
|
880
|
-
});
|
|
881
|
-
const emptyInitialState = cohortsAdapter.getInitialState({
|
|
882
|
-
currentCohortId: initialCohort.id,
|
|
883
|
-
message: undefined
|
|
884
|
-
});
|
|
885
|
-
// Set the initial cohort in the adapter state
|
|
886
|
-
const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
|
|
887
|
-
const getCurrentCohortId = (state)=>state.currentCohortId;
|
|
888
921
|
/**
|
|
889
|
-
*
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
922
|
+
* Type guard to check if an object is a GQLIntersection
|
|
923
|
+
* @param value - The value to check
|
|
924
|
+
* @returns True if the value is a GQLIntersection
|
|
925
|
+
*/ const isGQLUnion = (value)=>{
|
|
926
|
+
return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
|
|
927
|
+
};
|
|
928
|
+
class ToGqlHandler {
|
|
929
|
+
constructor(){
|
|
930
|
+
this.handleEquals = (op)=>({
|
|
931
|
+
'=': {
|
|
932
|
+
[op.field]: op.operand
|
|
933
|
+
}
|
|
900
934
|
});
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
updateCohortName: (state, action)=>{
|
|
905
|
-
const { id, name } = action.payload;
|
|
906
|
-
cohortsAdapter.updateOne(state, {
|
|
907
|
-
id: id,
|
|
908
|
-
changes: {
|
|
909
|
-
name: name,
|
|
910
|
-
modified: true,
|
|
911
|
-
modifiedDatetime: new Date().toISOString()
|
|
935
|
+
this.handleNotEquals = (op)=>({
|
|
936
|
+
'!=': {
|
|
937
|
+
[op.field]: op.operand
|
|
912
938
|
}
|
|
913
939
|
});
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
const removedCohortName = state.entities[cohortId].name;
|
|
918
|
-
const totalCohorts = Object.keys(state.entities).length;
|
|
919
|
-
if (totalCohorts <= 1) {
|
|
920
|
-
cohortsAdapter.removeAll(state);
|
|
921
|
-
const defaultCohort = newCohort({
|
|
922
|
-
filters: {},
|
|
923
|
-
customName: DEFAULT_COHORT_NAME
|
|
924
|
-
});
|
|
925
|
-
cohortsAdapter.addOne(state, defaultCohort);
|
|
926
|
-
state.currentCohortId = defaultCohort.id;
|
|
927
|
-
if (action?.payload.shouldShowMessage) {
|
|
928
|
-
state.message = [
|
|
929
|
-
`deleteCohort|${removedCohortName}|${state.currentCohortId}`
|
|
930
|
-
];
|
|
940
|
+
this.handleLessThan = (op)=>({
|
|
941
|
+
'<': {
|
|
942
|
+
[op.field]: op.operand
|
|
931
943
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
if (state.currentCohortId === cohortId) {
|
|
937
|
-
const remainingIds = Object.keys(state.entities);
|
|
938
|
-
state.currentCohortId = remainingIds[0];
|
|
939
|
-
}
|
|
940
|
-
if (action?.payload.shouldShowMessage) {
|
|
941
|
-
state.message = [
|
|
942
|
-
`deleteCohort|${removedCohortName}|${state.currentCohortId}`
|
|
943
|
-
];
|
|
944
|
-
}
|
|
945
|
-
},
|
|
946
|
-
// adds a filter to the cohort filter set at the given index
|
|
947
|
-
updateCohortFilter: (state, action)=>{
|
|
948
|
-
const { index, field, filter } = action.payload;
|
|
949
|
-
const currentCohortId = getCurrentCohortId(state);
|
|
950
|
-
if (!state.entities[currentCohortId]) {
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
cohortsAdapter.updateOne(state, {
|
|
954
|
-
id: currentCohortId,
|
|
955
|
-
changes: {
|
|
956
|
-
filters: {
|
|
957
|
-
...state.entities[currentCohortId].filters,
|
|
958
|
-
[index]: {
|
|
959
|
-
mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
|
|
960
|
-
root: {
|
|
961
|
-
...state.entities[currentCohortId]?.filters[index]?.root ?? {},
|
|
962
|
-
[field]: filter
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
},
|
|
966
|
-
modified: true,
|
|
967
|
-
modifiedDatetime: new Date().toISOString()
|
|
944
|
+
});
|
|
945
|
+
this.handleLessThanOrEquals = (op)=>({
|
|
946
|
+
'<=': {
|
|
947
|
+
[op.field]: op.operand
|
|
968
948
|
}
|
|
969
949
|
});
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
const currentCohortId = getCurrentCohortId(state);
|
|
974
|
-
if (!state.entities[currentCohortId]) {
|
|
975
|
-
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
cohortsAdapter.updateOne(state, {
|
|
979
|
-
id: currentCohortId,
|
|
980
|
-
changes: {
|
|
981
|
-
filters: {
|
|
982
|
-
...state.entities[currentCohortId].filters,
|
|
983
|
-
[index]: filters
|
|
984
|
-
},
|
|
985
|
-
modified: true,
|
|
986
|
-
modifiedDatetime: new Date().toISOString()
|
|
950
|
+
this.handleGreaterThan = (op)=>({
|
|
951
|
+
'>': {
|
|
952
|
+
[op.field]: op.operand
|
|
987
953
|
}
|
|
988
954
|
});
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
if (!state.entities[currentCohortId]) {
|
|
993
|
-
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
cohortsAdapter.updateOne(state, {
|
|
997
|
-
id: currentCohortId,
|
|
998
|
-
changes: {
|
|
999
|
-
filters: action.payload.filters,
|
|
1000
|
-
modified: true,
|
|
1001
|
-
modifiedDatetime: new Date().toISOString()
|
|
955
|
+
this.handleGreaterThanOrEquals = (op)=>({
|
|
956
|
+
'>=': {
|
|
957
|
+
[op.field]: op.operand
|
|
1002
958
|
}
|
|
1003
959
|
});
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const { index, field } = action.payload;
|
|
1008
|
-
const currentCohortId = getCurrentCohortId(state);
|
|
1009
|
-
if (!state.entities[currentCohortId]) {
|
|
1010
|
-
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
const filters = state.entities[currentCohortId]?.filters[index]?.root;
|
|
1014
|
-
if (!filters) {
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1018
|
-
const { [field]: _a, ...updated } = filters;
|
|
1019
|
-
cohortsAdapter.updateOne(state, {
|
|
1020
|
-
id: currentCohortId,
|
|
1021
|
-
changes: {
|
|
1022
|
-
filters: {
|
|
1023
|
-
...state.entities[currentCohortId]?.filters,
|
|
1024
|
-
[index]: {
|
|
1025
|
-
mode: state.entities[currentCohortId].filters[index].mode,
|
|
1026
|
-
root: updated
|
|
1027
|
-
}
|
|
1028
|
-
},
|
|
1029
|
-
modified: true,
|
|
1030
|
-
modifiedDatetime: new Date().toISOString()
|
|
960
|
+
this.handleIncludes = (op)=>({
|
|
961
|
+
in: {
|
|
962
|
+
[op.field]: op.operands
|
|
1031
963
|
}
|
|
1032
964
|
});
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const currentCohort = state.entities[currentCohortId];
|
|
1037
|
-
const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
|
|
1038
|
-
const duplicatedCohort = newCohort({
|
|
1039
|
-
filters: {
|
|
1040
|
-
...currentCohort.filters
|
|
1041
|
-
},
|
|
1042
|
-
customName: newName
|
|
1043
|
-
});
|
|
1044
|
-
cohortsAdapter.addOne(state, {
|
|
1045
|
-
...duplicatedCohort,
|
|
1046
|
-
counts: {
|
|
1047
|
-
...currentCohort.counts
|
|
965
|
+
this.handleExcludes = (op)=>({
|
|
966
|
+
exclude: {
|
|
967
|
+
[op.field]: op.operands
|
|
1048
968
|
}
|
|
1049
969
|
});
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
clearCohortFilters: (state, action)=>{
|
|
1054
|
-
const { index } = action.payload;
|
|
1055
|
-
const currentCohortId = getCurrentCohortId(state);
|
|
1056
|
-
if (!state.entities[currentCohortId]) {
|
|
1057
|
-
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
const filters = state.entities[currentCohortId]?.filters[index]?.root;
|
|
1061
|
-
if (!filters) {
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
cohortsAdapter.updateOne(state, {
|
|
1065
|
-
id: currentCohortId,
|
|
1066
|
-
changes: {
|
|
1067
|
-
filters: {
|
|
1068
|
-
...state.entities[currentCohortId]?.filters,
|
|
1069
|
-
[index]: {
|
|
1070
|
-
mode: 'and',
|
|
1071
|
-
root: {}
|
|
1072
|
-
}
|
|
1073
|
-
},
|
|
1074
|
-
modified: true,
|
|
1075
|
-
modifiedDatetime: new Date().toISOString()
|
|
970
|
+
this.handleExcludeIfAny = (op)=>({
|
|
971
|
+
excludeifany: {
|
|
972
|
+
[op.field]: op.operands
|
|
1076
973
|
}
|
|
1077
974
|
});
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
const currentCohortId = getCurrentCohortId(state);
|
|
1081
|
-
const currentCohort = state.entities[currentCohortId];
|
|
1082
|
-
cohortsAdapter.updateOne(state, {
|
|
1083
|
-
id: currentCohortId,
|
|
1084
|
-
changes: {
|
|
1085
|
-
counts: {
|
|
1086
|
-
...currentCohort.counts,
|
|
1087
|
-
...action.payload
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
975
|
+
this.handleIntersection = (op)=>({
|
|
976
|
+
and: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
1090
977
|
});
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
changes: {
|
|
1098
|
-
counts: {
|
|
1099
|
-
...cohort.counts,
|
|
1100
|
-
...{
|
|
1101
|
-
[index]: counts
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
978
|
+
this.handleUnion = (op)=>({
|
|
979
|
+
or: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
980
|
+
});
|
|
981
|
+
this.handleMissing = (op)=>({
|
|
982
|
+
is: {
|
|
983
|
+
[op.field]: 'MISSING'
|
|
1104
984
|
}
|
|
1105
985
|
});
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
},
|
|
1110
|
-
/** @hidden */ setCohortList: (state, action)=>{
|
|
1111
|
-
if (!action.payload) {
|
|
1112
|
-
cohortsAdapter.removeMany(state, state.ids);
|
|
1113
|
-
} else {
|
|
1114
|
-
cohortsAdapter.upsertMany(state, [
|
|
1115
|
-
...action.payload
|
|
1116
|
-
]);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
/**
|
|
1122
|
-
* Returns the selectors for the cohorts EntityAdapter
|
|
1123
|
-
* @param state - the CoreState
|
|
1124
|
-
*
|
|
1125
|
-
* @hidden
|
|
1126
|
-
*/ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
|
|
1127
|
-
// Filter actions: addFilter, removeFilter, updateFilter
|
|
1128
|
-
const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
|
|
1129
|
-
const cohortReducer = cohortManagerSlice.reducer;
|
|
1130
|
-
|
|
1131
|
-
const initialState$2 = {};
|
|
1132
|
-
const expandSlice$1 = toolkit.createSlice({
|
|
1133
|
-
name: 'CohortBuilder/filterExpand',
|
|
1134
|
-
initialState: initialState$2,
|
|
1135
|
-
reducers: {
|
|
1136
|
-
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
1137
|
-
return {
|
|
1138
|
-
...state,
|
|
1139
|
-
[action.payload.index]: {
|
|
1140
|
-
...state[action.payload.index],
|
|
1141
|
-
[action.payload.field]: action.payload.expanded
|
|
986
|
+
this.handleExists = (op)=>({
|
|
987
|
+
not: {
|
|
988
|
+
[op.field]: op?.operand ?? null
|
|
1142
989
|
}
|
|
1143
|
-
};
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
return {
|
|
1147
|
-
...state,
|
|
1148
|
-
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
1149
|
-
acc[k] = action.payload.expand;
|
|
1150
|
-
return acc;
|
|
1151
|
-
}, {})
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
});
|
|
1156
|
-
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
1157
|
-
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
1158
|
-
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
1159
|
-
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
1160
|
-
|
|
1161
|
-
const initialState$1 = {};
|
|
1162
|
-
const expandSlice = toolkit.createSlice({
|
|
1163
|
-
name: 'CohortBuilder/filterCombineMode',
|
|
1164
|
-
initialState: initialState$1,
|
|
1165
|
-
reducers: {
|
|
1166
|
-
setCohortFilterCombineMode: (state, action)=>{
|
|
990
|
+
});
|
|
991
|
+
this.handleNestedFilter = (op)=>{
|
|
992
|
+
const child = convertFilterToGqlFilter(op.operand);
|
|
1167
993
|
return {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
...
|
|
1171
|
-
[action.payload.field]: action.payload.mode
|
|
994
|
+
nested: {
|
|
995
|
+
path: op.path,
|
|
996
|
+
...child
|
|
1172
997
|
}
|
|
1173
998
|
};
|
|
1174
|
-
}
|
|
999
|
+
};
|
|
1175
1000
|
}
|
|
1176
|
-
}
|
|
1177
|
-
const
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const initialState = {
|
|
1182
|
-
shouldShareFilters: false,
|
|
1183
|
-
sharedFiltersMap: {}
|
|
1001
|
+
}
|
|
1002
|
+
const convertFilterToGqlFilter = (filter)=>{
|
|
1003
|
+
const handler = new ToGqlHandler();
|
|
1004
|
+
return handleOperation(handler, filter);
|
|
1184
1005
|
};
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
field
|
|
1202
|
-
];
|
|
1203
|
-
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
1204
|
-
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
1205
|
-
|
|
1206
|
-
const cohortReducers = toolkit.combineReducers({
|
|
1207
|
-
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
1208
|
-
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
1209
|
-
sharedFilters: cohortSharedFiltersReducer,
|
|
1210
|
-
cohortManager: cohortReducer
|
|
1211
|
-
});
|
|
1212
|
-
|
|
1213
|
-
const rootReducer = toolkit.combineReducers({
|
|
1214
|
-
gen3Services: gen3ServicesReducer,
|
|
1215
|
-
user: userReducer,
|
|
1216
|
-
gen3Apps: gen3AppReducer,
|
|
1217
|
-
drsHostnames: drsHostnamesReducer,
|
|
1218
|
-
modals: modalReducer,
|
|
1219
|
-
cohorts: cohortReducers,
|
|
1220
|
-
activeWorkspace: activeWorkspaceReducer,
|
|
1221
|
-
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
1222
|
-
[userAuthApiReducerPath]: userAuthApiReducer
|
|
1223
|
-
});
|
|
1224
|
-
|
|
1225
|
-
const isOperationWithField = (operation)=>{
|
|
1226
|
-
return operation?.field !== undefined;
|
|
1227
|
-
};
|
|
1228
|
-
const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
|
|
1229
|
-
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
|
|
1230
|
-
) {
|
|
1231
|
-
const { operator } = operation.operator;
|
|
1232
|
-
return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
|
|
1233
|
-
}
|
|
1234
|
-
return false;
|
|
1235
|
-
};
|
|
1236
|
-
const extractFilterValue = (op)=>{
|
|
1237
|
-
const valueExtractorHandler = new ValueExtractorHandler();
|
|
1238
|
-
return handleOperation(valueExtractorHandler, op);
|
|
1239
|
-
};
|
|
1240
|
-
const extractEnumFilterValue = (op)=>{
|
|
1241
|
-
const enumValueExtractorHandler = new EnumValueExtractorHandler();
|
|
1242
|
-
const results = handleOperation(enumValueExtractorHandler, op);
|
|
1243
|
-
return results ?? [];
|
|
1244
|
-
};
|
|
1245
|
-
const assertNever = (x)=>{
|
|
1246
|
-
throw Error(`Exhaustive comparison did not handle: ${x}`);
|
|
1247
|
-
};
|
|
1248
|
-
const handleOperation = (handler, op)=>{
|
|
1249
|
-
switch(op.operator){
|
|
1250
|
-
case '=':
|
|
1251
|
-
return handler.handleEquals(op);
|
|
1252
|
-
case '!=':
|
|
1253
|
-
return handler.handleNotEquals(op);
|
|
1254
|
-
case '<':
|
|
1255
|
-
return handler.handleLessThan(op);
|
|
1256
|
-
case '<=':
|
|
1257
|
-
return handler.handleLessThanOrEquals(op);
|
|
1258
|
-
case '>':
|
|
1259
|
-
return handler.handleGreaterThan(op);
|
|
1260
|
-
case '>=':
|
|
1261
|
-
return handler.handleGreaterThanOrEquals(op);
|
|
1262
|
-
case 'and':
|
|
1263
|
-
return handler.handleIntersection(op);
|
|
1264
|
-
case 'or':
|
|
1265
|
-
return handler.handleUnion(op);
|
|
1266
|
-
case 'nested':
|
|
1267
|
-
return handler.handleNestedFilter(op);
|
|
1268
|
-
case 'in':
|
|
1269
|
-
case 'includes':
|
|
1270
|
-
return handler.handleIncludes(op);
|
|
1271
|
-
case 'excludeifany':
|
|
1272
|
-
return handler.handleExcludeIfAny(op);
|
|
1273
|
-
case 'excludes':
|
|
1274
|
-
return handler.handleExcludes(op);
|
|
1275
|
-
case 'exists':
|
|
1276
|
-
return handler.handleExists(op);
|
|
1277
|
-
case 'missing':
|
|
1278
|
-
return handler.handleMissing(op);
|
|
1279
|
-
default:
|
|
1280
|
-
return assertNever(op);
|
|
1281
|
-
}
|
|
1282
|
-
};
|
|
1283
|
-
/**
|
|
1284
|
-
* Return true if a FilterSet's root value is an empty object
|
|
1285
|
-
* @param fs - FilterSet to test
|
|
1286
|
-
*/ const isFilterEmpty = (fs)=>lodash.isEqual({}, fs);
|
|
1287
|
-
/**
|
|
1288
|
-
* Type guard to check if an object is a GQLIntersection
|
|
1289
|
-
* @param value - The value to check
|
|
1290
|
-
* @returns True if the value is a GQLIntersection
|
|
1291
|
-
*/ const isGQLIntersection = (value)=>{
|
|
1292
|
-
return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
|
|
1293
|
-
};
|
|
1294
|
-
/**
|
|
1295
|
-
* Type guard to check if an object is a GQLIntersection
|
|
1296
|
-
* @param value - The value to check
|
|
1297
|
-
* @returns True if the value is a GQLIntersection
|
|
1298
|
-
*/ const isGQLUnion = (value)=>{
|
|
1299
|
-
return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
|
|
1300
|
-
};
|
|
1301
|
-
class ToGqlHandler {
|
|
1302
|
-
constructor(){
|
|
1303
|
-
this.handleEquals = (op)=>({
|
|
1304
|
-
'=': {
|
|
1305
|
-
[op.field]: op.operand
|
|
1306
|
-
}
|
|
1307
|
-
});
|
|
1308
|
-
this.handleNotEquals = (op)=>({
|
|
1309
|
-
'!=': {
|
|
1310
|
-
[op.field]: op.operand
|
|
1311
|
-
}
|
|
1312
|
-
});
|
|
1313
|
-
this.handleLessThan = (op)=>({
|
|
1314
|
-
'<': {
|
|
1315
|
-
[op.field]: op.operand
|
|
1316
|
-
}
|
|
1317
|
-
});
|
|
1318
|
-
this.handleLessThanOrEquals = (op)=>({
|
|
1319
|
-
'<=': {
|
|
1320
|
-
[op.field]: op.operand
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
this.handleGreaterThan = (op)=>({
|
|
1324
|
-
'>': {
|
|
1325
|
-
[op.field]: op.operand
|
|
1326
|
-
}
|
|
1327
|
-
});
|
|
1328
|
-
this.handleGreaterThanOrEquals = (op)=>({
|
|
1329
|
-
'>=': {
|
|
1330
|
-
[op.field]: op.operand
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
this.handleIncludes = (op)=>({
|
|
1334
|
-
in: {
|
|
1335
|
-
[op.field]: op.operands
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
this.handleExcludes = (op)=>({
|
|
1339
|
-
exclude: {
|
|
1340
|
-
[op.field]: op.operands
|
|
1341
|
-
}
|
|
1342
|
-
});
|
|
1343
|
-
this.handleExcludeIfAny = (op)=>({
|
|
1344
|
-
excludeifany: {
|
|
1345
|
-
[op.field]: op.operands
|
|
1346
|
-
}
|
|
1347
|
-
});
|
|
1348
|
-
this.handleIntersection = (op)=>({
|
|
1349
|
-
and: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
1350
|
-
});
|
|
1351
|
-
this.handleUnion = (op)=>({
|
|
1352
|
-
or: op.operands.map((x)=>convertFilterToGqlFilter(x))
|
|
1353
|
-
});
|
|
1354
|
-
this.handleMissing = (op)=>({
|
|
1355
|
-
is: {
|
|
1356
|
-
[op.field]: 'MISSING'
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
this.handleExists = (op)=>({
|
|
1360
|
-
not: {
|
|
1361
|
-
[op.field]: op?.operand ?? null
|
|
1362
|
-
}
|
|
1363
|
-
});
|
|
1364
|
-
this.handleNestedFilter = (op)=>{
|
|
1365
|
-
const child = convertFilterToGqlFilter(op.operand);
|
|
1366
|
-
return {
|
|
1367
|
-
nested: {
|
|
1368
|
-
path: op.path,
|
|
1369
|
-
...child
|
|
1370
|
-
}
|
|
1371
|
-
};
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
const convertFilterToGqlFilter = (filter)=>{
|
|
1376
|
-
const handler = new ToGqlHandler();
|
|
1377
|
-
return handleOperation(handler, filter);
|
|
1378
|
-
};
|
|
1379
|
-
const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
|
|
1380
|
-
const fsKeys = Object.keys(fs.root);
|
|
1381
|
-
// if no keys return undefined
|
|
1382
|
-
if (fsKeys.length === 0) return {
|
|
1383
|
-
and: []
|
|
1384
|
-
};
|
|
1385
|
-
return toplevelOp === 'and' ? {
|
|
1386
|
-
and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1387
|
-
} : {
|
|
1388
|
-
or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1389
|
-
};
|
|
1390
|
-
};
|
|
1391
|
-
const handleGqlOperation = (handler, op)=>{
|
|
1392
|
-
const operationKeys = Object.keys(op);
|
|
1393
|
-
if (operationKeys.includes('=')) {
|
|
1394
|
-
return handler.handleEquals(op);
|
|
1006
|
+
const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
|
|
1007
|
+
const fsKeys = Object.keys(fs.root);
|
|
1008
|
+
// if no keys return undefined
|
|
1009
|
+
if (fsKeys.length === 0) return {
|
|
1010
|
+
and: []
|
|
1011
|
+
};
|
|
1012
|
+
return toplevelOp === 'and' ? {
|
|
1013
|
+
and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1014
|
+
} : {
|
|
1015
|
+
or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
|
|
1016
|
+
};
|
|
1017
|
+
};
|
|
1018
|
+
const handleGqlOperation = (handler, op)=>{
|
|
1019
|
+
const operationKeys = Object.keys(op);
|
|
1020
|
+
if (operationKeys.includes('=')) {
|
|
1021
|
+
return handler.handleEquals(op);
|
|
1395
1022
|
}
|
|
1396
1023
|
if (operationKeys.includes('!=')) {
|
|
1397
1024
|
return handler.handleNotEquals(op);
|
|
@@ -1615,6 +1242,36 @@ const filterSetToOperation = (fs)=>{
|
|
|
1615
1242
|
}
|
|
1616
1243
|
return undefined;
|
|
1617
1244
|
};
|
|
1245
|
+
/**
|
|
1246
|
+
* Constructs a nested operation object based on the provided field and leaf operand.
|
|
1247
|
+
* If the field does not contain a dot '.', it either assigns the field to the leaf operand (if applicable)
|
|
1248
|
+
* or returns the leaf operand as is. When the field contains dots, it splits the field into parts,
|
|
1249
|
+
* creates a "nested" operation for the root field, and recursively constructs the nested structure
|
|
1250
|
+
* for the remaining portion of the field.
|
|
1251
|
+
*
|
|
1252
|
+
* @param {string} field - The hierarchical field path, with segments separated by dots (e.g., "root.child").
|
|
1253
|
+
* @param {Operation} leafOperand - The operation to be nested within the specified path.
|
|
1254
|
+
* @param parentPath - The parent path of the current field. Guppy nested filters require a parent path.
|
|
1255
|
+
* @param depth
|
|
1256
|
+
* @returns {Operation} A nested operation object that represents the structured path and operand.
|
|
1257
|
+
*/ const buildNestedGQLFilter = (field, leafOperand, parentPath = undefined)=>{
|
|
1258
|
+
if (!field.includes('.')) {
|
|
1259
|
+
return leafOperand;
|
|
1260
|
+
}
|
|
1261
|
+
const splitFieldArray = field.split('.');
|
|
1262
|
+
const nextField = splitFieldArray.shift();
|
|
1263
|
+
if (!nextField) {
|
|
1264
|
+
console.warn('Invalid field path:', field);
|
|
1265
|
+
return leafOperand;
|
|
1266
|
+
}
|
|
1267
|
+
const currentPath = parentPath ? `${parentPath}.${nextField}` : nextField;
|
|
1268
|
+
return {
|
|
1269
|
+
nested: {
|
|
1270
|
+
path: currentPath,
|
|
1271
|
+
...buildNestedGQLFilter(splitFieldArray.join('.'), leafOperand, currentPath)
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
};
|
|
1618
1275
|
|
|
1619
1276
|
const isFilterSet = (input)=>{
|
|
1620
1277
|
if (typeof input !== 'object' || input === null) {
|
|
@@ -1638,9 +1295,17 @@ const isUnion = (value)=>{
|
|
|
1638
1295
|
const isIntersection = (value)=>{
|
|
1639
1296
|
return typeof value === 'object' && value !== null && value.operator === 'and' && Array.isArray(value.operands);
|
|
1640
1297
|
};
|
|
1298
|
+
/**
|
|
1299
|
+
* Type guard for Union or Intersection
|
|
1300
|
+
* @param o - operator to check
|
|
1301
|
+
* @category Filters
|
|
1302
|
+
*/ const isIntersectionOrUnion = (o)=>o.operator === 'and' || o.operator === 'or';
|
|
1641
1303
|
const isOperandsType = (operation)=>{
|
|
1642
1304
|
return operation?.operands !== undefined;
|
|
1643
1305
|
};
|
|
1306
|
+
const isNestedFilter = (operation)=>{
|
|
1307
|
+
return operation.operator === 'nested';
|
|
1308
|
+
};
|
|
1644
1309
|
const isIndexedFilterSetEmpty = (filters)=>Object.values(filters).every((filterSet)=>Object.keys(filterSet).length === 0);
|
|
1645
1310
|
const EmptyFilterSet = {
|
|
1646
1311
|
mode: 'and',
|
|
@@ -1669,7 +1334,7 @@ const COMMON_PREPOSITIONS = [
|
|
|
1669
1334
|
'up',
|
|
1670
1335
|
'yet'
|
|
1671
1336
|
];
|
|
1672
|
-
const capitalize = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
|
|
1337
|
+
const capitalize$1 = (s)=>s.length > 0 ? s[0].toUpperCase() + s.slice(1) : '';
|
|
1673
1338
|
const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
1674
1339
|
if (trim) {
|
|
1675
1340
|
const source = fieldName.slice(fieldName.indexOf('.') + 1);
|
|
@@ -1687,28 +1352,459 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
|
|
|
1687
1352
|
if (fieldName in FieldNameOverrides) {
|
|
1688
1353
|
return FieldNameOverrides[fieldName];
|
|
1689
1354
|
}
|
|
1690
|
-
if (fieldName === undefined) return 'No Title';
|
|
1691
|
-
return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize(word)).join(' ');
|
|
1692
|
-
};
|
|
1693
|
-
/**
|
|
1694
|
-
* Extracts the index name from the field name
|
|
1695
|
-
* @param fieldName
|
|
1696
|
-
*/ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
|
|
1697
|
-
/**
|
|
1698
|
-
* prepend the index name to the field name
|
|
1699
|
-
*/ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
|
|
1700
|
-
/**
|
|
1701
|
-
* extract the field name from the index.field name
|
|
1702
|
-
*/ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
|
|
1703
|
-
/**
|
|
1704
|
-
* extract the field name and the index from the index.field name returning as a tuple
|
|
1705
|
-
*/ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
|
|
1706
|
-
const [index, ...rest] = fieldName.split('.');
|
|
1707
|
-
return [
|
|
1708
|
-
index,
|
|
1709
|
-
rest.join('.')
|
|
1355
|
+
if (fieldName === undefined) return 'No Title';
|
|
1356
|
+
return fieldName.split('.').slice(-sections).map((s)=>s.split('_')).flat().map((word)=>COMMON_PREPOSITIONS.includes(word) ? word : capitalize$1(word)).join(' ');
|
|
1357
|
+
};
|
|
1358
|
+
/**
|
|
1359
|
+
* Extracts the index name from the field name
|
|
1360
|
+
* @param fieldName
|
|
1361
|
+
*/ const extractIndexFromFullFieldName = (fieldName)=>fieldName.split('.')[0];
|
|
1362
|
+
/**
|
|
1363
|
+
* prepend the index name to the field name
|
|
1364
|
+
*/ const prependIndexToFieldName = (fieldName, index)=>`${index}.${fieldName}`;
|
|
1365
|
+
/**
|
|
1366
|
+
* extract the field name from the index.field name
|
|
1367
|
+
*/ const extractFieldNameFromFullFieldName = (fieldName)=>fieldName.split('.').slice(1).join('.');
|
|
1368
|
+
/**
|
|
1369
|
+
* extract the field name and the index from the index.field name returning as a tuple
|
|
1370
|
+
*/ const extractIndexAndFieldNameFromFullFieldName = (fieldName)=>{
|
|
1371
|
+
const [index, ...rest] = fieldName.split('.');
|
|
1372
|
+
return [
|
|
1373
|
+
index,
|
|
1374
|
+
rest.join('.')
|
|
1375
|
+
];
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1378
|
+
const defaultCohortNameGenerator = ()=>`Custom cohort ${new Date().toLocaleString('en-CA', {
|
|
1379
|
+
timeZone: 'America/Chicago',
|
|
1380
|
+
hour12: false
|
|
1381
|
+
}).replace(',', '')}`;
|
|
1382
|
+
const isNameUnique = (entities, name, excludeId)=>{
|
|
1383
|
+
const trimmedName = name.trim();
|
|
1384
|
+
if (!trimmedName) return false;
|
|
1385
|
+
return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
|
|
1386
|
+
};
|
|
1387
|
+
const generateUniqueName = (entities, baseName)=>{
|
|
1388
|
+
const trimmedBaseName = baseName.trim();
|
|
1389
|
+
// If base name is unique, use it
|
|
1390
|
+
if (isNameUnique(entities, trimmedBaseName)) {
|
|
1391
|
+
return trimmedBaseName;
|
|
1392
|
+
}
|
|
1393
|
+
// Find a unique name by appending numbers
|
|
1394
|
+
let counter = 1;
|
|
1395
|
+
let uniqueName;
|
|
1396
|
+
do {
|
|
1397
|
+
uniqueName = `${trimmedBaseName} (${counter})`;
|
|
1398
|
+
counter++;
|
|
1399
|
+
}while (!isNameUnique(entities, uniqueName))
|
|
1400
|
+
return uniqueName;
|
|
1401
|
+
};
|
|
1402
|
+
/**
|
|
1403
|
+
* This function takes a FilterSet object and a prefix string as input.
|
|
1404
|
+
* It filters the root property of the FilterSet object and returns a
|
|
1405
|
+
* new FilterSet object that only contains filters with field names
|
|
1406
|
+
* that start with the specified prefix.
|
|
1407
|
+
*
|
|
1408
|
+
* @param fs - The FilterSet object to filter
|
|
1409
|
+
* @param prefix - The prefix to filter by
|
|
1410
|
+
* @returns - A new FilterSet object that only contains filters with field names that start with the specified prefix
|
|
1411
|
+
* @category Filters
|
|
1412
|
+
*/ const extractFiltersWithPrefixFromFilterSet = (fs, prefix)=>{
|
|
1413
|
+
if (fs === undefined || fs.root === undefined) {
|
|
1414
|
+
return {
|
|
1415
|
+
mode: 'and',
|
|
1416
|
+
root: {}
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
return Object.values(fs.root).reduce((acc, filter)=>{
|
|
1420
|
+
if (isIntersectionOrUnion(filter) || isNestedFilter(filter)) return acc;
|
|
1421
|
+
if (filter.field.startsWith(prefix)) {
|
|
1422
|
+
acc.root[filter.field] = filter;
|
|
1423
|
+
}
|
|
1424
|
+
return acc;
|
|
1425
|
+
}, {
|
|
1426
|
+
mode: 'and',
|
|
1427
|
+
root: {}
|
|
1428
|
+
});
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* Cohorts in Gen3 are defined as a set of filters for each index in the data.
|
|
1433
|
+
* This means one cohort id defined for all "tabs" in CohortBuilder (explorer)
|
|
1434
|
+
* Switching a cohort id means all the cohorts for the index are changed.
|
|
1435
|
+
*/ const DEFAULT_COHORT_NAME = 'Cohort';
|
|
1436
|
+
const newCohort = ({ filters = {}, customName })=>{
|
|
1437
|
+
const ts = new Date().toISOString();
|
|
1438
|
+
const newName = customName ?? defaultCohortNameGenerator();
|
|
1439
|
+
const newId = createCohortId();
|
|
1440
|
+
return {
|
|
1441
|
+
name: newName,
|
|
1442
|
+
id: newId,
|
|
1443
|
+
filters: filters ?? {},
|
|
1444
|
+
modified: false,
|
|
1445
|
+
saved: false,
|
|
1446
|
+
createdDatetime: ts,
|
|
1447
|
+
modifiedDatetime: ts,
|
|
1448
|
+
counts: {}
|
|
1449
|
+
};
|
|
1450
|
+
};
|
|
1451
|
+
const nanoid = nanoid$1.customAlphabet('1234567890abcdef', 16);
|
|
1452
|
+
const createCohortId = ()=>nanoid();
|
|
1453
|
+
const cohortsAdapter = toolkit.createEntityAdapter({
|
|
1454
|
+
sortComparer: (a, b)=>{
|
|
1455
|
+
if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
|
|
1456
|
+
else return -1;
|
|
1457
|
+
},
|
|
1458
|
+
selectId: (cohort)=>cohort.id
|
|
1459
|
+
});
|
|
1460
|
+
// Create an initial unsaved cohort
|
|
1461
|
+
const initialCohort = newCohort({
|
|
1462
|
+
customName: DEFAULT_COHORT_NAME
|
|
1463
|
+
});
|
|
1464
|
+
const emptyInitialState = cohortsAdapter.getInitialState({
|
|
1465
|
+
currentCohortId: initialCohort.id,
|
|
1466
|
+
message: undefined
|
|
1467
|
+
});
|
|
1468
|
+
// Set the initial cohort in the adapter state
|
|
1469
|
+
const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
|
|
1470
|
+
const getCurrentCohortId = (state)=>state.currentCohortId;
|
|
1471
|
+
/**
|
|
1472
|
+
* Redux slice for cohort filters
|
|
1473
|
+
*/ const cohortManagerSlice = toolkit.createSlice({
|
|
1474
|
+
name: 'cohort',
|
|
1475
|
+
initialState: initialState$3,
|
|
1476
|
+
reducers: {
|
|
1477
|
+
createNewCohort: (state, action)=>{
|
|
1478
|
+
const baseName = action.payload.name || `Cohort`;
|
|
1479
|
+
const uniqueName = generateUniqueName(Object.values(state.entities), baseName);
|
|
1480
|
+
const cohort = newCohort({
|
|
1481
|
+
filters: action.payload.filters,
|
|
1482
|
+
customName: uniqueName
|
|
1483
|
+
});
|
|
1484
|
+
cohortsAdapter.addOne(state, cohort);
|
|
1485
|
+
state.currentCohortId = cohort.id;
|
|
1486
|
+
},
|
|
1487
|
+
updateCohortName: (state, action)=>{
|
|
1488
|
+
const { id, name } = action.payload;
|
|
1489
|
+
cohortsAdapter.updateOne(state, {
|
|
1490
|
+
id: id,
|
|
1491
|
+
changes: {
|
|
1492
|
+
name: name,
|
|
1493
|
+
modified: true,
|
|
1494
|
+
modifiedDatetime: new Date().toISOString()
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
},
|
|
1498
|
+
removeCohort: (state, action)=>{
|
|
1499
|
+
const { id: cohortId } = action.payload;
|
|
1500
|
+
const removedCohortName = state.entities[cohortId].name;
|
|
1501
|
+
const totalCohorts = Object.keys(state.entities).length;
|
|
1502
|
+
if (totalCohorts <= 1) {
|
|
1503
|
+
cohortsAdapter.removeAll(state);
|
|
1504
|
+
const defaultCohort = newCohort({
|
|
1505
|
+
filters: {},
|
|
1506
|
+
customName: DEFAULT_COHORT_NAME
|
|
1507
|
+
});
|
|
1508
|
+
cohortsAdapter.addOne(state, defaultCohort);
|
|
1509
|
+
state.currentCohortId = defaultCohort.id;
|
|
1510
|
+
if (action?.payload.shouldShowMessage) {
|
|
1511
|
+
state.message = [
|
|
1512
|
+
`deleteCohort|${removedCohortName}|${state.currentCohortId}`
|
|
1513
|
+
];
|
|
1514
|
+
}
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
cohortsAdapter.removeOne(state, cohortId);
|
|
1518
|
+
// deleted the current cohort so set to the most recent cohort
|
|
1519
|
+
if (state.currentCohortId === cohortId) {
|
|
1520
|
+
const remainingIds = Object.keys(state.entities);
|
|
1521
|
+
state.currentCohortId = remainingIds[0];
|
|
1522
|
+
}
|
|
1523
|
+
if (action?.payload.shouldShowMessage) {
|
|
1524
|
+
state.message = [
|
|
1525
|
+
`deleteCohort|${removedCohortName}|${state.currentCohortId}`
|
|
1526
|
+
];
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
// adds a filter to the cohort filter set at the given index
|
|
1530
|
+
updateCohortFilter: (state, action)=>{
|
|
1531
|
+
const { index, field, filter } = action.payload;
|
|
1532
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1533
|
+
if (!state.entities[currentCohortId]) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
cohortsAdapter.updateOne(state, {
|
|
1537
|
+
id: currentCohortId,
|
|
1538
|
+
changes: {
|
|
1539
|
+
filters: {
|
|
1540
|
+
...state.entities[currentCohortId].filters,
|
|
1541
|
+
[index]: {
|
|
1542
|
+
mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
|
|
1543
|
+
root: {
|
|
1544
|
+
...state.entities[currentCohortId]?.filters[index]?.root ?? {},
|
|
1545
|
+
[field]: filter
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
},
|
|
1549
|
+
modified: true,
|
|
1550
|
+
modifiedDatetime: new Date().toISOString()
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
},
|
|
1554
|
+
setCohortFilter: (state, action)=>{
|
|
1555
|
+
const { index, filters } = action.payload;
|
|
1556
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1557
|
+
if (!state.entities[currentCohortId]) {
|
|
1558
|
+
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
cohortsAdapter.updateOne(state, {
|
|
1562
|
+
id: currentCohortId,
|
|
1563
|
+
changes: {
|
|
1564
|
+
filters: {
|
|
1565
|
+
...state.entities[currentCohortId].filters,
|
|
1566
|
+
[index]: filters
|
|
1567
|
+
},
|
|
1568
|
+
modified: true,
|
|
1569
|
+
modifiedDatetime: new Date().toISOString()
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
},
|
|
1573
|
+
setCohortIndexFilters: (state, action)=>{
|
|
1574
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1575
|
+
if (!state.entities[currentCohortId]) {
|
|
1576
|
+
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
cohortsAdapter.updateOne(state, {
|
|
1580
|
+
id: currentCohortId,
|
|
1581
|
+
changes: {
|
|
1582
|
+
filters: action.payload.filters,
|
|
1583
|
+
modified: true,
|
|
1584
|
+
modifiedDatetime: new Date().toISOString()
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
},
|
|
1588
|
+
// removes a filter to the cohort filter set at the given index
|
|
1589
|
+
removeCohortFilter: (state, action)=>{
|
|
1590
|
+
const { index, field } = action.payload;
|
|
1591
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1592
|
+
if (!state.entities[currentCohortId]) {
|
|
1593
|
+
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
const filters = state.entities[currentCohortId]?.filters[index]?.root;
|
|
1597
|
+
if (!filters) {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1601
|
+
const { [field]: _a, ...updated } = filters;
|
|
1602
|
+
cohortsAdapter.updateOne(state, {
|
|
1603
|
+
id: currentCohortId,
|
|
1604
|
+
changes: {
|
|
1605
|
+
filters: {
|
|
1606
|
+
...state.entities[currentCohortId]?.filters,
|
|
1607
|
+
[index]: {
|
|
1608
|
+
mode: state.entities[currentCohortId].filters[index].mode,
|
|
1609
|
+
root: updated
|
|
1610
|
+
}
|
|
1611
|
+
},
|
|
1612
|
+
modified: true,
|
|
1613
|
+
modifiedDatetime: new Date().toISOString()
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
},
|
|
1617
|
+
duplicateCohort: (state)=>{
|
|
1618
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1619
|
+
const currentCohort = state.entities[currentCohortId];
|
|
1620
|
+
const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
|
|
1621
|
+
const duplicatedCohort = newCohort({
|
|
1622
|
+
filters: {
|
|
1623
|
+
...currentCohort.filters
|
|
1624
|
+
},
|
|
1625
|
+
customName: newName
|
|
1626
|
+
});
|
|
1627
|
+
cohortsAdapter.addOne(state, {
|
|
1628
|
+
...duplicatedCohort,
|
|
1629
|
+
counts: {
|
|
1630
|
+
...currentCohort.counts
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
state.currentCohortId = duplicatedCohort.id;
|
|
1634
|
+
},
|
|
1635
|
+
// removes all filters from the cohort filter set at the given index
|
|
1636
|
+
clearCohortFilters: (state, action)=>{
|
|
1637
|
+
const { index } = action.payload;
|
|
1638
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1639
|
+
if (!state.entities[currentCohortId]) {
|
|
1640
|
+
console.error(`no cohort with id=${currentCohortId} defined`);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const filters = state.entities[currentCohortId]?.filters[index]?.root;
|
|
1644
|
+
if (!filters) {
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
cohortsAdapter.updateOne(state, {
|
|
1648
|
+
id: currentCohortId,
|
|
1649
|
+
changes: {
|
|
1650
|
+
filters: {
|
|
1651
|
+
...state.entities[currentCohortId]?.filters,
|
|
1652
|
+
[index]: {
|
|
1653
|
+
mode: 'and',
|
|
1654
|
+
root: {}
|
|
1655
|
+
}
|
|
1656
|
+
},
|
|
1657
|
+
modified: true,
|
|
1658
|
+
modifiedDatetime: new Date().toISOString()
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
},
|
|
1662
|
+
updateCohortCounts: (state, action)=>{
|
|
1663
|
+
const currentCohortId = getCurrentCohortId(state);
|
|
1664
|
+
const currentCohort = state.entities[currentCohortId];
|
|
1665
|
+
cohortsAdapter.updateOne(state, {
|
|
1666
|
+
id: currentCohortId,
|
|
1667
|
+
changes: {
|
|
1668
|
+
counts: {
|
|
1669
|
+
...currentCohort.counts,
|
|
1670
|
+
...action.payload
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
},
|
|
1675
|
+
updateCohortIndexCountById: (state, action)=>{
|
|
1676
|
+
const { index, cohortId, counts } = action.payload;
|
|
1677
|
+
const cohort = state.entities[cohortId];
|
|
1678
|
+
cohortsAdapter.updateOne(state, {
|
|
1679
|
+
id: cohortId,
|
|
1680
|
+
changes: {
|
|
1681
|
+
counts: {
|
|
1682
|
+
...cohort.counts,
|
|
1683
|
+
...{
|
|
1684
|
+
[index]: counts
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
},
|
|
1690
|
+
setCurrentCohortId: (state, action)=>{
|
|
1691
|
+
state.currentCohortId = action.payload;
|
|
1692
|
+
},
|
|
1693
|
+
/** @hidden */ setCohortList: (state, action)=>{
|
|
1694
|
+
if (!action.payload) {
|
|
1695
|
+
cohortsAdapter.removeMany(state, state.ids);
|
|
1696
|
+
} else {
|
|
1697
|
+
cohortsAdapter.upsertMany(state, [
|
|
1698
|
+
...action.payload
|
|
1699
|
+
]);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
/**
|
|
1705
|
+
* Returns the selectors for the cohorts EntityAdapter
|
|
1706
|
+
* @param state - the CoreState
|
|
1707
|
+
*
|
|
1708
|
+
* @hidden
|
|
1709
|
+
*/ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
|
|
1710
|
+
// Filter actions: addFilter, removeFilter, updateFilter
|
|
1711
|
+
const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
|
|
1712
|
+
const cohortReducer = cohortManagerSlice.reducer;
|
|
1713
|
+
|
|
1714
|
+
const initialState$2 = {};
|
|
1715
|
+
const expandSlice$1 = toolkit.createSlice({
|
|
1716
|
+
name: 'CohortBuilder/filterExpand',
|
|
1717
|
+
initialState: initialState$2,
|
|
1718
|
+
reducers: {
|
|
1719
|
+
toggleCohortBuilderCategoryFilter: (state, action)=>{
|
|
1720
|
+
return {
|
|
1721
|
+
...state,
|
|
1722
|
+
[action.payload.index]: {
|
|
1723
|
+
...state[action.payload.index],
|
|
1724
|
+
[action.payload.field]: action.payload.expanded
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
},
|
|
1728
|
+
toggleCohortBuilderAllFilters: (state, action)=>{
|
|
1729
|
+
return {
|
|
1730
|
+
...state,
|
|
1731
|
+
[action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
|
|
1732
|
+
acc[k] = action.payload.expand;
|
|
1733
|
+
return acc;
|
|
1734
|
+
}, {})
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
|
|
1740
|
+
const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
|
|
1741
|
+
const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
|
|
1742
|
+
const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
|
|
1743
|
+
|
|
1744
|
+
const initialState$1 = {};
|
|
1745
|
+
const expandSlice = toolkit.createSlice({
|
|
1746
|
+
name: 'CohortBuilder/filterCombineMode',
|
|
1747
|
+
initialState: initialState$1,
|
|
1748
|
+
reducers: {
|
|
1749
|
+
setCohortFilterCombineMode: (state, action)=>{
|
|
1750
|
+
return {
|
|
1751
|
+
...state,
|
|
1752
|
+
[action.payload.index]: {
|
|
1753
|
+
...state[action.payload.index],
|
|
1754
|
+
[action.payload.field]: action.payload.mode
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
|
|
1761
|
+
const { setCohortFilterCombineMode } = expandSlice.actions;
|
|
1762
|
+
const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
|
|
1763
|
+
|
|
1764
|
+
const initialState = {
|
|
1765
|
+
shouldShareFilters: false,
|
|
1766
|
+
sharedFiltersMap: {}
|
|
1767
|
+
};
|
|
1768
|
+
const cohortSharedFiltersSlice = toolkit.createSlice({
|
|
1769
|
+
name: 'cohortSharedFilters',
|
|
1770
|
+
initialState: initialState,
|
|
1771
|
+
reducers: {
|
|
1772
|
+
setShouldShareFilters: (state, action)=>{
|
|
1773
|
+
state.shouldShareFilters = action.payload;
|
|
1774
|
+
return state;
|
|
1775
|
+
},
|
|
1776
|
+
setSharedFilters: (state, action)=>{
|
|
1777
|
+
state.sharedFiltersMap = action.payload;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
|
|
1782
|
+
const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
|
|
1783
|
+
const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
|
|
1784
|
+
field
|
|
1710
1785
|
];
|
|
1711
|
-
};
|
|
1786
|
+
const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
|
|
1787
|
+
const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
|
|
1788
|
+
|
|
1789
|
+
const cohortReducers = toolkit.combineReducers({
|
|
1790
|
+
filtersExpanded: cohortBuilderFiltersExpandedReducer,
|
|
1791
|
+
filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
|
|
1792
|
+
sharedFilters: cohortSharedFiltersReducer,
|
|
1793
|
+
cohortManager: cohortReducer
|
|
1794
|
+
});
|
|
1795
|
+
|
|
1796
|
+
const rootReducer = toolkit.combineReducers({
|
|
1797
|
+
gen3Services: gen3ServicesReducer,
|
|
1798
|
+
user: userReducer,
|
|
1799
|
+
gen3Apps: gen3AppReducer,
|
|
1800
|
+
drsHostnames: drsHostnamesReducer,
|
|
1801
|
+
modals: modalReducer,
|
|
1802
|
+
cohorts: cohortReducers,
|
|
1803
|
+
activeWorkspace: activeWorkspaceReducer,
|
|
1804
|
+
[guppyApiSliceReducerPath]: guppyApiReducer,
|
|
1805
|
+
[userAuthApiReducerPath]: userAuthApiReducer,
|
|
1806
|
+
[cartReducerPath]: cartReducer
|
|
1807
|
+
});
|
|
1712
1808
|
|
|
1713
1809
|
/**
|
|
1714
1810
|
* Flattens a deep nested JSON object skipping
|
|
@@ -1870,14 +1966,22 @@ function isHttpStatusError(error) {
|
|
|
1870
1966
|
* @param {string} csrfToken - The CSRF token to include in the request headers.
|
|
1871
1967
|
* @returns {FetchConfig} - The prepared fetch configuration object.
|
|
1872
1968
|
*/ const prepareFetchConfig = (parameters, csrfToken)=>{
|
|
1969
|
+
const headers = new Headers({
|
|
1970
|
+
Accept: 'application/json',
|
|
1971
|
+
'Content-Type': 'application/json',
|
|
1972
|
+
...csrfToken !== undefined && {
|
|
1973
|
+
'X-CSRF-Token': csrfToken
|
|
1974
|
+
}
|
|
1975
|
+
});
|
|
1976
|
+
if (process.env.NODE_ENV === 'development') {
|
|
1977
|
+
// NOTE: This cookie can only be accessed from the client side
|
|
1978
|
+
// in development mode. Otherwise, the cookie is set as httpOnly
|
|
1979
|
+
const accessToken = cookiesNext.getCookie('credentials_token');
|
|
1980
|
+
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
|
|
1981
|
+
}
|
|
1873
1982
|
return {
|
|
1874
1983
|
method: 'POST',
|
|
1875
|
-
headers:
|
|
1876
|
-
'Content-Type': 'application/json',
|
|
1877
|
-
...csrfToken !== undefined && {
|
|
1878
|
-
'X-CSRF-Token': csrfToken
|
|
1879
|
-
}
|
|
1880
|
-
},
|
|
1984
|
+
headers: headers,
|
|
1881
1985
|
body: JSON.stringify({
|
|
1882
1986
|
type: parameters.type,
|
|
1883
1987
|
filter: convertFilterSetToGqlFilter(parameters.filter),
|
|
@@ -2062,6 +2166,88 @@ const groupSharedFields = (data)=>{
|
|
|
2062
2166
|
return data;
|
|
2063
2167
|
};
|
|
2064
2168
|
|
|
2169
|
+
const customQueryStrForField = (field, query, depth = 0)=>{
|
|
2170
|
+
const indent = ' '.repeat(depth);
|
|
2171
|
+
const splittedFieldArray = field.split('.');
|
|
2172
|
+
const splittedField = splittedFieldArray.shift();
|
|
2173
|
+
if (splittedFieldArray.length === 0) {
|
|
2174
|
+
return `${indent}${splittedField} ${query}`;
|
|
2175
|
+
}
|
|
2176
|
+
return `${indent}${splittedField} {
|
|
2177
|
+
${customQueryStrForField(splittedFieldArray.join('.'), query, depth + 1)}
|
|
2178
|
+
${indent}}`;
|
|
2179
|
+
};
|
|
2180
|
+
// TODO: refactor the function below using customQueryStrForEachField and a wrapper function that passes the query
|
|
2181
|
+
const histogramQueryStrForEachField = (field)=>{
|
|
2182
|
+
const splittedFieldArray = field.split('.');
|
|
2183
|
+
const splittedField = splittedFieldArray.shift();
|
|
2184
|
+
if (splittedFieldArray.length === 0) {
|
|
2185
|
+
return `
|
|
2186
|
+
${splittedField} {
|
|
2187
|
+
histogram {
|
|
2188
|
+
key
|
|
2189
|
+
count
|
|
2190
|
+
}
|
|
2191
|
+
}`;
|
|
2192
|
+
}
|
|
2193
|
+
return `
|
|
2194
|
+
${splittedField} {
|
|
2195
|
+
${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
2196
|
+
}`;
|
|
2197
|
+
};
|
|
2198
|
+
const statsQueryStrForEachField = (field)=>{
|
|
2199
|
+
const splittedFieldArray = field.split('.');
|
|
2200
|
+
const splittedField = splittedFieldArray.shift();
|
|
2201
|
+
if (splittedFieldArray.length === 0) {
|
|
2202
|
+
return `
|
|
2203
|
+
${splittedField} {
|
|
2204
|
+
histogram {
|
|
2205
|
+
count
|
|
2206
|
+
min
|
|
2207
|
+
max
|
|
2208
|
+
avg
|
|
2209
|
+
sum
|
|
2210
|
+
}
|
|
2211
|
+
}`;
|
|
2212
|
+
}
|
|
2213
|
+
return `
|
|
2214
|
+
${splittedField} {
|
|
2215
|
+
${statsQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
2216
|
+
}`;
|
|
2217
|
+
};
|
|
2218
|
+
const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
|
|
2219
|
+
${mainField} {
|
|
2220
|
+
${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
|
|
2221
|
+
key
|
|
2222
|
+
count
|
|
2223
|
+
missingFields {
|
|
2224
|
+
field
|
|
2225
|
+
count
|
|
2226
|
+
}
|
|
2227
|
+
termsFields {
|
|
2228
|
+
field
|
|
2229
|
+
count
|
|
2230
|
+
terms {
|
|
2231
|
+
key
|
|
2232
|
+
count
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}`;
|
|
2237
|
+
const rawDataQueryStrForEachField = (field)=>{
|
|
2238
|
+
const splitFieldArray = field.split('.');
|
|
2239
|
+
const splitField = splitFieldArray.shift();
|
|
2240
|
+
if (splitFieldArray.length === 0) {
|
|
2241
|
+
return `
|
|
2242
|
+
${splitField}
|
|
2243
|
+
`;
|
|
2244
|
+
}
|
|
2245
|
+
return `
|
|
2246
|
+
${splitField} {
|
|
2247
|
+
${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
|
|
2248
|
+
}`;
|
|
2249
|
+
};
|
|
2250
|
+
|
|
2065
2251
|
const statusEndpoint = '/_status';
|
|
2066
2252
|
const fetchJson = async (url)=>{
|
|
2067
2253
|
const res = await fetch(url, {
|
|
@@ -2342,75 +2528,6 @@ const explorerTags = guppyApi.enhanceEndpoints({
|
|
|
2342
2528
|
})
|
|
2343
2529
|
})
|
|
2344
2530
|
});
|
|
2345
|
-
const histogramQueryStrForEachField = (field)=>{
|
|
2346
|
-
const splittedFieldArray = field.split('.');
|
|
2347
|
-
const splittedField = splittedFieldArray.shift();
|
|
2348
|
-
if (splittedFieldArray.length === 0) {
|
|
2349
|
-
return `
|
|
2350
|
-
${splittedField} {
|
|
2351
|
-
histogram {
|
|
2352
|
-
key
|
|
2353
|
-
count
|
|
2354
|
-
}
|
|
2355
|
-
}`;
|
|
2356
|
-
}
|
|
2357
|
-
return `
|
|
2358
|
-
${splittedField} {
|
|
2359
|
-
${histogramQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
2360
|
-
}`;
|
|
2361
|
-
};
|
|
2362
|
-
const statsQueryStrForEachField = (field)=>{
|
|
2363
|
-
const splittedFieldArray = field.split('.');
|
|
2364
|
-
const splittedField = splittedFieldArray.shift();
|
|
2365
|
-
if (splittedFieldArray.length === 0) {
|
|
2366
|
-
return `
|
|
2367
|
-
${splittedField} {
|
|
2368
|
-
histogram {
|
|
2369
|
-
count
|
|
2370
|
-
min
|
|
2371
|
-
max
|
|
2372
|
-
avg
|
|
2373
|
-
sum
|
|
2374
|
-
}
|
|
2375
|
-
}`;
|
|
2376
|
-
}
|
|
2377
|
-
return `
|
|
2378
|
-
${splittedField} {
|
|
2379
|
-
${statsQueryStrForEachField(splittedFieldArray.join('.'))}
|
|
2380
|
-
}`;
|
|
2381
|
-
};
|
|
2382
|
-
const nestedHistogramQueryStrForEachField = (mainField, numericAggAsText)=>`
|
|
2383
|
-
${mainField} {
|
|
2384
|
-
${numericAggAsText ? 'asTextHistogram' : 'histogram'} {
|
|
2385
|
-
key
|
|
2386
|
-
count
|
|
2387
|
-
missingFields {
|
|
2388
|
-
field
|
|
2389
|
-
count
|
|
2390
|
-
}
|
|
2391
|
-
termsFields {
|
|
2392
|
-
field
|
|
2393
|
-
count
|
|
2394
|
-
terms {
|
|
2395
|
-
key
|
|
2396
|
-
count
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
}`;
|
|
2401
|
-
const rawDataQueryStrForEachField = (field)=>{
|
|
2402
|
-
const splitFieldArray = field.split('.');
|
|
2403
|
-
const splitField = splitFieldArray.shift();
|
|
2404
|
-
if (splitFieldArray.length === 0) {
|
|
2405
|
-
return `
|
|
2406
|
-
${splitField}
|
|
2407
|
-
`;
|
|
2408
|
-
}
|
|
2409
|
-
return `
|
|
2410
|
-
${splitField} {
|
|
2411
|
-
${rawDataQueryStrForEachField(splitFieldArray.join('.'))}
|
|
2412
|
-
}`;
|
|
2413
|
-
};
|
|
2414
2531
|
const useGetArrayTypes = ()=>{
|
|
2415
2532
|
{
|
|
2416
2533
|
const { data, error } = useGetStatus();
|
|
@@ -2514,7 +2631,8 @@ const persistConfig = {
|
|
|
2514
2631
|
storage,
|
|
2515
2632
|
whitelist: [
|
|
2516
2633
|
'cohorts',
|
|
2517
|
-
'activeWorkspace'
|
|
2634
|
+
'activeWorkspace',
|
|
2635
|
+
'cart'
|
|
2518
2636
|
]
|
|
2519
2637
|
};
|
|
2520
2638
|
const persistedReducer = reduxPersist.persistReducer(persistConfig, rootReducer);
|
|
@@ -2762,6 +2880,28 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2762
2880
|
return await response.json();
|
|
2763
2881
|
};
|
|
2764
2882
|
|
|
2883
|
+
const extractValuesFromObject = (jsonPathMappings, obj)=>{
|
|
2884
|
+
const result = {};
|
|
2885
|
+
const extractObjectValue = (jsonPath, obj)=>{
|
|
2886
|
+
const extractedValues = jsonpathPlus.JSONPath({
|
|
2887
|
+
path: jsonPath,
|
|
2888
|
+
json: obj
|
|
2889
|
+
});
|
|
2890
|
+
return extractedValues.length > 0 ? extractedValues[0] : undefined;
|
|
2891
|
+
};
|
|
2892
|
+
for(const key in jsonPathMappings){
|
|
2893
|
+
if (key in Object.keys(jsonPathMappings)) {
|
|
2894
|
+
// Extract value from an object and store it in the result.
|
|
2895
|
+
result[key] = extractObjectValue(jsonPathMappings[key], obj);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
return result;
|
|
2899
|
+
};
|
|
2900
|
+
const ExtractValueFromObject = (obj, key, valueIfNotFound)=>{
|
|
2901
|
+
return obj?.[key] ?? valueIfNotFound;
|
|
2902
|
+
};
|
|
2903
|
+
|
|
2904
|
+
const DAYS_IN_YEAR = 365.25;
|
|
2765
2905
|
/**
|
|
2766
2906
|
* Converts HistogramData to HistogramDataAsStringKey by ensuring the key is a string.
|
|
2767
2907
|
* If the key is already a string, it's used as is.
|
|
@@ -2777,6 +2917,79 @@ const fetchFencePresignedURL = async ({ guid, method = 'GET', onAbort = ()=>null
|
|
|
2777
2917
|
};
|
|
2778
2918
|
const calculatePercentageAsNumber = (count, total)=>count ? count / total * 100 : 0;
|
|
2779
2919
|
const calculatePercentageAsString = (count, total)=>`${(count / total * 100).toFixed(2)}%`;
|
|
2920
|
+
const capitalize = (original)=>{
|
|
2921
|
+
const customCapitalizations = {
|
|
2922
|
+
id: 'ID',
|
|
2923
|
+
uuid: 'UUID',
|
|
2924
|
+
dna: 'DNA',
|
|
2925
|
+
dbsnp: 'dbSNP',
|
|
2926
|
+
cosmic: 'COSMIC',
|
|
2927
|
+
civic: 'CIViC',
|
|
2928
|
+
dbgap: 'dbGaP',
|
|
2929
|
+
ecog: 'ECOG',
|
|
2930
|
+
bmi: 'BMI',
|
|
2931
|
+
gdc: 'GDC',
|
|
2932
|
+
cnv: 'CNV',
|
|
2933
|
+
ssm: 'SSM',
|
|
2934
|
+
aa: 'AA'
|
|
2935
|
+
};
|
|
2936
|
+
return original.split(' ').map((word)=>customCapitalizations[word.toLowerCase()] || `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' ');
|
|
2937
|
+
};
|
|
2938
|
+
const humanify = ({ term = '', capitalize: cap = true, facetTerm = false })=>{
|
|
2939
|
+
let original;
|
|
2940
|
+
let humanified;
|
|
2941
|
+
if (facetTerm) {
|
|
2942
|
+
// Splits on capital letters followed by lowercase letters to find
|
|
2943
|
+
// words squished together in a string.
|
|
2944
|
+
original = term?.split(/(?=[A-Z][a-z])/).join(' ');
|
|
2945
|
+
humanified = term?.replace(/\./g, ' ').replace(/_/g, ' ').trim();
|
|
2946
|
+
} else {
|
|
2947
|
+
const split = (original || term)?.split('.');
|
|
2948
|
+
humanified = split[split.length - 1]?.replace(/_/g, ' ').trim();
|
|
2949
|
+
// Special case 'name' to include any parent nested for sake of
|
|
2950
|
+
// specificity in the UI
|
|
2951
|
+
if (humanified === 'name' && split?.length > 1) {
|
|
2952
|
+
humanified = `${split[split?.length - 2]} ${humanified}`;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
return cap ? capitalize(humanified) : humanified;
|
|
2956
|
+
};
|
|
2957
|
+
/*https://github.com/NCI-GDC/portal-ui/blob/develop/src/packages/%40ncigdc/utils/ageDisplay.js*/ /**
|
|
2958
|
+
* Converts age in days into a human-readable format.
|
|
2959
|
+
*
|
|
2960
|
+
* @param ageInDays - The age in days.
|
|
2961
|
+
* @param yearsOnly - If true, only display years.
|
|
2962
|
+
* @defaultValue false
|
|
2963
|
+
* @param defaultValue - The default value to return if ageInDays is falsy.
|
|
2964
|
+
* @defaultValue "--"
|
|
2965
|
+
* @returns The formatted age string.
|
|
2966
|
+
*/ const ageDisplay = (ageInDays, yearsOnly = false, defaultValue = '--')=>{
|
|
2967
|
+
if (ageInDays !== 0 && !ageInDays) {
|
|
2968
|
+
return defaultValue;
|
|
2969
|
+
}
|
|
2970
|
+
const calculateYearsAndDays = (years, days)=>days === 365 ? [
|
|
2971
|
+
years + 1,
|
|
2972
|
+
0
|
|
2973
|
+
] : [
|
|
2974
|
+
years,
|
|
2975
|
+
days
|
|
2976
|
+
];
|
|
2977
|
+
const ABS_AGE_DAYS = Math.abs(ageInDays);
|
|
2978
|
+
const [years, remainingDays] = calculateYearsAndDays(Math.floor(ABS_AGE_DAYS / DAYS_IN_YEAR), Math.ceil(ABS_AGE_DAYS % DAYS_IN_YEAR));
|
|
2979
|
+
const formattedYears = years === 0 ? '' : `${years} ${years === 1 ? 'year' : 'years'}`;
|
|
2980
|
+
const formattedDays = !yearsOnly && remainingDays > 0 ? `${remainingDays} ${remainingDays === 1 ? 'day' : 'days'}` : years === 0 && remainingDays === 0 ? '0 days' : '';
|
|
2981
|
+
const ageString = [
|
|
2982
|
+
formattedYears,
|
|
2983
|
+
formattedDays
|
|
2984
|
+
].filter(Boolean).join(' ');
|
|
2985
|
+
return ageInDays >= 0 ? ageString : `-${ageString}`;
|
|
2986
|
+
};
|
|
2987
|
+
/**
|
|
2988
|
+
* Given an object of JSON, stringify it into a string.
|
|
2989
|
+
* @param obj - the object to stringify
|
|
2990
|
+
* @param defaults - the default value to return if the object is undefined
|
|
2991
|
+
* @category Utility
|
|
2992
|
+
*/ const stringifyJSONParam = (obj, defaults = '{}')=>obj ? JSON.stringify(obj) : defaults;
|
|
2780
2993
|
|
|
2781
2994
|
const queryWTSFederatedLoginStatus = async (signal)=>{
|
|
2782
2995
|
try {
|
|
@@ -4768,7 +4981,7 @@ const { useGraphQLQuery } = graphQLAPI;
|
|
|
4768
4981
|
return {
|
|
4769
4982
|
url: `${GEN3_GUPPY_API}/download`,
|
|
4770
4983
|
method: 'POST',
|
|
4771
|
-
body:
|
|
4984
|
+
body: queryBody,
|
|
4772
4985
|
cache: 'no-cache'
|
|
4773
4986
|
};
|
|
4774
4987
|
},
|
|
@@ -5321,24 +5534,6 @@ const userHasMethodOnAnyProject = (method, userAuthMapping = {})=>{
|
|
|
5321
5534
|
};
|
|
5322
5535
|
const userHasCreateOrUpdateOnAnyProject = (userAuthMapping = {})=>userHasMethodOnAnyProject('create', userAuthMapping) || userHasMethodOnAnyProject('update', userAuthMapping);
|
|
5323
5536
|
|
|
5324
|
-
const extractValuesFromObject = (jsonPathMappings, obj)=>{
|
|
5325
|
-
const result = {};
|
|
5326
|
-
const extractObjectValue = (jsonPath, obj)=>{
|
|
5327
|
-
const extractedValues = jsonpathPlus.JSONPath({
|
|
5328
|
-
path: jsonPath,
|
|
5329
|
-
json: obj
|
|
5330
|
-
});
|
|
5331
|
-
return extractedValues.length > 0 ? extractedValues[0] : undefined;
|
|
5332
|
-
};
|
|
5333
|
-
for(const key in jsonPathMappings){
|
|
5334
|
-
if (key in Object.keys(jsonPathMappings)) {
|
|
5335
|
-
// Extract value from an object and store it in the result.
|
|
5336
|
-
result[key] = extractObjectValue(jsonPathMappings[key], obj);
|
|
5337
|
-
}
|
|
5338
|
-
}
|
|
5339
|
-
return result;
|
|
5340
|
-
};
|
|
5341
|
-
|
|
5342
5537
|
const SubmissionGraphqlQuery = `query transactionList {
|
|
5343
5538
|
transactionList: transaction_log(last: 20) {
|
|
5344
5539
|
id
|
|
@@ -5622,12 +5817,15 @@ const isWorkspaceActive = (status)=>status === WorkspaceStatus.Running || status
|
|
|
5622
5817
|
const isWorkspaceRunningOrStopping = (status)=>status === WorkspaceStatus.Running || status === WorkspaceStatus.Terminating;
|
|
5623
5818
|
|
|
5624
5819
|
exports.Accessibility = Accessibility;
|
|
5820
|
+
exports.CART_LIMIT = CART_LIMIT;
|
|
5625
5821
|
exports.CohortStorage = CohortStorage;
|
|
5626
5822
|
exports.CoreProvider = CoreProvider;
|
|
5823
|
+
exports.DAYS_IN_YEAR = DAYS_IN_YEAR;
|
|
5627
5824
|
exports.DataLibraryStoreMode = DataLibraryStoreMode;
|
|
5628
5825
|
exports.EmptyFilterSet = EmptyFilterSet;
|
|
5629
5826
|
exports.EmptyWorkspaceStatusResponse = EmptyWorkspaceStatusResponse;
|
|
5630
5827
|
exports.EnumValueExtractorHandler = EnumValueExtractorHandler;
|
|
5828
|
+
exports.ExtractValueFromObject = ExtractValueFromObject;
|
|
5631
5829
|
exports.GEN3_API = GEN3_API;
|
|
5632
5830
|
exports.GEN3_AUTHZ_API = GEN3_AUTHZ_API;
|
|
5633
5831
|
exports.GEN3_COMMONS_NAME = GEN3_COMMONS_NAME;
|
|
@@ -5653,12 +5851,18 @@ exports.RequestedWorkspaceStatus = RequestedWorkspaceStatus;
|
|
|
5653
5851
|
exports.ToGqlHandler = ToGqlHandler;
|
|
5654
5852
|
exports.ValueExtractorHandler = ValueExtractorHandler;
|
|
5655
5853
|
exports.WorkspaceStatus = WorkspaceStatus;
|
|
5854
|
+
exports.addItemsToCart = addItemsToCart;
|
|
5855
|
+
exports.ageDisplay = ageDisplay;
|
|
5656
5856
|
exports.appendFilterToOperation = appendFilterToOperation;
|
|
5657
5857
|
exports.buildGetAggregationQuery = buildGetAggregationQuery;
|
|
5658
5858
|
exports.buildGetStatsAggregationQuery = buildGetStatsAggregationQuery;
|
|
5659
5859
|
exports.buildListItemsGroupedByDataset = buildListItemsGroupedByDataset;
|
|
5860
|
+
exports.buildNestedGQLFilter = buildNestedGQLFilter;
|
|
5660
5861
|
exports.calculatePercentageAsNumber = calculatePercentageAsNumber;
|
|
5661
5862
|
exports.calculatePercentageAsString = calculatePercentageAsString;
|
|
5863
|
+
exports.capitalize = capitalize;
|
|
5864
|
+
exports.cartReducer = cartReducer;
|
|
5865
|
+
exports.cartReducerPath = cartReducerPath;
|
|
5662
5866
|
exports.clearActiveWorkspaceId = clearActiveWorkspaceId;
|
|
5663
5867
|
exports.clearCohortFilters = clearCohortFilters;
|
|
5664
5868
|
exports.cohortReducer = cohortReducer;
|
|
@@ -5674,6 +5878,7 @@ exports.createGen3App = createGen3App;
|
|
|
5674
5878
|
exports.createGen3AppWithOwnStore = createGen3AppWithOwnStore;
|
|
5675
5879
|
exports.createNewCohort = createNewCohort;
|
|
5676
5880
|
exports.createUseCoreDataHook = createUseCoreDataHook;
|
|
5881
|
+
exports.customQueryStrForField = customQueryStrForField;
|
|
5677
5882
|
exports.defaultCohortNameGenerator = defaultCohortNameGenerator;
|
|
5678
5883
|
exports.downloadFromGuppyToBlob = downloadFromGuppyToBlob;
|
|
5679
5884
|
exports.downloadJSONDataFromGuppy = downloadJSONDataFromGuppy;
|
|
@@ -5685,6 +5890,7 @@ exports.extractEnumFilterValue = extractEnumFilterValue;
|
|
|
5685
5890
|
exports.extractFieldNameFromFullFieldName = extractFieldNameFromFullFieldName;
|
|
5686
5891
|
exports.extractFileDatasetsInRecords = extractFileDatasetsInRecords;
|
|
5687
5892
|
exports.extractFilterValue = extractFilterValue;
|
|
5893
|
+
exports.extractFiltersWithPrefixFromFilterSet = extractFiltersWithPrefixFromFilterSet;
|
|
5688
5894
|
exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFromFullFieldName;
|
|
5689
5895
|
exports.extractIndexFromDataLibraryCohort = extractIndexFromDataLibraryCohort;
|
|
5690
5896
|
exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
|
|
@@ -5714,6 +5920,7 @@ exports.handleGqlOperation = handleGqlOperation;
|
|
|
5714
5920
|
exports.handleOperation = handleOperation;
|
|
5715
5921
|
exports.hideModal = hideModal;
|
|
5716
5922
|
exports.histogramQueryStrForEachField = histogramQueryStrForEachField;
|
|
5923
|
+
exports.humanify = humanify;
|
|
5717
5924
|
exports.isAdditionalDataItem = isAdditionalDataItem;
|
|
5718
5925
|
exports.isArray = isArray;
|
|
5719
5926
|
exports.isAuthenticated = isAuthenticated;
|
|
@@ -5741,10 +5948,12 @@ exports.isHistogramRangeData = isHistogramRangeData;
|
|
|
5741
5948
|
exports.isHttpStatusError = isHttpStatusError;
|
|
5742
5949
|
exports.isIndexedFilterSetEmpty = isIndexedFilterSetEmpty;
|
|
5743
5950
|
exports.isIntersection = isIntersection;
|
|
5951
|
+
exports.isIntersectionOrUnion = isIntersectionOrUnion;
|
|
5744
5952
|
exports.isJSONObject = isJSONObject;
|
|
5745
5953
|
exports.isJSONValue = isJSONValue;
|
|
5746
5954
|
exports.isJSONValueArray = isJSONValueArray;
|
|
5747
5955
|
exports.isNameUnique = isNameUnique;
|
|
5956
|
+
exports.isNestedFilter = isNestedFilter;
|
|
5748
5957
|
exports.isNotDefined = isNotDefined;
|
|
5749
5958
|
exports.isObject = isObject;
|
|
5750
5959
|
exports.isOperandsType = isOperandsType;
|
|
@@ -5774,6 +5983,7 @@ exports.rawDataQueryStrForEachField = rawDataQueryStrForEachField;
|
|
|
5774
5983
|
exports.registerDefaultRemoteSupport = registerDefaultRemoteSupport;
|
|
5775
5984
|
exports.removeCohort = removeCohort;
|
|
5776
5985
|
exports.removeCohortFilter = removeCohortFilter;
|
|
5986
|
+
exports.removeItemsFromCart = removeItemsFromCart;
|
|
5777
5987
|
exports.requestorApi = requestorApi;
|
|
5778
5988
|
exports.resetUserState = resetUserState;
|
|
5779
5989
|
exports.resourcePathFromProjectID = resourcePathFromProjectID;
|
|
@@ -5787,6 +5997,10 @@ exports.selectAvailableCohortByName = selectAvailableCohortByName;
|
|
|
5787
5997
|
exports.selectAvailableCohorts = selectAvailableCohorts;
|
|
5788
5998
|
exports.selectCSRFToken = selectCSRFToken;
|
|
5789
5999
|
exports.selectCSRFTokenData = selectCSRFTokenData;
|
|
6000
|
+
exports.selectCart = selectCart;
|
|
6001
|
+
exports.selectCartCount = selectCartCount;
|
|
6002
|
+
exports.selectCartItem = selectCartItem;
|
|
6003
|
+
exports.selectCartItems = selectCartItems;
|
|
5790
6004
|
exports.selectCohortById = selectCohortById;
|
|
5791
6005
|
exports.selectCohortFilterCombineMode = selectCohortFilterCombineMode;
|
|
5792
6006
|
exports.selectCohortFilterExpanded = selectCohortFilterExpanded;
|
|
@@ -5834,6 +6048,7 @@ exports.setShouldShareFilters = setShouldShareFilters;
|
|
|
5834
6048
|
exports.setupCoreStore = setupCoreStore;
|
|
5835
6049
|
exports.showModal = showModal;
|
|
5836
6050
|
exports.statsQueryStrForEachField = statsQueryStrForEachField;
|
|
6051
|
+
exports.stringifyJSONParam = stringifyJSONParam;
|
|
5837
6052
|
exports.submissionApi = submissionApi;
|
|
5838
6053
|
exports.toggleCohortBuilderAllFilters = toggleCohortBuilderAllFilters;
|
|
5839
6054
|
exports.toggleCohortBuilderCategoryFilter = toggleCohortBuilderCategoryFilter;
|