diff --git a/public/config.js b/public/config.js index 51047de3538bcf7b82d3559bb9b25764705acdf9..4d245aa0d429a56c8c6eb2e3cf44078f4edef59e 100644 --- a/public/config.js +++ b/public/config.js @@ -6,6 +6,6 @@ window.config = { BASE_API_URL: `${root}/minerva/api`, BASE_NEW_API_URL: `${root}/minerva/new_api/`, BASE_MAP_IMAGES_URL: `${root}/`, - DEFAULT_PROJECT_ID: 'minervar_example', + DEFAULT_PROJECT_ID: 'sample', ADMIN_PANEL_URL: `${root}/minerva/admin.xhtml`, }; diff --git a/src/components/FunctionalArea/ContextMenu/ContextMenu.component.tsx b/src/components/FunctionalArea/ContextMenu/ContextMenu.component.tsx index 873cdf573c813ff7e0b09adffe05dcd3e5a1133a..973785e63078e5201eac60db07a5f1ecda9bb398 100644 --- a/src/components/FunctionalArea/ContextMenu/ContextMenu.component.tsx +++ b/src/components/FunctionalArea/ContextMenu/ContextMenu.component.tsx @@ -9,7 +9,7 @@ import { twMerge } from 'tailwind-merge'; import { FIRST_ARRAY_ELEMENT, SECOND_ARRAY_ELEMENT, ZERO } from '@/constants/common'; import { PluginsContextMenu } from '@/services/pluginsManager/pluginContextMenu/pluginsContextMenu'; -import { BioEntity, Reaction } from '@/types/models'; +import { BioEntity, NewReaction } from '@/types/models'; import { ClickCoordinates } from '@/services/pluginsManager/pluginContextMenu/pluginsContextMenu.types'; import { currentModelSelector } from '@/redux/models/models.selectors'; import { mapDataLastPositionSelector } from '@/redux/map/map.selectors'; @@ -44,7 +44,7 @@ export const ContextMenu = (): React.ReactNode => { const modelId = model ? model.idObject : ZERO; const handleCallback = ( - callback: (coordinates: ClickCoordinates, element: BioEntity | Reaction | undefined) => void, + callback: (coordinates: ClickCoordinates, element: BioEntity | NewReaction | undefined) => void, ) => { return () => { dispatch(closeContextMenu()); diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx index 3a899dec667f65ac55c8a25c9ff47da618640e46..802c56a2b8b76ee3a34fc80bc59c2dcafced0a9b 100644 --- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx +++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx @@ -1,4 +1,3 @@ -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures'; import { StoreType } from '@/redux/store'; import { @@ -8,6 +7,7 @@ import { import { fireEvent, render, screen } from '@testing-library/react'; import { useRouter } from 'next/router'; import { initialStateFixture } from '@/redux/drawer/drawerFixture'; +import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture'; import { SearchBar } from './SearchBar.component'; const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => { @@ -89,7 +89,7 @@ describe('SearchBar - component', () => { const { store } = renderComponent({ reactions: { ...INITIAL_STORE_STATE_MOCK.reactions, - data: reactionsFixture, + data: newReactionsFixture, }, }); const input = screen.getByTestId<HTMLInputElement>('search-input'); diff --git a/src/components/Map/Drawer/Drawer.component.test.tsx b/src/components/Map/Drawer/Drawer.component.test.tsx index 2499e7f3b459484389a46d4453426b5123cc12ac..eb258f9d310fdc4a8ba9166bae6af051811ba314 100644 --- a/src/components/Map/Drawer/Drawer.component.test.tsx +++ b/src/components/Map/Drawer/Drawer.component.test.tsx @@ -1,5 +1,4 @@ import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { openBioEntityDrawerById, openReactionDrawerById, @@ -15,6 +14,7 @@ import { import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import type {} from 'redux-thunk/extend-redux'; import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; +import { newReactionFixture } from '@/models/fixtures/newReactionFixture'; import { Drawer } from './Drawer.component'; const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => { @@ -93,11 +93,11 @@ describe('Drawer - component', () => { describe('reaction drawer', () => { it('should open drawer and display reaction', async () => { - const { id } = reactionsFixture[FIRST_ARRAY_ELEMENT]; + const { id, model } = newReactionFixture; const { store } = renderComponent({ reactions: { - data: reactionsFixture, + data: [newReactionFixture], loading: 'succeeded', error: { message: '', name: '' }, }, @@ -105,7 +105,7 @@ describe('Drawer - component', () => { expect(screen.queryByTestId('reaction-drawer')).not.toBeInTheDocument(); await act(() => { - store.dispatch(getReactionsByIds([id])); + store.dispatch(getReactionsByIds({ ids: [{ id, modelId: model }] })); store.dispatch(openReactionDrawerById(id)); }); await waitFor(() => expect(screen.getByTestId('reaction-drawer')).toBeInTheDocument()); diff --git a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx index 80c6cd351b1dce422bbc0cec3c861e2f481c1dbf..f1a8a2b897513e95c9f58850870214518ee04765 100644 --- a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx +++ b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx @@ -31,9 +31,6 @@ export const ProjectInfoDrawer = (): JSX.Element => { const sourceDownloadLink = BASE_API_URL + apiPath.getSourceFile(); - // eslint-disable-next-line no-console - console.log(sourceDownloadLink); - let licenseName: string = ''; if (project) { licenseName = project.license ? project.license.name : project.customLicenseName; diff --git a/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.test.tsx b/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.test.tsx index 1a635a0b5ac357f43962460455aa1c9683780c41..82c68ae9fe71a836edf39f23b7435cbc60a4c898 100644 --- a/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.test.tsx +++ b/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.test.tsx @@ -1,5 +1,3 @@ -import { SECOND_ARRAY_ELEMENT } from '@/constants/common'; -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { DRAWER_INITIAL_STATE } from '@/redux/drawer/drawer.constants'; import { StoreType } from '@/redux/store'; import { @@ -7,8 +5,10 @@ import { getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; -import { ReactionDrawer } from './ReactionDrawer.component'; +import { newReactionFixture } from '@/models/fixtures/newReactionFixture'; +import { referenceFixture } from '@/models/fixtures/referenceFixture'; import { DEFAULT_REFERENCE_SOURCE } from './ReactionDrawer.constants'; +import { ReactionDrawer } from './ReactionDrawer.component'; const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); @@ -25,6 +25,8 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St ); }; +const reference = { ...referenceFixture, link: 'https://uni.lu' }; + describe('ReactionDrawer - component', () => { beforeEach(() => { jest.resetAllMocks(); @@ -52,7 +54,7 @@ describe('ReactionDrawer - component', () => { }); describe('when there IS a matching reaction', () => { - const reaction = reactionsFixture[SECOND_ARRAY_ELEMENT]; + const reaction = { ...newReactionFixture, references: [reference] }; const filteredReferences = reaction.references.filter( ref => ref.link !== null && ref.link !== undefined, @@ -70,7 +72,7 @@ describe('ReactionDrawer - component', () => { beforeEach(() => renderComponent({ reactions: { - data: reactionsFixture, + data: [reaction], loading: 'succeeded', error: { message: '', name: '' }, }, @@ -85,12 +87,12 @@ describe('ReactionDrawer - component', () => { it('should show drawer header', () => { expect(screen.getByText('Reaction:')).toBeInTheDocument(); - expect(screen.getByText(reaction.reactionId)).toBeInTheDocument(); + expect(screen.getByText(reaction.idReaction)).toBeInTheDocument(); }); it('should show drawer reaction type', () => { expect(screen.getByText('Type:')).toBeInTheDocument(); - expect(screen.getByText(reaction.type)).toBeInTheDocument(); + // expect(screen.getByText(reaction.type)).toBeInTheDocument(); }); it('should show drawer reaction annotations title', () => { diff --git a/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.tsx b/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.tsx index 1423ef7d11a71e2b51e8206a047ba28d3c0e4d83..8658b1c8946e5d2e5bc99a8f1c596abb4101e668 100644 --- a/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.tsx +++ b/src/components/Map/Drawer/ReactionDrawer/ReactionDrawer.component.tsx @@ -8,6 +8,7 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { currentDrawerReactionCommentsSelector } from '@/redux/bioEntity/bioEntity.selectors'; import { CommentItem } from '@/components/Map/Drawer/BioEntityDrawer/Comments/CommentItem.component'; import { ZERO } from '@/constants/common'; +import ReactionTypeEnum from '@/utils/reaction/ReactionTypeEnum'; import { ReferenceGroup } from './ReferenceGroup'; import { ConnectedBioEntitiesList } from './ConnectedBioEntitiesList'; @@ -22,19 +23,21 @@ export const ReactionDrawer = (): React.ReactNode => { } const isCommentAvailable = commentsData.length > ZERO; + type ReactionTypeKey = keyof typeof ReactionTypeEnum; + const type = ReactionTypeEnum[reaction.sboTerm as ReactionTypeKey]; return ( <div className="h-full max-h-full" data-testid="reaction-drawer"> <DrawerHeading title={ <> - <span className="font-normal">Reaction:</span> {reaction.reactionId} + <span className="font-normal">Reaction:</span> {reaction.idReaction} </> } /> <div className="flex h-[calc(100%-93px)] max-h-[calc(100%-93px)] flex-col gap-6 overflow-y-auto p-6"> <div className="text-sm font-normal"> - Type: <b className="font-semibold">{reaction.type}</b> + Type: <b className="font-semibold">{type}</b> </div> <hr className="border-b border-b-divide" /> {reaction.notes && <div className="text-sm font-normal">{reaction.notes}</div>} diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem/BioEntitiesPinsListItem.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem/BioEntitiesPinsListItem.component.test.tsx index e2d365f6b978ff8e05c5a21732e9c2640cd74e63..a217830adf2ec4c1e922d330ffcfaca56aa6d094 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem/BioEntitiesPinsListItem.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem/BioEntitiesPinsListItem.component.test.tsx @@ -1,7 +1,6 @@ /* eslint-disable no-magic-numbers */ import { DEFAULT_MAX_ZOOM } from '@/constants/map'; import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { MAP_INITIAL_STATE } from '@/redux/map/map.constants'; import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures'; import { AppDispatch, RootState, StoreType } from '@/redux/store'; @@ -14,6 +13,7 @@ import { render, screen } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; import { MockStoreEnhanced } from 'redux-mock-store'; import { getTypeBySBOTerm } from '@/utils/bioEntity/getTypeBySBOTerm'; +import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture'; import { BioEntitiesPinsListItem } from './BioEntitiesPinsListItem.component'; import { PinListBioEntity } from './BioEntitiesPinsListItem.types'; @@ -332,7 +332,7 @@ describe('BioEntitiesPinsListItem - component ', () => { }, reactions: { ...INITIAL_STORE_STATE_MOCK.reactions, - data: reactionsFixture, + data: newReactionsFixture, }, }, ); diff --git a/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.test.ts b/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.test.ts index 5282e2464e30e3b39011d1bb8cac05c8185a9ad5..47b21acd073d2ff3528c5fe0fa8ec6d018662ed4 100644 --- a/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.test.ts @@ -2,7 +2,6 @@ import { openReactionDrawerById, selectTab } from '@/redux/drawer/drawer.slice'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds'; -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { apiPath } from '@/redux/apiPath'; import { HttpStatusCode } from 'axios'; @@ -30,26 +29,24 @@ describe('clickHandleReaction', () => { return { unwrap: jest.fn().mockResolvedValue([]), payload: { - data: [reactionsFixture[0]], + data: [newReactionFixture], }, }; }); - reactionId = reactionsFixture[0].id; - modelId = reactionsFixture[0].modelId; + reactionId = newReactionFixture.id; + modelId = newReactionFixture.model; mockedAxiosClient - .onGet(apiPath.getReactionByIdInNewApi(reactionId, modelId)) + .onGet(apiPath.getNewReaction(modelId, reactionId)) .reply(HttpStatusCode.Ok, bioEntityFixture); [ - ...reactionsFixture[0].products, - ...reactionsFixture[0].reactants, - ...reactionsFixture[0].modifiers, + ...newReactionFixture.products, + ...newReactionFixture.reactants, + ...newReactionFixture.modifiers, ].forEach(element => { mockedAxiosClient - .onGet( - apiPath.getElementById('aliasId' in element ? element.aliasId : element.element, modelId), - ) + .onGet(apiPath.getElementById(element.element, modelId)) .reply(HttpStatusCode.Ok, bioEntityFixture); }); clickHandleReaction(dispatch, hasFitBounds)( diff --git a/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.ts b/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.ts index 392e4c3eb66f4f79f9179004c16b98abad13e59c..f9cbc6acdbec7395730b802a229eaac0f3ece550 100644 --- a/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.ts +++ b/src/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/clickHandleReaction.ts @@ -7,7 +7,6 @@ import { FEATURE_TYPE } from '@/constants/features'; import { setMultipleBioEntityContents } from '@/redux/bioEntity/bioEntity.slice'; import { addNumbersToEntityNumberData } from '@/redux/entityNumber/entityNumber.slice'; import { setReactions } from '@/redux/reactions/reactions.slice'; -import mapNewReactionToReaction from '@/utils/reaction/mapNewReactionToReaction'; import { mapReactionToBioEntity } from '@/utils/bioEntity/mapReactionToBioEntity'; import getModelElementsIdsFromReaction from '@/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/getModelElementsIdsFromReaction'; import { mapModelElementToBioEntity } from '@/utils/bioEntity/mapModelElementToBioEntity'; @@ -40,7 +39,7 @@ export const clickHandleReaction = const bioEntityReaction = mapReactionToBioEntity(reaction); dispatch(setMultipleBioEntityContents(reactionBioEntities)); dispatch(addNumbersToEntityNumberData(reactionBioEntities.map(reactionBioEntity => reactionBioEntity.elementId))); - dispatch(setReactions([mapNewReactionToReaction(reaction)])); + dispatch(setReactions([reaction])); const result = reactionBioEntities.map((bioEntity) => {return { bioEntity, perfect: true };}); result.push({ bioEntity: bioEntityReaction, perfect: true }); diff --git a/src/components/Map/MapViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts index a6350f6357cf3b6d6bcd8acedbcca8099010e5d1..fa7c4196fa3cb80bd59a41e66013561d2a9c425e 100644 --- a/src/components/Map/MapViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts +++ b/src/components/Map/MapViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts @@ -2,7 +2,7 @@ import { LINE_COLOR, LINE_WIDTH } from '@/constants/canvas'; import { markersLinesCurrentMapDataSelector } from '@/redux/markers/markers.selectors'; import { allReactionsSelectorOfCurrentMap } from '@/redux/reactions/reactions.selector'; -import { MarkerLine, Reaction } from '@/types/models'; +import { NewReaction } from '@/types/models'; import { LinePoint } from '@/types/reactions'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; import { Feature } from 'ol'; @@ -15,15 +15,20 @@ import { useEffect, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { createOverlayLineFeature } from '@/components/Map/MapViewer/utils/config/overlaysLayer/createOverlayLineFeature'; import { Geometry } from 'ol/geom'; +import { getReactionLineSegments } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint'; import { getLineFeature } from './getLineFeature'; -const getLinePoints = ({ start, end }: Pick<MarkerLine, 'start' | 'end'>): LinePoint => [ - start, - end, -]; - -const getReactionsLines = (reactions: Reaction[]): LinePoint[] => - reactions.map(({ lines }) => lines.map(getLinePoints)).flat(); +const getReactionsLines = (reactions: NewReaction[]): LinePoint[] => + reactions + .map(reaction => getReactionLineSegments(reaction)) + .flat() + .map(segment => [ + { + x: segment.x1, + y: segment.y1, + }, + { x: segment.x2, y: segment.y2 }, + ]); export const useOlMapReactionsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>> => { const pointToProjection = usePointToProjection(); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.ts index bdaabeaf599386a29cc7dfc878aaadd53f695887..77d8be74b8f7cf8df6af8885821b0678fb5bcfb6 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapView.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapView.ts @@ -1,5 +1,5 @@ /* eslint-disable no-magic-numbers */ -import { EXTENT_PADDING_MULTIPLICATOR, OPTIONS } from '@/constants/map'; +import { EXTENT_PADDING_MULTIPLICATOR, OPTIONS, ZOOM_RESCALING_FACTOR } from '@/constants/map'; import { mapDataInitialPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors'; import { MapInstance, Point } from '@/types/map'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; @@ -56,13 +56,13 @@ export const useOlMapView = ({ mapInstance }: UseOlMapViewInput): MapConfig['vie center: [center.x, center.y], zoom: mapInitialPosition.z, showFullExtent: OPTIONS.showFullExtent, - zoomFactor: 2 ** (1 / 3), - originalMaxZoom: mapSize.maxZoom * 3, + zoomFactor: 2 ** (1 / ZOOM_RESCALING_FACTOR), + originalMaxZoom: mapSize.maxZoom * ZOOM_RESCALING_FACTOR, maxZoom: mapSize.width < 1.6 * mapSize.tileSize || mapSize.height < 1.6 * mapSize.tileSize - ? Math.max(15, mapSize.maxZoom * 3) - : mapSize.maxZoom * 3, - minZoom: mapSize.minZoom * 3, + ? Math.max(15, mapSize.maxZoom * ZOOM_RESCALING_FACTOR) + : mapSize.maxZoom * ZOOM_RESCALING_FACTOR, + minZoom: mapSize.minZoom * ZOOM_RESCALING_FACTOR, extent, }), [ diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.test.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.test.ts index b318ffc16bb9d58724352e7f05ec6390fa5751bd..943699197355550107fb3d0a17e03e30c0575974 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.test.ts @@ -1,21 +1,27 @@ /* eslint-disable no-magic-numbers */ -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; -import { findClosestReactionPoint } from './findClosestReactionPoint'; +import { newReactionFixture } from '@/models/fixtures/newReactionFixture'; +import { ZOOM_RESCALING_FACTOR } from '@/constants/map'; +import { findClosestReactionPoint, getReactionLineSegments } from './findClosestReactionPoint'; describe('findClosestReactionPoint', () => { const reaction = { - ...reactionsFixture[0], - lines: [{ start: { x: 0, y: 0 }, end: { x: 3, y: 4 }, type: 'START' }], + ...newReactionFixture, }; - const validPoint = { x: 1, y: 1 }; - const invalidPoint = { - x: 1115, - y: 2225, - }; + const validPoint = { x: reaction.line.segments[0].x1, y: reaction.line.segments[0].y1 }; + + let x = reaction.line.segments[0].x1; + let y = reaction.line.segments[0].y1; + getReactionLineSegments(reaction).forEach(lineSegment => { + x = Math.min(x, lineSegment.x1); + x = Math.min(x, lineSegment.x2); + y = Math.min(y, lineSegment.y1); + y = Math.min(y, lineSegment.y2); + }); + const invalidPoint = { x: x - 1000, y: y - 1000 }; const searchDistance = '10'; const maxZoom = 10; - const zoom = 5; + const zoom = ZOOM_RESCALING_FACTOR * 5; it('should return the matching line segment if a point within the search distance is found', () => { const result = findClosestReactionPoint({ @@ -26,7 +32,7 @@ describe('findClosestReactionPoint', () => { point: validPoint, }); - expect(result).toEqual(reaction.lines[0]); + expect(result).toEqual(reaction.line.segments[0]); }); it('should return undefined if no point within the search distance is found', () => { diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.ts index c29fc89a830fdd5e8a9472ca51c99cb1e58b44bc..33fbd42f9f6b89b519f71ddba3898c6dfe50a6ad 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/findClosestReactionPoint.ts @@ -1,20 +1,38 @@ /* eslint-disable no-magic-numbers */ import { Point as PointType } from '@/types/map'; -import { Reaction } from '@/types/models'; +import { NewReaction, Segment } from '@/types/models'; import { distance } from 'ol/coordinate'; import { LineString, Point } from 'ol/geom'; import { getMaxClickDistance } from './getMaxClickDistance'; -type ReactionLine = Reaction['lines'][0]; +type ReactionLine = Segment; type FindClosestReactionArgs = { - reaction: Reaction; + reaction: NewReaction; searchDistance: string; maxZoom: number; zoom: number; point: PointType; }; +export function getReactionLineSegments(reaction: NewReaction): Segment[] { + const result: Segment[] = []; + result.push(...reaction.line.segments); + reaction.reactants.forEach(reactant => { + result.push(...reactant.line.segments); + }); + reaction.products.forEach(product => { + result.push(...product.line.segments); + }); + reaction.modifiers.forEach(modifier => { + result.push(...modifier.line.segments); + }); + reaction.operators.forEach(operator => { + result.push(...operator.line.segments); + }); + return result; +} + export const findClosestReactionPoint = ({ reaction, searchDistance, @@ -26,10 +44,12 @@ export const findClosestReactionPoint = ({ const clickedPoint = new Point([point.x, point.y]); - const closestLine = reaction.lines.find(line => { + const lines = getReactionLineSegments(reaction); + + const closestLine = lines.find(line => { const lineString = new LineString([ - [line.start.x, line.start.y], - [line.end.x, line.end.y], + [line.x1, line.y1], + [line.x2, line.y2], ]); const closestPointOnLine = lineString.getClosestPoint(clickedPoint.getCoordinates()); diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getBioEntitiesIdsFromReaction.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getBioEntitiesIdsFromReaction.ts deleted file mode 100644 index 2707040e2a180a028179d9baeebcdcc4e23c8253..0000000000000000000000000000000000000000 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getBioEntitiesIdsFromReaction.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Reaction } from '@/types/models'; - -export const getBioEntitiesIdsFromReaction = (reaction: Reaction): string[] => { - const { products, reactants, modifiers } = reaction; - const productsIds = products.map(p => ('aliasId' in p ? p.aliasId : p.element)); - const reactantsIds = reactants.map(r => ('aliasId' in r ? r.aliasId : r.element)); - const modifiersIds = modifiers.map(m => ('aliasId' in m ? m.aliasId : m.element)); - return [...productsIds, ...reactantsIds, ...modifiersIds].map(identifier => String(identifier)); -}; diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.test.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.test.ts index 35711a82103c21f5a6b5d8ef23906de3846a9a54..3425b44236afae43040dffcf155a08db91f28a4d 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.test.ts @@ -1,12 +1,13 @@ /* eslint-disable no-magic-numbers */ +import { ZOOM_RESCALING_FACTOR } from '@/constants/map'; import { getMaxClickDistance } from './getMaxClickDistance'; describe('getMaxClickDistance', () => { it.each([ - [10, 5, '10', 320], - [10, 5, '20', 640], - [10, 2, '10', 2560], - [10, 3, '18', 2304], + [10, ZOOM_RESCALING_FACTOR * 5, '10', 320], + [10, ZOOM_RESCALING_FACTOR * 5, '20', 640], + [10, ZOOM_RESCALING_FACTOR * 2, '10', 2560], + [10, ZOOM_RESCALING_FACTOR * 3, '18', 2304], ])( 'should calculate the maximum click distance correctly', (maxZoom, zoom, searchDistance, expected) => { diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.ts index bc02c450001a0c11ed798aafe18095ce3f350c1a..42317465fbd5e03beeec21c1f6a99f133cc4270c 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/getMaxClickDistance.ts @@ -1,6 +1,7 @@ /* eslint-disable no-magic-numbers */ import { ZOOM_FACTOR } from '@/constants/common'; +import { ZOOM_RESCALING_FACTOR } from '@/constants/map'; export const getMaxClickDistance = ( maxZoom: number, @@ -13,7 +14,7 @@ export const getMaxClickDistance = ( throw new Error('Invalid search distance. Please provide a valid number.'); } - const zoomDiff = maxZoom - zoom; + const zoomDiff = (ZOOM_RESCALING_FACTOR * maxZoom - zoom) / ZOOM_RESCALING_FACTOR; const maxDistance = distance * ZOOM_FACTOR ** zoomDiff; return maxDistance; diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.test.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.test.ts index aae76f614efd91b111eb16846f5a3ef92ae9f222..bd25417f10ee1dd4c71662c9e84b96c86a971d75 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.test.ts @@ -9,6 +9,7 @@ import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreA import { waitFor } from '@testing-library/react'; import { HttpStatusCode } from 'axios'; import { bioEntityFixture } from '@/models/fixtures/bioEntityFixture'; +import { ZOOM_RESCALING_FACTOR } from '@/constants/map'; import { handleAliasResults } from './handleAliasResults'; jest.mock('../../../../../../services/pluginsManager/map/triggerSearch/searchFitBounds'); @@ -22,7 +23,7 @@ const SEARCH_CONFIG_MOCK = { y: 700, }, maxZoom: 9, - zoom: 5, + zoom: ZOOM_RESCALING_FACTOR * 5, isResultDrawerOpen: false, }; diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.test.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.test.ts index cdd0e68dbdd816071ec6f6ca2d7e4d6475100094..f44aa7c86cfd4fc74609b1540a14c261fbc5bfcc 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.test.ts @@ -1,7 +1,6 @@ /* eslint-disable no-magic-numbers */ import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { bioEntityResponseFixture } from '@/models/fixtures/bioEntityContentsFixture'; -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { ELEMENT_SEARCH_RESULT_MOCK_ALIAS, ELEMENT_SEARCH_RESULT_MOCK_REACTION, @@ -13,6 +12,8 @@ import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreA import { HttpStatusCode } from 'axios'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { bioEntityFixture } from '@/models/fixtures/bioEntityFixture'; +import { newReactionFixture } from '@/models/fixtures/newReactionFixture'; +import { ZOOM_RESCALING_FACTOR } from '@/constants/map'; import * as findClosestReactionPoint from './findClosestReactionPoint'; import { handleReactionResults } from './handleReactionResults'; @@ -37,18 +38,17 @@ const SEARCH_CONFIG_MOCK = { y: 3012, }, maxZoom: 9, - zoom: 3, + zoom: ZOOM_RESCALING_FACTOR * 3, isResultDrawerOpen: false, }; const reaction = { - ...reactionsFixture[0], + ...newReactionFixture, id: ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, - modelId: ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, + model: ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, products: [], reactants: [], modifiers: [], - lines: [{ start: { x: 0, y: 0 }, end: { x: 3, y: 4 }, type: 'START' }], }; describe('handleReactionResults - util', () => { @@ -84,28 +84,22 @@ describe('handleReactionResults - util', () => { mockedAxiosNewClient .onGet( - apiPath.getReactionByIdInNewApi( - ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, + apiPath.getNewReaction( ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, + ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, ), ) - .reply(HttpStatusCode.Ok, bioEntityFixture); - - mockedAxiosOldClient - .onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id])) - .reply(HttpStatusCode.Ok, [ - { - ...reaction, - reactants: [], - products: [], - modifiers: [ - { - ...reactionsFixture[0].modifiers[0], - aliasId: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id, - }, - ], - }, - ]); + .reply(HttpStatusCode.Ok, { + ...reaction, + reactants: [], + products: [], + modifiers: [ + { + ...newReactionFixture.modifiers[0], + element: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id, + }, + ], + }); beforeEach(async () => { await handleReactionResults( @@ -167,12 +161,12 @@ describe('handleReactionResults - util', () => { mockedAxiosNewClient .onGet( - apiPath.getReactionByIdInNewApi( - ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, + apiPath.getNewReaction( ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, + ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, ), ) - .reply(HttpStatusCode.Ok, bioEntityFixture); + .reply(HttpStatusCode.Ok, reaction); mockedAxiosOldClient .onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id])) @@ -183,8 +177,8 @@ describe('handleReactionResults - util', () => { products: [], modifiers: [ { - ...reactionsFixture[0].modifiers[0], - aliasId: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id, + ...newReactionFixture.modifiers[0], + element: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id, }, ], }, @@ -205,9 +199,9 @@ describe('handleReactionResults - util', () => { })(ELEMENT_SEARCH_RESULT_MOCK_REACTION); const actions = store.getActions(); - const acionTypes = actions.map(action => action.type); + const actionTypes = actions.map(action => action.type); - expect(acionTypes).toStrictEqual([ + expect(actionTypes).toStrictEqual([ 'reactions/getByIds/pending', 'reactions/getByIds/fulfilled', 'drawer/closeDrawer', @@ -242,7 +236,7 @@ describe('handleReactionResults - util', () => { describe('when search config provided and matching reaction found', () => { const point = { x: 1, y: 1 }; const maxZoom = 10; - const zoom = 5; + const zoom = ZOOM_RESCALING_FACTOR * 5; it('should open reaction drawer and fetch bio entities', async () => { const { store } = getReduxStoreWithActionsListener(); @@ -257,16 +251,12 @@ describe('handleReactionResults - util', () => { mockedAxiosNewClient .onGet( - apiPath.getReactionByIdInNewApi( - ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, + apiPath.getNewReaction( ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, + ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, ), ) - .reply(HttpStatusCode.Ok, bioEntityFixture); - - mockedAxiosOldClient - .onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id])) - .reply(HttpStatusCode.Ok, [reaction]); + .reply(HttpStatusCode.Ok, reaction); await handleReactionResults(store.dispatch, ELEMENT_SEARCH_RESULT_MOCK_REACTION, { searchDistance, @@ -277,9 +267,9 @@ describe('handleReactionResults - util', () => { })(ELEMENT_SEARCH_RESULT_MOCK_REACTION); const actions = store.getActions(); - const acionTypes = actions.map(action => action.type); + const actionTypes = actions.map(action => action.type); - expect(acionTypes).toStrictEqual([ + expect(actionTypes).toStrictEqual([ 'reactions/getByIds/pending', 'reactions/getByIds/fulfilled', 'drawer/openReactionDrawerById', @@ -291,9 +281,9 @@ describe('handleReactionResults - util', () => { }); }); describe('when matching reaction found', () => { - const point = { x: 1, y: 1 }; + const point = { x: reaction.line.segments[0].x1, y: reaction.line.segments[0].y1 }; const maxZoom = 10; - const zoom = 5; + const zoom = ZOOM_RESCALING_FACTOR * 5; it('should dispatch onSearch event with reaction data', async () => { const { store } = getReduxStoreWithActionsListener(); @@ -308,16 +298,12 @@ describe('handleReactionResults - util', () => { mockedAxiosNewClient .onGet( - apiPath.getReactionByIdInNewApi( - ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, + apiPath.getNewReaction( ELEMENT_SEARCH_RESULT_MOCK_REACTION.modelId, + ELEMENT_SEARCH_RESULT_MOCK_REACTION.id, ), ) - .reply(HttpStatusCode.Ok, bioEntityFixture); - - mockedAxiosOldClient - .onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id])) - .reply(HttpStatusCode.Ok, [reaction]); + .reply(HttpStatusCode.Ok, reaction); await handleReactionResults(store.dispatch, ELEMENT_SEARCH_RESULT_MOCK_REACTION, { searchDistance, @@ -328,7 +314,7 @@ describe('handleReactionResults - util', () => { })(ELEMENT_SEARCH_RESULT_MOCK_REACTION); expect(PluginsEventBus.dispatchEvent).toHaveBeenCalledWith('onSearch', { - results: [[{ bioEntity: bioEntityFixture, perfect: true }]], + results: [[{ bioEntity: reaction, perfect: true }]], searchValues: [ELEMENT_SEARCH_RESULT_MOCK_REACTION], type: 'reaction', }); diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts index 012e646f83554ac91ae0e24af833c5c405d5d547..963fc428dadfd5066eb64c9cf20298c281657117 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts @@ -9,10 +9,10 @@ import { BioEntity, ElementSearchResult } from '@/types/models'; import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { apiPath } from '@/redux/apiPath'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; -import { bioEntitySchema } from '@/models/bioEntitySchema'; import { getMultiBioEntityByIds } from '@/redux/bioEntity/thunks/getMultiBioEntity'; +import { newReactionSchema } from '@/models/newReactionSchema'; +import getModelElementsIdsFromReaction from '@/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/getModelElementsIdsFromReaction'; import { handleReactionSearchClickFailure } from './handleReactionSearchClickFailure'; -import { getBioEntitiesIdsFromReaction } from './getBioEntitiesIdsFromReaction'; import { findClosestReactionPoint } from './findClosestReactionPoint'; type SearchConfig = { @@ -28,14 +28,14 @@ type SearchConfig = { export const handleReactionResults = (dispatch: AppDispatch, closestSearchResult: ElementSearchResult, searchConfig?: SearchConfig) => async ({ id, modelId }: ElementSearchResult): Promise<void> => { - const data = await dispatch(getReactionsByIds([id])); + const data = await dispatch(getReactionsByIds({ ids: [{ id, modelId }] })); const payload = data?.payload; if (!data || !payload || typeof payload === 'string' || payload.data.length === SIZE_OF_EMPTY_ARRAY) { return; } const reaction = payload.data[FIRST_ARRAY_ELEMENT]; - const bioEntitiesIds = getBioEntitiesIdsFromReaction(reaction); + const bioEntitiesIds = getModelElementsIdsFromReaction(reaction); if (searchConfig && searchConfig.searchDistance) { const { maxZoom, point, searchDistance, zoom, isResultDrawerOpen } = searchConfig; @@ -54,8 +54,8 @@ export const handleReactionResults = dispatch(selectTab('')); - const response = await axiosInstanceNewAPI.get<BioEntity>(apiPath.getReactionByIdInNewApi(reaction.id, reaction.modelId)); - const isDataValid = validateDataUsingZodSchema(response.data, bioEntitySchema); + const response = await axiosInstanceNewAPI.get<BioEntity>(apiPath.getNewReaction(reaction.model, reaction.id)); + const isDataValid = validateDataUsingZodSchema(response.data, newReactionSchema); if (isDataValid) { const reactionNewApi = response.data; @@ -64,7 +64,7 @@ export const handleReactionResults = getMultiBioEntityByIds({ elementsToFetch: bioEntitiesIds.map((bioEntityId) => { return { - elementId: parseInt(bioEntityId, 10), + elementId: bioEntityId, modelId, type: 'ALIAS' }; diff --git a/src/constants/map.ts b/src/constants/map.ts index ba730d08a28b991817343a63a787cb97ddd722e6..07775a8127ec329758241cb8f4d5ebad611d020b 100644 --- a/src/constants/map.ts +++ b/src/constants/map.ts @@ -12,6 +12,8 @@ export const DEFAULT_CENTER_Y = 0; export const LATLNG_FALLBACK: LatLng = [0, 0]; export const EXTENT_PADDING_MULTIPLICATOR = 1; +export const ZOOM_RESCALING_FACTOR = 3; + export const DEFAULT_CENTER_POINT: Point = { x: DEFAULT_CENTER_X, y: DEFAULT_CENTER_Y, diff --git a/src/models/bioEntitySchema.ts b/src/models/bioEntitySchema.ts index 83f7f0c7eab111cbeea3ee399eeb56d8ff87035e..650832a9d92d649acbd79f1df377a4d2f6ea7c2a 100644 --- a/src/models/bioEntitySchema.ts +++ b/src/models/bioEntitySchema.ts @@ -1,11 +1,11 @@ import { ZERO } from '@/constants/common'; import { z } from 'zod'; +import { reactionProduct } from '@/models/reactionProduct'; import { colorSchema } from './colorSchema'; import { glyphSchema } from './glyphSchema'; import { lineSchema } from './lineSchema'; import { modificationResiduesSchema } from './modificationResiduesSchema'; import { operatorSchema } from './operatorSchema'; -import { productsSchema } from './products'; import { referenceSchema } from './referenceSchema'; import { structuralStateSchema } from './structuralStateSchema'; import { submodelSchema } from './submodelSchema'; @@ -84,9 +84,9 @@ export const bioEntitySchema = z.object({ subsystem: z.optional(z.string()).nullable().optional(), geneProteinReaction: z.optional(z.string()).nullable().optional(), kinetics: z.optional(z.null()), - products: z.optional(z.array(productsSchema)), - reactants: z.optional(z.array(productsSchema)), - modifiers: z.optional(z.array(productsSchema)), + products: z.optional(z.array(reactionProduct)), + reactants: z.optional(z.array(reactionProduct)), + modifiers: z.optional(z.array(reactionProduct)), processCoordinates: z.optional(z.null()), line: z.optional(lineSchema), operators: z.optional(z.array(operatorSchema)), diff --git a/src/models/fixtures/reactionFixture.ts b/src/models/fixtures/referenceFixture.ts similarity index 58% rename from src/models/fixtures/reactionFixture.ts rename to src/models/fixtures/referenceFixture.ts index 703e8be58ae84570971fe358c02eec6d2b0ecac6..73614291eb041deead47467730f3a01ff0bf1749 100644 --- a/src/models/fixtures/reactionFixture.ts +++ b/src/models/fixtures/referenceFixture.ts @@ -1,10 +1,9 @@ import { ZOD_SEED } from '@/constants'; -import { z } from 'zod'; // eslint-disable-next-line import/no-extraneous-dependencies import { createFixture } from 'zod-fixture'; -import { reactionSchema } from '../reaction'; +import { referenceSchema } from '@/models/referenceSchema'; -export const reactionsFixture = createFixture(z.array(reactionSchema), { +export const referenceFixture = createFixture(referenceSchema, { seed: ZOD_SEED, array: { min: 2, max: 2 }, }); diff --git a/src/models/newReactionSchema.ts b/src/models/newReactionSchema.ts index 21ee1a44b28a021efa188dd4179c082de0692a32..b500ea333682e5e18b62e9934d9777b988b1b05e 100644 --- a/src/models/newReactionSchema.ts +++ b/src/models/newReactionSchema.ts @@ -5,7 +5,7 @@ import { referenceSchema } from '@/models/referenceSchema'; import { operatorSchema } from '@/models/operatorSchema'; export const newReactionSchema = z.object({ - id: z.number(), + id: z.number().int().positive(), abbreviation: z.string().nullable(), elementId: z.string(), formula: z.string().nullable(), diff --git a/src/models/products.ts b/src/models/products.ts deleted file mode 100644 index 56e205fe4b3cbc9c9746edd22f9cd266ab865f3d..0000000000000000000000000000000000000000 --- a/src/models/products.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { z } from 'zod'; - -export const productsSchema = z.union([ - z.object({ - aliasId: z.number(), - stoichiometry: z.number().nullable(), - type: z.optional(z.string()), - }), - z.object({ - element: z.number(), - stoichiometry: z.number().nullable(), - type: z.optional(z.string()), - }), -]); diff --git a/src/models/reaction.ts b/src/models/reaction.ts deleted file mode 100644 index 18a438f72386e9f790394dca2cf37114a9d24c29..0000000000000000000000000000000000000000 --- a/src/models/reaction.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from 'zod'; -import { positionSchema } from './positionSchema'; -import { productsSchema } from './products'; -import { reactionLineSchema } from './reactionLineSchema'; -import { referenceSchema } from './referenceSchema'; - -export const reactionSchema = z.object({ - centerPoint: positionSchema, - hierarchyVisibilityLevel: z.string().nullable(), - id: z.number(), - kineticLaw: z.null(), - lines: z.array(reactionLineSchema), - modelId: z.number(), - modifiers: z.array(productsSchema), - name: z.string(), - notes: z.string(), - products: z.array(productsSchema), - reactants: z.array(productsSchema), - reactionId: z.string(), - references: z.array(referenceSchema), - type: z.string(), -}); diff --git a/src/models/reactionLineSchema.ts b/src/models/reactionLineSchema.ts deleted file mode 100644 index 4be9fa0c959b2fbe78bb38bc3d53a829db7d4eda..0000000000000000000000000000000000000000 --- a/src/models/reactionLineSchema.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { z } from 'zod'; -import { positionSchema } from './positionSchema'; - -export const reactionLineSchema = z.object({ - start: positionSchema, - end: positionSchema, - type: z.string(), -}); diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts index 784ec8427b0058270d8fc5cd3ac533e812fe680a..5346ef4d1bb1aa5ae919214414dfc9cdd22b2891 100644 --- a/src/redux/apiPath.ts +++ b/src/redux/apiPath.ts @@ -22,11 +22,7 @@ const getPublicationsURLSearchParams = ( export const apiPath = { getElementById: (elementId: number, modelId: number): string => `projects/${PROJECT_ID}/models/${modelId}/bioEntities/elements/${elementId}`, - getReactionByIdInNewApi: (reactionId: number, modelId: number): string => - `projects/${PROJECT_ID}/models/${modelId}/bioEntities/reactions/${reactionId}`, - getReactionById: (reactionId: number, modelId: number): string => - `projects/${PROJECT_ID}/models/${modelId}/bioEntities/reactions/?id=${reactionId}`, getBioEntityContentsStringWithQuery: ({ searchQuery, isPerfectMatch, @@ -66,6 +62,8 @@ export const apiPath = { `projects/${PROJECT_ID}/glyphs/${glyphId}/fileContent`, getNewReactions: (modelId: number): string => `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/reactions/?size=2000`, + getNewReaction: (modelId: number, reactionId: number): string => + `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/reactions/${reactionId}`, getChemicalsStringWithQuery: (searchQuery: string): string => `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`, getAllOverlaysByProjectIdQuery: ( diff --git a/src/redux/bioEntity/bioEntity.selectors.ts b/src/redux/bioEntity/bioEntity.selectors.ts index e5b70204d4717e7271abf7add212bc70664611a6..5ad5c7668ee5a09f21207d172d600f6116c929a7 100644 --- a/src/redux/bioEntity/bioEntity.selectors.ts +++ b/src/redux/bioEntity/bioEntity.selectors.ts @@ -340,7 +340,7 @@ export const currentDrawerReactionCommentsSelector = createSelector( return comments.filter( comment => comment.type === 'REACTION' && - comment.modelId === reaction.modelId && + comment.modelId === reaction.model && Number(comment.elementId) === reaction.id, ); } diff --git a/src/redux/bioEntity/thunks/getMultiBioEntity.ts b/src/redux/bioEntity/thunks/getMultiBioEntity.ts index dad84c23999040857bbf4cae0cce37a6a6c923bb..2b84e65ab0a67b57e4e9843150a806812d0cfa88 100644 --- a/src/redux/bioEntity/thunks/getMultiBioEntity.ts +++ b/src/redux/bioEntity/thunks/getMultiBioEntity.ts @@ -44,8 +44,11 @@ export const getMultiBioEntity = createAsyncThunk< dispatch: dispatch as AppDispatch, getState: getState as typeof store.getState, }); + const bioEntitiesStringIds = bioEntitiesIds.map(id => String(id)); if (bioEntitiesIds.length > ZERO) { - await dispatch(getMultiBioEntity({ searchQueries: bioEntitiesIds, isPerfectMatch: true })); + await dispatch( + getMultiBioEntity({ searchQueries: bioEntitiesStringIds, isPerfectMatch: true }), + ); } return bioEntityContents; @@ -90,7 +93,8 @@ export const getMultiBioEntityByIds = createAsyncThunk< getState: getState as typeof store.getState, }); if (bioEntitiesIds.length > ZERO) { - await dispatch(getMultiBioEntity({ searchQueries: bioEntitiesIds, isPerfectMatch: true })); + const searchQueries = bioEntitiesIds.map(id => String(id)); + await dispatch(getMultiBioEntity({ searchQueries, isPerfectMatch: true })); } return bioEntities; diff --git a/src/redux/bioEntity/thunks/utils/fetchReactionsAndGetBioEntitiesIds.ts b/src/redux/bioEntity/thunks/utils/fetchReactionsAndGetBioEntitiesIds.ts index 0b8af2061effb0bd7976361c1f3bcb0a6a5fde59..cae4025d558e37db7152454cedd14d1a4358dfe6 100644 --- a/src/redux/bioEntity/thunks/utils/fetchReactionsAndGetBioEntitiesIds.ts +++ b/src/redux/bioEntity/thunks/utils/fetchReactionsAndGetBioEntitiesIds.ts @@ -1,11 +1,11 @@ -import { getBioEntitiesIdsFromReaction } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/getBioEntitiesIdsFromReaction'; import { FIRST_ARRAY_ELEMENT, SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { openReactionDrawerById, selectTab } from '@/redux/drawer/drawer.slice'; import { openMapAndOrSetActiveIfSelected } from '@/redux/map/map.slice'; import { modelsNameMapSelector } from '@/redux/models/models.selectors'; import { getReactionsByIds } from '@/redux/reactions/reactions.thunks'; import type { AppDispatch, store } from '@/redux/store'; -import type { BioEntityContent, Reaction } from '@/types/models'; +import type { BioEntityContent, NewReaction } from '@/types/models'; +import getModelElementsIdsFromReaction from '@/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/getModelElementsIdsFromReaction'; interface Args { bioEntityContents: BioEntityContent[]; @@ -13,17 +13,30 @@ interface Args { getState: typeof store.getState; } -const getReactionsIdsFromBioEntities = (bioEntites: BioEntityContent[]): number[] => { +type ReactionId = { + id: number; + modelId: number; +}; + +const getReactionsIdsFromBioEntities = (bioEntites: BioEntityContent[]): ReactionId[] => { return bioEntites .filter(c => c?.bioEntity?.idReaction) - .map(c => c?.bioEntity?.id) - .filter((id): id is number => typeof id === 'number'); + .filter(c => typeof c?.bioEntity?.id === 'number') + .map(c => { + let id: number; + if (typeof c.bioEntity.id === 'string') { + id = parseInt(c.bioEntity.id, 10); + } else { + id = c.bioEntity.id; + } + return { id, modelId: c.bioEntity.model }; + }); }; const fetchReactions = async ( - reactionsIds: number[], + reactionsIds: ReactionId[], dispatch: AppDispatch, -): Promise<Reaction[]> => { +): Promise<NewReaction[]> => { const result = await dispatch( getReactionsByIds({ ids: reactionsIds, @@ -31,7 +44,7 @@ const fetchReactions = async ( }), ); - // if has error (toast show should be handled by getReactionsByIds) + // if it has error (toast show should be handled by getReactionsByIds) if (typeof result.payload === 'string') { return []; } @@ -46,7 +59,7 @@ const fetchReactions = async ( const handleReactionShowInfoAndOpenMap = async ( { dispatch, getState }: Args, - firstReaction: Reaction, + firstReaction: NewReaction, ): Promise<void> => { const modelsNames = modelsNameMapSelector(getState()); @@ -54,13 +67,13 @@ const handleReactionShowInfoAndOpenMap = async ( dispatch(selectTab('')); dispatch( openMapAndOrSetActiveIfSelected({ - modelId: firstReaction.modelId, - modelName: modelsNames[firstReaction.modelId], + modelId: firstReaction.model, + modelName: modelsNames[firstReaction.model], }), ); }; -export const fetchReactionsAndGetBioEntitiesIds = async (args: Args): Promise<string[]> => { +export const fetchReactionsAndGetBioEntitiesIds = async (args: Args): Promise<number[]> => { const { dispatch, bioEntityContents } = args; const bioEntityReactionsIds = getReactionsIdsFromBioEntities(bioEntityContents || []); @@ -73,7 +86,9 @@ export const fetchReactionsAndGetBioEntitiesIds = async (args: Args): Promise<st return []; } - const bioEntitiesIds = reactions.map(reaction => getBioEntitiesIdsFromReaction(reaction)).flat(); + const bioEntitiesIds = reactions + .map(reaction => getModelElementsIdsFromReaction(reaction)) + .flat(); const firstReaction = reactions[FIRST_ARRAY_ELEMENT]; if (firstReaction) { handleReactionShowInfoAndOpenMap(args, firstReaction); diff --git a/src/redux/comment/comment.types.ts b/src/redux/comment/comment.types.ts index 8cb0e4b59872fc53a0ddeb147b506628abf4f1af..1e727562c842f4e3835c43f3ff18df3110a305fe 100644 --- a/src/redux/comment/comment.types.ts +++ b/src/redux/comment/comment.types.ts @@ -1,12 +1,12 @@ import { FetchDataState } from '@/types/fetchDataState'; -import { BioEntity, Comment, Reaction } from '@/types/models'; +import { BioEntity, Comment, NewReaction } from '@/types/models'; import { PayloadAction } from '@reduxjs/toolkit'; import { Point } from '@/types/map'; export interface CommentsState extends FetchDataState<Comment[], []> { isOpen: boolean; commentElement: BioEntity | null; - commentReaction: Reaction | null; + commentReaction: NewReaction | null; } export type OpenCommentByIdPayload = number | string; diff --git a/src/redux/comment/thunks/getComments.ts b/src/redux/comment/thunks/getComments.ts index 05156ede750d4f74c8f43fce95d44ef4690f13f5..abf2d96e12ed7abbc4200a121bdb84ddf68f30c6 100644 --- a/src/redux/comment/thunks/getComments.ts +++ b/src/redux/comment/thunks/getComments.ts @@ -4,13 +4,12 @@ import { axiosInstance, axiosInstanceNewAPI } from '@/services/api/utils/axiosIn import { ThunkConfig } from '@/types/store'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; -import { BioEntity, Comment, Reaction } from '@/types/models'; +import { BioEntity, Comment, NewReaction } from '@/types/models'; import { z } from 'zod'; import { bioEntitySchema } from '@/models/bioEntitySchema'; import { GetElementProps } from '@/redux/comment/comment.types'; -import { reactionSchema } from '@/models/reaction'; -import { ZERO } from '@/constants/common'; import { getError } from '@/utils/error-report/getError'; +import { newReactionSchema } from '@/models/newReactionSchema'; export const getComments = createAsyncThunk<Comment[], void, ThunkConfig>( 'project/getComments', @@ -44,19 +43,20 @@ export const getCommentElement = createAsyncThunk<BioEntity | null, GetElementPr }, ); -export const getCommentReaction = createAsyncThunk<Reaction | null, GetElementProps, ThunkConfig>( - 'project/getCommentReaction', - async ({ elementId, modelId }) => { - try { - const response = await axiosInstance.get<Reaction[]>( - apiPath.getReactionById(elementId, modelId), - ); - - const isDataValid = validateDataUsingZodSchema(response.data, z.array(reactionSchema)); - - return isDataValid && response.data.length > ZERO ? response.data[ZERO] : null; - } catch (error) { - return Promise.reject(getError({ error })); - } - }, -); +export const getCommentReaction = createAsyncThunk< + NewReaction | null, + GetElementProps, + ThunkConfig +>('project/getCommentReaction', async ({ elementId, modelId }) => { + try { + const response = await axiosInstanceNewAPI.get<NewReaction>( + apiPath.getNewReaction(modelId, elementId), + ); + + const isDataValid = validateDataUsingZodSchema(response.data, z.array(newReactionSchema)); + + return isDataValid ? response.data : null; + } catch (error) { + return Promise.reject(getError({ error })); + } +}); diff --git a/src/redux/reactions/reactions.reducers.ts b/src/redux/reactions/reactions.reducers.ts index 848b4ab797b6f385871d262030d994b46832727b..58550371366a379da65f23a4d01634398df03f03 100644 --- a/src/redux/reactions/reactions.reducers.ts +++ b/src/redux/reactions/reactions.reducers.ts @@ -1,5 +1,5 @@ import { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit'; -import { Reaction } from '@/types/models'; +import { NewReaction } from '@/types/models'; import { REACTIONS_INITIAL_STATE } from './reactions.constants'; import { getReactionsByIds } from './reactions.thunks'; import { ReactionsState } from './reactions.types'; @@ -30,7 +30,7 @@ export const resetReactionsDataReducer = (state: ReactionsState): void => { export const setReactionsReducer = ( state: ReactionsState, - action: PayloadAction<Array<Reaction>>, + action: PayloadAction<Array<NewReaction>>, ): void => { state.data = action.payload; state.loading = 'succeeded'; diff --git a/src/redux/reactions/reactions.selector.ts b/src/redux/reactions/reactions.selector.ts index 14590fdaf229a209cbf1b052f4f76be6a5e5c85b..93a62fed147438094dd78026fef176a071edcede 100644 --- a/src/redux/reactions/reactions.selector.ts +++ b/src/redux/reactions/reactions.selector.ts @@ -1,4 +1,4 @@ -import { Reaction } from '@/types/models'; +import { NewReaction } from '@/types/models'; import { createSelector } from '@reduxjs/toolkit'; import { commentReactionSelector } from '@/redux/comment/comment.selectors'; import { currentDrawerReactionIdSelector } from '../drawer/drawer.selectors'; @@ -17,8 +17,8 @@ export const reactionsDataSelector = createSelector( export const allReactionsSelectorOfCurrentMap = createSelector( reactionsDataSelector, currentModelIdSelector, - (reactions, currentModelId): Reaction[] => { - return reactions.filter(({ modelId }) => modelId === currentModelId); + (reactions, currentModelId): NewReaction[] => { + return reactions.filter(({ model }) => model === currentModelId); }, ); diff --git a/src/redux/reactions/reactions.thunks.test.ts b/src/redux/reactions/reactions.thunks.test.ts index d570a341bf204a66eaf062403f6698da0908b654..baa96235b5dc526640fccf1abea4bee7190116b3 100644 --- a/src/redux/reactions/reactions.thunks.test.ts +++ b/src/redux/reactions/reactions.thunks.test.ts @@ -1,17 +1,20 @@ /* eslint-disable no-magic-numbers */ -import { reactionsFixture } from '@/models/fixtures/reactionFixture'; import { ToolkitStoreWithSingleSlice, createStoreInstanceUsingSliceReducer, } from '@/utils/createStoreInstanceUsingSliceReducer'; -import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; +import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { newReactionFixture } from '@/models/fixtures/newReactionFixture'; import { apiPath } from '../apiPath'; import reactionsReducer from './reactions.slice'; import { getReactionsByIds } from './reactions.thunks'; import { ReactionsState } from './reactions.types'; -const mockedAxiosClient = mockNetworkResponse(); +const mockedAxiosNewClient = mockNetworkNewAPIResponse(); + +const reactionId = 1; +const modelId = 2; describe('reactions thunks', () => { let store = {} as ToolkitStoreWithSingleSlice<ReactionsState>; @@ -21,29 +24,18 @@ describe('reactions thunks', () => { describe('getReactionsByIds', () => { it('should return data when data response from API is valid', async () => { - const ids = [1]; + const ids = { ids: [{ id: reactionId, modelId }] }; - mockedAxiosClient - .onGet(apiPath.getReactionsWithIds(ids)) - .reply(HttpStatusCode.Ok, reactionsFixture); + mockedAxiosNewClient + .onGet(apiPath.getNewReaction(modelId, reactionId)) + .reply(HttpStatusCode.Ok, newReactionFixture); const { payload } = await store.dispatch(getReactionsByIds(ids)); - expect(payload).toEqual({ data: reactionsFixture, shouldConcat: false }); + expect(payload).toEqual({ data: [newReactionFixture], shouldConcat: false }); }); - it('should return undefined when data response from API is not valid ', async () => { - mockedAxiosClient - .onGet(apiPath.getReactionsWithIds([])) - .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' }); - - const { payload } = await store.dispatch(getReactionsByIds([])); - expect(payload).toEqual(undefined); - }); - - it('should return empty array when data response from API is empty', async () => { - mockedAxiosClient.onGet(apiPath.getReactionsWithIds([100])).reply(HttpStatusCode.Ok, []); - - const { payload } = await store.dispatch(getReactionsByIds([100])); + it('should return empty array when data asking for empty reaction list', async () => { + const { payload } = await store.dispatch(getReactionsByIds({ ids: [] })); expect(payload).toEqual({ data: [], shouldConcat: false }); }); }); diff --git a/src/redux/reactions/reactions.thunks.ts b/src/redux/reactions/reactions.thunks.ts index af0b8c057f8af9724fba767b6ee04cc974f23f70..2ba747c02640de2100311ccf629f29af75d97aae 100644 --- a/src/redux/reactions/reactions.thunks.ts +++ b/src/redux/reactions/reactions.thunks.ts @@ -1,23 +1,26 @@ -import { reactionSchema } from '@/models/reaction'; +/* eslint-disable no-magic-numbers */ import { apiPath } from '@/redux/apiPath'; -import { axiosInstance } from '@/services/api/utils/axiosInstance'; -import { Reaction } from '@/types/models'; +import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; +import { NewReaction } from '@/types/models'; import { ThunkConfig } from '@/types/store'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; -import { z } from 'zod'; import { getError } from '@/utils/error-report/getError'; +import { newReactionSchema } from '@/models/newReactionSchema'; import { REACTIONS_FETCHING_ERROR_PREFIX } from './reactions.constants'; -type GetReactionsByIdsArgs = - | number[] - | { - ids: number[]; - shouldConcat?: boolean; - }; +type ReactionId = { + id: number; + modelId: number; +}; + +type GetReactionsByIdsArgs = { + ids: ReactionId[]; + shouldConcat?: boolean; +}; export type GetReactionsByIdsResult = { - data: Reaction[]; + data: NewReaction[]; shouldConcat: boolean; }; @@ -26,22 +29,30 @@ export const getReactionsByIds = createAsyncThunk< GetReactionsByIdsArgs, ThunkConfig >('reactions/getByIds', async args => { - const ids = args instanceof Array ? args : args.ids; - const shouldConcat = args instanceof Array ? false : args.shouldConcat || false; + const { ids } = args; + const shouldConcat = args.shouldConcat || false; try { - const response = await axiosInstance.get<Reaction[]>(apiPath.getReactionsWithIds(ids)); - - const isDataValid = validateDataUsingZodSchema(response.data, z.array(reactionSchema)); + const reactionPromises = ids.map(reactionId => + axiosInstanceNewAPI.get<NewReaction>( + apiPath.getNewReaction(reactionId.modelId, reactionId.id), + ), + ); + const reactionResponses = await Promise.all(reactionPromises); - if (!isDataValid) { - return undefined; - } - - return { - data: response.data, + const result: GetReactionsByIdsResult = { + data: [], shouldConcat, }; + for (let i = 0; i < reactionResponses.length; i += 1) { + const response = reactionResponses[i]; + const isDataValid = validateDataUsingZodSchema(response.data, newReactionSchema); + if (isDataValid) { + result.data.push(response.data); + } + } + + return result; } catch (error) { return Promise.reject(getError({ error, prefix: REACTIONS_FETCHING_ERROR_PREFIX })); } diff --git a/src/redux/reactions/reactions.types.ts b/src/redux/reactions/reactions.types.ts index 02bd81cd1a77eb600ea5accac1317868c60ef7e2..58a34aaea02da82395f4928407edd42ddeccf1e6 100644 --- a/src/redux/reactions/reactions.types.ts +++ b/src/redux/reactions/reactions.types.ts @@ -1,4 +1,4 @@ import { FetchDataState } from '@/types/fetchDataState'; -import { Reaction } from '@/types/models'; +import { NewReaction } from '@/types/models'; -export type ReactionsState = FetchDataState<Reaction[]>; +export type ReactionsState = FetchDataState<NewReaction[]>; diff --git a/src/redux/reactions/utils/getFilteredReferences.test.ts b/src/redux/reactions/utils/getFilteredReferences.test.ts index b02b2cc3dc556abcfc96ad22e59e6563751dfef7..a194ab9e23e5d93bf3c1cd163047009d8686c7f3 100644 --- a/src/redux/reactions/utils/getFilteredReferences.test.ts +++ b/src/redux/reactions/utils/getFilteredReferences.test.ts @@ -2,12 +2,12 @@ import { REFERENCES_MOCK_ALL_INVALID, REFERENCES_MOCK_ALL_VALID, } from '@/models/mocks/referencesMock'; -import { Reaction } from '@/types/models'; import { ReferenceFiltered } from '@/types/reference'; +import { NewReaction } from '@/types/models'; import { getReferencesWithoutEmptyLink } from './getFilteredReferences'; describe('getFilteredReferences - subUtil', () => { - const cases: [Pick<Reaction, 'references'>, ReferenceFiltered[]][] = [ + const cases: [Pick<NewReaction, 'references'>, ReferenceFiltered[]][] = [ [ { references: REFERENCES_MOCK_ALL_VALID, diff --git a/src/redux/reactions/utils/getFilteredReferences.ts b/src/redux/reactions/utils/getFilteredReferences.ts index 987eb1a0637a0b901dcb1a8ea13df76196190ef2..bdd85d952e3faabdbd2ee8b4bc6ded0d3ac976e2 100644 --- a/src/redux/reactions/utils/getFilteredReferences.ts +++ b/src/redux/reactions/utils/getFilteredReferences.ts @@ -1,7 +1,7 @@ -import { Reaction } from '@/types/models'; +import { NewReaction } from '@/types/models'; import { ReferenceFiltered } from '@/types/reference'; -type InputReaction = Pick<Reaction, 'references'>; +type InputReaction = Pick<NewReaction, 'references'>; export const getReferencesWithoutEmptyLink = ( reaction: InputReaction | undefined, diff --git a/src/services/pluginsManager/pluginContextMenu/pluginsContextMenu.types.ts b/src/services/pluginsManager/pluginContextMenu/pluginsContextMenu.types.ts index 0a54586b39e154501e2118311e0abbfeef7c1dcf..0ea6b81167c6eb565b6097aac0fef7c49008edc0 100644 --- a/src/services/pluginsManager/pluginContextMenu/pluginsContextMenu.types.ts +++ b/src/services/pluginsManager/pluginContextMenu/pluginsContextMenu.types.ts @@ -1,4 +1,4 @@ -import { BioEntity, Reaction } from '@/types/models'; +import { BioEntity, NewReaction } from '@/types/models'; export type ClickCoordinates = { modelId: number; @@ -13,7 +13,7 @@ export type PluginContextMenuItemType = { name: string; style: string; enabled: boolean; - callback: (coordinates: ClickCoordinates, element: BioEntity | Reaction | undefined) => void; + callback: (coordinates: ClickCoordinates, element: BioEntity | NewReaction | undefined) => void; }; export type PluginsContextMenuType = { @@ -23,7 +23,7 @@ export type PluginsContextMenuType = { name: string, style: string, enabled: boolean, - callback: (coordinates: ClickCoordinates, element: BioEntity | Reaction | undefined) => void, + callback: (coordinates: ClickCoordinates, element: BioEntity | NewReaction | undefined) => void, ) => string; removeMenu: (hash: string, id: string) => void; updateMenu: (hash: string, id: string, name: string, style: string, enabled: boolean) => void; diff --git a/src/types/models.ts b/src/types/models.ts index 35d90c37ca5d2b5f8bce51eb29a7afdb46f6e703..4821833d59a22bbbf9941454cd5830deaf618735 100644 --- a/src/types/models.ts +++ b/src/types/models.ts @@ -50,8 +50,6 @@ import { pluginSchema } from '@/models/pluginSchema'; import { projectSchema } from '@/models/projectSchema'; import { publicationsResponseSchema } from '@/models/publicationsResponseSchema'; import { publicationSchema } from '@/models/publicationsSchema'; -import { reactionSchema } from '@/models/reaction'; -import { reactionLineSchema } from '@/models/reactionLineSchema'; import { referenceSchema } from '@/models/referenceSchema'; import { sessionSchemaValid } from '@/models/sessionValidSchema'; import { statisticsSchema } from '@/models/statisticsSchema'; @@ -84,6 +82,7 @@ import { newReactionSchema } from '@/models/newReactionSchema'; import { reactionProduct } from '@/models/reactionProduct'; import { operatorSchema } from '@/models/operatorSchema'; import { modificationResiduesSchema } from '@/models/modificationResiduesSchema'; +import { segmentSchema } from '@/models/segmentSchema'; export type Project = z.infer<typeof projectSchema>; export type OverviewImageView = z.infer<typeof overviewImageView>; @@ -119,17 +118,16 @@ export type BioEntity = z.infer<typeof bioEntitySchema>; export type BioEntityContent = z.infer<typeof bioEntityContentSchema>; export type BioEntityResponse = z.infer<typeof bioEntityResponseSchema>; export type Chemical = z.infer<typeof chemicalSchema>; -export type Reaction = z.infer<typeof reactionSchema>; export type NewReaction = z.infer<typeof newReactionSchema>; const newReactionsSchema = pageableSchema(newReactionSchema); export type NewReactions = z.infer<typeof newReactionsSchema>; export type Operator = z.infer<typeof operatorSchema>; export type ReactionProduct = z.infer<typeof reactionProduct>; export type Reference = z.infer<typeof referenceSchema>; -export type ReactionLine = z.infer<typeof reactionLineSchema>; export type ElementSearchResult = z.infer<typeof elementSearchResult>; export type ElementSearchResultType = z.infer<typeof elementSearchResultType>; export type SessionValid = z.infer<typeof sessionSchemaValid>; +export type Segment = z.infer<typeof segmentSchema>; export type Login = z.infer<typeof loginSchema>; export type ConfigurationOption = z.infer<typeof configurationOptionSchema>; export type Configuration = z.infer<typeof configurationSchema>; diff --git a/src/utils/reaction/mapNewReactionToReaction.test.ts b/src/utils/reaction/mapNewReactionToReaction.test.ts deleted file mode 100644 index 23097b83e3ec53126b3216e239a4ae9a8946e463..0000000000000000000000000000000000000000 --- a/src/utils/reaction/mapNewReactionToReaction.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import mapNewReactionToReaction from '@/utils/reaction/mapNewReactionToReaction'; - -describe('mapNewReactionToReaction', () => { - const newReaction = { - id: 31141, - notes: '', - idReaction: 're22', - name: '', - reversible: false, - symbol: null, - abbreviation: null, - formula: null, - mechanicalConfidenceScore: null, - lowerBound: null, - upperBound: null, - subsystem: null, - geneProteinReaction: null, - visibilityLevel: '', - z: 45, - synonyms: [], - model: 137, - kinetics: null, - line: { - id: 109668, - width: 1, - color: { - alpha: 255, - rgb: -16777216, - }, - z: 0, - segments: [ - { - x1: 149.31765717927775, - y1: 319.13724818355684, - x2: 142.5553586937381, - y2: 314.86275181644316, - }, - ], - startArrow: { - arrowType: 'NONE', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - endArrow: { - arrowType: 'NONE', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - lineType: 'SOLID', - }, - processCoordinates: null, - modifiers: [], - products: [ - { - id: 85169, - line: { - id: 109670, - width: 1, - color: { - alpha: 255, - rgb: -16777216, - }, - z: 0, - segments: [ - { - x1: 142.5553586937381, - y1: 314.86275181644316, - x2: 122.2063492063492, - y2: 302, - }, - ], - startArrow: { - arrowType: 'NONE', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - endArrow: { - arrowType: 'OPEN', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - lineType: 'SOLID', - }, - stoichiometry: null, - element: 58886, - }, - ], - reactants: [ - { - id: 85168, - line: { - id: 109669, - width: 1, - color: { - alpha: 255, - rgb: -16777216, - }, - z: 0, - segments: [ - { - x1: 169.66666666666666, - y1: 332, - x2: 149.31765717927775, - y2: 319.13724818355684, - }, - ], - startArrow: { - arrowType: 'NONE', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - endArrow: { - arrowType: 'NONE', - angle: 2.748893571891069, - lineType: 'SOLID', - length: 15, - }, - lineType: 'SOLID', - }, - stoichiometry: null, - element: 58872, - }, - ], - operators: [], - elementId: 're22', - references: [], - sboTerm: 'SBO:0000171', - }; - const expectedReaction = { - centerPoint: { - x: 0, - y: 0, - }, - hierarchyVisibilityLevel: '', - id: 31141, - kineticLaw: null, - lines: [ - { - start: { - x: 149.31765717927775, - y: 319.13724818355684, - }, - end: { - x: 142.5553586937381, - y: 314.86275181644316, - }, - type: '', - }, - { - start: { - x: 142.5553586937381, - y: 314.86275181644316, - }, - end: { - x: 122.2063492063492, - y: 302, - }, - type: '', - }, - { - start: { - x: 169.66666666666666, - y: 332, - }, - end: { - x: 149.31765717927775, - y: 319.13724818355684, - }, - type: '', - }, - ], - modelId: 137, - modifiers: [], - name: '', - notes: '', - products: [], - reactants: [], - reactionId: 're22', - references: [], - type: 'Positive influence', - }; - - it('should return correct reaction object from new reaction object', () => { - const result = mapNewReactionToReaction(newReaction); - expect(result).toEqual(expectedReaction); - }); -}); diff --git a/src/utils/reaction/mapNewReactionToReaction.ts b/src/utils/reaction/mapNewReactionToReaction.ts deleted file mode 100644 index a5fbd0e4f144430e5adbeeece3230e3baa352e59..0000000000000000000000000000000000000000 --- a/src/utils/reaction/mapNewReactionToReaction.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import { NewReaction, Reaction, ReactionLine } from '@/types/models'; -import ReactionTypeEnum from '@/utils/reaction/ReactionTypeEnum'; - -type ReactionTypeKey = keyof typeof ReactionTypeEnum; - -export default function mapNewReactionToReaction(newReaction: NewReaction): Reaction { - const lines: Array<ReactionLine> = []; - let start; - let end; - newReaction.line.segments.forEach(segment => { - start = { x: segment.x1, y: segment.y1 }; - end = { x: segment.x2, y: segment.y2 }; - lines.push({ start, end, type: '' }); - }); - [ - ...newReaction.products, - ...newReaction.reactants, - ...newReaction.modifiers, - ...newReaction.operators, - ].forEach(element => { - element.line.segments.forEach(segment => { - start = { x: segment.x1, y: segment.y1 }; - end = { x: segment.x2, y: segment.y2 }; - lines.push({ start, end, type: '' }); - }); - }); - - return { - centerPoint: { x: 0, y: 0 }, - hierarchyVisibilityLevel: '', - id: newReaction.id, - kineticLaw: null, - lines, - modelId: newReaction.model, - modifiers: [], - name: '', - notes: newReaction.notes, - products: [], - reactants: [], - reactionId: newReaction.idReaction, - references: newReaction.references, - type: ReactionTypeEnum[newReaction.sboTerm as ReactionTypeKey], - }; -}