Skip to content

Commit

Permalink
fix: transform policy to contract offer (#3626)
Browse files Browse the repository at this point in the history
* fix: transform policy to contract offer

* pr remarks
  • Loading branch information
ndr-brt committed Nov 20, 2023
1 parent 8fb17e3 commit 6012c7a
Show file tree
Hide file tree
Showing 17 changed files with 332 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ public JsonObject visitPolicy(Policy policy) {
.add(ODRL_PROHIBITION_ATTRIBUTE, prohibitionsBuilder)
.add(ODRL_OBLIGATION_ATTRIBUTE, obligationsBuilder);

Optional.ofNullable(policy.getTarget()).ifPresent(it -> builder.add(ODRL_TARGET_ATTRIBUTE, it));
Optional.ofNullable(policy.getTarget())
.ifPresent(target -> builder.add(
ODRL_TARGET_ATTRIBUTE,
jsonFactory.createArrayBuilder().add(jsonFactory.createObjectBuilder().add(ID, target)))
);

return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.policy.model.Action;
import org.eclipse.edc.policy.model.AndConstraint;
import org.eclipse.edc.policy.model.AtomicConstraint;
Expand All @@ -36,6 +38,7 @@
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE;
Expand Down Expand Up @@ -90,7 +93,14 @@ void transform_policyWithAllRuleTypes_returnJsonObject() {

assertThat(result).isNotNull();
assertThat(result.getJsonString(TYPE).getString()).isEqualTo(OdrlNamespace.ODRL_SCHEMA + "Set");
assertThat(result.getString(ODRL_TARGET_ATTRIBUTE)).isEqualTo("target");
assertThat(result.get(ODRL_TARGET_ATTRIBUTE))
.isNotNull()
.isInstanceOf(JsonArray.class)
.extracting(JsonValue::asJsonArray)
.matches(it -> !it.isEmpty())
.asList().first()
.asInstanceOf(type(JsonObject.class))
.matches(it -> it.getString(ID).equals("target"));

assertThat(result.get(ODRL_PERMISSION_ATTRIBUTE))
.isNotNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectFromContractNegotiationTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectFromNegotiationStateTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractOfferDescriptionTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.validation.ContractRequestValidator;
Expand Down Expand Up @@ -68,7 +69,8 @@ public void initialize(ServiceExtensionContext context) {
var factory = Json.createBuilderFactory(Map.of());
var monitor = context.getMonitor();

transformerRegistry.register(new JsonObjectToContractRequestTransformer(monitor));
transformerRegistry.register(new JsonObjectToContractRequestTransformer());
transformerRegistry.register(new JsonObjectToContractOfferTransformer());
transformerRegistry.register(new JsonObjectToContractOfferDescriptionTransformer());
transformerRegistry.register(new JsonObjectToTerminateNegotiationCommandTransformer());
transformerRegistry.register(new JsonObjectFromContractNegotiationTransformer(factory));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.connector.api.management.contractnegotiation.transform;

import jakarta.json.JsonObject;
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.types.domain.offer.ContractOffer;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class JsonObjectToContractOfferTransformer extends AbstractJsonLdTransformer<JsonObject, ContractOffer> {

public JsonObjectToContractOfferTransformer() {
super(JsonObject.class, ContractOffer.class);
}

@Override
public @Nullable ContractOffer transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {
var policy = context.transform(jsonObject, Policy.class);
if (policy == null) {
return null;
}
var id = nodeId(jsonObject);
return ContractOffer.Builder.newInstance()
.id(id)
.assetId(policy.getTarget())
.policy(policy)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@
import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest;
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.types.domain.callback.CallbackAddress;
import org.eclipse.edc.spi.types.domain.offer.ContractOffer;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;

import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CALLBACK_ADDRESSES;
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CONNECTOR_ADDRESS;
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.CONTRACT_REQUEST_COUNTER_PARTY_ADDRESS;
Expand All @@ -38,11 +34,8 @@

public class JsonObjectToContractRequestTransformer extends AbstractJsonLdTransformer<JsonObject, ContractRequest> {

private final Monitor monitor;

public JsonObjectToContractRequestTransformer(Monitor monitor) {
public JsonObjectToContractRequestTransformer() {
super(JsonObject.class, ContractRequest.class);
this.monitor = monitor;
}

@Override
Expand All @@ -52,31 +45,33 @@ public JsonObjectToContractRequestTransformer(Monitor monitor) {
.counterPartyAddress(counterPartyAddressOrConnectorAddress(jsonObject, context))
.protocol(transformString(jsonObject.get(PROTOCOL), context));

var policyJson = jsonObject.get(POLICY);
if (policyJson != null) {
var policy = transformObject(jsonObject.get(POLICY), Policy.class, context);
contractRequestBuilder.policy(policy);
contractRequestBuilder.contractOffer(contractOffer(jsonObject, context));

var callbackAddress = jsonObject.get(CALLBACK_ADDRESSES);
if (callbackAddress != null) {
contractRequestBuilder.callbackAddresses(transformArray(callbackAddress, CallbackAddress.class, context));
}

return contractRequestBuilder.build();
}

private ContractOffer contractOffer(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {
var policy = jsonObject.get(POLICY);
if (policy != null) {
return transformObject(policy, ContractOffer.class, context);
}

var offerJson = jsonObject.get(OFFER);
if (offerJson != null) {
var contractOfferDescription = transformObject(jsonObject.get(OFFER), ContractOfferDescription.class, context);
var contractOffer = ContractOffer.Builder.newInstance()
return ContractOffer.Builder.newInstance()
.id(contractOfferDescription.getOfferId())
.assetId(contractOfferDescription.getAssetId())
.policy(contractOfferDescription.getPolicy())
.build();
contractRequestBuilder.contractOffer(contractOffer);
}

var callbackAddress = jsonObject.get(CALLBACK_ADDRESSES);
if (callbackAddress != null) {
var addresses = new ArrayList<CallbackAddress>();
transformArrayOrObject(callbackAddress, CallbackAddress.class, addresses::add, context);
contractRequestBuilder.callbackAddresses(addresses);
}

return contractRequestBuilder.build();
return null;
}

private String getProviderId(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.validator.jsonobject.JsonLdPath;
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryIdNotBlank;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryObject;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;
import org.eclipse.edc.validator.spi.Violation;

import static java.lang.String.format;
import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.ASSET_ID;
Expand All @@ -34,6 +34,7 @@
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.OFFER;
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.POLICY;
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest.PROTOCOL;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_TARGET_ATTRIBUTE;

public class ContractRequestValidator {

Expand All @@ -60,12 +61,15 @@ public ValidationResult validate(JsonObject input) {
).build().validate(input);
}

var policyValidity = new MandatoryObject(path.append(POLICY)).validate(input);
if (policyValidity.succeeded()) {
return ValidationResult.success();
}
var validator = JsonObjectValidator.newValidator()
.verify(POLICY, MandatoryObject::new)
.verifyObject(POLICY, builder -> builder
.verifyId(MandatoryIdNotBlank::new)
.verify(ODRL_TARGET_ATTRIBUTE, MandatoryObject::new)
.verifyObject(ODRL_TARGET_ATTRIBUTE, b -> b.verifyId(MandatoryIdNotBlank::new)))
.build();

return ValidationResult.failure(Violation.violation(format("'%s' or '%s' must not be empty", OFFER, POLICY), path.toString()));
return validator.validate(input);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ void getSingleContractNegotiationAgreement_whenNoneFound() {
}

@Test
void initiate_with_contractOffer() {
void initiate() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success());
var contractNegotiation = createContractNegotiation("cn1");
var responseBody = createObjectBuilder().add(TYPE, ID_RESPONSE_TYPE).add(ID, contractNegotiation.getId()).build();
Expand Down Expand Up @@ -363,39 +363,6 @@ void initiate_with_contractOffer() {
verifyNoMoreInteractions(transformerRegistry, service);
}

@Test
void initiate_with_policy() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success());
var contractNegotiation = createContractNegotiation("cn1");
var responseBody = createObjectBuilder().add(TYPE, ID_RESPONSE_TYPE).add(ID, contractNegotiation.getId()).build();

when(transformerRegistry.transform(any(JsonObject.class), eq(ContractRequest.class))).thenReturn(Result.success(
ContractRequest.Builder.newInstance()
.protocol("test-protocol")
.providerId("test-provider-id")
.counterPartyAddress("test-cb")
.policy(Policy.Builder.newInstance().build())
.build()));

when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(responseBody));
when(service.initiateNegotiation(any(ContractRequest.class))).thenReturn(contractNegotiation);

when(transformerRegistry.transform(any(IdResponse.class), eq(JsonObject.class))).thenReturn(Result.success(responseBody));

baseRequest()
.contentType(JSON)
.body(createObjectBuilder().build())
.post()
.then()
.statusCode(200)
.body(ID, is(contractNegotiation.getId()));

verify(service).initiateNegotiation(any());
verify(transformerRegistry).transform(any(JsonObject.class), eq(ContractRequest.class));
verify(transformerRegistry).transform(any(IdResponse.class), eq(JsonObject.class));
verifyNoMoreInteractions(transformerRegistry, service);
}

@Test
void initiate_shouldReturnBadRequest_whenValidationFails() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("error", "path")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import jakarta.json.JsonObject;
import org.eclipse.edc.api.transformer.JsonObjectToCallbackAddressTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractOfferDescriptionTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer;
import org.eclipse.edc.connector.api.management.contractnegotiation.validation.ContractRequestValidator;
Expand Down Expand Up @@ -56,7 +57,8 @@ class ContractNegotiationApiTest {

@BeforeEach
void setUp() {
transformer.register(new JsonObjectToContractRequestTransformer(monitor));
transformer.register(new JsonObjectToContractRequestTransformer());
transformer.register(new JsonObjectToContractOfferTransformer());
transformer.register(new JsonObjectToContractOfferDescriptionTransformer());
transformer.register(new JsonObjectToCallbackAddressTransformer());
transformer.register(new JsonObjectToTerminateNegotiationCommandTransformer());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.connector.api.management.contractnegotiation.transform;

import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.jsonld.TitaniumJsonLd;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static jakarta.json.Json.createObjectBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OBLIGATION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PERMISSION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_SET;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PROHIBITION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_TARGET_ATTRIBUTE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class JsonObjectToContractOfferTransformerTest {

private final JsonLd jsonLd = new TitaniumJsonLd(mock(Monitor.class));
private final TransformerContext context = mock();
private JsonObjectToContractOfferTransformer transformer;

@BeforeEach
void setUp() {
transformer = new JsonObjectToContractOfferTransformer();
}

@Test
void transform() {
var offerPolicy = createObjectBuilder()
.add(TYPE, ODRL_POLICY_TYPE_SET)
.add(ID, "test-offer-id")
.add(ODRL_TARGET_ATTRIBUTE, "test-asset")
.add(ODRL_PERMISSION_ATTRIBUTE, getJsonObject("permission"))
.add(ODRL_PROHIBITION_ATTRIBUTE, getJsonObject("prohibition"))
.add(ODRL_OBLIGATION_ATTRIBUTE, getJsonObject("duty"))
.build();

var policy = Policy.Builder.newInstance().target("test-asset").build();
when(context.transform(any(JsonValue.class), eq(Policy.class))).thenReturn(policy);

var result = transformer.transform(jsonLd.expand(offerPolicy).getContent(), context);

assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo("test-offer-id");
assertThat(result.getAssetId()).isEqualTo("test-asset");
}

@Test
void shouldReturnNull_whenPolicyIsNull() {
when(context.transform(any(JsonValue.class), eq(Policy.class))).thenReturn(null);

var result = transformer.transform(createObjectBuilder().build(), context);

assertThat(result).isNull();
}

private JsonObject getJsonObject(String type) {
return createObjectBuilder()
.add(TYPE, type)
.build();
}
}

0 comments on commit 6012c7a

Please sign in to comment.