From c8c900b65a8178f77389cd62941f6890450b298c Mon Sep 17 00:00:00 2001 From: Alan Cleary Date: Mon, 11 Feb 2019 15:06:14 -0700 Subject: [PATCH] Implemented span-based searching via a new URL route, route guard, and service call. --- client/src/app/app-routing.module.ts | 7 ++- .../search/search-params.component.ts | 2 +- client/src/app/guards/default-search.guard.ts | 3 +- client/src/app/guards/index.ts | 3 ++ client/src/app/guards/span-search.guard.ts | 45 +++++++++++++++++++ client/src/app/models/server.model.ts | 1 + .../src/app/services/micro-tracks.service.ts | 17 +++++++ client/src/config/config.json | 4 ++ 8 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 client/src/app/guards/span-search.guard.ts diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 6476ba7a..9c423132 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -3,7 +3,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; // app import { InstructionsComponent, MultiComponent, SearchComponent } from "./components"; -import { DefaultSearchGuard, MultiGuard, SearchGuard } from "./guards"; +import { DefaultSearchGuard, MultiGuard, SearchGuard, SpanSearchGuard } from "./guards"; const routes: Routes = [ { @@ -38,6 +38,11 @@ const routes: Routes = [ component: SearchComponent, path: "search/:source/:gene", }, + { + canActivate: [SpanSearchGuard], + component: SearchComponent, + path: "search/:source/:chromosome/:span", + }, ]; @NgModule({ diff --git a/client/src/app/components/search/search-params.component.ts b/client/src/app/components/search/search-params.component.ts index 4291e068..ed5aba66 100644 --- a/client/src/app/components/search/search-params.component.ts +++ b/client/src/app/components/search/search-params.component.ts @@ -50,7 +50,7 @@ export class SearchParamsComponent implements OnDestroy, OnInit { ngOnInit(): void { // initialize block group and subscribe to store updates const defaultBlock = new BlockParams(); - this.blockGroup = this.fb.group(defaultBlock.formControls()); + this.blockGroup = this.fb.group(defaultBlock.formControls()); this.macroTracksService.blockParams .pipe(takeUntil(this.destroy)) .subscribe((params) => this._updateGroup(this.blockGroup, params)); diff --git a/client/src/app/guards/default-search.guard.ts b/client/src/app/guards/default-search.guard.ts index 662a78d9..94944a14 100644 --- a/client/src/app/guards/default-search.guard.ts +++ b/client/src/app/guards/default-search.guard.ts @@ -19,7 +19,8 @@ export class DefaultSearchGuard implements CanActivate { const url = "/search" + "/" + AppConfig.getDefaultServer().id + "/" + route.params.gene; - this.store.dispatch(new routerActions.Go({path: [url, {routeParam: 1}]})); + // TODO: update url so back button skips search + this.store.dispatch(new routerActions.Go({path: [url]})); return false; } } diff --git a/client/src/app/guards/index.ts b/client/src/app/guards/index.ts index 808ee120..4e9c9b0e 100644 --- a/client/src/app/guards/index.ts +++ b/client/src/app/guards/index.ts @@ -1,13 +1,16 @@ import { DefaultSearchGuard } from "./default-search.guard"; import { MultiGuard } from "./multi.guard"; import { SearchGuard } from "./search.guard"; +import { SpanSearchGuard } from "./span-search.guard"; export const guards: any[] = [ DefaultSearchGuard, MultiGuard, SearchGuard, + SpanSearchGuard, ]; export * from "./default-search.guard"; export * from "./multi.guard"; export * from "./search.guard"; +export * from "./span-search.guard"; diff --git a/client/src/app/guards/span-search.guard.ts b/client/src/app/guards/span-search.guard.ts new file mode 100644 index 00000000..d4edbf4c --- /dev/null +++ b/client/src/app/guards/span-search.guard.ts @@ -0,0 +1,45 @@ +// Angular +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable, throwError } from "rxjs"; +import { catchError, map, tap } from "rxjs/operators"; +// store +import { Store } from "@ngrx/store"; +import * as routerActions from "../store/actions/router.actions"; +import * as fromRoot from "../store/reducers"; +// app +import { MicroTracksService } from "../services"; + +@Injectable() +export class SpanSearchGuard implements CanActivate { + + constructor( + private microTracksService: MicroTracksService, + private router: Router, + private store: Store) { } + + // this guard is only triggered when no server is provided, which Search needs, + // so instead of activating it always redirects to the default server + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + const span = route.params.span.split("-"); + return this.microTracksService.getSearchFromSpan( + route.params.chromosome, + span[0], + (span.length === 1 ? span[0] : span[1]), + route.params.source) + .pipe( + tap((context) => { + const url = "/search" + "/" + route.params.source + "/" + context.gene; + this.store.dispatch(new routerActions.Go({ + path: [url], // TODO: update url so back button skips search + query: {"neighbors": context.neighbors}})); + }), + catchError((error) => { + // TODO: update url so back button skips search + this.store.dispatch(new routerActions.Go({path: ["/instructions"]})); + return throwError(error); + }), + map((context) => false) + ) + } +} diff --git a/client/src/app/models/server.model.ts b/client/src/app/models/server.model.ts index ea0088fe..1585e58d 100644 --- a/client/src/app/models/server.model.ts +++ b/client/src/app/models/server.model.ts @@ -18,4 +18,5 @@ export class Server { plotGlobal: Request; nearestGene: Request; chromosome: Request; + spanToSearch: Request; } diff --git a/client/src/app/services/micro-tracks.service.ts b/client/src/app/services/micro-tracks.service.ts index f58edb51..97454d55 100644 --- a/client/src/app/services/micro-tracks.service.ts +++ b/client/src/app/services/micro-tracks.service.ts @@ -103,6 +103,23 @@ export class MicroTracksService extends HttpService { }))); } + // takes a span for a specific chromosome and retrieves the relevant search + // (query gene and neighbors) + getSearchFromSpan( + chromosome: string, + begin: number, + end: number, + serverID: string): + Observable<{gene: string, neighbors: number}> { + const body = { + chromosome, + begin: String(begin), + end: String(end), + }; + return this._makeRequest<{gene: string, neighbors: number}>(serverID, "spanToSearch", body) + .pipe(catchError((error) => throwError(error))); + } + updateParams(params: QueryParams): void { const path = []; const query = Object.assign({}, params, {sources: params.sources.join(",")}); diff --git a/client/src/config/config.json b/client/src/config/config.json index 0ccf6cb0..4975ef8f 100644 --- a/client/src/config/config.json +++ b/client/src/config/config.json @@ -69,6 +69,10 @@ "chromosome": { "type": "POST", "url": "http://localhost:8000/services/v1_1/chromosome/" + }, + "spanToSearch": { + "type": "POST", + "url": "http://localhost:8000/services/v1_1/span-to-context/" } } ]