Skip to content

Commit 6196d34

Browse files
authored
Merge pull request #158 from common-workflow-language/rdf-permalinks
Consistent RDF permalinks with content negotiation
2 parents 7c99558 + ac24cee commit 6196d34

22 files changed

+952
-339
lines changed

pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@
9393
<artifactId>jena-osgi</artifactId>
9494
<version>3.3.0</version>
9595
</dependency>
96+
<dependency>
97+
<groupId>com.github.jsonld-java</groupId>
98+
<artifactId>jsonld-java</artifactId>
99+
<version>0.9.0</version>
100+
</dependency>
96101
<dependency>
97102
<groupId>org.eclipse.jgit</groupId>
98103
<artifactId>org.eclipse.jgit</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.commonwl.view;
21+
22+
import org.commonwl.view.workflow.RepresentationNotFoundException;
23+
import org.commonwl.view.workflow.WorkflowNotFoundException;
24+
import org.springframework.http.HttpHeaders;
25+
import org.springframework.http.HttpStatus;
26+
import org.springframework.http.MediaType;
27+
import org.springframework.http.ResponseEntity;
28+
import org.springframework.web.bind.annotation.ControllerAdvice;
29+
import org.springframework.web.bind.annotation.ExceptionHandler;
30+
31+
/**
32+
* Handles exception handling across the application
33+
*/
34+
@ControllerAdvice
35+
public class GlobalControllerErrorHandling {
36+
37+
/**
38+
* Workflow can not be found
39+
* @return A plain text error message
40+
*/
41+
@ExceptionHandler(WorkflowNotFoundException.class)
42+
public ResponseEntity<?> handleNotFound() {
43+
final HttpHeaders headers = new HttpHeaders();
44+
headers.setContentType(MediaType.TEXT_PLAIN);
45+
return new ResponseEntity<>("Workflow could not be found", headers, HttpStatus.NOT_FOUND);
46+
}
47+
48+
/**
49+
* Workflow exists but representation is not found
50+
* eg Generic git workflow asking for raw workflow URL
51+
* @return A plain text error message
52+
*/
53+
@ExceptionHandler(RepresentationNotFoundException.class)
54+
public ResponseEntity<?> handleNoRepresentation() {
55+
final HttpHeaders headers = new HttpHeaders();
56+
headers.setContentType(MediaType.TEXT_PLAIN);
57+
return new ResponseEntity<>("The workflow exists but the requested representation could not be found",
58+
headers, HttpStatus.NOT_FOUND);
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.commonwl.view;
21+
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.http.MediaType;
24+
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
25+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
26+
27+
import static org.springframework.http.MediaType.parseMediaType;
28+
29+
@Configuration
30+
public class WebConfig extends WebMvcConfigurerAdapter {
31+
32+
/**
33+
* Allows the use of the format query parameter to be used
34+
* instead of the Accept HTTP header
35+
*/
36+
@Override
37+
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
38+
configurer.favorParameter(true)
39+
.mediaType("html", MediaType.TEXT_HTML)
40+
.mediaType("json", MediaType.APPLICATION_JSON)
41+
.mediaType("turtle", parseMediaType("text/turtle"))
42+
.mediaType("jsonld", parseMediaType("application/ld+json"))
43+
.mediaType("rdfxml", parseMediaType("application/rdf+xml"))
44+
.mediaType("svg", parseMediaType("image/svg+xml"))
45+
.mediaType("png", MediaType.IMAGE_PNG)
46+
.mediaType("ro", parseMediaType("application/vnd.wf4ever.robundle+zip"))
47+
.mediaType("zip", parseMediaType("application/zip"))
48+
.mediaType("dot", parseMediaType("text/vnd+graphviz"))
49+
.mediaType("yaml", parseMediaType("text/x-yaml"))
50+
.mediaType("raw", MediaType.APPLICATION_OCTET_STREAM);
51+
}
52+
}

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

+21-18
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import java.io.File;
5353
import java.io.IOException;
5454
import java.io.StringWriter;
55+
import java.nio.file.Files;
56+
import java.nio.file.Path;
5557
import java.util.*;
5658

5759
import static org.apache.commons.io.FileUtils.readFileToString;
@@ -161,14 +163,14 @@ public List<WorkflowOverview> getWorkflowOverviewsFromPacked(File packedFile) th
161163
* @param packedWorkflowId The ID of the workflow object if the file is packed
162164
* @return The constructed workflow object
163165
*/
164-
public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId) throws IOException {
166+
public Workflow parseWorkflowNative(Path workflowFile, String packedWorkflowId) throws IOException {
165167

166168
// Check file size limit before parsing
167-
long fileSizeBytes = workflowFile.length();
169+
long fileSizeBytes = Files.size(workflowFile);
168170
if (fileSizeBytes <= singleFileSizeLimit) {
169171

170172
// Parse file as yaml
171-
JsonNode cwlFile = yamlStringToJson(readFileToString(workflowFile));
173+
JsonNode cwlFile = yamlStringToJson(readFileToString(workflowFile.toFile()));
172174

173175
// Check packed workflow occurs
174176
if (packedWorkflowId != null) {
@@ -199,7 +201,7 @@ public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId)
199201
// Use filename for label if there is no defined one
200202
String label = extractLabel(cwlFile);
201203
if (label == null) {
202-
label = FilenameUtils.getName(workflowFile.getPath());
204+
label = workflowFile.getFileName().toString();
203205
}
204206

205207
// Construct the rest of the workflow model
@@ -221,7 +223,7 @@ public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId)
221223
return workflowModel;
222224

223225
} else {
224-
throw new IOException("File '" + workflowFile.getName() + "' is over singleFileSizeLimit - " +
226+
throw new IOException("File '" + workflowFile.getFileName() + "' is over singleFileSizeLimit - " +
225227
FileUtils.byteCountToDisplaySize(fileSizeBytes) + "/" +
226228
FileUtils.byteCountToDisplaySize(singleFileSizeLimit));
227229
}
@@ -235,29 +237,32 @@ public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId)
235237
* @return The constructed workflow object
236238
*/
237239
public Workflow parseWorkflowWithCwltool(Workflow basicModel,
238-
File workflowFile) throws CWLValidationException {
240+
Path workflowFile,
241+
Path workTree) throws CWLValidationException {
239242
GitDetails gitDetails = basicModel.getRetrievedFrom();
240243
String latestCommit = basicModel.getLastCommit();
241244
String packedWorkflowID = gitDetails.getPackedId();
242245

243246
// Get paths to workflow
244-
String url = gitDetails.getUrl(latestCommit).replace("https://", "");
245-
String localPath = workflowFile.toPath().toAbsolutePath().toString();
247+
String url = basicModel.getPermalink();
248+
String localPath = workflowFile.toAbsolutePath().toString();
246249
String gitPath = gitDetails.getPath();
247250
if (packedWorkflowID != null) {
248251
if (packedWorkflowID.charAt(0) != '#') {
249252
localPath += "#";
250-
url += "#";
251253
gitPath += "#";
252254
}
253255
localPath += packedWorkflowID;
254-
url += packedWorkflowID;
255256
gitPath += packedWorkflowID;
256257
}
257258

258259
// Get RDF representation from cwltool
259260
if (!rdfService.graphExists(url)) {
260261
String rdf = cwlTool.getRDF(localPath);
262+
rdf = rdf.replace("file://" + workTree.toAbsolutePath().toString(),
263+
"https://w3id.org/cwl/view/git/" + latestCommit);
264+
// Workaround for common-workflow-language/cwltool#427
265+
rdf = rdf.replace("<rdfs:>", "<http://www.w3.org/2000/01/rdf-schema#>");
261266

262267
// Create a workflow model from RDF representation
263268
Model model = ModelFactory.createDefaultModel();
@@ -270,7 +275,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
270275
// Base workflow details
271276
String label = FilenameUtils.getName(url);
272277
String doc = null;
273-
ResultSet labelAndDoc = rdfService.getLabelAndDoc(gitPath, url);
278+
ResultSet labelAndDoc = rdfService.getLabelAndDoc(url);
274279
if (labelAndDoc.hasNext()) {
275280
QuerySolution labelAndDocSoln = labelAndDoc.nextSolution();
276281
if (labelAndDocSoln.contains("label")) {
@@ -283,7 +288,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
283288

284289
// Inputs
285290
Map<String, CWLElement> wfInputs = new HashMap<>();
286-
ResultSet inputs = rdfService.getInputs(gitPath, url);
291+
ResultSet inputs = rdfService.getInputs(url);
287292
while (inputs.hasNext()) {
288293
QuerySolution input = inputs.nextSolution();
289294
String inputName = rdfService.stepNameFromURI(gitPath, input.get("name").toString());
@@ -316,7 +321,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
316321

317322
// Outputs
318323
Map<String, CWLElement> wfOutputs = new HashMap<>();
319-
ResultSet outputs = rdfService.getOutputs(gitPath, url);
324+
ResultSet outputs = rdfService.getOutputs(url);
320325
while (outputs.hasNext()) {
321326
QuerySolution output = outputs.nextSolution();
322327
CWLElement wfOutput = new CWLElement();
@@ -355,7 +360,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
355360

356361
// Steps
357362
Map<String, CWLStep> wfSteps = new HashMap<>();
358-
ResultSet steps = rdfService.getSteps(gitPath, url);
363+
ResultSet steps = rdfService.getSteps(url);
359364
while (steps.hasNext()) {
360365
QuerySolution step = steps.nextSolution();
361366
String uri = rdfService.stepNameFromURI(gitPath, step.get("step").toString());
@@ -376,9 +381,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
376381
// Add new step
377382
CWLStep wfStep = new CWLStep();
378383

379-
IRI wfStepUri = iriFactory.construct(step.get("wf").asResource().getURI());
380-
IRI workflowPath = wfStepUri.resolve("./");
381-
384+
IRI workflowPath = iriFactory.construct(url).resolve("./");
382385
IRI runPath = iriFactory.construct(step.get("run").asResource().getURI());
383386
wfStep.setRun(workflowPath.relativize(runPath).toString());
384387
wfStep.setRunType(rdfService.strToRuntype(step.get("runtype").toString()));
@@ -409,7 +412,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
409412
}
410413

411414
// Docker link
412-
ResultSet dockerResult = rdfService.getDockerLink(gitPath, url);
415+
ResultSet dockerResult = rdfService.getDockerLink(url);
413416
String dockerLink = null;
414417
if (dockerResult.hasNext()) {
415418
QuerySolution docker = dockerResult.nextSolution();

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919

2020
package org.commonwl.view.cwl;
2121

22-
import java.io.IOException;
23-
import java.nio.file.Path;
24-
import java.util.Date;
25-
2622
import org.apache.jena.query.QueryException;
2723
import org.commonwl.view.git.GitDetails;
2824
import org.commonwl.view.git.GitSemaphore;
@@ -40,6 +36,10 @@
4036
import org.springframework.scheduling.annotation.EnableAsync;
4137
import org.springframework.stereotype.Component;
4238

39+
import java.io.IOException;
40+
import java.nio.file.Path;
41+
import java.util.Date;
42+
4343
/**
4444
* Replace existing workflow with the one given by cwltool
4545
*/
@@ -85,7 +85,8 @@ public void createWorkflowFromQueued(QueuedWorkflow queuedWorkflow)
8585
Path workflowFile = localPath.resolve(gitInfo.getPath()).normalize().toAbsolutePath();
8686
Workflow newWorkflow = cwlService.parseWorkflowWithCwltool(
8787
tempWorkflow,
88-
workflowFile.toFile());
88+
workflowFile,
89+
localPath);
8990

9091
// Success
9192
newWorkflow.setRetrievedFrom(tempWorkflow.getRetrievedFrom());

0 commit comments

Comments
 (0)