aboutsummaryrefslogtreecommitdiffstats
path: root/policy-executor-stub/src
diff options
context:
space:
mode:
Diffstat (limited to 'policy-executor-stub/src')
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/PolicyExecutorApplication.java31
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java112
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java35
-rw-r--r--policy-executor-stub/src/main/resources/application.yml19
-rw-r--r--policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/PolicyExecutorApplicationSpec.groovy34
-rw-r--r--policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy173
6 files changed, 404 insertions, 0 deletions
diff --git a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/PolicyExecutorApplication.java b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/PolicyExecutorApplication.java
new file mode 100644
index 0000000000..367a470e2d
--- /dev/null
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/PolicyExecutorApplication.java
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.policyexecutor.stub;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class PolicyExecutorApplication {
+ public static void main(final String[] args) {
+ SpringApplication.run(PolicyExecutorApplication.class, args);
+ }
+}
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
new file mode 100644
index 0000000000..88073c0a0f
--- /dev/null
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
@@ -0,0 +1,112 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.policyexecutor.stub.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequiredArgsConstructor
+@Slf4j
+public class PolicyExecutorStubController implements PolicyExecutorApi {
+
+ private final Sleeper sleeper;
+ private final ObjectMapper objectMapper;
+ private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("(\\d{3})");
+ private int decisionCounter = 0;
+ private static int slowResponseTimeInSeconds = 40;
+
+ @Override
+ public ResponseEntity<PolicyExecutionResponse> executePolicyAction(
+ final String action,
+ final PolicyExecutionRequest policyExecutionRequest,
+ final String authorization) {
+ log.info("Stub Policy Executor Invoked (only supports 'delete' operations)");
+ if (policyExecutionRequest.getRequests().isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+ final Request firstRequest = policyExecutionRequest.getRequests().iterator().next();
+ log.info("1st Request Schema:{}", firstRequest.getSchema());
+ if (firstRequest.getSchema().contains("ncmp-delete-schema:1.0.0")) {
+ return handleNcmpDeleteSchema(firstRequest);
+ }
+ log.warn("This stub only supports 'delete' operations");
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ private ResponseEntity<PolicyExecutionResponse> handleNcmpDeleteSchema(final Request request) {
+ final NcmpDelete ncmpDelete = objectMapper.convertValue(request.getData(), NcmpDelete.class);
+
+ final String targetIdentifier = ncmpDelete.getTargetIdentifier();
+
+ if (targetIdentifier == null) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ 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 (targetIdentifier.toLowerCase(Locale.getDefault()).contains("slow")) {
+ try {
+ sleeper.haveALittleRest(slowResponseTimeInSeconds);
+ } catch (final InterruptedException e) {
+ log.trace("Sleep interrupted, re-interrupting the thread");
+ Thread.currentThread().interrupt(); // Re-interrupt the thread
+ }
+ }
+ 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 allowed";
+ }
+ log.info("Decision: {} ({})", decision, message);
+ final PolicyExecutionResponse policyExecutionResponse =
+ new PolicyExecutionResponse(decisionId, decision, message);
+
+ return ResponseEntity.ok(policyExecutionResponse);
+ }
+
+}
diff --git a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java
new file mode 100644
index 0000000000..789201ffce
--- /dev/null
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java
@@ -0,0 +1,35 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.policyexecutor.stub.controller;
+
+import java.util.concurrent.TimeUnit;
+import org.springframework.stereotype.Service;
+
+/**
+ * This class is to extract out sleep functionality so the interrupted exception handling can
+ * be covered with a test (e.g. using spy on Sleeper) and help to get too 100% code coverage.
+ */
+@Service
+public class Sleeper {
+ public void haveALittleRest(final int timeInSeconds) throws InterruptedException {
+ TimeUnit.SECONDS.sleep(timeInSeconds);
+ }
+}
diff --git a/policy-executor-stub/src/main/resources/application.yml b/policy-executor-stub/src/main/resources/application.yml
new file mode 100644
index 0000000000..8ed5742a41
--- /dev/null
+++ b/policy-executor-stub/src/main/resources/application.yml
@@ -0,0 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2024 Nordix
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://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
+# ============LICENSE_END=========================================================
+
+server:
+ port: 8093
diff --git a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/PolicyExecutorApplicationSpec.groovy b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/PolicyExecutorApplicationSpec.groovy
new file mode 100644
index 0000000000..565932d9f0
--- /dev/null
+++ b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/PolicyExecutorApplicationSpec.groovy
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.policyexecutor.stub
+
+import spock.lang.Specification
+
+class PolicyExecutorApplicationSpec extends Specification {
+
+ def 'Execute Policy Action.'() {
+ when: 'Starting the application (for coverage)'
+ PolicyExecutorApplication.main()
+ then: 'all goes well'
+ noExceptionThrown()
+ }
+
+}
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
new file mode 100644
index 0000000000..44460daa7e
--- /dev/null
+++ b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
@@ -0,0 +1,173 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.policyexecutor.stub.controller
+
+import com.fasterxml.jackson.databind.ObjectMapper
+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.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.springframework.test.web.servlet.MockMvc
+import spock.lang.Specification
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+
+@WebMvcTest(PolicyExecutorStubController)
+class PolicyExecutorStubControllerSpec extends Specification {
+
+ @Autowired
+ MockMvc mockMvc
+
+ @Autowired
+ ObjectMapper objectMapper
+
+ @SpringBean
+ Sleeper sleeper = Spy()
+
+ def url = '/policy-executor/api/v1/some-action'
+
+ def setup() {
+ PolicyExecutorStubController.slowResponseTimeInSeconds = 1
+ }
+
+ 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')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody))
+ .andReturn().response
+ then: 'response status is Ok'
+ assert response.status == HttpStatus.OK.value()
+ and: 'the response body has the expected decision details'
+ def responseBody = response.contentAsString
+ def policyExecutionResponse = objectMapper.readValue(responseBody, PolicyExecutionResponse.class)
+ assert policyExecutionResponse.decisionId == expectedDecsisonId
+ assert policyExecutionResponse.decision == expectedDecision
+ assert policyExecutionResponse.message == expectedMessage
+ where: 'the following targets are used'
+ 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'
+ 'slow' || '3' | 'deny' | "Only FDNs containing 'cps-is-great' are allowed"
+ }
+
+ 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 requestBody = createRequestBody('target with error code 418')
+ 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 the same error code as in target fdn'
+ assert response.status == 418
+ }
+
+ def 'Execute policy action without authorization header.'() {
+ given: 'a valid policy execution request'
+ def requestBody = createRequestBody('some target')
+ when: 'request is posted without authorization header'
+ def response = mockMvc.perform(post(url)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody))
+ .andReturn().response
+ then: 'response status is OK'
+ assert response.status == HttpStatus.OK.value()
+ }
+
+ 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)
+ .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 invalid json for request data.'() {
+ when: 'request is posted'
+ def response = mockMvc.perform(post(url)
+ .header('Authorization','some string')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('invalid json'))
+ .andReturn().response
+ then: 'response status is Bad Request'
+ assert response.status == HttpStatus.BAD_REQUEST.value()
+ }
+
+ def 'Execute policy action with interrupted exception during slow response.'() {
+ given: 'a policy execution request with target: "slow"'
+ def requestBody = createRequestBody('slow')
+ sleeper.haveALittleRest(_) >> { throw new InterruptedException() }
+ when: 'request is posted'
+ mockMvc.perform(post(url)
+ .header('Authorization','some string')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody))
+ then: 'response status is Bad Request'
+ noExceptionThrown()
+ }
+
+ 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 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, 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)
+ }
+
+}