Skip to content
Snippets Groups Projects
Commit 77c58674 authored by Adrian Orłów's avatar Adrian Orłów :fire:
Browse files

Merge branch 'feature/MIN-103-render-map-base-data-and-api-comm' into 'development'

feat: add map full data mgmt

See merge request !43
parents 1f1cf84b 8eea9f3d
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...,!43feat: add map full data mgmt
Pipeline #80323 passed
Showing
with 273 additions and 40 deletions
NEXT_PUBLIC_BASE_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/api'
NEXT_PUBLIC_BASE_NEW_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/new_api/'
NEXT_PUBLIC_PROJECT_ID = 'pdmap_appu_test'
\ No newline at end of file
NEXT_PUBLIC_PROJECT_ID = 'pdmap_appu_test'
ZOD_SEED = 997
......@@ -4,6 +4,12 @@ const nextConfig = {
experimental: {
fontLoaders: [{ loader: '@next/font/google', options: { subsets: ['latin'] } }],
},
env: {
BASE_API_URL: process.env.NEXT_PUBLIC_BASE_API_URL || '',
BASE_MAP_IMAGES_URL: process.env.BASE_MAP_IMAGES_URL || '',
PROJECT_ID: process.env.NEXT_PUBLIC_PROJECT_ID || '',
ZOD_SEED: process.env.ZOD_SEED || 123,
},
};
module.exports = nextConfig;
import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { MODELS_MOCK } from '@/models/mocks/modelsMock';
import { StoreType } from '@/redux/store';
import { Accordion } from '@/shared/Accordion';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { Accordion } from '@/shared/Accordion';
import { MODELS_MOCK } from '@/models/mocks/modelsMock';
import { BioEntitiesAccordion } from './BioEntitiesAccordion.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
......@@ -35,7 +35,7 @@ describe('BioEntitiesAccordion - component', () => {
error: { name: '', message: '' },
},
models: {
data: undefined,
data: [],
loading: 'pending',
error: { name: '', message: '' },
},
......
/* eslint-disable no-magic-numbers */
export const BASE_API_URL = process.env.NEXT_PUBLIC_BASE_API_URL || '';
export const BASE_MAP_IMAGES_URL = process.env.BASE_MAP_IMAGES_URL || '';
export const BASE_NEW_API_URL = process.env.NEXT_PUBLIC_BASE_NEW_API_URL || '';
export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || '';
export const ZOD_SEED = 997;
export const ZOD_SEED = parseInt(process.env.ZOD_SEED || '123', 10);
export const BIO_ENTITY = 'bioEntity';
export const DRUGS_CHEMICALS_MIRNA = ['drugs', 'chemicals', 'mirna'];
import { ZOD_SEED } from '@/constants';
import { z } from 'zod';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { mapBackground } from '../mapBackground';
export const backgroundsFixture = createFixture(z.array(mapBackground), {
seed: ZOD_SEED,
array: { min: 2, max: 2 },
});
import { ZOD_SEED } from '@/constants';
import { mapModelSchema } from '@/models/modelSchema';
import { z } from 'zod';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { z } from 'zod';
import { ZOD_SEED } from '@/constants';
import { modelSchema } from '@/models/modelSchema';
export const modelsFixture = createFixture(z.array(modelSchema), {
export const modelsFixture = createFixture(z.array(mapModelSchema), {
seed: ZOD_SEED,
array: { min: 3, max: 3 },
});
import { ZOD_SEED } from '@/constants';
import { z } from 'zod';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { mapOverlay } from '../mapOverlay';
export const overlaysFixture = createFixture(z.array(mapOverlay), {
seed: ZOD_SEED,
array: { min: 2, max: 2 },
});
import { ZOD_SEED } from '@/constants';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
import { projectSchema } from '../project';
export const projectFixture = createFixture(projectSchema, {
seed: ZOD_SEED,
array: { min: 1, max: 1 },
});
import { z } from 'zod';
export const mapBackground = z.object({
id: z.number(),
name: z.string(),
defaultOverlay: z.boolean(),
project: z.object({ projectId: z.string() }),
creator: z.object({ login: z.string() }),
status: z.string(),
progress: z.number(),
description: z.null(),
order: z.number(),
images: z.array(
z.object({
id: z.number(),
model: z.object({ id: z.number() }),
projectBackground: z.object({ id: z.number() }),
path: z.string(),
}),
),
});
import { z } from 'zod';
export const mapOverlay = z.object({
name: z.string(),
googleLicenseConsent: z.boolean(),
creator: z.string(),
description: z.string(),
genomeType: z.null(),
genomeVersion: z.null(),
idObject: z.number(),
publicOverlay: z.boolean(),
type: z.string(),
order: z.number(),
});
import { Model } from '@/types/models';
import { MapModel } from '@/types/models';
export const MODELS_MOCK: Model[] = [
export const MODELS_MOCK: MapModel[] = [
{
idObject: 5053,
width: 26779.25,
......
import { z } from 'zod';
import { referenceSchema } from './referenceSchema';
import { authorSchema } from './authorSchema';
import { referenceSchema } from './referenceSchema';
export const modelSchema = z.object({
export const mapModelSchema = z.object({
/** name of the map */
name: z.string(),
description: z.string(),
......
import { z } from 'zod';
import { positionSchema } from './positionSchema';
export const overviewImageLink = z.union([
z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
imageLinkId: z.number(),
type: z.string(),
}),
z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
zoomLevel: z.number(),
modelPoint: positionSchema,
modelLinkId: z.number(),
type: z.string(),
}),
]);
import { z } from 'zod';
import { overviewImageLink } from './overviewImageLink';
export const overviewImageView = z.object({
idObject: z.number(),
filename: z.string(),
width: z.number(),
height: z.number(),
links: z.array(overviewImageLink),
});
import { z } from 'zod';
import { disease } from './disease';
import { organism } from './organism';
import { overviewImageView } from './overviewImageView';
export const projectSchema = z.object({
version: z.string(),
......@@ -18,30 +19,5 @@ export const projectSchema = z.object({
projectId: z.string(),
creationDate: z.string(),
mapCanvasType: z.string(),
overviewImageViews: z.array(
z.object({
idObject: z.number(),
filename: z.string(),
width: z.number(),
height: z.number(),
links: z.array(
z.union([
z.object({
idObject: z.number(),
polygon: z.array(z.object({ x: z.number(), y: z.number() })),
imageLinkId: z.number(),
type: z.string(),
}),
z.object({
idObject: z.number(),
polygon: z.array(z.object({ x: z.number(), y: z.number() })),
zoomLevel: z.number(),
modelPoint: z.object({ x: z.number(), y: z.number() }),
modelLinkId: z.number(),
type: z.string(),
}),
]),
),
}),
),
overviewImageViews: z.array(overviewImageView),
});
......@@ -10,4 +10,11 @@ export const apiPath = {
getModelsString: (): string => `projects/${PROJECT_ID}/models/`,
getChemicalsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
getAllOverlaysByProjectIdQuery: (
projectId: string,
{ publicOverlay }: { publicOverlay: boolean },
): string => `projects/${projectId}/overlays/?publicOverlay=${String(publicOverlay)}`,
getAllBackgroundsByProjectIdQuery: (projectId: string): string =>
`projects/${projectId}/backgrounds/`,
getProjectById: (projectId: string): string => `projects/${projectId}`,
};
import { createSelector } from '@reduxjs/toolkit';
import { mapDataSelector } from '../map/map.selectors';
import { rootSelector } from '../root/root.selectors';
export const backgroundsSelector = createSelector(rootSelector, state => state.backgrounds);
export const backgroundsDataSelector = createSelector(
backgroundsSelector,
backgrounds => backgrounds.data || [],
);
export const currentBackgroundSelector = createSelector(
backgroundsDataSelector,
mapDataSelector,
(backgrounds, mapData) => backgrounds.find(background => background.id === mapData.backgroundId),
);
export const currentBackgroundImageSelector = createSelector(
mapDataSelector,
currentBackgroundSelector,
(mapData, background) =>
background ? background.images.find(image => image.model.id === mapData.modelId) : undefined,
);
export const currentBackgroundImagePathSelector = createSelector(
currentBackgroundImageSelector,
image => (image ? image.path : ''),
);
import { PROJECT_ID } from '@/constants';
import { backgroundsFixture } from '@/models/fixtures/backgroundsFixture';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { apiPath } from '../apiPath';
import backgroundsReducer from './backgrounds.slice';
import { getAllBackgroundsByProjectId } from './backgrounds.thunks';
import { BackgroundsState } from './backgrounds.types';
const mockedAxiosClient = mockNetworkResponse();
const INITIAL_STATE: BackgroundsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
describe('backgrounds reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<BackgroundsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('backgrounds', backgroundsReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(backgroundsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after succesfull getAllBackgroundsByProjectId query', async () => {
mockedAxiosClient
.onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
.reply(HttpStatusCode.Ok, backgroundsFixture);
const { type } = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID));
const { data, loading, error } = store.getState().backgrounds;
expect(type).toBe('backgrounds/getAllBackgroundsByProjectId/fulfilled');
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(backgroundsFixture);
});
it('should update store after failed getAllBackgroundsByProjectId query', async () => {
mockedAxiosClient
.onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
.reply(HttpStatusCode.NotFound, []);
const { type } = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID));
const { data, loading, error } = store.getState().backgrounds;
expect(type).toBe('backgrounds/getAllBackgroundsByProjectId/rejected');
expect(loading).toEqual('failed');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual([]);
});
it('should update store on loading getAllBackgroundsByProjectId query', async () => {
mockedAxiosClient
.onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
.reply(HttpStatusCode.Ok, backgroundsFixture);
const actionPromise = store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID));
const { data, loading } = store.getState().backgrounds;
expect(data).toEqual([]);
expect(loading).toEqual('pending');
actionPromise.then(() => {
const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
store.getState().backgrounds;
expect(dataPromiseFulfilled).toEqual(backgroundsFixture);
expect(promiseFulfilled).toEqual('succeeded');
});
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { getAllBackgroundsByProjectId } from './backgrounds.thunks';
import { BackgroundsState } from './backgrounds.types';
export const getAllBackgroundsByProjectIdReducer = (
builder: ActionReducerMapBuilder<BackgroundsState>,
): void => {
builder.addCase(getAllBackgroundsByProjectId.pending, state => {
state.loading = 'pending';
});
builder.addCase(getAllBackgroundsByProjectId.fulfilled, (state, action) => {
state.data = action.payload || [];
state.loading = 'succeeded';
});
builder.addCase(getAllBackgroundsByProjectId.rejected, state => {
state.loading = 'failed';
// TODO to discuss manage state of failure
});
};
import { createSlice } from '@reduxjs/toolkit';
import { getAllBackgroundsByProjectIdReducer } from './backgrounds.reducers';
import { BackgroundsState } from './backgrounds.types';
const initialState: BackgroundsState = {
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
const backgroundsState = createSlice({
name: 'backgrounds',
initialState,
reducers: {},
extraReducers: builder => {
getAllBackgroundsByProjectIdReducer(builder);
},
});
export default backgroundsState.reducer;
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