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(ControlPlaneAdapter): callback integration for transfer process #293

Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 4 additions & 1 deletion .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ on:
paths-ignore:
- 'charts/**'
branches:
- '*'
- releases
wolf4ood marked this conversation as resolved.
Show resolved Hide resolved
- release/**
- main
- previews/**
wolf4ood marked this conversation as resolved.
Show resolved Hide resolved
workflow_dispatch:

concurrency:
Expand Down
5 changes: 5 additions & 0 deletions edc-controlplane/edc-controlplane-base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies {
runtimeOnly(project(":edc-extensions:control-plane-adapter"))
runtimeOnly(project(":edc-extensions:provision-additional-headers"))
runtimeOnly(project(":edc-extensions:observability-api-customization"))
runtimeOnly(project(":edc-extensions:control-plane-adapter-api"))
runtimeOnly(project(":edc-extensions:control-plane-adapter-callback"))

runtimeOnly(libs.edc.core.controlplane)
runtimeOnly(libs.edc.config.filesystem)
Expand All @@ -24,4 +26,7 @@ dependencies {
runtimeOnly(libs.edc.ext.http)
runtimeOnly(libs.bundles.edc.monitoring)
runtimeOnly(libs.edc.transfer.dynamicreceiver)
runtimeOnly(libs.edc.controlplane.callback.dispatcher.event)
runtimeOnly(libs.edc.controlplane.callback.dispatcher.http)

}
30 changes: 30 additions & 0 deletions edc-extensions/control-plane-adapter-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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
*
*/

plugins {
`java-library`
`maven-publish`
id("io.swagger.core.v3.swagger-gradle-plugin")
}

dependencies {
implementation(project(":spi:control-plane-adapter-spi"))
implementation(libs.edc.api.management)
implementation(libs.edc.spi.aggregateservices)
implementation(libs.jakarta.rsApi)

testImplementation(testFixtures(libs.edc.core.jersey))
testImplementation(libs.restAssured)
testImplementation(libs.edc.junit)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.cp.adapter;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
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.validation.Valid;
import org.eclipse.edc.web.spi.ApiErrorDetail;
import org.eclipse.tractusx.edc.api.cp.adapter.dto.TransferOpenRequestDto;

@OpenAPIDefinition
@Tag(name = "Control Plane Adapter Api")
public interface AdapterApi {

@Operation(description = "Initiates a contract negotiation and a transfer process for a given offer and with the given counter part. Please note that successfully invoking this endpoint " +
"only means that the negotiation was initiated.",
responses = {
@ApiResponse(responseCode = "204", description = "The negotiation was successfully initiated."
),
wolf4ood marked this conversation as resolved.
Show resolved Hide resolved
@ApiResponse(responseCode = "400", description = "Request body was malformed",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))),
})
void openTransfer(@Valid TransferOpenRequestDto dto);
}
wolf4ood marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.cp.adapter;

import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.edc.web.spi.WebService;
import org.eclipse.tractusx.edc.api.cp.adapter.transform.TransferOpenRequestDtoToTransferOpenRequestTransformer;
import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService;

import java.time.Clock;

public class AdapterApiExtension implements ServiceExtension {

@Inject
private WebService webService;
@Inject
private ManagementApiConfiguration apiConfig;

@Inject
private AdapterTransferProcessService adapterTransferProcessService;

@Inject
private Clock clock;

@Inject
private TypeTransformerRegistry transformerRegistry;

@Override
public void initialize(ServiceExtensionContext context) {
var idsId = context.getSetting("edc.ids.id", null);

transformerRegistry.register(new TransferOpenRequestDtoToTransferOpenRequestTransformer(clock, idsId));
webService.registerResource(apiConfig.getContextAlias(), new AdapterController(adapterTransferProcessService, transformerRegistry));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.cp.adapter;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.edc.web.spi.exception.InvalidRequestException;
import org.eclipse.tractusx.edc.api.cp.adapter.dto.TransferOpenRequestDto;
import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService;
import org.eclipse.tractusx.edc.spi.cp.adapter.types.TransferOpenRequest;

import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper;

@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Path("/adapter/transfer")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note sure adapter is a good choice for a REST path. maybe something like /api/transfer? This is assuming that people who will use the CPA will likely not use the Management API as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are mounted in the same context of the Management API currently like the current control plane adapter

public class AdapterController implements AdapterApi {

private final AdapterTransferProcessService adapterTransferProcessService;

private final TypeTransformerRegistry transformerRegistry;

public AdapterController(AdapterTransferProcessService adapterTransferProcessService, TypeTransformerRegistry transformerRegistry) {
this.adapterTransferProcessService = adapterTransferProcessService;
this.transformerRegistry = transformerRegistry;
}

@POST
@Path("/open")
@Override
public void openTransfer(TransferOpenRequestDto dto) {
var transformResult = transformerRegistry.transform(dto, TransferOpenRequest.class)
.orElseThrow(InvalidRequestException::new);

adapterTransferProcessService.openTransfer(transformResult).orElseThrow(exceptionMapper(TransferOpenRequest.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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.cp.adapter.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.eclipse.edc.api.model.CallbackAddressDto;
import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription;

import java.util.ArrayList;
import java.util.List;

public class TransferOpenRequestDto {

@NotBlank(message = "connectorAddress is mandatory")
private String connectorAddress;
@NotBlank(message = "protocol is mandatory")
private String protocol = "ids-multipart";
@NotBlank(message = "connectorId is mandatory")
private String connectorId;

private String providerId;
private String consumerId;

@NotNull(message = "offer cannot be null")
private ContractOfferDescription offer;
private List<CallbackAddressDto> callbackAddresses = new ArrayList<>();

private TransferOpenRequestDto() {

}

public String getConnectorAddress() {
return connectorAddress;
}

public String getProtocol() {
return protocol;
}

public String getConnectorId() {
return connectorId;
}


public String getConsumerId() {
return consumerId;
}

public String getProviderId() {
return providerId;
}

public List<CallbackAddressDto> getCallbackAddresses() {
return callbackAddresses;
}

public ContractOfferDescription getOffer() {
return offer;
}

public ContractOfferDescription getOfferId() {
return offer;
}

public static final class Builder {
private final TransferOpenRequestDto dto;

private Builder() {
dto = new TransferOpenRequestDto();
}

public static Builder newInstance() {
return new Builder();
}

public Builder connectorAddress(String connectorAddress) {
dto.connectorAddress = connectorAddress;
return this;
}

public Builder protocol(String protocol) {
dto.protocol = protocol;
return this;
}

public Builder connectorId(String connectorId) {
dto.connectorId = connectorId;
return this;
}

public Builder offer(ContractOfferDescription offer) {
dto.offer = offer;
return this;
}

public Builder consumerId(String consumerId) {
dto.consumerId = consumerId;
return this;
}

public Builder providerId(String providerId) {
dto.providerId = providerId;
return this;
}

public Builder callbackAddresses(List<CallbackAddressDto> callbackAddresses) {
dto.callbackAddresses = callbackAddresses;
return this;
}

public TransferOpenRequestDto build() {
return dto;
}
}
}
Loading