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: adds OpenApi spec for the new signaling APIs #3906

Merged
merged 1 commit into from
Feb 22, 2024
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
35 changes: 35 additions & 0 deletions extensions/data-plane/data-plane-signaling-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 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`
id("io.swagger.core.v3.swagger-gradle-plugin")
}

dependencies {
api(project(":spi:common:web-spi"))
api(project(":spi:common:json-ld-spi"))
api(project(":spi:data-plane:data-plane-spi"))

implementation(libs.jakarta.rsApi)

}
edcBuild {
swagger {
apiGroup.set("control-api")
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* Copyright (c) 2024 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.dataplane.api.controller.v1;


import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.json.JsonObject;
import jakarta.ws.rs.container.AsyncResponse;
import org.eclipse.edc.connector.dataplane.api.model.DataFlowState;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.spi.types.domain.transfer.DataFlowResponseMessage;
import org.eclipse.edc.spi.types.domain.transfer.DataFlowSuspendMessage;
import org.eclipse.edc.spi.types.domain.transfer.DataFlowTerminateMessage;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
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.types.domain.transfer.DataFlowRequest.EDC_DATA_FLOW_START_MESSAGE_SIMPLE_TYPE;

@OpenAPIDefinition
@Tag(name = "Data Plane Signaling api API",
description = "Api targeted by the Control Plane to delegate a data transfer " +
"to the Data Plane after the contract has been successfully negotiated and agreed between the two participants. ")
public interface DataPlaneSignalingApi {

@Operation(description = "Initiates a data transfer for the given start message. If the data transfer is handled by the data plane, it will be performed asynchronously. " +
"If it's a consumer-pull scenario, a data address will be returned",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DataFlowStartMessageSchema.class))),
responses = {
@ApiResponse(responseCode = "400", description = "Failed to validate request"),
@ApiResponse(responseCode = "200", description = "Data transfer initiated",
content = @Content(schema = @Schema(implementation = DataFlowResponseMessageSchema.class))),
}
)
JsonObject start(JsonObject request, AsyncResponse response);

@Operation(description = "Get the current state of a data transfer.",
responses = {
@ApiResponse(responseCode = "200", description = "State of the data transfer",
content = @Content(schema = @Schema(implementation = DataFlowStateSchema.class))),
@ApiResponse(responseCode = "404", description = "Data transfer not found in the data plane")
}
)
JsonObject getTransferState(String id);

@Operation(description = "Terminates a data transfer.",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DataFlowTerminateMessageSchema.class))),
responses = {
@ApiResponse(responseCode = "204", description = "Data transfer terminated"),
@ApiResponse(responseCode = "404", description = "Data transfer not handled by the data plane"),
@ApiResponse(responseCode = "409", description = "Cannot terminate the transfer"),
}
)
void terminate(String id, JsonObject body);

@Operation(description = "Suspend a data transfer.",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DataFlowSuspendMessageSchema.class))),
responses = {
@ApiResponse(responseCode = "204", description = "Data transfer suspended"),
@ApiResponse(responseCode = "404", description = "Data transfer not handled by the data plane"),
@ApiResponse(responseCode = "409", description = "Cannot suspend the transfer"),
}
)
void suspend(String transferProcessId, JsonObject body);


@Schema(name = "DataFlowStartMessage", example = DataFlowStartMessageSchema.DATA_FLOW_START_EXAMPLE)
record DataFlowStartMessageSchema(
@Schema(name = CONTEXT, requiredMode = REQUIRED)
Object context,
@Schema(name = ID)
String id,
@Schema(name = TYPE, example = EDC_DATA_FLOW_START_MESSAGE_SIMPLE_TYPE)
String type,
@Schema(requiredMode = REQUIRED)
String processId,
@Schema(requiredMode = REQUIRED)
String datasetId,
@Schema(requiredMode = REQUIRED)
String participantId,
@Schema(requiredMode = REQUIRED)
String agreementId,
@Schema(requiredMode = REQUIRED)
String transferType,
@Schema(requiredMode = REQUIRED, example = DataAddressSchema.DATA_ADDRESS_EXAMPLE)
DataAddressSchema sourceDataAddress,
@Schema(example = DataAddressSchema.DATA_ADDRESS_EXAMPLE)
DataAddressSchema destinationDataAddress,
@Schema
String callbackAddress,
@Schema(requiredMode = REQUIRED)
FreeFormPropertiesSchema properties
) {
public static final String DATA_FLOW_START_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@id": "transfer-id",
"@type": "DataFlowStartMessage",
"processId": "process-id",
"datasetId": "dataset-id",
"participantId": "participant-id",
"agreementId": "agreement-id",
"transferType": "HttpData-PUSH",
"sourceDataAddress": {
"type": "HttpData",
"baseUrl": "https://jsonplaceholder.typicode.com/todos"
},
"destinationDataAddress": {
"type": "HttpData",
"baseUrl": "https://jsonplaceholder.typicode.com/todos"
},
"callbackAddress" : "http://control-plane",
"properties": {
"key": "value"
}
}
""";
}

@Schema(name = "Properties", additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
record FreeFormPropertiesSchema() {
}

@Schema(name = "DataAddress", additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
record DataAddressSchema(
@Schema(name = TYPE, example = DataAddress.EDC_DATA_ADDRESS_TYPE)
String type,
@Schema(name = "type")
String typeProperty
) {
public static final String DATA_ADDRESS_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "https://w3id.org/edc/v0.0.1/ns/DataAddress",
"type": "HttpData",
"baseUrl": "http://example.com"
}
""";
}

@Schema(name = "DataFlowResponseMessage", example = DataFlowResponseMessageSchema.DATA_FLOW_RESPONSE_MESSAGE_EXAMPLE)
record DataFlowResponseMessageSchema(
@Schema(name = CONTEXT)
Object context,
@Schema(name = TYPE, example = DataFlowResponseMessage.DATA_FLOW_RESPONSE_MESSAGE_SIMPLE_TYPE)
String ldType,
DataAddressSchema dataAddress
) {
public static final String DATA_FLOW_RESPONSE_MESSAGE_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "DataFlowResponseMessage",
"dataAddress": {
"type": "HttpData",
"baseUrl": "https://jsonplaceholder.typicode.com/todos"
}
}
""";
}

@Schema(name = "DataFlowState", example = DataFlowStateSchema.DATA_FLOW_STATE_EXAMPLE)
record DataFlowStateSchema(
@Schema(name = CONTEXT)
Object context,
@Schema(name = TYPE, example = DataFlowState.DATA_FLOW_STATE_SIMPLE_TYPE)
String ldType,
String state
) {
public static final String DATA_FLOW_STATE_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "DataFlowState",
"state": "STARTED"
}
""";
}

@Schema(name = "DataFlowTerminateMessage", example = DataFlowTerminateMessageSchema.TERMINATE_DATA_FLOW_EXAMPLE)
record DataFlowTerminateMessageSchema(
@Schema(name = TYPE, example = DataFlowTerminateMessage.DATA_FLOW_TERMINATE_MESSAGE_SIMPLE_TYPE)
String ldType,
String state
) {
public static final String TERMINATE_DATA_FLOW_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "DataFlowTerminateMessage",
"reason": "reason"
}
""";
}

@Schema(name = "DataFlowSuspendMessage", example = DataFlowSuspendMessageSchema.TERMINATE_DATA_FLOW_EXAMPLE)
record DataFlowSuspendMessageSchema(
@Schema(name = TYPE, example = DataFlowSuspendMessage.DATA_FLOW_SUSPEND_MESSAGE_SIMPLE_TYPE)
String ldType,
String state
) {
public static final String TERMINATE_DATA_FLOW_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "DataFlowSuspendMessage",
"reason": "reason"
}
""";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 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.dataplane.api.controller.v1;

import jakarta.json.JsonObject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.core.MediaType;

@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Path("/v1/dataflows")
public class DataPlaneSignalingApiController implements DataPlaneSignalingApi {

@POST
@Override
public JsonObject start(JsonObject request, AsyncResponse response) {
throw new UnsupportedOperationException();
}

@GET
@Path("/{id}/state")
@Override
public JsonObject getTransferState(@PathParam("id") String id) {
throw new UnsupportedOperationException();
}

@POST
@Path("/{id}/terminate")
@Override
public void terminate(@PathParam("id") String id, JsonObject body) {
throw new UnsupportedOperationException();
}

@POST
@Path("/{id}/suspend")
@Override
public void suspend(@PathParam("id") String id, JsonObject body) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 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.dataplane.api.model;

import org.eclipse.edc.connector.dataplane.spi.DataFlowStates;

import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE;

/**
* Wrapper for {@link DataFlowStates} formatted as String. Used to format a simple string as JSON.
*/
public record DataFlowState(String state) {
public static final String DATA_FLOW_STATE_SIMPLE_TYPE = "DataFlowState";
public static final String DATA_FLOW_STATE_TYPE = EDC_NAMESPACE + DATA_FLOW_STATE_SIMPLE_TYPE;
public static final String DATA_FLOW_STATE_STATE = EDC_NAMESPACE + "state";

}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ include(":extensions:control-plane:callback:callback-static-endpoint")

include(":extensions:data-plane:data-plane-client")
include(":extensions:data-plane:data-plane-control-api")
include(":extensions:data-plane:data-plane-signaling-api")
include(":extensions:data-plane:data-plane-public-api")

include(":extensions:data-plane:data-plane-http")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,28 @@
import java.util.Objects;
import java.util.UUID;

import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE;

/**
* A request to transfer data from a source to destination.
*/
@JsonTypeName("dataspaceconnector:dataflowrequest")
@JsonDeserialize(builder = DataFlowRequest.Builder.class)
Copy link
Member

Choose a reason for hiding this comment

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

do we actually need this here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Json stuff you mean?

public class DataFlowRequest implements Polymorphic, TraceCarrier {

public static final String DC_DATA_FLOW_START_MESSAGE_ID = EDC_NAMESPACE + "id";
public static final String DC_DATA_FLOW_START_MESSAGE_PROCESS_ID = EDC_NAMESPACE + "processId";
public static final String EDC_DATA_FLOW_START_MESSAGE_SIMPLE_TYPE = "DataFlowStartMessage";
public static final String EDC_DATA_FLOW_START_MESSAGE_TYPE = EDC_NAMESPACE + EDC_DATA_FLOW_START_MESSAGE_SIMPLE_TYPE;
public static final String EDC_DATA_FLOW_START_MESSAGE_TRANSFER_TYPE = EDC_NAMESPACE + "transferType";
public static final String EDC_DATA_FLOW_START_MESSAGE_DATASET_ID = EDC_NAMESPACE + "datasetId";
public static final String EDC_DATA_FLOW_START_MESSAGE_PARTICIPANT_ID = EDC_NAMESPACE + "participantId";
public static final String EDC_DATA_FLOW_START_MESSAGE_AGREEMENT_ID = EDC_NAMESPACE + "agreementId";
public static final String EDC_DATA_FLOW_START_MESSAGE_SOURCE_DATA_ADDRESS = EDC_NAMESPACE + "sourceDataAddress";
public static final String EDC_DATA_FLOW_START_MESSAGE_DESTINATION_DATA_ADDRESS = EDC_NAMESPACE + "destinationDataAddress";
public static final String EDC_DATA_FLOW_START_MESSAGE_DESTINATION_CALLBACK_ADDRESS = EDC_NAMESPACE + "callbackAddress";
public static final String EDC_DATA_FLOW_START_MESSAGE_DESTINATION_PROPERTIES = EDC_NAMESPACE + "properties";

private String id;
private String processId;

Expand Down
Loading
Loading