Skip to content

Consistent RDF permalinks with content negotiation #158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Aug 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0e6d8ba
Rename WorkflowRESTController -> WorkflowJSONController
MarkRobbo Aug 21, 2017
be1ac59
Add permalink controller method stubs
MarkRobbo Aug 21, 2017
cd5fdb3
Correct mime type for Research Object Bundles
MarkRobbo Aug 21, 2017
f4e327d
Add simple redirects to viewer and raw URL
MarkRobbo Aug 21, 2017
3410e79
Refactor and addition of RO bundle content negotiation
MarkRobbo Aug 21, 2017
258514b
Add image viewing through content negotiation
MarkRobbo Aug 21, 2017
4f7b740
Add basic RDF through content negotiation
MarkRobbo Aug 21, 2017
25ff4d2
Merge branch 'packed-workflows' into rdf-permalinks
MarkRobbo Aug 21, 2017
46683c7
RO bundle gets RDF model from local store
MarkRobbo Aug 21, 2017
64f5b51
Refactor tests
MarkRobbo Aug 21, 2017
3e756d3
Refactor to use Path instead of File objects
MarkRobbo Aug 22, 2017
ffd8f26
Merge remote-tracking branch 'origin/master' into rdf-permalinks
MarkRobbo Aug 22, 2017
ba8bd9f
Content negotiation via query parameters and use these permalinks in RO
MarkRobbo Aug 22, 2017
2dc657a
Fix resolving of static resources
MarkRobbo Aug 22, 2017
6f0250b
Update RDFS namespace
MarkRobbo Aug 22, 2017
7d27475
Add correct permalinks to overall retrievedFrom and ID
MarkRobbo Aug 22, 2017
03903b5
Keep HTTPServletResponse object within controller
MarkRobbo Aug 23, 2017
0e9e008
RO bundle permalink testing
MarkRobbo Aug 23, 2017
a5cbfe2
Create a version added by commit ID when branches move
MarkRobbo Aug 23, 2017
7bfc307
Test coverage for PermalinkController
MarkRobbo Aug 23, 2017
7bc2daf
Require jsonld-java 0.9.0 for writing using jena
MarkRobbo Aug 23, 2017
fb914f4
Test coverage for WorkflowController generic git
MarkRobbo Aug 23, 2017
e9880e5
Improve error handling
MarkRobbo Aug 24, 2017
67ea196
Add packed ID to workflow permalink
MarkRobbo Aug 24, 2017
74d43db
Merge branch 'master' into rdf-permalinks
stain Aug 24, 2017
a90cbdd
Fix merge conflict
MarkRobbo Aug 24, 2017
1f69623
Add missing slashes to visualisation permalink
MarkRobbo Aug 24, 2017
5f81d9a
Fix ordering of format parameter in workflow RO ID
MarkRobbo Aug 24, 2017
adb6b8c
Check if workflow already exists before creating during cache update
MarkRobbo Aug 24, 2017
92a7d06
Permalinks for graph names in SPARQL store
MarkRobbo Aug 24, 2017
acebcde
Change prefix from /v/git to /view/git
MarkRobbo Aug 25, 2017
0aeec6a
Test for permalinks in RO and RDF
MarkRobbo Aug 25, 2017
a062122
Fix use of null in tests
MarkRobbo Aug 25, 2017
bebabcf
No format URL when empty format is given
MarkRobbo Aug 25, 2017
5afde1e
Simplify SPARQL queries
MarkRobbo Aug 25, 2017
ac24cee
Fix commit ID used in test RDF file
MarkRobbo Aug 25, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
<artifactId>jena-osgi</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.github.jsonld-java</groupId>
<artifactId>jsonld-java</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/org/commonwl/view/GlobalControllerErrorHandling.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.commonwl.view;

import org.commonwl.view.workflow.RepresentationNotFoundException;
import org.commonwl.view.workflow.WorkflowNotFoundException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
* Handles exception handling across the application
*/
@ControllerAdvice
public class GlobalControllerErrorHandling {

/**
* Workflow can not be found
* @return A plain text error message
*/
@ExceptionHandler(WorkflowNotFoundException.class)
public ResponseEntity<?> handleNotFound() {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity<>("Workflow could not be found", headers, HttpStatus.NOT_FOUND);
}

/**
* Workflow exists but representation is not found
* eg Generic git workflow asking for raw workflow URL
* @return A plain text error message
*/
@ExceptionHandler(RepresentationNotFoundException.class)
public ResponseEntity<?> handleNoRepresentation() {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity<>("The workflow exists but the requested representation could not be found",
headers, HttpStatus.NOT_FOUND);
}

}
52 changes: 52 additions & 0 deletions src/main/java/org/commonwl/view/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.commonwl.view;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import static org.springframework.http.MediaType.parseMediaType;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

/**
* Allows the use of the format query parameter to be used
* instead of the Accept HTTP header
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorParameter(true)
.mediaType("html", MediaType.TEXT_HTML)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("turtle", parseMediaType("text/turtle"))
.mediaType("jsonld", parseMediaType("application/ld+json"))
.mediaType("rdfxml", parseMediaType("application/rdf+xml"))
.mediaType("svg", parseMediaType("image/svg+xml"))
.mediaType("png", MediaType.IMAGE_PNG)
.mediaType("ro", parseMediaType("application/vnd.wf4ever.robundle+zip"))
.mediaType("zip", parseMediaType("application/zip"))
.mediaType("dot", parseMediaType("text/vnd+graphviz"))
.mediaType("yaml", parseMediaType("text/x-yaml"))
.mediaType("raw", MediaType.APPLICATION_OCTET_STREAM);
}
}
39 changes: 21 additions & 18 deletions src/main/java/org/commonwl/view/cwl/CWLService.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

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

// Check file size limit before parsing
long fileSizeBytes = workflowFile.length();
long fileSizeBytes = Files.size(workflowFile);
if (fileSizeBytes <= singleFileSizeLimit) {

// Parse file as yaml
JsonNode cwlFile = yamlStringToJson(readFileToString(workflowFile));
JsonNode cwlFile = yamlStringToJson(readFileToString(workflowFile.toFile()));

// Check packed workflow occurs
if (packedWorkflowId != null) {
Expand Down Expand Up @@ -199,7 +201,7 @@ public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId)
// Use filename for label if there is no defined one
String label = extractLabel(cwlFile);
if (label == null) {
label = FilenameUtils.getName(workflowFile.getPath());
label = workflowFile.getFileName().toString();
}

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

} else {
throw new IOException("File '" + workflowFile.getName() + "' is over singleFileSizeLimit - " +
throw new IOException("File '" + workflowFile.getFileName() + "' is over singleFileSizeLimit - " +
FileUtils.byteCountToDisplaySize(fileSizeBytes) + "/" +
FileUtils.byteCountToDisplaySize(singleFileSizeLimit));
}
Expand All @@ -235,29 +237,32 @@ public Workflow parseWorkflowNative(File workflowFile, String packedWorkflowId)
* @return The constructed workflow object
*/
public Workflow parseWorkflowWithCwltool(Workflow basicModel,
File workflowFile) throws CWLValidationException {
Path workflowFile,
Path workTree) throws CWLValidationException {
GitDetails gitDetails = basicModel.getRetrievedFrom();
String latestCommit = basicModel.getLastCommit();
String packedWorkflowID = gitDetails.getPackedId();

// Get paths to workflow
String url = gitDetails.getUrl(latestCommit).replace("https://", "");
String localPath = workflowFile.toPath().toAbsolutePath().toString();
String url = basicModel.getPermalink();
String localPath = workflowFile.toAbsolutePath().toString();
String gitPath = gitDetails.getPath();
if (packedWorkflowID != null) {
if (packedWorkflowID.charAt(0) != '#') {
localPath += "#";
url += "#";
gitPath += "#";
}
localPath += packedWorkflowID;
url += packedWorkflowID;
gitPath += packedWorkflowID;
}

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

// Create a workflow model from RDF representation
Model model = ModelFactory.createDefaultModel();
Expand All @@ -270,7 +275,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
// Base workflow details
String label = FilenameUtils.getName(url);
String doc = null;
ResultSet labelAndDoc = rdfService.getLabelAndDoc(gitPath, url);
ResultSet labelAndDoc = rdfService.getLabelAndDoc(url);
if (labelAndDoc.hasNext()) {
QuerySolution labelAndDocSoln = labelAndDoc.nextSolution();
if (labelAndDocSoln.contains("label")) {
Expand All @@ -283,7 +288,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,

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

// Outputs
Map<String, CWLElement> wfOutputs = new HashMap<>();
ResultSet outputs = rdfService.getOutputs(gitPath, url);
ResultSet outputs = rdfService.getOutputs(url);
while (outputs.hasNext()) {
QuerySolution output = outputs.nextSolution();
CWLElement wfOutput = new CWLElement();
Expand Down Expand Up @@ -355,7 +360,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,

// Steps
Map<String, CWLStep> wfSteps = new HashMap<>();
ResultSet steps = rdfService.getSteps(gitPath, url);
ResultSet steps = rdfService.getSteps(url);
while (steps.hasNext()) {
QuerySolution step = steps.nextSolution();
String uri = rdfService.stepNameFromURI(gitPath, step.get("step").toString());
Expand All @@ -376,9 +381,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
// Add new step
CWLStep wfStep = new CWLStep();

IRI wfStepUri = iriFactory.construct(step.get("wf").asResource().getURI());
IRI workflowPath = wfStepUri.resolve("./");

IRI workflowPath = iriFactory.construct(url).resolve("./");
IRI runPath = iriFactory.construct(step.get("run").asResource().getURI());
wfStep.setRun(workflowPath.relativize(runPath).toString());
wfStep.setRunType(rdfService.strToRuntype(step.get("runtype").toString()));
Expand Down Expand Up @@ -409,7 +412,7 @@ public Workflow parseWorkflowWithCwltool(Workflow basicModel,
}

// Docker link
ResultSet dockerResult = rdfService.getDockerLink(gitPath, url);
ResultSet dockerResult = rdfService.getDockerLink(url);
String dockerLink = null;
if (dockerResult.hasNext()) {
QuerySolution docker = dockerResult.nextSolution();
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/org/commonwl/view/cwl/CWLToolRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@

package org.commonwl.view.cwl;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Date;

import org.apache.jena.query.QueryException;
import org.commonwl.view.git.GitDetails;
import org.commonwl.view.git.GitSemaphore;
Expand All @@ -40,6 +36,10 @@
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Date;

/**
* Replace existing workflow with the one given by cwltool
*/
Expand Down Expand Up @@ -85,7 +85,8 @@ public void createWorkflowFromQueued(QueuedWorkflow queuedWorkflow)
Path workflowFile = localPath.resolve(gitInfo.getPath()).normalize().toAbsolutePath();
Workflow newWorkflow = cwlService.parseWorkflowWithCwltool(
tempWorkflow,
workflowFile.toFile());
workflowFile,
localPath);

// Success
newWorkflow.setRetrievedFrom(tempWorkflow.getRetrievedFrom());
Expand Down
Loading