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

feat(edrs): add init edr request api validator #703

Merged
merged 1 commit into from
Aug 11, 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
2 changes: 2 additions & 0 deletions edc-extensions/edr/edr-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ dependencies {

implementation(libs.edc.api.management)
implementation(libs.edc.spi.aggregateservices)
implementation(libs.edc.core.validator)
implementation(libs.jakarta.rsApi)

testImplementation(testFixtures(libs.edc.core.jersey))
testImplementation(libs.restAssured)
testImplementation(libs.edc.junit)
testImplementation(libs.edc.ext.jersey.providers)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
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 java.util.List;

@OpenAPIDefinition
@Tag(name = "Control Plane EDR Api")
public interface EdrApi {
Expand All @@ -49,9 +48,9 @@ public interface EdrApi {
@ApiResponse(responseCode = "200",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntry.class)))),
@ApiResponse(responseCode = "400", description = "Request was malformed",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class))))}
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) }
)
List<JsonObject> queryEdrs(String assetId, String agreementId, String providerId);
JsonArray queryEdrs(String assetId, String agreementId, String providerId);

@Operation(description = "Gets an EDR with the given transfer process ID",
responses = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@
import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.eclipse.edc.web.spi.WebService;
import org.eclipse.tractusx.edc.api.edr.transform.EndpointDataReferenceToDataAddressTransformer;
import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectFromEndpointDataReferenceEntryTransformer;
import org.eclipse.tractusx.edc.api.edr.transform.JsonObjectToNegotiateEdrRequestDtoTransformer;
import org.eclipse.tractusx.edc.api.edr.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer;
import org.eclipse.tractusx.edc.api.edr.validation.NegotiateEdrRequestDtoValidator;
import org.eclipse.tractusx.edc.edr.spi.service.EdrService;

import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE;
import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE;
import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX;

Expand All @@ -46,13 +50,20 @@ public class EdrApiExtension implements ServiceExtension {
@Inject
private JsonLd jsonLdService;

@Inject
private JsonObjectValidatorRegistry validatorRegistry;

@Inject
private Monitor monitor;

@Override
public void initialize(ServiceExtensionContext context) {
jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE);
transformerRegistry.register(new NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer());
transformerRegistry.register(new JsonObjectToNegotiateEdrRequestDtoTransformer());
transformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer());
transformerRegistry.register(new EndpointDataReferenceToDataAddressTransformer());
webService.registerResource(apiConfig.getContextAlias(), new EdrController(edrService, jsonLdService, transformerRegistry));
validatorRegistry.register(EDR_REQUEST_DTO_TYPE, NegotiateEdrRequestDtoValidator.instance());
webService.registerResource(apiConfig.getContextAlias(), new EdrController(edrService, transformerRegistry, validatorRegistry, monitor));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.eclipse.tractusx.edc.api.edr;

import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
Expand All @@ -26,50 +27,52 @@
import jakarta.ws.rs.core.MediaType;
import org.eclipse.edc.api.model.IdResponse;
import org.eclipse.edc.connector.api.management.configuration.transform.ManagementApiTypeTransformerRegistry;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.query.Criterion;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.eclipse.edc.web.spi.exception.InvalidRequestException;
import org.eclipse.edc.web.spi.exception.ValidationFailureException;
import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto;
import org.eclipse.tractusx.edc.edr.spi.service.EdrService;
import org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry;
import org.eclipse.tractusx.edc.edr.spi.types.NegotiateEdrRequest;

import java.util.List;
import java.util.stream.Collectors;

import static jakarta.json.stream.JsonCollectors.toJsonArray;
import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper;
import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.AGREEMENT_ID;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.ASSET_ID;
import static org.eclipse.tractusx.edc.edr.spi.types.EndpointDataReferenceEntry.PROVIDER_ID;

@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Path("/edrs")
public class EdrController implements EdrApi {

private final EdrService edrService;
private final ManagementApiTypeTransformerRegistry transformerRegistry;
private final JsonLd jsonLdService;

private Monitor monitor;
private final JsonObjectValidatorRegistry validatorRegistry;
private final Monitor monitor;

public EdrController(EdrService edrService, JsonLd jsonLdService, ManagementApiTypeTransformerRegistry transformerRegistry) {
public EdrController(EdrService edrService, ManagementApiTypeTransformerRegistry transformerRegistry,
JsonObjectValidatorRegistry validatorRegistry, Monitor monitor) {
this.edrService = edrService;
this.jsonLdService = jsonLdService;
this.transformerRegistry = transformerRegistry;
this.validatorRegistry = validatorRegistry;
this.monitor = monitor;
}

@POST
@Override
public JsonObject initiateEdrNegotiation(JsonObject requestObject) {
var edrNegotiationRequest = jsonLdService.expand(requestObject)
.compose(expanded -> transformerRegistry.transform(expanded, NegotiateEdrRequestDto.class))
validatorRegistry.validate(EDR_REQUEST_DTO_TYPE, requestObject).orElseThrow(ValidationFailureException::new);

var edrNegotiationRequest = transformerRegistry.transform(requestObject, NegotiateEdrRequestDto.class)
.compose(dto -> transformerRegistry.transform(dto, NegotiateEdrRequest.class))
.orElseThrow(InvalidRequestException::new);

Expand All @@ -81,25 +84,23 @@ public JsonObject initiateEdrNegotiation(JsonObject requestObject) {
.build();

return transformerRegistry.transform(idResponse, JsonObject.class)
.compose(jsonLdService::compact)
.orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail()));
}

@GET
@Override
public List<JsonObject> queryEdrs(@QueryParam("assetId") String assetId, @QueryParam("agreementId") String agreementId, @QueryParam("providerId") String providerId) {
public JsonArray queryEdrs(@QueryParam("assetId") String assetId, @QueryParam("agreementId") String agreementId, @QueryParam("providerId") String providerId) {
if (assetId == null && agreementId == null) {
throw new InvalidRequestException("At least one of this query parameter is required [assetId,agreementId]");
}
return edrService.findBy(querySpec(assetId, agreementId, providerId))
.orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class))
.stream()
.map(edrCached -> transformerRegistry.transform(edrCached, JsonObject.class)
.compose(jsonLdService::compact))
.map(edrCached -> transformerRegistry.transform(edrCached, JsonObject.class))
.peek(this::logIfError)
.filter(Result::succeeded)
.map(Result::getContent)
.collect(Collectors.toList());
.collect(toJsonArray());
}

@GET
Expand All @@ -109,7 +110,6 @@ public JsonObject getEdr(@PathParam("id") String transferProcessId) {
var edr = edrService.findByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId));
return transformerRegistry.transform(edr, DataAddress.class)
.compose(dataAddress -> transformerRegistry.transform(dataAddress, JsonObject.class))
.compose(jsonLdService::compact)
.orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail()));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.validation;

import jakarta.json.JsonObject;
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryObject;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
import org.eclipse.edc.validator.spi.Validator;

import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.ASSET_ID;
import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.OFFER_ID;
import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription.POLICY;
import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_ADDRESS;
import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER;
import static org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL;


public class NegotiateEdrRequestDtoValidator {

private NegotiateEdrRequestDtoValidator() {
}

public static Validator<JsonObject> instance() {
return JsonObjectValidator.newValidator()
.verify(EDR_REQUEST_DTO_CONNECTOR_ADDRESS, MandatoryValue::new)
.verify(EDR_REQUEST_DTO_PROTOCOL, MandatoryValue::new)
.verify(EDR_REQUEST_DTO_OFFER, MandatoryObject::new)
.verifyObject(EDR_REQUEST_DTO_OFFER, v -> v
.verify(OFFER_ID, MandatoryValue::new)
.verify(ASSET_ID, MandatoryValue::new)
.verify(POLICY, MandatoryObject::new)
)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.eclipse.tractusx.edc.api.edr;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.specification.RequestSpecification;
import jakarta.json.Json;
import jakarta.json.JsonObject;
Expand All @@ -23,13 +24,18 @@
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation;
import org.eclipse.edc.jsonld.TitaniumJsonLd;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.jsonld.util.JacksonJsonLd;
import org.eclipse.edc.junit.annotations.ApiTest;
import org.eclipse.edc.service.spi.result.ServiceResult;
import org.eclipse.edc.spi.query.Criterion;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Violation;
import org.eclipse.edc.web.jersey.jsonld.JerseyJsonLdInterceptor;
import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase;
import org.eclipse.tractusx.edc.api.edr.dto.NegotiateEdrRequestDto;
import org.eclipse.tractusx.edc.edr.spi.service.EdrService;
Expand Down Expand Up @@ -63,6 +69,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

@ApiTest
Expand All @@ -72,6 +79,7 @@ public class EdrControllerTest extends RestControllerTestBase {
private final JsonLd jsonLdService = new TitaniumJsonLd(monitor);
EdrService edrService = mock(EdrService.class);
ManagementApiTypeTransformerRegistry transformerRegistry = mock();
JsonObjectValidatorRegistry validatorRegistry = mock();

@BeforeEach
void setup() {
Expand All @@ -81,6 +89,7 @@ void setup() {

@Test
void initEdrNegotiation_shouldWork_whenValidRequest() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success());

var openRequest = openRequest();
var contractNegotiation = getContractNegotiation();
Expand All @@ -105,6 +114,7 @@ void initEdrNegotiation_shouldWork_whenValidRequest() {

@Test
void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success());

var request = NegotiateEdrRequestDto.Builder.newInstance().build();
when(transformerRegistry.transform(any(JsonObject.class), eq(NegotiateEdrRequestDto.class))).thenReturn(Result.failure("fail"));
Expand All @@ -118,6 +128,23 @@ void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() {

}

@Test
void initEdrNegotiation_shouldReturnBadRequest_whenValidationFails() {
when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(Violation.violation("failure", "failure path")));
var request = negotiationRequest();

given()
.port(port)
.body(request)
.contentType(MediaType.APPLICATION_JSON)
.post(EDR_PATH)
.then()
.statusCode(400)
.contentType(MediaType.APPLICATION_JSON);

verifyNoInteractions(transformerRegistry);
}

@Test
void initEdrNegotiation_shouldReturnError_whenNotFound() {
var transferProcessId = "id";
Expand Down Expand Up @@ -279,7 +306,13 @@ void queryEdrs_shouldFail_whenNoQueryParameter() {

@Override
protected Object controller() {
return new EdrController(edrService, jsonLdService, transformerRegistry);
return new EdrController(edrService, transformerRegistry, validatorRegistry, monitor);
}

@Override
protected Object additionalResource() {
final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper();
return new JerseyJsonLdInterceptor(this.jsonLdService, objectMapper);
}

private RequestSpecification baseRequest() {
Expand Down
Loading