Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DT-969: Add simple TD scenario group #102

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand All @@ -23,8 +24,15 @@
@Slf4j
class EblScenarioListBuilder extends ScenarioListBuilder<EblScenarioListBuilder> {
static final String SCENARIO_SUITE_CONFORMANCE_SI_ONLY = "Conformance SI-only";
static final String SCENARIO_SUITE_CONFORMANCE_TD_ONLY = "Conformance TD-only";
static final String SCENARIO_SUITE_RI = "Reference Implementation";

static final Set<String> SCENARIOS = Set.of(
SCENARIO_SUITE_CONFORMANCE_SI_ONLY,
SCENARIO_SUITE_CONFORMANCE_TD_ONLY,
SCENARIO_SUITE_RI
);

private static final ThreadLocal<String> STANDARD_VERSION = new ThreadLocal<>();
private static final ThreadLocal<String> threadLocalCarrierPartyName = new ThreadLocal<>();
private static final ThreadLocal<String> threadLocalShipperPartyName = new ThreadLocal<>();
Expand Down Expand Up @@ -54,6 +62,9 @@ public static LinkedHashMap<String, EblScenarioListBuilder> createModuleScenario
if (SCENARIO_SUITE_CONFORMANCE_SI_ONLY.equals(componentFactory.getScenarioSuite())) {
return createConformanceSiOnlyScenarios();
}
if (SCENARIO_SUITE_CONFORMANCE_TD_ONLY.equals(componentFactory.getScenarioSuite())) {
return createConformanceTdOnlyScenarios();
}
if (SCENARIO_SUITE_RI.equals(componentFactory.getScenarioSuite())) {
return createReferenceImplementationScenarios();
}
Expand Down Expand Up @@ -91,6 +102,32 @@ private static LinkedHashMap<String, EblScenarioListBuilder> createConformanceSi
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}


private static LinkedHashMap<String, EblScenarioListBuilder> createConformanceTdOnlyScenarios() {
return Stream.of(
Map.entry(
"Supported shipment types scenarios",
noAction()
.thenEither(
Arrays.stream(ScenarioType.values())
.map(
scenarioType ->
carrier_SupplyScenarioParameters(scenarioType)
.then(_uc6_get(true, _uc8_get(_uc12_get(_uc13_get())))))
.toList()
.toArray(new EblScenarioListBuilder[] {}))),
Map.entry(
"Shipper interactions with transport document",
carrier_SupplyScenarioParameters(ScenarioType.REGULAR_BOL)
.then(_uc6_get(true, _uc7_get(_uc8_get(_uc12_get(_uc13_get()))),
_uc8_get(_uc12_get(_uc13_get())),
_oob_amendment(_uc6_get(false, _uc8_get(_uc12_get(_uc13_get())))),
_uc8_get(_oob_amendment(_uc9_get(_uc10_get(_uc11_get(_uc12_get(_uc13_get()))))))))))
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}

@SuppressWarnings("unused")
private static void _ignoreSingleValueArgs() {
_uc1_get(SI_ANY);
Expand Down Expand Up @@ -181,11 +218,75 @@ private static EblScenarioListBuilder _uc5_get(
.thenEither(thenEither));
}

private static EblScenarioListBuilder _uc6_get(boolean start, EblScenarioListBuilder... thenEither) {
return uc6_carrier_publishDraftTransportDocument(start).then(
shipper_GetTransportDocument(TD_DRAFT)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc7_get(EblScenarioListBuilder... thenEither) {
return uc7_shipper_approveDraftTransportDocument().then(
shipper_GetTransportDocument(TD_APPROVED)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc8_get(EblScenarioListBuilder... thenEither) {
return uc8_carrier_issueTransportDocument().then(
shipper_GetTransportDocument(TD_ISSUED)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc9_get(EblScenarioListBuilder... thenEither) {
return uc9_carrier_awaitSurrenderRequestForAmendment().then(
shipper_GetTransportDocument(TD_PENDING_SURRENDER_FOR_AMENDMENT)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc10_get(EblScenarioListBuilder... thenEither) {
return uc10a_carrier_acceptSurrenderRequestForAmendment().then(
shipper_GetTransportDocument(TD_SURRENDERED_FOR_AMENDMENT)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc11_get(EblScenarioListBuilder... thenEither) {
return uc11_carrier_voidTransportDocument().then(
uc11i_carrier_issueAmendedTransportDocument().then(
shipper_GetTransportDocument(TD_ISSUED)
.thenEither(thenEither)

));
}

private static EblScenarioListBuilder _uc12_get(EblScenarioListBuilder... thenEither) {
return uc12_carrier_awaitSurrenderRequestForDelivery().then(
shipper_GetTransportDocument(TD_PENDING_SURRENDER_FOR_DELIVERY)
.thenEither(thenEither)
);
}

private static EblScenarioListBuilder _uc13_get(EblScenarioListBuilder... thenEither) {
return uc13a_carrier_acceptSurrenderRequestForDelivery().then(
shipper_GetTransportDocument(TD_SURRENDERED_FOR_DELIVERY)
.thenEither(thenEither)
);
}


private static EblScenarioListBuilder _uc14_get(ShippingInstructionsStatus siState) {
return uc14_carrier_confirmShippingInstructionsComplete()
.then(shipper_GetShippingInstructions(siState, false));
}

private static EblScenarioListBuilder _oob_amendment(EblScenarioListBuilder... thenEither) {
return oob_carrier_processOutOfBoundTDUpdateRequest()
.thenEither(thenEither);
}

private static LinkedHashMap<String, EblScenarioListBuilder> createReferenceImplementationScenarios() {
return Stream.of(
Map.entry(
Expand Down Expand Up @@ -248,10 +349,10 @@ yield then(
.then(shipper_GetShippingInstructions(SI_RECEIVED, SI_UPDATE_RECEIVED, true, useTDRef)
.thenAllPathsFrom(SI_UPDATE_RECEIVED, SI_RECEIVED, transportDocumentStatus, useTDRef))),
switch (transportDocumentStatus) {
case TD_START -> uc6_carrier_publishDraftTransportDocument().then(
case TD_START -> uc6_carrier_publishDraftTransportDocument(false).then(
shipper_GetShippingInstructionsRecordTDRef()
.then(shipper_GetTransportDocument(TD_DRAFT).thenAllPathsFrom(TD_DRAFT)));
case TD_DRAFT -> uc6_carrier_publishDraftTransportDocument().then(
case TD_DRAFT -> uc6_carrier_publishDraftTransportDocument(false).then(
shipper_GetShippingInstructionsRecordTDRef()
.then(shipper_GetTransportDocument(TD_DRAFT).thenHappyPathFrom(TD_DRAFT)));
case TD_ISSUED -> uc9_carrier_awaitSurrenderRequestForAmendment().then(
Expand Down Expand Up @@ -427,7 +528,7 @@ private EblScenarioListBuilder thenHappyPathFrom(
.thenHappyPathFrom(SI_RECEIVED, transportDocumentStatus, useTDRef)));
case SI_UPDATE_CONFIRMED, SI_RECEIVED -> then(
switch (transportDocumentStatus) {
case TD_START, TD_DRAFT -> uc6_carrier_publishDraftTransportDocument().then(
case TD_START, TD_DRAFT -> uc6_carrier_publishDraftTransportDocument(false).then(
shipper_GetShippingInstructionsRecordTDRef()
.then(shipper_GetTransportDocument(TD_DRAFT).thenHappyPathFrom(TD_DRAFT)));
case TD_ISSUED -> uc9_carrier_awaitSurrenderRequestForAmendment().then(
Expand Down Expand Up @@ -794,7 +895,7 @@ private static EblScenarioListBuilder uc5_shipper_cancelUpdateToShippingInstruct
EBL_NOTIFICATIONS_API, EBL_SI_NOTIFICATION_SCHEMA_NAME)));
}

private static EblScenarioListBuilder uc6_carrier_publishDraftTransportDocument() {
private static EblScenarioListBuilder uc6_carrier_publishDraftTransportDocument(boolean skipSI) {
String carrierPartyName = threadLocalCarrierPartyName.get();
String shipperPartyName = threadLocalShipperPartyName.get();
return new EblScenarioListBuilder(
Expand All @@ -804,9 +905,12 @@ private static EblScenarioListBuilder uc6_carrier_publishDraftTransportDocument(
shipperPartyName,
(EblAction) previousAction,
resolveMessageSchemaValidator(
EBL_NOTIFICATIONS_API, EBL_TD_NOTIFICATION_SCHEMA_NAME)));
EBL_NOTIFICATIONS_API, EBL_TD_NOTIFICATION_SCHEMA_NAME),
skipSI));
}



private static EblScenarioListBuilder uc7_shipper_approveDraftTransportDocument() {
String carrierPartyName = threadLocalCarrierPartyName.get();
String shipperPartyName = threadLocalShipperPartyName.get();
Expand Down Expand Up @@ -961,6 +1065,17 @@ private static EblScenarioListBuilder uc14_carrier_confirmShippingInstructionsCo
EBL_NOTIFICATIONS_API, EBL_SI_NOTIFICATION_SCHEMA_NAME)));
}

private static EblScenarioListBuilder oob_carrier_processOutOfBoundTDUpdateRequest() {
String carrierPartyName = threadLocalCarrierPartyName.get();
String shipperPartyName = threadLocalShipperPartyName.get();
return new EblScenarioListBuilder(
previousAction ->
new UCX_Carrier_TDOnlyProcessOutOfBandUpdateOrAmendmentRequestDraftTransportDocumentAction(
carrierPartyName,
shipperPartyName,
(EblAction) previousAction));
}

private static EblScenarioListBuilder auc_shipper_sendOutOfOrderSIMessage(OutOfOrderMessageType outOfOrderMessageType, boolean useTDRef) {
String carrierPartyName = threadLocalCarrierPartyName.get();
String shipperPartyName = threadLocalShipperPartyName.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ public SortedMap<String, SortedSet<String>> getScenarioSuitesByStandardVersion()
Map.ofEntries(
Map.entry(
"3.0.0",
new TreeSet<>(
Set.of(
EblScenarioListBuilder.SCENARIO_SUITE_CONFORMANCE_SI_ONLY,
EblScenarioListBuilder.SCENARIO_SUITE_RI)))));
new TreeSet<>(EblScenarioListBuilder.SCENARIOS))));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.dcsa.conformance.standards.ebl.checks.ScenarioType;
import org.dcsa.conformance.standards.ebl.party.*;

import static org.dcsa.conformance.core.toolkit.JsonToolkit.OBJECT_MAPPER;

public abstract class EblAction extends ConformanceAction {
protected final int expectedStatus;
private final OverwritingReference<DynamicScenarioParameters> dspReference;
Expand All @@ -31,7 +33,7 @@ public EblAction(
this.dspReference =
previousAction == null
? new OverwritingReference<>(
null, new DynamicScenarioParameters(ScenarioType.REGULAR_SWB, null, null, null, null, null, null, null))
null, new DynamicScenarioParameters(ScenarioType.REGULAR_SWB, null, null, null, null, null, null, null, false, OBJECT_MAPPER.createObjectNode(), OBJECT_MAPPER.createObjectNode()))
: new OverwritingReference<>(previousAction.dspReference, null);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package org.dcsa.conformance.standards.ebl.action;

import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;

import org.dcsa.conformance.core.check.*;
import org.dcsa.conformance.core.traffic.ConformanceExchange;
import org.dcsa.conformance.core.traffic.HttpMessageType;
import org.dcsa.conformance.standards.ebl.checks.EBLChecks;
import org.dcsa.conformance.standards.ebl.crypto.Checksums;
import org.dcsa.conformance.standards.ebl.party.*;

public class Shipper_GetTransportDocumentAction extends EblAction {
Expand Down Expand Up @@ -36,6 +42,16 @@ public String getHumanReadablePrompt() {
.formatted(getDspSupplier().get().transportDocumentReference());
}

protected void doHandleExchange(ConformanceExchange exchange) {
super.doHandleExchange(exchange);
var dsp = getDspSupplier().get();
var response = exchange.getResponse().message().body().getJsonBody();
var previousTD = dsp.transportDocument();
dsp = dsp.withPreviousTransportDocument(previousTD)
.withTransportDocument(response);
getDspConsumer().accept(dsp);
}

@Override
public ConformanceCheck createCheck(String expectedApiVersion) {
var dsp = getDspSupplier().get();
Expand Down Expand Up @@ -65,8 +81,62 @@ protected Stream<? extends ConformanceCheck> createSubChecks() {
getMatchedExchangeUuid(),
HttpMessageType.RESPONSE,
responseSchemaValidator),
checkTDChanged(getMatchedExchangeUuid(), expectedApiVersion, dsp),
EBLChecks.tdPlusScenarioContentChecks(getMatchedExchangeUuid(), expectedApiVersion, expectedTdStatus, getCspSupplier(), getDspSupplier()));
}
};
}

private static ActionCheck checkTDChanged(UUID matched, String standardsVersion, DynamicScenarioParameters dsp) {
var deltaCheck = JsonAttribute.lostAttributeCheck(
"(ignored)",
dsp::previousTransportDocument,
(baselineTD, currentTD) -> {
if (baselineTD instanceof ObjectNode td) {
td.remove("transportDocumentStatus");
}
});
JsonContentMatchedValidation hadChangesCheck = (nodeToValidate, contextPath) -> {
var currentStatus = nodeToValidate.path("transportDocumentStatus").asText("");
var comparisonTD = dsp.previousTransportDocument();
var comparisonStatus = comparisonTD.path("transportDocumentStatus").asText("");
if (dsp.newTransportDocumentContent()) {
return Set.of();
}
if (!(nodeToValidate instanceof ObjectNode currentTDObj) || !(comparisonTD instanceof ObjectNode comparisonTDObj)) {
// Schema validation takes care of this.
return Set.of();
}

var currentTDObjCopy = currentTDObj.deepCopy();
var comparisonTDObjCopy = comparisonTDObj.deepCopy();
currentTDObjCopy.remove("transportDocumentStatus");
comparisonTDObjCopy.remove("transportDocumentStatus");
var checksum = Checksums.sha256CanonicalJson(currentTDObjCopy);
var previousChecksum = Checksums.sha256CanonicalJson(comparisonTDObjCopy);
if (checksum.equals(previousChecksum)) {
return Set.of("Expected a change, but it is the same TD. " + currentStatus + " - " + comparisonStatus);
}
return Set.of();
};
return JsonAttribute.contentChecks(
"",
"[Scenario] Validate TD changes match the expected",
EblRole::isCarrier,
matched,
HttpMessageType.RESPONSE,
standardsVersion,
JsonAttribute.customValidator("The TD match the scenario step",
JsonAttribute.ifMatchedThenElse(
// For some cases, we assume the TD will change in ways we cannot predict, so here
// we just effectively skip the check
//
// Common cases are new drafts and amendments.
(ignored) -> dsp.newTransportDocumentContent(),
hadChangesCheck,
deltaCheck::validate
)
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.stream.Stream;
import lombok.Getter;
import org.dcsa.conformance.core.check.*;
import org.dcsa.conformance.standards.ebl.checks.EBLChecks;
import org.dcsa.conformance.core.traffic.ConformanceExchange;
import org.dcsa.conformance.standards.ebl.party.TransportDocumentStatus;

@Getter
Expand Down Expand Up @@ -40,6 +40,15 @@ public ObjectNode asJsonNode() {
.put("acceptAmendmentRequest", acceptAmendmentRequest);
}

protected void doHandleExchange(ConformanceExchange exchange) {
super.doHandleExchange(exchange);
var dsp = getDspSupplier().get();
// Clear the flag if set.
if (dsp.newTransportDocumentContent()) {
getDspConsumer().accept(dsp.withNewTransportDocumentContent(false));
}
}

@Override
public ConformanceCheck createCheck(String expectedApiVersion) {
return new ConformanceCheck(getActionTitle()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.stream.Stream;
import lombok.Getter;
import org.dcsa.conformance.core.check.*;
import org.dcsa.conformance.core.traffic.ConformanceExchange;
import org.dcsa.conformance.standards.ebl.party.TransportDocumentStatus;

@Getter
Expand All @@ -25,6 +26,15 @@ public String getHumanReadablePrompt() {
.formatted(getDspSupplier().get().transportDocumentReference()));
}

protected void doHandleExchange(ConformanceExchange exchange) {
super.doHandleExchange(exchange);
var dsp = getDspSupplier().get();
// This is a re-issuance; those will de facto change the TD.
if (!dsp.newTransportDocumentContent()) {
getDspConsumer().accept(dsp.withNewTransportDocumentContent(true));
}
}

@Override
public ObjectNode asJsonNode() {
var dsp = getDspSupplier().get();
Expand Down
Loading
Loading