Skip to content

Commit d0f4dac

Browse files
authored
Merge pull request #198 from common-workflow-language/show-license
Show workflow license
2 parents bc441d5 + 0fe02e8 commit d0f4dac

File tree

11 files changed

+145
-33
lines changed

11 files changed

+145
-33
lines changed

src/main/java/org/commonwl/view/cwl/CWLService.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import java.io.File;
2626
import java.io.IOException;
2727
import java.io.StringWriter;
28+
import java.net.URI;
2829
import java.nio.file.Files;
2930
import java.nio.file.Path;
31+
import java.nio.file.Paths;
3032
import java.util.ArrayList;
33+
import java.util.Arrays;
3134
import java.util.HashMap;
3235
import java.util.Iterator;
3336
import java.util.List;
@@ -211,7 +214,7 @@ public Workflow parseWorkflowNative(Path workflowFile, String packedWorkflowId)
211214

212215
// Construct the rest of the workflow model
213216
Workflow workflowModel = new Workflow(label, extractDoc(cwlFile), getInputs(cwlFile),
214-
getOutputs(cwlFile), getSteps(cwlFile), null);
217+
getOutputs(cwlFile), getSteps(cwlFile));
215218

216219
workflowModel.setCwltoolVersion(cwlTool.getVersion());
217220

@@ -251,7 +254,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
251254
// Get paths to workflow
252255
String url = basicModel.getIdentifier();
253256
String workflowFileURI = workflowFile.toAbsolutePath().toUri().toString();
254-
String workTreeUri = workTree.toAbsolutePath().toUri().toString();
257+
URI workTreeUri = workTree.toAbsolutePath().toUri();
255258
String localPath = workflowFileURI;
256259
String gitPath = gitDetails.getPath();
257260
if (packedWorkflowID != null) {
@@ -268,7 +271,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
268271
String rdf = cwlTool.getRDF(localPath);
269272
// Replace /tmp/123123 with permalink base
270273
// NOTE: We do not just replace workflowFileURI, all referenced files will also get rewritten
271-
rdf = rdf.replace(workTreeUri,
274+
rdf = rdf.replace(workTreeUri.toString(),
272275
"https://w3id.org/cwl/view/git/" + latestCommit + "/");
273276
// Workaround for common-workflow-language/cwltool#427
274277
rdf = rdf.replace("<rdfs:>", "<http://www.w3.org/2000/01/rdf-schema#>");
@@ -419,6 +422,22 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
419422
wfSteps.put(rdfService.labelFromName(uri), wfStep);
420423
}
421424
}
425+
// Try to determine license
426+
ResultSet licenseResult = rdfService.getLicense(url);
427+
String licenseLink = null;
428+
if (licenseResult.hasNext()) {
429+
licenseLink = licenseResult.next().get("license").toString();
430+
} else {
431+
// Check for "LICENSE"-like files in root of git repo
432+
for (String licenseCandidate : new String[]{"LICENSE", "LICENSE.txt", "LICENSE.md"}) {
433+
// FIXME: This might wrongly match lower-case "license.txt" in case-insensitive file systems
434+
// but the URL would not work
435+
if (Files.isRegularFile(workTree.resolve(licenseCandidate))) {
436+
// Link to it by raw URL
437+
licenseLink = basicModel.getRetrievedFrom().getRawUrl(null, licenseCandidate);
438+
}
439+
}
440+
}
422441

423442
// Docker link
424443
ResultSet dockerResult = rdfService.getDockerLink(url);
@@ -434,7 +453,8 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
434453

435454
// Create workflow model
436455
Workflow workflowModel = new Workflow(label, doc,
437-
wfInputs, wfOutputs, wfSteps, dockerLink);
456+
wfInputs, wfOutputs, wfSteps,
457+
dockerLink, licenseLink);
438458

439459
// Generate DOT graph
440460
StringWriter graphWriter = new StringWriter();

src/main/java/org/commonwl/view/cwl/RDFService.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public class RDFService {
3636
private final String queryCtx = "PREFIX cwl: <https://w3id.org/cwl/cwl#>\n" +
3737
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
3838
"PREFIX sld: <https://w3id.org/cwl/salad#>\n" +
39+
"PREFIX dct: <http://purl.org/dc/terms/>\n" +
40+
"PREFIX doap: <http://usefulinc.com/ns/doap#>\n" +
3941
"PREFIX Workflow: <https://w3id.org/cwl/cwl#Workflow/>\n" +
4042
"PREFIX DockerRequirement: <https://w3id.org/cwl/cwl#DockerRequirement/>\n" +
4143
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" +
@@ -431,4 +433,20 @@ ResultSet runQuery(ParameterizedSparqlString queryString) {
431433
}
432434
}
433435

436+
public ResultSet getLicense(String workflowURI) {
437+
ParameterizedSparqlString licenseQuery = new ParameterizedSparqlString();
438+
licenseQuery.setCommandText(queryCtx +
439+
"SELECT ?license \n" +
440+
"WHERE {\n" +
441+
" GRAPH ?wf {" +
442+
" ?wf rdf:type cwl:Workflow .\n" +
443+
" { ?wf s:license ?license } \n" +
444+
"UNION { ?wf doap:license ?license } \n" +
445+
"UNION { ?wf dct:license ?license } \n" +
446+
" }" +
447+
"}");
448+
licenseQuery.setIri("wf", workflowURI);
449+
return runQuery(licenseQuery);
450+
}
451+
434452
}

src/main/java/org/commonwl/view/git/GitDetails.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,23 +171,39 @@ public String getInternalUrl() {
171171

172172
/**
173173
* Get the URL directly to the resource
174-
* @param branchOverride The branch to use instead of the one in this instance
174+
* @param branchOverride The branch to use, or <code>null</code> to use this instance's branch
175+
* @param pathOverride The path to use, or <code>null</code> to use this instance's path
175176
* @return The URL
176177
*/
177-
public String getRawUrl(String branchOverride) {
178+
public String getRawUrl(String branchOverride, String pathOverride) {
179+
if (branchOverride == null) {
180+
branchOverride = branch;
181+
}
182+
if (pathOverride == null) {
183+
pathOverride = path;
184+
}
178185
switch (getType()) {
179-
case GITHUB:
180-
return "https://raw.githubusercontent.com/" +
181-
normaliseUrl(repoUrl).replace("github.com/", "").replace(".git", "") +
182-
"/" + branchOverride + "/" + path;
183-
case GITLAB:
184-
case BITBUCKET:
185-
return "https://" + normaliseUrl(repoUrl).replace(".git", "")
186-
+ "/raw/" + branchOverride + "/" + path;
187-
default:
188-
return repoUrl;
186+
case GITHUB:
187+
return "https://raw.githubusercontent.com/" +
188+
normaliseUrl(repoUrl).replace("github.com/", "").replace(".git", "") +
189+
"/" + branchOverride + "/" + pathOverride;
190+
case GITLAB:
191+
case BITBUCKET:
192+
return "https://" + normaliseUrl(repoUrl).replace(".git", "")
193+
+ "/raw/" + branchOverride + "/" + pathOverride;
194+
default:
195+
return repoUrl;
189196
}
190197
}
198+
199+
/**
200+
* Get the URL directly to the resource
201+
* @param branchOverride The branch to use instead of the one in this instance
202+
* @return The URL
203+
*/
204+
public String getRawUrl(String branchOverride) {
205+
return getRawUrl(branchOverride, path);
206+
}
191207

192208
/**
193209
* Get the URL directly to the resource

src/main/java/org/commonwl/view/workflow/Workflow.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,29 @@ public class Workflow {
7979

8080
private final String permaLinkBase = "https://w3id.org/cwl/view";
8181

82+
private String licenseLink;
83+
8284
public Workflow(String label, String doc, Map<String, CWLElement> inputs,
83-
Map<String, CWLElement> outputs, Map<String, CWLStep> steps, String dockerLink) {
85+
Map<String, CWLElement> outputs, Map<String, CWLStep> steps, String dockerLink, String licenseLink) {
8486
this.label = label;
8587
this.doc = doc;
8688
this.inputs = inputs;
8789
this.outputs = outputs;
8890
this.steps = steps;
8991
this.dockerLink = dockerLink;
92+
this.licenseLink = licenseLink;
9093
}
9194

92-
public String getID() { return id; }
95+
public Workflow(String label, String doc, Map<String, CWLElement> inputs,
96+
Map<String, CWLElement> outputs, Map<String, CWLStep> steps) {
97+
this(label, doc, inputs, outputs, steps, null, null);
98+
}
99+
100+
public Workflow() {
101+
this(null, null, null, null,null, null, null);
102+
}
103+
104+
public String getID() { return id; }
93105

94106
public void setId(String id) {
95107
this.id = id;
@@ -270,4 +282,12 @@ public boolean isPacked() {
270282
return retrievedFrom.getPackedId() != null;
271283
}
272284

285+
public String getLicenseLink() {
286+
return licenseLink;
287+
}
288+
289+
public void setLicenseLink(String licenseLink) {
290+
this.licenseLink = licenseLink;
291+
}
292+
273293
}

src/main/resources/templates/about.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ <h2>Data controller</h2>
138138
<a href="https://view.commonwl.org/">https://view.commonwl.org/</a> instance of CWL Viewer
139139
is The University of Manchester, contact
140140
<a href="javascript:window.location.href=atob('bWFpbHRvOmluZm9AZXNjaWVuY2VsYWIub3JnLnVr')">
141-
<code><span class="obf-hide">NOT</span>info<span class="obf-at"></span><span class="obf-hide">()</span><span class="obf-hide">NONE</span>esciencelab.org.uk</code></a> for any enquiries.
141+
<code><span class="obf-hide" aria-hidden="true">NOT</span>info<span class="obf-at"></span><span class="obf-hide" aria-hidden="true">()</span><span class="obf-hide" aria-hidden="true">NONE</span>esciencelab.org.uk</code></a> for any enquiries.
142142
If this viewer is accessed at a different URL, the data controller is likely someone else.
143143
</p>
144144
<p>
145-
<span class="obf-hide">(sorry non-css-non-JS folks, manual de-obfuscation needed above)</span>
145+
<span class="obf-hide" aria-hidden="true">(manual de-obfuscation needed above)</span>
146146
You may contact the Data Controller to request access to data about yourself,
147147
or to request update or removal of your personally identifiable information.
148148
You may also want to follow the <em>Shown Workflow</em> link at the bottom

src/main/resources/templates/workflow.html

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ <h2>Workflow: <span th:text="${workflow.label}">Workflow Name</span></h2>
144144
<div class="col-md-6 text-right hidden-xs">
145145
<img class="verification_icon" src="../static/img/tick.svg" th:src="@{/img/tick.svg}" width="20" height="22" /> Verified with cwltool version <samp th:text="${workflow.cwltoolVersion}">1.0.20170622090721</samp>
146146
</div>
147-
147+
148148
<div class="col-md-12" style="margin-top:5px;" th:if="${workflow.doc != null}">
149149
<p id="workflow-doc" th:text="${workflow.doc}">This workflow documentation explains the purpose of the workflow and the main techniques used. Steps are documented individually further down. This is just an example documentation for the template, real documentation might be even longer than this!</p>
150150
</div>
@@ -157,8 +157,8 @@ <h2>Workflow: <span th:text="${workflow.label}">Workflow Name</span></h2>
157157
<a id="permalink" th:href="${workflow.permalink}" th:text="${workflow.permalink}"
158158
href="#">https://w3id.org/cwl/view/git/933bf2a1a1cce32d88f88f136275535da9df0954/workflows/larger/test-hello.cwl</a>
159159
</div>
160-
161-
</div>
160+
161+
</div>
162162
<div class="col-lg-3 col-md-4 col-sm-6 hidden-xs">
163163
<div id="graph-menu hidden-print" class="pull-right">
164164
<button id="view-dot" class="hidden-print hidden-sm-down btn btn-primary" type="button" data-toggle="modal" data-target="#dotGraph">View DOT</button>
@@ -198,6 +198,44 @@ <h2 style="float:left;">Requires: </h2>
198198
</a>
199199
<img th:if="${workflow.dockerLink == 'true'}" id="dockerLogo" th:src="@{/img/Docker-logo.png}" src="../static/img/Docker-logo.png" alt="docker logo" />
200200
</div>
201+
<div th:if="${workflow.licenseLink != null}" class="alert alert-success" role="alert">
202+
<span class="hidden-xs">This workflow is Open Source and may be reused according to the terms of:</span>
203+
<a href="http://example.com/" th:href="@{${workflow.licenseLink}}" class="alert-link">
204+
<span th:remove="tag" th:switch="${workflow.licenseLink}">
205+
<!-- TODO: Move license 'registry' to controller? -->
206+
<span th:remove="tag" th:case="'https://www.apache.org/licenses/LICENSE-2.0'">
207+
Apache License, version 2.0
208+
</span>
209+
<span th:remove="tag" th:case="'http://www.apache.org/licenses/LICENSE-2.0'">
210+
Apache License, version 2.0
211+
</span>
212+
<span th:remove="tag" th:case="'https://spdx.org/licenses/Apache-2.0'">
213+
Apache License, version 2.0
214+
</span>
215+
<span th:remove="tag" th:case="'https://mit-license.org/'">
216+
MIT License
217+
</span>
218+
<span th:remove="tag" th:case="'https://spdx.org/licenses/AGPL-3.0'">
219+
GNU Affero General Public License v3.0
220+
</span>
221+
<span th:remove="tag" th:case="'http://www.gnu.org/licenses/agpl.txt'">
222+
GNU Affero General Public License
223+
</span>
224+
<span th:remove="tag" th:case="'http://www.opensource.org/licenses/AGPL-3.0'">
225+
GNU Affero General Public License v3.0
226+
</span>
227+
<div th:remove="tag" th:case="*" th:text="${workflow.licenseLink}">
228+
<!-- FIXME: This may not be an open source license! -->
229+
http://example.com/LICENSE
230+
</div>
231+
</span>
232+
</a>
233+
<div class="hidden-xs"><small>Note that the <em>tools</em> invoked by the workflow may have separate licenses.</small></div>
234+
</div>
235+
<div th:unless="${workflow.licenseLink}" class="alert alert-warning" role="alert">
236+
Unknown workflow license, check
237+
<a th:href="@{${workflow.retrievedFrom.getUrl()}}" href="#" rel="noopener" target="_blank">source repository</a>.
238+
</div>
201239
<h2>Inputs</h2>
202240
<div th:if="${workflow.inputs.isEmpty()}" class="alert alert-info">
203241
<p>There are no inputs in this workflow</p>
@@ -295,7 +333,7 @@ <h2>Outputs</h2>
295333
</div>
296334
<div class="row hidden-print">
297335
<div class="col-md-12 text-center" id="formats">
298-
<span th:each="format : ${formats}">
336+
<span th:each="format : ${formats}">
299337
<a th:id="|format-${format}|" role="button" class="btn btn-default btn-sm" th:href="${workflow.getPermalink(format.name())}" th:text="${format}"
300338
href="#">html</a>
301339
</span>
@@ -305,7 +343,7 @@ <h2>Outputs</h2>
305343
<address>Permalink:
306344
<code th:text="${workflow.permalink}">https://w3id.org/cwl/view/git/933bf2a1a1cce32d88f88f136275535da9df0954/workflows/larger/test-hello.cwl</code>
307345
</address>
308-
</div>
346+
</div>
309347
</div>
310348

311349
<div th:replace="fragments/footer :: copy"></div>

src/test/java/org/commonwl/view/cwl/CWLServiceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public void parseWorkflowWithCwltool() throws Exception {
140140

141141
GitDetails gitInfo = new GitDetails("https://github.com/common-workflow-language/workflows.git",
142142
"549c973ccc01781595ce562dea4cedc6c9540fe0", "workflows/make-to-cwl/dna.cwl");
143-
Workflow basicModel = new Workflow(null, null, null, null, null, null);
143+
Workflow basicModel = new Workflow();
144144
basicModel.setRetrievedFrom(gitInfo);
145145
gitInfo.setPackedId("main");
146146
basicModel.setLastCommit("549c973ccc01781595ce562dea4cedc6c9540fe0");

src/test/java/org/commonwl/view/graphviz/ModelDotWriterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void setUp() throws Exception {
9191
outputs.put("output", output);
9292

9393
// Save workflow model
94-
testWorkflow = new Workflow("Example Workflow", "Description", inputs, outputs, steps, null);
94+
testWorkflow = new Workflow("Example Workflow", "Description", inputs, outputs, steps);
9595
}
9696

9797
/**

src/test/java/org/commonwl/view/researchobject/ROBundleFactoryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class ROBundleFactoryTest {
4444
public void bundleForValidWorkflow() throws Exception {
4545

4646
Workflow validWorkflow = new Workflow("Valid Workflow", "Doc for Valid Workflow",
47-
new HashMap<>(), new HashMap<>(), new HashMap<>(), null);
47+
new HashMap<>(), new HashMap<>(), new HashMap<>());
4848
validWorkflow.setRetrievedFrom(Mockito.mock(GitDetails.class));
4949

5050
// Mocked path to a RO bundle

src/test/java/org/commonwl/view/workflow/WorkflowJSONControllerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public void newWorkflowFromGithubURLJson() throws Exception {
151151
@Test
152152
public void getWorkflowByGithubDetailsJson() throws Exception {
153153

154-
Workflow workflow1 = new Workflow("label", "doc", null, null, null, null);
154+
Workflow workflow1 = new Workflow("label", "doc", null, null, null);
155155
workflow1.setRetrievedFrom(new GitDetails("https://github.com/owner/repo.git",
156156
"branch", "path/to/workflow.cwl"));
157157

@@ -198,7 +198,7 @@ public void checkQueue() throws Exception {
198198

199199
QueuedWorkflow qwfSuccess = new QueuedWorkflow();
200200
qwfSuccess.setCwltoolStatus(CWLToolStatus.SUCCESS);
201-
Workflow wfSuccess = new Workflow(null, null, null, null, null, null);
201+
Workflow wfSuccess = new Workflow(null, null, null, null, null);
202202
wfSuccess.setRetrievedFrom(new GitDetails("https://github.com/owner/repoName.git",
203203
"branch", "path/to/workflow.cwl"));
204204
qwfSuccess.setTempRepresentation(wfSuccess);

0 commit comments

Comments
 (0)