Skip to content

Commit 9177462

Browse files
author
Ziroli Plutschow
committed
First version of shacl validation of live handle record
1 parent e4e430b commit 9177462

File tree

5 files changed

+95
-8
lines changed

5 files changed

+95
-8
lines changed

src/integration-test/java/org/nanopub/TheseTestsRequireOtherSystemsIT.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.eclipse.rdf4j.rio.RDFFormat;
66
import org.eclipse.rdf4j.rio.RDFWriter;
77
import org.eclipse.rdf4j.rio.Rio;
8+
import org.junit.Assert;
89
import org.junit.jupiter.api.Test;
910
import org.nanopub.extra.security.SignNanopub;
1011
import org.nanopub.extra.security.SignatureAlgorithm;
@@ -80,11 +81,19 @@ void retrieveMetadataFromHandleSystem() throws URISyntaxException, IOException,
8081
// it would be great to compare all statements here
8182
}
8283

83-
// @Test // TODO
84-
void validateFdo () throws URISyntaxException, IOException, InterruptedException, MalformedNanopubException {
85-
String id = "21.T11967/39b0ec87d17a4856c5f7";
84+
@Test
85+
void validateValidFdo() throws URISyntaxException, IOException, InterruptedException, MalformedNanopubException {
86+
String id = "21.T11966/82045bd97a0acce88378";
8687
FdoMetadata metadata = RetrieveFdo.retrieveMetadataFromId(id);
87-
ValidateFdo.isValid(metadata);
88+
89+
Assert.assertTrue(ValidateFdo.isValid(metadata));
8890
}
8991

92+
@Test
93+
void validateInvalidFdo() throws URISyntaxException, IOException, InterruptedException, MalformedNanopubException {
94+
String id = "21.T11967/39b0ec87d17a4856c5f7";
95+
FdoMetadata metadata = RetrieveFdo.retrieveMetadataFromId(id);
96+
97+
Assert.assertFalse(ValidateFdo.isValid(metadata));
98+
}
9099
}

src/main/java/org/nanopub/fdo/FdoMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public String getSchemaUrl() {
8989
Value schemaEntry = tuples.get(vf.createIRI(FDO_TYPE_PREFIX + "21.T11966/JsonSchema"));
9090
if (schemaEntry != null) {
9191
// assume the entry looks like {"$ref": "https://the-url"}
92-
String url = schemaEntry.stringValue().substring(10, schemaEntry.stringValue().length() - 3);
92+
String url = schemaEntry.stringValue().substring(10, schemaEntry.stringValue().length() - 2);
9393
return url;
9494
}
9595
return null;

src/main/java/org/nanopub/fdo/ShaclValidator.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,26 @@ public static void main(String[] args) {
4747
}
4848
}
4949

50+
51+
private static void printStatements(Set<Statement> statements) {
52+
for (Statement s : statements) {
53+
System.out.println(s.getSubject().toString() + " " + s.getPredicate().toString() + " " + s.getObject().toString());
54+
}
55+
}
56+
5057
/**
5158
* Logs constraints validation to System.out
5259
*
5360
* @return true, iff the data respects the specification of the shape.
5461
*/
5562
public static boolean validateShacl(Set<Statement> shape, Set<Statement> data) {
63+
64+
// // Debug info
65+
// System.out.println("Validating Data ");
66+
// printStatements(data);
67+
// System.out.println("Against Schema ");
68+
// printStatements(shape);
69+
5670
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
5771
Repository repo = new SailRepository(shaclSail);
5872

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
package org.nanopub.fdo;
22

3+
import com.google.gson.Gson;
4+
import org.eclipse.rdf4j.model.IRI;
5+
import org.eclipse.rdf4j.model.Statement;
6+
import org.eclipse.rdf4j.model.ValueFactory;
7+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
8+
import org.eclipse.rdf4j.model.vocabulary.RDF;
39
import org.nanopub.MalformedNanopubException;
10+
import org.nanopub.fdo.rest.gson.ParsedSchemaResponse;
411

512
import java.io.IOException;
13+
import java.net.URI;
614
import java.net.URISyntaxException;
15+
import java.net.http.HttpClient;
16+
import java.net.http.HttpRequest;
17+
import java.net.http.HttpResponse;
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
21+
import static org.nanopub.fdo.FdoNanopubCreator.FDO_TYPE_PREFIX;
722

823
// TODO class that provides the Op.Validate operations.
924
// See https://fdo-connect.gitlab.io/ap1/architecture-documentation/main/operation-specification/
1025
public class ValidateFdo {
1126

27+
private static final ValueFactory vf = SimpleValueFactory.getInstance();
28+
29+
private static final IRI SHACL_MAX_COUNT = vf.createIRI("http://www.w3.org/ns/shacl#maxCount");
30+
private static final IRI SHACL_MIN_COUNT = vf.createIRI("http://www.w3.org/ns/shacl#minCount");
31+
private static final IRI SHACL_PATH = vf.createIRI("http://www.w3.org/ns/shacl#path");
32+
private static final IRI SHACL_TARGET = vf.createIRI("http://www.w3.org/ns/shacl#targetClass");
33+
private static final IRI SHACL_PROPERTY = vf.createIRI("http://www.w3.org/ns/shacl#property");
34+
private static final IRI SHACL_NODE_SHAPE = vf.createIRI("http://www.w3.org/ns/shacl#NodeShape");
35+
36+
private static final IRI TEMP_TYPE = vf.createIRI("https://w3id.org/kpxl/temptype");
37+
38+
private static HttpClient client = HttpClient.newHttpClient();
39+
1240
private ValidateFdo() {} // no instances allowed
1341

1442
// TODO Just a boolean as return value. Later probably an object that also includes errors/warnings.
@@ -17,11 +45,42 @@ public static boolean isValid(FdoMetadata fdoMetadata) throws MalformedNanopubEx
1745
String profileId = FdoUtils.extractHandle(fdoMetadata.getProfile());
1846
String schemaUrl = RetrieveFdo.retrieveMetadataFromHandle(profileId).getSchemaUrl();
1947

20-
// TODO get construct shacl from schema
21-
throw new RuntimeException("Not yet implemented");
48+
HttpRequest req = HttpRequest.newBuilder().GET().uri(new URI(schemaUrl)).build();
49+
HttpResponse<String> httpResponse = client.send(req, HttpResponse.BodyHandlers.ofString());
50+
51+
Set<Statement> shaclShape = createShaclShapeFromJson(httpResponse);
52+
Set<Statement> data = addTypeStatement(fdoMetadata);
2253

23-
// return ShaclValidator.validateShacl(shaclShape, fdoMetadata.getStatements());
54+
System.out.println("Validating FdoMetadata " + fdoMetadata.getId());
55+
System.out.println("Against Schema " + schemaUrl);
56+
57+
return ShaclValidator.validateShacl(shaclShape, data);
58+
}
59+
60+
private static Set<Statement> addTypeStatement(FdoMetadata fdoMetadata) {
61+
Set<Statement> data = fdoMetadata.getStatements();
62+
Statement first = data.toArray(new Statement[0])[0];
63+
data.add(vf.createStatement(first.getSubject(), RDF.TYPE, TEMP_TYPE));
64+
return data;
2465
}
2566

67+
public static Set<Statement> createShaclShapeFromJson(HttpResponse<String> httpResponse) {
68+
ParsedSchemaResponse r = new Gson().fromJson(httpResponse.body(), ParsedSchemaResponse.class);
69+
70+
Set<Statement> shaclShape = new HashSet<>();
71+
String SUBJ_PREFIX = "https://w3id.org/kpxl/shacl/temp/";
72+
int i = 0;
73+
for (String s: r.required) {
74+
i++;
75+
shaclShape.add(vf.createStatement(vf.createIRI(SUBJ_PREFIX+i), SHACL_MAX_COUNT, vf.createLiteral(1)));
76+
shaclShape.add(vf.createStatement(vf.createIRI(SUBJ_PREFIX+i), SHACL_MIN_COUNT, vf.createLiteral(1)));
77+
shaclShape.add(vf.createStatement(vf.createIRI(SUBJ_PREFIX+i), SHACL_PATH, vf.createIRI(FDO_TYPE_PREFIX + s)));
78+
shaclShape.add(vf.createStatement(vf.createIRI(SUBJ_PREFIX), SHACL_PROPERTY, vf.createIRI(SUBJ_PREFIX+i)));
79+
}
80+
shaclShape.add(vf.createStatement(FdoUtils.createIri(SUBJ_PREFIX), SHACL_TARGET, TEMP_TYPE));
81+
shaclShape.add(vf.createStatement(FdoUtils.createIri(SUBJ_PREFIX), RDF.TYPE, SHACL_NODE_SHAPE));
82+
83+
return shaclShape;
84+
}
2685

2786
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.nanopub.fdo.rest.gson;
2+
3+
public class ParsedSchemaResponse {
4+
public String[] required;
5+
}

0 commit comments

Comments
 (0)