ShaclValidationReportIT.java
/*******************************************************************************
* Copyright (c) 2020 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.http.server;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.rdf4j.common.exception.ValidationException;
import org.eclipse.rdf4j.http.client.shacl.RemoteShaclValidationException;
import org.eclipse.rdf4j.http.protocol.Protocol;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.model.vocabulary.SHACL;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.RepositoryException;
import org.eclipse.rdf4j.repository.http.HTTPRepository;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ShaclValidationReportIT {
private static TestServer server;
private static final ValueFactory vf = SimpleValueFactory.getInstance();
@BeforeAll
public static void startServer() throws Exception {
server = new TestServer();
try {
server.start();
} catch (Exception e) {
server.stop();
throw e;
}
}
@AfterAll
public static void stopServer() throws Exception {
server.stop();
}
String shacl = "@base <http://example.com/ns> .\n" +
"@prefix ex: <http://example.com/ns#> .\n" +
"@prefix owl: <http://www.w3.org/2002/07/owl#> .\n" +
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n" +
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n" +
"@prefix sh: <http://www.w3.org/ns/shacl#> .\n" +
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n" +
"\n" +
"ex:PersonShape\n" +
"\ta sh:NodeShape ;\n" +
"\tsh:targetClass rdfs:Resource ;\n" +
"\tsh:property _:bnode .\n" +
"\n" +
"\n" +
"_:bnode\n" +
" sh:path rdfs:label ;\n" +
" rdfs:label \"abc\" ;\n" +
" sh:minCount 1 .";
@Test
public void testSparqlUpdate() throws IOException {
Repository systemRepo = new HTTPRepository(
Protocol.getRepositoryLocation(TestServer.SERVER_URL, TestServer.TEST_SHACL_REPO_ID));
try (RepositoryConnection connection = systemRepo.getConnection()) {
connection.begin();
connection.add(new StringReader(shacl), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
try {
connection.begin();
connection.prepareUpdate("INSERT DATA {[] a rdfs:Resource}").execute();
connection.commit();
fail();
} catch (Throwable e) {
assertExceptionIsShaclReport(e);
}
}
}
private void assertExceptionIsShaclReport(Throwable e) {
ValidationException remoteShaclSailValidationException = null;
if (e instanceof ValidationException) {
remoteShaclSailValidationException = (ValidationException) e;
} else if (e.getCause() instanceof ValidationException) {
remoteShaclSailValidationException = (ValidationException) e.getCause();
}
assert remoteShaclSailValidationException != null;
assertTrue(remoteShaclSailValidationException.validationReportAsModel().contains(null, SHACL.CONFORMS, null));
}
@Test
public void testAddingData() throws IOException {
Repository systemRepo = new HTTPRepository(
Protocol.getRepositoryLocation(TestServer.SERVER_URL, TestServer.TEST_SHACL_REPO_ID));
try (RepositoryConnection connection = systemRepo.getConnection()) {
connection.begin();
connection.add(new StringReader(shacl), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
try {
connection.begin();
connection.add(vf.createBNode(), RDF.TYPE, RDFS.RESOURCE);
connection.commit();
fail();
} catch (Exception e) {
assertExceptionIsShaclReport(e);
}
}
}
@Test
public void testBlankNodeIdsPreserved() throws IOException {
Repository repository = new HTTPRepository(
Protocol.getRepositoryLocation(TestServer.SERVER_URL, TestServer.TEST_SHACL_REPO_ID));
try (RepositoryConnection connection = repository.getConnection()) {
connection.begin();
connection.add(new StringReader(shacl), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
}
try (RepositoryConnection connection = repository.getConnection()) {
connection.begin();
connection.add(RDFS.RESOURCE, RDF.TYPE, RDFS.RESOURCE);
connection.commit();
} catch (RepositoryException repositoryException) {
Model validationReport = ((RemoteShaclValidationException) repositoryException.getCause())
.validationReportAsModel();
BNode shapeBnode = (BNode) validationReport
.filter(null, SHACL.SOURCE_SHAPE, null)
.objects()
.stream()
.findAny()
.orElseThrow();
try (RepositoryConnection connection = repository.getConnection()) {
List<Statement> collect = connection
.getStatements(shapeBnode, null, null, RDF4J.SHACL_SHAPE_GRAPH)
.stream()
.collect(Collectors.toList());
Assertions.assertEquals(3, collect.size());
Value rdfsLabel = collect
.stream()
.filter(s -> s.getPredicate().equals(RDFS.LABEL))
.map(Statement::getObject)
.findAny()
.orElseThrow();
Assertions.assertEquals(Values.literal("abc"), rdfsLabel);
}
}
}
}