From e4a37a863a71a98e596f855dae560764e91e82c2 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 8 Jul 2024 12:51:45 +0200 Subject: [PATCH 1/5] fix(api-test): [#639] fix test test was always green because in the optimization to output the generated API yaml to a file it was forgotten to re-throw the assertion exception --- .../test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java | 1 + 1 file changed, 1 insertion(+) 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 b902f986e4..f107e06114 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 @@ -107,6 +107,7 @@ void generatedOpenApiMatchesContract() throws Exception { } catch (AssertionError e) { // write changed API to file for easier comparison Files.writeString(Paths.get("../docs/src/api/irs-api.actual.yaml"), generatedYaml); + throw e; } } From 68073db631e3945a98ecdcc4adf44e6fa9b26f0d Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 8 Jul 2024 12:59:42 +0200 Subject: [PATCH 2/5] feat(policy-api):[#639] Open-API documentation --- docs/src/api/irs-api.yaml | 195 +++++++++++++++++- .../irs/configuration/OpenApiExamples.java | 69 +++++-- .../controllers/PolicyStoreController.java | 54 ++++- 3 files changed, 289 insertions(+), 29 deletions(-) diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index 8580810ea0..fc84fdcf25 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -860,7 +860,7 @@ paths: - api_key: [] summary: Lists the registered policies that should be accepted in EDC negotiation. tags: - - Item Relationship Service + - Item Relationship Service - Policy API post: description: Register a policy that should be accepted in EDC negotiation. operationId: registerAllowedPolicy @@ -917,7 +917,7 @@ paths: - api_key: [] summary: Register a policy that should be accepted in EDC negotiation. tags: - - Item Relationship Service + - Item Relationship Service - Policy API put: description: Updates existing policies. operationId: updateAllowedPolicy @@ -970,7 +970,65 @@ paths: - api_key: [] summary: Updates existing policies. tags: - - Item Relationship Service + - Item Relationship Service - Policy API + /irs/policies/paged: + get: + description: | + Fetch a page of policies with options to filter and sort. + + - **Filtering:** + `search=,[EQUALS|STARTS_WITH|BEFORE_LOCAL_DATE|AFTER_LOCAL_DATE],`. + Example: 'search=BPN,STARTS_WITH,BPNL12&search=policyId,STARTS_WITH,policy2'. + + - **Sorting:** + `sort=,[asc|desc]`. + Example: 'sort=BPN,asc&sort=policyId,desc'. + + - **Paging:** + Example: `page=1&size=20` + operationId: getPoliciesPaged + parameters: + - description: List of business partner numbers. + in: query + name: businessPartnerNumbers + required: false + schema: + type: array + items: + type: string + responses: + "200": + content: + application/json: + examples: + error: + $ref: '#/components/examples/get-policies-paged-result' + schema: + $ref: '#/components/schemas/Page' + description: Successfully retrieved the paged policies + "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: Find policies. + tags: + - Item Relationship Service - Policy API /irs/policies/{policyId}: delete: description: Removes a policy that should no longer be accepted in EDC negotiation. @@ -1015,7 +1073,7 @@ paths: - api_key: [] summary: Removes a policy that should no longer be accepted in EDC negotiation. tags: - - Item Relationship Service + - Item Relationship Service - Policy API /irs/policies/{policyId}/bpnl/{bpnl}: delete: description: Removes a policy from BPNL that should no longer be accepted in @@ -1032,7 +1090,7 @@ paths: required: true schema: type: string - pattern: "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}" + pattern: "(BPNL)[\\w\\d]{10}[\\w\\d]{2}" responses: "200": description: OK @@ -1068,7 +1126,7 @@ paths: summary: Removes a policy from BPNL that should no longer be accepted in EDC negotiation. tags: - - Item Relationship Service + - Item Relationship Service - Policy API components: examples: aspect-models-list: @@ -1459,6 +1517,69 @@ components: relationships: [] submodels: [] tombstones: [] + get-policies-paged-result: + value: + content: + - bpn: BPNL1234567890AB + policy: + createdOn: 2024-07-08T12:01:19.4677109+02:00 + permissions: + - action: use + constraint: + and: [ ] + or: + - leftOperand: Membership + operator: + '@id': eq + rightOperand: active + - leftOperand: FrameworkAgreement.traceability + operator: + '@id': eq + rightOperand: active + - leftOperand: PURPOSE + operator: + '@id': eq + rightOperand: ID 3.1 Trace + - action: access + constraint: + and: [ ] + or: + - leftOperand: Membership + operator: + '@id': eq + rightOperand: active + - leftOperand: FrameworkAgreement.traceability + operator: + '@id': eq + rightOperand: active + - leftOperand: PURPOSE + operator: + '@id': eq + rightOperand: ID 3.1 Trace + policyId: 13a8523f-c80a-40b8-9e43-faab47fbceaa + validUntil: 2025-07-08T12:01:19.4677109+02:00 + empty: false + first: false + last: true + number: 1 + numberOfElements: 1 + pageable: + offset: 10 + pageNumber: 1 + pageSize: 10 + paged: true + sort: + empty: true + sorted: false + unsorted: true + unpaged: false + size: 10 + sort: + empty: true + sorted: false + unsorted: true + totalElements: 11 + totalPages: 2 job-handle: value: id: 6c311d29-5753-46d4-b32c-19b918ea93b0 @@ -2331,6 +2452,39 @@ components: lexicalValue: type: string example: piece + Page: + type: object + additionalProperties: false + properties: + content: + type: array + items: + type: object + empty: + type: boolean + first: + type: boolean + last: + type: boolean + number: + type: integer + format: int32 + numberOfElements: + type: integer + format: int32 + pageable: + $ref: '#/components/schemas/PageableObject' + size: + type: integer + format: int32 + sort: + $ref: '#/components/schemas/SortObject' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + format: int32 PageResult: type: object additionalProperties: false @@ -2354,6 +2508,25 @@ components: totalElements: type: integer format: int32 + PageableObject: + type: object + additionalProperties: false + properties: + offset: + type: integer + format: int64 + pageNumber: + type: integer + format: int32 + pageSize: + type: integer + format: int32 + paged: + type: boolean + sort: + $ref: '#/components/schemas/SortObject' + unpaged: + type: boolean PartChainIdentificationKey: type: object additionalProperties: false @@ -2757,6 +2930,16 @@ components: type: string payload: $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + SortObject: + type: object + additionalProperties: false + properties: + empty: + type: boolean + sorted: + type: boolean + unsorted: + type: boolean Submodel: type: object additionalProperties: false diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java index 947086e23b..9b2c8981cc 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java @@ -23,7 +23,13 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.configuration; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.ACTIVE_MEMBERSHIP; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.PURPOSE_ID_3_1_TRACE; + +import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; @@ -66,11 +72,18 @@ import org.eclipse.tractusx.irs.component.enums.ProcessStep; import org.eclipse.tractusx.irs.component.enums.ProcessingState; import org.eclipse.tractusx.irs.dtos.ErrorResponse; +import org.eclipse.tractusx.irs.edc.client.policy.Constraints; +import org.eclipse.tractusx.irs.edc.client.policy.Permission; +import org.eclipse.tractusx.irs.edc.client.policy.Policy; +import org.eclipse.tractusx.irs.edc.client.policy.PolicyType; import org.eclipse.tractusx.irs.ess.service.NotificationSummary; +import org.eclipse.tractusx.irs.policystore.models.PolicyWithBpn; import org.eclipse.tractusx.irs.semanticshub.AspectModel; import org.eclipse.tractusx.irs.semanticshub.AspectModels; import org.eclipse.tractusx.irs.util.JsonUtil; import org.springframework.beans.support.PagedListHolder; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpStatus; /** @@ -139,6 +152,28 @@ public void createExamples(final Components components) { components.addExamples("canceled-job-response", createCanceledJobResponse()); components.addExamples("complete-job-list-processing-state", createJobListProcessingState()); components.addExamples("aspect-models-list", createAspectModelsResult()); + components.addExamples("get-policies-paged-result", createPageOfPolicies()); + } + + private Example createPageOfPolicies() { + final PageImpl page = new PageImpl<>(List.of(new PolicyWithBpn("BPNL1234567890AB", + Policy.builder() + .policyId("13a8523f-c80a-40b8-9e43-faab47fbceaa") + .createdOn(OffsetDateTime.parse("2024-07-08T12:01:19.4677109+02:00")) + .validUntil(OffsetDateTime.parse("2025-07-08T12:01:19.4677109+02:00")) + .permissions(createPermissions()) + .build())), PageRequest.of(1, 10), 1); + return toExample(page); + } + + private List createPermissions() { + return List.of(new Permission(PolicyType.USE, createConstraints()), + new Permission(PolicyType.ACCESS, createConstraints())); + } + + private Constraints createConstraints() { + return new Constraints(Collections.emptyList(), + List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE, PURPOSE_ID_3_1_TRACE)); } private Example createAspectModelsResult() { @@ -394,22 +429,24 @@ private Tombstone createTombstone() { } private Shell createShell() { - return new Shell(EXAMPLE_ID, - AssetAdministrationShellDescriptor.builder() - .description(List.of(LangString.builder() - .language("en") - .text("The shell for a vehicle") - .build())) - .globalAssetId("urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9") - .idShort("future concept x") - .id("urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8") - .specificAssetIds(List.of(IdentifierKeyValuePair.builder() - .name("engineserialid") - .value("12309481209312") - .build())) - .submodelDescriptors(List.of(createBaseSubmodelDescriptor(), - createPartSubmodelDescriptor())) - .build()); + return new Shell(EXAMPLE_ID, AssetAdministrationShellDescriptor.builder() + .description(List.of(LangString.builder() + .language("en") + .text("The shell for a vehicle") + .build())) + .globalAssetId( + "urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9") + .idShort("future concept x") + .id("urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8") + .specificAssetIds( + List.of(IdentifierKeyValuePair.builder() + .name("engineserialid") + .value("12309481209312") + .build())) + .submodelDescriptors( + List.of(createBaseSubmodelDescriptor(), + createPartSubmodelDescriptor())) + .build()); } private Relationship createRelationship() { 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 72014e26d5..51137c6717 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 @@ -102,6 +102,8 @@ public class PolicyStoreController { public static final int DEFAULT_PAGE_SIZE = 10; public static final int MAX_PAGE_SIZE = 1000; public static final String SEARCH = "search"; + public static final String POLICY_API_TAG = "Item Relationship Service - Policy API"; + public static final String API_KEY = "api_key"; private final PolicyStoreService service; @@ -111,7 +113,7 @@ public class PolicyStoreController { @Operation(operationId = "registerAllowedPolicy", summary = "Register a policy that should be accepted in EDC negotiation.", - security = @SecurityRequirement(name = "api_key"), tags = { "Item Relationship Service" }, + security = @SecurityRequirement(name = API_KEY), tags = { POLICY_API_TAG }, description = "Register a policy that should be accepted in EDC negotiation.") @ApiResponses(value = { @ApiResponse(responseCode = "201"), @ApiResponse(responseCode = "500", @@ -162,7 +164,7 @@ public CreatePoliciesResponse registerAllowedPolicy(@Valid @RequestBody final Cr @Operation(operationId = "getAllowedPoliciesByBpn", summary = "Lists the registered policies that should be accepted in EDC negotiation.", - security = @SecurityRequirement(name = "api_key"), tags = { "Item Relationship Service" }, + security = @SecurityRequirement(name = API_KEY), tags = { POLICY_API_TAG }, description = "Lists the registered policies that should be accepted in EDC negotiation.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returns the policies as map of BPN to list of policies.", @@ -208,16 +210,54 @@ public Map> getPolicies(// .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); } - // TODO (mfischer): #639: add documentation and insomnia collection @GetMapping("/policies/paged") @ResponseStatus(HttpStatus.OK) + @Operation(summary = "Find policies.", // + description = """ + Fetch a page of policies with options to filter and sort. + + - **Filtering:** + `search=,[EQUALS|STARTS_WITH|BEFORE_LOCAL_DATE|AFTER_LOCAL_DATE],`. + Example: 'search=BPN,STARTS_WITH,BPNL12&search=policyId,STARTS_WITH,policy2'. + + - **Sorting:** + `sort=,[asc|desc]`. + Example: 'sort=BPN,asc&sort=policyId,desc'. + + - **Paging:** + Example: `page=1&size=20` + """,// + security = @SecurityRequirement(name = API_KEY), // + tags = { POLICY_API_TAG }, // + responses = { // + @ApiResponse(responseCode = "200", + description = "Successfully retrieved the paged policies", + content = @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Page.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/get-policies-paged-result"))), + + @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")) + }), + }) @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')") public Page getPoliciesPaged(// @PageableDefault(size = DEFAULT_PAGE_SIZE, sort = PROPERTY_BPN, direction = Sort.Direction.ASC) // + @Parameter(description = "Page configuration", hidden = true) // final Pageable pageable, // @RequestParam(required = false) // @ValidListOfBusinessPartnerNumbers // - @Parameter(description = "List of business partner numbers.") // + @Parameter(name = "businessPartnerNumbers", description = "List of business partner numbers.") // final List businessPartnerNumbers) { if (pageable.getPageSize() > MAX_PAGE_SIZE) { @@ -252,7 +292,7 @@ private static void ensureParamBusinessPartnerNumberCorrectlyNamed(final Map Date: Mon, 8 Jul 2024 14:49:20 +0200 Subject: [PATCH 3/5] feat(policy-api):[#639] fix checkstyle error --- .../irs/policystore/controllers/PolicyStoreController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 51137c6717..ea203fa4b4 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 @@ -226,7 +226,7 @@ public Map> getPolicies(// - **Paging:** Example: `page=1&size=20` - """,// + """, // security = @SecurityRequirement(name = API_KEY), // tags = { POLICY_API_TAG }, // responses = { // From d06fed68f0742410fc0667b4536fa5fc142712b8 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 8 Jul 2024 19:40:14 +0200 Subject: [PATCH 4/5] feat(policy-api):[#639] rename heading in OpenAPI --- docs/src/api/irs-api.yaml | 12 ++++++------ .../controllers/PolicyStoreController.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index 5301517c47..ad01c467e1 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -860,7 +860,7 @@ paths: - api_key: [] summary: Lists the registered policies that should be accepted in EDC negotiation. tags: - - Item Relationship Service - Policy API + - Policy Store API post: description: Register a policy that should be accepted in EDC negotiation. operationId: registerAllowedPolicy @@ -917,7 +917,7 @@ paths: - api_key: [] summary: Register a policy that should be accepted in EDC negotiation. tags: - - Item Relationship Service - Policy API + - Policy Store API put: description: Updates existing policies. operationId: updateAllowedPolicy @@ -970,7 +970,7 @@ paths: - api_key: [] summary: Updates existing policies. tags: - - Item Relationship Service - Policy API + - Policy Store API /irs/policies/paged: get: description: | @@ -1028,7 +1028,7 @@ paths: - api_key: [ ] summary: Find policies. tags: - - Item Relationship Service - Policy API + - Policy Store API /irs/policies/{policyId}: delete: description: Removes a policy that should no longer be accepted in EDC negotiation. @@ -1073,7 +1073,7 @@ paths: - api_key: [] summary: Removes a policy that should no longer be accepted in EDC negotiation. tags: - - Item Relationship Service - Policy API + - Policy Store API /irs/policies/{policyId}/bpnl/{bpnl}: delete: description: Removes a policy from BPNL that should no longer be accepted in @@ -1126,7 +1126,7 @@ paths: summary: Removes a policy from BPNL that should no longer be accepted in EDC negotiation. tags: - - Item Relationship Service - Policy API + - Policy Store API components: examples: aspect-models-list: 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 ea203fa4b4..abb43ba73f 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 @@ -102,7 +102,7 @@ public class PolicyStoreController { public static final int DEFAULT_PAGE_SIZE = 10; public static final int MAX_PAGE_SIZE = 1000; public static final String SEARCH = "search"; - public static final String POLICY_API_TAG = "Item Relationship Service - Policy API"; + public static final String POLICY_API_TAG = "Policy Store API"; public static final String API_KEY = "api_key"; private final PolicyStoreService service; From 865001bd6cd67207ad550e85be106e3f787db9a6 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 8 Jul 2024 23:42:02 +0200 Subject: [PATCH 5/5] feat(policy-api):[#639] update CHANGELOG.md --- CHANGELOG.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f591855c..048f414a85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,12 +36,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha - Integration Test Policy Store API Unhappy Path. #519 - Support for SingleLevelUsageAsPlanned. #470 - Documentation to describe the delegate process. #470 -- Added file for CC BY 4.0 license for TRG 7. #681 -- Paging for Policy Store API GET policies. #639 - - New endpoint GET /irs/policies/paged. - - Supports sorting by multiple of the properties "bpn", "validUntil", "policyId", "createdOn", "action" with ascending / descending order. - - Supports filtering by multiple of properties "bpn", "validUntil", "policyId" (AND). - - note: filtering by "createdOn", "validUntil" has not been implemted yet +- Added file for CC BY 4.0 license for TRG 7 #681 +- Paging endpoint for Policy Store API: `GET /irs/policies/paged`. #639 + - Supports multi-sort for the properties "bpn", "validUntil", "policyId", "createdOn", "action" with ascending / descending order. + - Supports AND-connected multi-filtering by the properties "bpn", "validUntil", "policyId". + - Note: filtering by "createdOn", "validUntil" will be implemented in a subsequent story. ## [5.1.4] - 2024-05-27