Skip to content

Commit

Permalink
fix: lenient version ingest (#1426)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Yalz <[email protected]>
  • Loading branch information
Yalz and Yalz authored Nov 22, 2024
1 parent e67c2f4 commit 072c00e
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,73 @@
import be.vlaanderen.informatievlaanderen.ldes.server.domain.model.EventStream;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.rdf.model.*;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;

import static org.apache.jena.rdf.model.ResourceFactory.createProperty;

@Order(2)
@Component
public class PathsValidator implements IngestReportValidator {

@Override
public void validate(Model model, EventStream eventStream, ShaclReportManager reportManager) {
List<Resource> memberSubjects = model.listSubjects().filterDrop(RDFNode::isAnon).toList();

if (memberSubjects.size() > 1 && !eventStream.isVersionCreationEnabled()) {
memberSubjects.forEach(subject -> reportManager.addEntry(subject,
"Only 1 member is allowed per request on collection with version creation disabled"
)
);
}
@Override
public void validate(Model model, EventStream eventStream, ShaclReportManager reportManager) {
List<Resource> memberSubjects = model.listSubjects()
.filterDrop(RDFNode::isAnon)
.toList();

validateTimestampPath(memberSubjects, model, eventStream, reportManager);
validateVersionOfPath(memberSubjects, model, eventStream, reportManager);
}
validateTimestampPath(memberSubjects, model, eventStream, reportManager);
validateVersionOfPath(memberSubjects, model, eventStream, reportManager);
}

private void validateTimestampPath(List<Resource> memberSubjects, Model model, EventStream eventStream, ShaclReportManager reportManager) {
int expectedNumber = eventStream.isVersionCreationEnabled() ? 0 : 1;
List<RDFDatatype> validTypes = List.of(XSDDatatype.XSDdateTime, XSDDatatype.XSDstring);
memberSubjects.forEach(subject -> {
List<Statement> timestampStatements = getStatementsOfPath(subject, model, eventStream.getTimestampPath());
if (timestampStatements.size() != expectedNumber) {
reportManager.addEntry(subject,
String.format(String.format("Member must have exactly %s statement%s with timestamp path: %s as predicate.", expectedNumber, expectedNumber == 1 ? "" : "s", eventStream.getTimestampPath()))
);
}
private void validateTimestampPath(List<Resource> memberSubjects, Model model, EventStream eventStream, ShaclReportManager reportManager) {
int expectedNumber = eventStream.isVersionCreationEnabled() ? 0 : 1;
List<RDFDatatype> validTypes = List.of(XSDDatatype.XSDdateTime, XSDDatatype.XSDstring);
memberSubjects.forEach(subject -> {
List<Statement> timestampStatements = getStatementsOfPath(subject, model, eventStream.getTimestampPath());
if (timestampStatements.size() != expectedNumber) {
reportManager.addEntry(subject,
String.format(String.format("Member must have exactly %s statement%s with timestamp path: %s as predicate.", expectedNumber, expectedNumber == 1 ? "" : "s", eventStream.getTimestampPath()))
);
}

timestampStatements.forEach(statement -> {
if (!statement.getObject().isLiteral() || !validTypes.contains(statement.getLiteral().getDatatype())) {
reportManager.addEntry(subject,
String.format(String.format("Object of statement with predicate: %s should be a literal either of type %s or %s", eventStream.getTimestampPath(), XSDDatatype.XSDdateTime.getURI(), XSDDatatype.XSDstring.getURI()))
);
}
});
});
}
timestampStatements.forEach(statement -> {
if (!statement.getObject().isLiteral() || !validTypes.contains(statement.getLiteral().getDatatype())) {
reportManager.addEntry(subject,
String.format(String.format("Object of statement with predicate: %s should be a literal either of type %s or %s", eventStream.getTimestampPath(), XSDDatatype.XSDdateTime.getURI(), XSDDatatype.XSDstring.getURI()))
);
}
});
});
}

private void validateVersionOfPath(List<Resource> memberSubjects, Model model, EventStream eventStream, ShaclReportManager reportManager) {
int expectedNumber = eventStream.isVersionCreationEnabled() ? 0 : 1;
memberSubjects.forEach(subject -> {
List<Statement> versionOfStatements = getStatementsOfPath(subject, model, eventStream.getVersionOfPath());
if (versionOfStatements.size() != expectedNumber) {
reportManager.addEntry(subject,
String.format("Member must have exactly %s statement%s with versionOf path: %s as predicate.", expectedNumber, expectedNumber == 1 ? "" : "s", eventStream.getVersionOfPath())
);
}
private void validateVersionOfPath(List<Resource> memberSubjects, Model model, EventStream eventStream, ShaclReportManager reportManager) {
int expectedNumber = eventStream.isVersionCreationEnabled() ? 0 : 1;
memberSubjects.forEach(subject -> {
List<Statement> versionOfStatements = getStatementsOfPath(subject, model, eventStream.getVersionOfPath());
if (versionOfStatements.size() != expectedNumber) {
reportManager.addEntry(subject,
String.format("Member must have exactly %s statement%s with versionOf path: %s as predicate.", expectedNumber, expectedNumber == 1 ? "" : "s", eventStream.getVersionOfPath())
);
}

versionOfStatements.forEach(statement -> {
if (!statement.getObject().isResource()) {
reportManager.addEntry(subject,
String.format("Object of statement with predicate: %s should be a resource", eventStream.getVersionOfPath())
);
}
});
});
}
versionOfStatements.forEach(statement -> {
if (!statement.getObject().isResource()) {
reportManager.addEntry(subject,
String.format("Object of statement with predicate: %s should be a resource", eventStream.getVersionOfPath())
);
}
});
});
}

private List<Statement> getStatementsOfPath(Resource memberSubject, Model model, String path) {
return model.listStatements(memberSubject, ResourceFactory.createProperty(path), (RDFNode) null).toList();
}
private List<Statement> getStatementsOfPath(Resource memberSubject, Model model, String path) {
return model.listStatements(memberSubject, createProperty(path), (RDFNode) null).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
requires org.apache.jena.arq;
requires micrometer.core;
requires micrometer.observation;
exports be.vlaanderen.informatievlaanderen.ldes.server.ingest.entities;
requires spring.core;
exports be.vlaanderen.informatievlaanderen.ldes.server.ingest.entities;
exports be.vlaanderen.informatievlaanderen.ldes.server.ingest.repositories;
exports be.vlaanderen.informatievlaanderen.ldes.server.ingest;
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ void when_POSTRequestIsPerformedWithMultipleNamedNodes_ThrowException() throws E
.content(ldesMemberBytes))
.andExpect(status().isBadRequest())
.andExpect(header().exists("X-App-Version"))
.andExpect(content().string(containsString("Only 1 member is allowed per request on collection with version creation disabled")));
.andExpect(content().string(containsString("Member must have exactly 1 statement with versionOf path")))
.andExpect(content().string(containsString("Member must have exactly 1 statement with timestamp path")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
List.of("Member must have exactly 1 statement with timestamp path: " + TIMESTAMP_PATH)),
Arguments.of("example-ldes-member-multiple-version-ofs.nq", VERSION,
List.of("Member must have exactly 1 statement with versionOf path: " + VERSIONOF_PATH)),
Arguments.of("example-ldes-member-without-version-of.nq", VERSION,
List.of("Member must have exactly 1 statement with versionOf path: " + VERSIONOF_PATH)),
Arguments.of("example-ldes-member-wrong-type-version-of.nq", VERSION,
List.of("Object of statement with predicate: " + VERSIONOF_PATH + " should be a resource")),
Arguments.of("example-ldes-member-without-version-of.nq", VERSION,
List.of("Member must have exactly 1 statement with versionOf path: " + VERSIONOF_PATH)),
Arguments.of("example-ldes-member-wrong-type-timestamp.nq", VERSION,
List.of("Object of statement with predicate: " + TIMESTAMP_PATH + " should be a literal either of type " + XSDDatatype.XSDdateTime.getURI() + " or " + XSDDatatype.XSDstring.getURI())),
Arguments.of("example-ldes-member-dangling-nodes.nq", VERSION, List.of("Object graphs don't allow blank nodes to occur outside of a named object.")),
Expand Down

0 comments on commit 072c00e

Please sign in to comment.