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

334 backend UI api wrapper negotiate contract endpoint with 02x #519

Closed
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
21 changes: 18 additions & 3 deletions extensions/wrapper/client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,27 @@ dependencies {
testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}")

testImplementation("${edcGroup}:control-plane-core:${edcVersion}")
testImplementation("${edcGroup}:junit:${edcVersion}")
testImplementation("${edcGroup}:management-api:${edcVersion}")
testImplementation("${edcGroup}:api-observability:${edcVersion}")
testImplementation("${edcGroup}:configuration-filesystem:${edcVersion}")
testImplementation("${edcGroup}:control-plane-aggregate-services:${edcVersion}")
testImplementation("${edcGroup}:http:${edcVersion}")
testImplementation("${edcGroup}:json-ld-spi:${edcVersion}")
testImplementation("${edcGroup}:dsp-http-spi:${edcVersion}")
testImplementation("${edcGroup}:dsp:${edcVersion}")
testImplementation("${edcGroup}:json-ld:${edcVersion}")
testImplementation("${edcGroup}:junit:${edcVersion}")
testImplementation("${edcGroup}:auth-tokenbased:${edcVersion}")
testImplementation("${edcGroup}:transfer-data-plane:${edcVersion}")
testImplementation("${edcGroup}:data-plane-selector-core:${edcVersion}")
testImplementation("${edcGroup}:data-plane-selector-client:${edcVersion}")
testImplementation("${edcGroup}:data-plane-http:${edcVersion}")
testImplementation("${edcGroup}:data-plane-framework:${edcVersion}")
testImplementation("${edcGroup}:data-plane-core:${edcVersion}")
testImplementation("${edcGroup}:data-plane-util:${edcVersion}")
testImplementation("${edcGroup}:iam-mock:${edcVersion}")
testImplementation(project(":extensions:wrapper:wrapper"))
testImplementation(project(":extensions:wrapper:wrapper-common-mappers"))
testImplementation(project(":extensions:test-backend-controller"))
testImplementation(project(":utils:test-connector-remote"))
testImplementation("io.rest-assured:rest-assured:${restAssured}")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
testImplementation("${edcGroup}:data-plane-selector-core:${edcVersion}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2023 sovity GmbH
*
* 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:
* sovity GmbH - initial API and implementation
*
*/

package de.sovity.edc.client;

import de.sovity.edc.client.gen.model.ContractNegotiationRequest;
import de.sovity.edc.ext.wrapper.api.common.mappers.PolicyJsonMapper;
import de.sovity.edc.extension.e2e.connector.ConnectorRemote;
import de.sovity.edc.extension.e2e.connector.MockDataAddressRemote;
import jakarta.json.JsonObject;
import org.awaitility.Awaitility;
import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry;
import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl;
import org.eclipse.edc.junit.annotations.ApiTest;
import org.eclipse.edc.junit.extensions.EdcExtension;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.policy.model.PolicyType;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;

import java.net.URI;
import java.time.Duration;
import java.util.UUID;

import static de.sovity.edc.extension.e2e.connector.DataTransferTestUtil.validateDataTransferred;
import static de.sovity.edc.extension.e2e.connector.config.ConnectorConfigFactory.basicEdcConfig;
import static de.sovity.edc.extension.e2e.connector.config.ConnectorRemoteConfigFactory.fromConnectorConfig;
import static io.restassured.http.ContentType.JSON;
import static jakarta.json.Json.createObjectBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates.FINALIZED;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE;
import static org.eclipse.edc.spi.CoreConstants.EDC_PREFIX;

@ApiTest
@ExtendWith(EdcExtension.class)
class ContractNegotiationApiServiceTest {


private static final String PROVIDER_PARTICIPANT_ID = "provider";
private static final String CONSUMER_PARTICIPANT_ID = "consumer";
private static final String TEST_BACKEND_TEST_DATA = UUID.randomUUID().toString();

@RegisterExtension
static EdcExtension providerEdcContext = new EdcExtension();
@RegisterExtension
static EdcExtension consumerEdcContext = new EdcExtension();

private ConnectorRemote providerConnector;
private ConnectorRemote consumerConnector;
private MockDataAddressRemote dataAddress;

private EdcClient consumerClient;

@BeforeEach
void setup() {
var providerConfig = basicEdcConfig(PROVIDER_PARTICIPANT_ID, 24000);
providerEdcContext.setConfiguration(providerConfig.getProperties());
providerConnector = new ConnectorRemote(fromConnectorConfig(providerConfig));

var consumerConfig = basicEdcConfig(CONSUMER_PARTICIPANT_ID, 25000);
consumerEdcContext.setConfiguration(consumerConfig.getProperties());
consumerConnector = new ConnectorRemote(fromConnectorConfig(consumerConfig));

consumerClient = EdcClient.builder()
.managementApiUrl(consumerConnector.getConfig().getManagementEndpoint().getUri().toString())
.managementApiKey(consumerConfig.getProperties().get("edc.api.auth.key"))
.build();

// We use the provider EDC as data sink / data source (it has the test-backend-controller extension)
dataAddress = new MockDataAddressRemote(providerConnector.getConfig().getDefaultEndpoint());
}

@Test
void testContractNegotiation__usingManagementApiOnly__todo_remove() {
// arrange
var assetId = UUID.randomUUID().toString();
providerConnector.createDataOffer(assetId, dataAddress.getDataSourceUrl(TEST_BACKEND_TEST_DATA));
String providerId = providerConnector.getParticipantId();
URI providerProtocolApi = providerConnector.getConfig().getProtocolEndpoint().getUri();
JsonObject destination = dataAddress.getDataSinkJsonLd();
var dataset = consumerConnector.getDatasetForAsset(assetId, providerProtocolApi);
var contractId = consumerConnector.getDatasetContractId(dataset);

var policyJsonLd = createObjectBuilder()
.add(CONTEXT, "https://www.w3.org/ns/odrl.jsonld")
.add(TYPE, "use")
.build()
.toString();

// act
var negotiationId = consumerConnector.prepareManagementApiCall()
.contentType(JSON)
.body(createObjectBuilder()
.add(CONTEXT, createObjectBuilder().add(EDC_PREFIX, EDC_NAMESPACE))
.add(TYPE, "NegotiationInitiateRequestDto")
.add("connectorId", providerId)
.add("consumerId", consumerConnector.getParticipantId())
.add("providerId", providerId)
.add("connectorAddress", providerProtocolApi.toString())
.add("protocol", "dataspace-protocol-http")
.add("offer", createObjectBuilder()
.add("offerId", contractId.toString())
.add("assetId", contractId.assetIdPart())
.add("policy", policyJsonLd.toString())
)
.build())
.when()
.post("/v2/contractnegotiations")
.then()
.statusCode(200)
.extract().body().jsonPath().getString(ID);

Awaitility.await().atMost(Duration.ofSeconds(60)).untilAsserted(() -> {
var state = consumerConnector.prepareManagementApiCall()
.contentType(JSON)
.when()
.get("/v2/contractnegotiations/{id}/state", negotiationId)
.then()
.statusCode(200)
.extract().body().jsonPath().getString("'edc:state'");
assertThat(state).isEqualTo(FINALIZED.name());
});

var contractAgreementId = consumerConnector.getContractAgreementId(negotiationId);

// assert
var transferProcessId = consumerConnector.initiateTransfer(
contractAgreementId,
assetId,
providerProtocolApi,
destination);

assertThat(transferProcessId).isNotNull();
validateDataTransferred(dataAddress.getDataSinkSpyUrl(), TEST_BACKEND_TEST_DATA);
}

@Test
void testContractNegotiation() {
// arrange
TypeTransformerRegistry typeTransformerRegistry = providerEdcContext.getContext().getService(ManagementApiTypeTransformerRegistry.class, false);
Policy policy = Policy.Builder.newInstance()
.assigner("sampleAssigner")
.assignee("sampleAssignee")
.target("sampleTarget")
.inheritsFrom("sampleInheritsFrom")
.type(PolicyType.OFFER)
.extensibleProperty("sampleKey", "sampleValue")
.build();
var policyJson = typeTransformerRegistry.transform(policy, JsonObject.class).getContent();
var assetId = UUID.randomUUID().toString();
providerConnector.createDataOffer(assetId, dataAddress.getDataSourceUrl(TEST_BACKEND_TEST_DATA));
String providerId = providerConnector.getParticipantId();
URI providerProtocolApi = providerConnector.getConfig().getProtocolEndpoint().getUri();
JsonObject destination = dataAddress.getDataSinkJsonLd();
var dataset = consumerConnector.getDatasetForAsset(assetId, providerProtocolApi);
var contractId = consumerConnector.getDatasetContractId(dataset);

var contractNegotiationRequest = ContractNegotiationRequest.builder()
.protocol("dataspace-protocol-http")
.counterPartyAddress(providerProtocolApi.toString())
.contractOfferId(contractId.toString())
.assetId(contractId.assetIdPart())
.policyJsonLd(String.valueOf(policyJson))
.build();

// act

var contractNegotiationDto = consumerClient.uiApi().initiateContractNegotiation(contractNegotiationRequest);

Awaitility.await().atMost(Duration.ofSeconds(60)).untilAsserted(() -> {
var state = consumerConnector.prepareManagementApiCall()
.contentType(JSON)
.when()
.get("/v2/contractnegotiations/{id}/state", contractNegotiationDto.getContractNegotiationId())
.then()
.statusCode(200)
.extract().body().jsonPath().getString("'edc:state'");
assertThat(state).isEqualTo(FINALIZED.name());
});


var existingContractNegotiationDto = consumerClient.uiApi().getContractNegotiation(contractNegotiationDto.getContractNegotiationId());
var state = existingContractNegotiationDto.getStatus();


// assert
assertThat(state).isEqualTo(FINALIZED.name());
var transferProcessId = consumerConnector.initiateTransfer(
contractNegotiationDto.getContractAgreementId(),
assetId,
providerProtocolApi,
destination);

assertThat(transferProcessId).isNotNull();
validateDataTransferred(dataAddress.getDataSinkSpyUrl(), TEST_BACKEND_TEST_DATA);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@
package de.sovity.edc.client;


import de.sovity.edc.client.gen.model.ContractNegotiationRequest;
import de.sovity.edc.client.gen.model.PolicyDefinitionCreateRequest;
import de.sovity.edc.client.gen.model.PolicyDefinitionDto;
import de.sovity.edc.client.gen.model.UiPolicyConstraint;
import de.sovity.edc.client.gen.model.UiPolicyCreateRequest;
import de.sovity.edc.client.gen.model.UiPolicyLiteral;
import jakarta.json.Json;
import lombok.SneakyThrows;
import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService;
import org.eclipse.edc.connector.spi.policydefinition.PolicyDefinitionService;
import org.eclipse.edc.junit.annotations.ApiTest;
import org.eclipse.edc.junit.extensions.EdcExtension;
Expand All @@ -32,7 +35,10 @@

import java.util.List;

import static jakarta.json.Json.createObjectBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;

@ApiTest
@ExtendWith(EdcExtension.class)
Expand Down
1 change: 1 addition & 0 deletions extensions/wrapper/wrapper-common-mappers/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
compileOnly("org.projectlombok:lombok:${lombokVersion}")

api("${edcGroup}:policy-model:${edcVersion}")
api("${edcGroup}:transform-core:${edcVersion}")
api(project(":extensions:wrapper:wrapper-common-api"))

implementation("org.apache.commons:commons-lang3:3.13.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.sovity.edc.ext.wrapper.api.common.mappers;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.sovity.edc.ext.wrapper.api.common.mappers.utils.AtomicConstraintMapper;
import de.sovity.edc.ext.wrapper.api.common.mappers.utils.ConstraintExtractor;
import de.sovity.edc.ext.wrapper.api.common.mappers.utils.MappingErrors;
import de.sovity.edc.ext.wrapper.api.common.mappers.utils.PolicyValidator;
import de.sovity.edc.ext.wrapper.api.common.model.UiPolicyCreateRequest;
import de.sovity.edc.ext.wrapper.api.common.model.UiPolicyDto;
import jakarta.json.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.eclipse.edc.policy.model.Action;
import org.eclipse.edc.policy.model.Constraint;
import org.eclipse.edc.policy.model.Permission;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.policy.model.PolicyType;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;

import java.util.ArrayList;

@RequiredArgsConstructor
public class PolicyJsonMapper {
private final TypeTransformerRegistry transformerRegistry;

@SneakyThrows
public Result<JsonObject> getPolicyJsonLd(Policy policy) {
return transformerRegistry.transform(policy, JsonObject.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
import de.sovity.edc.ext.wrapper.api.common.mappers.utils.PolicyValidator;
import de.sovity.edc.ext.wrapper.api.common.model.UiPolicyCreateRequest;
import de.sovity.edc.ext.wrapper.api.common.model.UiPolicyDto;
import jakarta.json.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.eclipse.edc.policy.model.Action;
import org.eclipse.edc.policy.model.Constraint;
import org.eclipse.edc.policy.model.Permission;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.policy.model.PolicyType;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;

import java.util.ArrayList;

Expand All @@ -25,7 +28,7 @@ public class PolicyMapper {
private final ObjectMapper jsonLdObjectMapper;
private final ConstraintExtractor constraintExtractor;
private final AtomicConstraintMapper atomicConstraintMapper;

private final TypeTransformerRegistry transformerRegistry;
/**
* Builds a simplified UI Policy Model from an ODRL Policy.
* <p>
Expand All @@ -34,14 +37,13 @@ public class PolicyMapper {
* @param policy ODRL policy
* @return ui policy
*/
@SneakyThrows
public UiPolicyDto buildPolicyDto(Policy policy) {
MappingErrors errors = MappingErrors.root();

var constraints = constraintExtractor.getPermissionConstraints(policy, errors);

return UiPolicyDto.builder()
.policyJsonLd(jsonLdObjectMapper.writeValueAsString(policy))
.policyJsonLd(getPolicyJsonLd(policy).toString())
.constraints(constraints)
.errors(errors.getErrors())
.build();
Expand Down Expand Up @@ -71,4 +73,30 @@ public Policy buildPolicy(UiPolicyCreateRequest policyCreateDto) {
.permission(permission)
.build();
}

/**
* Builds an ODRL Policy from JSON-LD.
* <p>
* This operation is lossless.
*
* @param policyJsonLd policy
* @return ODRL policy
*/
@SneakyThrows
public Policy buildPolicy(String policyJsonLd) {
return jsonLdObjectMapper.readValue(policyJsonLd, Policy.class);
}

/**
* Get an ODRL Policy as JSON-LD
* <p>
* This operation is lossless.
*
* @param policy ODRL policy
* @return JSON-LD
*/
@SneakyThrows
public Result<JsonObject> getPolicyJsonLd(Policy policy) {
return transformerRegistry.transform(policy,JsonObject.class);
}
}
Loading