Skip to content
Snippets Groups Projects
Commit 9315c62b authored by Piotr Gawron's avatar Piotr Gawron
Browse files

redux for fetching projects data

parent 713567ea
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...,!204Resolve "[MIN-320] opening project without permission"
import { ZOD_SEED } from '@/constants';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { z } from 'zod';
import { projectSchema } from '../projectSchema';
export const projectsFixture = createFixture(z.array(projectSchema), {
seed: ZOD_SEED,
array: { min: 1, max: 1 },
});
...@@ -50,6 +50,7 @@ export const apiPath = { ...@@ -50,6 +50,7 @@ export const apiPath = {
getAllBackgroundsByProjectIdQuery: (projectId: string): string => getAllBackgroundsByProjectIdQuery: (projectId: string): string =>
`projects/${projectId}/backgrounds/`, `projects/${projectId}/backgrounds/`,
getProjectById: (projectId: string): string => `projects/${projectId}`, getProjectById: (projectId: string): string => `projects/${projectId}`,
getProjects: (): string => `projects/`,
getSessionValid: (): string => `users/isSessionValid`, getSessionValid: (): string => `users/isSessionValid`,
postLogin: (): string => `doLogin`, postLogin: (): string => `doLogin`,
getConfigurationOptions: (): string => 'configuration/options/', getConfigurationOptions: (): string => 'configuration/options/',
......
import { DEFAULT_ERROR } from '@/constants/errors';
import { ProjectsState } from '@/redux/projects/projects.types';
export const PROJECTS_STATE_INITIAL_MOCK: ProjectsState = {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
import { projectsFixture } from '@/models/fixtures/projectsFixture';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { unwrapResult } from '@reduxjs/toolkit';
import { ProjectsState } from '@/redux/projects/projects.types';
import { getProjects } from '@/redux/projects/projects.thunks';
import { apiPath } from '../apiPath';
import projectsReducer from './projects.slice';
const mockedAxiosClient = mockNetworkNewAPIResponse();
const INITIAL_STATE: ProjectsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
describe('projects reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<ProjectsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('projects', projectsReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(projectsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after successfull getProjects query', async () => {
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.Ok, projectsFixture);
const { type } = await store.dispatch(getProjects());
const { data, loading, error } = store.getState().projects;
expect(type).toBe('project/getProjects/fulfilled');
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(projectsFixture);
});
it('should update store after failed getProjects query', async () => {
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.NotFound, []);
const action = await store.dispatch(getProjects());
const { data, loading, error } = store.getState().projects;
expect(action.type).toBe('project/getProjects/rejected');
expect(() => unwrapResult(action)).toThrow('Request failed with status code 404');
expect(loading).toEqual('failed');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual([]);
});
it('should update store on loading getProjects query', async () => {
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.Ok, projectsFixture);
const actionPromise = store.dispatch(getProjects());
const { data, loading } = store.getState().projects;
expect(data).toEqual([]);
expect(loading).toEqual('pending');
actionPromise.then(() => {
const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().projects;
expect(dataPromiseFulfilled).toEqual(projectsFixture);
expect(promiseFulfilled).toEqual('succeeded');
});
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { ProjectsState } from '@/redux/projects/projects.types';
import { getProjects } from '@/redux/projects/projects.thunks';
export const getProjectsReducer = (builder: ActionReducerMapBuilder<ProjectsState>): void => {
builder.addCase(getProjects.pending, state => {
state.loading = 'pending';
});
builder.addCase(getProjects.fulfilled, (state, action) => {
state.data = action.payload || undefined;
state.loading = 'succeeded';
});
builder.addCase(getProjects.rejected, state => {
state.loading = 'failed';
// TODO to discuss manage state of failure
});
};
import { createSlice } from '@reduxjs/toolkit';
import { ProjectsState } from '@/redux/projects/projects.types';
import { getProjectsReducer } from '@/redux/projects/projects.reducers';
const initialState: ProjectsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
const projectsSlice = createSlice({
name: 'project',
initialState,
reducers: {},
extraReducers: builder => {
getProjectsReducer(builder);
},
});
export default projectsSlice.reducer;
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { projectSchema } from '@/models/projectSchema';
import { Project } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { ThunkConfig } from '@/types/store';
import { z } from 'zod';
import { apiPath } from '../apiPath';
export const getProjects = createAsyncThunk<Project[], void, ThunkConfig>(
'project/getProjects',
async () => {
try {
const response = await axiosInstanceNewAPI.get<Project[]>(apiPath.getProjects());
const isDataValid = validateDataUsingZodSchema(response.data, z.array(projectSchema));
return isDataValid ? response.data : [];
} catch (error) {
return Promise.reject(error);
}
},
);
import { Project } from '@/types/models';
import { FetchDataState } from '@/types/fetchDataState';
export type ProjectsState = FetchDataState<Project[], []>;
import { CONSTANT_INITIAL_STATE } from '@/redux/constant/constant.adapter'; import { CONSTANT_INITIAL_STATE } from '@/redux/constant/constant.adapter';
import { PROJECTS_STATE_INITIAL_MOCK } from '@/redux/projects/projects.mock';
import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock'; import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock';
import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock'; import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock';
import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock'; import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
...@@ -29,6 +30,7 @@ import { USER_INITIAL_STATE_MOCK } from '../user/user.mock'; ...@@ -29,6 +30,7 @@ import { USER_INITIAL_STATE_MOCK } from '../user/user.mock';
export const INITIAL_STORE_STATE_MOCK: RootState = { export const INITIAL_STORE_STATE_MOCK: RootState = {
search: SEARCH_STATE_INITIAL_MOCK, search: SEARCH_STATE_INITIAL_MOCK,
project: PROJECT_STATE_INITIAL_MOCK, project: PROJECT_STATE_INITIAL_MOCK,
projects: PROJECTS_STATE_INITIAL_MOCK,
drugs: DRUGS_INITIAL_STATE_MOCK, drugs: DRUGS_INITIAL_STATE_MOCK,
chemicals: CHEMICALS_INITIAL_STATE_MOCK, chemicals: CHEMICALS_INITIAL_STATE_MOCK,
models: MODELS_INITIAL_STATE_MOCK, models: MODELS_INITIAL_STATE_MOCK,
......
...@@ -13,6 +13,7 @@ import modelsReducer from '@/redux/models/models.slice'; ...@@ -13,6 +13,7 @@ import modelsReducer from '@/redux/models/models.slice';
import overlayBioEntityReducer from '@/redux/overlayBioEntity/overlayBioEntity.slice'; import overlayBioEntityReducer from '@/redux/overlayBioEntity/overlayBioEntity.slice';
import overlaysReducer from '@/redux/overlays/overlays.slice'; import overlaysReducer from '@/redux/overlays/overlays.slice';
import projectReducer from '@/redux/project/project.slice'; import projectReducer from '@/redux/project/project.slice';
import projectsReducer from '@/redux/projects/projects.slice';
import reactionsReducer from '@/redux/reactions/reactions.slice'; import reactionsReducer from '@/redux/reactions/reactions.slice';
import searchReducer from '@/redux/search/search.slice'; import searchReducer from '@/redux/search/search.slice';
import userReducer from '@/redux/user/user.slice'; import userReducer from '@/redux/user/user.slice';
...@@ -37,6 +38,7 @@ import statisticsReducer from './statistics/statistics.slice'; ...@@ -37,6 +38,7 @@ import statisticsReducer from './statistics/statistics.slice';
export const reducers = { export const reducers = {
search: searchReducer, search: searchReducer,
project: projectReducer, project: projectReducer,
projects: projectsReducer,
drugs: drugsReducer, drugs: drugsReducer,
chemicals: chemicalsReducer, chemicals: chemicalsReducer,
bioEntity: bioEntityReducer, bioEntity: bioEntityReducer,
......
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