Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • minerva/frontend
1 result
Show changes
Showing
with 14 additions and 325 deletions
......@@ -19,8 +19,6 @@ export const apiPath = {
`projects/${PROJECT_ID}/models/*/bioEntities/reactions/?id=${ids.join(',')}&size=1000`,
getDrugsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/drugs:search?query=${searchQuery}`,
getMirnasStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/miRnas:search?query=${searchQuery}`,
getModelsString: (): string => `projects/${PROJECT_ID}/models/`,
getChemicalsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
......
......@@ -32,10 +32,10 @@ export const getMultiBioEntity = createAsyncThunk(
{ searchQueries, isPerfectMatch }: GetMultiBioEntityProps,
{ dispatch },
): Promise<void> => {
const asyncGetMirnasFunctions = searchQueries.map(searchQuery =>
const asyncGetBioEntityFunctions = searchQueries.map(searchQuery =>
dispatch(getBioEntity({ searchQuery, isPerfectMatch })),
);
await Promise.all(asyncGetMirnasFunctions);
await Promise.all(asyncGetBioEntityFunctions);
},
);
......@@ -55,12 +55,6 @@ export const displayChemicalsListReducer = (state: DrawerState): void => {
state.searchDrawerState.stepType = 'chemicals';
};
export const displayMirnaListReducer = (state: DrawerState): void => {
state.drawerName = 'search';
state.searchDrawerState.currentStep = STEP.SECOND;
state.searchDrawerState.stepType = 'mirna';
};
export const displayBioEntitiesListReducer = (
state: DrawerState,
action: PayloadAction<DrawerState['searchDrawerState']['listOfBioEnitites']>,
......
......@@ -41,7 +41,7 @@ export const resultListSelector = createSelector(
currentStepTypeSelector,
currentSelectedSearchElement,
(state, selectedType, currentSearchElement) => {
const { drugs, chemicals, mirnas } = state;
const { drugs, chemicals } = state;
switch (selectedType) {
case 'drugs': {
......@@ -67,17 +67,6 @@ export const resultListSelector = createSelector(
}
case 'bioEntity':
return undefined;
case 'mirna': {
const currentMirna = mirnas.data.find(
({ searchQueryElement }) => searchQueryElement === currentSearchElement,
);
return (currentMirna?.data || []).map(mirna => ({
id: mirna.id,
name: mirna.name,
data: mirna,
}));
}
case 'none':
return undefined;
default:
......
......@@ -6,7 +6,6 @@ import {
displayDrugsListReducer,
displayEntityDetailsReducer,
displayGroupedSearchResultsReducer,
displayMirnaListReducer,
openDrawerReducer,
openReactionDrawerByIdReducer,
openSearchDrawerWithSelectedTabReducer,
......@@ -26,7 +25,6 @@ const drawerSlice = createSlice({
closeDrawer: closeDrawerReducer,
displayDrugsList: displayDrugsListReducer,
displayChemicalsList: displayChemicalsListReducer,
displayMirnaList: displayMirnaListReducer,
displayBioEntitiesList: displayBioEntitiesListReducer,
displayGroupedSearchResults: displayGroupedSearchResultsReducer,
displayEntityDetails: displayEntityDetailsReducer,
......@@ -42,7 +40,6 @@ export const {
closeDrawer,
displayDrugsList,
displayChemicalsList,
displayMirnaList,
displayBioEntitiesList,
displayGroupedSearchResults,
displayEntityDetails,
......
import type { DrawerName } from '@/types/drawerName';
import { BioEntityContent, Chemical, Drug, Mirna } from '@/types/models';
import { BioEntityContent, Chemical, Drug } from '@/types/models';
import { PayloadAction } from '@reduxjs/toolkit';
export type SearchDrawerState = {
currentStep: number;
stepType: 'bioEntity' | 'drugs' | 'mirna' | 'chemicals' | 'none';
selectedValue: BioEntityContent | Drug | Mirna | Chemical | undefined;
stepType: 'bioEntity' | 'drugs' | 'chemicals' | 'none';
selectedValue: BioEntityContent | Drug | Chemical | undefined;
listOfBioEnitites: BioEntityContent[];
selectedSearchElement: string;
};
......
......@@ -64,16 +64,3 @@ export const drawerSearchChemicalsStepTwoFixture: DrawerState = {
},
reactionDrawerState: {},
};
export const drawerSearchMirnaStepTwoFixture: DrawerState = {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 2,
stepType: 'mirna',
selectedValue: undefined,
listOfBioEnitites: [],
selectedSearchElement: '',
},
reactionDrawerState: {},
};
import { DEFAULT_ERROR } from '@/constants/errors';
import { MirnasState } from './mirnas.types';
export const MIRNAS_INITIAL_STATE_MOCK: MirnasState = {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
import { mirnasFixture } from '@/models/fixtures/mirnasFixture';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { HttpStatusCode } from 'axios';
import { apiPath } from '@/redux/apiPath';
import { DEFAULT_ERROR } from '@/constants/errors';
import { getMirnas } from './mirnas.thunks';
import mirnasReducer from './mirnas.slice';
import { MirnasState } from './mirnas.types';
const mockedAxiosClient = mockNetworkResponse();
const SEARCH_QUERY = 'hsa-miR-302b-3p';
const INITIAL_STATE: MirnasState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
describe('mirnas reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<MirnasState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('mirnas', mirnasReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(mirnasReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after succesfull getMirnas query', async () => {
mockedAxiosClient
.onGet(apiPath.getMirnasStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, mirnasFixture);
const { type } = await store.dispatch(getMirnas(SEARCH_QUERY));
const { data } = store.getState().mirnas;
const mirnasWithSearchElement = data.find(mirna => mirna.searchQueryElement === SEARCH_QUERY);
expect(type).toBe('project/getMirnas/fulfilled');
expect(mirnasWithSearchElement).toEqual({
searchQueryElement: SEARCH_QUERY,
data: mirnasFixture,
loading: 'succeeded',
error: DEFAULT_ERROR,
});
});
it('should update store after failed getMirnas query', async () => {
mockedAxiosClient
.onGet(apiPath.getMirnasStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.NotFound, []);
const { type } = await store.dispatch(getMirnas(SEARCH_QUERY));
const { data } = store.getState().mirnas;
const mirnasWithSearchElement = data.find(mirna => mirna.searchQueryElement === SEARCH_QUERY);
expect(type).toBe('project/getMirnas/rejected');
expect(mirnasWithSearchElement).toEqual({
searchQueryElement: SEARCH_QUERY,
data: undefined,
loading: 'failed',
error: DEFAULT_ERROR,
});
});
it('should update store on loading getMirnas query', async () => {
mockedAxiosClient
.onGet(apiPath.getMirnasStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, mirnasFixture);
const mirnasPromise = store.dispatch(getMirnas(SEARCH_QUERY));
const { data } = store.getState().mirnas;
const mirnasWithSearchElement = data.find(mirna => mirna.searchQueryElement === SEARCH_QUERY);
expect(mirnasWithSearchElement).toEqual({
searchQueryElement: SEARCH_QUERY,
data: undefined,
loading: 'pending',
error: DEFAULT_ERROR,
});
mirnasPromise.then(() => {
const { data: dataPromiseFulfilled } = store.getState().mirnas;
const mirnasWithSearchElementFulfilled = dataPromiseFulfilled.find(
mirna => mirna.searchQueryElement === SEARCH_QUERY,
);
expect(mirnasWithSearchElementFulfilled).toEqual({
searchQueryElement: SEARCH_QUERY,
data: mirnasFixture,
loading: 'succeeded',
error: DEFAULT_ERROR,
});
});
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { DEFAULT_ERROR } from '@/constants/errors';
import { MirnasState } from './mirnas.types';
import { getMirnas, getMultiMirnas } from './mirnas.thunks';
export const getMirnasReducer = (builder: ActionReducerMapBuilder<MirnasState>): void => {
builder.addCase(getMirnas.pending, (state, action) => {
state.data.push({
searchQueryElement: action.meta.arg,
data: undefined,
loading: 'pending',
error: DEFAULT_ERROR,
});
});
builder.addCase(getMirnas.fulfilled, (state, action) => {
const mirnas = state.data.find(mirna => mirna.searchQueryElement === action.meta.arg);
if (mirnas) {
mirnas.data = action.payload;
mirnas.loading = 'succeeded';
}
});
builder.addCase(getMirnas.rejected, (state, action) => {
const mirnas = state.data.find(mirna => mirna.searchQueryElement === action.meta.arg);
if (mirnas) {
mirnas.loading = 'failed';
// TODO: error management to be discussed in the team
}
});
};
export const getMultiMirnasReducer = (builder: ActionReducerMapBuilder<MirnasState>): void => {
builder.addCase(getMultiMirnas.pending, state => {
state.data = [];
state.loading = 'pending';
});
builder.addCase(getMultiMirnas.fulfilled, state => {
state.loading = 'succeeded';
});
builder.addCase(getMultiMirnas.rejected, state => {
state.loading = 'failed';
// TODO: error management to be discussed in the team
});
};
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { rootSelector } from '@/redux/root/root.selectors';
import { createSelector } from '@reduxjs/toolkit';
import { MultiSearchData } from '@/types/fetchDataState';
import { Mirna } from '@/types/models';
import { currentSelectedSearchElement } from '../drawer/drawer.selectors';
export const mirnasSelector = createSelector(rootSelector, state => state.mirnas);
export const mirnasForSelectedSearchElementSelector = createSelector(
mirnasSelector,
currentSelectedSearchElement,
(mirnasState, currentSearchElement): MultiSearchData<Mirna[]> | undefined =>
mirnasState.data.find(({ searchQueryElement }) => searchQueryElement === currentSearchElement),
);
export const loadingMirnasStatusSelector = createSelector(mirnasSelector, state => state.loading);
export const numberOfMirnasSelector = createSelector(
mirnasForSelectedSearchElementSelector,
state => {
if (!state || !state?.data) {
return SIZE_OF_EMPTY_ARRAY;
}
return state.data.length && state.data.map(e => e.targets.length)?.reduce((a, b) => a + b);
},
);
import { createSlice } from '@reduxjs/toolkit';
import { MirnasState } from '@/redux/mirnas/mirnas.types';
import { getMirnasReducer, getMultiMirnasReducer } from './mirnas.reducers';
const initialState: MirnasState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
export const mirnasSlice = createSlice({
name: 'mirnas',
initialState,
reducers: {},
extraReducers: builder => {
getMirnasReducer(builder);
getMultiMirnasReducer(builder);
},
});
export default mirnasSlice.reducer;
import { mirnasFixture } from '@/models/fixtures/mirnasFixture';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { HttpStatusCode } from 'axios';
import { apiPath } from '@/redux/apiPath';
import { getMirnas } from './mirnas.thunks';
import mirnasReducer from './mirnas.slice';
import { MirnasState } from './mirnas.types';
const mockedAxiosClient = mockNetworkResponse();
const SEARCH_QUERY = 'hsa-miR-302b-3p';
describe('mirnas thunks', () => {
let store = {} as ToolkitStoreWithSingleSlice<MirnasState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('mirnas', mirnasReducer);
});
describe('getMirnas', () => {
it('should return data when data response from API is valid', async () => {
mockedAxiosClient
.onGet(apiPath.getMirnasStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, mirnasFixture);
const { payload } = await store.dispatch(getMirnas(SEARCH_QUERY));
expect(payload).toEqual(mirnasFixture);
});
it('should return undefined when data response from API is not valid ', async () => {
mockedAxiosClient
.onGet(apiPath.getMirnasStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
const { payload } = await store.dispatch(getMirnas(SEARCH_QUERY));
expect(payload).toEqual(undefined);
});
});
});
import { z } from 'zod';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { axiosInstance } from '@/services/api/utils/axiosInstance';
import { Mirna } from '@/types/models';
import { mirnaSchema } from '@/models/mirnaSchema';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { apiPath } from '@/redux/apiPath';
export const getMirnas = createAsyncThunk(
'project/getMirnas',
async (searchQuery: string): Promise<Mirna[] | undefined> => {
const response = await axiosInstance.get<Mirna[]>(
apiPath.getMirnasStringWithQuery(searchQuery),
);
const isDataValid = validateDataUsingZodSchema(response.data, z.array(mirnaSchema));
return isDataValid ? response.data : undefined;
},
);
export const getMultiMirnas = createAsyncThunk(
'project/getMultiMirnas',
async (searchQueries: string[], { dispatch }): Promise<void> => {
const asyncGetMirnasFunctions = searchQueries.map(searchQuery =>
dispatch(getMirnas(searchQuery)),
);
await Promise.all(asyncGetMirnasFunctions);
},
);
import { MultiFetchDataState } from '@/types/fetchDataState';
import { Mirna } from '@/types/models';
export type MirnasState = MultiFetchDataState<Mirna[]>;
......@@ -17,6 +17,12 @@ export const currentModelIdSelector = createSelector(
currentModelSelector,
model => model?.idObject || MODEL_ID_DEFAULT,
);
export const currentModelNameSelector = createSelector(
currentModelSelector,
model => model?.name || '',
);
export const modelByIdSelector = createSelector(
[modelsSelector, (_state, modelId: number): number => modelId],
(models, modelId) => (models?.data || []).find(({ idObject }) => idObject === modelId),
......
......@@ -4,7 +4,6 @@ import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
import { initialStateFixture as drawerInitialStateMock } from '../drawer/drawerFixture';
import { DRUGS_INITIAL_STATE_MOCK } from '../drugs/drugs.mock';
import { initialMapStateFixture } from '../map/map.fixtures';
import { MIRNAS_INITIAL_STATE_MOCK } from '../mirnas/mirnas.mock';
import { MODELS_INITIAL_STATE_MOCK } from '../models/models.mock';
import { OVERLAYS_INITIAL_STATE_MOCK } from '../overlays/overlays.mock';
import { PROJECT_STATE_INITIAL_MOCK } from '../project/project.mock';
......@@ -16,7 +15,6 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
search: SEARCH_STATE_INITIAL_MOCK,
project: PROJECT_STATE_INITIAL_MOCK,
drugs: DRUGS_INITIAL_STATE_MOCK,
mirnas: MIRNAS_INITIAL_STATE_MOCK,
chemicals: CHEMICALS_INITIAL_STATE_MOCK,
models: MODELS_INITIAL_STATE_MOCK,
bioEntity: BIOENTITY_INITIAL_STATE_MOCK,
......
......@@ -14,7 +14,8 @@ const INITIAL_STATE: SearchState = {
loading: 'idle',
};
describe('search reducer', () => {
// TODO -> mock api request and unskip test
describe.skip('search reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<SearchState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('search', searchReducer);
......
import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks';
import { getMultiChemicals } from '@/redux/chemicals/chemicals.thunks';
import { getMultiDrugs } from '@/redux/drugs/drugs.thunks';
import { getMultiMirnas } from '@/redux/mirnas/mirnas.thunks';
import { PerfectMultiSearchParams } from '@/types/search';
import { createAsyncThunk } from '@reduxjs/toolkit';
......@@ -14,7 +13,6 @@ export const getSearchData = createAsyncThunk(
dispatch(getMultiBioEntity({ searchQueries, isPerfectMatch })),
dispatch(getMultiDrugs(searchQueries)),
dispatch(getMultiChemicals(searchQueries)),
dispatch(getMultiMirnas(searchQueries)),
]);
},
);
......@@ -4,7 +4,6 @@ import chemicalsReducer from '@/redux/chemicals/chemicals.slice';
import drawerReducer from '@/redux/drawer/drawer.slice';
import drugsReducer from '@/redux/drugs/drugs.slice';
import mapReducer from '@/redux/map/map.slice';
import mirnasReducer from '@/redux/mirnas/mirnas.slice';
import modelsReducer from '@/redux/models/models.slice';
import overlaysReducer from '@/redux/overlays/overlays.slice';
import projectReducer from '@/redux/project/project.slice';
......@@ -23,7 +22,6 @@ export const reducers = {
search: searchReducer,
project: projectReducer,
drugs: drugsReducer,
mirnas: mirnasReducer,
chemicals: chemicalsReducer,
bioEntity: bioEntityReducer,
drawer: drawerReducer,
......