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

fetch projects

parent 9315c62b
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"
......@@ -3,18 +3,22 @@ import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { loginUserSelector } from '@/redux/user/user.selectors';
import { openLoginModal } from '@/redux/modal/modal.slice';
import { MINUS_ONE } from '@/constants/common';
import { MINUS_ONE, ZERO } from '@/constants/common';
import { Button } from '@/shared/Button';
import { adminEmailValSelector } from '@/redux/configuration/configuration.selectors';
import { projectsSelector } from '@/redux/projects/projects.selectors';
export const AccessDeniedModal: React.FC = () => {
const dispatch = useAppDispatch();
const login = useAppSelector(loginUserSelector);
const projects = useAppSelector(projectsSelector);
const adminEmail = useAppSelector(adminEmailValSelector);
const isAnonymousLogin = !login;
const isProjectsAvailable = projects.length > ZERO;
const isAdminEmail = adminEmail !== '' && adminEmail !== undefined;
const handleGoBack = async (e: React.FormEvent<HTMLButtonElement>): Promise<void> => {
......@@ -52,6 +56,7 @@ export const AccessDeniedModal: React.FC = () => {
</div>
</div>
)}
{isProjectsAvailable && <div>Switch to another map</div>}
{isAdminEmail && (
<div className="mt-1 text-center">
<Button
......
import { ZOD_SEED } from '@/constants';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { z } from 'zod';
import { pageableSchema } from '@/models/pageableSchema';
import { projectSchema } from '../projectSchema';
export const projectsFixture = createFixture(z.array(projectSchema), {
export const projectPageFixture = createFixture(pageableSchema(projectSchema), {
seed: ZOD_SEED,
array: { min: 1, max: 1 },
});
import { z } from 'zod';
import { ZodTypeAny } from 'zod/lib/types';
export const pageableSchema = <T extends ZodTypeAny>(type: T): ZodTypeAny =>
z.object({
totalPages: z.number().nonnegative(),
totalElements: z.number().nonnegative(),
numberOfElements: z.number().nonnegative(),
size: z.number().positive(),
number: z.number().nonnegative(),
content: z.array(type),
});
......@@ -2,6 +2,7 @@ import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store';
import { Action, createListenerMiddleware, isRejected } from '@reduxjs/toolkit';
import { createErrorData } from '@/utils/error-report/errorReporting';
import { openAccessDeniedModal, openErrorReportModal } from '@/redux/modal/modal.slice';
import { getProjects } from '@/redux/projects/projects.thunks';
export const errorListenerMiddleware = createListenerMiddleware();
......@@ -13,6 +14,7 @@ export const errorMiddlewareListener = async (
): Promise<void> => {
if (isRejected(action) && action.type !== 'user/getSessionValid/rejected') {
if (action.error.code === '403') {
dispatch(getProjects());
dispatch(openAccessDeniedModal());
} else {
const errorData = await createErrorData(action.error, getState());
......
export const PROJECTS_FETCHING_ERROR_PREFIX = 'Failed to fetch projects';
import { projectsFixture } from '@/models/fixtures/projectsFixture';
import { projectPageFixture } from '@/models/fixtures/projectsFixture';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
......@@ -31,7 +31,7 @@ describe('projects reducer', () => {
expect(projectsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after successfull getProjects query', async () => {
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.Ok, projectsFixture);
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.Ok, projectPageFixture);
const { type } = await store.dispatch(getProjects());
const { data, loading, error } = store.getState().projects;
......@@ -39,7 +39,7 @@ describe('projects reducer', () => {
expect(type).toBe('project/getProjects/fulfilled');
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(projectsFixture);
expect(data).toEqual(projectPageFixture.content);
});
it('should update store after failed getProjects query', async () => {
......@@ -49,14 +49,16 @@ describe('projects reducer', () => {
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(() => unwrapResult(action)).toThrow(
"Failed to fetch projects: The page you're looking for doesn't exist. Please verify the URL and try again.",
);
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);
mockedAxiosClient.onGet(apiPath.getProjects()).reply(HttpStatusCode.Ok, projectPageFixture);
const actionPromise = store.dispatch(getProjects());
......@@ -67,7 +69,7 @@ describe('projects reducer', () => {
actionPromise.then(() => {
const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().projects;
expect(dataPromiseFulfilled).toEqual(projectsFixture);
expect(dataPromiseFulfilled).toEqual(projectPageFixture.content);
expect(promiseFulfilled).toEqual('succeeded');
});
});
......
import { rootSelector } from '@/redux/root/root.selectors';
import { createSelector } from '@reduxjs/toolkit';
export const projectsRootSelector = createSelector(rootSelector, state => state.projects);
export const projectsSelector = createSelector(projectsRootSelector, state => state.data);
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { projectSchema } from '@/models/projectSchema';
import { Project } from '@/types/models';
import { PageOf, Project } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { ThunkConfig } from '@/types/store';
import { z } from 'zod';
import { getError } from '@/utils/error-report/getError';
import { PROJECTS_FETCHING_ERROR_PREFIX } from '@/redux/projects/projects.constants';
import { pageableSchema } from '@/models/pageableSchema';
import { apiPath } from '../apiPath';
export const getProjects = createAsyncThunk<Project[], void, ThunkConfig>(
'project/getProjects',
async () => {
try {
const response = await axiosInstanceNewAPI.get<Project[]>(apiPath.getProjects());
const response = await axiosInstanceNewAPI.get<PageOf<Project>>(apiPath.getProjects());
const isDataValid = validateDataUsingZodSchema(response.data, z.array(projectSchema));
const isDataValid = validateDataUsingZodSchema(response.data, pageableSchema(projectSchema));
return isDataValid ? response.data : [];
return isDataValid ? response.data.content : [];
} catch (error) {
return Promise.reject(error);
return Promise.reject(getError({ error, prefix: PROJECTS_FETCHING_ERROR_PREFIX }));
}
},
);
......@@ -122,3 +122,12 @@ export type MarkerLine = z.infer<typeof markerLineSchema>;
export type MarkerWithPosition = z.infer<typeof markerWithPositionSchema>;
export type Marker = z.infer<typeof markerSchema>;
export type JavaStacktrace = z.infer<typeof javaStacktraceSchema>;
export type PageOf<T> = {
totalPages: number;
totalElements: number;
numberOfElements: number;
size: number;
number: number;
content: T[];
};
......@@ -9,6 +9,8 @@ export const validateDataUsingZodSchema: IsApiResponseValid = (data, schema: Zod
// TODO - probably need to rething way of handling parsing errors, for now let's leave it to console.log
// eslint-disable-next-line no-console
console.error('Error on parsing data', validationResults.error);
// eslint-disable-next-line no-console
console.error(validationResults.error.message);
}
return validationResults.success;
......
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