diff --git a/.gitignore b/.gitignore index 95c2c9c5ec..4ed293562b 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ docs/src/diagram-replacer/plantuml.jar # Helm Chart Dependencies /charts/item-relationship-service/Chart.lock /charts/item-relationship-service/charts/ +/docs/src/api/irs-api.actual.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d46874a6..08e34f655f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ _**For better traceability add the corresponding GitHub issue number in each cha ## [Unreleased] -### Added +## Added + +- Added endpoint for dedicated removal of policy from BPNL. #559 - Integration Test Policy Store API Unhappy Path. #519 ### Fixed diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index 87a2c8aef5..f9c3995be0 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -1016,6 +1016,59 @@ paths: summary: Removes a policy that should no longer be accepted in EDC negotiation. tags: - Item Relationship Service + /irs/policies/{policyId}/bpnl/{bpnl}: + delete: + description: Removes a policy from BPNL that should no longer be accepted in + EDC negotiation. + operationId: removeAllowedPolicyFromBpnl + parameters: + - in: path + name: policyId + required: true + schema: + type: string + - in: path + name: bpnl + required: true + schema: + type: string + pattern: "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}" + responses: + "200": + description: OK + "400": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-400' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Policy deletion failed. + "401": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-401' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: No valid authentication credentials. + "403": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-403' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Authorization refused by server. + security: + - api_key: [] + summary: Removes a policy from BPNL that should no longer be accepted in EDC + negotiation. + tags: + - Item Relationship Service components: examples: aspect-models-list: diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java index da34d33cc1..b902f986e4 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -88,20 +89,25 @@ void generatedOpenApiMatchesContract() throws Exception { final Map definedYamlMap = mapper.readerForMapOf(Object.class).readValue(definedYaml); final Map generatedYamlMap = mapper.readerForMapOf(Object.class).readValue(generatedYaml); - // To correctly display both documentations examples - manual and generated by annotations - - // we need to remove verification for some "examples", otherwise one or another won't display correctly - assertThat(generatedYamlMap) - - .usingRecursiveComparison() - - .ignoringFields("components.schemas.PageResult.example") - .ignoringFields("components.schemas.AspectModels.example") - .ignoringFields("components.schemas.BatchOrderResponse.example") - .ignoringFields("components.schemas.Jobs.example") - .ignoringFields("components.schemas.Policy") - .ignoringFields("components.schemas.BatchResponse.example") - - .isEqualTo(definedYamlMap); + try { + // To correctly display both documentations examples - manual and generated by annotations - + // we need to remove verification for some "examples", otherwise one or another won't display correctly + assertThat(generatedYamlMap) + + .usingRecursiveComparison() + + .ignoringFields("components.schemas.PageResult.example") + .ignoringFields("components.schemas.AspectModels.example") + .ignoringFields("components.schemas.BatchOrderResponse.example") + .ignoringFields("components.schemas.Jobs.example") + .ignoringFields("components.schemas.Policy") + .ignoringFields("components.schemas.BatchResponse.example") + + .isEqualTo(definedYamlMap); + } catch (AssertionError e) { + // write changed API to file for easier comparison + Files.writeString(Paths.get("../docs/src/api/irs-api.actual.yaml"), generatedYaml); + } } diff --git a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestHelperForPolicyStoreApi.java b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestHelperForPolicyStoreApi.java index f3a7742af4..281692b7b1 100644 --- a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestHelperForPolicyStoreApi.java +++ b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestHelperForPolicyStoreApi.java @@ -264,6 +264,16 @@ public static ValidatableResponse deletePolicy( .then(); } + public static ValidatableResponse removePolicyFromBpnl( + final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final String policyId, + final String bpnl) { + return givenAuthentication(authenticationPropertiesBuilder).pathParam("policyId", policyId) + .pathParam("bpnl", bpnl) + .when() + .delete(URL_IRS_POLICIES + "/{policyId}/bpnl/{bpnl}") + .then(); + } + @Data @NoArgsConstructor public static final class PolicyAttributes { diff --git a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java index 605190f241..ca0e3a2902 100644 --- a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java +++ b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java @@ -179,6 +179,12 @@ public void iDeletePolicyWithPolicyId(final String policyId) { policyId); } + @When("I remove the policy {string} from BPN {string}") + public void iRemovePolicyFromBpnl(final String policyId, final String bpnl) { + this.deletePoliciesResponse = E2ETestHelperForPolicyStoreApi.removePolicyFromBpnl( + this.authenticationPropertiesBuilder, policyId, bpnl); + } + @Then("the delete policy response should have HTTP status {int}") public void theDeletePolicyResponseShouldHaveStatus(final int httpStatus) { this.deletePoliciesResponse.statusCode(httpStatus); diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java index 470f714830..10968b392d 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java @@ -42,6 +42,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import jakarta.validation.constraints.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; @@ -217,6 +218,40 @@ public void deleteAllowedPolicy(@PathVariable("policyId") final String policyId) service.deletePolicy(policyId); } + @Operation(operationId = "removeAllowedPolicyFromBpnl", + summary = "Removes a policy from BPNL that should no longer be accepted in EDC negotiation.", + security = @SecurityRequirement(name = "api_key"), tags = { "Item Relationship Service" }, + description = "Removes a policy from BPNL that should no longer be accepted in EDC negotiation.") + @ApiResponses(value = { @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "400", description = "Policy deletion failed.", + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-400")) + }), + @ApiResponse(responseCode = "401", description = UNAUTHORIZED_DESC, + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-401")) + }), + @ApiResponse(responseCode = "403", description = FORBIDDEN_DESC, + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-403")) + }), + }) + @DeleteMapping("/policies/{policyId}/bpnl/{bpnl}") + @ResponseStatus(HttpStatus.OK) + @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')") + public void removeAllowedPolicyFromBpnl( + @PathVariable("policyId") final String policyId, // + @Pattern(regexp = BPN_REGEX, message = " Invalid BPN.") // + @PathVariable("bpnl") final String bpnl) { + service.deletePolicyForEachBpn(policyId, List.of(bpnl)); + } + @Operation(operationId = "updateAllowedPolicy", summary = "Updates existing policies.", security = @SecurityRequirement(name = "api_key"), tags = { "Item Relationship Service" }, description = "Updates existing policies.") diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java index a47ba48ea5..8f6f83afb0 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java @@ -218,7 +218,7 @@ public void deletePolicy(final String policyId) { } } - private void deletePolicyForEachBpn(final String policyId, final List bpnList) { + public void deletePolicyForEachBpn(final String policyId, final List bpnList) { try { for (final String bpn : bpnList) { persistence.delete(bpn, policyId); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java index 802a60114c..ac47fd0b4d 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java @@ -40,6 +40,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.UUID; import jakarta.json.Json; import jakarta.json.JsonObject; @@ -355,6 +356,17 @@ private Constraints createConstraints() { @Nested class DeletePolicyTests { + @Test + void deletePolicyForEachBpn_success() { + // ACT + final String policyId = UUID.randomUUID().toString(); + testee.deletePolicyForEachBpn(policyId, List.of("BPN1", "BPN2")); + + // ASSERT + verify(persistenceMock).delete("BPN1", policyId); + verify(persistenceMock).delete("BPN2", policyId); + } + @Test void deletePolicy_deleteSuccessful() { // ARRANGE