diff --git a/src/routerModule/util.ts b/src/routerModule/util.ts index 6f93f0da7e29c9fe31a3534a5c92df5bbc9ac3b4..cbe862d6c28572a60acca844e0de4df162e19aa2 100644 --- a/src/routerModule/util.ts +++ b/src/routerModule/util.ts @@ -3,7 +3,7 @@ import { encodeNumber, decodeToNumber, separator } from './cipher' import { UrlSegment, UrlTree } from "@angular/router" import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants" import { mixNgLayers } from "src/services/state/ngViewerState.store" -import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store' +import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.helper' import { viewerStateHelperStoreName } from "src/services/state/viewerState.store.helper" import { uiStatePreviewingDatasetFilesSelector } from "src/services/state/uiState/selectors" import { Component } from "@angular/core" diff --git a/src/services/effect/pluginUseEffect.spec.ts b/src/services/effect/pluginUseEffect.spec.ts index 693f8235226e9e2cca2514742404ff4b8d16a714..e89947a19bd3544260c890b369c69ab92ad7d732 100644 --- a/src/services/effect/pluginUseEffect.spec.ts +++ b/src/services/effect/pluginUseEffect.spec.ts @@ -6,13 +6,13 @@ import { Action } from "@ngrx/store"; import { provideMockActions } from "@ngrx/effects/testing"; import { provideMockStore } from "@ngrx/store/testing"; import { defaultRootState } from "../stateStore.service"; -import { PLUGINSTORE_CONSTANTS } from '../state/pluginState.store' -import { PLUGINSTORE_ACTION_TYPES } from '../state/pluginState.helper' +import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES } from '../state/pluginState.helper' import { Injectable } from "@angular/core"; import { getRandomHex } from 'common/util' import { PluginServices } from "src/plugin"; import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"; import { hot } from "jasmine-marbles"; +import { BS_ENDPOINT } from "src/util/constants"; const actions$: Observable<Action> = of({type: 'TEST'}) @@ -103,6 +103,10 @@ describe('pluginUseEffect.ts', () => { { provide: PluginServices, useClass: MockPluginService + }, + { + provide: BS_ENDPOINT, + useValue: `http://localhost:1234` } ] }).compileComponents() diff --git a/src/services/effect/pluginUseEffect.ts b/src/services/effect/pluginUseEffect.ts index e9263e7b7c5f6cbad29cda9c8ac50a0d1f8edbf3..383347f18ce032bc43ff468fffd44bc4f3fead40 100644 --- a/src/services/effect/pluginUseEffect.ts +++ b/src/services/effect/pluginUseEffect.ts @@ -5,9 +5,7 @@ import { Observable, forkJoin } from "rxjs" import { filter, map, startWith, switchMap } from "rxjs/operators" import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service" import { PluginServices } from "src/plugin/atlasViewer.pluginService.service" -import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store' -import { PLUGINSTORE_ACTION_TYPES, pluginStateSelectorInitManifests } from 'src/services/state/pluginState.helper' -import { IavRootStoreInterface } from "../stateStore.service" +import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES, pluginStateSelectorInitManifests } from 'src/services/state/pluginState.helper' import { HttpClient } from "@angular/common/http" @Injectable({ @@ -20,7 +18,7 @@ export class PluginServiceUseEffect { public initManifests$: Observable<any> constructor( - store$: Store<IavRootStoreInterface>, + store$: Store<any>, constantService: AtlasViewerConstantsServices, pluginService: PluginServices, http: HttpClient diff --git a/src/services/state/pluginState.helper.ts b/src/services/state/pluginState.helper.ts index d620e3f40b148f06c33b08fed673392cdf9f124a..e1c48c1941ab716f1bacc6c8980f62c2367a933a 100644 --- a/src/services/state/pluginState.helper.ts +++ b/src/services/state/pluginState.helper.ts @@ -8,4 +8,8 @@ export const PLUGINSTORE_ACTION_TYPES = { export const pluginStateSelectorInitManifests = createSelector( state => state['pluginState'], pluginState => pluginState.initManifests -) \ No newline at end of file +) + +export const PLUGINSTORE_CONSTANTS = { + INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC', +} diff --git a/src/services/state/pluginState.store.ts b/src/services/state/pluginState.store.ts index e71b49dc076b66bd375dbaddd2ede233d6e90fce..85bfa20a915ec8ab2e11497321cd66d95edabb91 100644 --- a/src/services/state/pluginState.store.ts +++ b/src/services/state/pluginState.store.ts @@ -1,6 +1,6 @@ import { Action } from '@ngrx/store' import { generalApplyState } from '../stateStore.helper' -import { PLUGINSTORE_ACTION_TYPES } from './pluginState.helper' +import { PLUGINSTORE_ACTION_TYPES, PLUGINSTORE_CONSTANTS } from './pluginState.helper' export const defaultState: StateInterface = { initManifests: [] } @@ -17,10 +17,6 @@ export interface ActionInterface extends Action { } -export const PLUGINSTORE_CONSTANTS = { - INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC', -} - export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface): StateInterface => { switch (action.type) { case PLUGINSTORE_ACTION_TYPES.SET_INIT_PLUGIN: { diff --git a/src/state/effects/viewerState.useEffect.spec.ts b/src/state/effects/viewerState.useEffect.spec.ts index 05adfe3a67c49d203c5d70cb10786dfc2407e698..88a52f438a64986a1bd3c6b9c673fde7fb3defc5 100644 --- a/src/state/effects/viewerState.useEffect.spec.ts +++ b/src/state/effects/viewerState.useEffect.spec.ts @@ -418,7 +418,8 @@ describe('> viewerState.useEffect.ts', () => { ]) mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{ ['@id']: 'foo-bar', - templateSpaces: [ mockTmplSpc ] + templateSpaces: [ mockTmplSpc ], + parcellations: [ mockParc0 ] }]) actions$ = hot('a', { a: viewerStateSelectAtlas({ @@ -450,7 +451,8 @@ describe('> viewerState.useEffect.ts', () => { ]) mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{ ['@id']: 'foo-bar', - templateSpaces: [ mockTmplSpc1 ] + templateSpaces: [ mockTmplSpc1 ], + parcellations: [ mockParc1 ] }]) actions$ = hot('a', { a: viewerStateSelectAtlas({ @@ -494,7 +496,8 @@ describe('> viewerState.useEffect.ts', () => { ]) mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{ ['@id']: 'foo-bar', - templateSpaces: [ mockTmplSpc, mockTmplSpc1 ] + templateSpaces: [ mockTmplSpc, mockTmplSpc1 ], + parcellations: [ mockParc0, mockParc1 ] }]) }) it('> will select template.@id', () => { diff --git a/src/state/effects/viewerState.useEffect.ts b/src/state/effects/viewerState.useEffect.ts index 9f20aa8ae2c02545cd244cf69e91739ecb92c01d..30bcb2d5f212b5203b546135595ac97b50b703bd 100644 --- a/src/state/effects/viewerState.useEffect.ts +++ b/src/state/effects/viewerState.useEffect.ts @@ -116,8 +116,15 @@ export class ViewerStateControllerUseEffect implements OnDestroy { ) || atlas.templateSpaces[0] const templateSpaceId = templateTobeSelected['@id'] - const atlasTmpl = atlas.templateSpaces.find(t => t['@id'] === templateSpaceId) + + const templateSelected = fetchedTemplates.find(t => templateSpaceId === t['@id']) + if (!templateSelected) { + return generalActionError({ + message: CONST.TEMPLATE_NOT_FOUND + }) + } + const atlasParcs = atlasTmpl.availableIn .map(availP => atlas.parcellations.find(p => availP['@id'] === p['@id'])) .filter(fullP => !!fullP) @@ -127,15 +134,8 @@ export class ViewerStateControllerUseEffect implements OnDestroy { return !p['@version']['@next'] } return true - }) + }) || templateSelected.parcellations[0] const parcellationId = atlasParc && atlasParc['@id'] - - const templateSelected = fetchedTemplates.find(t => templateSpaceId === t['@id']) - if (!templateSelected) { - return generalActionError({ - message: CONST.TEMPLATE_NOT_FOUND - }) - } const parcellationSelected = parcellationId && templateSelected.parcellations.find(p => p['@id'] === parcellationId) return viewerStateNewViewer({ selectTemplate: templateSelected, diff --git a/src/util/pureConstant.service.spec.ts b/src/util/pureConstant.service.spec.ts index 68302ae81cd99b1d239390d4a2b9aa6b2639f538..2ca6fb127878caace8d1fdf0d5049a65bd2cc2af 100644 --- a/src/util/pureConstant.service.spec.ts +++ b/src/util/pureConstant.service.spec.ts @@ -2,9 +2,13 @@ import { HttpClientTestingModule, HttpTestingController } from "@angular/common/ import { TestBed } from "@angular/core/testing" import { MockStore, provideMockStore } from "@ngrx/store/testing" import { hot } from "jasmine-marbles" +import { BS_ENDPOINT } from "src/atlasComponents/regionalFeatures/bsFeatures" import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service" import { viewerStateFetchedAtlasesSelector, viewerStateFetchedTemplatesSelector } from "src/services/state/viewerState/selectors" import { PureContantService } from "./pureConstant.service" +import { TAtlas } from "./siibraApiConstants/types" + +const MOCK_BS_ENDPOINT = `http://localhost:1234` describe('> pureConstant.service.ts', () => { describe('> PureContantService', () => { @@ -21,6 +25,10 @@ describe('> pureConstant.service.ts', () => { useValue: { worker: null } + }, + { + provide: BS_ENDPOINT, + useValue: MOCK_BS_ENDPOINT } ] }) @@ -37,26 +45,33 @@ describe('> pureConstant.service.ts', () => { it('> can be init', () => { const service = TestBed.inject(PureContantService) - const exp = httpController.expectOne(`${service.backendUrl}/atlases/`) + const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`) exp.flush([]) expect(service).toBeTruthy() }) describe('> allFetchingReady$', () => { - + const mockAtlas: TAtlas = { + id: 'mockatlas id', + name: 'mockatlas name', + links: { + parcellations: { + href: `${MOCK_BS_ENDPOINT}/mockatlas-parcellation-href` + }, + spaces: { + href: `${MOCK_BS_ENDPOINT}/atlas-spaces` + } + } + } it('> can be init, and configuration emits allFetchingReady$', () => { const service = TestBed.inject(PureContantService) - const exp = httpController.expectOne(`${service.backendUrl}/atlases/`) - exp.flush([]) + const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`) + exp.flush([mockAtlas]) service.allFetchingReady$.subscribe() - const expT = httpController.expectOne(`${service.backendUrl}templates`) - expT.flush([]) - expect( - service.allFetchingReady$ - ).toBeObservable( - hot('a', { - a: true, - }) - ) + + const expT1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/spaces`) + expT1.flush([]) + const expP1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/parcellations`) + expP1.flush([]) }) }) diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index b6900afe1a075251f680289607d351e81544399a..7ca778e726063db12662eae5793056813d88dc74 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -164,6 +164,8 @@ export class PureContantService implements OnDestroy{ ) } + private httpCallCache = new Map<string, Observable<any>>() + private getParcDetail(atlasId: string, parcId: string) { return this.http.get<TParc>( `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}`, @@ -186,10 +188,15 @@ export class PureContantService implements OnDestroy{ } private getSpacesAndParc(atlasId: string) { + const cacheKey = `getSpacesAndParc::${atlasId}` + if (this.httpCallCache.has(cacheKey)) return this.httpCallCache.get(cacheKey) + const spaces$ = this.getSpaces(atlasId).pipe( - switchMap(spaces => forkJoin( - spaces.map(space => this.getSpaceDetail(atlasId, parseId(space.id))) - )) + switchMap(spaces => spaces.length > 0 + ? forkJoin( + spaces.map(space => this.getSpaceDetail(atlasId, parseId(space.id))) + ) + : of([])) ) const parcs$ = this.getParcs(atlasId).pipe( // need not to get full parc data. first level gets all data @@ -197,14 +204,17 @@ export class PureContantService implements OnDestroy{ // parcs.map(parc => this.getParcDetail(atlasId, parseId(parc.id))) // )) ) - return forkJoin([ + const returnObs = forkJoin([ spaces$, parcs$, ]).pipe( map(([ templateSpaces, parcellations ]) => { return { templateSpaces, parcellations } - }) + }), + shareReplay(1) ) + this.httpCallCache.set(cacheKey, returnObs) + return returnObs } constructor(