Skip to content
Snippets Groups Projects
Commit 6f4401b0 authored by mateuszmiko's avatar mateuszmiko
Browse files

Merge branch 'feature/MIN-59-connect-search-chemical-query' into 'development'

feat: add chemical query (MIN-59)

See merge request !26
parents c250dcaa a7a26c81
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!26feat: add chemical query (MIN-59)
Pipeline #79428 passed
import { getBioEntityContents } from '@/redux/bioEntityContents/bioEntityContents.thunks';
import { getChemicals } from '@/redux/chemicals/chemicals.thunks';
import { getDrugs } from '@/redux/drugs/drugs.thunks';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { getMirnas } from '@/redux/mirnas/mirnas.thunks';
......@@ -17,6 +18,7 @@ const ReduxPage = (): JSX.Element => {
dispatch(getDrugs('aspirin'));
dispatch(getMirnas('hsa-miR-302b-3p'));
dispatch(getBioEntityContents('park7'));
dispatch(getChemicals('Corticosterone'));
};
return (
......
import { idSchema } from '@/models/idSchema';
import { z } from 'zod';
import { referenceSchema } from './referenceSchema';
import { targetSchema } from './targetSchema';
export const chemicalSchema = z.object({
id: idSchema,
name: z.string(),
description: z.string(),
directEvidence: z.string().nullable(),
directEvidenceReferences: z.array(referenceSchema),
synonyms: z.array(z.string()),
references: z.array(referenceSchema),
targets: z.array(targetSchema),
});
import { ZOD_SEED } from '@/constants';
import { chemicalSchema } from '@/models/chemicalSchema';
import { z } from 'zod';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
export const chemicalsFixture = createFixture(z.array(chemicalSchema), {
seed: ZOD_SEED,
array: { min: 2, max: 2 },
});
import { z } from 'zod';
export const idSchema = z.object({
annotatorClassName: z.string(),
id: z.number(),
link: z.string(),
resource: z.string(),
type: z.string(),
});
......@@ -19,4 +19,10 @@ describe('api path', () => {
`projects/${PROJECT_ID}/models/*/bioEntities:search?query=park7`,
);
});
it('should return url string for bio entity content', () => {
expect(apiPath.getChemicalsStringWithQuery('Corticosterone')).toBe(
`projects/${PROJECT_ID}/chemicals:search?query=Corticosterone`,
);
});
});
......@@ -7,4 +7,6 @@ export const apiPath = {
`projects/${PROJECT_ID}/drugs:search?query=${searchQuery}`,
getMirnasStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/miRnas:search?query=${searchQuery}`,
getChemicalsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
};
import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { apiPath } from '@/redux/apiPath';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import chemicalsReducer from './chemicals.slice';
import { getChemicals } from './chemicals.thunks';
import { ChemicalsState } from './chemicals.types';
const mockedAxiosClient = mockNetworkResponse();
const SEARCH_QUERY = 'Corticosterone';
const INITIAL_STATE: ChemicalsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
describe('chemicals reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<ChemicalsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('chemicals', chemicalsReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(chemicalsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after succesfull getChemicals query', async () => {
mockedAxiosClient
.onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, chemicalsFixture);
const { type } = await store.dispatch(getChemicals(SEARCH_QUERY));
const { data, loading, error } = store.getState().chemicals;
expect(type).toBe('project/getChemicals/fulfilled');
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(chemicalsFixture);
});
it('should update store after failed getChemicals query', async () => {
mockedAxiosClient
.onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.NotFound, chemicalsFixture);
const { type } = await store.dispatch(getChemicals(SEARCH_QUERY));
const { data, loading, error } = store.getState().chemicals;
expect(type).toBe('project/getChemicals/rejected');
expect(loading).toEqual('failed');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual([]);
});
it('should update store on loading getChemicals query', async () => {
mockedAxiosClient
.onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, chemicalsFixture);
const chemicalsPromise = store.dispatch(getChemicals(SEARCH_QUERY));
const { data, loading } = store.getState().chemicals;
expect(data).toEqual([]);
expect(loading).toEqual('pending');
chemicalsPromise.then(() => {
const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().chemicals;
expect(dataPromiseFulfilled).toEqual(chemicalsFixture);
expect(promiseFulfilled).toEqual('succeeded');
});
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { getChemicals } from './chemicals.thunks';
import { ChemicalsState } from './chemicals.types';
export const getChemicalsReducer = (builder: ActionReducerMapBuilder<ChemicalsState>): void => {
builder.addCase(getChemicals.pending, state => {
state.loading = 'pending';
});
builder.addCase(getChemicals.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = 'succeeded';
});
builder.addCase(getChemicals.rejected, state => {
state.loading = 'failed';
// TODO to discuss manage state of failure
});
};
import { ChemicalsState } from '@/redux/chemicals/chemicals.types';
import { createSlice } from '@reduxjs/toolkit';
import { getChemicalsReducer } from './chemicals.reducers';
const initialState: ChemicalsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
export const chemicalsSlice = createSlice({
name: 'chemicals',
initialState,
reducers: {},
extraReducers: builder => {
getChemicalsReducer(builder);
},
});
export default chemicalsSlice.reducer;
import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { apiPath } from '@/redux/apiPath';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import chemicalsReducer from './chemicals.slice';
import { getChemicals } from './chemicals.thunks';
import { ChemicalsState } from './chemicals.types';
const mockedAxiosClient = mockNetworkResponse();
const SEARCH_QUERY = 'Corticosterone';
describe('chemicals thunks', () => {
let store = {} as ToolkitStoreWithSingleSlice<ChemicalsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('chemicals', chemicalsReducer);
});
describe('getChemiclas', () => {
it('should return data when data response from API is valid', async () => {
mockedAxiosClient
.onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, chemicalsFixture);
const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY));
expect(payload).toEqual(chemicalsFixture);
});
it('should return undefined when data response from API is not valid ', async () => {
mockedAxiosClient
.onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
.reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY));
expect(payload).toEqual(undefined);
});
});
});
import { chemicalSchema } from '@/models/chemicalSchema';
import { apiPath } from '@/redux/apiPath';
import { axiosInstance } from '@/services/api/utils/axiosInstance';
import { Chemical } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { z } from 'zod';
export const getChemicals = createAsyncThunk(
'project/getChemicals',
async (searchQuery: string): Promise<Chemical[] | undefined> => {
const response = await axiosInstance.get<Chemical[]>(
apiPath.getChemicalsStringWithQuery(searchQuery),
);
const isDataValid = validateDataUsingZodSchema(response.data, z.array(chemicalSchema));
return isDataValid ? response.data : undefined;
},
);
import { FetchDataState } from '@/types/fetchDataState';
import { Chemical } from '@/types/models';
export type ChemicalsState = FetchDataState<Chemical[]>;
import { bioEntityContentSchema } from '@/models/bioEntityContentSchema';
import { chemicalSchema } from '@/models/chemicalSchema';
import { disease } from '@/models/disease';
import { drugSchema } from '@/models/drugSchema';
import { mirnaSchema } from '@/models/mirnaSchema';
import { organism } from '@/models/organism';
import { projectSchema } from '@/models/project';
import { mirnaSchema } from '@/models/mirnaSchema';
import { bioEntityContentSchema } from '@/models/bioEntityContentSchema';
import { z } from 'zod';
export type Project = z.infer<typeof projectSchema>;
......@@ -12,3 +13,4 @@ export type Disease = z.infer<typeof disease>;
export type Drug = z.infer<typeof drugSchema>;
export type Mirna = z.infer<typeof mirnaSchema>;
export type BioEntityContent = z.infer<typeof bioEntityContentSchema>;
export type Chemical = z.infer<typeof chemicalSchema>;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment