diff --git a/cypress/integration/checkerWorkflowFromWorkflows.js b/cypress/integration/checkerWorkflowFromWorkflows.js index 6c8b5252e5..9552b54e72 100644 --- a/cypress/integration/checkerWorkflowFromWorkflows.js +++ b/cypress/integration/checkerWorkflowFromWorkflows.js @@ -33,7 +33,7 @@ describe('Checker workflow test from workflows', function() { cy.get('#viewCheckerWorkflowButton').should('visible').click() // In the checker workflow right now - cy.url().should('eq', String(global.baseUrl) + '/workflows/github.com/A/l/_cwl_checker:master?tab=info') + cy.url().should('eq', String(global.baseUrl) + '/workflows/github.com/A/l/_cwl_checker?tab=info') cy.get('#viewCheckerWorkflowButton').should('not.be.visible') cy.get('#addCheckerWorkflowButton').should('not.be.visible') cy.get('#launchCheckerWorkflow').should('not.be.visible') diff --git a/src/app/container/container.component.ts b/src/app/container/container.component.ts index 330e715aa1..988dd61aa0 100644 --- a/src/app/container/container.component.ts +++ b/src/app/container/container.component.ts @@ -128,7 +128,7 @@ export class ContainerComponent extends Entry { if (this.tool.tags.length === 0) { this.selectedVersion = null; } else { - this.selectedVersion = this.selectVersion(this.tool.tags, this.urlVersion, this.tool.defaultVersion, this.selectedVersion); + this.selectedVersion = this.selectVersion(this.tool.tags, this.urlVersion, this.tool.defaultVersion); } } // Select version @@ -171,7 +171,7 @@ export class ContainerComponent extends Entry { this.containersService.getPublishedContainerByToolPath(this.title) .subscribe(tool => { this.containerService.setTool(tool); - this.selectedVersion = this.selectVersion(this.tool.tags, this.urlVersion, this.tool.defaultVersion, this.selectedVersion); + this.selectedVersion = this.selectVersion(this.tool.tags, this.urlVersion, this.tool.defaultVersion); this.selectTab(this.validTabs.indexOf(this.currentTab)); if (this.tool != null) { diff --git a/src/app/shared/entry.ts b/src/app/shared/entry.ts index 44069bb592..732d471926 100644 --- a/src/app/shared/entry.ts +++ b/src/app/shared/entry.ts @@ -175,34 +175,37 @@ export abstract class Entry implements OnInit, OnDestroy, AfterViewInit { }); } - public selectVersion(versions, urlVersion, defaultVersion, selectedVersion): any { - let useFirstTag = true; - let urlTagExists = false; - // Determine which tag to select - for (const item of versions) { - // If a tag is specified in the URL then use it - if (urlVersion !== null) { - if (item.name === urlVersion) { - selectedVersion = item; - useFirstTag = false; - urlTagExists = true; - break; - } - } else if (defaultVersion !== null && !urlTagExists) { - // If the tool has a default version then use it - if (item.name === defaultVersion) { - selectedVersion = item; - useFirstTag = false; - break; - } + /** + * Given an array of WorkflowVersions or Tag, decide which one to return (displayed to the user) + * 1. If the URL specifies a specific Tag or WorkflowVersion, choose to display that one. Otherwise, + * 2. If the default version is specified, choose to display the default version. Otherwise, + * 3. Choose the first version in the array (the last updated version?) + * @param {((Array))} versions All versions of an entry. + * This is supposed to be (Array | Array). + * No idea why errors appear when attempted to do so. + * @param {string} urlVersion The version of the entry specified in the URL (possibly null or undefined) + * @param {string} defaultVersion The default version of an entry (possibly null or undefined) + * @returns {((WorkflowVersion | Tag))} The version to display to the user + * @memberof Entry + */ + public selectVersion(versions: (Array), urlVersion: string, defaultVersion: string): (WorkflowVersion | Tag) { + if (!versions || versions.length === 0) { + return null; + } + let foundVersion: (WorkflowVersion | Tag); + if (urlVersion) { + foundVersion = versions.find((version: (WorkflowVersion | Tag)) => version.name === urlVersion); + if (foundVersion) { + return foundVersion; } } - - // If no url tag or default version, select first element in the dropdown - if (useFirstTag && versions.length > 0) { - selectedVersion = versions[0]; + if (defaultVersion) { + foundVersion = versions.find((version: (WorkflowVersion | Tag)) => version.name === defaultVersion); + if (foundVersion) { + return foundVersion; + } } - return selectedVersion; + return versions[0]; } public getEntryPathFromURL(): string { @@ -246,7 +249,7 @@ export abstract class Entry implements OnInit, OnDestroy, AfterViewInit { currentPath += ':' + this.selectedVersion.name; } currentPath += '?tab=' + this.currentTab; - this.location.go(currentPath); + this.location.replaceState(currentPath); } } @@ -315,7 +318,7 @@ export abstract class Entry implements OnInit, OnDestroy, AfterViewInit { const url = decodeURIComponent(window.location.href); const containersIndex = this.getIndexInURL('/' + type); const newPath = url.substring(containersIndex); - this.location.go(newPath); + this.location.replaceState(newPath); } /** diff --git a/src/app/shared/launch.service.ts b/src/app/shared/launch.service.ts index 6d3304b776..6fbb9a374c 100644 --- a/src/app/shared/launch.service.ts +++ b/src/app/shared/launch.service.ts @@ -84,11 +84,27 @@ export abstract class LaunchService { * @param path The GA4GH Tool's path * @param versionName The ToolVersion's name */ - getNxtTestJsonString(workflowPath: string, versionName: string) { + getTestJsonString(workflowPath: string, versionName: string, type: string) { + let urlType = 'PLAIN_NFL'; + switch (type) { + case 'wdl': + urlType = 'PLAIN_WDL'; + break; + case 'cwl': + urlType = 'PLAIN_CWL'; + break; + case 'nextflow': + urlType = 'PLAIN_NFL'; + break; + default: + console.log('Unknown descriptor type: ' + type); + return null; + } + const prefix = `$ wget --header='Accept: text/plain`; const outputFile = `-O Dockstore.json`; - const encodedID = encodeURIComponent('#workflow/ ${ workflowPath) }'); - const encodedVersion = encodeURIComponent('${ versionName }'); - return `${prefix}' ${ Dockstore.API_URI }${ga4ghPath}/tools/${ encodedID }/versions/${ encodedVersion }/NXT/tests ${outputFile}`; + const encodedID = encodeURIComponent(`#workflow/${ workflowPath }`); + const encodedVersion = encodeURIComponent(`${ versionName }`); + return `${prefix}' ${Dockstore.API_URI}${ga4ghPath}/tools/${encodedID}/versions/${encodedVersion}/${urlType}/tests ${outputFile}`; } } diff --git a/src/app/workflow/launch/launch.component.ts b/src/app/workflow/launch/launch.component.ts index 29cc7f1961..20a6593233 100644 --- a/src/app/workflow/launch/launch.component.ts +++ b/src/app/workflow/launch/launch.component.ts @@ -64,6 +64,6 @@ export class LaunchWorkflowComponent { this.checkEntryCommand = this.launchService.getCheckWorkflowString(workflowPath, versionName); this.consonance = this.launchService.getConsonanceString(workflowPath, versionName); this.nextflowNativeLaunchDescription = this.launchService.getNextflowNativeLaunchString(basePath, versionName); - this.wgetTestJsonDescription = this.launchService.getNxtTestJsonString(workflowPath, versionName); + this.wgetTestJsonDescription = this.launchService.getTestJsonString(workflowPath, versionName, this.currentDescriptor); } } diff --git a/src/app/workflow/launch/workflow-launch.service.spec.ts b/src/app/workflow/launch/workflow-launch.service.spec.ts index 5a80c7f6d5..a30ad91ed6 100644 --- a/src/app/workflow/launch/workflow-launch.service.spec.ts +++ b/src/app/workflow/launch/workflow-launch.service.spec.ts @@ -18,6 +18,7 @@ import { inject, TestBed } from '@angular/core/testing'; import { WorkflowService } from './../../shared/workflow.service'; import { WorkflowStubService } from './../../test/service-stubs'; import { WorkflowLaunchService } from './workflow-launch.service'; +import { Dockstore } from '../../shared/dockstore.model'; describe('WorkflowLaunchService', () => { beforeEach(() => { @@ -60,4 +61,20 @@ describe('WorkflowLaunchService', () => { expect(service.getCheckWorkflowString('potato', 'stew')).toBe('$ dockstore checker launch --entry potato:stew checkparam.json'); expect(service.getCheckWorkflowString(null, null)).toBe(''); })); + it('should get the wget test parameter file command', inject([WorkflowLaunchService], (service: WorkflowLaunchService) => { + expect(service.getTestJsonString('github.com/garyluu/example_cwl_workflow', 'v1.0', 'cwl')) + .toBe(`$ wget --header='Accept: text/plain' ` + + `${Dockstore.API_URI}/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fgaryluu%2Fexample_cwl_workflow/versions/v1.0/PLAIN_CWL/tests ` + + `-O Dockstore.json`); + expect(service.getTestJsonString('github.com/garyluu/example_cwl_workflow', 'v1.0', 'wdl')) + .toBe(`$ wget --header='Accept: text/plain' ` + + `${Dockstore.API_URI}/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fgaryluu%2Fexample_cwl_workflow/versions/v1.0/PLAIN_WDL/tests ` + + `-O Dockstore.json`); + expect(service.getTestJsonString('github.com/garyluu/example_cwl_workflow', 'v1.0', 'nextflow')) + .toBe(`$ wget --header='Accept: text/plain' ` + + `${Dockstore.API_URI}/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fgaryluu%2Fexample_cwl_workflow/versions/v1.0/PLAIN_NFL/tests ` + + `-O Dockstore.json`); + expect(service.getTestJsonString('github.com/garyluu/example_cwl_workflow', 'v1.0', 'potato')) + .toBe(null); + })); }); diff --git a/src/app/workflow/workflow.component.css b/src/app/workflow/workflow.component.css index 2e6cf08e36..bddbab6a19 100644 --- a/src/app/workflow/workflow.component.css +++ b/src/app/workflow/workflow.component.css @@ -34,3 +34,17 @@ pre { .button-wrap > .button:not(:last-child) { margin-bottom: 10px; } +a.disabled { + color: gray; + cursor: not-allowed; +} +a.disabled > img { + filter: brightness(90%); +} +p.import-warning { + font-size: 11px; + font-style: italic; + text-align: left; + line-height: 1em; + margin-bottom: 4px; +} diff --git a/src/app/workflow/workflow.component.html b/src/app/workflow/workflow.component.html index 6a8176216e..73cbba1e44 100644 --- a/src/app/workflow/workflow.component.html +++ b/src/app/workflow/workflow.component.html @@ -210,7 +210,7 @@

Source Repositories

-
+

Launch with

@@ -218,11 +218,12 @@

Launch with

+

Warning: this version of the WDL has imports, which are not supported by DNAstack. Make sure to select a version without imports in DNAstack.

-
diff --git a/src/app/workflow/workflow.component.ts b/src/app/workflow/workflow.component.ts index 63ca82568f..4c5b2371da 100644 --- a/src/app/workflow/workflow.component.ts +++ b/src/app/workflow/workflow.component.ts @@ -38,6 +38,8 @@ import { Workflow } from './../shared/swagger/model/workflow'; import { UrlResolverService } from './../shared/url-resolver.service'; import { SourceFile } from '../shared/swagger/model/sourceFile'; +const importHttpRegEx: RegExp = new RegExp(/^\s*import\s+"https?/, 'm'); + @Component({ selector: 'app-workflow', templateUrl: './workflow.component.html', @@ -47,6 +49,9 @@ export class WorkflowComponent extends Entry { workflowEditData: any; dnastackURL: string; fireCloudURL: string; + public wdlHasHttpImports: boolean; + public wdlHasFileImports: boolean; + public enableLaunchWithFireCloud = Dockstore.FEATURES.enableLaunchWithFireCloud; public workflow; public missingWarning: boolean; public title: string; @@ -110,18 +115,45 @@ export class WorkflowComponent extends Entry { } } - private isWdl(workflowRef: ExtendedWorkflow) { - return workflowRef.full_workflow_path && workflowRef.descriptorType === 'wdl'; + public isWdl(workflowRef: ExtendedWorkflow) { + return workflowRef && workflowRef.full_workflow_path && workflowRef.descriptorType === 'wdl'; + } + + public gotoFirecloud() { + if (this.fireCloudURL) { + window.open(this.fireCloudURL); + } } - private setupFireCloudUrl(workflowRef: ExtendedWorkflow) { - if (Dockstore.FEATURES.enableLaunchWithFireCloud) { - this.fireCloudURL = null; + /** + * Checks WDL for import statements, setting wdlHasFileImports and wdlHasHttpImports properties + * accordingly. If the WDL has file imports, doesn't bother to check if there are http imports, to save + * an Ajax call. + * + * Also sets the fireCloudURL property if the WDL has no file imports. + * + * This can be done more cleanly, but since code has been moved and substantially changed in develop branch, + * leaving like this for hot fix. + * + * @param {ExtendedWorkflow} workflowRef + */ + private checkWdlForImportsAndSetFirecloudUrl(workflowRef: ExtendedWorkflow) { + this.fireCloudURL = null; + this.wdlHasFileImports = false; + this.wdlHasHttpImports = false; + if (this.isWdl(workflowRef)) { const version: WorkflowVersion = this.selectedVersion; - if (version && this.isWdl(workflowRef)) { + if (version) { this.workflowsService.secondaryWdl(workflowRef.id, version.name).subscribe((sourceFiles: Array) => { if (!sourceFiles || sourceFiles.length === 0) { - this.fireCloudURL = `${Dockstore.FIRECLOUD_IMPORT_URL}/${workflowRef.full_workflow_path}:${version.name}`; + this.workflowsService.wdl(workflowRef.id, version.name).subscribe((sourceFile) => { + this.wdlHasHttpImports = importHttpRegEx.test(sourceFile.content); + }); + if (this.enableLaunchWithFireCloud) { + this.fireCloudURL = `${Dockstore.FIRECLOUD_IMPORT_URL}/${workflowRef.full_workflow_path}:${version.name}`; + } + } else { + this.wdlHasFileImports = true; } }); } @@ -142,7 +174,7 @@ export class WorkflowComponent extends Entry { this.title = this.workflow.full_workflow_path; this.initTool(); this.sortedVersions = this.getSortedVersions(this.workflow.workflowVersions, this.defaultVersion); - this.setupFireCloudUrl(this.workflow); + this.checkWdlForImportsAndSetFirecloudUrl(this.workflow); } } @@ -153,7 +185,7 @@ export class WorkflowComponent extends Entry { if (workflow) { this.published = this.workflow.is_published; this.selectedVersion = this.selectVersion(this.workflow.workflowVersions, this.urlVersion, - this.workflow.defaultVersion, this.selectedVersion); + this.workflow.defaultVersion); } this.setUpWorkflow(workflow); } @@ -173,7 +205,7 @@ export class WorkflowComponent extends Entry { .subscribe(workflow => { this.workflowService.setWorkflow(workflow); this.selectedVersion = this.selectVersion(this.workflow.workflowVersions, this.urlVersion, - this.workflow.defaultVersion, this.selectedVersion); + this.workflow.defaultVersion); this.selectTab(this.validTabs.indexOf(this.currentTab)); if (this.workflow != null) { @@ -297,7 +329,7 @@ export class WorkflowComponent extends Entry { if (this.workflow != null) { this.updateUrl(this.workflow.full_workflow_path, 'my-workflows', 'workflows'); } - this.setupFireCloudUrl(this.workflow); + this.checkWdlForImportsAndSetFirecloudUrl(this.workflow); } setEntryTab(tabName: string): void {