From 37672e4d05f04d02d7ff5fceaf86eb94c8839e88 Mon Sep 17 00:00:00 2001 From: gj0dcsa <135594855+gj0dcsa@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:17:01 +0100 Subject: [PATCH] TNT 220 showing only the validations for the right event type --- .../core/check/JsonSchemaValidator.java | 3 +- .../standards/tnt/TntComponentFactory.java | 16 +++-- .../standards/tnt/TntScenarioListBuilder.java | 4 +- .../tnt/action/TntGetEventsAction.java | 70 +++++++++++++++++-- 4 files changed, 79 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/dcsa/conformance/core/check/JsonSchemaValidator.java b/core/src/main/java/org/dcsa/conformance/core/check/JsonSchemaValidator.java index bfb3863c..bab21519 100644 --- a/core/src/main/java/org/dcsa/conformance/core/check/JsonSchemaValidator.java +++ b/core/src/main/java/org/dcsa/conformance/core/check/JsonSchemaValidator.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import lombok.SneakyThrows; @@ -59,6 +60,6 @@ public Set validate(JsonNode jsonNode) { Set validationMessageSet = jsonSchema.validate(jsonNode); return validationMessageSet.stream() .map(ValidationMessage::toString) - .collect(Collectors.toSet()); + .collect(Collectors.toCollection(TreeSet::new)); } } diff --git a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntComponentFactory.java b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntComponentFactory.java index c00dde91..44422698 100644 --- a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntComponentFactory.java +++ b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntComponentFactory.java @@ -14,6 +14,7 @@ import org.dcsa.conformance.core.scenario.ScenarioListBuilder; import org.dcsa.conformance.core.state.JsonNodeMap; import org.dcsa.conformance.core.toolkit.JsonToolkit; +import org.dcsa.conformance.standards.tnt.action.TntEventType; import org.dcsa.conformance.standards.tnt.party.TntPublisher; import org.dcsa.conformance.standards.tnt.party.TntRole; import org.dcsa.conformance.standards.tnt.party.TntSubscriber; @@ -112,11 +113,18 @@ public Set getReportRoleNames( .collect(Collectors.toSet()); } - public JsonSchemaValidator getMessageSchemaValidator(String apiProviderRole, boolean forRequest) { + public Map getEventSchemaValidators() { String schemaFilePath = "/standards/tnt/schemas/tnt-220-publisher.json"; - String schemaName = - TntRole.isPublisher(apiProviderRole) ? (forRequest ? null : "events") : null; - return JsonSchemaValidator.getInstance(schemaFilePath, schemaName); + return Map.ofEntries( + Map.entry( + TntEventType.EQUIPMENT, + JsonSchemaValidator.getInstance(schemaFilePath, "equipmentEvent")), + Map.entry( + TntEventType.SHIPMENT, + JsonSchemaValidator.getInstance(schemaFilePath, "shipmentEvent")), + Map.entry( + TntEventType.TRANSPORT, + JsonSchemaValidator.getInstance(schemaFilePath, "transportEvent"))); } @SneakyThrows diff --git a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntScenarioListBuilder.java b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntScenarioListBuilder.java index 99024004..332eb02b 100644 --- a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntScenarioListBuilder.java +++ b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/TntScenarioListBuilder.java @@ -14,7 +14,6 @@ import org.dcsa.conformance.standards.tnt.action.SupplyScenarioParametersAction; import org.dcsa.conformance.standards.tnt.action.TntGetEventsAction; import org.dcsa.conformance.standards.tnt.party.TntFilterParameter; -import org.dcsa.conformance.standards.tnt.party.TntRole; @Slf4j public class TntScenarioListBuilder extends ScenarioListBuilder { @@ -116,7 +115,6 @@ private static TntScenarioListBuilder getEvents() { subscriberPartyName, publisherPartyName, previousAction, - componentFactory.getMessageSchemaValidator( - TntRole.PUBLISHER.getConfigName(), false))); + componentFactory.getEventSchemaValidators())); } } diff --git a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/action/TntGetEventsAction.java b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/action/TntGetEventsAction.java index a644cc6d..dadd78b4 100644 --- a/tnt/src/main/java/org/dcsa/conformance/standards/tnt/action/TntGetEventsAction.java +++ b/tnt/src/main/java/org/dcsa/conformance/standards/tnt/action/TntGetEventsAction.java @@ -1,26 +1,31 @@ package org.dcsa.conformance.standards.tnt.action; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.*; +import java.util.function.Function; import java.util.stream.Stream; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.dcsa.conformance.core.check.*; import org.dcsa.conformance.core.scenario.ConformanceAction; +import org.dcsa.conformance.core.traffic.ConformanceExchange; import org.dcsa.conformance.core.traffic.HttpMessageType; import org.dcsa.conformance.standards.tnt.party.TntRole; @Getter @Slf4j public class TntGetEventsAction extends TntAction { - private final JsonSchemaValidator responseSchemaValidator; + private final Map eventSchemaValidators; public TntGetEventsAction( String subscriberPartyName, String publisherPartyName, ConformanceAction previousAction, - JsonSchemaValidator responseSchemaValidator) { + Map eventSchemaValidators) { super(subscriberPartyName, publisherPartyName, previousAction, "GetEvents", 200); - this.responseSchemaValidator = responseSchemaValidator; + this.eventSchemaValidators = eventSchemaValidators; } @Override @@ -37,11 +42,64 @@ protected Stream createSubChecks() { return Stream.of( new UrlPathCheck(TntRole::isSubscriber, getMatchedExchangeUuid(), "/events"), new ResponseStatusCheck(TntRole::isPublisher, getMatchedExchangeUuid(), expectedStatus), - new JsonSchemaCheck( + new ActionCheck( + "The HTTP %s matches the standard JSON schema" + .formatted(HttpMessageType.RESPONSE.name().toLowerCase()), TntRole::isPublisher, getMatchedExchangeUuid(), - HttpMessageType.RESPONSE, - responseSchemaValidator)); + HttpMessageType.RESPONSE) { + + private boolean isEventNode(JsonNode jsonNode) { + return jsonNode.isObject() && !jsonNode.path("eventType").isMissingNode(); + } + + private ArrayList _findEventNodes( + ArrayList foundEventNodes, JsonNode searchInJsonNode) { + if (isEventNode(searchInJsonNode)) { + foundEventNodes.add(searchInJsonNode); + } else { + searchInJsonNode.forEach( + elementNode -> _findEventNodes(foundEventNodes, elementNode)); + } + return foundEventNodes; + } + + @Override + protected Set checkConformance( + Function getExchangeByUuid) { + JsonNode jsonResponse = + getExchangeByUuid + .apply(getMatchedExchangeUuid()) + .getMessage(httpMessageType) + .body() + .getJsonBody(); + LinkedHashSet validationErrors = new LinkedHashSet<>(); + if (!jsonResponse.isArray()) { + validationErrors.add("The root JSON response must be an array of events"); + } + ArrayList eventNodes = _findEventNodes(new ArrayList<>(), jsonResponse); + int eventCount = eventNodes.size(); + for (int eventIndex = 0; eventIndex < eventCount; ++eventIndex) { + JsonNode eventNode = eventNodes.get(eventIndex); + JsonNode eventTypeNode = eventNode.path("eventType"); + TntEventType eventType; + String eventTypeText = eventTypeNode.asText().toUpperCase(); + try { + eventType = TntEventType.valueOf(eventTypeText); + } catch (RuntimeException e) { + validationErrors.add( + "Event #%d: incorrect eventType attribute: %s" + .formatted(eventIndex, eventTypeNode)); + continue; + } + JsonSchemaValidator eventSchemaValidator = eventSchemaValidators.get(eventType); + for (String validationError : eventSchemaValidator.validate(eventNode)) { + validationErrors.add("Event #%d: %s".formatted(eventIndex, validationError)); + } + } + return validationErrors; + } + }); } }; }