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

fix: transform policy to contract offer #3626

Merged
merged 2 commits into from
Nov 20, 2023
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 @@ -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();
}
}
Loading
Loading