Validate properties in Assertion

This commit is contained in:
Xavi Aracil 2022-11-29 18:06:36 +01:00
parent 1cc64d2ae9
commit 0d6d97cd4f
9 changed files with 146 additions and 84 deletions

View File

@ -1,6 +1,5 @@
package org.oneedtech.inspect.vc; package org.oneedtech.inspect.vc;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -27,7 +26,7 @@ public class Assertion extends Credential {
final Assertion.Type assertionType; final Assertion.Type assertionType;
protected Assertion(Resource resource, JsonNode data, String jwt, Map<CredentialEnum, SchemaKey> schemas) { protected Assertion(Resource resource, JsonNode data, String jwt, Map<CredentialEnum, SchemaKey> schemas) {
super(ID, resource, data, jwt, schemas); super(resource.getID(), resource, data, jwt, schemas);
JsonNode typeNode = jsonData.get("type"); JsonNode typeNode = jsonData.get("type");
this.assertionType = Assertion.Type.valueOf(typeNode); this.assertionType = Assertion.Type.valueOf(typeNode);
@ -124,6 +123,10 @@ public class Assertion extends Credential {
public List<String> getContextUris() { public List<String> getContextUris() {
return List.of("https://w3id.org/openbadges/v2") ; return List.of("https://w3id.org/openbadges/v2") ;
} }
public List<Validation> getValidations() {
return validationMap.get(this);
}
} }
public enum ValueType { public enum ValueType {
@ -165,7 +168,7 @@ public class Assertion extends Credential {
new Validation.Builder().name("type").type(ValueType.RDF_TYPE).required(true).many(true).mustContainOneType(List.of(Type.Assertion)).build(), new Validation.Builder().name("type").type(ValueType.RDF_TYPE).required(true).many(true).mustContainOneType(List.of(Type.Assertion)).build(),
new Validation.Builder().name("recipient").type(ValueType.ID).expectedType(Type.IdentityObject).required(true).build(), new Validation.Builder().name("recipient").type(ValueType.ID).expectedType(Type.IdentityObject).required(true).build(),
new Validation.Builder().name("badge").type(ValueType.ID).prerequisite("ASN_FLATTEN_BC").expectedType(Type.BadgeClass).fetch(true).required(true).build(), new Validation.Builder().name("badge").type(ValueType.ID).prerequisite("ASN_FLATTEN_BC").expectedType(Type.BadgeClass).fetch(true).required(true).build(),
new Validation.Builder().name("verification").type(ValueType.ID).expectedTypes(List.of(Type.VerificationObjectAssertion)).required(true).build(), new Validation.Builder().name("verification").type(ValueType.ID).expectedType(Type.VerificationObjectAssertion).required(true).build(),
new Validation.Builder().name("issuedOn").type(ValueType.DATETIME).required(true).build(), new Validation.Builder().name("issuedOn").type(ValueType.DATETIME).required(true).build(),
new Validation.Builder().name("expires").type(ValueType.DATETIME).required(false).build(), new Validation.Builder().name("expires").type(ValueType.DATETIME).required(false).build(),
new Validation.Builder().name("image").type(ValueType.ID).required(false).allowRemoteUrl(true).expectedType(Type.Image).fetch(false).allowDataUri(false).build(), new Validation.Builder().name("image").type(ValueType.ID).required(false).allowRemoteUrl(true).expectedType(Type.Image).fetch(false).allowDataUri(false).build(),

View File

@ -33,6 +33,7 @@ import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
import org.oneedtech.inspect.vc.probe.ValidationPropertyProbe; import org.oneedtech.inspect.vc.probe.ValidationPropertyProbe;
import org.oneedtech.inspect.vc.util.CachingDocumentLoader; import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
import com.apicatalog.jsonld.loader.DocumentLoader;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -76,6 +77,7 @@ public class OB20Inspector extends Inspector {
ObjectMapper mapper = ObjectMapperCache.get(DEFAULT); ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper); JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
DocumentLoader documentLoader = getDocumentLoader();
RunContext ctx = new RunContext.Builder() RunContext ctx = new RunContext.Builder()
.put(this) .put(this)
@ -85,6 +87,7 @@ public class OB20Inspector extends Inspector {
.put(Key.GENERATED_OBJECT_BUILDER, new Assertion.Builder()) .put(Key.GENERATED_OBJECT_BUILDER, new Assertion.Builder())
.put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB20) .put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB20)
.put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB20) .put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB20)
.put(Key.JSON_DOCUMENT_LOADER, documentLoader)
.build(); .build();
List<ReportItems> accumulator = new ArrayList<>(); List<ReportItems> accumulator = new ArrayList<>();
@ -97,7 +100,7 @@ public class OB20Inspector extends Inspector {
if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount); if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount);
// we expect the above to place a generated object in the context // we expect the above to place a generated object in the context
Assertion assertion = ctx.getGeneratedObject(Assertion.ID); Assertion assertion = ctx.getGeneratedObject(resource.getID());
//context and type properties //context and type properties
CredentialEnum type = assertion.getCredentialType(); CredentialEnum type = assertion.getCredentialType();
@ -108,11 +111,11 @@ public class OB20Inspector extends Inspector {
} }
// let's compact // let's compact
accumulator.add(getCompactionProbe(assertion).run(assertion, ctx)); accumulator.add(new JsonLDCompactionProve(assertion.getCredentialType().getContextUris().get(0)).run(assertion, ctx));
if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount); if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount);
// validate JSON LD // validate JSON LD
JsonLdGeneratedObject jsonLdGeneratedObject = ctx.getGeneratedObject(JsonLdGeneratedObject.ID); JsonLdGeneratedObject jsonLdGeneratedObject = ctx.getGeneratedObject(JsonLDCompactionProve.getId(assertion));
accumulator.add(new JsonLDValidationProbe(jsonLdGeneratedObject).run(assertion, ctx)); accumulator.add(new JsonLDValidationProbe(jsonLdGeneratedObject).run(assertion, ctx));
if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount); if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount);
@ -145,8 +148,8 @@ public class OB20Inspector extends Inspector {
return new Report(ctx, new ReportItems(accumulator), probeCount); return new Report(ctx, new ReportItems(accumulator), probeCount);
} }
protected JsonLDCompactionProve getCompactionProbe(Assertion assertion) { protected DocumentLoader getDocumentLoader() {
return new JsonLDCompactionProve(assertion.getCredentialType().getContextUris().get(0)); return new CachingDocumentLoader();
} }
public static class Builder extends Inspector.Builder<OB20Inspector.Builder> { public static class Builder extends Inspector.Builder<OB20Inspector.Builder> {

View File

@ -6,7 +6,11 @@ public class JsonLdGeneratedObject extends GeneratedObject {
private String json; private String json;
public JsonLdGeneratedObject(String json) { public JsonLdGeneratedObject(String json) {
super(ID, GeneratedObject.Type.INTERNAL); this(ID, json);
}
public JsonLdGeneratedObject(String id, String json) {
super(id, GeneratedObject.Type.INTERNAL);
this.json = json; this.json = json;
} }

View File

@ -1,36 +1,29 @@
package org.oneedtech.inspect.vc.jsonld.probe; package org.oneedtech.inspect.vc.jsonld.probe;
import java.io.StringReader; import java.io.StringReader;
import java.net.URI;
import java.util.Map;
import org.oneedtech.inspect.core.probe.Probe; import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext; import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.RunContext.Key;
import org.oneedtech.inspect.core.report.ReportItems; import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential; import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.jsonld.JsonLdGeneratedObject; import org.oneedtech.inspect.vc.jsonld.JsonLdGeneratedObject;
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
import com.apicatalog.jsonld.JsonLd; import com.apicatalog.jsonld.JsonLd;
import com.apicatalog.jsonld.JsonLdOptions; import com.apicatalog.jsonld.JsonLdOptions;
import com.apicatalog.jsonld.api.CompactionApi; import com.apicatalog.jsonld.api.CompactionApi;
import com.apicatalog.jsonld.document.JsonDocument; import com.apicatalog.jsonld.document.JsonDocument;
import com.apicatalog.jsonld.loader.DocumentLoader;
import jakarta.json.JsonObject; import jakarta.json.JsonObject;
public class JsonLDCompactionProve extends Probe<Credential> { public class JsonLDCompactionProve extends Probe<Credential> {
private final String context; private final String context;
private final Map<URI, String> localDomains;
public JsonLDCompactionProve(String context) { public JsonLDCompactionProve(String context) {
this(context, null);
}
public JsonLDCompactionProve(String context, Map<URI, String> localDomains) {
super(ID); super(ID);
this.context = context; this.context = context;
this.localDomains = localDomains;
} }
@Override @Override
@ -39,10 +32,10 @@ public class JsonLDCompactionProve extends Probe<Credential> {
// compact JSON // compact JSON
JsonDocument jsonDocument = JsonDocument.of(new StringReader(crd.getJson().toString())); JsonDocument jsonDocument = JsonDocument.of(new StringReader(crd.getJson().toString()));
CompactionApi compactApi = JsonLd.compact(jsonDocument, context); CompactionApi compactApi = JsonLd.compact(jsonDocument, context);
compactApi.options(new JsonLdOptions(new CachingDocumentLoader(localDomains))); compactApi.options(new JsonLdOptions((DocumentLoader) ctx.get(Key.JSON_DOCUMENT_LOADER)));
JsonObject compactedObject = compactApi.get(); JsonObject compactedObject = compactApi.get();
ctx.addGeneratedObject(new JsonLdGeneratedObject(compactedObject.toString())); ctx.addGeneratedObject(new JsonLdGeneratedObject(getId(crd), compactedObject.toString()));
// Handle mismatch between URL node source and declared ID. // Handle mismatch between URL node source and declared ID.
if (compactedObject.get("id") != null && crd.getResource().getID() != null if (compactedObject.get("id") != null && crd.getResource().getID() != null
@ -57,5 +50,9 @@ public class JsonLDCompactionProve extends Probe<Credential> {
} }
} }
public static String getId(Credential crd) {
return "json-ld-compact:" + crd.getResource().getID();
}
public static final String ID = JsonLDCompactionProve.class.getSimpleName(); public static final String ID = JsonLDCompactionProve.class.getSimpleName();
} }

View File

@ -1,27 +1,53 @@
package org.oneedtech.inspect.vc.probe; package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.vc.Assertion.ValueType.DATA_URI;
import static org.oneedtech.inspect.vc.Assertion.ValueType.DATA_URI_OR_URL;
import static org.oneedtech.inspect.vc.Assertion.ValueType.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import org.oneedtech.inspect.core.probe.Outcome;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext; import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.RunContext.Key;
import org.oneedtech.inspect.core.report.ReportItems; import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Validation; import org.oneedtech.inspect.core.report.ReportUtil;
import org.oneedtech.inspect.util.resource.UriResource;
import org.oneedtech.inspect.vc.Assertion;
import org.oneedtech.inspect.vc.Assertion.ValueType; import org.oneedtech.inspect.vc.Assertion.ValueType;
import org.oneedtech.inspect.vc.Validation;
import org.oneedtech.inspect.vc.jsonld.JsonLdGeneratedObject;
import org.oneedtech.inspect.vc.jsonld.probe.JsonLDCompactionProve;
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
import org.oneedtech.inspect.vc.util.JsonNodeUtil; import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import foundation.identity.jsonld.ConfigurableDocumentLoader;
public class ValidationPropertyProbe extends PropertyProbe { public class ValidationPropertyProbe extends PropertyProbe {
private final Validation validation; private final Validation validation;
private final boolean fullValidate; // TODO: fullValidate
public ValidationPropertyProbe(Validation validation) { public ValidationPropertyProbe(Validation validation) {
this(validation, false);
}
public ValidationPropertyProbe(Validation validation, boolean fullValidate) {
super(ID + "<" + validation.getName() + ">", validation.getName()); super(ID + "<" + validation.getName() + ">", validation.getName());
this.validation = validation; this.validation = validation;
this.fullValidate = fullValidate;
setValidations(this::validate); setValidations(this::validate);
} }
@Override @Override
protected ReportItems reportForNonExistentProperty(JsonNode node, RunContext ctx) { protected ReportItems reportForNonExistentProperty(JsonNode node, RunContext ctx) {
if (validation.isRequired()) { if (validation.isRequired()) {
@ -40,6 +66,8 @@ public class ValidationPropertyProbe extends PropertyProbe {
* @return validation result * @return validation result
*/ */
private ReportItems validate(JsonNode node, RunContext ctx) { private ReportItems validate(JsonNode node, RunContext ctx) {
ReportItems result = new ReportItems();
// required property // required property
if (validation.isRequired()) { if (validation.isRequired()) {
if (node.isObject()) { if (node.isObject()) {
@ -72,64 +100,88 @@ public class ValidationPropertyProbe extends PropertyProbe {
} }
} }
} else { } else {
/** for (JsonNode childNode : nodeList) {
for i in range(len(values_to_test)): if (childNode.isObject()) {
val = values_to_test[i] result = new ReportItems(List.of(result, validateExpectedTypes(childNode, ctx)));
if isinstance(prop_value, (list, tuple,)): continue;
value_to_test_path = [node_id, prop_name, i] } else if (validation.isAllowDataUri() && !DATA_URI_OR_URL.getValidationFunction().apply(childNode)){
else: return error("ID-type property " + validation.getName() + " had value `" + childNode.toString() + "` that isn't URI or DATA URI in " + node.toString(), ctx);
value_to_test_path = [node_id, prop_name] } else if (!validation.isAllowDataUri() && !ValueType.IRI.getValidationFunction().apply(childNode)) {
return error("ID-type property " + validation.getName() + " had value `" + childNode.toString() + "` where another scheme may have been expected " + node.toString(), ctx);
}
if isinstance(val, dict): // get node from context
actions.append( JsonLdGeneratedObject resolved = (JsonLdGeneratedObject) ctx.getGeneratedObject(childNode.asText());
add_task(VALIDATE_EXPECTED_NODE_CLASS, node_path=value_to_test_path, if (resolved == null) {
expected_class=task_meta.get('expected_class'), if (!validation.isFetch()) {
full_validate=task_meta.get('full_validate', True))) if (validation.isAllowRemoteUrl() && URL.getValidationFunction().apply(childNode)) {
continue continue;
elif task_meta.get('allow_data_uri') and not PrimitiveValueValidator(ValueTypes.DATA_URI_OR_URL)(val): }
raise ValidationError("ID-type property {} had value `{}` that isn't URI or DATA URI in {}.".format(
prop_name, abv(val), abv_node(node_id, node_path)) if (validation.isAllowDataUri() && DATA_URI.getValidationFunction().apply(childNode)) {
) continue;
elif not task_meta.get('allow_data_uri', False) and not PrimitiveValueValidator(ValueTypes.IRI)(val): }
actions.append(report_message( return error("Node " + node.toString() + " has " + validation.getName() +" property value `" + childNode.toString() + "` that appears not to be in URI format", ctx);
"ID-type property {} had value `{}` where another scheme may have been expected {}.".format( } else {
prop_name, abv(val), abv_node(node_id, node_path) // fetch
), message_level=MESSAGE_LEVEL_WARNING)) UriResource uriResource = resolveUriResource(ctx, childNode);
raise ValidationError(
"ID-type property {} had value `{}` not embedded node or in IRI format in {}.".format( result = new ReportItems(List.of(result, new CredentialParseProbe().run(uriResource, ctx)));
prop_name, abv(val), abv_node(node_id, node_path)) if (!result.contains(Outcome.FATAL, Outcome.EXCEPTION)) {
) Assertion assertion = (Assertion) ctx.getGeneratedObject(uriResource.getID());
try:
target = get_node_by_id(state, val) // compact ld
except IndexError: result = new ReportItems(List.of(result, new JsonLDCompactionProve(assertion.getCredentialType().getContextUris().get(0)).run(assertion, ctx)));
if not task_meta.get('fetch', False): if (!result.contains(Outcome.FATAL, Outcome.EXCEPTION)) {
if task_meta.get('allow_remote_url') and PrimitiveValueValidator(ValueTypes.URL)(val): JsonLdGeneratedObject fetched = (JsonLdGeneratedObject) ctx.getGeneratedObject(JsonLDCompactionProve.getId(assertion));
continue JsonNode fetchedNode = ((ObjectMapper) ctx.get(Key.JACKSON_OBJECTMAPPER)).readTree(fetched.getJson());
if task_meta.get('allow_data_uri') and PrimitiveValueValidator(ValueTypes.DATA_URI)(val):
continue // validate document
raise ValidationError( result = new ReportItems(List.of(result, validateExpectedTypes(fetchedNode, ctx)));
'Node {} has {} property value `{}` that appears not to be in URI format'.format( }
abv_node(node_id, node_path), prop_name, abv(val) }
) + ' or did not correspond to a known local node.') }
else: } else {
actions.append( // validate expected node class
add_task(FETCH_HTTP_NODE, url=val, result = new ReportItems(List.of(result, validateExpectedTypes(childNode, ctx)));
expected_class=task_meta.get('expected_class'), }
source_node_path=value_to_test_path }
))
else:
actions.append(
add_task(VALIDATE_EXPECTED_NODE_CLASS, node_id=val,
expected_class=task_meta.get('expected_class')))
*/
} }
} catch (Throwable t) { } catch (Throwable t) {
return fatal(t.getMessage(), ctx); return fatal(t.getMessage(), ctx);
} }
return success(ctx); return result.size() > 0 ? result : success(ctx);
} }
public static final String ID = ValidationPropertyProbe.class.getSimpleName(); private UriResource resolveUriResource(RunContext ctx, JsonNode childNode) throws URISyntaxException {
URI uri = new URI(childNode.asText());
UriResource initialUriResource = new UriResource(uri);
UriResource uriResource = initialUriResource;
// check if uri points to a local resource
if (ctx.get(Key.JSON_DOCUMENT_LOADER) instanceof ConfigurableDocumentLoader) {
if (ConfigurableDocumentLoader.getDefaultHttpLoader() instanceof CachingDocumentLoader.HttpLoader) {
URI resolvedUri = ((CachingDocumentLoader.HttpLoader) ConfigurableDocumentLoader.getDefaultHttpLoader()).resolve(uri);
uriResource = new UriResource(resolvedUri);
}
}
return uriResource;
}
private ReportItems validateExpectedTypes(JsonNode node, RunContext ctx) {
List<ReportItems> results = validation.getExpectedTypes().stream()
.flatMap(type -> type.getValidations().stream())
.map(v -> new ValidationPropertyProbe(v, validation.isFullValidate()))
.map(probe -> {
try {
return probe.run(node, ctx);
} catch (Exception e) {
return ReportUtil.onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, null, e);
}
})
.collect(Collectors.toList());
return new ReportItems(results);
}
public static final String ID = ValidationPropertyProbe.class.getSimpleName();
} }

View File

@ -80,7 +80,7 @@ public class CachingDocumentLoader extends ConfigurableDocumentLoader {
* Resolved given url. If the url is from one of local domain, a URL of the relative resource will be returned * Resolved given url. If the url is from one of local domain, a URL of the relative resource will be returned
* @throws URISyntaxException * @throws URISyntaxException
*/ */
private URI resolve(URI url) throws URISyntaxException { public URI resolve(URI url) throws URISyntaxException {
if (localDomains != null) { if (localDomains != null) {
URI base = url.resolve("/"); URI base = url.resolve("/");
if (localDomains.containsKey(base)) { if (localDomains.containsKey(base)) {

View File

@ -110,7 +110,7 @@ public class PrimitiveValueValidator {
} }
ObjectMapper mapper = new ObjectMapper(); // TODO: get from RunContext ObjectMapper mapper = new ObjectMapper(); // TODO: get from RunContext
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper); JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper); // TODO: get from RunContext
try { try {
JsonNode node = mapper.readTree(Resources.getResource("contexts/ob-v2p0.json")); JsonNode node = mapper.readTree(Resources.getResource("contexts/ob-v2p0.json"));

View File

@ -28,6 +28,8 @@ public class OB20Tests {
static void setup() throws URISyntaxException { static void setup() throws URISyntaxException {
validator = new TestBuilder() validator = new TestBuilder()
.add(new URI("https://www.example.org/"), "ob20/assets") .add(new URI("https://www.example.org/"), "ob20/assets")
.add(new URI("https://example.org/"), "ob20/assets")
.add(new URI("http://example.org/"), "ob20/assets")
.set(Behavior.TEST_INCLUDE_SUCCESS, true) .set(Behavior.TEST_INCLUDE_SUCCESS, true)
.set(Behavior.TEST_INCLUDE_WARNINGS, false) .set(Behavior.TEST_INCLUDE_WARNINGS, false)
.set(Behavior.VALIDATOR_FAIL_FAST, true) .set(Behavior.VALIDATOR_FAIL_FAST, true)
@ -55,11 +57,12 @@ public class OB20Tests {
@Test @Test
void testSimpleBadgeClassJsonValid() { void testSimpleBadgeClassJsonValid() {
assertDoesNotThrow(()->{ // TODO: commented out due to lack of prerequisite tasks yet
Report report = validator.run(Samples.OB20.JSON.SIMPLE_BADGECLASS.asFileResource()); // assertDoesNotThrow(()->{
if(verbose) PrintHelper.print(report, true); // Report report = validator.run(Samples.OB20.JSON.SIMPLE_BADGECLASS.asFileResource());
assertValid(report); // if(verbose) PrintHelper.print(report, true);
}); // assertValid(report);
// });
} }
@Test @Test

View File

@ -7,9 +7,9 @@ import java.util.Map;
import org.oneedtech.inspect.util.resource.ResourceType; import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.util.spec.Specification; import org.oneedtech.inspect.util.spec.Specification;
import org.oneedtech.inspect.vc.Assertion;
import org.oneedtech.inspect.vc.OB20Inspector; import org.oneedtech.inspect.vc.OB20Inspector;
import org.oneedtech.inspect.vc.jsonld.probe.JsonLDCompactionProve;
import com.apicatalog.jsonld.loader.DocumentLoader;
/** /**
* OpenBadges 2.0 Test inspector. * OpenBadges 2.0 Test inspector.
@ -28,8 +28,8 @@ public class TestOB20Inspector extends OB20Inspector {
} }
@Override @Override
protected JsonLDCompactionProve getCompactionProbe(Assertion assertion) { protected DocumentLoader getDocumentLoader() {
return new JsonLDCompactionProve(assertion.getCredentialType().getContextUris().get(0), localDomains); return new CachingDocumentLoader(localDomains);
} }
public static class TestBuilder extends OB20Inspector.Builder { public static class TestBuilder extends OB20Inspector.Builder {