From: aravind.est Date: Fri, 16 Feb 2024 17:16:09 +0000 (+0000) Subject: A1-PMS Add A1-Mediator Custom Adapter (Release I) X-Git-Tag: 2.8.0~6 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=8f3c66ffb336346c13919f67d57aa57cd4221b9c;p=nonrtric%2Fplt%2Fa1policymanagementservice.git A1-PMS Add A1-Mediator Custom Adapter (Release I) Change-Id: Ic984c1498b5078869885a7151ee7999bbe2f232c Issue-ID: NONRTRIC-978 Signed-off-by: aravind.est --- diff --git a/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterI.java b/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterI.java new file mode 100644 index 0000000..145a823 --- /dev/null +++ b/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterI.java @@ -0,0 +1,139 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved. + * %% + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.clients; + +import static org.onap.ccsdk.oran.a1policymanagementservice.clients.OscA1Client.extractCreateSchema; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Set; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Client for accessing OSC A1-Mediator A1-P Version (Release I) + */ +@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally +public class A1MediatorAdapterI implements A1Client { + static final int CONCURRENCY_RIC = 1; // How many parallel requests that is sent to one NearRT RIC + + public static class Factory implements A1Client.Factory { + @Override + public A1Client create(RicConfig ricConfig, AsyncRestClientFactory restClientFactory) { + return new A1MediatorAdapterI(ricConfig, restClientFactory); + } + } + + public static class A1MediatorRelIUriBuilder extends StdA1ClientVersion2.OranV2UriBuilder{ + public A1MediatorRelIUriBuilder(RicConfig ricConfig) { + super(ricConfig); + } + } + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private final AsyncRestClient restClient; + private final A1MediatorRelIUriBuilder uriBuilder; + + public A1MediatorAdapterI(RicConfig ricConfig, AsyncRestClientFactory restClientFactory) { + this(ricConfig, restClientFactory.createRestClientUseHttpProxy("")); + } + + public A1MediatorAdapterI(RicConfig ricConfig, AsyncRestClient restClient) { + this.restClient = restClient; + logger.debug("A1MediatorAdapterI for ric: {}", ricConfig.getRicId()); + uriBuilder = new A1MediatorRelIUriBuilder(ricConfig); + } + + @Override + public Mono> getPolicyTypeIdentities() { + return getPolicyTypeIds() // + .collectList(); + } + + @Override + public Mono> getPolicyIdentities() { + return getPolicyTypeIds() // + .flatMap(this::getPolicyIdentitiesByType) // + .collectList(); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + String schemaUri = uriBuilder.createGetSchemaUri(policyTypeId); + return restClient.get(schemaUri) // + .flatMap(response -> extractCreateSchema(response, policyTypeId)); + } + + @Override + public Mono putPolicy(Policy policy) { + String policyUri = this.uriBuilder.createPutPolicyUri(policy.getType().getId(), policy.getId(), + policy.getStatusNotificationUri()); + return restClient.put(policyUri, policy.getJson()); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicyById(policy.getType().getId(), policy.getId()); + } + + @Override + public Mono getProtocolVersion() { + return Mono.just(A1ProtocolType.CUSTOM_PROTOCOL); + } + + @Override + public Flux deleteAllPolicies(Set excludePolicyIds) { + return getPolicyTypeIds() // + .flatMap(typeId -> deletePoliciesForType(typeId, excludePolicyIds), CONCURRENCY_RIC); + } + + @Override + public Mono getPolicyStatus(Policy policy) { + String statusUri = uriBuilder.createGetPolicyStatusUri(policy.getType().getId(), policy.getId()); + return restClient.get(statusUri); + + } + + private Flux getPolicyTypeIds() { + return restClient.get(uriBuilder.createPolicyTypesUri()) // + .flatMapMany(A1AdapterJsonHelper::parseJsonArrayOfString); + } + + private Flux getPolicyIdentitiesByType(String typeId) { + return restClient.get(uriBuilder.createGetPolicyIdsUri(typeId)) // + .flatMapMany(A1AdapterJsonHelper::parseJsonArrayOfString); + } + + private Mono deletePolicyById(String typeId, String policyId) { + String policyUri = uriBuilder.createDeleteUri(typeId, policyId); + return restClient.delete(policyUri); + } + + private Flux deletePoliciesForType(String typeId, Set excludePolicyIds) { + return getPolicyIdentitiesByType(typeId) // + .filter(policyId -> !excludePolicyIds.contains(policyId)) // + .flatMap(policyId -> deletePolicyById(typeId, policyId), CONCURRENCY_RIC); + } +} diff --git a/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDK.java b/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDK.java new file mode 100644 index 0000000..55d4235 --- /dev/null +++ b/add-src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDK.java @@ -0,0 +1,242 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved. + * %% + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.clients; + +import static org.onap.ccsdk.oran.a1policymanagementservice.clients.OscA1Client.extractCreateSchema; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.GsonBuilder; + +import java.lang.invoke.MethodHandles; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import lombok.Getter; + +import org.json.JSONObject; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1MediatorAdapterI.A1MediatorRelIUriBuilder; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ControllerConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Client for accessing the A1 adapter in the CCSDK in ONAP using custom protocol defined in A1MediatorAdapterI. + */ +@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally +public class A1MediatorAdapterICCSDK implements A1Client { + + static final int CONCURRENCY_RIC = 1; // How many parallel requests that is sent to one NearRT RIC + + static com.google.gson.Gson gson = new GsonBuilder() // + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) // + .create(); // + + @Getter + public static class AdapterRequest { + private String nearRtRicUrl = null; + private String body = null; + + public AdapterRequest(String url, String body) { + this.nearRtRicUrl = url; + this.body = body; + } + + public AdapterRequest() {} + } + + @Getter + public static class AdapterOutput { + private String body = null; + private int httpStatus = 0; + + public AdapterOutput(int status, String body) { + this.httpStatus = status; + this.body = body; + } + + public AdapterOutput() {} + } + + private static final String GET_POLICY_RPC = "getA1Policy"; + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private final AsyncRestClient restClient; + private final RicConfig ricConfig; + private final A1MediatorRelIUriBuilder uriBuilder; + + + /** + * Constructor that creates the REST client to use. + * + * @param ricConfig the configuration of the Near-RT RIC to communicate + * with + * @param restClientFactory the factory for creating the REST Client + * + * @throws IllegalArgumentException when the protocolType is wrong. + */ + public A1MediatorAdapterICCSDK(RicConfig ricConfig, + AsyncRestClientFactory restClientFactory) { + this(A1ProtocolType.CUSTOM_PROTOCOL, ricConfig, restClientFactory + .createRestClientNoHttpProxy(ricConfig.getControllerConfig().getBaseUrl() + "/rests/operations")); + } + + /** + * Constructor where the REST client to use is provided. + * + * @param protocolType the southbound protocol of the controller + * @param ricConfig the configuration of the Near-RT RIC to communicate + * with + * @param restClient the REST client to use + * + * @throws IllegalArgumentException when the protocolType is illegal. + */ + A1MediatorAdapterICCSDK(A1ProtocolType protocolType, RicConfig ricConfig, AsyncRestClient restClient) { + if (A1ProtocolType.CUSTOM_PROTOCOL.equals(protocolType)) { + this.restClient = restClient; + this.ricConfig = ricConfig; + this.uriBuilder = new A1MediatorAdapterI.A1MediatorRelIUriBuilder(ricConfig); + logger.debug("CcsdkA1AdapterClient for ric: {}, a1Controller: {}", ricConfig.getRicId(), + ricConfig.getControllerConfig()); + } else { + logger.error("Not supported protocoltype: {}", protocolType); + throw new IllegalArgumentException("Not handled protocolversion: " + protocolType); + } + } + + @Override + public Mono> getPolicyTypeIdentities() { + return post(GET_POLICY_RPC, uriBuilder.createPolicyTypesUri(), Optional.empty()) // + .flatMapMany(A1AdapterJsonHelper::parseJsonArrayOfString) // + .collectList(); + } + + @Override + public Mono> getPolicyIdentities() { + return getPolicyIds() // + .collectList(); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + final String ricUrl = uriBuilder.createGetSchemaUri(policyTypeId); + return post(GET_POLICY_RPC, ricUrl, Optional.empty()) // + .flatMap(response -> extractCreateSchema(response, policyTypeId)); + } + + @Override + public Mono putPolicy(Policy policy) { + String ricUrl = uriBuilder.createPutPolicyUri(policy.getType().getId(), policy.getId(), + policy.getStatusNotificationUri()); + return post("putA1Policy", ricUrl, Optional.of(policy.getJson())); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicyById(policy.getType().getId(), policy.getId()); + } + + @Override + public Flux deleteAllPolicies(Set excludePolicyIds) { + return getPolicyTypeIdentities() // + .flatMapMany(Flux::fromIterable) // + .flatMap(type -> deleteAllInstancesForType(uriBuilder, type, excludePolicyIds), CONCURRENCY_RIC); + } + + private Flux getInstancesForType(A1UriBuilder uriBuilder, String type) { + return post(GET_POLICY_RPC, uriBuilder.createGetPolicyIdsUri(type), Optional.empty()) // + .flatMapMany(A1AdapterJsonHelper::parseJsonArrayOfString); + } + + private Flux deleteAllInstancesForType(A1UriBuilder uriBuilder, String type, Set excludePolicyIds) { + return getInstancesForType(uriBuilder, type) // + .filter(policyId -> !excludePolicyIds.contains(policyId)) // + .flatMap(policyId -> deletePolicyById(type, policyId), CONCURRENCY_RIC); + } + + @Override + public Mono getProtocolVersion() { + return Mono.just(A1ProtocolType.CUSTOM_PROTOCOL); + } + + @Override + public Mono getPolicyStatus(Policy policy) { + String ricUrl = uriBuilder.createGetPolicyStatusUri(policy.getType().getId(), policy.getId()); + return post("getA1PolicyStatus", ricUrl, Optional.empty()); + } + + private Flux getPolicyIds() { + return getPolicyTypeIdentities() // + .flatMapMany(Flux::fromIterable) + .flatMap(type -> post(GET_POLICY_RPC, uriBuilder.createGetPolicyIdsUri(type), Optional.empty())) // + .flatMap(A1AdapterJsonHelper::parseJsonArrayOfString); + } + + private Mono deletePolicyById(String type, String policyId) { + String ricUrl = uriBuilder.createDeleteUri(type, policyId); + return post("deleteA1Policy", ricUrl, Optional.empty()); + } + + private Mono post(String rpcName, String ricUrl, Optional body) { + AdapterRequest inputParams = new AdapterRequest(ricUrl, body.isPresent() ? body.get() : null); + + final String inputJsonString = A1AdapterJsonHelper.createInputJsonString(inputParams); + logger.debug("POST inputJsonString = {}", inputJsonString); + ControllerConfig controllerConfig = this.ricConfig.getControllerConfig(); + return restClient + .postWithAuthHeader(controllerUrl(rpcName), inputJsonString, controllerConfig.getUserName(), + controllerConfig.getPassword()) // + .flatMap(resp -> extractResponseBody(resp, ricUrl)); + } + + private Mono extractResponse(JSONObject responseOutput, String ricUrl) { + AdapterOutput output = gson.fromJson(responseOutput.toString(), AdapterOutput.class); + + String body = output.body == null ? "" : output.body; + if (HttpStatus.valueOf(output.httpStatus).is2xxSuccessful()) { + return Mono.just(body); + } else { + logger.debug("Error response: {} {}, from: {}", output.httpStatus, body, ricUrl); + byte[] responseBodyBytes = body.getBytes(StandardCharsets.UTF_8); + HttpStatus httpStatus = HttpStatus.valueOf(output.httpStatus); + WebClientResponseException responseException = new WebClientResponseException(httpStatus.value(), + httpStatus.getReasonPhrase(), null, responseBodyBytes, StandardCharsets.UTF_8, null); + + return Mono.error(responseException); + } + } + + private Mono extractResponseBody(String responseStr, String ricUrl) { + return A1AdapterJsonHelper.getOutput(responseStr) // + .flatMap(responseOutput -> extractResponse(responseOutput, ricUrl)); + } + + private String controllerUrl(String rpcName) { + return "/A1-ADAPTER-API:" + rpcName; + } +} diff --git a/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDKTest.java b/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDKTest.java new file mode 100644 index 0000000..ad16874 --- /dev/null +++ b/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterICCSDKTest.java @@ -0,0 +1,333 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved. + * %% + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.clients; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.OngoingStubbing; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1Client.A1ProtocolType; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1MediatorAdapterICCSDK.AdapterOutput; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1MediatorAdapterICCSDK.AdapterRequest; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ControllerConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ExtendWith(MockitoExtension.class) +class A1MediatorAdapterICCSDKTest { + private static final String CONTROLLER_USERNAME = "username"; + private static final String CONTROLLER_PASSWORD = "password"; + private static final String RIC_1_URL = "RicUrl"; + private static final String GET_A1_POLICY_URL = "/A1-ADAPTER-API:getA1Policy"; + private static final String PUT_A1_URL = "/A1-ADAPTER-API:putA1Policy"; + private static final String DELETE_A1_URL = "/A1-ADAPTER-API:deleteA1Policy"; + private static final String GET_A1_POLICY_STATUS_URL = "/A1-ADAPTER-API:getA1PolicyStatus"; + private static final String POLICY_TYPE_1_ID = "type1"; + private static final String POLICY_1_ID = "policy1"; + private static final String POLICY_JSON_VALID = "{\"scope\":{\"ueId\":\"ue1\"}}"; + + A1MediatorAdapterICCSDK clientUnderTest; + + @Mock + AsyncRestClient asyncRestClientMock; + + private ControllerConfig controllerConfig() { + return ControllerConfig.builder() // + .name("name") // + .baseUrl("baseUrl") // + .password(CONTROLLER_PASSWORD) // + .userName(CONTROLLER_USERNAME) // + .build(); + } + + @Test + @DisplayName("test create Client With Wrong Protocol then Error Is Thrown") + void createClientWithWrongProtocol_thenErrorIsThrown() { + AsyncRestClient asyncRestClient = new AsyncRestClient("", null, null, new SecurityContext("")); + assertThrows(IllegalArgumentException.class, () -> { + new A1MediatorAdapterICCSDK(A1ProtocolType.STD_V1_1, null, asyncRestClient); + }); + } + + private Ric createRic(String url) { + RicConfig cfg = RicConfig.builder().ricId("ric") // + .baseUrl(url) // + .managedElementIds(new Vector(Arrays.asList("kista_1", "kista_2"))) // + .controllerConfig(controllerConfig()) // + .build(); + return new Ric(cfg); + } + + private void testGetPolicyTypeIdentities(A1ProtocolType protocolType, String expUrl) { + clientUnderTest = new A1MediatorAdapterICCSDK(protocolType, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + + String response = createOkResponseWithBody(Arrays.asList(POLICY_TYPE_1_ID)); + whenAsyncPostThenReturn(Mono.just(response)); + + List policyTypeIds = clientUnderTest.getPolicyTypeIdentities().block(); + + assertEquals(1, policyTypeIds.size()); + assertEquals(POLICY_TYPE_1_ID, policyTypeIds.get(0)); + + AdapterRequest expectedParams = new AdapterRequest(expUrl, null); + + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedParams); + verify(asyncRestClientMock).postWithAuthHeader(GET_A1_POLICY_URL, expInput, CONTROLLER_USERNAME, + CONTROLLER_PASSWORD); + } + + @Test + @DisplayName("test get Policy Type Identities A1MediatorAdapterICCSDK") + void getPolicyTypeIdentities_A1MediatorAdapterICCSDK() { + testGetPolicyTypeIdentities(A1ProtocolType.CUSTOM_PROTOCOL, RIC_1_URL + "/A1-P/v2/policytypes"); + } + + private void testGetTypeSchema(A1ProtocolType protocolType, String expUrl, String policyTypeId, + String getSchemaResponseFile) throws IOException { + clientUnderTest = new A1MediatorAdapterICCSDK(protocolType, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + + String ricResponse = loadFile(getSchemaResponseFile); + JsonElement elem = gson().fromJson(ricResponse, JsonElement.class); + String responseFromController = createOkResponseWithBody(elem); + whenAsyncPostThenReturn(Mono.just(responseFromController)); + + String response = clientUnderTest.getPolicyTypeSchema(policyTypeId).block(); + + JsonElement respJson = gson().fromJson(response, JsonElement.class); + assertEquals(policyTypeId, respJson.getAsJsonObject().get("title").getAsString(), + "title should be updated to contain policyType ID"); + + AdapterRequest expectedParams = new AdapterRequest(expUrl, null); + + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedParams); + + verify(asyncRestClientMock).postWithAuthHeader(GET_A1_POLICY_URL, expInput, CONTROLLER_USERNAME, + CONTROLLER_PASSWORD); + } + + @Test + @DisplayName("test get Type Schema A1MediatorAdapterICCSDK") + void getTypeSchema_STD_V2() throws IOException { + String expUrl = RIC_1_URL + "/A1-P/v2/policytypes/policyTypeId"; + testGetTypeSchema(A1ProtocolType.CUSTOM_PROTOCOL, expUrl, "policyTypeId", + "test_osc_get_schema_response.json"); + } + + @Test + @DisplayName("test parse Json Array Of String") + void parseJsonArrayOfString() { + // One integer and one string + String inputString = "[1, \"1\" ]"; + + List result = A1AdapterJsonHelper.parseJsonArrayOfString(inputString).collectList().block(); + assertEquals(2, result.size()); + assertEquals("1", result.get(0)); + assertEquals("1", result.get(1)); + } + + private void getPolicyIdentities(A1ProtocolType protocolType, String... expUrls) { + clientUnderTest = new A1MediatorAdapterICCSDK(protocolType, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + String resp = createOkResponseWithBody(Arrays.asList("xxx")); + whenAsyncPostThenReturn(Mono.just(resp)); + + List returned = clientUnderTest.getPolicyIdentities().block(); + + assertEquals(1, returned.size()); + for (String expUrl : expUrls) { + AdapterRequest expectedParams = new AdapterRequest(expUrl, null); + + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedParams); + verify(asyncRestClientMock).postWithAuthHeader(GET_A1_POLICY_URL, expInput, CONTROLLER_USERNAME, + CONTROLLER_PASSWORD); + } + } + + @Test + @DisplayName("test get Policy Identities A1MediatorAdapterICCSDK") + void getPolicyIdentities_STD_V2() { + String expUrlPolicies = RIC_1_URL + "/A1-P/v2/policytypes"; + String expUrlInstances = RIC_1_URL + "/A1-P/v2/policytypes/xxx/policies"; + getPolicyIdentities(A1ProtocolType.CUSTOM_PROTOCOL, expUrlPolicies, expUrlInstances); + } + + private void putPolicy(A1ProtocolType protocolType, String expUrl) { + clientUnderTest = new A1MediatorAdapterICCSDK(protocolType, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + + whenPostReturnOkResponse(); + + String returned = clientUnderTest + .putPolicy(A1ClientHelper.createPolicy(RIC_1_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE_1_ID)) + .block(); + + assertEquals("OK", returned); + AdapterRequest expectedInputParams = new AdapterRequest(expUrl, POLICY_JSON_VALID); + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedInputParams); + + verify(asyncRestClientMock).postWithAuthHeader(PUT_A1_URL, expInput, CONTROLLER_USERNAME, CONTROLLER_PASSWORD); + + } + + @Test + @DisplayName("test put Policy A1MediatorAdapterICCSDK") + void putPolicy_A1MediatorAdapterICCSDK() { + String expUrl = + RIC_1_URL + "/A1-P/v2/policytypes/type1/policies/policy1?notificationDestination=https://test.com"; + putPolicy(A1ProtocolType.CUSTOM_PROTOCOL, expUrl); + } + + @Test + @DisplayName("test post Rejected") + void postRejected() { + clientUnderTest = new A1MediatorAdapterICCSDK(A1ProtocolType.CUSTOM_PROTOCOL, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + + final String policyJson = "{}"; + AdapterOutput adapterOutput = new AdapterOutput(HttpStatus.BAD_REQUEST.value(), "NOK"); + + String resp = A1AdapterJsonHelper.createOutputJsonString(adapterOutput); + whenAsyncPostThenReturn(Mono.just(resp)); + + Mono returnedMono = clientUnderTest + .putPolicy(A1ClientHelper.createPolicy(RIC_1_URL, POLICY_1_ID, policyJson, POLICY_TYPE_1_ID)); + StepVerifier.create(returnedMono) // + .expectSubscription() // + .expectErrorMatches(t -> t instanceof WebClientResponseException) // + .verify(); + + StepVerifier.create(returnedMono).expectErrorMatches(throwable -> { + return throwable instanceof WebClientResponseException; + }).verify(); + } + + private void deleteAllPolicies(A1ProtocolType protocolType, String expUrl) { + clientUnderTest = new A1MediatorAdapterICCSDK(protocolType, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + String resp = createOkResponseWithBody(Arrays.asList("xxx")); + whenAsyncPostThenReturn(Mono.just(resp)); + + clientUnderTest.deleteAllPolicies().blockLast(); + + AdapterRequest expectedParams = new AdapterRequest(expUrl, null); + + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedParams); + verify(asyncRestClientMock).postWithAuthHeader(DELETE_A1_URL, expInput, CONTROLLER_USERNAME, + CONTROLLER_PASSWORD); + } + + @Test + @DisplayName("test delete All Policies A1MediatorAdapterICCSDK") + void deleteAllPolicies_A1MediatorAdapterICCSDK() { + String expUrl1 = RIC_1_URL + "/A1-P/v2/policytypes/xxx/policies/xxx"; + deleteAllPolicies(A1ProtocolType.CUSTOM_PROTOCOL, expUrl1); + } + + @Test + @DisplayName("test Get Status") + void testGetStatus() { + clientUnderTest = new A1MediatorAdapterICCSDK(A1ProtocolType.CUSTOM_PROTOCOL, // + createRic(RIC_1_URL).getConfig(), // + asyncRestClientMock); + whenPostReturnOkResponse(); + + Policy policy = A1ClientHelper.createPolicy(RIC_1_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE_1_ID); + + String response = clientUnderTest.getPolicyStatus(policy).block(); + assertEquals("OK", response); + + String expUrl = RIC_1_URL + "/A1-P/v2/policytypes/type1/policies/policy1/status"; + AdapterRequest expectedParams = new AdapterRequest(expUrl, null); + + String expInput = A1AdapterJsonHelper.createInputJsonString(expectedParams); + verify(asyncRestClientMock).postWithAuthHeader(GET_A1_POLICY_STATUS_URL, expInput, CONTROLLER_USERNAME, + CONTROLLER_PASSWORD); + + } + + private Gson gson() { + return A1MediatorAdapterICCSDK.gson; + } + + private String loadFile(String fileName) throws IOException { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + URL url = loader.getResource(fileName); + File file = new File(url.getFile()); + return new String(Files.readAllBytes(file.toPath())); + } + + private void whenPostReturnOkResponse() { + whenAsyncPostThenReturn(Mono.just(createOkResponseString(true))); + } + + void whenPostReturnOkResponseNoBody() { + whenAsyncPostThenReturn(Mono.just(createOkResponseString(false))); + } + + private String createOkResponseWithBody(Object body) { + AdapterOutput output = new AdapterOutput(HttpStatus.OK.value(), gson().toJson(body)); + return A1AdapterJsonHelper.createOutputJsonString(output); + } + + private String createOkResponseString(boolean withBody) { + String body = withBody ? HttpStatus.OK.name() : null; + AdapterOutput output = new AdapterOutput(HttpStatus.OK.value(), body); + return A1AdapterJsonHelper.createOutputJsonString(output); + } + + private OngoingStubbing> whenAsyncPostThenReturn(Mono response) { + return when(asyncRestClientMock.postWithAuthHeader(anyString(), anyString(), anyString(), anyString())) + .thenReturn(response); + } +} diff --git a/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterITest.java b/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterITest.java new file mode 100644 index 0000000..e5aa34f --- /dev/null +++ b/add-src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1MediatorAdapterITest.java @@ -0,0 +1,230 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved. + * %% + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.clients; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ExtendWith(MockitoExtension.class) +class A1MediatorAdapterITest { + + + private static final String RIC_URL = "https://ric.com"; + + private static final String RIC_BASE_URL = RIC_URL + "/A1-P/v2"; + + private static final String POLICYTYPES_IDENTITIES_URL = RIC_BASE_URL + "/policytypes"; + private static final String POLICIES = "/policies"; + private static final String POLICYTYPES_URL = RIC_BASE_URL + "/policytypes/"; + private static final String POLICY_TYPE_1_ID = "type1"; + private static final String POLICY_TYPE_2_ID = "type2"; + private static final String POLICY_TYPE_SCHEMA_VALID = "{\"type\":\"type1\"}"; + private static final String POLICY_TYPE_SCHEMA_INVALID = "\"type\":\"type1\"}"; + private static final String POLICY_1_ID = "policy1"; + private static final String POLICY_2_ID = "policy2"; + private static final String POLICY_JSON_VALID = "{\"policyId\":\"policy1\"}"; + + A1MediatorAdapterI clientUnderTest; + + AsyncRestClient asyncRestClientMock; + + @BeforeEach + void init() { + RicConfig ricConfig = RicConfig.builder() // + .ricId("name") // + .baseUrl(RIC_URL) // + .build(); + asyncRestClientMock = mock(AsyncRestClient.class); + clientUnderTest = new A1MediatorAdapterI(ricConfig, asyncRestClientMock); + } + + @Test + @DisplayName("test Get Policy Type Identities") + void testGetPolicyTypeIdentities() { + List policyTypeIds = Arrays.asList(POLICY_TYPE_1_ID, POLICY_TYPE_2_ID); + Mono policyTypeIdsResp = Mono.just(policyTypeIds.toString()); + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeIdsResp); + + Mono> returnedMono = clientUnderTest.getPolicyTypeIdentities(); + verify(asyncRestClientMock).get(POLICYTYPES_IDENTITIES_URL); + StepVerifier.create(returnedMono).expectNext(policyTypeIds).expectComplete().verify(); + } + + @Test + @DisplayName("test Get Policy Identities") + void testGetPolicyIdentities() { + Mono policyTypeIdsResp = Mono.just(Arrays.asList(POLICY_TYPE_1_ID, POLICY_TYPE_2_ID).toString()); + Mono policyIdsType1Resp = Mono.just(Arrays.asList(POLICY_1_ID).toString()); + Mono policyIdsType2Resp = Mono.just(Arrays.asList(POLICY_2_ID).toString()); + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeIdsResp).thenReturn(policyIdsType1Resp) + .thenReturn(policyIdsType2Resp); + + List returned = clientUnderTest.getPolicyIdentities().block(); + + assertEquals(2, returned.size(), ""); + verify(asyncRestClientMock).get(POLICYTYPES_IDENTITIES_URL); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_ID + POLICIES); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_2_ID + POLICIES); + } + + @Test + @DisplayName("test Get Valid PolicyType") + void testGetValidPolicyType() { + String policyType = "{\"create_schema\": " + POLICY_TYPE_SCHEMA_VALID + "}"; + Mono policyTypeResp = Mono.just(policyType); + + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeResp); + + Mono returnedMono = clientUnderTest.getPolicyTypeSchema(POLICY_TYPE_1_ID); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_ID); + StepVerifier.create(returnedMono).expectNext(getCreateSchema(policyType, POLICY_TYPE_1_ID)).expectComplete() + .verify(); + } + + @Test + @DisplayName("test Get In Valid Policy Type Json") + void testGetInValidPolicyTypeJson() { + String policyType = "{\"create_schema\": " + POLICY_TYPE_SCHEMA_INVALID + "}"; + Mono policyTypeResp = Mono.just(policyType); + + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeResp); + + Mono returnedMono = clientUnderTest.getPolicyTypeSchema(POLICY_TYPE_1_ID); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_ID); + StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof JSONException).verify(); + } + + @Test + @DisplayName("test Get Policy Type Without CreateS chema") + void testGetPolicyTypeWithoutCreateSchema() { + Mono policyTypeResp = Mono.just(POLICY_TYPE_SCHEMA_VALID); + + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeResp); + + Mono returnedMono = clientUnderTest.getPolicyTypeSchema(POLICY_TYPE_1_ID); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_ID); + StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof Exception).verify(); + } + + @Test + @DisplayName("test Put Policy") + void testPutPolicy() { + when(asyncRestClientMock.put(anyString(), anyString())).thenReturn(Mono.empty()); + + clientUnderTest + .putPolicy(createPolicy(RIC_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE_1_ID)) + .block(); + + ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class); + verify(asyncRestClientMock).put(urlCaptor.capture(), eq(POLICY_JSON_VALID)); + String actualUrl = urlCaptor.getValue(); + String expUrl = POLICYTYPES_URL + POLICY_TYPE_1_ID + POLICIES + "/" + POLICY_1_ID; + assertThat(actualUrl).contains(expUrl); + } + + @Test + @DisplayName("test Delete Policy") + void testDeletePolicy() { + when(asyncRestClientMock.delete(anyString())).thenReturn(Mono.empty()); + + Mono returnedMono = clientUnderTest + .deletePolicy(createPolicy(RIC_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE_1_ID)); + verify(asyncRestClientMock).delete(POLICYTYPES_URL + POLICY_TYPE_1_ID + POLICIES + "/" + POLICY_1_ID); + StepVerifier.create(returnedMono).expectComplete().verify(); + } + + @Test + @DisplayName("test Delete All Policies") + void testDeleteAllPolicies() { + Mono policyTypeIdsResp = Mono.just(Arrays.asList(POLICY_TYPE_1_ID, POLICY_TYPE_2_ID).toString()); + Mono policyIdsType1Resp = Mono.just(Arrays.asList(POLICY_1_ID).toString()); + Mono policyIdsType2Resp = Mono.just(Arrays.asList(POLICY_2_ID).toString()); + when(asyncRestClientMock.get(anyString())).thenReturn(policyTypeIdsResp).thenReturn(policyIdsType1Resp) + .thenReturn(policyIdsType2Resp); + when(asyncRestClientMock.delete(anyString())).thenReturn(Mono.empty()); + + Flux returnedFlux = clientUnderTest.deleteAllPolicies(); + StepVerifier.create(returnedFlux).expectComplete().verify(); + verify(asyncRestClientMock).get(POLICYTYPES_IDENTITIES_URL); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_ID + POLICIES); + verify(asyncRestClientMock).delete(POLICYTYPES_URL + POLICY_TYPE_1_ID + POLICIES + "/" + POLICY_1_ID); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_2_ID + POLICIES); + verify(asyncRestClientMock).delete(POLICYTYPES_URL + POLICY_TYPE_2_ID + POLICIES + "/" + POLICY_2_ID); + } + + private String getCreateSchema(String policyType, String policyTypeId) { + JSONObject obj = new JSONObject(policyType); + JSONObject schemaObj = obj.getJSONObject("create_schema"); + schemaObj.put("title", policyTypeId); + return schemaObj.toString(); + } + + private static Ric createRic(String url) { + RicConfig cfg = RicConfig.builder().ricId("ric") // + .baseUrl(url) // + .managedElementIds(new Vector(Arrays.asList("kista_1", "kista_2"))) // + .build(); + return new Ric(cfg); + } + + private static Policy createPolicy(String nearRtRicUrl, String policyId, String json, String type) { + String callbackUrl = "https://test.com"; + return Policy.builder() // + .id(policyId) // + .json(json) // + .ownerServiceId("service") // + .ric(createRic(nearRtRicUrl)) // + .type(createPolicyType(type)) // + .lastModified(Instant.now()) // + .isTransient(false) // + .statusNotificationUri(callbackUrl) // + .build(); + } + + private static PolicyType createPolicyType(String name) { + return PolicyType.builder().id(name).schema("schema").build(); + } +} diff --git a/pom.xml b/pom.xml index 210486a..c086f58 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ * O-RAN-SC * %% * Copyright (C) 2019-2023: Nordix Foundation -* Copyright (C) 2023: OpenInfra Foundation Europe +* Copyright (C) 2023-2024: OpenInfra Foundation Europe * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,7 +105,6 @@ com.google.code.gson gson - ${gson.version} org.json @@ -144,6 +143,11 @@ ${springdoc.openapi-ui.version} + + org.springframework.boot + spring-boot-test + test + org.springframework.boot spring-boot-starter-test @@ -216,7 +220,7 @@ build-helper-maven-plugin - add-source + add-additional-source-dirs generate-sources add-source @@ -224,6 +228,21 @@ ${project.build.directory}/generated-sources/annotations/ + ${project.basedir}/add-src/main/java + ${project.basedir}/add-src/main/resources + + + + + add-additional-testsource-dirs + generate-sources + + add-test-source + + + + ${project.basedir}/add-src/test/java + ${project.basedir}/add-src/test/resources