diff --git a/.helm/adhoc/configmap-siibra-explorer.yml b/.helm/adhoc/configmap-siibra-explorer.yml index 37fbb9fc0d6753f5a560085faeae13c8f0c96f3e..4bc6d90b16e6421ffe24e3e19c5b5e37c76ee29d 100644 --- a/.helm/adhoc/configmap-siibra-explorer.yml +++ b/.helm/adhoc/configmap-siibra-explorer.yml @@ -7,6 +7,8 @@ data: REDIS_ADDR: "cache-redis-service" V2_7_PLUGIN_URLS: "https://siibra-jugex.apps.tc.humanbrainproject.eu/viewer_plugin/manifest.json;https://ngpy.apps.hbp.eu/viewer_plugin/manifest.json" LOGGER_DIR: "/sxplr-log" + OVERWRITE_API_ENDPOINT: https://siibra-api-prod.apps.tc.humanbrainproject.eu/v3_0 + OVERWRITE_SPATIAL_ENDPOINT: 'https://siibra-spatial-backend.apps.tc.humanbrainproject.eu' kind: ConfigMap metadata: diff --git a/.helm/adhoc/example-secret-siibra-explorer.yml b/.helm/adhoc/example-secret-siibra-explorer.yml index e4118f80194842f5a40ffc849eec748fd4ce2180..c1c93e8a5bc964fffb67d8152ebf327664a5d2ed 100644 --- a/.helm/adhoc/example-secret-siibra-explorer.yml +++ b/.helm/adhoc/example-secret-siibra-explorer.yml @@ -7,7 +7,6 @@ data: # n.b. echo -n "foobar" | base64 # or else the new line will also be encoded, and you will # wonder why your application does not work - OVERWRITE_API_ENDPOINT: Zm9vYmFy HBP_CLIENTID_V2: Zm9vYmFy HBP_CLIENTSECRET_V2: Zm9vYmFy SXPLR_EBRAINS_IAM_SA_CLIENT_ID: Zm9vYmFy diff --git a/backend/app/config.py b/backend/app/config.py index bc824fd52776656cb2cbaf480f918d091a342e51..43f49b315245a838efafca87ba3cac56d47c94fb 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -8,6 +8,8 @@ HOSTNAME = os.getenv("HOSTNAME", "http://localhost:3000") OVERWRITE_API_ENDPOINT = os.getenv("OVERWRITE_API_ENDPOINT") +OVERWRITE_SPATIAL_ENDPOINT = os.getenv("OVERWRITE_SPATIAL_ENDPOINT") + LOCAL_CDN = os.getenv("LOCAL_CDN") HBP_CLIENTID_V2 = os.getenv("HBP_CLIENTID_V2", "no hbp id") diff --git a/backend/app/const.py b/backend/app/const.py index 9948b8e477d84b697da868c485ae17b117680ccc..ca720149957d66ec98f8379492579c6d10afea5b 100644 --- a/backend/app/const.py +++ b/backend/app/const.py @@ -19,3 +19,5 @@ SCOPES = [ DATA_ERROR_ATTR = "data-error" OVERWRITE_SAPI_ENDPOINT_ATTR = "x-sapi-base-url" + +OVERWRITE_SPATIAL_BACKEND_ATTR = "x-spatial-backend-url" diff --git a/backend/app/index_html.py b/backend/app/index_html.py index 903ec154768b2875a28b67a511a40da9b97d3d86..ea4baf68fce8a5748ca262a04f907248bd3031ce 100644 --- a/backend/app/index_html.py +++ b/backend/app/index_html.py @@ -2,8 +2,8 @@ from fastapi import APIRouter, Request from pathlib import Path from fastapi.responses import Response from typing import Dict -from .const import ERROR_KEY, DATA_ERROR_ATTR, OVERWRITE_SAPI_ENDPOINT_ATTR, COOKIE_KWARGS -from .config import PATH_TO_PUBLIC, OVERWRITE_API_ENDPOINT +from .const import ERROR_KEY, DATA_ERROR_ATTR, OVERWRITE_SAPI_ENDPOINT_ATTR, COOKIE_KWARGS, OVERWRITE_SPATIAL_BACKEND_ATTR +from .config import PATH_TO_PUBLIC, OVERWRITE_API_ENDPOINT, OVERWRITE_SPATIAL_ENDPOINT path_to_index = Path(PATH_TO_PUBLIC) / "index.html" index_html: str = None @@ -32,6 +32,8 @@ async def get_index_html(request: Request): if OVERWRITE_API_ENDPOINT: attributes_to_append[OVERWRITE_SAPI_ENDPOINT_ATTR] = OVERWRITE_API_ENDPOINT + if OVERWRITE_SPATIAL_ENDPOINT: + attributes_to_append[OVERWRITE_SPATIAL_BACKEND_ATTR] = OVERWRITE_SPATIAL_ENDPOINT attr_string = " ".join([f"{key}={_monkey_sanitize(value)}" for key, value in attributes_to_append.items()]) diff --git a/common/constants.js b/common/constants.js index fb5a018be88f017fb2b1767e07a7305288fb7b03..4ddf697531624b02c7cb8934dd2a9d2cb348859c 100644 --- a/common/constants.js +++ b/common/constants.js @@ -150,7 +150,8 @@ If you do not accept the Terms & Conditions you are not permitted to access or u AUXMESH_DESC: `Some templates contain auxiliary meshes, which compliment the appearance of the template in the perspective view.`, OVERWRITE_SAPI_ENDPOINT_ATTR: `x-sapi-base-url`, - DATA_ERROR_ATTR: `data-error` + OVERWRITE_SPATIAL_BACKEND_ATTR: `x-spatial-backend-url`, + DATA_ERROR_ATTR: `data-error`, } exports.QUICKTOUR_DESC ={ diff --git a/deploy_env.md b/deploy_env.md index 8c2b1a3d8623a6937f4ce8956eab4fe3e6daceda..00df9cfd338132989d45dd9521b3a153c4e3cc7f 100644 --- a/deploy_env.md +++ b/deploy_env.md @@ -9,6 +9,7 @@ | `V2_7_STAGING_PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''` | `BUILD_TEXT` | overlay text at bottom right of the viewer. set to `''` to hide. | | | `OVERWRITE_API_ENDPOINT` | overwrite build time siibra-api endpoint | +| `OVERWRITE_SPATIAL_ENDPOINT` | overwrite build time spatial transform endpoint | | | `PATH_TO_PUBLIC` | path to built frontend | `../dist/aot` | diff --git a/docs/releases/v2.14.5.md b/docs/releases/v2.14.5.md index da8180f83392263b1c09cbe4f9b43794e253e824..a1c15104ac8d92556681f6bf5be939a3f8f31151 100644 --- a/docs/releases/v2.14.5.md +++ b/docs/releases/v2.14.5.md @@ -18,3 +18,4 @@ - Removed dependency on connectivity-component - Removed reference to JSC OKD instance, as the instance is no longer available - Updated google-site-verification +- Allow inter space transform to be configured at runtime diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts index 30542d6fc0441e31a41fc7ac5b372208ede998d1..c9baedadf1fea46ba91d1354427e54fe39503f55 100644 --- a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts +++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts @@ -1,16 +1,25 @@ import { InterSpaceCoordXformSvc, VALID_TEMPLATE_SPACE_NAMES } from './interSpaceCoordXform.service' import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing' import { TestBed, fakeAsync, tick } from '@angular/core/testing' +import { GET_ATTR_TOKEN } from 'src/util/constants' describe('InterSpaceCoordXformSvc.service.spec.ts', () => { describe('InterSpaceCoordXformSvc', () => { + let attr: string = null + const defaultUrl = 'https://hbp-spatial-backend.apps.hbp.eu/v1/transform-points' beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ], providers: [ - InterSpaceCoordXformSvc + InterSpaceCoordXformSvc, + { + provide: GET_ATTR_TOKEN, + useFactory: () => { + return () => attr + } + } ] }) }) @@ -39,7 +48,7 @@ describe('InterSpaceCoordXformSvc.service.spec.ts', () => { ).subscribe((_ev) => { }) - const req = httpTestingController.expectOne(service['url']) + const req = httpTestingController.expectOne(defaultUrl) expect(req.request.method).toEqual('POST') expect( JSON.parse(req.request.body) @@ -67,7 +76,7 @@ describe('InterSpaceCoordXformSvc.service.spec.ts', () => { expect(status).toEqual('completed') expect(result).toEqual([1e6, 2e6, 3e6]) }) - const req = httpTestingController.expectOne(service['url']) + const req = httpTestingController.expectOne(defaultUrl) req.flush({ 'target_points':[ [1, 2, 3] @@ -87,7 +96,7 @@ describe('InterSpaceCoordXformSvc.service.spec.ts', () => { expect(status).toEqual('error') done() }) - const req = httpTestingController.expectOne(service['url']) + const req = httpTestingController.expectOne(defaultUrl) req.flush('intercepted', { status: 500, statusText: 'internal server error' }) }) @@ -105,10 +114,36 @@ describe('InterSpaceCoordXformSvc.service.spec.ts', () => { expect(status).toEqual('error') expect(statusText).toEqual(`Timeout after 3s`) }) - const req = httpTestingController.expectOne(service['url']) + const req = httpTestingController.expectOne(defaultUrl) tick(4000) expect(req.cancelled).toBe(true) })) + + describe("if injected override endpoint", () => { + beforeEach(() => { + attr = "http://foo-bar/" + }) + afterEach(() => { + attr = null + }) + it("trasnforms argument properly", () => { + + const service = TestBed.inject(InterSpaceCoordXformSvc) + const httpTestingController = TestBed.inject(HttpTestingController) + + // subscriptions are necessary for http fetch to occur + service.transform( + VALID_TEMPLATE_SPACE_NAMES.MNI152, + VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + [1,2,3] + ).subscribe((_ev) => { + + }) + const req = httpTestingController.expectOne("http://foo-bar/v1/transform-points") + expect(req.request.method).toEqual('POST') + req.flush({}) + }) + }) }) }) }) diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts index 8dff3f67d6bbc69939068718270e487ee3bbeb2c..18518da170f3c2d70e3839d0cfc611cdf8133d4f 100644 --- a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts +++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts @@ -1,9 +1,11 @@ -import { Injectable } from "@angular/core"; +import { Inject, Injectable } from "@angular/core"; import { HttpClient, HttpHeaders, HttpErrorResponse } from "@angular/common/http"; import { catchError, timeout, map } from "rxjs/operators"; import { of, Observable } from "rxjs"; import { environment } from 'src/environments/environment' import { IDS } from "src/atlasComponents/sapi/constants" +import { GET_ATTR_TOKEN, GetAttr } from "src/util/constants"; +import { CONST } from "common/constants" type ITemplateCoordXformResp = { status: 'pending' | 'error' | 'completed' | 'cached' @@ -49,9 +51,11 @@ export class InterSpaceCoordXformSvc { } } - constructor(private httpClient: HttpClient) {} + constructor(private httpClient: HttpClient, @Inject(GET_ATTR_TOKEN) getAttr: GetAttr) { + this.url = (getAttr(CONST.OVERWRITE_SPATIAL_BACKEND_ATTR) || environment.SPATIAL_TRANSFORM_BACKEND).replace(/\/$/, '') + '/v1/transform-points' + } - private url = `${environment.SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points` + private url: string // jasmine marble cannot test promise properly // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179 diff --git a/src/main.module.ts b/src/main.module.ts index 2735d71ea4b665116e3e090e446e166ac53049fa..aedbee438fac95a83a983c2704b2a7cee7bdb00d 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -53,6 +53,7 @@ import { CONST } from "common/constants" import { ViewerCommonEffects } from './viewerModule'; import { environment } from './environments/environment'; import { SAPI } from './atlasComponents/sapi'; +import { GET_ATTR_TOKEN, GetAttr } from './util/constants'; @NgModule({ imports: [ @@ -187,12 +188,20 @@ import { SAPI } from './atlasComponents/sapi'; multi: true, deps: [ AuthService, Store ] }, + { + provide: GET_ATTR_TOKEN, + useFactory: (document: Document) => { + return (attr: string) => { + const rootEl = document.querySelector("atlas-viewer") + return rootEl?.getAttribute(attr) + } + }, + deps: [ DOCUMENT ] + }, { provide: APP_INITIALIZER, - useFactory: (sapi: SAPI, document: Document) => { - - const rootEl = document.querySelector("atlas-viewer") - const overwriteSapiUrl = rootEl?.getAttribute(CONST.OVERWRITE_SAPI_ENDPOINT_ATTR) + useFactory: (sapi: SAPI, getAttr: GetAttr) => { + const overwriteSapiUrl = getAttr(CONST.OVERWRITE_SAPI_ENDPOINT_ATTR) const { SIIBRA_API_ENDPOINTS } = environment const endpoints = (overwriteSapiUrl && [ overwriteSapiUrl ]) || SIIBRA_API_ENDPOINTS.split(',') @@ -207,7 +216,7 @@ import { SAPI } from './atlasComponents/sapi'; } }, multi: true, - deps: [ SAPI, DOCUMENT ] + deps: [ SAPI, GET_ATTR_TOKEN ] } ], bootstrap: [ diff --git a/src/state/atlasSelection/effects.spec.ts b/src/state/atlasSelection/effects.spec.ts index a64b032338706a3342829d8337ba855ffc116965..aeefa764aaa2fe86cf15ed76ca11d16599f6061e 100644 --- a/src/state/atlasSelection/effects.spec.ts +++ b/src/state/atlasSelection/effects.spec.ts @@ -3,7 +3,7 @@ import { provideMockActions } from "@ngrx/effects/testing" import { Action } from "@ngrx/store" import { MockStore, provideMockStore } from "@ngrx/store/testing" import { hot } from "jasmine-marbles" -import { NEVER, ReplaySubject, of, throwError } from "rxjs" +import { EMPTY, NEVER, ReplaySubject, of, throwError } from "rxjs" import { SAPI, SAPIModule } from "src/atlasComponents/sapi" import { SxplrRegion, SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes" import { IDS } from "src/atlasComponents/sapi/constants" @@ -15,6 +15,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations" import { translateV3Entities } from "src/atlasComponents/sapi/translateV3" import { PathReturn } from "src/atlasComponents/sapi/typeV3" import { MatDialog } from 'src/sharedModules/angularMaterial.exports' +import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service" describe("> effects.ts", () => { describe("> Effect", () => { @@ -104,6 +105,14 @@ describe("> effects.ts", () => { } } }, + { + provide: InterSpaceCoordXformSvc, + useValue: { + transform() { + return EMPTY + } + } + } ] }) }) diff --git a/src/util/constants.ts b/src/util/constants.ts index b2a3735ee186204935f11e1fdb8492dd7daa0c3d..b1411dc432430330bffc9d552cc486dc2a3c95bb 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -101,3 +101,7 @@ export const parcBanList: string[] = [ "minds/core/parcellationatlas/v1.0.0/887da8eb4c36d944ef626ed5293db3ef", "minds/core/parcellationatlas/v1.0.0/f2b1ac621421708c1bef422bb5058456", ] + +export const GET_ATTR_TOKEN = new InjectionToken("GET_ATTR_TOKEN") + +export type GetAttr = (attr: string) => string|null \ No newline at end of file