Skip to content

Commit

Permalink
feat(policy-api): [eclipse-tractusx#802] get policies paged as POST r…
Browse files Browse the repository at this point in the history
…equest
  • Loading branch information
dsmf committed Jul 16, 2024
1 parent 5ca63e5 commit 6d83a51
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 6 deletions.
89 changes: 89 additions & 0 deletions docs/src/api/irs-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,48 @@ paths:
summary: Find policies.
tags:
- Policy Store API
post:
description: Fetch a page of policies with options to filter and sort.
operationId: getPoliciesPagedPost
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PoliciesPagedRequest'
required: true
responses:
"200":
content:
application/json:
examples:
success:
$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:
- Policy Store API
/irs/policies/{policyId}:
delete:
description: Removes a policy that should no longer be accepted in EDC negotiation.
Expand Down Expand Up @@ -2619,6 +2661,53 @@ components:
required:
- bpn
- globalAssetId
PoliciesPagedRequest:
type: object
additionalProperties: false
description: Request to query policies
properties:
businessPartnerNumbers:
type: array
description: List of business partner numbers. This may also contain the
value "default" in order to query the default policies.
items:
type: string
description: List of business partner numbers. This may also contain the
value "default" in order to query the default policies.
page:
type: integer
format: int32
description: Page number
search:
type: array
description: |
Search parameters, each in the following form:
`<property>,[EQUALS|STARTS_WITH|BEFORE_LOCAL_DATE|AFTER_LOCAL_DATE],<value>`.
Example:
`["BPN,STARTS_WITH,BPNL12", "policyId,STARTS_WITH,policy2", "validUntil,AFTER_LOCAL_DATE,2024-06-05"]`.
items:
type: string
description: |
Search parameters, each in the following form:
`<property>,[EQUALS|STARTS_WITH|BEFORE_LOCAL_DATE|AFTER_LOCAL_DATE],<value>`.
Example:
`["BPN,STARTS_WITH,BPNL12", "policyId,STARTS_WITH,policy2", "validUntil,AFTER_LOCAL_DATE,2024-06-05"]`.
size:
type: integer
format: int32
description: Page size
sort:
type: array
description: |
Sort parameters, each in the following form:
`<property>,[asc|desc]`.
Example: `["BPN,asc", "policyId,desc"]`.
items:
type: string
description: |
Sort parameters, each in the following form:
`<property>,[asc|desc]`.
Example: `["BPN,asc", "policyId,desc"]`.
ProcessingError:
type: object
additionalProperties: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
* Common constants.
*/
public class CommonConstants {

public static final String PROPERTY_BPN = "bpn";
public static final String PROPERTY_POLICY_ID = "policyId";
public static final String PROPERTY_ACTION = "action";
public static final String PROPERTY_CREATED_ON = "createdOn";
public static final String PROPERTY_VALID_UNTIL = "validUntil";

public static final String PARAM_BUSINESS_PARTNER_NUMBERS = "businessPartnerNumbers";
public static final String PARAM_SEARCH = "search";
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

import static org.eclipse.tractusx.irs.common.ApiConstants.FORBIDDEN_DESC;
import static org.eclipse.tractusx.irs.common.ApiConstants.UNAUTHORIZED_DESC;
import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PARAM_BUSINESS_PARTNER_NUMBERS;
import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PARAM_SEARCH;
import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PROPERTY_BPN;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

Expand Down Expand Up @@ -54,9 +56,11 @@
import org.eclipse.tractusx.irs.data.JsonParseException;
import org.eclipse.tractusx.irs.dtos.ErrorResponse;
import org.eclipse.tractusx.irs.edc.client.policy.Policy;
import org.eclipse.tractusx.irs.policystore.common.CommonConstants;
import org.eclipse.tractusx.irs.policystore.common.SearchParameterParser;
import org.eclipse.tractusx.irs.policystore.models.CreatePoliciesResponse;
import org.eclipse.tractusx.irs.policystore.models.CreatePolicyRequest;
import org.eclipse.tractusx.irs.policystore.models.PoliciesPagedRequest;
import org.eclipse.tractusx.irs.policystore.models.PolicyResponse;
import org.eclipse.tractusx.irs.policystore.models.PolicyWithBpn;
import org.eclipse.tractusx.irs.policystore.models.SearchCriteria;
Expand Down Expand Up @@ -94,15 +98,16 @@
@RequiredArgsConstructor
@SuppressWarnings({ "PMD.AvoidDuplicateLiterals",
"PMD.ExcessiveImports",
"PMD.UseVarargs"
"PMD.UseVarargs",
"PMD.TooManyStaticImports"
// actually this improves readability here
})
@Validated
public class PolicyStoreController {

public static final String BPN_REGEX = BusinessPartnerNumberListValidator.BPN_REGEX;
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 = "Policy Store API";
public static final String API_KEY = "api_key";
public static final int MAX_AUTOCOMPLETE_LIMIT = 100;
Expand Down Expand Up @@ -307,31 +312,71 @@ public Page<PolicyResponse> getPoliciesPaged(//
final Pageable pageable, //
@RequestParam(required = false) //
@ValidListOfBusinessPartnerNumbers(allowDefault = true) //
@Parameter(name = "businessPartnerNumbers", description = "List of business partner numbers. "
@Parameter(name = PARAM_BUSINESS_PARTNER_NUMBERS, description = "List of business partner numbers. "
+ "This may also contain the value \"default\" in order to query the default policies.") //
@SuppressWarnings("unused") // needed for OpenAPI, not used directly as it is read from the parameter map
final List<String> businessPartnerNumbers) {
return doGetPoliciesPaged(pageable, this.httpServletRequest.getParameterMap());
}

@PostMapping("/policies/paged")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Find policies.", //
description = "Fetch a page of policies with options to filter and sort.", //
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 = "success",
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<PolicyResponse> getPoliciesPagedPost(@Validated @RequestBody final PoliciesPagedRequest body) {
return doGetPoliciesPaged(body.getPageable(), body.getParameterMap());
}

private Page<PolicyResponse> doGetPoliciesPaged(final Pageable pageable, final Map<String, String[]> parameterMap) {

if (pageable.getPageSize() > MAX_PAGE_SIZE) {
throw new IllegalArgumentException("Page size too large");
}

final Map<String, String[]> parameterMap = this.httpServletRequest.getParameterMap();

ensureParamBusinessPartnerNumberCorrectlyNamed(parameterMap);

// There seems to be a bug concerning interpretation of delimiters
// (https://stackoverflow.com/questions/37058691/encoded-comma-in-url-is-read-as-list-in-spring).
// The described annotation Delimiter did not work either. Therefore, we read the params manually from request:
final List<SearchCriteria<?>> searchCriteria = new SearchParameterParser(
getSearchParameters(parameterMap)).getSearchCriteria();
final String[] arrBusinessPartnerNumbers = parameterMap.get(PARAM_BUSINESS_PARTNER_NUMBERS);
final List<String> businessPartnerNumbers =
arrBusinessPartnerNumbers != null ? Arrays.asList(arrBusinessPartnerNumbers) : null;
final Map<String, List<Policy>> bpnToPoliciesMap = service.getPolicies(businessPartnerNumbers);
final Page<PolicyWithBpn> policies = policyPagingService.getPolicies(bpnToPoliciesMap, pageable,
searchCriteria);
return policies.map(policyWithBpn -> PolicyResponse.from(policyWithBpn.policy(), policyWithBpn.bpn()));
}

private List<String> getSearchParameters(final Map<String, String[]> parameterMap) {
return parameterMap.get(SEARCH) != null ? Arrays.asList(parameterMap.get(SEARCH)) : Collections.emptyList();
return parameterMap.get(PARAM_SEARCH) != null
? Arrays.asList(parameterMap.get(PARAM_SEARCH))
: Collections.emptyList();
}

private static void ensureParamBusinessPartnerNumberCorrectlyNamed(final Map<String, String[]> parameterMap) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/********************************************************************************
* Copyright (c) 2022,2024
* 2022: ZF Friedrichshafen AG
* 2022: ISTOS GmbH
* 2022,2024: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
* 2022,2023: BOSCH AG
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
package org.eclipse.tractusx.irs.policystore.models;

import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PARAM_BUSINESS_PARTNER_NUMBERS;
import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PARAM_SEARCH;
import static org.eclipse.tractusx.irs.policystore.common.CommonConstants.PROPERTY_BPN;
import static org.eclipse.tractusx.irs.policystore.controllers.PolicyStoreController.DEFAULT_PAGE_SIZE;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.tractusx.irs.policystore.validators.ValidListOfBusinessPartnerNumbers;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.validation.annotation.Validated;

/**
* Object for API to create policy
*/
@SuppressWarnings("FileTabCharacter")
@Schema(description = "Request to query policies")
@NoArgsConstructor
@Data
@Validated
public class PoliciesPagedRequest {

public static final int SORT_PARTS = 2;

@Schema(description = "Page number")
private int page;

@Schema(description = "Page size")
private int size = DEFAULT_PAGE_SIZE;

@Schema(description = """
Search parameters, each in the following form:
`<property>,[EQUALS|STARTS_WITH|BEFORE_LOCAL_DATE|AFTER_LOCAL_DATE],<value>`.
Example:
`["BPN,STARTS_WITH,BPNL12", "policyId,STARTS_WITH,policy2", "validUntil,AFTER_LOCAL_DATE,2024-06-05"]`.
""")
private List<String> search;

@Schema(description = """
Sort parameters, each in the following form:
`<property>,[asc|desc]`.
Example: `["BPN,asc", "policyId,desc"]`.
""")
private List<String> sort;

@ValidListOfBusinessPartnerNumbers(allowDefault = true) //
@Schema(name = PARAM_BUSINESS_PARTNER_NUMBERS, description = "List of business partner numbers. "
+ "This may also contain the value \"default\" in order to query the default policies.") //
private List<String> businessPartnerNumbers;

@JsonIgnore
public Sort parseSortParameters() {
Sort sortObj = null;
if (sort != null) {
for (final String sortParam : sort) {
final String[] parts = sortParam.split(",");
if (parts.length == SORT_PARTS) {
final String property = parts[0];
final Sort.Direction direction = Sort.Direction.fromString(parts[1]);
if (sortObj == null || sortObj.isUnsorted()) {
sortObj = Sort.by(direction, property);
} else {
sortObj = sortObj.and(Sort.by(direction, property));
}
}
}
}
return sortObj != null ? sortObj : Sort.by(Sort.Direction.ASC, PROPERTY_BPN);
}

@JsonIgnore
public PageRequest getPageable() {
return PageRequest.of(getPage(), getSize(), parseSortParameters());
}

@JsonIgnore
public Map<String, String[]> getParameterMap() {

final Map<String, String[]> parameterMap = new ConcurrentHashMap<>();

if (getBusinessPartnerNumbers() != null) {
parameterMap.put(PARAM_BUSINESS_PARTNER_NUMBERS, getBusinessPartnerNumbers().toArray(new String[0]));
}

if (getSearch() != null) {
parameterMap.put(PARAM_SEARCH, getSearch().toArray(new String[0]));
}

return parameterMap;
}

}

0 comments on commit 6d83a51

Please sign in to comment.