Skip to content

Commit

Permalink
feat(edrs): add EDR api schema and example (eclipse-tractusx#705)
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood authored Aug 12, 2023
1 parent 95ca87a commit 91e03fc
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 8 deletions.
1 change: 1 addition & 0 deletions edc-extensions/edr/edr-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ dependencies {
testImplementation(libs.restAssured)
testImplementation(libs.edc.junit)
testImplementation(libs.edc.ext.jersey.providers)
testImplementation(libs.edc.core.transform)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
import org.eclipse.edc.api.model.ApiCoreSchema;
import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema;
import org.eclipse.edc.web.spi.ApiErrorDetail;
import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto;
import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry;
import org.eclipse.tractusx.edc.api.edr.schema.EdrSchema;

@OpenAPIDefinition
@Tag(name = "Control Plane EDR Api")
Expand All @@ -41,18 +40,18 @@ public interface EdrApi {
@ApiResponse(responseCode = "400", description = "Request body was malformed",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))),
})
JsonObject initiateEdrNegotiation(@Schema(implementation = NegotiateEdrRequestDto.class) JsonObject dto);
JsonObject initiateEdrNegotiation(@Schema(implementation = EdrSchema.NegotiateEdrRequestSchema.class) JsonObject dto);

@Operation(description = "Returns all EndpointDataReference entry according to a query",
responses = {
@ApiResponse(responseCode = "200",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntry.class)))),
content = @Content(array = @ArraySchema(schema = @Schema(implementation = EdrSchema.EndpointDataReferenceEntrySchema.class)))),
@ApiResponse(responseCode = "400", description = "Request was malformed",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) }
)
JsonArray queryEdrs(String assetId, String agreementId, String providerId);

@Operation(description = "Gets an EDR with the given transfer process ID",
@Operation(description = "Gets an EDR with the given transfer process ID",
responses = {
@ApiResponse(responseCode = "200", description = "The EDR cached",
content = @Content(schema = @Schema(implementation = ManagementApiSchema.DataAddressSchema.class))),
Expand All @@ -64,10 +63,9 @@ public interface EdrApi {
)
JsonObject getEdr(String transferProcessId);

@Operation(description = "Delete an EDR with the given transfer process ID",
@Operation(description = "Delete an EDR with the given transfer process ID",
responses = {
@ApiResponse(responseCode = "200", description = "The EDR cached",
content = @Content(schema = @Schema(implementation = ManagementApiSchema.DataAddressSchema.class))),
@ApiResponse(responseCode = "200", description = "The EDR cached was deleted successfully"),
@ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))),
@ApiResponse(responseCode = "404", description = "An EDR with the given ID does not exist",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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.tractusx.edc.api.edr.schema;

import io.swagger.v3.oas.annotations.media.Schema;
import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema;
import org.eclipse.edc.connector.api.management.contractnegotiation.ContractNegotiationApi;
import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry;

import java.util.List;

import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE;
import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.EndpointDataReferenceEntrySchema.ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE;
import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.NegotiateEdrRequestSchema.NEGOTIATE_EDR_REQUEST_EXAMPLE;

public class EdrSchema {

@Schema(name = "NegotiateEdrRequest", example = NEGOTIATE_EDR_REQUEST_EXAMPLE)
public record NegotiateEdrRequestSchema(
@Schema(name = TYPE, example = EDR_REQUEST_DTO_TYPE)
String type,
String protocol,
String connectorAddress,
@Deprecated(since = "0.1.3")
@Schema(deprecated = true, description = "please use providerId instead")
String connectorId,
String providerId,
ContractNegotiationApi.ContractOfferDescriptionSchema offer,
List<ManagementApiSchema.CallbackAddressSchema> callbackAddresses) {

public static final String NEGOTIATE_EDR_REQUEST_EXAMPLE = """
{
"@context": { "edc": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "NegotiateEdrRequestDto",
"connectorAddress": "http://provider-address",
"protocol": "dataspace-protocol-http",
"providerId": "provider-id",
"offer": {
"offerId": "offer-id",
"assetId": "asset-id",
"policy": {
"@context": "http://www.w3.org/ns/odrl.jsonld",
"@type": "Set",
"@id": "offer-id",
"permission": [{
"target": "asset-id",
"action": "display"
}]
}
},
"callbackAddresses": [{
"transactional": false,
"uri": "http://callback/url",
"events": ["contract.negotiation", "transfer.process"]
}]
}
""";
}

@Schema(name = "EndpointDataReferenceEntry", example = ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE)
public record EndpointDataReferenceEntrySchema(
@Schema(name = TYPE, example = EndpointDataReferenceEntry.SIMPLE_TYPE)
String type,
String agreementId,
String assetId,
String providerId,
String edrState,
Long expirationDate
) {
public static final String ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE = """
{
"@type": "tx:EndpointDataReferenceEntry",
"edc:agreementId": "MQ==:MQ==:ZTY3MzQ4YWEtNTdmZC00YzA0LTg2ZmQtMGMxNzk0MWM3OTkw",
"edc:transferProcessId": "78a66945-d638-4c0a-be71-b35a0318a410",
"edc:assetId": "1",
"edc:providerId": "BPNL00DATAP00001",
"tx:edrState": "NEGOTIATED",
"tx:expirationDate": 1690811364000,
"@context": {
"dct": "https://purl.org/dc/terms/",
"tx": "https://w3id.org/tractusx/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"dcat": "https://www.w3.org/ns/dcat/",
"odrl": "http://www.w3.org/ns/odrl/2/",
"dspace": "https://w3id.org/dspace/v0.8/"
}
}
""";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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.tractusx.edc.api.edr;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.JsonObjectToContractRequestTransformer;
import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl;
import org.eclipse.edc.core.transform.transformer.OdrlTransformersFactory;
import org.eclipse.edc.jsonld.JsonLdExtension;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.jsonld.util.JacksonJsonLd;
import org.eclipse.edc.junit.assertions.AbstractResultAssert;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto;
import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectToNegotiateEdrRequestDtoTransformer;
import org.eclipse.tractusx.edc.api.edr.validation.NegotiateEdrRequestDtoValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE;
import static org.eclipse.edc.junit.extensions.TestServiceExtensionContext.testServiceExtensionContext;
import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.EndpointDataReferenceEntrySchema.ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE;
import static org.eclipse.tractusx.edc.api.edr.schema.EdrSchema.NegotiateEdrRequestSchema.NEGOTIATE_EDR_REQUEST_EXAMPLE;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_EXPIRATION_DATE;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_STATE;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID;

public class EdrApiTest {

private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper();
private final JsonLd jsonLd = new JsonLdExtension().createJsonLdService(testServiceExtensionContext());

private final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl();


@BeforeEach
void setUp() {
transformer.register(new JsonObjectToContractRequestTransformer());
transformer.register(new JsonObjectToContractOfferDescriptionTransformer());
transformer.register(new JsonObjectToCallbackAddressTransformer());
transformer.register(new JsonObjectToNegotiateEdrRequestDtoTransformer());
OdrlTransformersFactory.jsonObjectToOdrlTransformers().forEach(transformer::register);
}

@Test
void edrRequestExample() throws JsonProcessingException {
var validator = NegotiateEdrRequestDtoValidator.instance();

var jsonObject = objectMapper.readValue(NEGOTIATE_EDR_REQUEST_EXAMPLE, JsonObject.class);
assertThat(jsonObject).isNotNull();

var expanded = jsonLd.expand(jsonObject);
AbstractResultAssert.assertThat(expanded).isSucceeded()
.satisfies(exp -> AbstractResultAssert.assertThat(validator.validate(exp)).isSucceeded())
.extracting(e -> transformer.transform(e, NegotiateEdrRequestDto.class))
.satisfies(transformResult -> AbstractResultAssert.assertThat(transformResult).isSucceeded()
.satisfies(transformed -> {
assertThat(transformed.getOffer()).isNotNull();
assertThat(transformed.getCallbackAddresses()).asList().hasSize(1);
assertThat(transformed.getProviderId()).isNotBlank();
}));
}

@Test
void edrEntryExample() throws JsonProcessingException {

var jsonObject = objectMapper.readValue(ENDPOINT_DATA_REFERENCE_ENTRY_EXAMPLE, JsonObject.class);
assertThat(jsonObject).isNotNull();

var expanded = jsonLd.expand(jsonObject);

AbstractResultAssert.assertThat(expanded).isSucceeded().satisfies(content -> {

assertThat(first(content, EDR_ENTRY_STATE).getJsonString(VALUE).getString())
.isEqualTo(jsonObject.getString("tx:edrState"));

assertThat(first(content, EDR_ENTRY_ASSET_ID).getJsonString(VALUE).getString())
.isEqualTo(jsonObject.getString("edc:assetId"));

assertThat(first(content, EDR_ENTRY_AGREEMENT_ID).getJsonString(VALUE).getString())
.isEqualTo(jsonObject.getString("edc:agreementId"));

assertThat(first(content, EDR_ENTRY_TRANSFER_PROCESS_ID).getJsonString(VALUE).getString())
.isEqualTo(jsonObject.getString("edc:transferProcessId"));

assertThat(first(content, EDR_ENTRY_PROVIDER_ID).getJsonString(VALUE).getString())
.isEqualTo(jsonObject.getString("edc:providerId"));

assertThat(first(content, EDR_ENTRY_EXPIRATION_DATE).getJsonNumber(VALUE).longValue())
.isEqualTo(jsonObject.getJsonNumber("tx:expirationDate").longValue());
});
}

private JsonObject first(JsonObject content, String name) {
return content.getJsonArray(name).getJsonObject(0);
}
}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ edc-core-jersey = { module = "org.eclipse.edc:jersey-core", version.ref = "edc"
edc-core-api = { module = "org.eclipse.edc:api-core", version.ref = "edc" }
edc-core-sql = { module = "org.eclipse.edc:sql-core", version.ref = "edc" }
edc-core-validator = { module = "org.eclipse.edc:validator-core", version.ref = "edc" }
edc-core-transform = { module = "org.eclipse.edc:transform-core", version.ref = "edc" }
edc-statemachine = { module = "org.eclipse.edc:state-machine", version.ref = "edc" }
edc-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" }
edc-api-management-config = { module = "org.eclipse.edc:management-api-configuration", version.ref = "edc" }
Expand Down

0 comments on commit 91e03fc

Please sign in to comment.