diff options
author | ToineSiebelink <toine.siebelink@est.tech> | 2024-07-29 17:45:52 +0100 |
---|---|---|
committer | ToineSiebelink <toine.siebelink@est.tech> | 2024-07-31 17:18:58 +0100 |
commit | 53375821fa2d156785a92ef57ef7948389260cc1 (patch) | |
tree | f7b63a0db182791f3fa2b73ea97355e692939e41 /policy-executor-stub | |
parent | 9d8e6852b2ebfffca8204557a005aba27c1fc581 (diff) |
Refactor OpenAPI Policy Executor
- replace payload with request(s)
- replace payloadType with schema (one schema for each operation)
- include conflict error response in OpenAPI
- introduce 4 schemas in NCMP (doc module) for create, update, patch & delete
- udpate stub & test to follow new API and use one schema for testign purposes
Issue-ID: CPS-2335
Change-Id: Ifc40062ae83429a9ffba350ec3bcc28cb7147293
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Diffstat (limited to 'policy-executor-stub')
3 files changed, 109 insertions, 39 deletions
diff --git a/policy-executor-stub/pom.xml b/policy-executor-stub/pom.xml index f076a2c55c..afdc1c7d3b 100644 --- a/policy-executor-stub/pom.xml +++ b/policy-executor-stub/pom.xml @@ -22,6 +22,11 @@ </properties> <dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency> <!-- S P R I N G D E P E N D E N C I E S --> <dependency> <groupId>org.springframework.boot</groupId> @@ -163,6 +168,20 @@ </execution> </executions> </plugin> + + <plugin> + <groupId>org.jsonschema2pojo</groupId> + <artifactId>jsonschema2pojo-maven-plugin</artifactId> + <configuration> + <useJakartaValidation>true</useJakartaValidation> + <sourceDirectory>${project.parent.basedir}/../docs/schemas/policy-executor</sourceDirectory> + <targetPackage>org.onap.cps.policyexecutor.stub.model</targetPackage> + <generateBuilders>true</generateBuilders> + <serializable>true</serializable> + <includeJsr303Annotations>true</includeJsr303Annotations> + </configuration> + </plugin> + </plugins> </build> diff --git a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java index a5ec6dcac9..5b3a9931a0 100644 --- a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java +++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java @@ -20,12 +20,17 @@ package org.onap.cps.policyexecutor.stub.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; +import lombok.RequiredArgsConstructor; import org.onap.cps.policyexecutor.stub.api.PolicyExecutorApi; +import org.onap.cps.policyexecutor.stub.model.NcmpDelete; import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest; import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse; +import org.onap.cps.policyexecutor.stub.model.Request; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; @@ -34,9 +39,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${rest.api.policy-executor-base-path}") +@RequiredArgsConstructor public class PolicyExecutorStubController implements PolicyExecutorApi { - private final Pattern errorCodePattern = Pattern.compile("(\\d{3})"); + private final ObjectMapper objectMapper; + + private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("(\\d{3})"); + private int decisionCounter = 0; @Override @@ -44,31 +53,55 @@ public class PolicyExecutorStubController implements PolicyExecutorApi { final String action, final PolicyExecutionRequest policyExecutionRequest, final String authorization) { - if (policyExecutionRequest.getPayload().isEmpty()) { + if (policyExecutionRequest.getRequests().isEmpty()) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + final Request firstRequest = policyExecutionRequest.getRequests().iterator().next(); + if ("ncmp-delete-schema:1.0.0".equals(firstRequest.getSchema())) { + return handleNcmpDeleteSchema(firstRequest); + } + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + private ResponseEntity<PolicyExecutionResponse> handleNcmpDeleteSchema(final Request request) { + final NcmpDelete ncmpDelete; + try { + ncmpDelete = objectMapper.readValue((String) request.getData(), NcmpDelete.class); + } catch (final JsonProcessingException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } - final String firstTargetFdn = policyExecutionRequest.getPayload().iterator().next().getTargetFdn(); + final String targetIdentifier = ncmpDelete.getTargetIdentifier(); + if (targetIdentifier == null) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } - final Matcher matcher = errorCodePattern.matcher(firstTargetFdn); + final Matcher matcher = ERROR_CODE_PATTERN.matcher(targetIdentifier); if (matcher.find()) { final int errorCode = Integer.parseInt(matcher.group(1)); return new ResponseEntity<>(HttpStatusCode.valueOf(errorCode)); } + return createPolicyExecutionResponse(targetIdentifier); + } + + private ResponseEntity<PolicyExecutionResponse> createPolicyExecutionResponse(final String targetIdentifier) { final String decisionId = String.valueOf(++decisionCounter); final String decision; final String message; - if (firstTargetFdn.toLowerCase(Locale.getDefault()).contains("cps-is-great")) { - decision = "permit"; + if (targetIdentifier.toLowerCase(Locale.getDefault()).contains("cps-is-great")) { + decision = "allow"; message = "All good"; } else { decision = "deny"; - message = "Only FDNs containing 'cps-is-great' are permitted"; + message = "Only FDNs containing 'cps-is-great' are allowed"; } + final PolicyExecutionResponse policyExecutionResponse = new PolicyExecutionResponse(decisionId, decision, message); + return ResponseEntity.ok(policyExecutionResponse); } + } diff --git a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy index 871db81ac8..efb12ac619 100644 --- a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy +++ b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy @@ -21,9 +21,10 @@ package org.onap.cps.policyexecutor.stub.controller import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.policyexecutor.stub.model.Payload +import org.onap.cps.policyexecutor.stub.model.NcmpDelete import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse +import org.onap.cps.policyexecutor.stub.model.Request import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.http.HttpStatus @@ -44,11 +45,9 @@ class PolicyExecutorStubControllerSpec extends Specification { def url = '/policy-executor/api/v1/some-action' - def 'Execute Policy Actions.'() { - given: 'a policy execution request with target fdn: #targetFdn' - def payload = new Payload(targetFdn, 'some change request') - def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload]) - def requestBody = objectMapper.writeValueAsString(policyExecutionRequest) + def 'Execute policy action.'() { + given: 'a policy execution request with target: #targetIdentifier' + def requestBody = createRequestBody(targetIdentifier) when: 'request is posted' def response = mockMvc.perform(post(url) .header('Authorization','some string') @@ -61,19 +60,17 @@ class PolicyExecutorStubControllerSpec extends Specification { def responseBody = response.contentAsString def policyExecutionResponse = objectMapper.readValue(responseBody, PolicyExecutionResponse.class) assert policyExecutionResponse.decisionId == expectedDecsisonId - assert policyExecutionResponse.decision == expectedDecsison + assert policyExecutionResponse.decision == expectedDecision assert policyExecutionResponse.message == expectedMessage where: 'the following targets are used' - targetFdn || expectedDecsisonId | expectedDecsison | expectedMessage - 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are permitted" - 'fdn with cps-is-great' || '2' | 'permit' | "All good" + targetIdentifier || expectedDecsisonId | expectedDecision | expectedMessage + 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are allowed" + 'fdn with cps-is-great' || '2' | 'allow' | 'All good' } - def 'Execute Policy Action with a HTTP Error Code.'() { + def 'Execute policy action with a HTTP error code.'() { given: 'a policy execution request with a target fdn with a 3-digit error code' - def payload = new Payload('fdn with error code 418', 'some change request') - def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload]) - def requestBody = objectMapper.writeValueAsString(policyExecutionRequest) + def requestBody = createRequestBody('target with error code 418') when: 'request is posted' def response = mockMvc.perform(post(url) .header('Authorization','some string') @@ -84,11 +81,9 @@ class PolicyExecutorStubControllerSpec extends Specification { assert response.status == 418 } - def 'Execute Policy Action without Authorization Header.'() { + def 'Execute policy action without authorization header.'() { given: 'a valid policy execution request' - def payload = new Payload('some fdn', 'some change request') - def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload]) - def requestBody = objectMapper.writeValueAsString(policyExecutionRequest) + def requestBody = createRequestBody('some target') when: 'request is posted without authorization header' def response = mockMvc.perform(post(url) .contentType(MediaType.APPLICATION_JSON) @@ -98,9 +93,9 @@ class PolicyExecutorStubControllerSpec extends Specification { assert response.status == HttpStatus.OK.value() } - def 'Execute Policy Action with Empty Payload.'() { - given: 'a policy execution request with empty payload list' - def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', []) + def 'Execute policy action with no requests.'() { + given: 'a policy execution request' + def policyExecutionRequest = new PolicyExecutionRequest('some decision type', []) def requestBody = objectMapper.writeValueAsString(policyExecutionRequest) when: 'request is posted' def response = mockMvc.perform(post(url) @@ -112,26 +107,49 @@ class PolicyExecutorStubControllerSpec extends Specification { assert response.status == HttpStatus.BAD_REQUEST.value() } - def 'Execute Policy Action without other required attributes.'() { - given: 'a policy execution request with payloadType=#payloadType, decisionType=decisionType, targetFdn=#targetFdn, changeRequest=#changeRequest' - def payload = new Payload(targetFdn, changeRequest) - def policyExecutionRequest = new PolicyExecutionRequest(payloadType, decisionType, [payload]) + def 'Execute policy action with invalid json for request data.'() { + given: 'a policy execution request' + def request = new Request('ncmp-delete-schema:1.0.0', 'invalid json') + def policyExecutionRequest = new PolicyExecutionRequest('some decision type', [request]) def requestBody = objectMapper.writeValueAsString(policyExecutionRequest) when: 'request is posted' def response = mockMvc.perform(post(url) + .header('Authorization','some string') + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andReturn().response + then: 'response status is Bad Request' + assert response.status == HttpStatus.BAD_REQUEST.value() + } + + def 'Execute policy action with missing or invalid attributes.'() { + given: 'a policy execution request with decisionType=#decisionType, schema=#schema, targetIdentifier=#targetIdentifier' + def requestBody = createRequestBody(decisionType, schema, targetIdentifier) + when: 'request is posted' + def response = mockMvc.perform(post(url) .header('Authorization','something') .contentType(MediaType.APPLICATION_JSON) .content(requestBody)) .andReturn().response then: 'response status as expected' assert response.status == expectedStatus.value() - where: 'following parameters are populated or not' - payloadType | decisionType | targetFdn | changeRequest || expectedStatus - 'something' | 'something' | 'something' | 'something' || HttpStatus.OK - null | 'something' | 'something' | 'something' || HttpStatus.BAD_REQUEST - 'something' | null | 'something' | 'something' || HttpStatus.BAD_REQUEST - 'something' | 'something' | null | 'something' || HttpStatus.BAD_REQUEST - 'something' | 'something' | 'something' | null || HttpStatus.BAD_REQUEST + where: 'following parameters are used' + decisionType | schema | targetIdentifier || expectedStatus + 'something' | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.OK + null | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.BAD_REQUEST + 'something' | 'other schema' | 'something' || HttpStatus.BAD_REQUEST + 'something' | 'ncmp-delete-schema:1.0.0' | null || HttpStatus.BAD_REQUEST + } + + def createRequestBody(decisionType, schema, targetIdentifier) { + def ncmpDelete = new NcmpDelete(targetIdentifier: targetIdentifier) + def request = new Request(schema, objectMapper.writeValueAsString(ncmpDelete)) + def policyExecutionRequest = new PolicyExecutionRequest(decisionType, [request]) + return objectMapper.writeValueAsString(policyExecutionRequest) + } + + def createRequestBody(targetIdentifier) { + return createRequestBody('some decision type', 'ncmp-delete-schema:1.0.0', targetIdentifier) } } |