From 9b840c26f1538b1a0ea27c9f9b8f502f9d6eecd8 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 24 May 2024 14:58:30 +0200 Subject: [PATCH] refactor: bump all management API endpoints to `v3` (#4211) * chore: introduce consistent `/v3` Management API * rebase aftermath * change mgmt api timestamp * checkstyle * fix service provider file * fixed imports, paths --- .../java/org/eclipse/edc/api/ApiWarnings.java | 22 ++ .../src/main/resources/version.json | 2 +- .../api/management/asset/v3/AssetApi.java | 10 +- ...ler.java => BaseCatalogApiController.java} | 13 +- .../catalog/CatalogApiExtension.java | 5 +- .../management/catalog/v2/CatalogApiV2.java | 253 ++++++++++++++ .../catalog/v2/CatalogApiV2Controller.java | 47 +++ .../{CatalogApi.java => v3/CatalogApiV3.java} | 15 +- .../catalog/v3/CatalogApiV3Controller.java | 28 ++ ...java => BaseCatalogApiControllerTest.java} | 34 +- .../catalog/CatalogApiExtensionTest.java | 14 + .../management/catalog/CatalogApiTest.java | 8 +- .../v2/CatalogApiV2ControllerTest.java | 32 ++ .../v3/CatalogApiV3ControllerTest.java | 30 ++ ...> BaseContractAgreementApiController.java} | 16 +- .../ContractAgreementApiExtension.java | 6 +- .../v2/ContractAgreementApiV2.java | 80 +++++ .../v2/ContractAgreementApiV2Controller.java | 49 +++ .../ContractAgreementApiV3.java} | 16 +- .../v3/ContractAgreementApiV3Controller.java | 29 ++ ...seContractAgreementApiControllerTest.java} | 20 +- .../ContractAgreementApiExtensionTest.java | 47 +++ .../ContractAgreementApiV2ControllerTest.java | 34 ++ .../ContractAgreementApiV3ControllerTest.java | 33 ++ ... BaseContractDefinitionApiController.java} | 26 +- .../ContractDefinitionApiExtension.java | 7 +- .../v2/ContractDefinitionApiV2.java | 166 ++++++++++ .../v2/ContractDefinitionApiV2Controller.java | 37 +++ .../ContractDefinitionApiV3.java} | 22 +- .../v3/ContractDefinitionApiV3Controller.java | 35 ++ ...eContractDefinitionApiControllerTest.java} | 20 +- .../ContractDefinitionApiExtensionTest.java | 14 + .../ContractDefinitionApiTest.java | 4 +- ...ContractDefinitionApiV2ControllerTest.java | 36 ++ ...ContractDefinitionApiV3ControllerTest.java | 35 ++ ...BaseContractNegotiationApiController.java} | 33 +- .../ContractNegotiationApiExtension.java | 9 +- .../model/NegotiationState.java | 4 +- ...ectFromContractNegotiationTransformer.java | 2 +- ...ObjectFromNegotiationStateTransformer.java | 8 +- .../JsonObjectToContractOfferTransformer.java | 4 +- ...sonObjectToContractRequestTransformer.java | 2 +- ...erminateNegotiationCommandTransformer.java | 2 +- .../v2/ContractNegotiationApiV2.java | 228 +++++++++++++ .../ContractNegotiationApiV2Controller.java | 69 ++++ .../ContractNegotiationApiV3.java} | 40 +-- .../ContractNegotiationApiV3Controller.java | 29 ++ .../validation/ContractRequestValidator.java | 2 +- .../TerminateNegotiationValidator.java | 2 +- ...ContractNegotiationApiControllerTest.java} | 29 +- ...va => BaseContractNegotiationApiTest.java} | 49 +-- .../ContractNegotiationApiExtensionTest.java | 34 +- ...romContractNegotiationTransformerTest.java | 2 +- ...ctFromNegotiationStateTransformerTest.java | 2 +- ...nObjectToContractOfferTransformerTest.java | 2 +- ...nateNegotiationCommandTransformerTest.java | 2 +- ...ontractNegotiationApiV2ControllerTest.java | 37 +++ .../v2/ContractNegotiationApiV2Test.java | 43 +++ ...ontractNegotiationApiV3ControllerTest.java | 36 ++ .../v3/ContractNegotiationApiV3Test.java | 44 +++ .../TerminateNegotiationValidatorTest.java | 2 +- ...er.java => BaseEdrCacheApiController.java} | 24 +- .../management/edr/EdrCacheApiExtension.java | 7 +- .../api/management/edr/v1/EdrCacheApiV1.java | 102 ++++++ .../edr/v1/EdrCacheApiV1Controller.java | 52 +++ .../EdrCacheApiV3.java} | 15 +- .../edr/v3/EdrCacheApiV3Controller.java | 30 ++ .../edr/BaseEdrCacheApiControllerTest.java | 202 ++++++++++++ .../{v1 => }/EdrCacheApiExtensionTest.java | 8 +- .../edr/{v1 => }/EdrCacheApiTest.java | 4 +- .../edr/v1/EdrCacheApiControllerTest.java | 187 +---------- .../edr/v3/EdrCacheApiControllerTest.java | 39 +++ ...=> BasePolicyDefinitionApiController.java} | 15 +- .../policy/PolicyDefinitionApiExtension.java | 5 +- .../policy/v2/PolicyDefinitionApiV2.java | 185 +++++++++++ .../v2/PolicyDefinitionApiV2Controller.java | 62 ++++ .../PolicyDefinitionApiV3.java} | 20 +- .../v3/PolicyDefinitionApiV3Controller.java | 29 ++ ...asePolicyDefinitionApiControllerTest.java} | 110 +++--- .../PolicyDefinitionApiExtensionTest.java | 14 + .../policy/PolicyDefinitionApiTest.java | 4 +- .../PolicyDefinitionApiV2ControllerTest.java | 36 ++ .../PolicyDefinitionApiV3ControllerTest.java | 36 ++ ...ler.java => BaseSecretsApiController.java} | 9 +- .../secret/SecretsApiExtension.java | 5 +- .../management/secret/v1/SecretsApiV1.java | 132 ++++++++ .../secret/v1/SecretsApiV1Controller.java | 58 ++++ .../{SecretsApi.java => v3/SecretsApiV3.java} | 22 +- .../secret/v3/SecretsApiV3Controller.java | 29 ++ ...java => BaseSecretsApiControllerTest.java} | 19 +- .../secret/SecretsApiExtensionTest.java | 5 +- .../api/management/secret/SecretsApiTest.java | 4 +- .../secret/v1/SecretsApiV1ControllerTest.java | 35 ++ .../secret/v3/SecretsApiV3ControllerTest.java | 34 ++ ... => BaseTransferProcessApiController.java} | 15 +- .../TransferProcessApiExtension.java | 6 +- .../v2/TransferProcessApiV2.java | 312 ++++++++++++++++++ .../v2/TransferProcessApiV2Controller.java | 29 ++ .../TransferProcessApiV3.java} | 22 +- .../v3/TransferProcessApiV3Controller.java | 29 ++ ...BaseTransferProcessApiControllerTest.java} | 173 ++++------ .../TransferProcessApiExtensionTest.java | 14 + .../TransferProcessApiTest.java | 10 +- .../TransferProcessApiV2ControllerTest.java | 34 ++ .../TransferProcessApiV3ControllerTest.java | 34 ++ .../managementapi/CatalogApiEndToEndTest.java | 61 ++-- .../ContractAgreementApiEndToEndTest.java | 61 ++-- .../ContractDefinitionApiEndToEndTest.java | 71 ++-- .../ContractNegotiationApiEndToEndTest.java | 67 ++-- .../PolicyDefinitionApiEndToEndTest.java | 87 +++-- .../managementapi/SecretsApiEndToEndTest.java | 34 +- .../TransferProcessApiEndToEndTest.java | 16 +- 112 files changed, 3662 insertions(+), 912 deletions(-) create mode 100644 extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiWarnings.java rename extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/{CatalogApiController.java => BaseCatalogApiController.java} (91%) create mode 100644 extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2.java create mode 100644 extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2Controller.java rename extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/{CatalogApi.java => v3/CatalogApiV3.java} (96%) create mode 100644 extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3Controller.java rename extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/{CatalogApiControllerTest.java => BaseCatalogApiControllerTest.java} (91%) create mode 100644 extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3ControllerTest.java rename extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/{ContractAgreementApiController.java => BaseContractAgreementApiController.java} (89%) create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2.java create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2Controller.java rename extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/{ContractAgreementApi.java => v3/ContractAgreementApiV3.java} (89%) create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3Controller.java rename extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/{ContractAgreementApiControllerTest.java => BaseContractAgreementApiControllerTest.java} (94%) create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtensionTest.java create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3ControllerTest.java rename extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/{ContractDefinitionApiController.java => BaseContractDefinitionApiController.java} (87%) create mode 100644 extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2.java create mode 100644 extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2Controller.java rename extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/{ContractDefinitionApi.java => v3/ContractDefinitionApiV3.java} (91%) create mode 100644 extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3Controller.java rename extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/{ContractDefinitionApiControllerTest.java => BaseContractDefinitionApiControllerTest.java} (95%) create mode 100644 extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3ControllerTest.java rename extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/{ContractNegotiationApiController.java => BaseContractNegotiationApiController.java} (87%) create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2.java create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Controller.java rename extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/{ContractNegotiationApi.java => v3/ContractNegotiationApiV3.java} (91%) create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Controller.java rename extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/{ContractNegotiationApiControllerTest.java => BaseContractNegotiationApiControllerTest.java} (95%) rename extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/{ContractNegotiationApiTest.java => BaseContractNegotiationApiTest.java} (70%) create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Test.java create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Test.java rename extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/{v1/EdrCacheApiController.java => BaseEdrCacheApiController.java} (87%) create mode 100644 extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1.java create mode 100644 extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1Controller.java rename extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/{v1/EdrCacheApi.java => v3/EdrCacheApiV3.java} (93%) create mode 100644 extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3Controller.java create mode 100644 extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiControllerTest.java rename extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/{v1 => }/EdrCacheApiExtensionTest.java (91%) rename extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/{v1 => }/EdrCacheApiTest.java (96%) create mode 100644 extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiControllerTest.java rename extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/{PolicyDefinitionApiController.java => BasePolicyDefinitionApiController.java} (92%) create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2.java create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2Controller.java rename extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/{PolicyDefinitionApi.java => v3/PolicyDefinitionApiV3.java} (94%) create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3Controller.java rename extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/{PolicyDefinitionApiControllerTest.java => BasePolicyDefinitionApiControllerTest.java} (87%) create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3ControllerTest.java rename extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/{SecretsApiController.java => BaseSecretsApiController.java} (92%) create mode 100644 extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1.java create mode 100644 extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1Controller.java rename extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/{SecretsApi.java => v3/SecretsApiV3.java} (92%) create mode 100644 extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3Controller.java rename extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/{SecretsApiControllerTest.java => BaseSecretsApiControllerTest.java} (94%) create mode 100644 extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3ControllerTest.java rename extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/{TransferProcessApiController.java => BaseTransferProcessApiController.java} (95%) create mode 100644 extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2.java create mode 100644 extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2Controller.java rename extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/{TransferProcessApi.java => v3/TransferProcessApiV3.java} (95%) create mode 100644 extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3Controller.java rename extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/{TransferProcessApiControllerTest.java => BaseTransferProcessApiControllerTest.java} (84%) create mode 100644 extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2ControllerTest.java create mode 100644 extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3ControllerTest.java diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiWarnings.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiWarnings.java new file mode 100644 index 00000000000..7bda645970e --- /dev/null +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiWarnings.java @@ -0,0 +1,22 @@ +/* + * 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.api; + +public interface ApiWarnings { + static String deprecationWarning(String deprecatedVersion, String newVersion) { + return "This version ('%s') of this API is deprecated. Please use version '%s' instead.".formatted(deprecatedVersion, newVersion); + } + +} diff --git a/extensions/common/api/management-api-configuration/src/main/resources/version.json b/extensions/common/api/management-api-configuration/src/main/resources/version.json index 439812969b6..e63a3148f77 100644 --- a/extensions/common/api/management-api-configuration/src/main/resources/version.json +++ b/extensions/common/api/management-api-configuration/src/main/resources/version.json @@ -1,5 +1,5 @@ { "version": "3.0.0", "urlPath": "/v3", - "lastUpdated": "2024-05-17T07:12:44Z" + "lastUpdated": "2024-05-23T15:52:00Z" } \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/asset/v3/AssetApi.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/asset/v3/AssetApi.java index 48ab4875e07..0c2679b8626 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/asset/v3/AssetApi.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/asset/v3/AssetApi.java @@ -36,11 +36,11 @@ @OpenAPIDefinition( info = @Info(description = "This contains both the current and the new Asset API, which accepts JSON-LD and will " + - "become the standard API once the Dataspace Protocol is stable.", title = "Asset API")) -@Tag(name = "Asset") + "become the standard API once the Dataspace Protocol is stable.", title = "Asset API", version = "v3")) +@Tag(name = "Asset V3") public interface AssetApi { - @Operation(description = "Creates a new asset together with a data address", + operationId = "createAssetV3", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetInputSchema.class))), responses = { @ApiResponse(responseCode = "200", description = "Asset was created successfully. Returns the asset Id and created timestamp", @@ -53,6 +53,7 @@ public interface AssetApi { JsonObject createAsset(JsonObject asset); @Operation(description = "Request all assets according to a particular query", + operationId = "requestAssetV3", requestBody = @RequestBody( content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class)) ), @@ -65,6 +66,7 @@ public interface AssetApi { JsonArray requestAssets(JsonObject querySpecJson); @Operation(description = "Gets an asset with the given ID", + operationId = "getAssetV3", responses = { @ApiResponse(responseCode = "200", description = "The asset", content = @Content(schema = @Schema(implementation = AssetOutputSchema.class))), @@ -79,6 +81,7 @@ public interface AssetApi { @Operation(description = "Removes an asset with the given ID if possible. Deleting an asset is only possible if that asset is not yet referenced " + "by a contract agreement, in which case an error is returned. " + "DANGER ZONE: Note that deleting assets can have unexpected results, especially for contract offers that have been sent out or ongoing or contract negotiations.", + operationId = "removeAssetV3", responses = { @ApiResponse(responseCode = "204", description = "Asset was deleted successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", @@ -92,6 +95,7 @@ public interface AssetApi { @Operation(description = "Updates an asset with the given ID if it exists. If the asset is not found, no further action is taken. " + "DANGER ZONE: Note that updating assets can have unexpected results, especially for contract offers that have been sent out or are ongoing in contract negotiations.", + operationId = "updateAssetV3", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetInputSchema.class))), responses = { @ApiResponse(responseCode = "204", description = "Asset was updated successfully"), diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiController.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiController.java similarity index 91% rename from extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiController.java rename to extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiController.java index 242849ef701..23ce284a7bd 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiController.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -8,7 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -38,21 +38,19 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) -@Path("/v2/catalog") -public class CatalogApiController implements CatalogApi { +public abstract class BaseCatalogApiController { private final CatalogService service; private final TypeTransformerRegistry transformerRegistry; private final JsonObjectValidatorRegistry validatorRegistry; - public CatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry, - JsonObjectValidatorRegistry validatorRegistry) { + public BaseCatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry, + JsonObjectValidatorRegistry validatorRegistry) { this.service = service; this.transformerRegistry = transformerRegistry; this.validatorRegistry = validatorRegistry; } - @Override @POST @Path("/request") public void requestCatalog(JsonObject requestBody, @Suspended AsyncResponse response) { @@ -71,7 +69,6 @@ public void requestCatalog(JsonObject requestBody, @Suspended AsyncResponse resp }); } - @Override @POST @Path("dataset/request") public void getDataset(JsonObject requestBody, @Suspended AsyncResponse response) { diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtension.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtension.java index 9d3a28979b9..403fa18504d 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtension.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtension.java @@ -17,6 +17,8 @@ import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.controlplane.api.management.catalog.transform.JsonObjectToCatalogRequestTransformer; import org.eclipse.edc.connector.controlplane.api.management.catalog.transform.JsonObjectToDatasetRequestTransformer; +import org.eclipse.edc.connector.controlplane.api.management.catalog.v2.CatalogApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3Controller; import org.eclipse.edc.connector.controlplane.api.management.catalog.validation.CatalogRequestValidator; import org.eclipse.edc.connector.controlplane.api.management.catalog.validation.DatasetRequestValidator; import org.eclipse.edc.connector.controlplane.services.spi.catalog.CatalogService; @@ -66,7 +68,8 @@ public void initialize(ServiceExtensionContext context) { transformerRegistry.register(new JsonObjectToDatasetRequestTransformer()); var managementApiTransformerRegistry = transformerRegistry.forContext("management-api"); - webService.registerResource(config.getContextAlias(), new CatalogApiController(service, managementApiTransformerRegistry, validatorRegistry)); + webService.registerResource(config.getContextAlias(), new CatalogApiV2Controller(service, managementApiTransformerRegistry, validatorRegistry, context.getMonitor())); + webService.registerResource(config.getContextAlias(), new CatalogApiV3Controller(service, managementApiTransformerRegistry, validatorRegistry)); validatorRegistry.register(CATALOG_REQUEST_TYPE, CatalogRequestValidator.instance(criterionOperatorRegistry)); validatorRegistry.register(DATASET_REQUEST_TYPE, DatasetRequestValidator.instance()); diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2.java new file mode 100644 index 00000000000..a3373e4665e --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2.java @@ -0,0 +1,253 @@ +/* + * 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.controlplane.api.management.catalog.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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 jakarta.ws.rs.container.Suspended; +import org.eclipse.edc.api.model.ApiCoreSchema; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.connector.controlplane.catalog.spi.CatalogRequest.CATALOG_REQUEST_TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Catalog V2") +public interface CatalogApiV2 { + + @Operation( + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = CatalogRequestSchema.class))), + operationId = "requestCatalogV2", + responses = { @ApiResponse( + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = CatalogSchema.class) + ), + description = "Gets contract offers (=catalog) of a single connector") }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void requestCatalog(JsonObject request, @Suspended AsyncResponse response); + + @Operation( + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DatasetRequestSchema.class))), + operationId = "getDatasetV2", + responses = { @ApiResponse( + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = DatasetSchema.class) + ), + description = "Gets single dataset from a connector") }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void getDataset(JsonObject request, @Suspended AsyncResponse response); + + @Schema(name = "CatalogRequest", example = CatalogRequestSchema.CATALOG_REQUEST_EXAMPLE) + record CatalogRequestSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = TYPE, example = CATALOG_REQUEST_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String counterPartyAddress, + // Switch to required in the next API iteration + @Schema(requiredMode = NOT_REQUIRED) + String counterPartyId, + @Schema(requiredMode = REQUIRED) + String protocol, + ApiCoreSchema.QuerySpecSchema querySpec) { + + public static final String CATALOG_REQUEST_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "CatalogRequest", + "counterPartyAddress": "http://provider-address", + "counterPartyId": "providerId", + "protocol": "dataspace-protocol-http", + "querySpec": { + "offset": 0, + "limit": 50, + "sortOrder": "DESC", + "sortField": "fieldName", + "filterExpression": [] + } + } + """; + } + + @Schema(name = "DatasetRequest", example = DatasetRequestSchema.DATASET_REQUEST_EXAMPLE) + record DatasetRequestSchema( + @Schema(name = TYPE, example = CATALOG_REQUEST_TYPE) + String type, + String counterPartyAddress, + String counterPartyId, + String protocol, + ApiCoreSchema.QuerySpecSchema querySpec) { + + public static final String DATASET_REQUEST_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "DatasetRequest", + "@id": "dataset-id", + "counterPartyAddress": "http://counter-party-address", + "counterPartyId": "counter-party-id", + "protocol": "dataspace-protocol-http" + } + """; + } + + @Schema(name = "Catalog", description = "DCAT catalog", example = CatalogSchema.CATALOG_EXAMPLE) + record CatalogSchema( + ) { + public static final String CATALOG_EXAMPLE = """ + { + "@id": "7df65569-8c59-4013-b1c0-fa14f6641bf2", + "@type": "dcat:Catalog", + "dcat:dataset": { + "@id": "bcca61be-e82e-4da6-bfec-9716a56cef35", + "@type": "dcat:Dataset", + "odrl:hasPolicy": { + "@id": "OGU0ZTMzMGMtODQ2ZS00ZWMxLThmOGQtNWQxNWM0NmI2NmY4:YmNjYTYxYmUtZTgyZS00ZGE2LWJmZWMtOTcxNmE1NmNlZjM1:NDY2ZTZhMmEtNjQ1Yy00ZGQ0LWFlZDktMjdjNGJkZTU4MDNj", + "@type": "odrl:Set", + "odrl:permission": { + "odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35", + "odrl:action": { + "odrl:type": "http://www.w3.org/ns/odrl/2/use" + }, + "odrl:constraint": { + "odrl:and": [ + { + "odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate", + "odrl:operator": { + "@id": "odrl:gteq" + }, + "odrl:rightOperand": "2023-07-07T07:19:58.585601395Z" + }, + { + "odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate", + "odrl:operator": { + "@id": "odrl:lteq" + }, + "odrl:rightOperand": "2023-07-12T07:19:58.585601395Z" + } + ] + } + }, + "odrl:prohibition": [], + "odrl:obligation": [] + }, + "dcat:distribution": [ + { + "@type": "dcat:Distribution", + "dct:format": { + "@id": "HttpData" + }, + "dcat:accessService": "5e839777-d93e-4785-8972-1005f51cf367" + } + ], + "description": "description", + "id": "bcca61be-e82e-4da6-bfec-9716a56cef35" + }, + "dcat:service": { + "@id": "5e839777-d93e-4785-8972-1005f51cf367", + "@type": "dcat:DataService", + "dct:terms": "connector", + "dct:endpointUrl": "http://localhost:16806/protocol" + }, + "dspace:participantId": "urn:connector:provider", + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/", + "dct": "http://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "http://www.w3.org/ns/dcat#", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } + } + """; + } + + @Schema(name = "Dataset", description = "DCAT dataset", example = DatasetSchema.DATASET_EXAMPLE) + record DatasetSchema( + ) { + public static final String DATASET_EXAMPLE = """ + { + "@id": "bcca61be-e82e-4da6-bfec-9716a56cef35", + "@type": "dcat:Dataset", + "odrl:hasPolicy": { + "@id": "OGU0ZTMzMGMtODQ2ZS00ZWMxLThmOGQtNWQxNWM0NmI2NmY4:YmNjYTYxYmUtZTgyZS00ZGE2LWJmZWMtOTcxNmE1NmNlZjM1:NDY2ZTZhMmEtNjQ1Yy00ZGQ0LWFlZDktMjdjNGJkZTU4MDNj", + "@type": "odrl:Set", + "odrl:permission": { + "odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35", + "odrl:action": { + "odrl:type": "http://www.w3.org/ns/odrl/2/use" + }, + "odrl:constraint": { + "odrl:and": [ + { + "odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate", + "odrl:operator": { + "@id": "odrl:gteq" + }, + "odrl:rightOperand": "2023-07-07T07:19:58.585601395Z" + }, + { + "odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate", + "odrl:operator": { + "@id": "odrl:lteq" + }, + "odrl:rightOperand": "2023-07-12T07:19:58.585601395Z" + } + ] + } + }, + "odrl:prohibition": [], + "odrl:obligation": [], + "odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35" + }, + "dcat:distribution": [ + { + "@type": "dcat:Distribution", + "dct:format": { + "@id": "HttpData" + }, + "dcat:accessService": "5e839777-d93e-4785-8972-1005f51cf367" + } + ], + "description": "description", + "id": "bcca61be-e82e-4da6-bfec-9716a56cef35", + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/", + "dct": "http://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "http://www.w3.org/ns/dcat#", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } + } + """; + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2Controller.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2Controller.java new file mode 100644 index 00000000000..9b7662193e5 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2Controller.java @@ -0,0 +1,47 @@ +/* + * 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.controlplane.api.management.catalog.v2; + +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.container.AsyncResponse; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.catalog.BaseCatalogApiController; +import org.eclipse.edc.connector.controlplane.services.spi.catalog.CatalogService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v2/catalog") +public class CatalogApiV2Controller extends BaseCatalogApiController implements CatalogApiV2 { + private final Monitor monitor; + + public CatalogApiV2Controller(CatalogService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry, Monitor monitor) { + super(service, transformerRegistry, validatorRegistry); + this.monitor = monitor; + } + + @Override + public void requestCatalog(JsonObject requestBody, AsyncResponse response) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + super.requestCatalog(requestBody, response); + } + + @Override + public void getDataset(JsonObject requestBody, AsyncResponse response) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + super.getDataset(requestBody, response); + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApi.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3.java similarity index 96% rename from extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApi.java rename to extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3.java index f42ef0760f4..9684cc22316 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApi.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Bayerische Motoren Werke Aktiengesellschaft + * 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 @@ -8,14 +8,15 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft - initial API and implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.edc.connector.controlplane.api.management.catalog; +package org.eclipse.edc.connector.controlplane.api.management.catalog.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -32,12 +33,13 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -@OpenAPIDefinition -@Tag(name = "Catalog") -public interface CatalogApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Catalog V3") +public interface CatalogApiV3 { @Operation( requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = CatalogRequestSchema.class))), + operationId = "requestCatalogV3", responses = { @ApiResponse( content = @Content( mediaType = "application/json", @@ -49,6 +51,7 @@ public interface CatalogApi { @Operation( requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DatasetRequestSchema.class))), + operationId = "getDatasetV3", responses = { @ApiResponse( content = @Content( mediaType = "application/json", diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3Controller.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3Controller.java new file mode 100644 index 00000000000..489e2ff087d --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3Controller.java @@ -0,0 +1,28 @@ +/* + * 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.controlplane.api.management.catalog.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.catalog.BaseCatalogApiController; +import org.eclipse.edc.connector.controlplane.services.spi.catalog.CatalogService; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/catalog") +public class CatalogApiV3Controller extends BaseCatalogApiController implements CatalogApiV3 { + public CatalogApiV3Controller(CatalogService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry) { + super(service, transformerRegistry, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiControllerTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiControllerTest.java similarity index 91% rename from extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiControllerTest.java rename to extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiControllerTest.java index ca338b6990e..037a1d55611 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiControllerTest.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/BaseCatalogApiControllerTest.java @@ -46,16 +46,11 @@ import static org.mockito.Mockito.when; @ApiTest -class CatalogApiControllerTest extends RestControllerTestBase { +public abstract class BaseCatalogApiControllerTest extends RestControllerTestBase { - private final CatalogService service = mock(); - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); - - @Override - protected Object controller() { - return new CatalogApiController(service, transformerRegistry, validatorRegistry); - } + protected final CatalogService service = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); @Test void requestCatalog() { @@ -69,7 +64,7 @@ void requestCatalog() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post(baseUrl() + "/request") .then() .statusCode(200) .contentType(JSON); @@ -85,7 +80,7 @@ void requestCatalog_shouldReturnBadRequest_whenValidationFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post(baseUrl() + "/request") .then() .statusCode(400); verify(validatorRegistry).validate(eq(CatalogRequest.CATALOG_REQUEST_TYPE), any()); @@ -102,7 +97,7 @@ void requestCatalog_shouldReturnBadRequest_whenTransformFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post(baseUrl() + "/request") .then() .statusCode(400); verifyNoInteractions(service); @@ -121,7 +116,7 @@ void requestCatalog_shouldReturnBadGateway_whenServiceFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post(baseUrl() + "/request") .then() .statusCode(502); } @@ -138,7 +133,7 @@ void requestCatalog_shouldReturnBadGateway_whenServiceThrowsException() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post(baseUrl() + "/request") .then() .statusCode(502); } @@ -160,7 +155,7 @@ void requestDataset_shouldCallService() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post(baseUrl() + "/dataset/request") .then() .statusCode(200); @@ -178,7 +173,7 @@ void requestDataset_shouldReturnBadRequest_whenValidationFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post(baseUrl() + "/dataset/request") .then() .statusCode(400); verify(validatorRegistry).validate(eq(DATASET_REQUEST_TYPE), any()); @@ -195,7 +190,7 @@ void requestDataset_shouldReturnBadRequest_whenTransformFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post(baseUrl() + "/dataset/request") .then() .statusCode(400); verifyNoInteractions(service); @@ -214,7 +209,7 @@ void requestDataset_shouldReturnBadGateway_whenServiceFails() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post(baseUrl() + "/dataset/request") .then() .statusCode(502); } @@ -231,9 +226,10 @@ void requestDataset_shouldReturnBadGateway_whenServiceThrowsException() { .port(port) .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post(baseUrl() + "/dataset/request") .then() .statusCode(502); } + protected abstract String baseUrl(); } diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtensionTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtensionTest.java index 3d63cb47a32..92097292447 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiExtensionTest.java @@ -14,9 +14,12 @@ package org.eclipse.edc.connector.controlplane.api.management.catalog; +import org.eclipse.edc.connector.controlplane.api.management.catalog.v2.CatalogApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,6 +28,7 @@ import static org.eclipse.edc.connector.controlplane.catalog.spi.DatasetRequest.DATASET_REQUEST_TYPE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -32,10 +36,12 @@ class CatalogApiExtensionTest { private final JsonObjectValidatorRegistry validatorRegistry = mock(); + private final WebService webService = mock(WebService.class); @BeforeEach void setUp(ServiceExtensionContext context) { context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); } @Test @@ -45,4 +51,12 @@ void initiate_shouldRegisterValidators(CatalogApiExtension extension, ServiceExt verify(validatorRegistry).register(eq(CATALOG_REQUEST_TYPE), any()); verify(validatorRegistry).register(eq(DATASET_REQUEST_TYPE), any()); } + + @Test + void initiate_shouldRegisterControllers(CatalogApiExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(CatalogApiV2Controller.class)); + verify(webService).registerResource(any(), isA(CatalogApiV3Controller.class)); + } } diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiTest.java index e1c9a5b9884..09b9ebb10ea 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiTest.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/CatalogApiTest.java @@ -33,10 +33,10 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.connector.controlplane.api.management.catalog.CatalogApi.CatalogRequestSchema.CATALOG_REQUEST_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.catalog.CatalogApi.CatalogSchema.CATALOG_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.catalog.CatalogApi.DatasetRequestSchema.DATASET_REQUEST_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.catalog.CatalogApi.DatasetSchema.DATASET_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3.CatalogRequestSchema.CATALOG_REQUEST_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3.CatalogSchema.CATALOG_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3.DatasetRequestSchema.DATASET_REQUEST_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3.DatasetSchema.DATASET_EXAMPLE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.DCAT_CATALOG_TYPE; diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2ControllerTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2ControllerTest.java new file mode 100644 index 00000000000..07a242ab6f8 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v2/CatalogApiV2ControllerTest.java @@ -0,0 +1,32 @@ +/* + * 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.controlplane.api.management.catalog.v2; + +import org.eclipse.edc.connector.controlplane.api.management.catalog.BaseCatalogApiControllerTest; + +import static org.mockito.Mockito.mock; + +class CatalogApiV2ControllerTest extends BaseCatalogApiControllerTest { + + @Override + protected Object controller() { + return new CatalogApiV2Controller(service, transformerRegistry, validatorRegistry, mock()); + } + + @Override + protected String baseUrl() { + return "/v2/catalog"; + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3ControllerTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3ControllerTest.java new file mode 100644 index 00000000000..cb8a62f9326 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/catalog/v3/CatalogApiV3ControllerTest.java @@ -0,0 +1,30 @@ +/* + * 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.controlplane.api.management.catalog.v3; + +import org.eclipse.edc.connector.controlplane.api.management.catalog.BaseCatalogApiControllerTest; + +class CatalogApiV3ControllerTest extends BaseCatalogApiControllerTest { + + @Override + protected String baseUrl() { + return "/v3/catalog"; + } + + @Override + protected Object controller() { + return new CatalogApiV3Controller(service, transformerRegistry, validatorRegistry); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiController.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiController.java similarity index 89% rename from extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiController.java rename to extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiController.java index 5281856e605..78cd981131d 100644 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiController.java +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiController.java @@ -42,17 +42,16 @@ import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_TYPE; import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -@Path("/v2/contractagreements") -public class ContractAgreementApiController implements ContractAgreementApi { +@Consumes({ MediaType.APPLICATION_JSON }) +@Produces({ MediaType.APPLICATION_JSON }) +public abstract class BaseContractAgreementApiController { + protected final Monitor monitor; private final ContractAgreementService service; private final TypeTransformerRegistry transformerRegistry; - private final Monitor monitor; private final JsonObjectValidatorRegistry validatorRegistry; - public ContractAgreementApiController(ContractAgreementService service, TypeTransformerRegistry transformerRegistry, - Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + public BaseContractAgreementApiController(ContractAgreementService service, TypeTransformerRegistry transformerRegistry, + Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { this.service = service; this.transformerRegistry = transformerRegistry; this.monitor = monitor; @@ -61,7 +60,6 @@ public ContractAgreementApiController(ContractAgreementService service, TypeTran @POST @Path("/request") - @Override public JsonArray queryAgreements(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -83,7 +81,6 @@ public JsonArray queryAgreements(JsonObject querySpecJson) { @GET @Path("{id}") - @Override public JsonObject getAgreementById(@PathParam("id") String id) { return Optional.of(id) .map(service::findById) @@ -94,7 +91,6 @@ public JsonObject getAgreementById(@PathParam("id") String id) { @GET @Path("{id}/negotiation") - @Override public JsonObject getNegotiationByAgreementId(@PathParam("id") String id) { return Optional.of(id) .map(service::findNegotiation) diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtension.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtension.java index 91b4a0456db..c9bd2399257 100644 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtension.java +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtension.java @@ -16,6 +16,8 @@ package org.eclipse.edc.connector.controlplane.api.management.contractagreement; import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.v2.ContractAgreementApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.v3.ContractAgreementApiV3Controller; import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -55,7 +57,7 @@ public void initialize(ServiceExtensionContext context) { var managementApiTransformerRegistry = transformerRegistry.forContext("management-api"); - var controller = new ContractAgreementApiController(service, managementApiTransformerRegistry, monitor, validatorRegistry); - webService.registerResource(config.getContextAlias(), controller); + webService.registerResource(config.getContextAlias(), new ContractAgreementApiV2Controller(service, managementApiTransformerRegistry, monitor, validatorRegistry)); + webService.registerResource(config.getContextAlias(), new ContractAgreementApiV3Controller(service, managementApiTransformerRegistry, monitor, validatorRegistry)); } } diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2.java new file mode 100644 index 00000000000..935daa1d73b --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2.java @@ -0,0 +1,80 @@ +/* + * 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.controlplane.api.management.contractagreement.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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.parameters.RequestBody; +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; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Contract Agreement V2") +public interface ContractAgreementApiV2 { + + @Operation(description = "Gets all contract agreements according to a particular query", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryAgreementsV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract agreements matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class)))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonArray queryAgreements(JsonObject querySpecJson); + + @Operation(description = "Gets an contract agreement with the given ID", + operationId = "getAgreementByIdV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract agreement", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract agreement with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getAgreementById(String id); + + + @Operation(description = "Gets a contract negotiation with the given contract agreement ID", + operationId = "getNegotiationByAgreementIdV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract negotiation", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract agreement with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getNegotiationByAgreementId(String id); + +} diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2Controller.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2Controller.java new file mode 100644 index 00000000000..e4257b61bce --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2Controller.java @@ -0,0 +1,49 @@ +/* + * 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.controlplane.api.management.contractagreement.v2; + +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.BaseContractAgreementApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v2/contractagreements") +public class ContractAgreementApiV2Controller extends BaseContractAgreementApiController implements ContractAgreementApiV2 { + public ContractAgreementApiV2Controller(ContractAgreementService service, TypeTransformerRegistry transformerRegistry, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(service, transformerRegistry, monitor, validatorRegistry); + } + + @Override + public JsonArray queryAgreements(JsonObject querySpecJson) { + return super.queryAgreements(querySpecJson); + } + + @Override + public JsonObject getAgreementById(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getAgreementById(id); + } + + @Override + public JsonObject getNegotiationByAgreementId(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getNegotiationByAgreementId(id); + } +} diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApi.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3.java similarity index 89% rename from extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApi.java rename to extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3.java index 7dc70c001a7..c45480a6590 100644 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApi.java +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -8,14 +8,15 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.edc.connector.controlplane.api.management.contractagreement; +package org.eclipse.edc.connector.controlplane.api.management.contractagreement.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -27,12 +28,13 @@ import org.eclipse.edc.api.model.ApiCoreSchema; import org.eclipse.edc.connector.api.management.configuration.ManagementApiSchema; -@OpenAPIDefinition -@Tag(name = "Contract Agreement") -public interface ContractAgreementApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Contract Agreement V3") +public interface ContractAgreementApiV3 { @Operation(description = "Gets all contract agreements according to a particular query", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryAgreementsV3", responses = { @ApiResponse(responseCode = "200", description = "The contract agreements matching the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class)))), @@ -43,6 +45,7 @@ public interface ContractAgreementApi { JsonArray queryAgreements(JsonObject querySpecJson); @Operation(description = "Gets an contract agreement with the given ID", + operationId = "getAgreementByIdV3", responses = { @ApiResponse(responseCode = "200", description = "The contract agreement", content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class))), @@ -56,6 +59,7 @@ public interface ContractAgreementApi { @Operation(description = "Gets a contract negotiation with the given contract agreement ID", + operationId = "getNegotiationByAgreementIdV3", responses = { @ApiResponse(responseCode = "200", description = "The contract negotiation", content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class))), diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3Controller.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3Controller.java new file mode 100644 index 00000000000..616d223f12b --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3Controller.java @@ -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.controlplane.api.management.contractagreement.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.BaseContractAgreementApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractagreement.ContractAgreementService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/contractagreements") +public class ContractAgreementApiV3Controller extends BaseContractAgreementApiController implements ContractAgreementApiV3 { + public ContractAgreementApiV3Controller(ContractAgreementService service, TypeTransformerRegistry transformerRegistry, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(service, transformerRegistry, monitor, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiControllerTest.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiControllerTest.java similarity index 94% rename from extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiControllerTest.java rename to extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiControllerTest.java index 24857cf0d47..7a8f55d1c35 100644 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiControllerTest.java +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/BaseContractAgreementApiControllerTest.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.UUID; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static java.util.Collections.emptyList; import static java.util.UUID.randomUUID; @@ -55,11 +54,11 @@ import static org.mockito.Mockito.when; @ApiTest -class ContractAgreementApiControllerTest extends RestControllerTestBase { +public abstract class BaseContractAgreementApiControllerTest extends RestControllerTestBase { - private final ContractAgreementService service = mock(); - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); + protected final ContractAgreementService service = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); @Test void queryAllAgreements_whenExists() { @@ -237,16 +236,7 @@ void getContractNegotiation_transformationFails() { verifyNoMoreInteractions(service, transformerRegistry); } - @Override - protected Object controller() { - return new ContractAgreementApiController(service, transformerRegistry, monitor, validatorRegistry); - } - - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port + "/v2/contractagreements") - .when(); - } + protected abstract RequestSpecification baseRequest(); private ContractAgreement createContractAgreement(String negotiationId) { return ContractAgreement.Builder.newInstance() diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtensionTest.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtensionTest.java new file mode 100644 index 00000000000..b39fe69f4dc --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/ContractAgreementApiExtensionTest.java @@ -0,0 +1,47 @@ +/* + * 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.controlplane.api.management.contractagreement; + +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.v2.ContractAgreementApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.v3.ContractAgreementApiV3Controller; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.web.spi.WebService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +class ContractAgreementApiExtensionTest { + private final WebService webService = mock(WebService.class); + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(WebService.class, webService); + } + + @Test + void initiate_shouldRegisterControllers(ContractAgreementApiExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(ContractAgreementApiV2Controller.class)); + verify(webService).registerResource(any(), isA(ContractAgreementApiV3Controller.class)); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2ControllerTest.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2ControllerTest.java new file mode 100644 index 00000000000..5f0394afa9b --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v2/ContractAgreementApiV2ControllerTest.java @@ -0,0 +1,34 @@ +/* + * 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.controlplane.api.management.contractagreement.v2; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.BaseContractAgreementApiControllerTest; + +import static io.restassured.RestAssured.given; + +class ContractAgreementApiV2ControllerTest extends BaseContractAgreementApiControllerTest { + + @Override + protected Object controller() { + return new ContractAgreementApiV2Controller(service, transformerRegistry, monitor, validatorRegistry); + } + + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v2/contractagreements") + .when(); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3ControllerTest.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3ControllerTest.java new file mode 100644 index 00000000000..38dc5ec9293 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractagreement/v3/ContractAgreementApiV3ControllerTest.java @@ -0,0 +1,33 @@ +/* + * 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.controlplane.api.management.contractagreement.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractagreement.BaseContractAgreementApiControllerTest; + +import static io.restassured.RestAssured.given; + +class ContractAgreementApiV3ControllerTest extends BaseContractAgreementApiControllerTest { + @Override + protected Object controller() { + return new ContractAgreementApiV3Controller(service, transformerRegistry, monitor, validatorRegistry); + } + + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v3/contractagreements") + .when(); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiController.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiController.java similarity index 87% rename from extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiController.java rename to extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiController.java index 5f534a47e4b..21cd00be091 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiController.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiController.java @@ -45,17 +45,16 @@ import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -@Path("/v2/contractdefinitions") -public class ContractDefinitionApiController implements ContractDefinitionApi { - private final TypeTransformerRegistry transformerRegistry; - private final ContractDefinitionService service; - private final Monitor monitor; - private final JsonObjectValidatorRegistry validatorRegistry; - - public ContractDefinitionApiController(TypeTransformerRegistry transformerRegistry, ContractDefinitionService service, - Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { +@Consumes({ MediaType.APPLICATION_JSON }) +@Produces({ MediaType.APPLICATION_JSON }) +public abstract class BaseContractDefinitionApiController { + protected final TypeTransformerRegistry transformerRegistry; + protected final ContractDefinitionService service; + protected final Monitor monitor; + protected final JsonObjectValidatorRegistry validatorRegistry; + + public BaseContractDefinitionApiController(TypeTransformerRegistry transformerRegistry, ContractDefinitionService service, + Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { this.transformerRegistry = transformerRegistry; this.service = service; this.monitor = monitor; @@ -64,7 +63,6 @@ public ContractDefinitionApiController(TypeTransformerRegistry transformerRegist @POST @Path("/request") - @Override public JsonArray queryContractDefinitions(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -87,7 +85,6 @@ public JsonArray queryContractDefinitions(JsonObject querySpecJson) { @GET @Path("{id}") - @Override public JsonObject getContractDefinition(@PathParam("id") String id) { return Optional.ofNullable(id) .map(service::findById) @@ -97,7 +94,6 @@ public JsonObject getContractDefinition(@PathParam("id") String id) { } @POST - @Override public JsonObject createContractDefinition(JsonObject createObject) { validatorRegistry.validate(CONTRACT_DEFINITION_TYPE, createObject) .orElseThrow(ValidationFailureException::new); @@ -118,13 +114,11 @@ public JsonObject createContractDefinition(JsonObject createObject) { @DELETE @Path("{id}") - @Override public void deleteContractDefinition(@PathParam("id") String id) { service.delete(id).orElseThrow(exceptionMapper(ContractDefinition.class, id)); } @PUT - @Override public void updateContractDefinition(JsonObject updateObject) { validatorRegistry.validate(CONTRACT_DEFINITION_TYPE, updateObject) .orElseThrow(ValidationFailureException::new); diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtension.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtension.java index 2c6950c096a..11d7585ed20 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtension.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtension.java @@ -20,6 +20,8 @@ import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.transform.JsonObjectFromContractDefinitionTransformer; import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.transform.JsonObjectToContractDefinitionTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3.ContractDefinitionApiV3Controller; import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.validation.ContractDefinitionValidator; import org.eclipse.edc.connector.controlplane.services.spi.contractdefinition.ContractDefinitionService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -77,10 +79,9 @@ public void initialize(ServiceExtensionContext context) { validatorRegistry.register(CONTRACT_DEFINITION_TYPE, ContractDefinitionValidator.instance(criterionOperatorRegistry)); - var monitor = context.getMonitor(); var managementApiTransformerRegistry = transformerRegistry.forContext("management-api"); - webService.registerResource(config.getContextAlias(), new ContractDefinitionApiController( - managementApiTransformerRegistry, service, monitor, validatorRegistry)); + webService.registerResource(config.getContextAlias(), new ContractDefinitionApiV2Controller(managementApiTransformerRegistry, service, context.getMonitor(), validatorRegistry)); + webService.registerResource(config.getContextAlias(), new ContractDefinitionApiV3Controller(managementApiTransformerRegistry, service, context.getMonitor(), validatorRegistry)); } } diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2.java new file mode 100644 index 00000000000..d6b654ae431 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2.java @@ -0,0 +1,166 @@ +/* + * 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.controlplane.api.management.contractdefinition.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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.parameters.RequestBody; +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 java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_TYPE; +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; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Contract Definition V2") +public interface ContractDefinitionApiV2 { + + @Operation(description = "Returns all contract definitions according to a query", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryContractDefV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract definitions matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ContractDefinitionOutputSchema.class)))), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonArray queryContractDefinitions(JsonObject querySpecJson); + + @Operation(description = "Gets an contract definition with the given ID", + operationId = "getContractDefV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract definition", + content = @Content(schema = @Schema(implementation = ContractDefinitionOutputSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract agreement with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getContractDefinition(String id); + + @Operation(description = "Creates a new contract definition", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractDefinitionInputSchema.class))), + operationId = "createContractDefinitionV2", + responses = { + @ApiResponse(responseCode = "200", description = "contract definition was created successfully. Returns the Contract Definition Id and created timestamp", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "Could not create contract definition, because a contract definition with that ID already exists", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject createContractDefinition(JsonObject createObject); + + @Operation(description = "Removes a contract definition with the given ID if possible. " + + "DANGER ZONE: Note that deleting contract definitions can have unexpected results, especially for contract offers that have been sent out or ongoing or contract negotiations.", + operationId = "deleteContractDefV2", + responses = { + @ApiResponse(responseCode = "204", description = "Contract definition was deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A contract definition with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void deleteContractDefinition(String id); + + @Operation(description = "Updated a contract definition with the given ID. The supplied JSON structure must be a valid JSON-LD object", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractDefinitionInputSchema.class))), + operationId = "updateContractDefV2", + responses = { + @ApiResponse(responseCode = "204", description = "Contract definition was updated successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A contract definition with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void updateContractDefinition(JsonObject updateObject); + + @Schema(name = "ContractDefinitionInput", example = CONTRACT_DEFINITION_INPUT_EXAMPLE) + record ContractDefinitionInputSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = CONTRACT_DEFINITION_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String accessPolicyId, + @Schema(requiredMode = REQUIRED) + String contractPolicyId, + @Schema(requiredMode = REQUIRED) + List assetsSelector) { + + public static final String CONTRACT_DEFINITION_INPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "accessPolicyId": "asset-policy-id", + "contractPolicyId": "contract-policy-id", + "assetsSelector": [] + } + """; + } + + @Schema(name = "ContractDefinitionOutput", example = CONTRACT_DEFINITION_OUTPUT_EXAMPLE) + record ContractDefinitionOutputSchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = CONTRACT_DEFINITION_TYPE) + String type, + String accessPolicyId, + String contractPolicyId, + List assetsSelector, + long createdAt) { + + public static final String CONTRACT_DEFINITION_OUTPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "accessPolicyId": "asset-policy-id", + "contractPolicyId": "contract-policy-id", + "assetsSelector": [], + "createdAt": 1688465655 + } + """; + } +} diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2Controller.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2Controller.java new file mode 100644 index 00000000000..d9ce53bca69 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2Controller.java @@ -0,0 +1,37 @@ +/* + * 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.controlplane.api.management.contractdefinition.v2; + +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.BaseContractDefinitionApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractdefinition.ContractDefinitionService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v2/contractdefinitions") +public class ContractDefinitionApiV2Controller extends BaseContractDefinitionApiController { + public ContractDefinitionApiV2Controller(TypeTransformerRegistry transformerRegistry, ContractDefinitionService service, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(transformerRegistry, service, monitor, validatorRegistry); + } + + @Override + public JsonObject createContractDefinition(JsonObject createObject) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.createContractDefinition(createObject); + } +} diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApi.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3.java similarity index 91% rename from extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApi.java rename to extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3.java index 61f9be8ccf5..9db5ae0a527 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApi.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -12,10 +12,11 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.contractdefinition; +package org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -29,19 +30,20 @@ import java.util.List; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3.ContractDefinitionApiV3.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3.ContractDefinitionApiV3.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_TYPE; 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; -@OpenAPIDefinition -@Tag(name = "Contract Definition") -public interface ContractDefinitionApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Contract Definition V3") +public interface ContractDefinitionApiV3 { @Operation(description = "Returns all contract definitions according to a query", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryContractDefV3", responses = { @ApiResponse(responseCode = "200", description = "The contract definitions matching the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ContractDefinitionOutputSchema.class)))), @@ -52,6 +54,7 @@ public interface ContractDefinitionApi { JsonArray queryContractDefinitions(JsonObject querySpecJson); @Operation(description = "Gets an contract definition with the given ID", + operationId = "getContractDefV3", responses = { @ApiResponse(responseCode = "200", description = "The contract definition", content = @Content(schema = @Schema(implementation = ContractDefinitionOutputSchema.class))), @@ -65,18 +68,20 @@ public interface ContractDefinitionApi { @Operation(description = "Creates a new contract definition", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractDefinitionInputSchema.class))), + operationId = "createContractDefinitionV3", responses = { @ApiResponse(responseCode = "200", description = "contract definition was created successfully. Returns the Contract Definition Id and created timestamp", content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), @ApiResponse(responseCode = "400", description = "Request body was malformed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), @ApiResponse(responseCode = "409", description = "Could not create contract definition, because a contract definition with that ID already exists", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } ) JsonObject createContractDefinition(JsonObject createObject); @Operation(description = "Removes a contract definition with the given ID if possible. " + "DANGER ZONE: Note that deleting contract definitions can have unexpected results, especially for contract offers that have been sent out or ongoing or contract negotiations.", + operationId = "deleteContractDefV3", responses = { @ApiResponse(responseCode = "204", description = "Contract definition was deleted successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", @@ -89,6 +94,7 @@ public interface ContractDefinitionApi { @Operation(description = "Updated a contract definition with the given ID. The supplied JSON structure must be a valid JSON-LD object", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractDefinitionInputSchema.class))), + operationId = "updateContractDefV3", responses = { @ApiResponse(responseCode = "204", description = "Contract definition was updated successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3Controller.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3Controller.java new file mode 100644 index 00000000000..1ef7a7c6350 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3Controller.java @@ -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 + * + */ + +package org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3; + +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.BaseContractDefinitionApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractdefinition.ContractDefinitionService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/contractdefinitions") +public class ContractDefinitionApiV3Controller extends BaseContractDefinitionApiController implements ContractDefinitionApiV3 { + public ContractDefinitionApiV3Controller(TypeTransformerRegistry transformerRegistry, ContractDefinitionService service, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(transformerRegistry, service, monitor, validatorRegistry); + } + + @Override + public JsonObject createContractDefinition(JsonObject createObject) { + return super.createContractDefinition(createObject); + } +} diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiControllerTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiControllerTest.java similarity index 95% rename from extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiControllerTest.java rename to extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiControllerTest.java index 877eaaf3930..55624b40100 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiControllerTest.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/BaseContractDefinitionApiControllerTest.java @@ -38,7 +38,6 @@ import java.util.List; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static jakarta.json.Json.createObjectBuilder; import static org.assertj.core.api.Assertions.assertThat; @@ -62,11 +61,11 @@ import static org.mockito.Mockito.when; @ApiTest -class ContractDefinitionApiControllerTest extends RestControllerTestBase { +public abstract class BaseContractDefinitionApiControllerTest extends RestControllerTestBase { - private final ContractDefinitionService service = mock(); - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); + protected final ContractDefinitionService service = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); @ParameterizedTest @ValueSource(strings = { "", "{}" }) @@ -361,10 +360,7 @@ void update_whenTransformationFails_shouldThrowException() { verify(service, never()).update(eq(entity)); } - @Override - protected Object controller() { - return new ContractDefinitionApiController(transformerRegistry, service, monitor, validatorRegistry); - } + protected abstract RequestSpecification baseRequest(); private JsonArrayBuilder createCriterionBuilder() { return Json.createArrayBuilder() @@ -386,12 +382,6 @@ private JsonObject createExpandedJsonObject() { .build(); } - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port + "/v2/contractdefinitions") - .when(); - } - private ContractDefinition.Builder createContractDefinition() { return ContractDefinition.Builder.newInstance() .id("1") diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtensionTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtensionTest.java index 2a2e39efdcb..a1728d7d78b 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiExtensionTest.java @@ -15,9 +15,12 @@ package org.eclipse.edc.connector.controlplane.api.management.contractdefinition; import org.eclipse.edc.boot.system.injection.ObjectFactory; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3.ContractDefinitionApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,6 +28,7 @@ import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_TYPE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -32,11 +36,13 @@ class ContractDefinitionApiExtensionTest { private final JsonObjectValidatorRegistry validatorRegistry = mock(); + private final WebService webService = mock(); private ContractDefinitionApiExtension extension; @BeforeEach void setUp(ObjectFactory factory, ServiceExtensionContext context) { context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); extension = factory.constructInstance(ContractDefinitionApiExtension.class); } @@ -48,4 +54,12 @@ void verifyValidatorIsRegistered(ServiceExtensionContext context) { verify(validatorRegistry).register(eq(CONTRACT_DEFINITION_TYPE), any()); } + @Test + void verifyControllersRegistered(ServiceExtensionContext context) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(ContractDefinitionApiV2Controller.class)); + verify(webService).registerResource(any(), isA(ContractDefinitionApiV3Controller.class)); + } + } diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiTest.java index 5aff971f797..d84a2c5c7e9 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiTest.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/ContractDefinitionApiTest.java @@ -29,8 +29,8 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v2.ContractDefinitionApiV2.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_ACCESSPOLICY_ID; import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_ASSETS_SELECTOR; import static org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_CONTRACTPOLICY_ID; diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2ControllerTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2ControllerTest.java new file mode 100644 index 00000000000..52d983579c4 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v2/ContractDefinitionApiV2ControllerTest.java @@ -0,0 +1,36 @@ +/* + * 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.controlplane.api.management.contractdefinition.v2; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.BaseContractDefinitionApiControllerTest; + +import static io.restassured.RestAssured.given; + +class ContractDefinitionApiV2ControllerTest extends BaseContractDefinitionApiControllerTest { + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v2/contractdefinitions") + .when(); + + } + + @Override + protected Object controller() { + return new ContractDefinitionApiV2Controller(transformerRegistry, service, monitor, validatorRegistry); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3ControllerTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3ControllerTest.java new file mode 100644 index 00000000000..d12154c329f --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractdefinition/v3/ContractDefinitionApiV3ControllerTest.java @@ -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 + * + */ + +package org.eclipse.edc.connector.controlplane.api.management.contractdefinition.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractdefinition.BaseContractDefinitionApiControllerTest; + +import static io.restassured.RestAssured.given; + +class ContractDefinitionApiV3ControllerTest extends BaseContractDefinitionApiControllerTest { + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v3/contractdefinitions") + .when(); + + } + + @Override + protected Object controller() { + return new ContractDefinitionApiV3Controller(transformerRegistry, service, monitor, validatorRegistry); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiController.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiController.java similarity index 87% rename from extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiController.java rename to extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiController.java index 1d5a3339532..5568da10c76 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiController.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 ZF Friedrichshafen AG + * 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 @@ -8,9 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * ZF Friedrichshafen AG - Initial API and Implementation - * Microsoft Corporation - Added initiate-negotiation endpoint - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - Improvements + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -49,17 +47,16 @@ import static org.eclipse.edc.spi.query.QuerySpec.EDC_QUERY_SPEC_TYPE; import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -@Path("/v2/contractnegotiations") -public class ContractNegotiationApiController implements ContractNegotiationApi { - private final ContractNegotiationService service; - private final TypeTransformerRegistry transformerRegistry; - private final Monitor monitor; - private final JsonObjectValidatorRegistry validatorRegistry; - - public ContractNegotiationApiController(ContractNegotiationService service, TypeTransformerRegistry transformerRegistry, - Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { +@Consumes({ MediaType.APPLICATION_JSON }) +@Produces({ MediaType.APPLICATION_JSON }) +public class BaseContractNegotiationApiController { + protected final ContractNegotiationService service; + protected final TypeTransformerRegistry transformerRegistry; + protected final Monitor monitor; + protected final JsonObjectValidatorRegistry validatorRegistry; + + public BaseContractNegotiationApiController(ContractNegotiationService service, TypeTransformerRegistry transformerRegistry, + Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { this.service = service; this.transformerRegistry = transformerRegistry; this.monitor = monitor; @@ -68,7 +65,6 @@ public ContractNegotiationApiController(ContractNegotiationService service, Type @POST @Path("/request") - @Override public JsonArray queryNegotiations(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -91,7 +87,6 @@ public JsonArray queryNegotiations(JsonObject querySpecJson) { @GET @Path("/{id}") - @Override public JsonObject getNegotiation(@PathParam("id") String id) { return Optional.of(id) @@ -103,7 +98,6 @@ public JsonObject getNegotiation(@PathParam("id") String id) { @GET @Path("/{id}/state") - @Override public JsonObject getNegotiationState(@PathParam("id") String id) { return Optional.of(id) .map(service::getState) @@ -115,7 +109,6 @@ public JsonObject getNegotiationState(@PathParam("id") String id) { @GET @Path("/{id}/agreement") - @Override public JsonObject getAgreementForNegotiation(@PathParam("id") String negotiationId) { return Optional.of(negotiationId) .map(service::getForNegotiation) @@ -125,7 +118,6 @@ public JsonObject getAgreementForNegotiation(@PathParam("id") String negotiation } @POST - @Override public JsonObject initiateContractNegotiation(JsonObject requestObject) { validatorRegistry.validate(CONTRACT_REQUEST_TYPE, requestObject) .orElseThrow(ValidationFailureException::new); @@ -146,7 +138,6 @@ public JsonObject initiateContractNegotiation(JsonObject requestObject) { @POST @Path("/{id}/terminate") - @Override public void terminateNegotiation(@PathParam("id") String id, JsonObject terminateNegotiation) { validatorRegistry.validate(TERMINATE_NEGOTIATION_TYPE, terminateNegotiation) .orElseThrow(ValidationFailureException::new); diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtension.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtension.java index 91761382d74..6be2eccb940 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtension.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 ZF Friedrichshafen AG + * 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 @@ -8,7 +8,6 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * ZF Friedrichshafen AG - Initial API and Implementation * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -22,6 +21,8 @@ import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v2.ContractNegotiationApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v3.ContractNegotiationApiV3Controller; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.validation.ContractRequestValidator; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.validation.TerminateNegotiationValidator; import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; @@ -79,7 +80,7 @@ public void initialize(ServiceExtensionContext context) { validatorRegistry.register(CONTRACT_REQUEST_TYPE, ContractRequestValidator.instance(monitor)); validatorRegistry.register(TERMINATE_NEGOTIATION_TYPE, TerminateNegotiationValidator.instance()); - var controller = new ContractNegotiationApiController(service, managementApiTransformerRegistry, monitor, validatorRegistry); - webService.registerResource(config.getContextAlias(), controller); + webService.registerResource(config.getContextAlias(), new ContractNegotiationApiV2Controller(service, managementApiTransformerRegistry, monitor, validatorRegistry)); + webService.registerResource(config.getContextAlias(), new ContractNegotiationApiV3Controller(service, managementApiTransformerRegistry, monitor, validatorRegistry)); } } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/model/NegotiationState.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/model/NegotiationState.java index 04c9181d619..d975bf87fee 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/model/NegotiationState.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/model/NegotiationState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Microsoft Corporation + * 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 @@ -8,7 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Microsoft Corporation - initial API and implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformer.java index 9a89253a2c3..998a7cf6976 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformer.java index df72b57abbc..2a6a45b545d 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -22,8 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.model.NegotiationState.NEGOTIATION_STATE_STATE; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.model.NegotiationState.NEGOTIATION_STATE_TYPE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; public class JsonObjectFromNegotiationStateTransformer extends AbstractJsonLdTransformer { @@ -37,8 +35,8 @@ public JsonObjectFromNegotiationStateTransformer(JsonBuilderFactory jsonFactory) @Override public @Nullable JsonObject transform(@NotNull NegotiationState negotiationState, @NotNull TransformerContext context) { return jsonFactory.createObjectBuilder() - .add(TYPE, NEGOTIATION_STATE_TYPE) - .add(NEGOTIATION_STATE_STATE, negotiationState.state()) + .add(TYPE, NegotiationState.NEGOTIATION_STATE_TYPE) + .add(NegotiationState.NEGOTIATION_STATE_STATE, negotiationState.state()) .build(); } } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformer.java index 2db963547c3..8edccb9c9c6 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class JsonObjectToContractOfferTransformer extends AbstractJsonLdTransformer { +public class JsonObjectToContractOfferTransformer extends AbstractJsonLdTransformer { public JsonObjectToContractOfferTransformer() { super(JsonObject.class, ContractOffer.class); diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractRequestTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractRequestTransformer.java index 1b63b9eb6fe..ec45570be5c 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractRequestTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractRequestTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformer.java index d74be49790b..b1b79faf3f5 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2.java new file mode 100644 index 00000000000..b3d64303841 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2.java @@ -0,0 +1,228 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.links.Link; +import io.swagger.v3.oas.annotations.links.LinkParameter; +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.parameters.RequestBody; +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.connector.controlplane.api.management.contractnegotiation.model.NegotiationState; + +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.command.TerminateNegotiationCommand.TERMINATE_NEGOTIATION_TYPE; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest.CONTRACT_REQUEST_TYPE; +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.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_OFFER; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Contract Negotiation V2") +public interface ContractNegotiationApiV2 { + + @Operation(description = "Returns all contract negotiations according to a query", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryNegotiationsV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract negotiations that match the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class)))), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonArray queryNegotiations(JsonObject querySpecJson); + + @Operation(description = "Gets a contract negotiation with the given ID", + operationId = "getNegotiationV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract negotiation", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract negotiation with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getNegotiation(String id); + + @Operation(description = "Gets the state of a contract negotiation with the given ID", + operationId = "getNegotiationStateV2", + responses = { + @ApiResponse(responseCode = "200", description = "The contract negotiation's state", + content = @Content(schema = @Schema(implementation = NegotiationState.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract negotiation with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getNegotiationState(String id); + + @Operation(description = "Gets a contract agreement for a contract negotiation with the given ID", + responses = { + @ApiResponse(responseCode = "200", description = "The contract agreement that is attached to the negotiation, or null", + content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An contract negotiation with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + operationId = "getAgreementForNegotiationV2", + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getAgreementForNegotiation(String negotiationId); + + @Operation(description = "Initiates a contract negotiation for a given offer and with the given counter part. Please note that successfully invoking this endpoint " + + "only means that the negotiation was initiated. Clients must poll the /{id}/state endpoint to track the state", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractRequestSchema.class))), + operationId = "initiateNegotiationV2", + responses = { + @ApiResponse(responseCode = "200", description = "The negotiation was successfully initiated. Returns the contract negotiation ID and created timestamp", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class)), + links = @Link(name = "poll-state", operationId = "getNegotiationState", parameters = { + @LinkParameter(name = "id", expression = "$response.body#/id") + }) + ), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + }, + deprecated = true) + @Deprecated(since = "0.7.0") + JsonObject initiateContractNegotiation(JsonObject requestDto); + + @Operation(description = "Terminates the contract negotiation.", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TerminateNegotiationSchema.class))), + operationId = "terminateNegotiationV2", + responses = { + @ApiResponse(responseCode = "200", description = "ContractNegotiation is terminating", + links = @Link(name = "poll-state", operationId = "getNegotiationState")), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A contract negotiation with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void terminateNegotiation(String id, JsonObject terminateNegotiation); + + @Schema(name = "ContractRequest", example = ContractRequestSchema.CONTRACT_REQUEST_EXAMPLE) + record ContractRequestSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = TYPE, example = CONTRACT_REQUEST_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String protocol, + @Schema(requiredMode = REQUIRED) + String counterPartyAddress, + @Deprecated(since = "0.5.1") + @Schema(deprecated = true, description = "please use policy.assigner instead") + String providerId, + @Schema(requiredMode = REQUIRED) + OfferSchema policy, + List callbackAddresses) { + + // policy example took from https://w3c.github.io/odrl/bp/ + public static final String CONTRACT_REQUEST_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/ContractRequest", + "counterPartyAddress": "http://provider-address", + "protocol": "dataspace-protocol-http", + "policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "odrl:Offer", + "@id": "offer-id", + "assigner": "providerId", + "permission": [], + "prohibition": [], + "obligation": [], + "target": "assetId" + }, + "callbackAddresses": [{ + "transactional": false, + "uri": "http://callback/url", + "events": ["contract.negotiation", "transfer.process"], + "authKey": "auth-key", + "authCodeId": "auth-code-id" + }] + } + """; + } + + @Schema(name = "Offer", description = "ODRL offer", example = OfferSchema.OFFER_EXAMPLE) + record OfferSchema( + @Schema(name = TYPE, example = ODRL_POLICY_TYPE_OFFER) + String type, + @Schema(name = ID, requiredMode = REQUIRED) + String id, + @Schema(requiredMode = REQUIRED) + String assigner, + @Schema(requiredMode = REQUIRED) + String target + ) { + public static final String OFFER_EXAMPLE = """ + { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "odrl:Offer", + "@id": "offer-id", + "assigner": "providerId", + "target": "assetId", + "permission": [], + "prohibition": [], + "obligation": [] + } + """; + } + + @Schema(example = TerminateNegotiationSchema.TERMINATE_NEGOTIATION_EXAMPLE) + record TerminateNegotiationSchema( + @Schema(name = TYPE, example = TERMINATE_NEGOTIATION_TYPE) + String ldType, + @Schema(name = ID) + String id, + String reason + ) { + public static final String TERMINATE_NEGOTIATION_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/TerminateNegotiation", + "@id": "negotiation-id", + "reason": "a reason to terminate" + } + """; + } + +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Controller.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Controller.java new file mode 100644 index 00000000000..88ea46e9098 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Controller.java @@ -0,0 +1,69 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v2; + +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + + +@Path("/v2/contractnegotiations") +public class ContractNegotiationApiV2Controller extends BaseContractNegotiationApiController implements ContractNegotiationApiV2 { + public ContractNegotiationApiV2Controller(ContractNegotiationService service, TypeTransformerRegistry transformerRegistry, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(service, transformerRegistry, monitor, validatorRegistry); + } + + @Override + public JsonArray queryNegotiations(JsonObject querySpecJson) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.queryNegotiations(querySpecJson); + } + + @Override + public JsonObject getNegotiation(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getNegotiation(id); + } + + @Override + public JsonObject getNegotiationState(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getNegotiationState(id); + } + + @Override + public JsonObject getAgreementForNegotiation(String negotiationId) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getAgreementForNegotiation(negotiationId); + } + + @Override + public JsonObject initiateContractNegotiation(JsonObject requestObject) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.initiateContractNegotiation(requestObject); + } + + @Override + public void terminateNegotiation(String id, JsonObject terminateNegotiation) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + super.terminateNegotiation(id, terminateNegotiation); + } +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApi.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3.java similarity index 91% rename from extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApi.java rename to extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3.java index 14c00798b6a..9c4e9a1df3a 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApi.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -8,14 +8,15 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.edc.connector.controlplane.api.management.contractnegotiation; +package org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.links.Link; import io.swagger.v3.oas.annotations.links.LinkParameter; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -40,21 +41,23 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_OFFER; -@OpenAPIDefinition -@Tag(name = "Contract Negotiation") -public interface ContractNegotiationApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Contract Negotiation V3") +public interface ContractNegotiationApiV3 { @Operation(description = "Returns all contract negotiations according to a query", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryNegotiationsV3", responses = { @ApiResponse(responseCode = "200", description = "The contract negotiations that match the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class)))), @ApiResponse(responseCode = "400", description = "Request was malformed", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } ) JsonArray queryNegotiations(JsonObject querySpecJson); @Operation(description = "Gets a contract negotiation with the given ID", + operationId = "getNegotiationsV3", responses = { @ApiResponse(responseCode = "200", description = "The contract negotiation", content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractNegotiationSchema.class))), @@ -67,7 +70,7 @@ public interface ContractNegotiationApi { JsonObject getNegotiation(String id); @Operation(description = "Gets the state of a contract negotiation with the given ID", - operationId = "getNegotiationState", + operationId = "getNegotiationStateV3", responses = { @ApiResponse(responseCode = "200", description = "The contract negotiation's state", content = @Content(schema = @Schema(implementation = NegotiationState.class))), @@ -80,6 +83,7 @@ public interface ContractNegotiationApi { JsonObject getNegotiationState(String id); @Operation(description = "Gets a contract agreement for a contract negotiation with the given ID", + operationId = "getAgreementForNegotiationV3", responses = { @ApiResponse(responseCode = "200", description = "The contract agreement that is attached to the negotiation, or null", content = @Content(schema = @Schema(implementation = ManagementApiSchema.ContractAgreementSchema.class))), @@ -94,6 +98,7 @@ public interface ContractNegotiationApi { @Operation(description = "Initiates a contract negotiation for a given offer and with the given counter part. Please note that successfully invoking this endpoint " + "only means that the negotiation was initiated. Clients must poll the /{id}/state endpoint to track the state", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ContractRequestSchema.class))), + operationId = "initiateNegotiationV3", responses = { @ApiResponse(responseCode = "200", description = "The negotiation was successfully initiated. Returns the contract negotiation ID and created timestamp", content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class)), @@ -108,6 +113,7 @@ public interface ContractNegotiationApi { @Operation(description = "Terminates the contract negotiation.", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TerminateNegotiationSchema.class))), + operationId = "terminateNegotiationV3", responses = { @ApiResponse(responseCode = "200", description = "ContractNegotiation is terminating", links = @Link(name = "poll-state", operationId = "getNegotiationState")), @@ -129,9 +135,6 @@ record ContractRequestSchema( String protocol, @Schema(requiredMode = REQUIRED) String counterPartyAddress, - @Deprecated(since = "0.5.1") - @Schema(deprecated = true, description = "please use policy.assigner instead") - String providerId, @Schema(requiredMode = REQUIRED) OfferSchema policy, List callbackAddresses) { @@ -189,21 +192,6 @@ record OfferSchema( """; } - @Schema(name = "NegotiationState", example = NegotiationStateSchema.NEGOTIATION_STATE_EXAMPLE) - record NegotiationStateSchema( - @Schema(name = TYPE, example = NegotiationState.NEGOTIATION_STATE_TYPE) - String ldType, - String state - ) { - public static final String NEGOTIATION_STATE_EXAMPLE = """ - { - "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, - "@type": "https://w3id.org/edc/v0.0.1/ns/NegotiationState", - "state": "REQUESTED" - } - """; - } - @Schema(example = TerminateNegotiationSchema.TERMINATE_NEGOTIATION_EXAMPLE) record TerminateNegotiationSchema( @Schema(name = TYPE, example = TERMINATE_NEGOTIATION_TYPE) diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Controller.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Controller.java new file mode 100644 index 00000000000..a270125b5d0 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Controller.java @@ -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.controlplane.api.management.contractnegotiation.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiController; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/contractnegotiations") +public class ContractNegotiationApiV3Controller extends BaseContractNegotiationApiController implements ContractNegotiationApiV3 { + public ContractNegotiationApiV3Controller(ContractNegotiationService service, TypeTransformerRegistry transformerRegistry, Monitor monitor, JsonObjectValidatorRegistry validatorRegistry) { + super(service, transformerRegistry, monitor, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/ContractRequestValidator.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/ContractRequestValidator.java index 0c090e39027..d50d83f1dc3 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/ContractRequestValidator.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/ContractRequestValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidator.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidator.java index 102f4c8a6bb..d1f635d0eb6 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidator.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiControllerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiControllerTest.java similarity index 95% rename from extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiControllerTest.java rename to extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiControllerTest.java index f855556af68..b33d2ca2fb8 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiControllerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - 2022 ZF Friedrichshafen AG + * 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 @@ -8,9 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * ZF Friedrichshafen AG - Initial API and Implementation - * Microsoft Corporation - Added initiate-negotiation endpoint tests - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -27,7 +25,6 @@ import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; import org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractOffer; import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; -import org.eclipse.edc.junit.annotations.ApiTest; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.result.Result; @@ -41,7 +38,6 @@ import java.util.List; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static jakarta.json.Json.createObjectBuilder; import static java.util.UUID.randomUUID; @@ -67,11 +63,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -@ApiTest -class ContractNegotiationApiControllerTest extends RestControllerTestBase { - private final ContractNegotiationService service = mock(); - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); +public abstract class BaseContractNegotiationApiControllerTest extends RestControllerTestBase { + protected final ContractNegotiationService service = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); @Test void getAll() { @@ -452,16 +447,7 @@ void terminate_shouldReturnBadRequest_whenTransformationFails() { verifyNoInteractions(transformerRegistry, service); } - @Override - protected Object controller() { - return new ContractNegotiationApiController(service, transformerRegistry, monitor, validatorRegistry); - } - - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port + "/v2/contractnegotiations") - .when(); - } + protected abstract RequestSpecification baseRequest(); private ContractNegotiation createContractNegotiation(String negotiationId) { return createContractNegotiationBuilder(negotiationId) @@ -488,5 +474,4 @@ private ContractNegotiation.Builder createContractNegotiationBuilder(String nego .build())) .protocol("protocol"); } - } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiTest.java similarity index 70% rename from extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiTest.java rename to extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiTest.java index 059e70d08ec..6cf585c85f8 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/BaseContractNegotiationApiTest.java @@ -21,6 +21,7 @@ import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v2.ContractNegotiationApiV2; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.validation.ContractRequestValidator; import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.validation.TerminateNegotiationValidator; import org.eclipse.edc.connector.controlplane.contract.spi.types.command.TerminateNegotiationCommand; @@ -29,6 +30,7 @@ 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.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantIdMapper; import org.eclipse.edc.spi.monitor.Monitor; @@ -39,24 +41,17 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.ContractNegotiationApi.ContractRequestSchema.CONTRACT_REQUEST_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.ContractNegotiationApi.NegotiationStateSchema.NEGOTIATION_STATE_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.ContractNegotiationApi.TerminateNegotiationSchema.TERMINATE_NEGOTIATION_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.model.NegotiationState.NEGOTIATION_STATE_STATE; -import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.model.NegotiationState.NEGOTIATION_STATE_TYPE; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; -import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v3.ContractNegotiationApiV3.ContractRequestSchema.CONTRACT_REQUEST_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v3.ContractNegotiationApiV3.TerminateNegotiationSchema.TERMINATE_NEGOTIATION_EXAMPLE; import static org.eclipse.edc.junit.extensions.TestServiceExtensionContext.testServiceExtensionContext; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -class ContractNegotiationApiTest { - +public abstract class BaseContractNegotiationApiTest { + protected final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl(); private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); private final JsonLd jsonLd = new JsonLdExtension().createJsonLdService(testServiceExtensionContext()); - private final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl(); private final Monitor monitor = mock(); @BeforeEach @@ -78,10 +73,10 @@ void contractRequestExample() throws JsonProcessingException { assertThat(jsonObject).isNotNull(); var expanded = jsonLd.expand(jsonObject); - assertThat(expanded).isSucceeded() - .satisfies(exp -> assertThat(validator.validate(exp)).isSucceeded()) + AbstractResultAssert.assertThat(expanded).isSucceeded() + .satisfies(exp -> AbstractResultAssert.assertThat(validator.validate(exp)).isSucceeded()) .extracting(e -> transformer.transform(e, ContractRequest.class)) - .satisfies(transformResult -> assertThat(transformResult).isSucceeded() + .satisfies(transformResult -> AbstractResultAssert.assertThat(transformResult).isSucceeded() .satisfies(transformed -> assertThat(transformed.getProtocol()).isNotBlank())); } @@ -89,14 +84,14 @@ void contractRequestExample() throws JsonProcessingException { void offerExample() throws JsonProcessingException { var validator = ContractRequestValidator.offerValidator(JsonObjectValidator.newValidator()).build(); - var jsonObject = objectMapper.readValue(ContractNegotiationApi.OfferSchema.OFFER_EXAMPLE, JsonObject.class); + var jsonObject = objectMapper.readValue(ContractNegotiationApiV2.OfferSchema.OFFER_EXAMPLE, JsonObject.class); assertThat(jsonObject).isNotNull(); var expanded = jsonLd.expand(jsonObject); - assertThat(expanded).isSucceeded() - .satisfies(exp -> assertThat(validator.validate(exp)).isSucceeded()) + AbstractResultAssert.assertThat(expanded).isSucceeded() + .satisfies(exp -> AbstractResultAssert.assertThat(validator.validate(exp)).isSucceeded()) .extracting(e -> transformer.transform(e, Policy.class)) - .satisfies(transformResult -> assertThat(transformResult).isSucceeded() + .satisfies(transformResult -> AbstractResultAssert.assertThat(transformResult).isSucceeded() .satisfies(transformed -> assertThat(transformed.getAssigner()).isNotBlank())); } @@ -108,25 +103,13 @@ void terminateNegotiationExample() throws JsonProcessingException { assertThat(jsonObject).isNotNull(); var expanded = jsonLd.expand(jsonObject); - assertThat(expanded).isSucceeded() - .satisfies(exp -> assertThat(validator.validate(exp)).isSucceeded()) + AbstractResultAssert.assertThat(expanded).isSucceeded() + .satisfies(exp -> AbstractResultAssert.assertThat(validator.validate(exp)).isSucceeded()) .extracting(e -> transformer.transform(e, TerminateNegotiationCommand.class)) - .satisfies(transformResult -> assertThat(transformResult).isSucceeded() + .satisfies(transformResult -> AbstractResultAssert.assertThat(transformResult).isSucceeded() .satisfies(transformed -> { assertThat(transformed.getEntityId()).isNotBlank(); assertThat(transformed.getReason()).isNotBlank(); })); } - - @Test - void negotiationStateExample() throws JsonProcessingException { - var jsonObject = objectMapper.readValue(NEGOTIATION_STATE_EXAMPLE, JsonObject.class); - var expanded = jsonLd.expand(jsonObject); - - assertThat(expanded).isSucceeded().satisfies(content -> { - assertThat(content.getJsonArray(TYPE).getString(0)).isEqualTo(NEGOTIATION_STATE_TYPE); - assertThat(content.getJsonArray(NEGOTIATION_STATE_STATE).getJsonObject(0).getString(VALUE)).isNotBlank(); - }); - } - } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtensionTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtensionTest.java index 2f6d67df74f..9c69a957e0e 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/ContractNegotiationApiExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -14,10 +14,17 @@ package org.eclipse.edc.connector.controlplane.api.management.contractnegotiation; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectFromContractNegotiationTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectFromNegotiationStateTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v2.ContractNegotiationApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.v3.ContractNegotiationApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -26,6 +33,7 @@ import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest.CONTRACT_REQUEST_TYPE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,13 +42,15 @@ class ContractNegotiationApiExtensionTest { private final JsonObjectValidatorRegistry validatorRegistry = mock(JsonObjectValidatorRegistry.class); + private final WebService webService = mock(); + private final TypeTransformerRegistry typeTransformerRegistry = mock(); @BeforeEach void setUp(ServiceExtensionContext context) { - TypeTransformerRegistry typeTransformerRegistry = mock(); when(typeTransformerRegistry.forContext(any())).thenReturn(mock()); context.registerService(TypeTransformerRegistry.class, typeTransformerRegistry); context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); } @Test @@ -50,4 +60,24 @@ void initiate_shouldRegisterValidators(ServiceExtensionContext context, Contract verify(validatorRegistry).register(eq(CONTRACT_REQUEST_TYPE), any()); verify(validatorRegistry).register(eq(TERMINATE_NEGOTIATION_TYPE), any()); } + + @Test + void initiate_shouldRegisterControllers(ServiceExtensionContext context, ContractNegotiationApiExtension extension) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(ContractNegotiationApiV2Controller.class)); + verify(webService).registerResource(any(), isA(ContractNegotiationApiV3Controller.class)); + } + + @Test + void initiate_shouldRegisterTransformers(ServiceExtensionContext context, ContractNegotiationApiExtension extension) { + var scopedRegistry = mock(TypeTransformerRegistry.class); + when(typeTransformerRegistry.forContext(eq("management-api"))).thenReturn(scopedRegistry); + extension.initialize(context); + + verify(scopedRegistry).register(isA(JsonObjectToContractOfferTransformer.class)); + verify(scopedRegistry).register(isA(JsonObjectToTerminateNegotiationCommandTransformer.class)); + verify(scopedRegistry).register(isA(JsonObjectFromContractNegotiationTransformer.class)); + verify(scopedRegistry).register(isA(JsonObjectFromNegotiationStateTransformer.class)); + } } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformerTest.java index 6a701f1fd9d..aed3f1e5e21 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromContractNegotiationTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformerTest.java index ac829442fc0..366a8b7af60 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectFromNegotiationStateTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformerTest.java index a9b2bf6e4b6..4b2c816c8fe 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToContractOfferTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformerTest.java index 65aaabdbb40..f7b6ff845e8 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/transform/JsonObjectToTerminateNegotiationCommandTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2ControllerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2ControllerTest.java new file mode 100644 index 00000000000..4dbde87e70a --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2ControllerTest.java @@ -0,0 +1,37 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v2; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiControllerTest; +import org.eclipse.edc.junit.annotations.ApiTest; + +import static io.restassured.RestAssured.given; + +@ApiTest +class ContractNegotiationApiV2ControllerTest extends BaseContractNegotiationApiControllerTest { + + @Override + protected Object controller() { + return new ContractNegotiationApiV2Controller(service, transformerRegistry, monitor, validatorRegistry); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v2/contractnegotiations") + .when(); + } +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Test.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Test.java new file mode 100644 index 00000000000..b6b863b3824 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v2/ContractNegotiationApiV2Test.java @@ -0,0 +1,43 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v2; + +import org.eclipse.edc.api.transformer.JsonObjectToCallbackAddressTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiTest; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer; +import org.eclipse.edc.connector.controlplane.transform.odrl.OdrlTransformersFactory; +import org.eclipse.edc.spi.agent.ParticipantIdMapper; +import org.junit.jupiter.api.BeforeEach; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class ContractNegotiationApiV2Test extends BaseContractNegotiationApiTest { + + @BeforeEach + void setUp() { + transformer.register(new JsonObjectToContractRequestTransformer()); + transformer.register(new JsonObjectToContractOfferTransformer()); + transformer.register(new JsonObjectToCallbackAddressTransformer()); + transformer.register(new JsonObjectToTerminateNegotiationCommandTransformer()); + ParticipantIdMapper participantIdMapper = mock(); + when(participantIdMapper.fromIri(any())).thenAnswer(a -> a.getArgument(0)); + OdrlTransformersFactory.jsonObjectToOdrlTransformers(participantIdMapper).forEach(transformer::register); + } + +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3ControllerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3ControllerTest.java new file mode 100644 index 00000000000..1c50fbb2df8 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3ControllerTest.java @@ -0,0 +1,36 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiControllerTest; +import org.eclipse.edc.junit.annotations.ApiTest; + +import static io.restassured.RestAssured.given; + +@ApiTest +class ContractNegotiationApiV3ControllerTest extends BaseContractNegotiationApiControllerTest { + + @Override + protected Object controller() { + return new ContractNegotiationApiV3Controller(service, transformerRegistry, monitor, validatorRegistry); + } + + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v3/contractnegotiations") + .when(); + } +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Test.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Test.java new file mode 100644 index 00000000000..945ac30f203 --- /dev/null +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/v3/ContractNegotiationApiV3Test.java @@ -0,0 +1,44 @@ +/* + * 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.controlplane.api.management.contractnegotiation.v3; + +import org.eclipse.edc.api.transformer.JsonObjectToCallbackAddressTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.BaseContractNegotiationApiTest; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractOfferTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToContractRequestTransformer; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.transform.JsonObjectToTerminateNegotiationCommandTransformer; +import org.eclipse.edc.connector.controlplane.transform.odrl.OdrlTransformersFactory; +import org.eclipse.edc.spi.agent.ParticipantIdMapper; +import org.junit.jupiter.api.BeforeEach; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class ContractNegotiationApiV3Test extends BaseContractNegotiationApiTest { + + @BeforeEach + void setUp() { + transformer.register(new JsonObjectToContractRequestTransformer()); + transformer.register(new JsonObjectToContractOfferTransformer()); + //missing: registering the deprecated transformer + transformer.register(new JsonObjectToCallbackAddressTransformer()); + transformer.register(new JsonObjectToTerminateNegotiationCommandTransformer()); + ParticipantIdMapper participantIdMapper = mock(); + when(participantIdMapper.fromIri(any())).thenAnswer(a -> a.getArgument(0)); + OdrlTransformersFactory.jsonObjectToOdrlTransformers(participantIdMapper).forEach(transformer::register); + } + +} diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidatorTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidatorTest.java index 4bb86c19fc5..2b3d7fdc666 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidatorTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/contractnegotiation/validation/TerminateNegotiationValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiController.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiController.java similarity index 87% rename from extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiController.java rename to extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiController.java index 2d1c98d9fb9..d3fe137bbf5 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiController.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiController.java @@ -12,8 +12,7 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.edr.v1; - +package org.eclipse.edc.connector.controlplane.api.management.edr; import jakarta.json.JsonArray; import jakarta.json.JsonObject; @@ -43,18 +42,13 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) -@Path("/v1/edrs") -public class EdrCacheApiController implements EdrCacheApi { - - private final EndpointDataReferenceStore edrStore; - - private final TypeTransformerRegistry transformerRegistry; +public class BaseEdrCacheApiController { + protected final EndpointDataReferenceStore edrStore; + protected final TypeTransformerRegistry transformerRegistry; + protected final JsonObjectValidatorRegistry validator; + protected final Monitor monitor; - private final JsonObjectValidatorRegistry validator; - - private final Monitor monitor; - - public EdrCacheApiController(EndpointDataReferenceStore edrStore, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator, Monitor monitor) { + public BaseEdrCacheApiController(EndpointDataReferenceStore edrStore, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator, Monitor monitor) { this.edrStore = edrStore; this.transformerRegistry = transformerRegistry; this.validator = validator; @@ -63,7 +57,6 @@ public EdrCacheApiController(EndpointDataReferenceStore edrStore, TypeTransforme @POST @Path("/request") - @Override public JsonArray requestEdrEntries(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -87,7 +80,6 @@ public JsonArray requestEdrEntries(JsonObject querySpecJson) { @GET @Path("{transferProcessId}/dataaddress") - @Override public JsonObject getEdrEntryDataAddress(@PathParam("transferProcessId") String transferProcessId) { var dataAddress = edrStore.resolveByTransferProcess(transferProcessId) .flatMap(ServiceResult::from) @@ -101,11 +93,9 @@ public JsonObject getEdrEntryDataAddress(@PathParam("transferProcessId") String @DELETE @Path("{transferProcessId}") - @Override public void removeEdrEntry(@PathParam("transferProcessId") String transferProcessId) { edrStore.delete(transferProcessId) .flatMap(ServiceResult::from) .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class, transferProcessId)); } - } diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtension.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtension.java index ee59fa64204..6c9a009f424 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtension.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtension.java @@ -17,7 +17,8 @@ import jakarta.json.Json; import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.controlplane.api.management.edr.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; -import org.eclipse.edc.connector.controlplane.api.management.edr.v1.EdrCacheApiController; +import org.eclipse.edc.connector.controlplane.api.management.edr.v1.EdrCacheApiV1Controller; +import org.eclipse.edc.connector.controlplane.api.management.edr.v3.EdrCacheApiV3Controller; import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; @@ -66,7 +67,7 @@ public void initialize(ServiceExtensionContext context) { managementTypeTransformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer(jsonFactory)); - webService.registerResource(config.getContextAlias(), new EdrCacheApiController(edrStore, - managementTypeTransformerRegistry, validator, monitor)); + webService.registerResource(config.getContextAlias(), new EdrCacheApiV1Controller(edrStore, managementTypeTransformerRegistry, validator, monitor)); + webService.registerResource(config.getContextAlias(), new EdrCacheApiV3Controller(edrStore, managementTypeTransformerRegistry, validator, monitor)); } } diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1.java new file mode 100644 index 00000000000..c8703d5f684 --- /dev/null +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1.java @@ -0,0 +1,102 @@ +/* + * 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.controlplane.api.management.edr.v1; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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.parameters.RequestBody; +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.edr.spi.types.EndpointDataReferenceEntry; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +@OpenAPIDefinition(info = @Info(version = "v1")) +@Tag(name = "EDR Cache V1") +public interface EdrCacheApiV1 { + + @Operation(description = "Request all Edr entries according to a particular query", + requestBody = @RequestBody( + content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class)) + ), + operationId = "requestEdrEntriesV1", + responses = { + @ApiResponse(responseCode = "200", description = "The edr entries matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntrySchema.class)))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, deprecated = true) + @Deprecated(since = "0.7.0") + JsonArray requestEdrEntries(JsonObject querySpecJson); + + @Operation(description = "Gets the EDR data address with the given transfer process ID", + operationId = "getEdrEndryDataAddressV1", + responses = { + @ApiResponse(responseCode = "200", description = "The data address", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.DataAddressSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An EDR data address with the given transfer process ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getEdrEntryDataAddress(String transferProcessId); + + @Operation(description = "Removes an EDR entry given the transfer process ID", + operationId = "removeEdrEntryV1", + responses = { + @ApiResponse(responseCode = "204", description = "EDR entry was deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An EDR entry with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, deprecated = true) + @Deprecated(since = "0.7.0") + void removeEdrEntry(String transferProcessId); + + + @ArraySchema() + @Schema(name = "EndpointDataReferenceEntry", example = EndpointDataReferenceEntrySchema.EDR_ENTRY_OUTPUT_EXAMPLE) + record EndpointDataReferenceEntrySchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EndpointDataReferenceEntry.EDR_ENTRY_TYPE) + String type + ) { + public static final String EDR_ENTRY_OUTPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "transfer-process-id", + "transferProcessId": "transfer-process-id", + "agreementId": "agreement-id", + "contractNegotiationId": "contract-negotiation-id", + "assetId": "asset-id", + "providerId": "provider-id", + "createdAt": 1688465655 + } + """; + } + +} diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1Controller.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1Controller.java new file mode 100644 index 00000000000..c72406babdf --- /dev/null +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiV1Controller.java @@ -0,0 +1,52 @@ +/* + * 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.controlplane.api.management.edr.v1; + + +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.edr.BaseEdrCacheApiController; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + + +@Path("/v1/edrs") +public class EdrCacheApiV1Controller extends BaseEdrCacheApiController implements EdrCacheApiV1 { + public EdrCacheApiV1Controller(EndpointDataReferenceStore edrStore, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator, Monitor monitor) { + super(edrStore, transformerRegistry, validator, monitor); + } + + @Override + public JsonArray requestEdrEntries(JsonObject querySpecJson) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + return super.requestEdrEntries(querySpecJson); + } + + @Override + public JsonObject getEdrEntryDataAddress(String transferProcessId) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + return super.getEdrEntryDataAddress(transferProcessId); + } + + @Override + public void removeEdrEntry(String transferProcessId) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + super.removeEdrEntry(transferProcessId); + } +} diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApi.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3.java similarity index 93% rename from extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApi.java rename to extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3.java index 41f86377eff..3e40545b1fb 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApi.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3.java @@ -12,10 +12,11 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.edr.v1; +package org.eclipse.edc.connector.controlplane.api.management.edr.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -30,14 +31,15 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -@OpenAPIDefinition -@Tag(name = "EDR Cache") -public interface EdrCacheApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "EDR Cache V3") +public interface EdrCacheApiV3 { @Operation(description = "Request all Edr entries according to a particular query", requestBody = @RequestBody( content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class)) ), + operationId = "requestEdrEntriesV3", responses = { @ApiResponse(responseCode = "200", description = "The edr entries matching the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntrySchema.class)))), @@ -47,6 +49,7 @@ public interface EdrCacheApi { JsonArray requestEdrEntries(JsonObject querySpecJson); @Operation(description = "Gets the EDR data address with the given transfer process ID", + operationId = "getEdrEndryDataAddressV3", responses = { @ApiResponse(responseCode = "200", description = "The data address", content = @Content(schema = @Schema(implementation = ApiCoreSchema.DataAddressSchema.class))), @@ -54,11 +57,13 @@ public interface EdrCacheApi { content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), @ApiResponse(responseCode = "404", description = "An EDR data address with the given transfer process ID does not exist", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) - } + }, + deprecated = true ) JsonObject getEdrEntryDataAddress(String transferProcessId); @Operation(description = "Removes an EDR entry given the transfer process ID", + operationId = "removeEdrEntryV3", responses = { @ApiResponse(responseCode = "204", description = "EDR entry was deleted successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3Controller.java b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3Controller.java new file mode 100644 index 00000000000..06699d61c5c --- /dev/null +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiV3Controller.java @@ -0,0 +1,30 @@ +/* + * 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.controlplane.api.management.edr.v3; + + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.edr.BaseEdrCacheApiController; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/edrs") +public class EdrCacheApiV3Controller extends BaseEdrCacheApiController implements EdrCacheApiV3 { + public EdrCacheApiV3Controller(EndpointDataReferenceStore edrStore, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator, Monitor monitor) { + super(edrStore, transformerRegistry, validator, monitor); + } +} diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiControllerTest.java b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiControllerTest.java new file mode 100644 index 00000000000..8fde43ed380 --- /dev/null +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/BaseEdrCacheApiControllerTest.java @@ -0,0 +1,202 @@ +/* + * 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.controlplane.api.management.edr; + +import io.restassured.specification.RequestSpecification; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; +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.jsonld.spi.JsonLdKeywords.VOCAB; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public abstract class BaseEdrCacheApiControllerTest extends RestControllerTestBase { + private static final String TEST_TRANSFER_PROCESS_ID = "test-transfer-process-id"; + private static final String TEST_TRANSFER_NEGOTIATION_ID = "test-cn-id"; + private static final String TEST_AGREEMENT_ID = "test-agreement-id"; + private static final String TEST_PROVIDER_ID = "test-provider-id"; + private static final String TEST_ASSET_ID = "test-asset-id"; + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validator = mock(); + protected final EndpointDataReferenceStore edrStore = mock(); + + @Test + void requestEdrEntries() { + when(edrStore.query(any())) + .thenReturn(StoreResult.success(List.of(createEdrEntry()))); + when(transformerRegistry.transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class))) + .thenReturn(Result.success(createEdrEntryJson().build())); + when(transformerRegistry.transform(isA(JsonObject.class), eq(QuerySpec.class))) + .thenReturn(Result.success(QuerySpec.Builder.newInstance().offset(10).build())); + when(validator.validate(any(), any())).thenReturn(ValidationResult.success()); + + baseRequest() + .contentType(JSON) + .body("{}") + .post("/edrs/request") + .then() + .log().ifError() + .statusCode(200) + .contentType(JSON) + .body("size()", is(1)); + + verify(edrStore).query(argThat(s -> s.getOffset() == 10)); + verify(transformerRegistry).transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class)); + verify(transformerRegistry).transform(isA(JsonObject.class), eq(QuerySpec.class)); + } + + @Test + void getEdrEntryDataAddress() { + + var dataAddressType = "type"; + var dataAddress = DataAddress.Builder.newInstance().type(dataAddressType).build(); + when(edrStore.resolveByTransferProcess("transferProcessId")) + .thenReturn(StoreResult.success(dataAddress)); + + when(transformerRegistry.transform(isA(DataAddress.class), eq(JsonObject.class))) + .thenReturn(Result.success(createDataAddress(dataAddressType).build())); + + baseRequest() + .contentType(JSON) + .get("/edrs/transferProcessId/dataaddress") + .then() + .log().ifError() + .statusCode(200) + .contentType(JSON) + .body("'%s'".formatted(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY), equalTo(dataAddressType)); + + verify(edrStore).resolveByTransferProcess("transferProcessId"); + verify(transformerRegistry).transform(isA(DataAddress.class), eq(JsonObject.class)); + verifyNoMoreInteractions(transformerRegistry); + } + + @Test + void getEdrEntryDataAddress_whenNotFound() { + + when(edrStore.resolveByTransferProcess("transferProcessId")) + .thenReturn(StoreResult.notFound("notFound")); + + + baseRequest() + .contentType(JSON) + .get("/edrs/transferProcessId/dataaddress") + .then() + .log().ifError() + .statusCode(404) + .contentType(JSON); + + verify(edrStore).resolveByTransferProcess("transferProcessId"); + verifyNoMoreInteractions(transformerRegistry); + } + + @Test + void removeEdrEntry() { + when(edrStore.delete("transferProcessId")) + .thenReturn(StoreResult.success(createEdrEntry())); + + baseRequest() + .contentType(JSON) + .delete("/edrs/transferProcessId") + .then() + .statusCode(204); + verify(edrStore).delete("transferProcessId"); + } + + @Test + void removeEdrEntry_whenNotFound() { + when(edrStore.delete("transferProcessId")) + .thenReturn(StoreResult.notFound("not found")); + + baseRequest() + .contentType(JSON) + .delete("/edrs/transferProcessId") + .then() + .statusCode(404); + + verify(edrStore).delete("transferProcessId"); + } + + protected abstract RequestSpecification baseRequest(); + + private JsonObjectBuilder createEdrEntryJson() { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, EDR_ENTRY_TYPE) + .add(ID, TEST_TRANSFER_PROCESS_ID) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, TEST_TRANSFER_PROCESS_ID) + .add(EDR_ENTRY_PROVIDER_ID, TEST_PROVIDER_ID) + .add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, TEST_TRANSFER_NEGOTIATION_ID) + .add(EDR_ENTRY_ASSET_ID, TEST_ASSET_ID) + .add(EDR_ENTRY_AGREEMENT_ID, TEST_AGREEMENT_ID); + } + + private JsonObjectBuilder createDataAddress(String type) { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, DataAddress.EDC_DATA_ADDRESS_TYPE) + .add(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY, type); + } + + private EndpointDataReferenceEntry createEdrEntry() { + return EndpointDataReferenceEntry.Builder.newInstance() + .agreementId(TEST_AGREEMENT_ID) + .assetId(TEST_ASSET_ID) + .providerId(TEST_PROVIDER_ID) + .transferProcessId(TEST_TRANSFER_PROCESS_ID) + .contractNegotiationId(TEST_TRANSFER_NEGOTIATION_ID) + .build(); + + } + + private JsonObjectBuilder createContextBuilder() { + return createObjectBuilder() + .add(VOCAB, EDC_NAMESPACE) + .add(EDC_PREFIX, EDC_NAMESPACE); + } +} diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiExtensionTest.java b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtensionTest.java similarity index 91% rename from extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiExtensionTest.java rename to extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtensionTest.java index 73d8ad51333..06495cf1653 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiExtensionTest.java @@ -12,10 +12,11 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.edr.v1; +package org.eclipse.edc.connector.controlplane.api.management.edr; -import org.eclipse.edc.connector.controlplane.api.management.edr.EdrCacheApiExtension; import org.eclipse.edc.connector.controlplane.api.management.edr.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; +import org.eclipse.edc.connector.controlplane.api.management.edr.v1.EdrCacheApiV1Controller; +import org.eclipse.edc.connector.controlplane.api.management.edr.v3.EdrCacheApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -52,7 +53,8 @@ void setUp(ServiceExtensionContext context) { void initialize_shouldRegisterControllers(EdrCacheApiExtension extension, ServiceExtensionContext context) { extension.initialize(context); - verify(webService).registerResource(any(), isA(EdrCacheApiController.class)); + verify(webService).registerResource(any(), isA(EdrCacheApiV1Controller.class)); + verify(webService).registerResource(any(), isA(EdrCacheApiV3Controller.class)); } @Test diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiTest.java b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiTest.java similarity index 96% rename from extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiTest.java rename to extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiTest.java index 2482444cb23..3830108350b 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiTest.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/EdrCacheApiTest.java @@ -12,7 +12,7 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.edr.v1; +package org.eclipse.edc.connector.controlplane.api.management.edr; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.connector.controlplane.api.management.edr.v1.EdrCacheApi.EndpointDataReferenceEntrySchema.EDR_ENTRY_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.edr.v1.EdrCacheApiV1.EndpointDataReferenceEntrySchema.EDR_ENTRY_OUTPUT_EXAMPLE; import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiControllerTest.java b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiControllerTest.java index 82fe0534050..50a254b5007 100644 --- a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiControllerTest.java +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v1/EdrCacheApiControllerTest.java @@ -15,202 +15,23 @@ package org.eclipse.edc.connector.controlplane.api.management.edr.v1; import io.restassured.specification.RequestSpecification; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; -import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.connector.controlplane.api.management.edr.BaseEdrCacheApiControllerTest; import org.eclipse.edc.junit.annotations.ApiTest; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.result.StoreResult; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; -import org.eclipse.edc.validator.spi.ValidationResult; -import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; -import org.junit.jupiter.api.Test; - -import java.util.List; import static io.restassured.RestAssured.given; -import static io.restassured.http.ContentType.JSON; -import static jakarta.json.Json.createObjectBuilder; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_CONTRACT_NEGOTIATION_ID; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_PROVIDER_ID; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; -import static org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; -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.jsonld.spi.JsonLdKeywords.VOCAB; -import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; -import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; @ApiTest -public class EdrCacheApiControllerTest extends RestControllerTestBase { - - private static final String TEST_TRANSFER_PROCESS_ID = "test-transfer-process-id"; - private static final String TEST_TRANSFER_NEGOTIATION_ID = "test-cn-id"; - private static final String TEST_AGREEMENT_ID = "test-agreement-id"; - private static final String TEST_PROVIDER_ID = "test-provider-id"; - private static final String TEST_ASSET_ID = "test-asset-id"; - - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validator = mock(); - private final EndpointDataReferenceStore edrStore = mock(); - - @Test - void requestEdrEntries() { - when(edrStore.query(any())) - .thenReturn(StoreResult.success(List.of(createEdrEntry()))); - when(transformerRegistry.transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class))) - .thenReturn(Result.success(createEdrEntryJson().build())); - when(transformerRegistry.transform(isA(JsonObject.class), eq(QuerySpec.class))) - .thenReturn(Result.success(QuerySpec.Builder.newInstance().offset(10).build())); - when(validator.validate(any(), any())).thenReturn(ValidationResult.success()); - - baseRequest() - .contentType(JSON) - .body("{}") - .post("/edrs/request") - .then() - .log().ifError() - .statusCode(200) - .contentType(JSON) - .body("size()", is(1)); - - verify(edrStore).query(argThat(s -> s.getOffset() == 10)); - verify(transformerRegistry).transform(isA(EndpointDataReferenceEntry.class), eq(JsonObject.class)); - verify(transformerRegistry).transform(isA(JsonObject.class), eq(QuerySpec.class)); - } - - - @Test - void getEdrEntryDataAddress() { +public class EdrCacheApiControllerTest extends BaseEdrCacheApiControllerTest { - var dataAddressType = "type"; - var dataAddress = DataAddress.Builder.newInstance().type(dataAddressType).build(); - when(edrStore.resolveByTransferProcess("transferProcessId")) - .thenReturn(StoreResult.success(dataAddress)); - - when(transformerRegistry.transform(isA(DataAddress.class), eq(JsonObject.class))) - .thenReturn(Result.success(createDataAddress(dataAddressType).build())); - - baseRequest() - .contentType(JSON) - .get("/edrs/transferProcessId/dataaddress") - .then() - .log().ifError() - .statusCode(200) - .contentType(JSON) - .body("'%s'".formatted(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY), equalTo(dataAddressType)); - - verify(edrStore).resolveByTransferProcess("transferProcessId"); - verify(transformerRegistry).transform(isA(DataAddress.class), eq(JsonObject.class)); - verifyNoMoreInteractions(transformerRegistry); - } - - @Test - void getEdrEntryDataAddress_whenNotFound() { - - when(edrStore.resolveByTransferProcess("transferProcessId")) - .thenReturn(StoreResult.notFound("notFound")); - - - baseRequest() - .contentType(JSON) - .get("/edrs/transferProcessId/dataaddress") - .then() - .log().ifError() - .statusCode(404) - .contentType(JSON); - - verify(edrStore).resolveByTransferProcess("transferProcessId"); - verifyNoMoreInteractions(transformerRegistry); - } - - @Test - void removeEdrEntry() { - when(edrStore.delete("transferProcessId")) - .thenReturn(StoreResult.success(createEdrEntry())); - - baseRequest() - .contentType(JSON) - .delete("/edrs/transferProcessId") - .then() - .statusCode(204); - verify(edrStore).delete("transferProcessId"); - } - - @Test - void removeEdrEntry_whenNotFound() { - when(edrStore.delete("transferProcessId")) - .thenReturn(StoreResult.notFound("not found")); - - baseRequest() - .contentType(JSON) - .delete("/edrs/transferProcessId") - .then() - .statusCode(404); - - verify(edrStore).delete("transferProcessId"); - } @Override protected Object controller() { - return new EdrCacheApiController(edrStore, transformerRegistry, validator, mock()); - } - - private JsonObjectBuilder createEdrEntryJson() { - return createObjectBuilder() - .add(CONTEXT, createContextBuilder().build()) - .add(TYPE, EDR_ENTRY_TYPE) - .add(ID, TEST_TRANSFER_PROCESS_ID) - .add(EDR_ENTRY_TRANSFER_PROCESS_ID, TEST_TRANSFER_PROCESS_ID) - .add(EDR_ENTRY_PROVIDER_ID, TEST_PROVIDER_ID) - .add(EDR_ENTRY_CONTRACT_NEGOTIATION_ID, TEST_TRANSFER_NEGOTIATION_ID) - .add(EDR_ENTRY_ASSET_ID, TEST_ASSET_ID) - .add(EDR_ENTRY_AGREEMENT_ID, TEST_AGREEMENT_ID); - } - - private JsonObjectBuilder createDataAddress(String type) { - return createObjectBuilder() - .add(CONTEXT, createContextBuilder().build()) - .add(TYPE, DataAddress.EDC_DATA_ADDRESS_TYPE) - .add(DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY, type); - } - - private EndpointDataReferenceEntry createEdrEntry() { - return EndpointDataReferenceEntry.Builder.newInstance() - .agreementId(TEST_AGREEMENT_ID) - .assetId(TEST_ASSET_ID) - .providerId(TEST_PROVIDER_ID) - .transferProcessId(TEST_TRANSFER_PROCESS_ID) - .contractNegotiationId(TEST_TRANSFER_NEGOTIATION_ID) - .build(); - - } - - private JsonObjectBuilder createContextBuilder() { - return createObjectBuilder() - .add(VOCAB, EDC_NAMESPACE) - .add(EDC_PREFIX, EDC_NAMESPACE); + return new EdrCacheApiV1Controller(edrStore, transformerRegistry, validator, mock()); } - private RequestSpecification baseRequest() { + protected RequestSpecification baseRequest() { return given() .baseUri("http://localhost:" + port + "/v1") .when(); diff --git a/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiControllerTest.java b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiControllerTest.java new file mode 100644 index 00000000000..a6bc71cbf4d --- /dev/null +++ b/extensions/control-plane/api/management-api/edr-cache-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/edr/v3/EdrCacheApiControllerTest.java @@ -0,0 +1,39 @@ +/* + * 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.controlplane.api.management.edr.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.edr.BaseEdrCacheApiControllerTest; +import org.eclipse.edc.junit.annotations.ApiTest; + +import static io.restassured.RestAssured.given; +import static org.mockito.Mockito.mock; + +@ApiTest +public class EdrCacheApiControllerTest extends BaseEdrCacheApiControllerTest { + + + @Override + protected Object controller() { + return new EdrCacheApiV3Controller(edrStore, transformerRegistry, validator, mock()); + } + + + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v3") + .when(); + } +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiController.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java similarity index 92% rename from extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiController.java rename to extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java index 09dd411af7f..5b44cccab1f 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiController.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java @@ -46,16 +46,15 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) -@Path("/v2/policydefinitions") -public class PolicyDefinitionApiController implements PolicyDefinitionApi { +public abstract class BasePolicyDefinitionApiController { - private final Monitor monitor; + protected final Monitor monitor; private final TypeTransformerRegistry transformerRegistry; private final PolicyDefinitionService service; private final JsonObjectValidatorRegistry validatorRegistry; - public PolicyDefinitionApiController(Monitor monitor, TypeTransformerRegistry transformerRegistry, - PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) { + public BasePolicyDefinitionApiController(Monitor monitor, TypeTransformerRegistry transformerRegistry, + PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) { this.monitor = monitor; this.transformerRegistry = transformerRegistry; this.service = service; @@ -64,7 +63,6 @@ public PolicyDefinitionApiController(Monitor monitor, TypeTransformerRegistry tr @POST @Path("request") - @Override public JsonArray queryPolicyDefinitions(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -85,7 +83,6 @@ public JsonArray queryPolicyDefinitions(JsonObject querySpecJson) { @GET @Path("{id}") - @Override public JsonObject getPolicyDefinition(@PathParam("id") String id) { var definition = service.findById(id); if (definition == null) { @@ -97,7 +94,6 @@ public JsonObject getPolicyDefinition(@PathParam("id") String id) { } @POST - @Override public JsonObject createPolicyDefinition(JsonObject request) { validatorRegistry.validate(EDC_POLICY_DEFINITION_TYPE, request).orElseThrow(ValidationFailureException::new); @@ -119,7 +115,6 @@ public JsonObject createPolicyDefinition(JsonObject request) { @DELETE @Path("{id}") - @Override public void deletePolicyDefinition(@PathParam("id") String id) { service.deleteById(id) .onSuccess(d -> monitor.debug(format("Policy Definition deleted %s", d.getId()))) @@ -128,7 +123,6 @@ public void deletePolicyDefinition(@PathParam("id") String id) { @PUT @Path("{id}") - @Override public void updatePolicyDefinition(@PathParam("id") String id, JsonObject input) { validatorRegistry.validate(EDC_POLICY_DEFINITION_TYPE, input).orElseThrow(ValidationFailureException::new); @@ -139,5 +133,4 @@ public void updatePolicyDefinition(@PathParam("id") String id, JsonObject input) .onSuccess(d -> monitor.debug(format("Policy Definition updated %s", d.getId()))) .orElseThrow(exceptionMapper(PolicyDefinition.class, id)); } - } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java index 376e91fe6fb..a1ae66355ac 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java @@ -18,6 +18,8 @@ import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyDefinitionTransformer; import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectToPolicyDefinitionTransformer; +import org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.policy.v3.PolicyDefinitionApiV3Controller; import org.eclipse.edc.connector.controlplane.api.management.policy.validation.PolicyDefinitionValidator; import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -74,6 +76,7 @@ public void initialize(ServiceExtensionContext context) { validatorRegistry.register(EDC_POLICY_DEFINITION_TYPE, PolicyDefinitionValidator.instance()); var monitor = context.getMonitor(); - webService.registerResource(configuration.getContextAlias(), new PolicyDefinitionApiController(monitor, managementApiTransformerRegistry, service, validatorRegistry)); + webService.registerResource(configuration.getContextAlias(), new PolicyDefinitionApiV2Controller(monitor, managementApiTransformerRegistry, service, validatorRegistry)); + webService.registerResource(configuration.getContextAlias(), new PolicyDefinitionApiV3Controller(monitor, managementApiTransformerRegistry, service, validatorRegistry)); } } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2.java new file mode 100644 index 00000000000..d06cd64e31f --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2.java @@ -0,0 +1,185 @@ +/* + * 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.controlplane.api.management.policy.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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.parameters.RequestBody; +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 static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_TYPE; +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; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Policy Definition V2") +public interface PolicyDefinitionApiV2 { + + @Operation(description = "Returns all policy definitions according to a query", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryPolicyDefinitionsV2", + responses = { + @ApiResponse(responseCode = "200", description = "The policy definitions matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = PolicyDefinitionOutputSchema.class)))), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonArray queryPolicyDefinitions(JsonObject querySpecJson); + + @Operation(description = "Gets a policy definition with the given ID", + operationId = "getPolicyDefinitionV2", + responses = { + @ApiResponse(responseCode = "200", description = "The policy definition", + content = @Content(schema = @Schema(implementation = PolicyDefinitionOutputSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An policy definition with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject getPolicyDefinition(String id); + + @Operation(description = "Creates a new policy definition", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = PolicyDefinitionInputSchema.class))), + operationId = "createPolicyDefinitionV2", + responses = { + @ApiResponse(responseCode = "200", description = "policy definition was created successfully. Returns the Policy Definition Id and created timestamp", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "Could not create policy definition, because a contract definition with that ID already exists", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + JsonObject createPolicyDefinition(JsonObject policyDefinition); + + @Operation(description = "Removes a policy definition with the given ID if possible. Deleting a policy definition is " + + "only possible if that policy definition is not yet referenced by a contract definition, in which case an error is returned. " + + "DANGER ZONE: Note that deleting policy definitions can have unexpected results, do this at your own risk!", + operationId = "deletePolicyDefinitionV2", + responses = { + @ApiResponse(responseCode = "204", description = "Policy definition was deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An policy definition with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "The policy definition cannot be deleted, because it is referenced by a contract definition", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void deletePolicyDefinition(String id); + + @Operation(description = "Updates an existing Policy, If the Policy is not found, an error is reported", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = PolicyDefinitionInputSchema.class))), + operationId = "updatePolicyDefinitionV2", + responses = { + @ApiResponse(responseCode = "204", description = "policy definition was updated successfully."), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "policy definition could not be updated, because it does not exists", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))) + }, + deprecated = true + ) + @Deprecated(since = "0.7.0") + void updatePolicyDefinition(String id, JsonObject policyDefinition); + + @Schema(name = "PolicyDefinitionInput", example = PolicyDefinitionInputSchema.POLICY_DEFINITION_INPUT_EXAMPLE) + record PolicyDefinitionInputSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_POLICY_DEFINITION_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + ManagementApiSchema.PolicySchema policy) { + + // policy example took from https://w3c.github.io/odrl/bp/ + public static final String POLICY_DEFINITION_INPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "Set", + "uid": "http://example.com/policy:1010", + "permission": [{ + "target": "http://example.com/asset:9898.movie", + "action": "display", + "constraint": [{ + "leftOperand": "spatial", + "operator": "eq", + "rightOperand": "https://www.wikidata.org/wiki/Q183", + "comment": "i.e Germany" + }] + }] + } + } + """; + } + + @Schema(name = "PolicyDefinitionOutput", example = PolicyDefinitionOutputSchema.POLICY_DEFINITION_OUTPUT_EXAMPLE) + record PolicyDefinitionOutputSchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_POLICY_DEFINITION_TYPE) + String type, + ManagementApiSchema.PolicySchema policy) { + + // policy example took from https://w3c.github.io/odrl/bp/ + public static final String POLICY_DEFINITION_OUTPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "policy": { + "@context": "http://www.w3.org/ns/odrl.jsonld", + "@type": "Set", + "uid": "http://example.com/policy:1010", + "permission": [{ + "target": "http://example.com/asset:9898.movie", + "action": "display", + "constraint": [{ + "leftOperand": "spatial", + "operator": "eq", + "rightOperand": "https://www.wikidata.org/wiki/Q183", + "comment": "i.e Germany" + }] + }] + }, + "createdAt": 1688465655 + } + """; + } + +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2Controller.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2Controller.java new file mode 100644 index 00000000000..06db15af3c4 --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2Controller.java @@ -0,0 +1,62 @@ +/* + * 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.controlplane.api.management.policy.v2; + +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiController; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v2/policydefinitions") +public class PolicyDefinitionApiV2Controller extends BasePolicyDefinitionApiController implements PolicyDefinitionApiV2 { + public PolicyDefinitionApiV2Controller(Monitor monitor, TypeTransformerRegistry transformerRegistry, PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) { + super(monitor, transformerRegistry, service, validatorRegistry); + } + + @Override + public JsonArray queryPolicyDefinitions(JsonObject querySpecJson) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.queryPolicyDefinitions(querySpecJson); + } + + @Override + public JsonObject getPolicyDefinition(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.getPolicyDefinition(id); + } + + @Override + public JsonObject createPolicyDefinition(JsonObject request) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + return super.createPolicyDefinition(request); + } + + @Override + public void deletePolicyDefinition(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + super.deletePolicyDefinition(id); + } + + @Override + public void updatePolicyDefinition(String id, JsonObject input) { + monitor.warning(ApiWarnings.deprecationWarning("/v2", "/v3")); + super.updatePolicyDefinition(id, input); + } +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApi.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3.java similarity index 94% rename from extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApi.java rename to extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3.java index c043ace7b87..4ee9d9fa09c 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApi.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -12,10 +12,11 @@ * */ -package org.eclipse.edc.connector.controlplane.api.management.policy; +package org.eclipse.edc.connector.controlplane.api.management.policy.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -33,21 +34,23 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -@OpenAPIDefinition -@Tag(name = "Policy Definition") -public interface PolicyDefinitionApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Policy Definition V3") +public interface PolicyDefinitionApiV3 { @Operation(description = "Returns all policy definitions according to a query", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryPolicyDefinitionsV3", responses = { @ApiResponse(responseCode = "200", description = "The policy definitions matching the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = PolicyDefinitionOutputSchema.class)))), @ApiResponse(responseCode = "400", description = "Request was malformed", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } ) JsonArray queryPolicyDefinitions(JsonObject querySpecJson); @Operation(description = "Gets a policy definition with the given ID", + operationId = "getPolicyDefinitionV3", responses = { @ApiResponse(responseCode = "200", description = "The policy definition", content = @Content(schema = @Schema(implementation = PolicyDefinitionOutputSchema.class))), @@ -61,19 +64,21 @@ public interface PolicyDefinitionApi { @Operation(description = "Creates a new policy definition", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = PolicyDefinitionInputSchema.class))), + operationId = "createPolicyDefinitionV3", responses = { @ApiResponse(responseCode = "200", description = "policy definition was created successfully. Returns the Policy Definition Id and created timestamp", content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), @ApiResponse(responseCode = "400", description = "Request body was malformed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), @ApiResponse(responseCode = "409", description = "Could not create policy definition, because a contract definition with that ID already exists", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } ) JsonObject createPolicyDefinition(JsonObject policyDefinition); @Operation(description = "Removes a policy definition with the given ID if possible. Deleting a policy definition is " + "only possible if that policy definition is not yet referenced by a contract definition, in which case an error is returned. " + "DANGER ZONE: Note that deleting policy definitions can have unexpected results, do this at your own risk!", + operationId = "deletePolicyDefinitionV3", responses = { @ApiResponse(responseCode = "204", description = "Policy definition was deleted successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", @@ -88,6 +93,7 @@ public interface PolicyDefinitionApi { @Operation(description = "Updates an existing Policy, If the Policy is not found, an error is reported", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = PolicyDefinitionInputSchema.class))), + operationId = "updatePolicyDefinitionV3", responses = { @ApiResponse(responseCode = "204", description = "policy definition was updated successfully."), @ApiResponse(responseCode = "400", description = "Request body was malformed", diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3Controller.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3Controller.java new file mode 100644 index 00000000000..949525deadc --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3Controller.java @@ -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.controlplane.api.management.policy.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiController; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/policydefinitions") +public class PolicyDefinitionApiV3Controller extends BasePolicyDefinitionApiController { + public PolicyDefinitionApiV3Controller(Monitor monitor, TypeTransformerRegistry transformerRegistry, PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) { + super(monitor, transformerRegistry, service, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiControllerTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiControllerTest.java similarity index 87% rename from extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiControllerTest.java rename to extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiControllerTest.java index e913784c67a..8898ee748a8 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiControllerTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiControllerTest.java @@ -14,6 +14,7 @@ package org.eclipse.edc.connector.controlplane.api.management.policy; +import io.restassured.specification.RequestSpecification; import jakarta.json.Json; import jakarta.json.JsonObject; import org.eclipse.edc.api.model.IdResponse; @@ -33,7 +34,6 @@ import java.util.List; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_TYPE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; @@ -51,11 +51,11 @@ import static org.mockito.Mockito.when; @ApiTest -class PolicyDefinitionApiControllerTest extends RestControllerTestBase { +public abstract class BasePolicyDefinitionApiControllerTest extends RestControllerTestBase { - private final TypeTransformerRegistry transformerRegistry = mock(); - private final PolicyDefinitionService service = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final PolicyDefinitionService service = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); @Test void create_shouldReturnDefinitionId() { @@ -77,11 +77,10 @@ void create_shouldReturnDefinitionId() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post() .then() .statusCode(200) .contentType(JSON) @@ -103,11 +102,10 @@ void create_shouldReturnBadRequest_whenValidationFails() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post() .then() .statusCode(400) .contentType(JSON); @@ -126,11 +124,10 @@ void create_shouldReturnBadRequest_whenTransformationFails() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post() .then() .statusCode(400) .contentType(JSON); @@ -150,11 +147,10 @@ void create_shouldReturnConflict_whenItAlreadyExists() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post() .then() .statusCode(409) .contentType(JSON); @@ -165,9 +161,8 @@ void delete_shouldCallService() { var policyDefinition = createPolicyDefinition().build(); when(service.deleteById(any())).thenReturn(ServiceResult.success(policyDefinition)); - given() - .port(port) - .delete("/v2/policydefinitions/id") + baseRequest() + .delete("/id") .then() .statusCode(204); @@ -178,9 +173,8 @@ void delete_shouldCallService() { void delete_shouldReturnNotFound_whenNotFound() { when(service.deleteById(any())).thenReturn(ServiceResult.notFound("not found")); - given() - .port(port) - .delete("/v2/policydefinitions/id") + baseRequest() + .delete("/id") .then() .statusCode(404); } @@ -198,11 +192,10 @@ void update_shouldCallService() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .put("/v2/policydefinitions/id") + .put("/id") .then() .statusCode(204); verify(validatorRegistry).validate(eq(EDC_POLICY_DEFINITION_TYPE), any()); @@ -220,11 +213,10 @@ void update_shouldReturnBadRequest_whenValidationFails() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .put("/v2/policydefinitions/id") + .put("/id") .then() .statusCode(400); verifyNoInteractions(transformerRegistry, service); @@ -241,11 +233,10 @@ void update_shouldReturnBadRequest_whenTransformationFails() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .put("/v2/policydefinitions/id") + .put("/id") .then() .statusCode(400); verifyNoInteractions(service); @@ -264,11 +255,10 @@ void update_shouldReturnNotFound_whenNotFound() { .build()) .build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .put("/v2/policydefinitions/id") + .put("/id") .then() .statusCode(404); } @@ -280,9 +270,8 @@ void get_shouldReturnPolicyDefinition() { when(service.findById(any())).thenReturn(policyDefinition); when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(expandedBody)); - given() - .port(port) - .get("/v2/policydefinitions/id") + baseRequest() + .get("/id") .then() .statusCode(200) .contentType(JSON) @@ -296,9 +285,8 @@ void get_shouldReturnPolicyDefinition() { void get_shouldReturnNotFound_whenNotFound() { when(service.findById(any())).thenReturn(null); - given() - .port(port) - .get("/v2/policydefinitions/id") + baseRequest() + .get("/id") .then() .statusCode(404) .contentType(JSON); @@ -310,9 +298,8 @@ void get_shouldReturnNotFound_whenTransformFails() { when(service.findById(any())).thenReturn(createPolicyDefinition().build()); when(transformerRegistry.transform(any(), any())).thenReturn(Result.failure("error")); - given() - .port(port) - .get("/v2/policydefinitions/id") + baseRequest() + .get("/id") .then() .statusCode(404) .contentType(JSON); @@ -329,11 +316,10 @@ void search_shouldReturnQueriedPolicyDefinitions() { when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(expandedResponseBody)); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(200) .contentType(JSON) @@ -352,11 +338,10 @@ void search_shouldBadRequest_whenValidationFails() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("failure", "failure path"))); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(400) .contentType(JSON); @@ -373,11 +358,10 @@ void search_shouldReturn400_whenInvalidQuery() { when(transformerRegistry.transform(any(JsonObject.class), eq(QuerySpec.class))).thenReturn(Result.failure("failure")); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(400); @@ -392,11 +376,10 @@ void search_shouldReturnBadRequest_whenQuerySpecTransformFails() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(QuerySpec.class))).thenReturn(Result.failure("error")); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(400) .contentType(JSON); @@ -411,11 +394,10 @@ void search_shouldReturnBadRequest_whenServiceReturnsBadRequest() { when(service.search(any())).thenReturn(ServiceResult.badRequest("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(400) .contentType(JSON); @@ -431,21 +413,17 @@ void search_shouldFilterOutResults_whenTransformFails() { when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.failure("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/request") .then() .statusCode(200) .contentType(JSON) .body("size()", is(0)); } - @Override - protected Object controller() { - return new PolicyDefinitionApiController(monitor, transformerRegistry, service, validatorRegistry); - } + protected abstract RequestSpecification baseRequest(); @NotNull private PolicyDefinition.Builder createPolicyDefinition() { diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java index 9254700e7f2..94a393201de 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java @@ -15,10 +15,13 @@ package org.eclipse.edc.connector.controlplane.api.management.policy; import org.eclipse.edc.boot.system.injection.ObjectFactory; +import org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.policy.v3.PolicyDefinitionApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -26,6 +29,7 @@ import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_TYPE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +38,7 @@ class PolicyDefinitionApiExtensionTest { private final JsonObjectValidatorRegistry validatorRegistry = mock(); + private final WebService webService = mock(); private PolicyDefinitionApiExtension extension; @BeforeEach @@ -42,6 +47,7 @@ void setUp(ServiceExtensionContext context, ObjectFactory factory) { when(typeTransformerRegistry.forContext(any())).thenReturn(mock()); context.registerService(TypeTransformerRegistry.class, typeTransformerRegistry); context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); extension = factory.constructInstance(PolicyDefinitionApiExtension.class); } @@ -51,4 +57,12 @@ void initialize_shouldRegisterValidatorForPolicyDefinition(ServiceExtensionConte verify(validatorRegistry).register(eq(EDC_POLICY_DEFINITION_TYPE), any()); } + + @Test + void initialize_shouldRegisterControllers(ServiceExtensionContext context) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(PolicyDefinitionApiV2Controller.class)); + verify(webService).registerResource(any(), isA(PolicyDefinitionApiV3Controller.class)); + } } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiTest.java index a823d9337bf..26ba7f72782 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiTest.java @@ -30,8 +30,8 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.connector.controlplane.api.management.policy.PolicyDefinitionApi.PolicyDefinitionInputSchema.POLICY_DEFINITION_INPUT_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.policy.PolicyDefinitionApi.PolicyDefinitionOutputSchema.POLICY_DEFINITION_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2.PolicyDefinitionInputSchema.POLICY_DEFINITION_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2.PolicyDefinitionOutputSchema.POLICY_DEFINITION_OUTPUT_EXAMPLE; import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_POLICY; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2ControllerTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2ControllerTest.java new file mode 100644 index 00000000000..5dfa77a4f0b --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v2/PolicyDefinitionApiV2ControllerTest.java @@ -0,0 +1,36 @@ +/* + * 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.controlplane.api.management.policy.v2; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiControllerTest; + +import static io.restassured.RestAssured.given; + +public class PolicyDefinitionApiV2ControllerTest extends BasePolicyDefinitionApiControllerTest { + + @Override + protected Object controller() { + return new PolicyDefinitionApiV2Controller(monitor, transformerRegistry, service, validatorRegistry); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:%d/v2/policydefinitions".formatted(port)) + .port(port); + } + +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3ControllerTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3ControllerTest.java new file mode 100644 index 00000000000..5bec59e4e56 --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v3/PolicyDefinitionApiV3ControllerTest.java @@ -0,0 +1,36 @@ +/* + * 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.controlplane.api.management.policy.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiControllerTest; + +import static io.restassured.RestAssured.given; + +public class PolicyDefinitionApiV3ControllerTest extends BasePolicyDefinitionApiControllerTest { + + @Override + protected Object controller() { + return new PolicyDefinitionApiV3Controller(monitor, transformerRegistry, service, validatorRegistry); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:%d/v3/policydefinitions".formatted(port)) + .port(port); + } + +} diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiController.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiController.java similarity index 92% rename from extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiController.java rename to extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiController.java index ce7a2a32307..72fb1330e3f 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiController.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiController.java @@ -40,20 +40,18 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) -@Path("/v1/secrets") -public class SecretsApiController implements SecretsApi { +public abstract class BaseSecretsApiController { private final TypeTransformerRegistry transformerRegistry; private final SecretService service; private final JsonObjectValidatorRegistry validator; - public SecretsApiController(SecretService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator) { + public BaseSecretsApiController(SecretService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator) { this.transformerRegistry = transformerRegistry; this.service = service; this.validator = validator; } @POST - @Override public JsonObject createSecret(JsonObject secretJson) { validator.validate(EDC_SECRET_TYPE, secretJson).orElseThrow(ValidationFailureException::new); @@ -73,7 +71,6 @@ public JsonObject createSecret(JsonObject secretJson) { @GET @Path("{id}") - @Override public JsonObject getSecret(@PathParam("id") String id) { var secret = of(id) .map(it -> service.findById(id)) @@ -85,13 +82,11 @@ public JsonObject getSecret(@PathParam("id") String id) { @DELETE @Path("{id}") - @Override public void removeSecret(@PathParam("id") String id) { service.delete(id).orElseThrow(exceptionMapper(Secret.class, id)); } @PUT - @Override public void updateSecret(JsonObject secretJson) { validator.validate(EDC_SECRET_TYPE, secretJson).orElseThrow(ValidationFailureException::new); diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtension.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtension.java index 18bf3b94835..62d30f45bc5 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtension.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtension.java @@ -18,6 +18,8 @@ import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.api.management.secret.transform.JsonObjectFromSecretTransformer; import org.eclipse.edc.connector.api.management.secret.transform.JsonObjectToSecretTransformer; +import org.eclipse.edc.connector.api.management.secret.v1.SecretsApiV1Controller; +import org.eclipse.edc.connector.api.management.secret.v3.SecretsApiV3Controller; import org.eclipse.edc.connector.api.management.secret.validation.SecretsValidator; import org.eclipse.edc.connector.spi.service.SecretService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -67,7 +69,8 @@ public void initialize(ServiceExtensionContext context) { managementApiTransformerRegistry.register(new JsonObjectFromSecretTransformer(jsonBuilderFactory)); managementApiTransformerRegistry.register(new JsonObjectToSecretTransformer()); - webService.registerResource(config.getContextAlias(), new SecretsApiController(secretService, managementApiTransformerRegistry, validator)); + webService.registerResource(config.getContextAlias(), new SecretsApiV1Controller(secretService, managementApiTransformerRegistry, validator, context.getMonitor())); + webService.registerResource(config.getContextAlias(), new SecretsApiV3Controller(secretService, managementApiTransformerRegistry, validator)); } } diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1.java new file mode 100644 index 00000000000..2a64ee03ad3 --- /dev/null +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1.java @@ -0,0 +1,132 @@ +/* + * 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.api.management.secret.v1; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +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.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.json.JsonObject; +import org.eclipse.edc.api.model.ApiCoreSchema; + +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.secret.Secret.EDC_SECRET_TYPE; + +@OpenAPIDefinition( + info = @Info(description = "This contains the secret management API, which allows to add, remove and update secrets in the Vault.", title = "Secret API", version = "v1")) +@Tag(name = "Secret V1") +public interface SecretsApiV1 { + + @Operation(description = "Creates a new secret.", + deprecated = true, operationId = "createSecretV1", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SecretInputSchema.class))), + responses = { + @ApiResponse(responseCode = "200", description = "Secret was created successfully. Returns the secret Id and created timestamp", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "Could not create secret, because a secret with that ID already exists", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } + ) + @Deprecated(since = "0.7.0") + JsonObject createSecret(JsonObject secret); + + @Operation(description = "Gets a secret with the given ID", + deprecated = true, operationId = "getSecretV1", + responses = { + @ApiResponse(responseCode = "200", description = "The secret", + content = @Content(schema = @Schema(implementation = SecretOutputSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A secret with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + } + ) + @Deprecated(since = "0.7.0") + JsonObject getSecret(String id); + + @Operation(description = "Removes a secret with the given ID if possible.", + deprecated = true, operationId = "removeSecretV1", + responses = { + @ApiResponse(responseCode = "204", description = "Secret was deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A secret with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + @Deprecated(since = "0.7.0") + void removeSecret(String id); + + @Operation(description = "Updates a secret with the given ID if it exists. If the secret is not found, no further action is taken. ", + deprecated = true, operationId = "updateSecretV1", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SecretInputSchema.class))), + responses = { + @ApiResponse(responseCode = "204", description = "Secret was updated successfully"), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "Secret could not be updated, because it does not exist.") + }) + @Deprecated(since = "0.7.0") + void updateSecret(JsonObject secret); + + @Schema(name = "SecretInput", example = SecretInputSchema.SECRET_INPUT_EXAMPLE) + record SecretInputSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_SECRET_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String value + ) { + public static final String SECRET_INPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "secret-id", + "value" : "secret-value" + } + """; + } + + @ArraySchema() + @Schema(name = "SecretOutput", example = SecretOutputSchema.SECRET_OUTPUT_EXAMPLE) + record SecretOutputSchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_SECRET_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String value + ) { + public static final String SECRET_OUTPUT_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "secret-id", + "@type": "https://w3id.org/edc/v0.0.1/ns/Secret", + "value": "secret-value" + } + """; + } + +} diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1Controller.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1Controller.java new file mode 100644 index 00000000000..98770724d57 --- /dev/null +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1Controller.java @@ -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.api.management.secret.v1; + +import jakarta.json.JsonObject; +import jakarta.ws.rs.Path; +import org.eclipse.edc.api.ApiWarnings; +import org.eclipse.edc.connector.api.management.secret.BaseSecretsApiController; +import org.eclipse.edc.connector.spi.service.SecretService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v1/secrets") +public class SecretsApiV1Controller extends BaseSecretsApiController { + private final Monitor monitor; + + public SecretsApiV1Controller(SecretService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator, Monitor monitor) { + super(service, transformerRegistry, validator); + this.monitor = monitor; + } + + @Override + public JsonObject createSecret(JsonObject secretJson) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + return super.createSecret(secretJson); + } + + @Override + public JsonObject getSecret(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + return super.getSecret(id); + } + + @Override + public void removeSecret(String id) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + super.removeSecret(id); + } + + @Override + public void updateSecret(JsonObject secretJson) { + monitor.warning(ApiWarnings.deprecationWarning("/v1", "/v3")); + super.updateSecret(secretJson); + } +} diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApi.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3.java similarity index 92% rename from extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApi.java rename to extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3.java index 58d04dbd73f..d2770ccbb3d 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/SecretsApi.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Amadeus + * 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 @@ -8,11 +8,11 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Amadeus - Initial API and Implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.edc.connector.api.management.secret; +package org.eclipse.edc.connector.api.management.secret.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; @@ -33,12 +33,12 @@ import static org.eclipse.edc.spi.types.domain.secret.Secret.EDC_SECRET_TYPE; @OpenAPIDefinition( - info = @Info(description = "This contains the secret management API, which allows to add, remove and update secrets in the Vault.", title = "Secret API")) -@Tag(name = "Secret") -public interface SecretsApi { + info = @Info(description = "This contains the secret management API, which allows to add, remove and update secrets in the Vault.", title = "Secret API", version = "v3")) +@Tag(name = "Secret V3") +public interface SecretsApiV3 { @Operation(description = "Creates a new secret.", - operationId = "createSecret", + operationId = "createSecretV3", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SecretInputSchema.class))), responses = { @ApiResponse(responseCode = "200", description = "Secret was created successfully. Returns the secret Id and created timestamp", @@ -46,12 +46,12 @@ public interface SecretsApi { @ApiResponse(responseCode = "400", description = "Request body was malformed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), @ApiResponse(responseCode = "409", description = "Could not create secret, because a secret with that ID already exists", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class))))} + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } ) JsonObject createSecret(JsonObject secret); @Operation(description = "Gets a secret with the given ID", - operationId = "getSecret", + operationId = "getSecretV3", responses = { @ApiResponse(responseCode = "200", description = "The secret", content = @Content(schema = @Schema(implementation = SecretOutputSchema.class))), @@ -64,7 +64,7 @@ public interface SecretsApi { JsonObject getSecret(String id); @Operation(description = "Removes a secret with the given ID if possible.", - operationId = "removeSecret", + operationId = "removeSecretV3", responses = { @ApiResponse(responseCode = "204", description = "Secret was deleted successfully"), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", @@ -75,7 +75,7 @@ public interface SecretsApi { void removeSecret(String id); @Operation(description = "Updates a secret with the given ID if it exists. If the secret is not found, no further action is taken. ", - operationId = "updateSecret", + operationId = "updateSecretV3", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SecretInputSchema.class))), responses = { @ApiResponse(responseCode = "204", description = "Secret was updated successfully"), diff --git a/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3Controller.java b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3Controller.java new file mode 100644 index 00000000000..55d5bfbc3c7 --- /dev/null +++ b/extensions/control-plane/api/management-api/secrets-api/src/main/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3Controller.java @@ -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.api.management.secret.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.api.management.secret.BaseSecretsApiController; +import org.eclipse.edc.connector.spi.service.SecretService; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/secrets") +public class SecretsApiV3Controller extends BaseSecretsApiController { + + public SecretsApiV3Controller(SecretService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validator) { + super(service, transformerRegistry, validator); + } +} diff --git a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiControllerTest.java b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiControllerTest.java similarity index 94% rename from extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiControllerTest.java rename to extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiControllerTest.java index bf5ee11d003..cd4562d01c6 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiControllerTest.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/BaseSecretsApiControllerTest.java @@ -30,7 +30,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static jakarta.json.Json.createObjectBuilder; import static org.eclipse.edc.api.model.IdResponse.ID_RESPONSE_CREATED_AT; @@ -56,13 +55,13 @@ import static org.mockito.Mockito.when; @ApiTest -class SecretsApiControllerTest extends RestControllerTestBase { +public abstract class BaseSecretsApiControllerTest extends RestControllerTestBase { private static final String TEST_SECRET_ID = "test-secret-id"; private static final String TEST_SECRET_VALUE = "test-secret-value"; - private final SecretService service = mock(); - private final TypeTransformerRegistry transformerRegistry = mock(); - private final JsonObjectValidatorRegistry validator = mock(); + protected final SecretService service = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final JsonObjectValidatorRegistry validator = mock(); @BeforeEach void setup() { @@ -266,10 +265,7 @@ void updateSecret_shouldReturnBadRequest_whenValidationFails() { verifyNoInteractions(service, transformerRegistry); } - @Override - protected Object controller() { - return new SecretsApiController(service, transformerRegistry, validator); - } + protected abstract RequestSpecification baseRequest(); private JsonObjectBuilder createSecretJson() { return createObjectBuilder() @@ -286,11 +282,6 @@ private JsonObjectBuilder createContextBuilder() { .add(EDC_PREFIX, EDC_NAMESPACE); } - private RequestSpecification baseRequest() { - return given() - .baseUri("http://localhost:" + port + "/v1") - .when(); - } private Secret.Builder createSecretBuilder() { return Secret.Builder.newInstance() diff --git a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtensionTest.java b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtensionTest.java index fb652bd4063..8456280a293 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiExtensionTest.java @@ -16,6 +16,8 @@ import org.eclipse.edc.connector.api.management.secret.transform.JsonObjectFromSecretTransformer; import org.eclipse.edc.connector.api.management.secret.transform.JsonObjectToSecretTransformer; +import org.eclipse.edc.connector.api.management.secret.v1.SecretsApiV1Controller; +import org.eclipse.edc.connector.api.management.secret.v3.SecretsApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -53,7 +55,8 @@ void setUp(ServiceExtensionContext context) { void initialize_shouldRegisterControllers(SecretsApiExtension extension, ServiceExtensionContext context) { extension.initialize(context); - verify(webService).registerResource(any(), isA(SecretsApiController.class)); + verify(webService).registerResource(any(), isA(SecretsApiV1Controller.class)); + verify(webService).registerResource(any(), isA(SecretsApiV3Controller.class)); } @Test diff --git a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiTest.java b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiTest.java index 7750ace23f3..8c3a6cb3d15 100644 --- a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiTest.java +++ b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/SecretsApiTest.java @@ -30,8 +30,8 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.eclipse.edc.connector.api.management.secret.SecretsApi.SecretInputSchema.SECRET_INPUT_EXAMPLE; -import static org.eclipse.edc.connector.api.management.secret.SecretsApi.SecretOutputSchema.SECRET_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.api.management.secret.v1.SecretsApiV1.SecretInputSchema.SECRET_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.api.management.secret.v1.SecretsApiV1.SecretOutputSchema.SECRET_OUTPUT_EXAMPLE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; diff --git a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1ControllerTest.java b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1ControllerTest.java new file mode 100644 index 00000000000..73476969cf8 --- /dev/null +++ b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v1/SecretsApiV1ControllerTest.java @@ -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 + * + */ + +package org.eclipse.edc.connector.api.management.secret.v1; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.api.management.secret.BaseSecretsApiControllerTest; + +import static io.restassured.RestAssured.given; +import static org.mockito.Mockito.mock; + +class SecretsApiV1ControllerTest extends BaseSecretsApiControllerTest { + @Override + protected Object controller() { + return new SecretsApiV1Controller(service, transformerRegistry, validator, mock()); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v1") + .when(); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3ControllerTest.java b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3ControllerTest.java new file mode 100644 index 00000000000..69a69827e10 --- /dev/null +++ b/extensions/control-plane/api/management-api/secrets-api/src/test/java/org/eclipse/edc/connector/api/management/secret/v3/SecretsApiV3ControllerTest.java @@ -0,0 +1,34 @@ +/* + * 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.api.management.secret.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.api.management.secret.BaseSecretsApiControllerTest; + +import static io.restassured.RestAssured.given; + +class SecretsApiV3ControllerTest extends BaseSecretsApiControllerTest { + @Override + protected Object controller() { + return new SecretsApiV3Controller(service, transformerRegistry, validator); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .baseUri("http://localhost:" + port + "/v3") + .when(); + } +} \ No newline at end of file diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiController.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiController.java similarity index 95% rename from extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiController.java rename to extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiController.java index 14ad871008e..f9e65628574 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiController.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiController.java @@ -56,16 +56,15 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) -@Path("/v2/transferprocesses") -public class TransferProcessApiController implements TransferProcessApi { +public abstract class BaseTransferProcessApiController { private final Monitor monitor; private final TransferProcessService service; private final TypeTransformerRegistry transformerRegistry; private final JsonObjectValidatorRegistry validatorRegistry; - public TransferProcessApiController(Monitor monitor, TransferProcessService service, - TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry) { + public BaseTransferProcessApiController(Monitor monitor, TransferProcessService service, + TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry) { this.monitor = monitor; this.service = service; this.transformerRegistry = transformerRegistry; @@ -74,7 +73,6 @@ public TransferProcessApiController(Monitor monitor, TransferProcessService serv @POST @Path("request") - @Override public JsonArray queryTransferProcesses(JsonObject querySpecJson) { QuerySpec querySpec; if (querySpecJson == null) { @@ -96,7 +94,6 @@ public JsonArray queryTransferProcesses(JsonObject querySpecJson) { @GET @Path("{id}") - @Override public JsonObject getTransferProcess(@PathParam("id") String id) { var definition = service.findById(id); if (definition == null) { @@ -110,7 +107,6 @@ public JsonObject getTransferProcess(@PathParam("id") String id) { @GET @Path("/{id}/state") - @Override public JsonObject getTransferProcessState(@PathParam("id") String id) { return Optional.of(id) .map(service::getState) @@ -122,7 +118,6 @@ public JsonObject getTransferProcessState(@PathParam("id") String id) { } @POST - @Override public JsonObject initiateTransferProcess(JsonObject request) { validatorRegistry.validate(TRANSFER_REQUEST_TYPE, request).orElseThrow(ValidationFailureException::new); @@ -144,7 +139,6 @@ public JsonObject initiateTransferProcess(JsonObject request) { @POST @Path("/{id}/deprovision") - @Override public void deprovisionTransferProcess(@PathParam("id") String id) { service.deprovision(id) .onSuccess(tp -> monitor.debug(format("Deprovision requested for TransferProcess with ID %s", id))) @@ -153,7 +147,6 @@ public void deprovisionTransferProcess(@PathParam("id") String id) { @POST @Path("/{id}/terminate") - @Override public void terminateTransferProcess(@PathParam("id") String id, JsonObject requestBody) { validatorRegistry.validate(TERMINATE_TRANSFER_TYPE, requestBody).orElseThrow(ValidationFailureException::new); @@ -167,7 +160,6 @@ public void terminateTransferProcess(@PathParam("id") String id, JsonObject requ @POST @Path("/{id}/suspend") - @Override public void suspendTransferProcess(@PathParam("id") String id, JsonObject requestBody) { validatorRegistry.validate(SUSPEND_TRANSFER_TYPE, requestBody).orElseThrow(ValidationFailureException::new); @@ -181,7 +173,6 @@ public void suspendTransferProcess(@PathParam("id") String id, JsonObject reques @POST @Path("/{id}/resume") - @Override public void resumeTransferProcess(@PathParam("id") String id) { service.resume(new ResumeTransferCommand(id)) .onSuccess(tp -> monitor.debug(format("Resumption requested for TransferProcess with ID %s", id))) diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtension.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtension.java index fa6ffb574f1..4ad4af38e15 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtension.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtension.java @@ -21,6 +21,8 @@ import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToSuspendTransferTransformer; import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToTerminateTransferTransformer; import org.eclipse.edc.connector.controlplane.api.management.transferprocess.transform.JsonObjectToTransferRequestTransformer; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.v2.TransferProcessApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3Controller; import org.eclipse.edc.connector.controlplane.api.management.transferprocess.validation.TerminateTransferValidator; import org.eclipse.edc.connector.controlplane.api.management.transferprocess.validation.TransferRequestValidator; import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; @@ -76,7 +78,7 @@ public void initialize(ServiceExtensionContext context) { validatorRegistry.register(TRANSFER_REQUEST_TYPE, TransferRequestValidator.instance()); validatorRegistry.register(TERMINATE_TRANSFER_TYPE, TerminateTransferValidator.instance()); - var newController = new TransferProcessApiController(context.getMonitor(), service, managementApiTransformerRegistry, validatorRegistry); - webService.registerResource(configuration.getContextAlias(), newController); + webService.registerResource(configuration.getContextAlias(), new TransferProcessApiV2Controller(context.getMonitor(), service, managementApiTransformerRegistry, validatorRegistry)); + webService.registerResource(configuration.getContextAlias(), new TransferProcessApiV3Controller(context.getMonitor(), service, managementApiTransformerRegistry, validatorRegistry)); } } diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2.java new file mode 100644 index 00000000000..45d877f38bf --- /dev/null +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2.java @@ -0,0 +1,312 @@ +/* + * 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.controlplane.api.management.transferprocess.v2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.links.Link; +import io.swagger.v3.oas.annotations.links.LinkParameter; +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.parameters.RequestBody; +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.connector.controlplane.api.management.transferprocess.model.SuspendTransfer; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TerminateTransfer; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TransferState; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; + +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess.TRANSFER_PROCESS_TYPE; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_TYPE; +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; + +@OpenAPIDefinition(info = @Info(version = "v2")) +@Tag(name = "Transfer Process V2") +public interface TransferProcessApiV2 { + + 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."; + + @Operation(description = "Returns all transfer process according to a query", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryTransferProcessesV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "200", description = "The transfer processes matching the query", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = TransferProcessSchema.class)))), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) } + ) + @Deprecated(since = "0.7.0") + JsonArray queryTransferProcesses(JsonObject querySpecJson); + + @Operation(description = "Gets an transfer process with the given ID", + operationId = "getTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "200", description = "The transfer process", + content = @Content(schema = @Schema(implementation = TransferProcessSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + } + ) + @Deprecated(since = "0.7.0") + JsonObject getTransferProcess(String id); + + @Operation(description = "Gets the state of a transfer process with the given ID", + operationId = "getTransferProcessStateV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "200", description = "The transfer process's state", + content = @Content(schema = @Schema(implementation = TransferStateSchema.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "An transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + } + ) + @Deprecated(since = "0.7.0") + JsonObject getTransferProcessState(String id); + + @Operation(description = "Initiates a data transfer with the given parameters. " + ASYNC_WARNING, + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TransferRequestSchema.class))), + operationId = "initiateTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "200", description = "The transfer was successfully initiated. Returns the transfer process ID and created timestamp", + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class)), + links = @Link(name = "poll-state", operationId = "getTransferProcessState", parameters = { + @LinkParameter(name = "id", expression = "$response.body#/id") + }) + ), + @ApiResponse(responseCode = "400", description = "Request body was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + }) + @Deprecated(since = "0.7.0") + JsonObject initiateTransferProcess(JsonObject transferRequest); + + @Operation(description = "Requests the deprovisioning of resources associated with a transfer process. " + ASYNC_WARNING, + operationId = "deprovisionTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "204", description = "Request to deprovision the transfer process was successfully received", + links = @Link(name = "poll-state", operationId = "deprovisionTransferProcess")), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + @Deprecated(since = "0.7.0") + void deprovisionTransferProcess(String id); + + @Operation(description = "Requests the termination of a transfer process. " + ASYNC_WARNING, + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TerminateTransferSchema.class))), + operationId = "terminateTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "204", description = "Request to terminate the transfer process was successfully received", + links = @Link(name = "poll-state", operationId = "terminateTransferProcess")), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "Could not terminate transfer process, because it is already completed or terminated.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + @Deprecated(since = "0.7.0") + void terminateTransferProcess(String id, JsonObject terminateTransfer); + + @Operation(description = "Requests the suspension of a transfer process. " + ASYNC_WARNING, + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SuspendTransferSchema.class))), + operationId = "suspendTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "204", description = "Request to suspend the transfer process was successfully received", + links = @Link(name = "poll-state", operationId = "suspendTransferProcess")), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "409", description = "Could not suspend the transfer process, because it is already completed or terminated.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + @Deprecated(since = "0.7.0") + void suspendTransferProcess(String id, JsonObject suspendTransfer); + + @Operation(description = "Requests the resumption of a suspended transfer process. " + ASYNC_WARNING, + operationId = "resumeTransferProcessV2", deprecated = true, + responses = { + @ApiResponse(responseCode = "204", description = "Request to resume the transfer process was successfully received", + links = @Link(name = "poll-state", operationId = "resumeTransferProcess")), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "404", description = "A transfer process with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiCoreSchema.ApiErrorDetailSchema.class)))) + }) + @Deprecated(since = "0.7.0") + void resumeTransferProcess(String id); + + @Schema(name = "TransferRequest", example = TransferRequestSchema.TRANSFER_REQUEST_EXAMPLE) + record TransferRequestSchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = TYPE, example = TRANSFER_REQUEST_TYPE) + String type, + @Schema(requiredMode = REQUIRED) + String protocol, + @Deprecated(since = "0.3.2") + @Schema(deprecated = true, description = "please use counterPartyAddress instead") + String connectorAddress, + @Schema(requiredMode = REQUIRED) + String counterPartyAddress, + @Deprecated(since = "0.3.2") + @Schema(deprecated = true, description = "Provider connector id is stored in the contract agreement") + String connectorId, + @Schema(requiredMode = REQUIRED) + String contractId, + @Schema(requiredMode = REQUIRED) + String assetId, + @Schema(requiredMode = REQUIRED) + String transferType, + ApiCoreSchema.DataAddressSchema dataDestination, + @Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE) + ManagementApiSchema.FreeFormPropertiesSchema privateProperties, + List callbackAddresses) { + + public static final String TRANSFER_REQUEST_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/TransferRequest", + "protocol": "dataspace-protocol-http", + "counterPartyAddress": "http://provider-address", + "contractId": "contract-id", + "assetId": "asset-id", + "transferType": "transferType", + "dataDestination": { + "type": "data-destination-type" + }, + "privateProperties": { + "private-key": "private-value" + }, + "callbackAddresses": [{ + "transactional": false, + "uri": "http://callback/url", + "events": ["contract.negotiation", "transfer.process"], + "authKey": "auth-key", + "authCodeId": "auth-code-id" + }] + } + """; + } + + @Schema(name = "TransferProcess", example = TransferProcessSchema.TRANSFER_PROCESS_EXAMPLE) + record TransferProcessSchema( + @Schema(name = TYPE, example = TRANSFER_PROCESS_TYPE) + String ldType, + @Schema(name = ID) + String id, + TransferProcess.Type type, + String protocol, + String counterPartyId, + String counterPartyAddress, + String state, + String contractAgreementId, + String errorDetail, + ApiCoreSchema.DataAddressSchema dataDestination, + ManagementApiSchema.FreeFormPropertiesSchema privateProperties, + List callbackAddresses + ) { + public static final String TRANSFER_PROCESS_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/TransferProcess", + "@id": "process-id", + "correlationId": "correlation-id", + "type": "PROVIDER", + "state": "STARTED", + "stateTimestamp": 1688465655, + "assetId": "asset-id", + "contractId": "contractId", + "dataDestination": { + "type": "data-destination-type" + }, + "privateProperties": { + "private-key": "private-value" + }, + "errorDetail": "eventual-error-detail", + "createdAt": 1688465655, + "callbackAddresses": [{ + "transactional": false, + "uri": "http://callback/url", + "events": ["contract.negotiation", "transfer.process"], + "authKey": "auth-key", + "authCodeId": "auth-code-id" + }] + } + """; + } + + @Schema(name = "TransferState", example = TransferStateSchema.TRANSFER_STATE_EXAMPLE) + record TransferStateSchema( + @Schema(name = TYPE, example = TransferState.TRANSFER_STATE_TYPE) + String ldType, + String state + ) { + public static final String TRANSFER_STATE_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/TransferState", + "state": "STARTED" + } + """; + } + + @Schema(name = "TerminateTransfer", example = TerminateTransferSchema.TERMINATE_TRANSFER_EXAMPLE) + record TerminateTransferSchema( + @Schema(name = TYPE, example = TerminateTransfer.TERMINATE_TRANSFER_TYPE) + String ldType, + String state + ) { + public static final String TERMINATE_TRANSFER_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/TerminateTransfer", + "reason": "a reason to terminate" + } + """; + } + + @Schema(name = "SuspendTransfer", example = SuspendTransferSchema.SUSPEND_TRANSFER_EXAMPLE) + record SuspendTransferSchema( + @Schema(name = TYPE, example = SuspendTransfer.SUSPEND_TRANSFER_TYPE) + String ldType, + String state + ) { + public static final String SUSPEND_TRANSFER_EXAMPLE = """ + { + "@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" }, + "@type": "https://w3id.org/edc/v0.0.1/ns/SuspendTransfer", + "reason": "a reason to suspend" + } + """; + } +} diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2Controller.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2Controller.java new file mode 100644 index 00000000000..c9a9f755577 --- /dev/null +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2Controller.java @@ -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.controlplane.api.management.transferprocess.v2; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.BaseTransferProcessApiController; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v2/transferprocesses") +public class TransferProcessApiV2Controller extends BaseTransferProcessApiController implements TransferProcessApiV2 { + public TransferProcessApiV2Controller(Monitor monitor, TransferProcessService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry) { + super(monitor, service, transformerRegistry, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApi.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3.java similarity index 95% rename from extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApi.java rename to extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3.java index a59ba8116ef..708f59455ae 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApi.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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 @@ -8,14 +8,15 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ -package org.eclipse.edc.connector.controlplane.api.management.transferprocess; +package org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.links.Link; import io.swagger.v3.oas.annotations.links.LinkParameter; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -42,9 +43,9 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; -@OpenAPIDefinition -@Tag(name = "Transfer Process") -public interface TransferProcessApi { +@OpenAPIDefinition(info = @Info(version = "v3")) +@Tag(name = "Transfer Process V3") +public interface TransferProcessApiV3 { 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 " + @@ -52,6 +53,7 @@ public interface TransferProcessApi { @Operation(description = "Returns all transfer process according to a query", requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), + operationId = "queryTransferProcessesV3", responses = { @ApiResponse(responseCode = "200", description = "The transfer processes matching the query", content = @Content(array = @ArraySchema(schema = @Schema(implementation = TransferProcessSchema.class)))), @@ -61,6 +63,7 @@ public interface TransferProcessApi { JsonArray queryTransferProcesses(JsonObject querySpecJson); @Operation(description = "Gets an transfer process with the given ID", + operationId = "getTransferProcessV3", responses = { @ApiResponse(responseCode = "200", description = "The transfer process", content = @Content(schema = @Schema(implementation = TransferProcessSchema.class))), @@ -73,7 +76,7 @@ public interface TransferProcessApi { JsonObject getTransferProcess(String id); @Operation(description = "Gets the state of a transfer process with the given ID", - operationId = "getTransferProcessState", + operationId = "getTransferProcessStateV3", responses = { @ApiResponse(responseCode = "200", description = "The transfer process's state", content = @Content(schema = @Schema(implementation = TransferStateSchema.class))), @@ -87,6 +90,7 @@ public interface TransferProcessApi { @Operation(description = "Initiates a data transfer with the given parameters. " + ASYNC_WARNING, requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TransferRequestSchema.class))), + operationId = "initiateTransferProcessV3", responses = { @ApiResponse(responseCode = "200", description = "The transfer was successfully initiated. Returns the transfer process ID and created timestamp", content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class)), @@ -100,6 +104,7 @@ public interface TransferProcessApi { JsonObject initiateTransferProcess(JsonObject transferRequest); @Operation(description = "Requests the deprovisioning of resources associated with a transfer process. " + ASYNC_WARNING, + operationId = "deprovisionTransferProcessV3", responses = { @ApiResponse(responseCode = "204", description = "Request to deprovision the transfer process was successfully received", links = @Link(name = "poll-state", operationId = "deprovisionTransferProcess")), @@ -112,6 +117,7 @@ public interface TransferProcessApi { @Operation(description = "Requests the termination of a transfer process. " + ASYNC_WARNING, requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TerminateTransferSchema.class))), + operationId = "terminateTransferProcessV3", responses = { @ApiResponse(responseCode = "204", description = "Request to terminate the transfer process was successfully received", links = @Link(name = "poll-state", operationId = "terminateTransferProcess")), @@ -126,6 +132,7 @@ public interface TransferProcessApi { @Operation(description = "Requests the suspension of a transfer process. " + ASYNC_WARNING, requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = SuspendTransferSchema.class))), + operationId = "suspendTransferProcessV3", responses = { @ApiResponse(responseCode = "204", description = "Request to suspend the transfer process was successfully received", links = @Link(name = "poll-state", operationId = "suspendTransferProcess")), @@ -139,6 +146,7 @@ public interface TransferProcessApi { void suspendTransferProcess(String id, JsonObject suspendTransfer); @Operation(description = "Requests the resumption of a suspended transfer process. " + ASYNC_WARNING, + operationId = "resumeTransferProcessV3", responses = { @ApiResponse(responseCode = "204", description = "Request to resume the transfer process was successfully received", links = @Link(name = "poll-state", operationId = "resumeTransferProcess")), diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3Controller.java b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3Controller.java new file mode 100644 index 00000000000..7a8b81238d1 --- /dev/null +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3Controller.java @@ -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.controlplane.api.management.transferprocess.v3; + +import jakarta.ws.rs.Path; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.BaseTransferProcessApiController; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; + +@Path("/v3/transferprocesses") +public class TransferProcessApiV3Controller extends BaseTransferProcessApiController implements TransferProcessApiV3 { + public TransferProcessApiV3Controller(Monitor monitor, TransferProcessService service, TypeTransformerRegistry transformerRegistry, JsonObjectValidatorRegistry validatorRegistry) { + super(monitor, service, transformerRegistry, validatorRegistry); + } +} diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiControllerTest.java b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiControllerTest.java similarity index 84% rename from extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiControllerTest.java rename to extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiControllerTest.java index 85cc765702e..aea4c9effe8 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiControllerTest.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/BaseTransferProcessApiControllerTest.java @@ -14,6 +14,7 @@ package org.eclipse.edc.connector.controlplane.api.management.transferprocess; +import io.restassured.specification.RequestSpecification; import jakarta.json.Json; import jakarta.json.JsonObject; import org.eclipse.edc.api.model.IdResponse; @@ -40,7 +41,6 @@ import java.util.List; import java.util.UUID; -import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static java.util.Collections.emptyList; import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.SuspendTransfer.SUSPEND_TRANSFER_TYPE; @@ -59,11 +59,18 @@ import static org.mockito.Mockito.when; @ApiTest -class TransferProcessApiControllerTest extends RestControllerTestBase { +public abstract class BaseTransferProcessApiControllerTest extends RestControllerTestBase { - private final TypeTransformerRegistry transformerRegistry = mock(); - private final TransferProcessService service = mock(); - private final JsonObjectValidatorRegistry validatorRegistry = mock(); + protected final TypeTransformerRegistry transformerRegistry = mock(); + protected final TransferProcessService service = mock(); + protected final JsonObjectValidatorRegistry validatorRegistry = mock(); + + protected abstract RequestSpecification baseRequest(); + + @NotNull + private TransferProcess.Builder createTransferProcess() { + return TransferProcess.Builder.newInstance().id(UUID.randomUUID().toString()); + } @Nested class Get { @@ -74,9 +81,8 @@ void shouldReturnTransferProcess() { when(service.findById(any())).thenReturn(transferProcess); when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(responseBody)); - given() - .port(port) - .get("/v2/transferprocesses/id") + baseRequest() + .get("/id") .then() .statusCode(200) .contentType(JSON) @@ -90,9 +96,8 @@ void shouldReturnTransferProcess() { void shouldReturnNotFound_whenNotFound() { when(service.findById(any())).thenReturn(null); - given() - .port(port) - .get("/v2/transferprocesses/id") + baseRequest() + .get("/id") .then() .statusCode(404) .contentType(JSON); @@ -104,9 +109,8 @@ void shouldReturnNotFound_whenTransformFails() { when(service.findById(any())).thenReturn(createTransferProcess().build()); when(transformerRegistry.transform(any(), any())).thenReturn(Result.failure("error")); - given() - .port(port) - .get("/v2/transferprocesses/id") + baseRequest() + .get("/id") .then() .statusCode(404) .contentType(JSON); @@ -122,9 +126,8 @@ void shouldReturnTheState() { var result = Json.createObjectBuilder().add("state", "INITIAL").build(); when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(result)); - given() - .port(port) - .get("/v2/transferprocesses/id/state") + baseRequest() + .get("/id/state") .then() .statusCode(200) .contentType(JSON) @@ -136,9 +139,8 @@ void shouldReturnTheState() { void shouldReturnNotFound_whenTransferProcessIsNotFound() { when(service.getState(any())).thenReturn(null); - given() - .port(port) - .get("/v2/transferprocesses/id/state") + baseRequest() + .get("/id/state") .then() .statusCode(404); verify(service).getState("id"); @@ -160,11 +162,10 @@ void shouldReturnQueriedTransferProcesses() { when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(expandedResponseBody)); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(200) .contentType(JSON) @@ -181,10 +182,9 @@ void shouldNotReturnError_whenEmptyBody() { var querySpec = QuerySpec.none(); when(service.search(any())).thenReturn(ServiceResult.success(emptyList())); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(200) .contentType(JSON) @@ -199,11 +199,10 @@ void shouldReturnBadRequest_whenValidationFails() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("error", "path"))); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(400); @@ -217,11 +216,10 @@ void shouldReturn400_whenQuerySpecTransformFails() { when(transformerRegistry.transform(any(), eq(QuerySpec.class))).thenReturn(Result.failure("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(400); verifyNoInteractions(service); @@ -235,11 +233,10 @@ void shouldReturnBadRequest_whenServiceReturnsBadRequest() { when(service.search(any())).thenReturn(ServiceResult.badRequest("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(400) .contentType(JSON); @@ -255,11 +252,10 @@ void shouldFilterOutResults_whenTransformFails() { when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.failure("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses/request") + .post("/request") .then() .statusCode(200) .contentType(JSON) @@ -281,11 +277,10 @@ void shouldReturnId() { when(transformerRegistry.transform(any(), eq(JsonObject.class))).thenReturn(Result.success(responseBody)); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses") + .post("") .then() .statusCode(200) .contentType(JSON) @@ -300,11 +295,10 @@ void shouldReturnBadRequest_whenValidationFails() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("error", "path"))); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses") + .post("") .then() .statusCode(400) .contentType(JSON); @@ -319,11 +313,10 @@ void shouldReturnBadRequest_whenTransformationFails() { when(transformerRegistry.transform(any(), any())).thenReturn(Result.failure("error")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses") + .post("") .then() .statusCode(400) .contentType(JSON); @@ -338,11 +331,10 @@ void shouldReturnConflict_whenItAlreadyExists() { when(service.initiateTransfer(any())).thenReturn(ServiceResult.conflict("already exists")); var requestBody = Json.createObjectBuilder().build(); - given() - .port(port) + baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/transferprocesses") + .post("") .then() .statusCode(409) .contentType(JSON); @@ -356,10 +348,9 @@ class Deprovision { void shouldDeprovision() { when(service.deprovision(any())).thenReturn(ServiceResult.success()); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/deprovision") + .post("/id/deprovision") .then() .statusCode(204); verify(service).deprovision("id"); @@ -369,10 +360,9 @@ void shouldDeprovision() { void shouldReturnConflict_whenServiceReturnsConflict() { when(service.deprovision(any())).thenReturn(ServiceResult.conflict("conflict")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/deprovision") + .post("/id/deprovision") .then() .statusCode(409); } @@ -381,10 +371,9 @@ void shouldReturnConflict_whenServiceReturnsConflict() { void shouldReturnNotFound_whenServiceReturnsNotFound() { when(service.deprovision(any())).thenReturn(ServiceResult.notFound("not found")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/deprovision") + .post("/id/deprovision") .then() .statusCode(404); } @@ -402,11 +391,10 @@ void shouldTerminate() { when(transformerRegistry.transform(any(), eq(TerminateTransfer.class))).thenReturn(Result.success(terminateTransfer)); when(service.terminate(any())).thenReturn(ServiceResult.success()); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/terminate") + .post("/id/terminate") .then() .statusCode(204); verify(transformerRegistry).transform(expanded, TerminateTransfer.class); @@ -417,11 +405,10 @@ void shouldTerminate() { void shouldReturnBadRequest_whenValidationFail() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("error", "path"))); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/terminate") + .post("/id/terminate") .then() .statusCode(400); @@ -434,11 +421,10 @@ void shouldReturnBadRequest_whenTransformFail() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(TerminateTransfer.class))).thenReturn(Result.failure("failure")); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/terminate") + .post("/id/terminate") .then() .statusCode(400); verifyNoInteractions(service); @@ -451,17 +437,15 @@ void shouldReturnConflict_whenServiceReturnsConflict() { when(transformerRegistry.transform(any(), eq(TerminateTransfer.class))).thenReturn(Result.success(terminateTransfer)); when(service.terminate(any())).thenReturn(ServiceResult.conflict("conflict")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/terminate") + .post("/id/terminate") .then() .statusCode(409); } } - @Nested class Suspend { @@ -473,11 +457,10 @@ void shouldSuspend() { when(transformerRegistry.transform(any(), eq(SuspendTransfer.class))).thenReturn(Result.success(suspendTransfer)); when(service.suspend(any())).thenReturn(ServiceResult.success()); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/suspend") + .post("/id/suspend") .then() .statusCode(204); verify(transformerRegistry).transform(expanded, SuspendTransfer.class); @@ -488,11 +471,10 @@ void shouldSuspend() { void shouldReturnBadRequest_whenValidationFail() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(violation("error", "path"))); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/suspend") + .post("/id/suspend") .then() .statusCode(400); @@ -505,11 +487,10 @@ void shouldReturnBadRequest_whenTransformFail() { when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(SuspendTransfer.class))).thenReturn(Result.failure("failure")); - given() - .port(port) + baseRequest() .contentType(JSON) .body(Json.createObjectBuilder().build()) - .post("/v2/transferprocesses/id/suspend") + .post("/id/suspend") .then() .statusCode(400); verifyNoInteractions(service); @@ -522,10 +503,9 @@ void shouldReturnConflict_whenServiceReturnsConflict() { when(transformerRegistry.transform(any(), eq(SuspendTransfer.class))).thenReturn(Result.success(suspendTransfer)); when(service.suspend(any())).thenReturn(ServiceResult.conflict("conflict")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/suspend") + .post("/id/suspend") .then() .statusCode(409); } @@ -539,10 +519,9 @@ class Resume { void shouldResumeTransfer() { when(service.resume(any())).thenReturn(ServiceResult.success()); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/resume") + .post("/id/resume") .then() .statusCode(204); verify(service).resume(isA(ResumeTransferCommand.class)); @@ -552,10 +531,9 @@ void shouldResumeTransfer() { void shouldReturnConflict_whenServiceReturnsConflict() { when(service.resume(any())).thenReturn(ServiceResult.conflict("conflict")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/resume") + .post("/id/resume") .then() .statusCode(409); } @@ -564,24 +542,13 @@ void shouldReturnConflict_whenServiceReturnsConflict() { void shouldReturnNotFound_whenServiceReturnsNotFound() { when(service.resume(any())).thenReturn(ServiceResult.notFound("not found")); - given() - .port(port) + baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/id/resume") + .post("/id/resume") .then() .statusCode(404); } } - @Override - protected Object controller() { - return new TransferProcessApiController(monitor, service, transformerRegistry, validatorRegistry); - } - - @NotNull - private TransferProcess.Builder createTransferProcess() { - return TransferProcess.Builder.newInstance().id(UUID.randomUUID().toString()); - } - } diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtensionTest.java b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtensionTest.java index df23c482212..f32cab84ab4 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiExtensionTest.java @@ -14,10 +14,13 @@ package org.eclipse.edc.connector.controlplane.api.management.transferprocess; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.v2.TransferProcessApiV2Controller; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3Controller; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.WebService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -26,6 +29,7 @@ import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest.TRANSFER_REQUEST_TYPE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +38,7 @@ class TransferProcessApiExtensionTest { private final JsonObjectValidatorRegistry validatorRegistry = mock(); + private final WebService webService = mock(); @BeforeEach void setUp(ServiceExtensionContext context) { @@ -41,6 +46,7 @@ void setUp(ServiceExtensionContext context) { when(typeTransformerRegistry.forContext(any())).thenReturn(mock()); context.registerService(TypeTransformerRegistry.class, typeTransformerRegistry); context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + context.registerService(WebService.class, webService); } @Test @@ -50,4 +56,12 @@ void initialize_shouldRegisterValidators(TransferProcessApiExtension extension, verify(validatorRegistry).register(eq(TRANSFER_REQUEST_TYPE), any()); verify(validatorRegistry).register(eq(TERMINATE_TRANSFER_TYPE), any()); } + + @Test + void initialize_shouldRegisterControllers(ServiceExtensionContext context, TransferProcessApiExtension extension) { + extension.initialize(context); + + verify(webService).registerResource(any(), isA(TransferProcessApiV2Controller.class)); + verify(webService).registerResource(any(), isA(TransferProcessApiV3Controller.class)); + } } diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiTest.java b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiTest.java index f08329332a4..69c006f23de 100644 --- a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiTest.java +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/TransferProcessApiTest.java @@ -39,13 +39,13 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.map; -import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.TransferProcessApi.SuspendTransferSchema.SUSPEND_TRANSFER_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.TransferProcessApi.TerminateTransferSchema.TERMINATE_TRANSFER_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.TransferProcessApi.TransferProcessSchema.TRANSFER_PROCESS_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.TransferProcessApi.TransferRequestSchema.TRANSFER_REQUEST_EXAMPLE; -import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.TransferProcessApi.TransferStateSchema.TRANSFER_STATE_EXAMPLE; import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TransferState.TRANSFER_STATE_STATE; import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TransferState.TRANSFER_STATE_TYPE; +import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3.SuspendTransferSchema.SUSPEND_TRANSFER_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3.TerminateTransferSchema.TERMINATE_TRANSFER_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3.TransferProcessSchema.TRANSFER_PROCESS_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3.TransferRequestSchema.TRANSFER_REQUEST_EXAMPLE; +import static org.eclipse.edc.connector.controlplane.api.management.transferprocess.v3.TransferProcessApiV3.TransferStateSchema.TRANSFER_STATE_EXAMPLE; import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess.TRANSFER_PROCESS_ASSET_ID; import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess.TRANSFER_PROCESS_CALLBACK_ADDRESSES; import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess.TRANSFER_PROCESS_CONTRACT_ID; diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2ControllerTest.java b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2ControllerTest.java new file mode 100644 index 00000000000..e50dc38a5aa --- /dev/null +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v2/TransferProcessApiV2ControllerTest.java @@ -0,0 +1,34 @@ +/* + * 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.controlplane.api.management.transferprocess.v2; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.BaseTransferProcessApiControllerTest; + +import static io.restassured.RestAssured.given; + +public class TransferProcessApiV2ControllerTest extends BaseTransferProcessApiControllerTest { + @Override + protected Object controller() { + return new TransferProcessApiV2Controller(monitor, service, transformerRegistry, validatorRegistry); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .port(port) + .baseUri("http://localhost:" + port + "/v2/transferprocesses"); + } +} diff --git a/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3ControllerTest.java b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3ControllerTest.java new file mode 100644 index 00000000000..f85d36d1506 --- /dev/null +++ b/extensions/control-plane/api/management-api/transfer-process-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/transferprocess/v3/TransferProcessApiV3ControllerTest.java @@ -0,0 +1,34 @@ +/* + * 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.controlplane.api.management.transferprocess.v3; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.BaseTransferProcessApiControllerTest; + +import static io.restassured.RestAssured.given; + +public class TransferProcessApiV3ControllerTest extends BaseTransferProcessApiControllerTest { + @Override + protected Object controller() { + return new TransferProcessApiV3Controller(monitor, service, transformerRegistry, validatorRegistry); + } + + @Override + protected RequestSpecification baseRequest() { + return given() + .port(port) + .baseUri("http://localhost:" + port + "/v3/transferprocesses"); + } +} diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/CatalogApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/CatalogApiEndToEndTest.java index c848cbe92b2..30b5b483d04 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/CatalogApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/CatalogApiEndToEndTest.java @@ -50,34 +50,6 @@ public class CatalogApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - - @Nested - @PostgresqlIntegrationTest - class Postgres extends Tests { - - @RegisterExtension - static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); - - Postgres() { - super(RUNTIME); - } - } - abstract static class Tests extends ManagementApiEndToEndTestBase { // requests the catalog to itself, to save another connector. private final String providerUrl = "http://localhost:" + PROTOCOL_PORT + "/protocol"; @@ -98,7 +70,7 @@ void requestCatalog_shouldReturnCatalog_withoutQuerySpec() { baseRequest() .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post("/v3/catalog/request") .then() .log().ifValidationFails() .statusCode(200) @@ -155,7 +127,7 @@ void requestCatalog_shouldReturnCatalog_withQuerySpec() { baseRequest() .contentType(JSON) .body(requestBody) - .post("/v2/catalog/request") + .post("/v3/catalog/request") .then() .statusCode(200) .contentType(JSON) @@ -182,7 +154,7 @@ void getDataset_shouldReturnDataset() { baseRequest() .contentType(JSON) .body(requestBody) - .post("/v2/catalog/dataset/request") + .post("/v3/catalog/dataset/request") .then() .log().ifValidationFails() .statusCode(200) @@ -200,4 +172,31 @@ private Asset.Builder createAsset(String id, String sourceType) { } + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); + @RegisterExtension + static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); + + Postgres() { + super(RUNTIME); + } + } + } diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractAgreementApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractAgreementApiEndToEndTest.java index e270221ada2..60960f69fb7 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractAgreementApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractAgreementApiEndToEndTest.java @@ -42,34 +42,6 @@ public class ContractAgreementApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - - @Nested - @PostgresqlIntegrationTest - class Postgres extends Tests { - - @RegisterExtension - static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); - - Postgres() { - super(RUNTIME); - } - } - abstract static class Tests extends ManagementApiEndToEndTestBase { Tests(EdcRuntimeExtension runtime) { @@ -84,7 +56,7 @@ void getAll() { var jsonPath = baseRequest() .contentType(JSON) - .post("/v2/contractagreements/request") + .post("/v3/contractagreements/request") .then() .log().ifError() .statusCode(200) @@ -105,7 +77,7 @@ void getById() { var json = baseRequest() .contentType(JSON) - .get("/v2/contractagreements/cn1") + .get("/v3/contractagreements/cn1") .then() .statusCode(200) .contentType(JSON) @@ -124,7 +96,7 @@ void getNegotiationByAgreementId() { var json = baseRequest() .contentType(JSON) - .get("/v2/contractagreements/agreement-id/negotiation") + .get("/v3/contractagreements/agreement-id/negotiation") .then() .statusCode(200) .contentType(JSON) @@ -164,4 +136,31 @@ private ContractAgreement createContractAgreement(String negotiationId) { .build(); } } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); + @RegisterExtension + static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); + + Postgres() { + super(RUNTIME); + } + } } diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractDefinitionApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractDefinitionApiEndToEndTest.java index 3cff5e6c528..72890d9aaf0 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractDefinitionApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractDefinitionApiEndToEndTest.java @@ -49,34 +49,6 @@ public class ContractDefinitionApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - - @Nested - @PostgresqlIntegrationTest - class Postgres extends Tests { - - @RegisterExtension - static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); - - Postgres() { - super(RUNTIME); - } - } - abstract static class Tests extends ManagementApiEndToEndTestBase { Tests(EdcRuntimeExtension runtime) { @@ -91,7 +63,7 @@ void queryContractDefinitions_noQuerySpec() { var body = baseRequest() .contentType(JSON) - .post("/v2/contractdefinitions/request") + .post("/v3/contractdefinitions/request") .then() .statusCode(200) .body("size()", greaterThan(0)) @@ -117,7 +89,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { baseRequest() .contentType(JSON) .body(requestJson) - .post("/v2/contractdefinitions") + .post("/v3/contractdefinitions") .then() .statusCode(200) .body("@id", equalTo(id)); @@ -130,7 +102,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { baseRequest() .body(matchingQuery) .contentType(JSON) - .post("/v2/contractdefinitions/request") + .post("/v3/contractdefinitions/request") .then() .log().ifError() .statusCode(200) @@ -145,7 +117,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { baseRequest() .body(nonMatchingQuery) .contentType(JSON) - .post("/v2/contractdefinitions/request") + .post("/v3/contractdefinitions/request") .then() .log().ifError() .statusCode(200) @@ -161,7 +133,7 @@ void shouldCreateAndRetrieve() { baseRequest() .contentType(JSON) .body(requestJson) - .post("/v2/contractdefinitions") + .post("/v3/contractdefinitions") .then() .statusCode(200) .body("@id", equalTo(id)); @@ -178,7 +150,7 @@ void delete() { getContractDefinitionStore().save(entity); baseRequest() - .delete("/v2/contractdefinitions/" + id) + .delete("/v3/contractdefinitions/" + id) .then() .statusCode(204); @@ -201,7 +173,7 @@ void update_whenExists() { baseRequest() .contentType(JSON) .body(updated) - .put("/v2/contractdefinitions") + .put("/v3/contractdefinitions") .then() .statusCode(204); @@ -219,7 +191,7 @@ void update_whenNotExists() { baseRequest() .contentType(JSON) .body(updated) - .put("/v2/contractdefinitions") + .put("/v3/contractdefinitions") .then() .statusCode(404); } @@ -258,4 +230,31 @@ private ContractDefinition.Builder createContractDefinition(String id) { } } + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); + @RegisterExtension + static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); + + Postgres() { + super(RUNTIME); + } + } + } diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractNegotiationApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractNegotiationApiEndToEndTest.java index 9c5e61861ea..06e54d03cb7 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractNegotiationApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/ContractNegotiationApiEndToEndTest.java @@ -59,34 +59,6 @@ public class ContractNegotiationApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - - @Nested - @PostgresqlIntegrationTest - class Postgres extends Tests { - - @RegisterExtension - static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); - - Postgres() { - super(RUNTIME); - } - } - abstract static class Tests extends ManagementApiEndToEndTestBase { private final String protocolUrl = "http://localhost:" + PROTOCOL_PORT + "/protocol"; @@ -116,7 +88,7 @@ void getAll() { ) .build() ) - .post("/v2/contractnegotiations/request") + .post("/v3/contractnegotiations/request") .then() .statusCode(200) .contentType(JSON) @@ -137,7 +109,7 @@ void getById() { var json = baseRequest() .contentType(JSON) - .get("/v2/contractnegotiations/cn1") + .get("/v3/contractnegotiations/cn1") .then() .statusCode(200) .contentType(JSON) @@ -155,7 +127,7 @@ void getState() { baseRequest() .contentType(JSON) - .get("/v2/contractnegotiations/cn1/state") + .get("/v3/contractnegotiations/cn1/state") .then() .statusCode(200) .contentType(JSON) @@ -170,7 +142,7 @@ void getAgreementForNegotiation() { var json = baseRequest() .contentType(JSON) - .get("/v2/contractnegotiations/cn1/agreement") + .get("/v3/contractnegotiations/cn1/agreement") .then() .statusCode(200) .contentType(JSON) @@ -197,7 +169,7 @@ void initiateNegotiation() { var id = baseRequest() .contentType(JSON) .body(requestJson) - .post("/v2/contractnegotiations") + .post("/v3/contractnegotiations") .then() .statusCode(200) .contentType(JSON) @@ -221,7 +193,7 @@ void terminate() { baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/contractnegotiations/cn1/terminate") + .post("/v3/contractnegotiations/cn1/terminate") .then() .log().ifError() .statusCode(204); @@ -293,4 +265,31 @@ private ContractNegotiationStore getContractNegotiationStore() { } } + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); + @RegisterExtension + static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); + + Postgres() { + super(RUNTIME); + } + } + } diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java index cc9651897c6..b50080ebfc9 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java @@ -51,34 +51,6 @@ public class PolicyDefinitionApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - - @Nested - @PostgresqlIntegrationTest - class Postgres extends Tests { - - @RegisterExtension - static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); - - Postgres() { - super(RUNTIME); - } - } - abstract static class Tests extends ManagementApiEndToEndTestBase { Tests(EdcRuntimeExtension runtime) { @@ -98,13 +70,13 @@ void shouldStorePolicyDefinition() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .contentType(JSON) .extract().jsonPath().getString(ID); baseRequest() - .get("/v2/policydefinitions/" + id) + .get("/v3/policydefinitions/" + id) .then() .log().ifValidationFails() .statusCode(200) @@ -133,7 +105,7 @@ void shouldStorePolicyDefinitionWithPrivateProperties() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .contentType(JSON) .extract().jsonPath().getString(ID); @@ -149,7 +121,7 @@ void shouldStorePolicyDefinitionWithPrivateProperties() { .extracting(PolicyDefinition::getPrivateProperties).isEqualTo(privateProp); baseRequest() - .get("/v2/policydefinitions/" + id) + .get("/v3/policydefinitions/" + id) .then() .statusCode(200) .contentType(JSON) @@ -176,7 +148,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .contentType(JSON) .extract().jsonPath().getString(ID); @@ -189,7 +161,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { baseRequest() .body(matchingQuery) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/v3/policydefinitions/request") .then() .log().ifError() .statusCode(200) @@ -204,7 +176,7 @@ void queryPolicyDefinitionWithSimplePrivateProperties() { baseRequest() .body(nonMatchingQuery) .contentType(JSON) - .post("/v2/policydefinitions/request") + .post("/v3/policydefinitions/request") .then() .log().ifError() .statusCode(200) @@ -224,14 +196,14 @@ void shouldUpdate() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .statusCode(200) .extract().jsonPath().getString(ID); baseRequest() .contentType(JSON) - .get("/v2/policydefinitions/" + id) + .get("/v3/policydefinitions/" + id) .then() .statusCode(200); @@ -241,7 +213,7 @@ void shouldUpdate() { .add(ID, id) .add("privateProperties", createObjectBuilder().add("privateProperty", "value")) .build()) - .put("/v2/policydefinitions/" + id) + .put("/v3/policydefinitions/" + id) .then() .statusCode(204); @@ -264,18 +236,18 @@ void shouldDelete() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .statusCode(200) .extract().jsonPath().getString(ID); baseRequest() - .delete("/v2/policydefinitions/" + id) + .delete("/v3/policydefinitions/" + id) .then() .statusCode(204); baseRequest() - .get("/v2/policydefinitions/" + id) + .get("/v3/policydefinitions/" + id) .then() .statusCode(404); } @@ -297,18 +269,18 @@ void shouldDeleteWithProperties() { var id = baseRequest() .body(requestBody) .contentType(JSON) - .post("/v2/policydefinitions") + .post("/v3/policydefinitions") .then() .statusCode(200) .extract().jsonPath().getString(ID); baseRequest() - .delete("/v2/policydefinitions/" + id) + .delete("/v3/policydefinitions/" + id) .then() .statusCode(204); baseRequest() - .get("/v2/policydefinitions/" + id) + .get("/v3/policydefinitions/" + id) .then() .statusCode(404); } @@ -353,4 +325,31 @@ private PolicyDefinitionStore store() { } + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = postgresRuntime(); + @RegisterExtension + static final BeforeAllCallback CREATE_DATABASE = context -> createDatabase("runtime"); + + Postgres() { + super(RUNTIME); + } + } + } diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/SecretsApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/SecretsApiEndToEndTest.java index da301452451..f499dc79359 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/SecretsApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/SecretsApiEndToEndTest.java @@ -44,19 +44,6 @@ */ public class SecretsApiEndToEndTest { - @Nested - @EndToEndTest - class InMemory extends Tests { - - @RegisterExtension - public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); - - InMemory() { - super(RUNTIME); - } - - } - abstract static class Tests extends ManagementApiEndToEndTestBase { Tests(EdcRuntimeExtension runtime) { @@ -70,7 +57,7 @@ void getSecretById() { getVault().storeSecret(id, value); baseRequest() - .get("/v1/secrets/" + id) + .get("/v3/secrets/" + id) .then() .statusCode(200) .body(notNullValue()) @@ -93,7 +80,7 @@ void createSecret_shouldBeStored() { baseRequest() .contentType(ContentType.JSON) .body(secretJson) - .post("/v1/secrets") + .post("/v3/secrets") .then() .log().ifError() .statusCode(200) @@ -116,7 +103,7 @@ void createSecret_shouldFail_whenBodyIsNotValid() { baseRequest() .contentType(ContentType.JSON) .body(secretJson) - .post("/v1/secrets") + .post("/v3/secrets") .then() .log().ifError() .statusCode(400); @@ -138,7 +125,7 @@ void updateSecret() { baseRequest() .contentType(ContentType.JSON) .body(secretJson) - .put("/v1/secrets") + .put("/v3/secrets") .then() .log().all() .statusCode(204) @@ -152,4 +139,17 @@ private Vault getVault() { return runtime.getContext().getService(Vault.class); } } + + @Nested + @EndToEndTest + class InMemory extends Tests { + + @RegisterExtension + public static final EdcRuntimeExtension RUNTIME = inMemoryRuntime(); + + InMemory() { + super(RUNTIME); + } + + } } \ No newline at end of file diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/TransferProcessApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/TransferProcessApiEndToEndTest.java index f14665e64ca..85e2d30c24f 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/TransferProcessApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/TransferProcessApiEndToEndTest.java @@ -79,7 +79,7 @@ void getAll() { baseRequest() .contentType(JSON) .body(query(criterion("id", "in", List.of(id1, id2)))) - .post("/v2/transferprocesses/request") + .post("/v3/transferprocesses/request") .then() .log().ifError() .statusCode(200) @@ -94,7 +94,7 @@ void getById() { getStore().save(createTransferProcess("tp2")); baseRequest() - .get("/v2/transferprocesses/tp2") + .get("/v3/transferprocesses/tp2") .then() .statusCode(200) .body("@id", is("tp2")) @@ -106,7 +106,7 @@ void getState() { getStore().save(createTransferProcessBuilder("tp2").state(COMPLETED.code()).build()); baseRequest() - .get("/v2/transferprocesses/tp2/state") + .get("/v3/transferprocesses/tp2/state") .then() .statusCode(200) .contentType(JSON) @@ -138,7 +138,7 @@ void create() { var id = baseRequest() .contentType(JSON) .body(requestBody) - .post("/v2/transferprocesses/") + .post("/v3/transferprocesses/") .then() .log().ifError() .statusCode(200) @@ -154,7 +154,7 @@ void deprovision() { baseRequest() .contentType(JSON) - .post("/v2/transferprocesses/" + id + "/deprovision") + .post("/v3/transferprocesses/" + id + "/deprovision") .then() .statusCode(204); } @@ -171,7 +171,7 @@ void terminate() { baseRequest() .contentType(JSON) .body(requestBody) - .post("/v2/transferprocesses/" + id + "/terminate") + .post("/v3/transferprocesses/" + id + "/terminate") .then() .log().ifError() .statusCode(204); @@ -210,7 +210,7 @@ void request_byState() throws JsonProcessingException { var result = baseRequest() .contentType(JSON) .body(query) - .post("/v2/transferprocesses/request") + .post("/v3/transferprocesses/request") .then() .statusCode(200) .extract().body().as(JsonArray.class); @@ -247,7 +247,7 @@ void request_sortByStateTimestamp() throws JsonProcessingException, InterruptedE var result = baseRequest() .contentType(JSON) .body(query) - .post("/v2/transferprocesses/request") + .post("/v3/transferprocesses/request") .then() .log().ifError() .statusCode(200)