From d7694831656b3c76bfc12226d8cd3cff367dd68d Mon Sep 17 00:00:00 2001 From: Charles Overbeck Date: Fri, 6 Apr 2018 13:04:34 -0700 Subject: [PATCH] Various Improvements to Export to FireCloud (#8) DataBiosphere/boardwalk#6 DataBiosphere/dcc-dashboard-service#12 * While exporting, change label of "Export" button to "Exporting..." * Don't use ngrx for the export to FireCloud dialog, since application state is not being changed. This simplifies the code, although we may want to revisit this decision. * Noticed that 401 with FC causes a text/html response to be returned, so add some safeguards for that. * Display existing workspaces in UI. As part of that, need to not only display your namespaces/billing projects, but also the namespaces/billing projects of workspaces that have been shared with you. * Dialog now makes clear whether you are creating a new workspace or exporting to an existing workspace (although the new workspace case should check that it is new). * Add launch icon * Show DOS URI instead of File Id. * Retry up to 5 times in the calls to FireCloud API to fetch workspace and namespaces, as we have sometimes random failures. --- spa/src/app/cc-http/shared/cc-base.dao.ts | 25 ++-- .../_ngrx/file-export/file-export.actions.ts | 27 ----- .../_ngrx/file-export/file-export.reducer.ts | 23 ---- .../_ngrx/file-export/file-export.state.ts | 19 --- spa/src/app/files/_ngrx/file.effects.ts | 25 +--- spa/src/app/files/_ngrx/file.reducer.ts | 4 +- spa/src/app/files/_ngrx/file.selectors.ts | 3 - spa/src/app/files/_ngrx/file.state.ts | 2 - .../file-export/file-export.component.css | 7 ++ .../file-export/file-export.component.html | 23 ++-- .../file-export/file-export.component.ts | 108 +++++++++++++----- .../app/files/file-export/fire-cloud-dao.ts | 27 ++++- spa/src/app/files/files.component.ts | 80 ++++++++----- spa/src/app/files/files.module.ts | 2 + spa/src/app/files/shared/files.dao.ts | 7 +- spa/src/app/files/shared/files.service.ts | 6 +- spa/src/app/files/table/table.component.html | 6 +- spa/src/app/files/table/table.component.ts | 6 +- 18 files changed, 209 insertions(+), 191 deletions(-) delete mode 100644 spa/src/app/files/_ngrx/file-export/file-export.actions.ts delete mode 100644 spa/src/app/files/_ngrx/file-export/file-export.reducer.ts delete mode 100644 spa/src/app/files/_ngrx/file-export/file-export.state.ts diff --git a/spa/src/app/cc-http/shared/cc-base.dao.ts b/spa/src/app/cc-http/shared/cc-base.dao.ts index 6bdcc35..fc9fd8d 100644 --- a/spa/src/app/cc-http/shared/cc-base.dao.ts +++ b/spa/src/app/cc-http/shared/cc-base.dao.ts @@ -11,6 +11,7 @@ import * as _ from "lodash"; // App dependencies import { CONFLICT, NO_CONTENT } from "./http-response-status"; import { ConflictError } from "./error"; +import "rxjs/add/operator/retry"; /** * Base DAO (service) for handing, formatting and parsing HTTP requests/responses. @@ -56,13 +57,8 @@ export class CCBaseDAO { * @param queryStringParams {any} * @returns {Observable} */ - public get(url: string, queryStringParams?: any): Observable { + public get(url: string, queryStringParams?: any, retry = 0): Observable { - return this.getNoCatch(url, queryStringParams) - .catch(response => this.handleError(response)); - } - - public getNoCatch(url: string, queryStringParams?: any): Observable { // Build up GET headers let headers = new Headers(); this.addAcceptHeader(headers); @@ -84,7 +80,9 @@ export class CCBaseDAO { return >this.http // TODO revisit typing here... .get(url, requestOptions) - .map(response => this.toJSON(response)); + .map(response => this.toJSON(response)) + .retry(retry) + .catch(response => this.handleError(response)); } /** @@ -128,11 +126,20 @@ export class CCBaseDAO { // TODO conflict here with session timeout vs invalid login - disabled this to enable handling of 401 on // invalid login in LoginComponent if ( response.status === UNAUTHORIZED ) { window.location.reload(); } + let body; + try { + body = response.json(); + } + catch (ex) { + // If you get a 401 with FireCloud API, response body is text/html, regardless of the + // request's Accept header value. + body = response.statusText; + } if (response.status === CONFLICT) { - return Observable.throw(new ConflictError(response.json())); + return Observable.throw(new ConflictError(body)); } - return Observable.throw(response.json()); + return Observable.throw(body); } /** diff --git a/spa/src/app/files/_ngrx/file-export/file-export.actions.ts b/spa/src/app/files/_ngrx/file-export/file-export.actions.ts deleted file mode 100644 index 8004025..0000000 --- a/spa/src/app/files/_ngrx/file-export/file-export.actions.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Action } from "@ngrx/store"; -import { WorkspaceDescriptor } from "./file-export.state"; - -export class FileExportManifestRequestAction implements Action { - public static ACTION_TYPE = "FILE.MANIFEST_EXPORT_REQUEST"; - public readonly type = FileExportManifestRequestAction.ACTION_TYPE; - constructor(public payload: WorkspaceDescriptor) { - } -} - -export class FileExportManifestSuccessAction implements Action { - public static ACTION_TYPE = "FILE.MANIFEST_EXPORT_SUCCESS"; - public readonly type = FileExportManifestSuccessAction.ACTION_TYPE; - constructor(public fireCloudUrl: string) { - } -} - -export class FileExportManifestErrorAction implements Action { - public static ACTION_TYPE = "FILE.MANIFEST_EXPORT_ERROR"; - public readonly type = FileExportManifestErrorAction.ACTION_TYPE; - constructor(public errorReason: string) { - } -} - -export type All = FileExportManifestRequestAction - | FileExportManifestSuccessAction - | FileExportManifestErrorAction; \ No newline at end of file diff --git a/spa/src/app/files/_ngrx/file-export/file-export.reducer.ts b/spa/src/app/files/_ngrx/file-export/file-export.reducer.ts deleted file mode 100644 index dbea1a0..0000000 --- a/spa/src/app/files/_ngrx/file-export/file-export.reducer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Action } from "@ngrx/store"; -import { - FileExportManifestRequestAction, FileExportManifestErrorAction, - FileExportManifestSuccessAction -} from "./file-export.actions"; -import { FileExportManifestState } from "./file-export.state"; - -export function reducer(state: FileExportManifestState = FileExportManifestState.getDefaultState(), action: Action): FileExportManifestState { - switch (action.type) { - case FileExportManifestSuccessAction.ACTION_TYPE: { - return new FileExportManifestState((action as FileExportManifestSuccessAction).fireCloudUrl, "success"); - } - case FileExportManifestErrorAction.ACTION_TYPE: { - return new FileExportManifestState(null, "error", - (action as FileExportManifestErrorAction).errorReason); - } - case FileExportManifestRequestAction.ACTION_TYPE: { - return new FileExportManifestState(null, "request"); - } - default: - return state; - } -} \ No newline at end of file diff --git a/spa/src/app/files/_ngrx/file-export/file-export.state.ts b/spa/src/app/files/_ngrx/file-export/file-export.state.ts deleted file mode 100644 index eb49e99..0000000 --- a/spa/src/app/files/_ngrx/file-export/file-export.state.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface WorkspaceDescriptor { - name: string; - namespace: string; -} - -export type FileExportManifestStatus = "request" | "success" | "error" | null; - -// export interface FileExportStatus -export class FileExportManifestState { - constructor(public fireCloudUrl = "", - public status: FileExportManifestStatus = null, - public statusMessage = "") { - } - - public static getDefaultState() { - return new FileExportManifestState(); - } - -} \ No newline at end of file diff --git a/spa/src/app/files/_ngrx/file.effects.ts b/spa/src/app/files/_ngrx/file.effects.ts index 3bde17d..5367d52 100644 --- a/spa/src/app/files/_ngrx/file.effects.ts +++ b/spa/src/app/files/_ngrx/file.effects.ts @@ -18,11 +18,9 @@ import "rxjs/add/observable/concat"; import "rxjs/add/observable/of"; import "rxjs/add/operator/withLatestFrom"; import * as _ from "lodash"; - // App dependencies import { FilesService } from "../shared/files.service"; import { FileSummary } from "../file-summary/file-summary"; -import { FileFacetMetadata } from "../file-facet-metadata/file-facet-metadata.model"; import { FetchFileFacetsRequestAction, FetchFileFacetsSuccessAction, @@ -43,15 +41,12 @@ import { } from "app/files/_ngrx/file.selectors"; import { AppState } from "../../_ngrx/app.state"; import { - FetchInitialTableDataRequestAction, FetchPagedOrSortedTableDataRequestAction, + FetchInitialTableDataRequestAction, + FetchPagedOrSortedTableDataRequestAction, FetchTableDataSuccessAction } from "./table/table.actions"; import { TableModel } from "../table/table.model"; import { DEFAULT_TABLE_PARAMS } from "../table/table-params.model"; -import { - FileExportManifestRequestAction, FileExportManifestErrorAction, - FileExportManifestSuccessAction -} from "./file-export/file-export.actions"; @Injectable() export class FileEffects { @@ -194,22 +189,6 @@ export class FileEffects { return this.fileService.downloadFileManifest(query); }); - @Effect() - exportToFireCloud$: Observable = this.actions$ - .ofType(FileExportManifestRequestAction.ACTION_TYPE) - .map(action => action.payload) - .withLatestFrom(this.store.select(selectSelectedFileFacets)) - .switchMap((results) => { - const [payload, selectedFacets] = results; - return this.fileService.exportToFireCloud(selectedFacets, payload.name, payload.namespace); - }) - .map((fcUrl) => { - if (fcUrl.startsWith("Error")) { - return new FileExportManifestErrorAction(fcUrl); - } - return new FileExportManifestSuccessAction(fcUrl); - }); - private colorWheel: Map; /** diff --git a/spa/src/app/files/_ngrx/file.reducer.ts b/spa/src/app/files/_ngrx/file.reducer.ts index 24128de..a672f13 100644 --- a/spa/src/app/files/_ngrx/file.reducer.ts +++ b/spa/src/app/files/_ngrx/file.reducer.ts @@ -2,7 +2,6 @@ import * as fileSummaryReducer from "./file-summary/file-summary.reducer"; import * as fileFacetListReducer from "./file-facet-list/file-facet-list.reducer"; import * as fileManifestSummaryReducer from "./file-manifest-summary/file-manifest-summary.reducer"; import * as fileFacetMetadataSummaryReducer from "./file-facet-metadata-summary/file-facet-metadata-summary.reducer"; -import * as fileExportReducer from "./file-export/file-export.reducer"; import * as tableReducer from "./table/table.reducer"; export const reducer = { @@ -10,6 +9,5 @@ export const reducer = { fileFacetList: fileFacetListReducer.reducer, fileManifestSummary: fileManifestSummaryReducer.reducer, fileFacetMetadataSummary: fileFacetMetadataSummaryReducer.reducer, - tableState: tableReducer.reducer, - fileExportState: fileExportReducer.reducer + tableState: tableReducer.reducer }; \ No newline at end of file diff --git a/spa/src/app/files/_ngrx/file.selectors.ts b/spa/src/app/files/_ngrx/file.selectors.ts index d153913..cecb956 100644 --- a/spa/src/app/files/_ngrx/file.selectors.ts +++ b/spa/src/app/files/_ngrx/file.selectors.ts @@ -12,7 +12,6 @@ import { createFeatureSelector, createSelector } from "@ngrx/store"; import { FileSummaryState } from "./file-summary/file-summary.state"; import { FileFacetListState } from "./file-facet-list/file-facet-list.state"; import { FileFacetMetadataSummaryState } from "./file-facet-metadata-summary/file-facet-metadata-summary.state"; -import { FileExportManifestState } from "./file-export/file-export.state"; import { TableState } from "./table/table.state"; export const selectFileFacets = createFeatureSelector("fileFacetList"); @@ -51,5 +50,3 @@ export const selectTableQueryParams = createSelector(selectSelectedFacetsMap, se return { selectedFacets, pagination }; }); -export const selectExportFileState = createFeatureSelector("fileExportState"); - diff --git a/spa/src/app/files/_ngrx/file.state.ts b/spa/src/app/files/_ngrx/file.state.ts index c5e6a08..0e6e7b8 100644 --- a/spa/src/app/files/_ngrx/file.state.ts +++ b/spa/src/app/files/_ngrx/file.state.ts @@ -3,7 +3,6 @@ import { FileFacetListState } from "./file-facet-list/file-facet-list.state"; import { FileManifestSummaryState } from "./file-manifest-summary/file-manifest-summary.state"; import { FileFacetMetadataSummaryState } from "./file-facet-metadata-summary/file-facet-metadata-summary.state"; import { TableState } from "./table/table.state"; -import { FileExportManifestState } from "./file-export/file-export.state"; export interface FileState { fileSummary: FileSummaryState; @@ -11,5 +10,4 @@ export interface FileState { fileManifestSummary: FileManifestSummaryState; fileFacetMetadataSummary: FileFacetMetadataSummaryState; tableState: TableState; - exportFileManifestState: FileExportManifestState; } \ No newline at end of file diff --git a/spa/src/app/files/file-export/file-export.component.css b/spa/src/app/files/file-export/file-export.component.css index 7458e6d..f6c6e08 100644 --- a/spa/src/app/files/file-export/file-export.component.css +++ b/spa/src/app/files/file-export/file-export.component.css @@ -3,3 +3,10 @@ display: flex; font-size: large; } +mat-radio-group { + margin-bottom: 10px; +} + +mat-radio-button { + margin-right: 10px; +} diff --git a/spa/src/app/files/file-export/file-export.component.html b/spa/src/app/files/file-export/file-export.component.html index b557865..5b16f1e 100644 --- a/spa/src/app/files/file-export/file-export.component.html +++ b/spa/src/app/files/file-export/file-export.component.html @@ -1,17 +1,26 @@

Export to FireCloud

-

The selected files were exported to the FireCloud {{data.workspace}} workspace.

+

The selected files were exported to the FireCloud {{data.workspace}} workspace. launch

There was an error exporting to FireCloud: {{errorMessage}}

-

Export the selected facets into a new FireCloud workspace.

+

Export the selected facets into a FireCloud workspace.

- + + {{namespace}} + - - - {{namespace}} + + New workspace + Existing workspace + + + + + + + {{workspace.workspace.name}}
@@ -19,6 +28,6 @@

Export to FireCloud

- +
diff --git a/spa/src/app/files/file-export/file-export.component.ts b/spa/src/app/files/file-export/file-export.component.ts index 5c5d93a..ba24fb0 100644 --- a/spa/src/app/files/file-export/file-export.component.ts +++ b/spa/src/app/files/file-export/file-export.component.ts @@ -1,12 +1,10 @@ -import { Component, Inject, OnInit } from "@angular/core"; -import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material"; -import { selectExportFileState } from "../_ngrx/file.selectors"; -import { FileExportManifestState } from "../_ngrx/file-export/file-export.state"; -import { Observable } from "rxjs/Observable"; +import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; +import { MAT_DIALOG_DATA, MatDialogRef, MatRadioChange, MatSelectChange } from "@angular/material"; +import { selectSelectedFileFacets } from "../_ngrx/file.selectors"; import { Store } from "@ngrx/store"; import { AppState } from "../../_ngrx/app.state"; -import { Subscription } from "rxjs/Subscription"; -import { FileExportManifestRequestAction } from "../_ngrx/file-export/file-export.actions"; +import { FilesService } from "../shared/files.service"; +import { FireCloudDAO, FirecloudWorkspace } from "./fire-cloud-dao"; @Component({ selector: "bw-file-export", @@ -20,44 +18,98 @@ export class FileExportComponent implements OnInit { public exported = false; public errorMessage: string; public firecloudUrl: string; - private exportManifest$: Observable; private store: Store; - private manifestSubscription: Subscription; + public filteredWorkspaces: FirecloudWorkspace[] = []; + public workspaceType: "new" | "existing" = "new"; + private newWorkspace = ""; + private billingProjects: string[]; + private canCreateWorkspace: boolean; constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private data: any) { - this.exportManifest$ = data.exportManifest$; + @Inject(MAT_DIALOG_DATA) private data: any, + private fireCloudDAO: FireCloudDAO, + private filesService: FilesService) { this.store = data.store; + this.billingProjects = this.calculateBillingProjects(data.namespaces, data.workspaces); + this.setupForBillingProject(); } ngOnInit() { } onClose() { - if (this.manifestSubscription) { - this.manifestSubscription.unsubscribe(); - } this.dialogRef.close(); } onExport() { this.exporting = true; this.errorMessage = null; - this.exportManifest$ = this.store.select(selectExportFileState); - this.manifestSubscription = this.exportManifest$.subscribe(state => { - if (state.status === "success") { - this.manifestSubscription.unsubscribe(); - this.manifestSubscription = null; - this.exporting = false; - this.exported = true; - this.firecloudUrl = state.fireCloudUrl; - } - else if (state.status === "error") { - this.errorMessage = state.statusMessage; - this.exporting = false; - } + this.store.select(selectSelectedFileFacets).take(1).subscribe(selectedFacets => { + this.filesService.exportToFireCloud(selectedFacets, this.data.workspace, this.data.namespace).subscribe( + (fcUrl) => { + this.exporting = false; + this.exported = true; + this.firecloudUrl = fcUrl; + }, + (error: any) => { + this.errorMessage = (error && error.message) || error || "Unknown error"; + this.exporting = false; + } + ); }); - this.store.dispatch(new FileExportManifestRequestAction({name: this.data.workspace, namespace: this.data.namespace})); + } + + onBillingProjectChanged($event: MatSelectChange) { + this.setupForBillingProject(); + } + + onWorkspaceTypeChanged($event: MatRadioChange) { + if (this.workspaceType === "existing") { + this.newWorkspace = this.data.workspace; + this.data.workspace = this.filteredWorkspaces.length > 0 ? this.filteredWorkspaces[0].workspace.name : ""; + } + else { + this.data.workspace = this.newWorkspace; + } + } + + private filterWorkspaces(workspaces, namespace: string) { + return workspaces.filter(workspace => workspace.workspace.namespace + === namespace).sort((a, b) => a.workspace.name.toLocaleLowerCase().localeCompare( + b.workspace.name.toLocaleLowerCase())); + } + /** + * Calculates all the billing projects where there is write and/or owner access. + * This is the billing projects you are part of, as well as the namespaces of projects + * that have been shared with you. + * @param {string[]} ownNamespaces + * @param {FirecloudWorkspace[]} workspaces all workspaces that you have access to + * @returns {string[]} an array of namespaces that you can write to. + */ + private calculateBillingProjects(ownNamespaces: string[], workspaces: FirecloudWorkspace[]): string[] { + const projectsFromWorkspaces = workspaces + .filter( + workspace => (workspace.accessLevel === "OWNER" || workspace.accessLevel === "WRITER") + && (!ownNamespaces.find(n => n === workspace.workspace.namespace))) + .map(workspace => workspace.workspace.namespace) + .reduce((acc: string[], value) => { + if (!acc.includes(value)) { + acc.push(value); + } + return acc; + }, []); + return [].concat(ownNamespaces).concat(projectsFromWorkspaces); + } + + private setupForBillingProject() { + this.filteredWorkspaces = this.filterWorkspaces(this.data.workspaces, this.data.namespace); + this.canCreateWorkspace = this.data.namespaces.includes(this.data.namespace); + if (!this.canCreateWorkspace) { + this.workspaceType = "existing"; + } + if (this.workspaceType === "existing") { + this.data.workspace = this.filteredWorkspaces.length > 0 ? this.filteredWorkspaces[0].workspace.name : ""; + } } } diff --git a/spa/src/app/files/file-export/fire-cloud-dao.ts b/spa/src/app/files/file-export/fire-cloud-dao.ts index ff1ff78..10bb15d 100644 --- a/spa/src/app/files/file-export/fire-cloud-dao.ts +++ b/spa/src/app/files/file-export/fire-cloud-dao.ts @@ -10,27 +10,42 @@ export interface FirecloudNamespace { creationStatus: string; } +export interface FirecloudWorkspace { + accessLevel: "OWNER" | "READER" | "WRITER" | "NO ACCESS"; + workspace: { + name: string; + namespace: string; + }; +} + @Injectable() export class FireCloudDAO extends CCBaseDAO { - constructor(http: Http, private configService: ConfigService) { - super(http); - } + constructor(http: Http, private configService: ConfigService) { + super(http); + } fetchNamespaces(): Observable { const params = { path: "/api/profile/billing" }; - return this.get(this.buildDataUrl("/proxy_firecloud"), params); + return this.get(this.buildFireCloudProxyUrl(), params, 5); + } + + fetchWorkspaces(): Observable { + const params = { + path: "/api/workspaces" + }; + return this.get(this.buildFireCloudProxyUrl(), params, 5); } /** * Privates */ - private buildDataUrl(url: string) { + private buildFireCloudProxyUrl() { const domain = this.configService.getDataURL(); - return `${domain}${url}`; + return `${domain}/proxy_firecloud`; } diff --git a/spa/src/app/files/files.component.ts b/spa/src/app/files/files.component.ts index 7d47f4f..f154d60 100644 --- a/spa/src/app/files/files.component.ts +++ b/spa/src/app/files/files.component.ts @@ -24,10 +24,12 @@ import { AppState } from "../_ngrx/app.state"; import { FetchFileFacetsRequestAction } from "./_ngrx/file-facet-list/file-facet-list.actions"; import { MatDialog, MatIconRegistry } from "@angular/material"; import { FileExportComponent } from "./file-export/file-export.component"; -import { FileExportManifestState } from "./_ngrx/file-export/file-export.state"; -import { FireCloudDAO } from "./file-export/fire-cloud-dao"; +import { FireCloudDAO, FirecloudNamespace, FirecloudWorkspace } from "./file-export/fire-cloud-dao"; import { CCAlertDialogComponent } from "../shared/cc-alert-dialog/cc-alert-dialog.component"; import { DomSanitizer } from "@angular/platform-browser"; +import { User } from "../data/user/user.model"; +import { selectAuthenticatedUser } from "../auth/_ngrx/auth.selectors"; +import { forkJoin } from "rxjs/observable/forkJoin"; @Component({ selector: "bw-files", @@ -40,10 +42,12 @@ export class FilesComponent implements OnInit { private route: ActivatedRoute; private store: Store; + private exportDialogUp = false; + private authenticated = false; + // Public variables public selectFileSummary$: Observable; public fileFacets$: Observable; - public exportManifest$: Observable; /** * @param route {ActivatedRoute} @@ -60,6 +64,7 @@ export class FilesComponent implements OnInit { this.store = store; iconRegistry.addSvgIcon("firecloud", sanitizer.bypassSecurityTrustResourceUrl("/assets/images/thirdparty/FireCloud-white-icon.svg")); + } /** @@ -83,37 +88,58 @@ export class FilesComponent implements OnInit { } onExportToFireCloud() { - this.fireCloudDAO.fetchNamespaces() - .subscribe(namespaces => { - if (namespaces.length > 0) { - const projectNames: string[] = namespaces.map(namespace => namespace.projectName); - this.dialog.open(FileExportComponent, { + if (!this.exportDialogUp) { + this.store.select(selectAuthenticatedUser).take(1).subscribe((user: User) => { + if (!user || !user.email) { + this.dialog.open(CCAlertDialogComponent, { data: { - workspace: "", - namespace: projectNames[0], - namespaces: projectNames, - store: this.store, - exportManifest$: this.exportManifest$ + title: "Login Required", + message: "You must be logged in before you can export data to FireCloud." } }); } else { - this.dialog.open(CCAlertDialogComponent, { - data: { - title: "Error", - message: "You do not have any billing projects associated with your FireCloud account. You must have at least one in order to proceed." - } - }); + this.exportDialogUp = true; + const fetchNamespaces = this.fireCloudDAO.fetchNamespaces(); + const fetchWorkspaces = this.fireCloudDAO.fetchWorkspaces(); + forkJoin(fetchNamespaces, fetchWorkspaces).subscribe(results => { + const namespaces: FirecloudNamespace[] = results[0]; + const workspaces: FirecloudWorkspace[] = results[1]; + if (namespaces.length > 0 || workspaces.length > 0) { + const projectNames: string[] = namespaces.map(namespace => namespace.projectName); + const dialogRef = this.dialog.open(FileExportComponent, { + data: { + workspace: "", + namespace: projectNames[0], + namespaces: projectNames, + workspaces: workspaces, + store: this.store + } + }); + dialogRef.afterClosed().subscribe(() => this.exportDialogUp = false); + } + else { + const dialogRef = this.dialog.open(CCAlertDialogComponent, { + data: { + title: "Error", + message: "You do not have any billing projects associated with your FireCloud account. You must have at least one in order to proceed." + } + }); + dialogRef.afterClosed().subscribe(() => this.exportDialogUp = false); + } + }, + () => { + const dialogRef = this.dialog.open(CCAlertDialogComponent, { + data: { + title: "Error", + message: "There was an error retrieving information from FireCloud. Please try again." + } + }); + dialogRef.afterClosed().subscribe(() => this.exportDialogUp = false); + }); } - }, - () => { - this.dialog.open(CCAlertDialogComponent, { - data: { - title: "Error", - message: "There was an error fetching your FireCloud billing accounts. Try logging in again." - } - }); }); + } } /** diff --git a/spa/src/app/files/files.module.ts b/spa/src/app/files/files.module.ts index da62f41..0fc8dd4 100644 --- a/spa/src/app/files/files.module.ts +++ b/spa/src/app/files/files.module.ts @@ -20,6 +20,7 @@ import { MatInputModule, MatMenuModule, MatProgressBarModule, + MatRadioModule, MatSelectModule, MatSortModule, MatSnackBarModule, @@ -70,6 +71,7 @@ import { HttpClientModule } from "@angular/common/http"; MatMenuModule, MatSelectModule, MatProgressBarModule, + MatRadioModule, MatSortModule, MatSnackBarModule, MatTableModule, diff --git a/spa/src/app/files/shared/files.dao.ts b/spa/src/app/files/shared/files.dao.ts index af687af..f6371ed 100644 --- a/spa/src/app/files/shared/files.dao.ts +++ b/spa/src/app/files/shared/files.dao.ts @@ -25,6 +25,7 @@ import { ConfigService } from "../../config/config.service"; import { TableModel } from "../table/table.model"; import { PaginationModel } from "../table/pagination.model"; import { TableParamsModel } from "../table/table-params.model"; +import "rxjs/add/operator/delay"; @Injectable() export class FilesDAO extends CCBaseDAO { @@ -182,9 +183,9 @@ export class FilesDAO extends CCBaseDAO { const query = new ICGCQuery(this.facetsToQueryString(selectedFacets), "tarball"); const params = Object.assign({"workspace": workspaceName, "namespace": workspaceNamespace}, query); - const url = this.buildDataUrl(`//export_to_firecloud`); - // return Observable.of(`https://portal.firecloud.org/${workspaceNamespace}/${workspaceName}`); - return this.getNoCatch(url, params) + const url = this.buildDataUrl(`/export_to_firecloud`); + // return Observable.of(`https://portal.firecloud.org/${workspaceNamespace}/${workspaceName}`).delay(5000); + return this.get(url, params) .map(() => { // TODO: Should get this from response return `https://portal.firecloud.org/#workspaces/${workspaceNamespace}/${workspaceName}`; diff --git a/spa/src/app/files/shared/files.service.ts b/spa/src/app/files/shared/files.service.ts index dc9ff9f..7107928 100644 --- a/spa/src/app/files/shared/files.service.ts +++ b/spa/src/app/files/shared/files.service.ts @@ -27,11 +27,7 @@ export class FilesService { public exportToFireCloud(selectedFacets: FileFacet[], workspaceName: string, workspaceNamespace: string): Observable { - return this.fileDAO.exportToFireCloud(selectedFacets, workspaceName, workspaceNamespace) - .catch(err => { - const message = err.statusText || "Unknown error"; - return Observable.of(`Error: ${message}`); - }); + return this.fileDAO.exportToFireCloud(selectedFacets, workspaceName, workspaceNamespace); } /** diff --git a/spa/src/app/files/table/table.component.html b/spa/src/app/files/table/table.component.html index 1a91ecf..8e40689 100644 --- a/spa/src/app/files/table/table.component.html +++ b/spa/src/app/files/table/table.component.html @@ -42,9 +42,9 @@ Filename {{element.title}} - - File Id - {{element.file_id}} + + DOS URI + {{element.dos_uri}} File Size diff --git a/spa/src/app/files/table/table.component.ts b/spa/src/app/files/table/table.component.ts index 1f42b66..4167100 100644 --- a/spa/src/app/files/table/table.component.ts +++ b/spa/src/app/files/table/table.component.ts @@ -32,7 +32,7 @@ export class TableComponent implements OnInit { displayedColumns = [ "program", "project", "submittedDonorId", "submittedSpecimenId", "specimen_type", "submittedSampleId", - "software", "title", "file_id", "fileSize" + "software", "title", "dos_uri", "fileSize" ]; tableElementDataSource: TableElementDataSource; pagination$: Observable; @@ -261,7 +261,7 @@ export interface Element { sample: string; workflow: string; title: string; - file_id: string; + dos_uri: string; size: number; } @@ -292,7 +292,7 @@ class TableElementDataSource extends DataSource { sample: row.donors[0].submittedSampleId, workflow: row.analysisMethod.software, title: row.fileCopies[0].fileName, - file_id: row.id, + dos_uri: row.fileCopies[0].dosUri, size: row.fileCopies[0].fileSize }; });