use of io.apicurio.datamodels.core.models.Document in project apicurio-data-models by Apicurio.
the class Library method validateDocument.
/**
* Called to validate a data model node. All validation rules will be evaluated and reported. The list
* of validation problems found during validation is returned. In addition, validation problems will be
* reported on the individual nodes themselves. Validation problem severity is determined by checking
* with the included severity registry. If the severity registry is null, a default registry is used.
* Custom validators can be passed to provide additional validation rules beyond what this Library offers out of the box.
*
* @param node The document to be validated
* @param severityRegistry Supply a custom severity registry. If nothing is passed, the default severity registry will be used
* @param extensions Supply an optional list of validation extensions, enabling the use of 3rd-party validators or custom validation rules
* @return full list of the validation problems found in the document
*/
public static CompletableFuture<List<ValidationProblem>> validateDocument(Node node, IValidationSeverityRegistry severityRegistry, List<IDocumentValidatorExtension> extensions) {
List<ValidationProblem> totalValidationProblems = Library.validate(node, severityRegistry);
if (extensions != null && !extensions.isEmpty()) {
for (IDocumentValidatorExtension extension : extensions) {
CompletableFuture<List<ValidationProblem>> extensionValidationProblems = extension.validateDocument(node);
extensionValidationProblems.thenAccept(problems -> problems.forEach(p -> {
totalValidationProblems.add(p);
node.ownerDocument().addValidationProblem(p.errorCode, p.nodePath, p.property, p.message, p.severity);
}));
}
}
return CompletableFuture.completedFuture(totalValidationProblems);
}
use of io.apicurio.datamodels.core.models.Document in project apicurio-data-models by Apicurio.
the class TestValidationExtension method testReadDocumentFromJSONString.
@Test
public void testReadDocumentFromJSONString() {
String jsonString = "{\r\n" + " \"openapi\": \"3.0.2\",\r\n" + " \"info\": {\r\n" + " \"title\": \"Very Simple API\",\r\n" + " \"version\": \"1.0.0\"\r\n" + " }\r\n" + "}";
Document doc = Library.readDocumentFromJSONString(jsonString);
Assert.assertEquals(DocumentType.openapi3, doc.getDocumentType());
Assert.assertEquals("3.0.2", ((Oas30Document) doc).openapi);
}
use of io.apicurio.datamodels.core.models.Document in project apicurio-data-models by Apicurio.
the class TestValidationExtension method testValidate.
@Test
public void testValidate() throws Exception {
String jsonString = "{\r\n" + " \"openapi\": \"3.0.9\",\r\n" + " \"info\": {\r\n" + " \"title\": \"Very Simple API\",\r\n" + " \"version\": \"1.0.0\"\r\n" + " }\r\n" + "}";
Document doc = Library.readDocumentFromJSONString(jsonString);
List<ValidationProblem> problems = Library.validateDocument(doc, null, null).get();
Assert.assertTrue(problems.size() > 0);
}
use of io.apicurio.datamodels.core.models.Document in project apicurio-data-models by Apicurio.
the class TestValidationExtension method testValidateDocumentWithCustomExtension.
@Test
public void testValidateDocumentWithCustomExtension() {
String jsonString = "{\r\n" + " \"openapi\": \"3.0.9\",\r\n" + " \"info\": {\r\n" + " \"title\": \"Very Simple API\",\r\n" + " \"version\": \"1.0.0\"\r\n" + " }\r\n" + "}";
Document doc = Library.readDocumentFromJSONString(jsonString);
List<IDocumentValidatorExtension> extensions = new ArrayList<>();
extensions.add(new TestValidationExtension());
CompletableFuture<List<ValidationProblem>> problems = Library.validateDocument(doc, null, extensions);
problems.whenCompleteAsync((validationProblems, done) -> {
List<String> expectedErrorCodes = Arrays.asList("R-003", "TEST-001");
List<String> documentErrorCodes = doc.getValidationProblemCodes();
Assert.assertEquals(expectedErrorCodes, documentErrorCodes);
List<String> problemErrorCodes = validationProblems.stream().map(p -> p.errorCode).collect(Collectors.toList());
Assert.assertEquals(expectedErrorCodes, problemErrorCodes);
});
}
use of io.apicurio.datamodels.core.models.Document in project apicurio-data-models by Apicurio.
the class Dereferencer method dereference.
/**
* Execute the algorithm.
*
* @return dereferenced clone of the source document.
* @throws java.lang.RuntimeException if strict and some references could not be dereferenced
*/
public Document dereference() {
Document clone = Library.cloneDocument(source);
IReferenceManipulationStrategy strategy = null;
switch(clone.getDocumentType()) {
case asyncapi2:
strategy = new Aai20IReferenceManipulationStrategy();
break;
case openapi2:
strategy = new Oas20IReferenceManipulationStrategy();
break;
case openapi3:
strategy = new Oas30IReferenceManipulationStrategy();
break;
default:
throw new RuntimeException("Unknown document type: " + clone.getDocumentType());
}
// Keeps the nodes waiting to be processed (BFS-style)
Queue<Context> processQueue = new LinkedList<>();
// start with the whole model
processQueue.add(new Context(null, clone));
// Prevents recursive loops
Map<String, String> resolvedToLocalMap = new HashMap<>();
for (String ref : strategy.getExistingLocalComponents(clone).keySet()) {
resolvedToLocalMap.put(ref, ref);
}
while (!processQueue.isEmpty()) {
Context item = processQueue.remove();
// Local components
Map<String, Node> localComponents = strategy.getExistingLocalComponents(clone);
// Collect all reference objects in the processed node
ReferenceCollectionVisitor rcv = new ReferenceCollectionVisitor();
VisitorUtil.visitTree(item.node, rcv, TraverserDirection.down);
List<IReferenceNode> referencedNodes = rcv.getReferencedNodes();
// For each reference node ...
for (IReferenceNode node : referencedNodes) {
// skip if already local, this makes sense if we're initially processing the whole Document
if (item.parentRef == null && localComponents.containsKey(node.getReference()))
continue;
// Reference to be resolved
Reference originalRef = new Reference(node.getReference());
// Attempt to resolve
if (item.parentRef != null && originalRef.isRelative()) {
originalRef = originalRef.withAbsoluteFrom(new Reference(item.parentRef));
}
// to avoid cycles
if (resolvedToLocalMap.containsKey(originalRef.getRef())) {
node.setReference(resolvedToLocalMap.get(originalRef.getRef()));
continue;
}
Node resolved = resolver.resolveRef(originalRef.getRef(), (Node) node);
// if null keep the reference in an 'unresolvable' set to decide later
if (resolved == null) {
unresolvable.add(node.getReference());
} else {
IReferenceManipulationStrategy.ReferenceAndNode localRef = null;
String name = originalRef.getName();
// Remove the reference node so its name can be reused if possible.
boolean nameReused = false;
if (name.equals(strategy.getComponentName(clone, resolved))) {
nameReused = strategy.removeComponent(clone, name);
}
// repeat in case of name conflict
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (i > 0)
name = originalRef.getName() + i;
try {
localRef = strategy.attachAsComponent(clone, name, resolved);
break;
} catch (IllegalArgumentException ex) {
// Assert we should be reusing the name
if (nameReused)
throw new IllegalStateException("Assertion error: " + "Component with name '" + name + "' should be reused, but can't be attached.");
// TODO maybe avoid exceptions?
}
}
if (localRef == null) {
unresolvable.add(node.getReference());
} else {
// rename the original reference
if (!nameReused) {
node.setReference(localRef.getRef());
}
// add resolved node to processing queue
processQueue.add(new Context(originalRef.getRef(), localRef.getNode()));
// remember, to prevent cycles
resolvedToLocalMap.put(originalRef.getRef(), localRef.getRef());
}
}
}
}
// we're done, if strict, throw
if (unresolvable.size() != 0 && strict)
throw new RuntimeException("Could not resolve some references: " + unresolvable);
return clone;
}
Aggregations