Skip to content

Commit b69f6e4

Browse files
author
Pablo Pérez López
committed
Merge branch 'feature/440645-transferencia-inesdata' into 'develop'
Feature/440645 transferencia inesdata See merge request upm-inesdata/inesdata-connector!34
2 parents e88a279 + 69c3518 commit b69f6e4

File tree

10 files changed

+353
-0
lines changed

10 files changed

+353
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# InesData Transfer Process API
2+
Provides a management API to handle InesData Transfer Process entities. This API expands the functionality of the existing control-plane management API by introducing a new endpoint for the initialization of data transfers to the connector's own MinIO storage.
3+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
plugins {
2+
`java-library`
3+
id("com.gmv.inesdata.edc-application")
4+
}
5+
6+
dependencies {
7+
api(libs.edc.spi.core)
8+
implementation(libs.edc.transfer.process.api)
9+
implementation(libs.edc.api.management.lib)
10+
implementation(libs.edc.web.spi)
11+
12+
implementation(libs.edc.connector.core)
13+
implementation(libs.edc.api.core)
14+
implementation(libs.edc.lib.util)
15+
implementation(libs.edc.lib.transform)
16+
implementation(libs.edc.dsp.api.configuration)
17+
implementation(libs.edc.api.management.config)
18+
implementation(libs.swagger.annotations.jakarta)
19+
implementation(libs.edc.transaction.spi)
20+
implementation(libs.edc.lib.validator)
21+
implementation(libs.edc.validator.spi)
22+
implementation(libs.swagger.annotations.jakarta)
23+
runtimeOnly(libs.edc.spi.jsonld)
24+
runtimeOnly(libs.edc.json.ld.lib)
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.upm.inesdata.inesdatatransfer;
2+
3+
import jakarta.json.Json;
4+
import jakarta.json.JsonBuilderFactory;
5+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectFromTransferProcessTransformer;
6+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectFromTransferStateTransformer;
7+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToSuspendTransferTransformer;
8+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToTerminateTransferTransformer;
9+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToTransferRequestTransformer;
10+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.validation.TerminateTransferValidator;
11+
import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService;
12+
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
13+
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
14+
import org.eclipse.edc.spi.security.Vault;
15+
import org.eclipse.edc.spi.system.ServiceExtension;
16+
import org.eclipse.edc.spi.system.ServiceExtensionContext;
17+
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
18+
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
19+
import org.eclipse.edc.web.spi.WebService;
20+
import org.upm.inesdata.inesdatatransfer.controller.InesdataTransferProcessApiController;
21+
import org.upm.inesdata.inesdatatransfer.validations.InesdataTransferRequestValidator;
22+
23+
import java.util.Collections;
24+
25+
@Extension("Management API: Inesdata Transfer Process")
26+
public class InesdataTransferProcessApiExtension implements ServiceExtension {
27+
public static final String NAME = "Management API: Transfer Process";
28+
public static final String DEFAULT_VALUE = "";
29+
public static final String AWS_ACCESS_KEY = "edc.aws.access.key";
30+
public static final String AWS_SECRET_ACCESS = "edc.aws.secret.access.key";
31+
public static final String AWS_ENDPOINT_OVERRIDE = "edc.aws.endpoint.override";
32+
public static final String AWS_REGION = "edc.aws.region";
33+
public static final String AWS_BUCKET_NAME = "edc.aws.bucket.name";
34+
35+
36+
@Inject
37+
private WebService webService;
38+
@Inject
39+
private TypeTransformerRegistry transformerRegistry;
40+
@Inject
41+
private TransferProcessService service;
42+
@Inject
43+
private JsonObjectValidatorRegistry validatorRegistry;
44+
@Inject
45+
private Vault vault;
46+
47+
public InesdataTransferProcessApiExtension() {
48+
}
49+
50+
public String name() {
51+
return "Management API: Inesdata Transfer Process";
52+
}
53+
54+
public void initialize(ServiceExtensionContext context) {
55+
JsonBuilderFactory builderFactory = Json.createBuilderFactory(Collections.emptyMap());
56+
TypeTransformerRegistry managementApiTransformerRegistry = this.transformerRegistry.forContext("management-api");
57+
managementApiTransformerRegistry.register(new JsonObjectFromTransferProcessTransformer(builderFactory));
58+
managementApiTransformerRegistry.register(new JsonObjectFromTransferStateTransformer(builderFactory));
59+
managementApiTransformerRegistry.register(new JsonObjectToTerminateTransferTransformer());
60+
managementApiTransformerRegistry.register(new JsonObjectToSuspendTransferTransformer());
61+
managementApiTransformerRegistry.register(new JsonObjectToTransferRequestTransformer());
62+
// Leer las variables de entorno
63+
var accessKey = vault.resolveSecret(context.getSetting(AWS_ACCESS_KEY, DEFAULT_VALUE));
64+
var secretKey = vault.resolveSecret(context.getSetting(AWS_SECRET_ACCESS, DEFAULT_VALUE));
65+
var endpointOverride = context.getSetting(AWS_ENDPOINT_OVERRIDE, DEFAULT_VALUE);
66+
var regionName = context.getSetting(AWS_REGION, DEFAULT_VALUE);
67+
var bucketName = context.getSetting(AWS_BUCKET_NAME, DEFAULT_VALUE);
68+
69+
this.validatorRegistry.register("https://w3id.org/edc/v0.0.1/ns/TransferRequest", InesdataTransferRequestValidator.instance(context.getMonitor()));
70+
this.validatorRegistry.register("https://w3id.org/edc/v0.0.1/ns/TerminateTransfer", TerminateTransferValidator.instance());
71+
this.webService.registerResource("management", new InesdataTransferProcessApiController(context.getMonitor(), this.service, managementApiTransformerRegistry, this.validatorRegistry, bucketName, regionName, accessKey, secretKey, endpointOverride));
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package org.upm.inesdata.inesdatatransfer.controller;
2+
3+
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
4+
import io.swagger.v3.oas.annotations.Operation;
5+
import io.swagger.v3.oas.annotations.info.Info;
6+
import io.swagger.v3.oas.annotations.links.Link;
7+
import io.swagger.v3.oas.annotations.links.LinkParameter;
8+
import io.swagger.v3.oas.annotations.media.ArraySchema;
9+
import io.swagger.v3.oas.annotations.media.Content;
10+
import io.swagger.v3.oas.annotations.media.Schema;
11+
import io.swagger.v3.oas.annotations.parameters.RequestBody;
12+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
13+
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import jakarta.json.JsonArray;
15+
import jakarta.json.JsonObject;
16+
import org.eclipse.edc.api.management.schema.ManagementApiSchema;
17+
import org.eclipse.edc.api.model.ApiCoreSchema;
18+
import org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3;
19+
20+
import java.util.List;
21+
22+
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
23+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_TYPE;
24+
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT;
25+
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
26+
27+
@OpenAPIDefinition(
28+
info = @Info(
29+
version = "v3"
30+
)
31+
)
32+
@Tag(
33+
name = "Transfer Process V3"
34+
)
35+
public interface InesdataTransferProcessApi {
36+
String ASYNC_WARNING = "Due to the asynchronous nature of transfers, a successful response only indicates that the request was successfully received. This may take a long time, so clients must poll the /{id}/state endpoint to track the state.";
37+
38+
@Operation(
39+
description = "Initiates a data transfer with the given parameters. Due to the asynchronous nature of transfers, a successful response only indicates that the request was successfully received. This may take a long time, so clients must poll the /{id}/state endpoint to track the state.",
40+
requestBody = @RequestBody(
41+
content = {@Content(
42+
schema = @Schema(
43+
implementation = TransferRequestSchema.class
44+
)
45+
)}
46+
),
47+
responses = {@ApiResponse(
48+
responseCode = "200",
49+
description = "The transfer was successfully initiated. Returns the transfer process ID and created timestamp",
50+
content = {@Content(
51+
schema = @Schema(
52+
implementation = ApiCoreSchema.IdResponseSchema.class
53+
)
54+
)},
55+
links = {@Link(
56+
name = "poll-state",
57+
operationId = "getTransferProcessStateV3",
58+
parameters = {@LinkParameter(
59+
name = "id",
60+
expression = "$response.body#/id"
61+
)}
62+
)}
63+
), @ApiResponse(
64+
responseCode = "400",
65+
description = "Request body was malformed",
66+
content = {@Content(
67+
array = @ArraySchema(
68+
schema = @Schema(
69+
implementation = ApiCoreSchema.ApiErrorDetailSchema.class
70+
)
71+
)
72+
)}
73+
)}
74+
)
75+
JsonObject initiateTransferProcess(JsonObject var1);
76+
77+
78+
@Schema(name = "TransferRequest", example = TransferProcessApiV3.TransferRequestSchema.TRANSFER_REQUEST_EXAMPLE)
79+
record TransferRequestSchema(
80+
@Schema(name = CONTEXT, requiredMode = REQUIRED)
81+
Object context,
82+
@Schema(name = TYPE, example = TRANSFER_REQUEST_TYPE)
83+
String type,
84+
@Schema(requiredMode = REQUIRED)
85+
String protocol,
86+
@Schema(requiredMode = REQUIRED)
87+
String counterPartyAddress,
88+
@Schema(requiredMode = REQUIRED)
89+
String contractId,
90+
@Schema(deprecated = true)
91+
String assetId,
92+
@Schema(requiredMode = REQUIRED)
93+
String transferType,
94+
ApiCoreSchema.DataAddressSchema dataDestination,
95+
@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
96+
ManagementApiSchema.FreeFormPropertiesSchema privateProperties,
97+
List<ManagementApiSchema.CallbackAddressSchema> callbackAddresses) {
98+
99+
public static final String TRANSFER_REQUEST_EXAMPLE = """
100+
{
101+
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
102+
"@type": "https://w3id.org/edc/v0.0.1/ns/TransferRequest",
103+
"protocol": "dataspace-protocol-http",
104+
"counterPartyAddress": "http://provider-address",
105+
"contractId": "contract-id",
106+
"transferType": "transferType",
107+
"dataDestination": {
108+
"type": "data-destination-type"
109+
},
110+
"privateProperties": {
111+
"private-key": "private-value"
112+
},
113+
"callbackAddresses": [{
114+
"transactional": false,
115+
"uri": "http://callback/url",
116+
"events": ["contract.negotiation", "transfer.process"],
117+
"authKey": "auth-key",
118+
"authCodeId": "auth-code-id"
119+
}]
120+
}
121+
""";
122+
}
123+
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package org.upm.inesdata.inesdatatransfer.controller;
2+
3+
import jakarta.json.JsonObject;
4+
import jakarta.ws.rs.Consumes;
5+
import jakarta.ws.rs.POST;
6+
import jakarta.ws.rs.Path;
7+
import jakarta.ws.rs.Produces;
8+
import org.eclipse.edc.api.model.IdResponse;
9+
import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService;
10+
import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess;
11+
import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest;
12+
import org.eclipse.edc.spi.EdcException;
13+
import org.eclipse.edc.spi.constants.CoreConstants;
14+
import org.eclipse.edc.spi.monitor.Monitor;
15+
import org.eclipse.edc.spi.types.domain.DataAddress;
16+
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
17+
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
18+
import org.eclipse.edc.web.spi.exception.InvalidRequestException;
19+
import org.eclipse.edc.web.spi.exception.ValidationFailureException;
20+
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
import static java.lang.String.format;
25+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_TYPE;
26+
import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.mapToException;
27+
28+
@Consumes({ "application/json" })
29+
@Produces({ "application/json" })
30+
@Path("/v3/inesdatatransferprocesses")
31+
public class InesdataTransferProcessApiController implements InesdataTransferProcessApi {
32+
33+
protected final Monitor monitor;
34+
private final TransferProcessService service;
35+
private final TypeTransformerRegistry transformerRegistry;
36+
private final JsonObjectValidatorRegistry validatorRegistry;
37+
private final String bucketName;
38+
private final String region;
39+
private final String accessKey;
40+
private final String secretKey;
41+
private final String endpointOverride;
42+
43+
public InesdataTransferProcessApiController(Monitor monitor, TransferProcessService service,
44+
TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry, String bucketName,
45+
String region, String accessKey, String secretKey, String endpointOverride) {
46+
this.monitor = monitor;
47+
this.service = service;
48+
this.transformerRegistry = transformerRegistry;
49+
this.validatorRegistry = validatorRegistry;
50+
this.bucketName = bucketName;
51+
this.region = region;
52+
this.accessKey = accessKey;
53+
this.secretKey = secretKey;
54+
this.endpointOverride = endpointOverride;
55+
}
56+
57+
@POST
58+
public JsonObject initiateTransferProcess(JsonObject request) {
59+
validatorRegistry.validate(TRANSFER_REQUEST_TYPE, request).orElseThrow(ValidationFailureException::new);
60+
61+
var transferRequest = transformerRegistry.transform(request, TransferRequest.class)
62+
.orElseThrow(InvalidRequestException::new);
63+
64+
DataAddress dataDestination = getDataDestinationProperties();
65+
66+
var tRequest = TransferRequest.Builder.newInstance().id(transferRequest.getId())
67+
.transferType(transferRequest.getTransferType()).callbackAddresses(transferRequest.getCallbackAddresses())
68+
.contractId(transferRequest.getContractId()).counterPartyAddress(transferRequest.getCounterPartyAddress())
69+
.protocol(transferRequest.getProtocol()).privateProperties(transferRequest.getPrivateProperties())
70+
.dataDestination(dataDestination).build();
71+
72+
var createdTransfer = service.initiateTransfer(tRequest)
73+
.onSuccess(d -> monitor.debug(format("Transfer Process created %s", d.getId())))
74+
.orElseThrow(it -> mapToException(it, TransferProcess.class));
75+
76+
var responseDto = IdResponse.Builder.newInstance().id(createdTransfer.getId())
77+
.createdAt(createdTransfer.getCreatedAt()).build();
78+
79+
return transformerRegistry.transform(responseDto, JsonObject.class)
80+
.orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail()));
81+
}
82+
83+
private DataAddress getDataDestinationProperties() {
84+
Map<String, Object> properties = new HashMap<>();
85+
properties.put(CoreConstants.EDC_NAMESPACE + "bucketName", bucketName);
86+
properties.put(CoreConstants.EDC_NAMESPACE + "region", region);
87+
properties.put(CoreConstants.EDC_NAMESPACE + "type", "AmazonS3");
88+
properties.put(CoreConstants.EDC_NAMESPACE + "endpointOverride", endpointOverride);
89+
properties.put(CoreConstants.EDC_NAMESPACE + "accessKeyId", accessKey);
90+
properties.put(CoreConstants.EDC_NAMESPACE + "secretAccessKey", secretKey);
91+
return DataAddress.Builder.newInstance().properties(properties).build();
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.upm.inesdata.inesdatatransfer.validations;
2+
3+
import jakarta.json.JsonObject;
4+
import org.eclipse.edc.spi.monitor.Monitor;
5+
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
6+
import org.eclipse.edc.validator.jsonobject.validators.LogDeprecatedValue;
7+
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
8+
import org.eclipse.edc.validator.jsonobject.validators.OptionalIdNotBlank;
9+
import org.eclipse.edc.validator.spi.Validator;
10+
11+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_ASSET_ID;
12+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_CONTRACT_ID;
13+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_COUNTER_PARTY_ADDRESS;
14+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_PROTOCOL;
15+
import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_TRANSFER_TYPE;
16+
17+
public class InesdataTransferRequestValidator {
18+
19+
public static Validator<JsonObject> instance(Monitor monitor) {
20+
return JsonObjectValidator.newValidator()
21+
.verifyId(OptionalIdNotBlank::new)
22+
.verify(TRANSFER_REQUEST_ASSET_ID, path -> new LogDeprecatedValue(path, TRANSFER_REQUEST_ASSET_ID, "no attribute, as %s already provide such information".formatted(TRANSFER_REQUEST_CONTRACT_ID), monitor))
23+
.verify(TRANSFER_REQUEST_COUNTER_PARTY_ADDRESS, MandatoryValue::new)
24+
.verify(TRANSFER_REQUEST_CONTRACT_ID, MandatoryValue::new)
25+
.verify(TRANSFER_REQUEST_PROTOCOL, MandatoryValue::new)
26+
.verify(TRANSFER_REQUEST_TRANSFER_TYPE, MandatoryValue::new)
27+
.build();
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.upm.inesdata.inesdatatransfer.InesdataTransferProcessApiExtension

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ lombok = "1.18.30"
2222
edc-api-asset = { module = "org.eclipse.edc:asset-api", version.ref = "edc" }
2323
edc-api-core = { module = "org.eclipse.edc:api-core", version.ref = "edc" }
2424
edc-api-management-config = { module = "org.eclipse.edc:management-api-configuration", version.ref = "edc" }
25+
edc-api-management-lib = { module = "org.eclipse.edc:management-api-lib", version.ref = "edc" }
2526
edc-api-control-config = { module = "org.eclipse.edc:control-api-configuration", version.ref = "edc" }
2627
edc-auth-spi = { module = "org.eclipse.edc:auth-spi", version.ref = "edc" }
2728
edc-build-plugin = { module = "org.eclipse.edc.edc-build:org.eclipse.edc.edc-build.gradle.plugin", version.ref = "edc" }
@@ -84,6 +85,7 @@ edc-sql-contract-definition-store = { module = "org.eclipse.edc:contract-definit
8485
edc-sql-contract-negotiation-store = { module = "org.eclipse.edc:contract-negotiation-store-sql", version.ref = "edc" }
8586
edc-sql-policy-definition-store = { module = "org.eclipse.edc:policy-definition-store-sql", version.ref = "edc" }
8687
edc-sql-transfer-process-store = { module = "org.eclipse.edc:transfer-process-store-sql", version.ref = "edc" }
88+
edc-transfer-process-api = { module = "org.eclipse.edc:transfer-process-api", version.ref = "edc" }
8789
edc-sql-data-plane-store = { module = "org.eclipse.edc:data-plane-store-sql", version.ref = "edc" }
8890

8991
# EDC aws s3 stuff

launchers/connector/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dependencies {
5252
implementation(project(":extensions:vocabulary-api"))
5353
implementation(project(":extensions:vocabulary-shared-api"))
5454
implementation(project(":extensions:vocabulary-shared-retrieval"))
55+
implementation(project(":extensions:inesdata-transfer-process-api"))
5556

5657
// Policies
5758
implementation(project(":extensions:policy-always-true"))

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ include(":extensions:dsp-vocabulary-http")
3737
include(":extensions:shared-api-configuration")
3838
include(":extensions:vocabulary-shared-api")
3939
include(":extensions:vocabulary-shared-retrieval")
40+
include(":extensions:inesdata-transfer-process-api")
4041

4142
// Connector
4243
include(":launchers:connector")

0 commit comments

Comments
 (0)