Skip to content

Commit

Permalink
feat: introduce typed transformation problem reporting (#3004)
Browse files Browse the repository at this point in the history
* Create problem builders and implement typed problem reporting

* Fix merge

* Formatting

* Fix javdoc
  • Loading branch information
jimmarino authored May 15, 2023
1 parent 38b96f2 commit cb5a4f3
Show file tree
Hide file tree
Showing 46 changed files with 1,121 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.eclipse.edc.connector.core.transform;

import org.eclipse.edc.transform.spi.ProblemBuilder;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -44,6 +45,11 @@ public void reportProblem(String problem) {
problems.add(problem);
}

@Override
public ProblemBuilder problem() {
return new ProblemBuilder(this);
}

@Override
public <INPUT, OUTPUT> @Nullable OUTPUT transform(INPUT object, Class<OUTPUT> outputType) {
if (object == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.catalog.spi.CatalogRequestMessage;
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.lang.String.format;
import static jakarta.json.JsonValue.ValueType.ARRAY;
import static jakarta.json.JsonValue.ValueType.OBJECT;
import static org.eclipse.edc.protocol.dsp.catalog.transform.DspCatalogPropertyAndTypeNames.DSPACE_CATALOG_REQUEST_TYPE;
import static org.eclipse.edc.protocol.dsp.catalog.transform.DspCatalogPropertyAndTypeNames.DSPACE_FILTER_PROPERTY;

/**
Expand All @@ -44,21 +45,35 @@ public JsonObjectToCatalogRequestMessageTransformer(ObjectMapper mapper) {
public @Nullable CatalogRequestMessage transform(@NotNull JsonObject object, @NotNull TransformerContext context) {
var builder = CatalogRequestMessage.Builder.newInstance();

if (object.get(DSPACE_FILTER_PROPERTY) != null) {
builder.querySpec(transformQuerySpec(object.get(DSPACE_FILTER_PROPERTY), context));
var querySpec = transformQuerySpec(object, context);
if (querySpec != null) {
builder.querySpec(querySpec);
}

return builder.build();
}

private QuerySpec transformQuerySpec(JsonValue value, TransformerContext context) {
@Nullable
private QuerySpec transformQuerySpec(JsonObject object, TransformerContext context) {
var value = object.get(DSPACE_FILTER_PROPERTY);
if (value == null) {
return null;
}

if (value instanceof JsonObject) {
return mapper.convertValue(value, QuerySpec.class);
} else if (value instanceof JsonArray) {
var array = (JsonArray) value;
return transformQuerySpec(array.getJsonObject(0), context);
} else {
context.reportProblem(format("Expected filter to be JsonObject or JsonArray, but was %s", value.getClass().getSimpleName()));
context.problem()
.unexpectedType()
.type(DSPACE_CATALOG_REQUEST_TYPE)
.property(DSPACE_FILTER_PROPERTY)
.actual(value.getValueType())
.expected(OBJECT)
.expected(ARRAY)
.report();
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ public JsonObjectFromContractAgreementMessageTransformer(JsonBuilderFactory json

var policy = context.transform(agreement.getPolicy(), JsonObject.class);
if (policy == null) {
context.reportProblem("Cannot transform from ContractAgreementMessage with null policy");
context.problem()
.nullProperty()
.type(ContractAgreementMessage.class)
.property("policy")
.report();
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,16 @@ public JsonObjectFromContractNegotiationTransformer(JsonBuilderFactory jsonFacto
}

private String state(Integer state, TransformerContext context) {
switch (ContractNegotiationStates.from(state)) {
var negotiationState = ContractNegotiationStates.from(state);
if (negotiationState == null) {
context.problem()
.nullProperty()
.type(ContractNegotiation.class)
.property(DSPACE_NEGOTIATION_PROPERTY_STATE)
.report();
return null;
}
switch (negotiationState) {
case REQUESTING:
case REQUESTED:
return DSPACE_NEGOTIATION_STATE_REQUESTED;
Expand All @@ -83,7 +92,13 @@ private String state(Integer state, TransformerContext context) {
case TERMINATED:
return DSPACE_NEGOTIATION_STATE_TERMINATED;
default:
context.reportProblem(String.format("Could not map state %s in ContractNegotiation", state));
context.problem()
.unexpectedType()
.type(ContractNegotiation.class)
.property("state")
.actual(negotiationState.toString())
.expected(ContractNegotiationStates.class)
.report();
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ public JsonObjectFromContractRequestMessageTransformer(JsonBuilderFactory jsonFa
builder.add(DSPACE_NEGOTIATION_PROPERTY_DATASET, requestMessage.getContractOffer().getAssetId());
var policy = context.transform(requestMessage.getContractOffer().getPolicy(), JsonObject.class);
if (policy == null) {
context.reportProblem("Cannot transform from ContractRequestMessage policy");
context.problem()
.nullProperty()
.type(ContractRequestMessage.class)
.property("contractOffer")
.report();
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import java.time.format.DateTimeParseException;
import java.util.Set;

import static java.lang.String.format;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_AGREEMENT_MESSAGE;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_AGREEMENT;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_CONSUMER_ID;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID;
Expand All @@ -50,7 +51,11 @@ public JsonObjectToContractAgreementMessageTransformer() {
public @Nullable ContractAgreementMessage transform(@NotNull JsonObject object, @NotNull TransformerContext context) {
var messageBuilder = ContractAgreementMessage.Builder.newInstance();
if (!transformMandatoryString(object.get(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID), messageBuilder::processId, context)) {
context.reportProblem(format("No '%s' specified on ContractAgreementMessage", DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID)
.report();
return null;
}

Expand All @@ -63,13 +68,17 @@ public JsonObjectToContractAgreementMessageTransformer() {

var policy = context.transform(filteredJsonAgreement, Policy.class);
if (policy == null) {
context.reportProblem("Cannot transform to ContractAgreementMessage with invalid policy");
context.problem()
.invalidProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_AGREEMENT)
.report();
return null;
}

var agreement = contractAgreement(jsonAgreement, policy, context);
if (agreement == null) {
context.reportProblem("Cannot transform to ContractAgreementMessage with null agreement");
// problem already reported
return null;
}

Expand All @@ -91,18 +100,30 @@ private ContractAgreement contractAgreement(JsonObject jsonAgreement, Policy pol
var builder = ContractAgreement.Builder.newInstance();
var agreementId = nodeId(jsonAgreement);
if (agreementId == null) {
context.reportProblem("No id specified on ContractAgreement");
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(ID)
.report();
return null;
}
builder.id(agreementId);

if (!transformMandatoryString(jsonAgreement.get(DSPACE_NEGOTIATION_PROPERTY_CONSUMER_ID), builder::consumerId, context)) {
context.reportProblem(format("No '%s' specified on ContractAgreement", DSPACE_NEGOTIATION_PROPERTY_CONSUMER_ID));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_CONSUMER_ID)
.report();
return null;
}

if (!transformMandatoryString(jsonAgreement.get(DSPACE_NEGOTIATION_PROPERTY_PROVIDER_ID), builder::providerId, context)) {
context.reportProblem(format("No '%s' specified on ContractAgreement", DSPACE_NEGOTIATION_PROPERTY_PROVIDER_ID));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_PROVIDER_ID)
.report();
return null;
}

Expand All @@ -111,13 +132,23 @@ private ContractAgreement contractAgreement(JsonObject jsonAgreement, Policy pol

var timestamp = transformString(jsonAgreement.get(DSPACE_NEGOTIATION_PROPERTY_TIMESTAMP), context);
if (timestamp == null) {
context.reportProblem(format("No '%s' specified on ContractAgreement", DSPACE_NEGOTIATION_PROPERTY_TIMESTAMP));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_TIMESTAMP)
.report();
return null;
}
try {
builder.contractSigningDate(Instant.parse(timestamp).getEpochSecond());
} catch (DateTimeParseException e) {
context.reportProblem(format("Invalid '%s' specified on ContractAgreement: %s", DSPACE_NEGOTIATION_PROPERTY_TIMESTAMP, e.getMessage()));
context.problem()
.invalidProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_TIMESTAMP)
.value(timestamp)
.error(e.getMessage())
.report();
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.lang.String.format;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_AGREEMENT_VERIFICATION_MESSAGE;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID;

/**
Expand All @@ -37,7 +37,11 @@ public JsonObjectToContractAgreementVerificationMessageTransformer() {
public @Nullable ContractAgreementVerificationMessage transform(@NotNull JsonObject object, @NotNull TransformerContext context) {
var builder = ContractAgreementVerificationMessage.Builder.newInstance();
if (!transformMandatoryString(object.get(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID), builder::processId, context)) {
context.reportProblem(format("ContractAgreementVerificationMessage is missing the '%s' property", DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_AGREEMENT_VERIFICATION_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID)
.report();
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.lang.String.format;
import static org.eclipse.edc.connector.contract.spi.types.agreement.ContractNegotiationEventMessage.Type.ACCEPTED;
import static org.eclipse.edc.connector.contract.spi.types.agreement.ContractNegotiationEventMessage.Type.FINALIZED;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_EVENT_MESSAGE;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE_ACCEPTED;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE_FINALIZED;
Expand All @@ -44,7 +44,11 @@ public JsonObjectToContractNegotiationEventMessageTransformer() {
var builder = ContractNegotiationEventMessage.Builder.newInstance();

if (!transformMandatoryString(object.get(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID), builder::processId, context)) {
context.reportProblem(format("ContractNegotiationEventMessage is missing the %s property", DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID));
context.problem()
.missingProperty()
.type(DSPACE_NEGOTIATION_EVENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID)
.report();
return null;
}

Expand All @@ -54,7 +58,13 @@ public JsonObjectToContractNegotiationEventMessageTransformer() {
} else if (DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE_FINALIZED.equals(eventType)) {
builder.type(FINALIZED);
} else {
context.reportProblem(format("Could not map '%s' in ContractNegotiationEventMessage: %s", DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE, eventType));
context.problem()
.unexpectedType()
.type(DSPACE_NEGOTIATION_EVENT_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE)
.expected(DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE_ACCEPTED)
.expected(DSPACE_NEGOTIATION_PROPERTY_EVENT_TYPE_FINALIZED)
.report();
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@

package org.eclipse.edc.protocol.dsp.negotiation.transform.to;

import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationTerminationMessage;
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.lang.String.format;
import static jakarta.json.JsonValue.ValueType.ARRAY;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_CODE;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_PROPERTY_REASON;
import static org.eclipse.edc.protocol.dsp.negotiation.transform.DspNegotiationPropertyAndTypeNames.DSPACE_NEGOTIATION_TERMINATION_MESSAGE;

/**
* Creates a {@link ContractNegotiationTerminationMessage} from a {@link JsonObject}.
Expand All @@ -39,7 +41,9 @@ public JsonObjectToContractNegotiationTerminationMessageTransformer() {
public @Nullable ContractNegotiationTerminationMessage transform(@NotNull JsonObject object, @NotNull TransformerContext context) {
var builder = ContractNegotiationTerminationMessage.Builder.newInstance();

transformString(object.get(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID), builder::processId, context);
if (!transformMandatoryString(object.get(DSPACE_NEGOTIATION_PROPERTY_PROCESS_ID), builder::processId, context)) {
return null;
}

var code = object.get(DSPACE_NEGOTIATION_PROPERTY_CODE);
if (code != null) { // optional property
Expand All @@ -48,13 +52,19 @@ public JsonObjectToContractNegotiationTerminationMessageTransformer() {

var reasons = object.get(DSPACE_NEGOTIATION_PROPERTY_REASON);
if (reasons != null) { // optional property
var result = typeValueArray(reasons, context);
if (result == null) {
context.reportProblem(format("Cannot transform property %s in ContractNegotiationTerminationMessage", DSPACE_NEGOTIATION_PROPERTY_REASON));
} else {
if (result.size() > 0) {
builder.rejectionReason(result.toString());
if (reasons instanceof JsonArray) {
var array = (JsonArray) reasons;
if (array.size() > 0) {
builder.rejectionReason(array.toString());
}
} else {
context.problem()
.unexpectedType()
.type(DSPACE_NEGOTIATION_TERMINATION_MESSAGE)
.property(DSPACE_NEGOTIATION_PROPERTY_REASON)
.actual(reasons.getValueType().toString())
.expected(ARRAY)
.report();
}
}

Expand Down
Loading

0 comments on commit cb5a4f3

Please sign in to comment.