From 3de1cf6c6d618fe398fef834015b54530911704c Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Mon, 1 Aug 2022 12:12:28 +0200 Subject: [PATCH] bugfix: comprehensive migration of BSEndPt to obs --- .../sapi/core/sapiParcellation.ts | 57 +++++++----- src/atlasComponents/sapi/core/sapiRegion.ts | 89 +++++++++++-------- src/atlasComponents/sapi/core/sapiSpace.ts | 51 +++++++---- .../sapi/features/sapiFeature.ts | 9 +- src/atlasComponents/sapi/module.ts | 9 +- src/atlasComponents/sapi/sapi.service.spec.ts | 82 +++++++++-------- src/atlasComponents/sapi/sapi.service.ts | 81 ++++++++++------- src/atlasComponents/sapi/stories.base.ts | 48 ++++++---- .../parcellationVersion.pipe.spec.ts | 5 +- .../layerCtrl.service/layerCtrl.effects.ts | 13 +-- 10 files changed, 267 insertions(+), 177 deletions(-) diff --git a/src/atlasComponents/sapi/core/sapiParcellation.ts b/src/atlasComponents/sapi/core/sapiParcellation.ts index 4767cc289..085c5a82b 100644 --- a/src/atlasComponents/sapi/core/sapiParcellation.ts +++ b/src/atlasComponents/sapi/core/sapiParcellation.ts @@ -1,4 +1,5 @@ import { Observable } from "rxjs" +import { switchMap } from "rxjs/operators" import { SapiVolumeModel } from ".." import { SAPI } from "../sapi.service" import {SapiParcellationFeatureModel, SapiParcellationModel, SapiQueryPriorityArg, SapiRegionModel} from "../type" @@ -20,43 +21,53 @@ export class SAPIParcellation{ } getDetail(queryParam?: SapiQueryPriorityArg): Observable<SapiParcellationModel>{ - return this.sapi.httpGet<SapiParcellationModel>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}`, - null, - queryParam + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.httpGet<SapiParcellationModel>( + `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}`, + null, + queryParam + )) ) } getRegions(spaceId: string, queryParam?: SapiQueryPriorityArg): Observable<SapiRegionModel[]> { - return this.sapi.httpGet<SapiRegionModel[]>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/regions`, - { - space_id: spaceId - }, - queryParam + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.httpGet<SapiRegionModel[]>( + `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/regions`, + { + space_id: spaceId + }, + queryParam + )) ) } getVolumes(): Observable<SapiVolumeModel[]>{ - return this.sapi.httpGet<SapiVolumeModel[]>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/volumes` - ) + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.httpGet<SapiVolumeModel[]>( + `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/volumes` + )) + ) } getFeatures(parcPagination?: ParcellationPaginationQuery, queryParam?: SapiQueryPriorityArg): Observable<SapiParcellationFeatureModel[]> { - return this.sapi.httpGet<SapiParcellationFeatureModel[]>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features`, - { - type: parcPagination?.type, - size: parcPagination?.size?.toString() || '5', - page: parcPagination?.page.toString() || '0', - }, - queryParam + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.httpGet<SapiParcellationFeatureModel[]>( + `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features`, + { + type: parcPagination?.type, + size: parcPagination?.size?.toString() || '5', + page: parcPagination?.page.toString() || '0', + }, + queryParam + )) ) } getFeatureInstance(instanceId: string): Observable<SapiParcellationFeatureModel> { - return this.sapi.http.get<SapiParcellationFeatureModel>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features/${encodeURIComponent(instanceId)}`, + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.http.get<SapiParcellationFeatureModel>( + `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features/${encodeURIComponent(instanceId)}`, + )) ) } } diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts index d7b82c7d9..d9263cea0 100644 --- a/src/atlasComponents/sapi/core/sapiRegion.ts +++ b/src/atlasComponents/sapi/core/sapiRegion.ts @@ -2,7 +2,7 @@ import { SAPI } from ".."; import { SapiRegionalFeatureModel, SapiRegionMapInfoModel, SapiRegionModel, cleanIeegSessionDatasets, SapiIeegSessionModel, CleanedIeegDataset, SapiVolumeModel, PaginatedResponse } from "../type"; import { strToRgb, hexToRgb } from 'common/util' import { merge, Observable, of } from "rxjs"; -import { catchError, map, scan } from "rxjs/operators"; +import { catchError, map, scan, switchMap } from "rxjs/operators"; export class SAPIRegion{ @@ -16,7 +16,7 @@ export class SAPIRegion{ return strToRgb(JSON.stringify(region)) } - private prefix: string + private prefix$: Observable<string> constructor( private sapi: SAPI, @@ -24,20 +24,26 @@ export class SAPIRegion{ public parcId: string, public id: string, ){ - this.prefix = `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcId)}/regions/${encodeURIComponent(this.id)}` + this.prefix$ = SAPI.BsEndpoint$.pipe( + map(endpt => `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcId)}/regions/${encodeURIComponent(this.id)}`) + ) } getFeatures(spaceId: string): Observable<(SapiRegionalFeatureModel | CleanedIeegDataset)[]> { return merge( - this.sapi.httpGet<SapiRegionalFeatureModel[]>( - `${this.prefix}/features`, - { - space_id: spaceId - } - ).pipe( - catchError((err, obs) => { - return of([]) - }) + this.prefix$.pipe( + switchMap(prefix => + this.sapi.httpGet<SapiRegionalFeatureModel[]>( + `${prefix}/features`, + { + space_id: spaceId + } + ).pipe( + catchError((err, obs) => { + return of([]) + }) + ) + ) ), spaceId ? this.sapi.getSpace(this.atlasId, spaceId).getFeatures({ parcellationId: this.parcId, region: this.id }).pipe( @@ -56,50 +62,59 @@ export class SAPIRegion{ } getFeatureInstance(instanceId: string, spaceId: string = null): Observable<SapiRegionalFeatureModel> { - return this.sapi.httpGet<SapiRegionalFeatureModel>( - `${this.prefix}/features/${encodeURIComponent(instanceId)}`, - { - space_id: spaceId - } + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiRegionalFeatureModel>( + `${prefix}/features/${encodeURIComponent(instanceId)}`, + { + space_id: spaceId + } + )) ) } getMapInfo(spaceId: string): Observable<SapiRegionMapInfoModel> { - return this.sapi.http.get<SapiRegionMapInfoModel>( - `${this.prefix}/regional_map/info`, - { - params: { - space_id: spaceId + return this.prefix$.pipe( + switchMap(prefix => this.sapi.http.get<SapiRegionMapInfoModel>( + `${prefix}/regional_map/info`, + { + params: { + space_id: spaceId + } } - } + )) ) } - getMapUrl(spaceId: string): string { - return `${this.prefix}/regional_map/map?space_id=${encodeURI(spaceId)}` + getMapUrl(spaceId: string): Observable<string> { + return this.prefix$.pipe( + map(prefix => `${prefix}/regional_map/map?space_id=${encodeURI(spaceId)}`) + ) } getVolumes(): Observable<PaginatedResponse<SapiVolumeModel>>{ - const url = `${this.prefix}/volumes` - return this.sapi.httpGet<PaginatedResponse<SapiVolumeModel>>( - url + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<PaginatedResponse<SapiVolumeModel>>( + `${prefix}/volumes` + )) ) } getVolumeInstance(volumeId: string): Observable<SapiVolumeModel> { - const url = `${this.prefix}/volumes/${encodeURIComponent(volumeId)}` - return this.sapi.httpGet<SapiVolumeModel>( - url + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiVolumeModel>( + `${prefix}/volumes/${encodeURIComponent(volumeId)}` + )) ) } getDetail(spaceId: string): Observable<SapiRegionModel> { - const url = `${this.prefix}` - return this.sapi.httpGet<SapiRegionModel>( - url, - { - space_id: spaceId - } + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiRegionModel>( + prefix, + { + space_id: spaceId + } + )) ) } } diff --git a/src/atlasComponents/sapi/core/sapiSpace.ts b/src/atlasComponents/sapi/core/sapiSpace.ts index 3effd6f16..5f61ae6f6 100644 --- a/src/atlasComponents/sapi/core/sapiSpace.ts +++ b/src/atlasComponents/sapi/core/sapiSpace.ts @@ -2,6 +2,7 @@ import { Observable } from "rxjs" import { SAPI } from '../sapi.service' import { camelToSnake } from 'common/util' import {SapiQueryPriorityArg, SapiSpaceModel, SapiSpatialFeatureModel, SapiVolumeModel} from "../type" +import { map, switchMap } from "rxjs/operators" type FeatureResponse = { features: { @@ -22,13 +23,21 @@ type SpatialFeatureOpts = RegionalSpatialFeatureOpts | BBoxSpatialFEatureOpts export class SAPISpace{ - constructor(private sapi: SAPI, public atlasId: string, public id: string){} + constructor(private sapi: SAPI, public atlasId: string, public id: string){ + this.prefix$ = SAPI.BsEndpoint$.pipe( + map(endpt => `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}`) + ) + } + + private prefix$: Observable<string> getModalities(param?: SapiQueryPriorityArg): Observable<FeatureResponse> { - return this.sapi.httpGet<FeatureResponse>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features`, - null, - param + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<FeatureResponse>( + `${prefix}/features`, + null, + param + )) ) } @@ -37,9 +46,11 @@ export class SAPISpace{ for (const [key, value] of Object.entries(opts)) { query[camelToSnake(key)] = value } - return this.sapi.httpGet<SapiSpatialFeatureModel[]>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features`, - query + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiSpatialFeatureModel[]>( + `${prefix}/features`, + query + )) ) } @@ -48,23 +59,29 @@ export class SAPISpace{ for (const [key, value] of Object.entries(opts)) { query[camelToSnake(key)] = value } - return this.sapi.httpGet<SapiSpatialFeatureModel>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features/${encodeURIComponent(instanceId)}`, - query + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiSpatialFeatureModel>( + `${prefix}/features/${encodeURIComponent(instanceId)}`, + query + )) ) } getDetail(param?: SapiQueryPriorityArg): Observable<SapiSpaceModel>{ - return this.sapi.httpGet<SapiSpaceModel>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}`, - null, - param + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiSpaceModel>( + `${prefix}`, + null, + param + )) ) } getVolumes(): Observable<SapiVolumeModel[]>{ - return this.sapi.httpGet<SapiVolumeModel[]>( - `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/volumes`, + return this.prefix$.pipe( + switchMap(prefix => this.sapi.httpGet<SapiVolumeModel[]>( + `${prefix}/volumes`, + )) ) } } diff --git a/src/atlasComponents/sapi/features/sapiFeature.ts b/src/atlasComponents/sapi/features/sapiFeature.ts index 8290da0ca..f2f341acc 100644 --- a/src/atlasComponents/sapi/features/sapiFeature.ts +++ b/src/atlasComponents/sapi/features/sapiFeature.ts @@ -1,3 +1,4 @@ +import { switchMap } from "rxjs/operators"; import { SAPI } from "../sapi.service"; import { SapiFeatureModel } from "../type"; @@ -6,8 +7,10 @@ export class SAPIFeature { } - public detail$ = this.sapi.httpGet<SapiFeatureModel>( - `${SAPI.BsEndpoint}/features/${this.id}`, - this.opts + public detail$ = SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.sapi.httpGet<SapiFeatureModel>( + `${endpt}/features/${this.id}`, + this.opts + )) ) } diff --git a/src/atlasComponents/sapi/module.ts b/src/atlasComponents/sapi/module.ts index 6d0757bb5..9b9efaf0e 100644 --- a/src/atlasComponents/sapi/module.ts +++ b/src/atlasComponents/sapi/module.ts @@ -1,5 +1,4 @@ -import { APP_INITIALIZER, NgModule } from "@angular/core"; -import { SAPI } from "./sapi.service"; +import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http"; import { PriorityHttpInterceptor } from "src/util/priority"; @@ -16,16 +15,10 @@ import { MatSnackBarModule } from "@angular/material/snack-bar"; exports: [ ], providers: [ - SAPI, { provide: HTTP_INTERCEPTORS, useClass: PriorityHttpInterceptor, multi: true - }, - { - provide: APP_INITIALIZER, - useValue: () => SAPI.SetBsEndPoint(), - multi: true } ] }) diff --git a/src/atlasComponents/sapi/sapi.service.spec.ts b/src/atlasComponents/sapi/sapi.service.spec.ts index 0448de0cc..20233eca6 100644 --- a/src/atlasComponents/sapi/sapi.service.spec.ts +++ b/src/atlasComponents/sapi/sapi.service.spec.ts @@ -1,10 +1,10 @@ -import { NEVER } from "rxjs" +import { finalize } from "rxjs/operators" import * as env from "src/environments/environment" import { SAPI } from "./sapi.service" describe("> sapi.service.ts", () => { describe("> SAPI", () => { - describe("#SetBsEndPoint", () => { + describe("#BsEndpoint$", () => { let fetchSpy: jasmine.Spy let environmentSpy: jasmine.Spy @@ -14,16 +14,10 @@ describe("> sapi.service.ts", () => { const atlas1 = 'foo' const atlas2 = 'bar' - let originalBsEndpoint: string - beforeAll(() => { - originalBsEndpoint = SAPI.BsEndpoint - }) + let subscribedVal: string - afterAll(() => { - SAPI.BsEndpoint = originalBsEndpoint - }) - beforeEach(() => { + SAPI.ClearBsEndPoint() fetchSpy = spyOn(window, 'fetch') fetchSpy.and.callThrough() @@ -33,43 +27,70 @@ describe("> sapi.service.ts", () => { }) }) + afterEach(() => { + SAPI.ClearBsEndPoint() fetchSpy.calls.reset() environmentSpy.calls.reset() + subscribedVal = null }) describe("> first passes", () => { - beforeEach(() => { + beforeEach(done => { const resp = new Response(JSON.stringify([atlas1]), { headers: { 'content-type': 'application/json' }, status: 200 }) - fetchSpy.and.resolveTo(resp) + fetchSpy.and.callFake(async url => { + if (url === `${endpt1}/atlases`) { + return resp + } + throw new Error("controlled throw") + }) + SAPI.BsEndpoint$.pipe( + finalize(() => done()) + ).subscribe(val => { + subscribedVal = val + }) }) - it("> should call fetch once", async () => { - await SAPI.SetBsEndPoint() - expect(fetchSpy).toHaveBeenCalledTimes(1) - expect(fetchSpy).toHaveBeenCalledOnceWith(`${endpt1}/atlases`) + it("> should call fetch twice", async () => { + expect(fetchSpy).toHaveBeenCalledTimes(2) + + const allArgs = fetchSpy.calls.allArgs() + expect(allArgs.length).toEqual(2) + expect(allArgs[0]).toEqual([`${endpt1}/atlases`]) + expect(allArgs[1]).toEqual([`${endpt2}/atlases`]) }) it("> endpoint should be set", async () => { - await SAPI.SetBsEndPoint() - expect(SAPI.BsEndpoint).toBe(endpt1) + expect(subscribedVal).toBe(endpt1) + }) + + it("> additional calls should return cached observable", () => { + + expect(fetchSpy).toHaveBeenCalledTimes(2) + SAPI.BsEndpoint$.subscribe() + SAPI.BsEndpoint$.subscribe() + + expect(fetchSpy).toHaveBeenCalledTimes(2) }) }) describe("> first fails", () => { - beforeEach(() => { - let counter = 0 - fetchSpy.and.callFake(async () => { - if (counter === 0) { - counter ++ + beforeEach(done => { + fetchSpy.and.callFake(async url => { + if (url === `${endpt1}/atlases`) { throw new Error(`bla`) } const resp = new Response(JSON.stringify([atlas1]), { headers: { 'content-type': 'application/json' }, status: 200 }) return resp }) + + SAPI.BsEndpoint$.pipe( + finalize(() => done()) + ).subscribe(val => { + subscribedVal = val + }) }) it("> should call twice", async () => { - await SAPI.SetBsEndPoint() expect(fetchSpy).toHaveBeenCalledTimes(2) expect(fetchSpy.calls.allArgs()).toEqual([ [`${endpt1}/atlases`], @@ -78,18 +99,7 @@ describe("> sapi.service.ts", () => { }) it('> should set endpt2', async () => { - await SAPI.SetBsEndPoint() - expect(SAPI.BsEndpoint).toBe(endpt2) - }) - - it("> instances bsendpoint should be the updated version", async () => { - await SAPI.SetBsEndPoint() - const mockHttpClient = { - get: jasmine.createSpy() - } - mockHttpClient.get.and.returnValue(NEVER) - const sapi = new SAPI(mockHttpClient as any, null, null) - expect(sapi.bsEndpoint).toBe(endpt2) + expect(subscribedVal).toBe(endpt2) }) }) }) diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts index b2b4cb332..ef3dc46e1 100644 --- a/src/atlasComponents/sapi/sapi.service.ts +++ b/src/atlasComponents/sapi/sapi.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import { HttpClient } from '@angular/common/http'; -import { filter, map, shareReplay, switchMap, take } from "rxjs/operators"; +import { catchError, filter, map, shareReplay, switchMap, take, tap } from "rxjs/operators"; import { SAPIAtlas, SAPISpace } from './core' import { SapiAtlasModel, @@ -20,7 +20,7 @@ import { MatSnackBar } from "@angular/material/snack-bar"; import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; import { EnumColorMapName } from "src/util/colorMaps"; import { PRIORITY_HEADER } from "src/util/priority"; -import { interval, Observable } from "rxjs"; +import { concat, EMPTY, from, merge, Observable, of } from "rxjs"; import { SAPIFeature } from "./features"; import { environment } from "src/environments/environment" @@ -29,34 +29,56 @@ export const SIIBRA_API_VERSION = '0.2.2' type RegistryType = SAPIAtlas | SAPISpace | SAPIParcellation -@Injectable() +let BS_ENDPOINT_CACHED_VALUE: Observable<string> = null + +@Injectable({ + providedIn: 'root' +}) export class SAPI{ - static async SetBsEndPoint() { - let idx = 0 - const siibraApiEndpts = environment.SIIBRA_API_ENDPOINTS.split(',') - while (idx < siibraApiEndpts.length) { - const url = siibraApiEndpts[idx] - try { - const resp = await fetch(`${url}/atlases`) - const atlases = await resp.json() - if (atlases.length > 0) { - SAPI.BsEndpoint = url - return - } - } catch (e) { - idx ++ - } - } - SAPI.ErrorMessage = `It appears all of our mirrors are not working. The viewer may not be working properly...` + /** + * Used to clear BsEndPoint, so the next static get BsEndpoints$ will + * fetch again. Only used for unit test of BsEndpoint$ + */ + static ClearBsEndPoint(){ + BS_ENDPOINT_CACHED_VALUE = null } - static ErrorMessage = null - static BsEndpoint = null - - get bsEndpoint() { - return SAPI.BsEndpoint + /** + * BsEndpoint$ is designed as a static getter mainly for unit testing purposes. + * see usage of BsEndpoint$ and ClearBsEndPoint in sapi.service.spec.ts + */ + static get BsEndpoint$(): Observable<string> { + if (!!BS_ENDPOINT_CACHED_VALUE) return BS_ENDPOINT_CACHED_VALUE + BS_ENDPOINT_CACHED_VALUE = concat( + merge( + ...environment.SIIBRA_API_ENDPOINTS.split(',').map(url => { + return from((async () => { + const resp = await fetch(`${url}/atlases`) + const atlases = await resp.json() + if (atlases.length == 0) { + throw new Error(`atlas length == 0`) + } + return url + })()).pipe( + catchError(() => EMPTY) + ) + }) + ), + of(null).pipe( + tap(() => { + SAPI.ErrorMessage = `It appears all of our mirrors are not working. The viewer may not be working properly...` + }), + filter(() => false) + ) + ).pipe( + take(1), + shareReplay(1), + ) + return BS_ENDPOINT_CACHED_VALUE } + + static ErrorMessage = null registry = { _map: {} as Record<string, { @@ -116,7 +138,9 @@ export class SAPI{ } getModalities(): Observable<SapiModalityModel[]> { - return this.http.get<SapiModalityModel[]>(`${SAPI.BsEndpoint}/modalities`) + return SAPI.BsEndpoint$.pipe( + switchMap(endpt => this.http.get<SapiModalityModel[]>(`${endpt}/modalities`)) + ) } httpGet<T>(url: string, params?: Record<string, string>, sapiParam?: SapiQueryPriorityArg){ @@ -133,10 +157,7 @@ export class SAPI{ ) } - public atlases$ = interval(160).pipe( - map(() => this.bsEndpoint), - filter(v => !!v), - take(1), + public atlases$ = SAPI.BsEndpoint$.pipe( switchMap(endpt => this.http.get<SapiAtlasModel[]>( `${endpt}/atlases`, { diff --git a/src/atlasComponents/sapi/stories.base.ts b/src/atlasComponents/sapi/stories.base.ts index 1fa58bef8..57e28f20d 100644 --- a/src/atlasComponents/sapi/stories.base.ts +++ b/src/atlasComponents/sapi/stories.base.ts @@ -67,22 +67,27 @@ export const parcId = { } export async function getAtlases(): Promise<SapiAtlasModel[]> { - return await (await fetch(`${SAPI.BsEndpoint}/atlases`)).json() as SapiAtlasModel[] + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases`)).json() as SapiAtlasModel[] } export async function getAtlas(id: string): Promise<SapiAtlasModel>{ - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${id}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${id}`)).json() } export async function getParc(atlasId: string, id: string): Promise<SapiParcellationModel>{ - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/parcellations/${id}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId}/parcellations/${id}`)).json() } export async function getParcRegions(atlasId: string, id: string, spaceId: string): Promise<SapiRegionModel[]>{ - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/parcellations/${id}/regions?space_id=${encodeURIComponent(spaceId)}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId}/parcellations/${id}/regions?space_id=${encodeURIComponent(spaceId)}`)).json() } export async function getSpace(atlasId: string, id: string): Promise<SapiSpaceModel> { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId}/spaces/${id}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId}/spaces/${id}`)).json() } export async function getHumanAtlas(): Promise<SapiAtlasModel> { @@ -90,7 +95,8 @@ export async function getHumanAtlas(): Promise<SapiAtlasModel> { } export async function getMni152(): Promise<SapiSpaceModel> { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}`)).json() } export async function getJba29(): Promise<SapiParcellationModel> { @@ -103,33 +109,41 @@ export async function getJba29Regions(): Promise<SapiRegionModel[]> { export async function getHoc1Right(spaceId=null): Promise<SapiRegionModel> { if (!spaceId) { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right`)).json() } - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right?space_id=${encodeURIComponent(spaceId)}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right?space_id=${encodeURIComponent(spaceId)}`)).json() } export async function get44Left(spaceId=null): Promise<SapiRegionModel> { if (!spaceId) { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left`)).json() } - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left?space_id=${encodeURIComponent(spaceId)}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left?space_id=${encodeURIComponent(spaceId)}`)).json() } export async function getHoc1RightSpatialFeatures(): Promise<SxplrCleanedFeatureModel[]> { - const json: SapiSpatialFeatureModel[] = await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features?parcellation_id=2.9®ion=hoc1%20right`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + const json: SapiSpatialFeatureModel[] = await (await fetch(`${endPt}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features?parcellation_id=2.9®ion=hoc1%20right`)).json() return cleanIeegSessionDatasets(json.filter(it => it['@type'] === "siibra/features/ieegSession")) } export async function getHoc1RightFeatures(): Promise<SapiRegionalFeatureModel[]> { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features`)).json() } export async function getHoc1RightFeatureDetail(featId: string): Promise<SapiRegionalFeatureModel>{ - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features/${encodeURIComponent(featId)}`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features/${encodeURIComponent(featId)}`)).json() } export async function getJba29Features(): Promise<SapiParcellationFeatureModel[]> { - return await (await fetch(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/features`)).json() + const endPt = await SAPI.BsEndpoint$.toPromise() + return await (await fetch(`${endPt}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/features`)).json() } export async function getBigbrainSpatialFeatures(): Promise<SapiSpatialFeatureModel[]>{ @@ -137,14 +151,16 @@ export async function getBigbrainSpatialFeatures(): Promise<SapiSpatialFeatureMo [-1000, -1000, -1000], [1000, 1000, 1000] ] - const url = new URL(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.bigbrain}/features`) + const endPt = await SAPI.BsEndpoint$.toPromise() + const url = new URL(`${endPt}/atlases/${atlasId.human}/spaces/${spaceId.human.bigbrain}/features`) url.searchParams.set(`bbox`, JSON.stringify(bbox)) return await (await fetch(url.toString())).json() } export async function getMni152SpatialFeatureHoc1Right(): Promise<SapiSpatialFeatureModel[]>{ - const url = new URL(`${SAPI.BsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features`) + const endPt = await SAPI.BsEndpoint$.toPromise() + const url = new URL(`${endPt}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features`) url.searchParams.set(`parcellation_id`, parcId.human.jba29) url.searchParams.set("region", 'hoc1 right') return await (await fetch(url.toString())).json() diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts index 972e41179..ed2edbf53 100644 --- a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts +++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts @@ -3,11 +3,12 @@ import { SAPI } from "src/atlasComponents/sapi/sapi.service" import { SapiParcellationModel } from "src/atlasComponents/sapi/type" import { getTraverseFunctions } from "./parcellationVersion.pipe" -describe(`parcellationVersion.pipe.ts (endpoint at ${SAPI.BsEndpoint})`, () => { +describe(`parcellationVersion.pipe.ts`, () => { describe("getTraverseFunctions", () => { let julichBrainParcellations: SapiParcellationModel[] = [] beforeAll(async () => { - const res = await fetch(`${SAPI.BsEndpoint}/atlases/${encodeURIComponent(IDS.ATLAES.HUMAN)}/parcellations`) + const bsEndPoint = await SAPI.BsEndpoint$.toPromise() + const res = await fetch(`${bsEndPoint}/atlases/${encodeURIComponent(IDS.ATLAES.HUMAN)}/parcellations`) const arr: SapiParcellationModel[] = await res.json() julichBrainParcellations = arr.filter(it => /Julich-Brain Cytoarchitectonic Maps/.test(it.name)) }) diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts index 579f7fb79..6886634b6 100644 --- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts +++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts @@ -37,17 +37,20 @@ export class LayerCtrlEffects { ), switchMap(([ regions, { atlas, parcellation, template } ]) => { const sapiRegion = this.sapi.getRegion(atlas["@id"], parcellation["@id"], regions[0].name) - return sapiRegion.getMapInfo(template["@id"]).pipe( - map(val => + return forkJoin([ + sapiRegion.getMapInfo(template["@id"]), + sapiRegion.getMapUrl(template["@id"]) + ]).pipe( + map(([mapInfo, mapUrl]) => atlasAppearance.actions.addCustomLayer({ customLayer: { clType: "customlayer/nglayer", id: PMAP_LAYER_NAME, - source: `nifti://${sapiRegion.getMapUrl(template["@id"])}`, + source: `nifti://${mapUrl}`, shader: getShader({ colormap: EnumColorMapName.VIRIDIS, - highThreshold: val.max, - lowThreshold: val.min, + highThreshold: mapInfo.max, + lowThreshold: mapInfo.min, removeBg: true, }) } -- GitLab