From 797bf1d50236fddcbbb2ca091729e2e64b047f73 Mon Sep 17 00:00:00 2001 From: Phillip Leigh Date: Tue, 4 Sep 2018 11:28:49 -0400 Subject: Obtain resouce-link based on service-instance Issue-ID: LOG-461 Change-Id: Ieb3fb761be2237808ab9d709b3fadc7e241963de Signed-off-by: Phillip Leigh --- config/application.properties | 2 + pom.xml | 6 ++ .../pomba/contextbuilder/aai/AAIConfiguration.java | 8 +++ .../aai/service/SpringServiceImpl.java | 4 +- .../pomba/contextbuilder/aai/util/RestUtil.java | 68 +++++++++++++++++- .../pomba_aai_context_builder/RestUtilTest.java | 82 +++++++++++++++++++++- src/test/resources/junit/queryNodeData-1.json | 8 +++ .../junit/queryNodeData-nullResourceLink.json | 8 +++ 8 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/junit/queryNodeData-1.json create mode 100644 src/test/resources/junit/queryNodeData-nullResourceLink.json diff --git a/config/application.properties b/config/application.properties index a3cc78b..4ce5de9 100644 --- a/config/application.properties +++ b/config/application.properties @@ -41,5 +41,7 @@ http.password=OBF:1u2a1toa1w8v1tok1u30 # {0} = customerId {1} = serviceType {2} = serviceInstanceId aai.serviceInstancePath=/aai/v11/business/customers/customer/{0}/service-subscriptions/service-subscription/{1}/service-instances/service-instance/{2} +aai.searchNodeQuery=/aai/v11/search/nodes-query?search-node-type=service-instance&filter=service-instance-id:EQUALS: + #Servlet context parameters server.context_parameters.p-name=value #context parameter with p-name as key and value as value. diff --git a/pom.xml b/pom.xml index 061e528..bab6e32 100644 --- a/pom.xml +++ b/pom.xml @@ -148,6 +148,12 @@ 1.10.19 test + + com.github.tomakehurst + wiremock + 2.18.0 + test + diff --git a/src/main/java/org/onap/pomba/contextbuilder/aai/AAIConfiguration.java b/src/main/java/org/onap/pomba/contextbuilder/aai/AAIConfiguration.java index 9c6d11d..7befdc9 100644 --- a/src/main/java/org/onap/pomba/contextbuilder/aai/AAIConfiguration.java +++ b/src/main/java/org/onap/pomba/contextbuilder/aai/AAIConfiguration.java @@ -104,4 +104,12 @@ public class AAIConfiguration { return serviceInstancePath; } + @Value("${aai.searchNodeQuery}") + private String searchNodeQuery; + + @Bean(name="aaiPathToSearchNodeQuery") + public String getAaiPathToSearchNodeQuery() { + return searchNodeQuery.trim(); + } + } diff --git a/src/main/java/org/onap/pomba/contextbuilder/aai/service/SpringServiceImpl.java b/src/main/java/org/onap/pomba/contextbuilder/aai/service/SpringServiceImpl.java index ff09e55..2ec49f3 100644 --- a/src/main/java/org/onap/pomba/contextbuilder/aai/service/SpringServiceImpl.java +++ b/src/main/java/org/onap/pomba/contextbuilder/aai/service/SpringServiceImpl.java @@ -39,6 +39,8 @@ public class SpringServiceImpl implements SpringService { private String aaiServiceInstancePath; @Autowired private String aaiBasicAuthorization; + @Autowired + private String aaiPathToSearchNodeQuery; public SpringServiceImpl() { // needed for instantiation @@ -55,7 +57,7 @@ public class SpringServiceImpl implements SpringService { // Retrieve the service instance information from AAI try { - context= RestUtil.retrieveAAIModelData(aaiClient, aaiBaseUrl, aaiServiceInstancePath, tranId, serviceInstanceId, modelVersionId, modelInvariantId, serviceType, customerId,aaiBasicAuthorization); + context= RestUtil.retrieveAAIModelData(aaiClient, aaiBaseUrl, aaiPathToSearchNodeQuery, aaiServiceInstancePath, tranId, serviceInstanceId, modelVersionId, modelInvariantId, serviceType, customerId,aaiBasicAuthorization); } catch (AuditException ae) { throw ae; } catch (Exception e) { diff --git a/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java b/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java index da7d241..905a8bc 100644 --- a/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java +++ b/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java @@ -31,6 +31,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response.Status; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.onap.aai.restclient.client.OperationResult; import org.onap.aai.restclient.client.RestClient; @@ -48,7 +49,7 @@ import org.onap.pomba.contextbuilder.aai.exception.AuditError; import org.onap.pomba.contextbuilder.aai.exception.AuditException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import org.springframework.beans.factory.annotation.Autowired; public class RestUtil { @@ -72,12 +73,17 @@ public class RestUtil { // Service Catalog private static final String CATALOG_GENERIC_VNF = "generic-vnf"; private static final String CATALOG_VNFC = "vnfc"; + private static final String CATALOG_SERVICE_INSTANCE = "service-instance"; + // Relationship Json Path private static final String RELATIONSHIP_LIST = "relationship-list"; private static final String RELATIONSHIP = "relationship"; + private static final String RESULT_DATA = "result-data"; private static final String JSON_ATT_RELATED_TO = "related-to"; private static final String JSON_ATT_RELATED_LINK = "related-link"; + private static final String JSON_ATT_RESOURCE_TYPE = "resource-type"; + private static final String JSON_ATT_RESOURCE_LINK = "resource-link"; private static final String EMPTY_JSON_STRING = "{}"; private static final String DELIMITER = "$"; @@ -105,6 +111,7 @@ public class RestUtil { throw new AuditException(AuditError.INVALID_REQUEST_URL_MISSING_PARAMETER + MODEL_INVARIANT_ID, Status.BAD_REQUEST); } + // serviceType if (serviceType == null || serviceType.isEmpty()) { throw new AuditException(AuditError.INVALID_REQUEST_URL_MISSING_PARAMETER + SERVICE_TYPE, Status.BAD_REQUEST); @@ -161,7 +168,7 @@ public class RestUtil { /* * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC) */ - public static ModelContext retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiServiceInstancePath, + public static ModelContext retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiPathToSearchNodeQuery, String aaiServiceInstancePath, String transactionId, String serviceInstanceId, String modelVersionId, String modelInvariantId, String serviceType, String customerId, String aaiBasicAuthorization) throws AuditException { String serviceInstancePayload = null; @@ -173,6 +180,8 @@ public class RestUtil { Map> vnfMap = new HashMap>(); // MAP the vnf-id as the // key, and list of the vNFC // pojo object + // Obtain resource-link based on resource-type = service-Instance + String resourceLink = obtainResouceLinkBasedOnServiceInstanceFromAAI(aaiClient, baseURL, aaiPathToSearchNodeQuery, serviceInstanceId, transactionId, aaiBasicAuthorization); String url = baseURL + generateServiceInstanceURL(aaiServiceInstancePath, customerId, serviceType, serviceInstanceId); @@ -442,5 +451,60 @@ public class RestUtil { return MessageFormat.format(siPath, customerId, serviceType, serviceInstanceId); } + public static String obtainResouceLinkBasedOnServiceInstanceFromAAI(RestClient aaiClient, String baseURL, String aaiPathToSearchNodeQuery, String serviceInstanceId, + String transactionId, String aaiBasicAuthorization) throws AuditException { + + String url = generateGetCustomerInfoUrl(baseURL, aaiPathToSearchNodeQuery, serviceInstanceId); + String customerInfoString = getResource(aaiClient, url, aaiBasicAuthorization, transactionId, MediaType.valueOf(MediaType.APPLICATION_JSON)); + + // Handle the case if the service instance is not found in AAI + if (isEmptyJson(customerInfoString)) { + log.info(LogMessages.NOT_FOUND, "Service Instance" , serviceInstanceId); + // Only return the empty Json on the root level. i.e service instance + return null; + } + + return extractResourceLinkBasedOnResourceType(customerInfoString, CATALOG_SERVICE_INSTANCE); + } + + private static String generateGetCustomerInfoUrl (String baseURL, String aaiPathToSearchNodeQuery ,String serviceInstanceId) { + return baseURL + aaiPathToSearchNodeQuery + serviceInstanceId; + } + + /* + * Extract the resource-Link from Json payload. For example + * { + * "result-data": [ + * { + * "resource-type": "service-instance", + * "resource-link": "/aai/v11/business/customers/customer/DemoCust_651800ed-2a3c-45f5-b920-85c1ed155fc2/service-subscriptions/service-subscription/vFW/service-instances/service-instance/adc3cc2a-c73e-414f-8ddb-367de81300cb" + * } + * ] + * } + */ + private static String extractResourceLinkBasedOnResourceType(String payload, String catalog) throws AuditException { + String resourceLink = null; + log.info("Fetching the resource-link based on resource-type=" + catalog); + + try { + JSONArray result_data_list = new JSONObject(payload).getJSONArray(RESULT_DATA); + if (result_data_list != null) { + for (int i = 0; i < result_data_list.length(); i++) { + JSONObject obj = result_data_list.optJSONObject(i); + if (obj.has(JSON_ATT_RESOURCE_TYPE) && (obj.getString(JSON_ATT_RESOURCE_TYPE).equals(catalog) )) { + resourceLink = obj.getString(JSON_ATT_RESOURCE_LINK); + log.info(resourceLink); + return resourceLink; + } + } + } + } catch (JSONException e) { + log.error(e.getMessage()); + throw new AuditException(AuditError.JSON_READER_PARSE_ERROR + " " + e.getMessage()); + } + + log.warn("resource-link CANNOT be found: ", payload ); + return resourceLink; + } } diff --git a/src/test/java/org/onap/logging_analytics/pomba/pomba_aai_context_builder/RestUtilTest.java b/src/test/java/org/onap/logging_analytics/pomba/pomba_aai_context_builder/RestUtilTest.java index 9757a2c..f8991c1 100644 --- a/src/test/java/org/onap/logging_analytics/pomba/pomba_aai_context_builder/RestUtilTest.java +++ b/src/test/java/org/onap/logging_analytics/pomba/pomba_aai_context_builder/RestUtilTest.java @@ -17,13 +17,49 @@ */ package org.onap.logging_analytics.pomba.pomba_aai_context_builder; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; - +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.UUID; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.aai.restclient.client.RestClient; +import org.onap.pomba.contextbuilder.aai.Application; import org.onap.pomba.contextbuilder.aai.exception.AuditException; import org.onap.pomba.contextbuilder.aai.util.RestUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +@RunWith(SpringJUnit4ClassRunner.class) +@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) +@WebAppConfiguration +@SpringBootTest(classes = Application.class) +@TestPropertySource(properties = { "aai.serviceName=localhost","aai.servicePort=9808", "aai.httpProtocol=http" }) public class RestUtilTest { + @Autowired + private RestClient aaiClient; + @Autowired + private String aaiBaseUrl; + @Autowired + private String aaiPathToSearchNodeQuery; + @Autowired + private String aaiBasicAuthorization; + + @Rule + public WireMockRule aaiEnricherRule = new WireMockRule(wireMockConfig().port(9808)); @Test public void testValidateURL() { @@ -99,4 +135,48 @@ public class RestUtilTest { assertTrue(RestUtil.isEmptyJson("{}")); assertTrue(!RestUtil.isEmptyJson("{Not Empty}")); } + + @Test + public void testObtainResouceLinkBasedOnServiceInstanceFromAAI() throws Exception { + String transactionId = UUID.randomUUID().toString(); + String serviceInstanceId = "adc3cc2a-c73e-414f-8ddb-367de81300cb"; //match to the test data in junit/queryNodeData-1.json + String queryNodeUrl = aaiPathToSearchNodeQuery + serviceInstanceId; + addResponse(queryNodeUrl, "junit/queryNodeData-1.json", aaiEnricherRule); + + String resourceLinkUlr = RestUtil.obtainResouceLinkBasedOnServiceInstanceFromAAI(aaiClient, aaiBaseUrl, aaiPathToSearchNodeQuery, serviceInstanceId, transactionId, aaiBasicAuthorization); + + String returnedInstanceId = resourceLinkUlr.substring(resourceLinkUlr.lastIndexOf("/")+1).trim(); + assertEquals(serviceInstanceId, returnedInstanceId); + } + + @Test + public void testObtainResouceLinkBasedOnServiceInstanceFromAAI_nullResourceLink() throws Exception { + String transactionId = UUID.randomUUID().toString(); + String serviceInstanceId = "adc3cc2a-c73e-414f-8ddb-367de81300cb"; //match to the test data in junit/queryNodeData-1.json + String queryNodeUrl = aaiPathToSearchNodeQuery + serviceInstanceId; + addResponse(queryNodeUrl, "junit/queryNodeData-nullResourceLink.json", aaiEnricherRule); + + try { + RestUtil.obtainResouceLinkBasedOnServiceInstanceFromAAI(aaiClient, aaiBaseUrl, aaiPathToSearchNodeQuery, serviceInstanceId, transactionId, aaiBasicAuthorization); + } catch (AuditException e) { + assertTrue(e.getMessage().contains("JSONObject[\"resource-link\"] not found")); + } + } + + private void addResponse(String path, String classpathResource, WireMockRule thisMock) throws IOException { + String payload = readFully(ClassLoader.getSystemResourceAsStream(classpathResource)); + thisMock.stubFor(get(path).willReturn(okJson(payload))); + } + + private String readFully(InputStream in) throws IOException { + char[] cbuf = new char[1024]; + StringBuilder content = new StringBuilder(); + try (InputStreamReader reader = new InputStreamReader(in, "UTF-8")) { + int count; + while ((count = reader.read(cbuf)) >= 0) { + content.append(cbuf, 0, count); + } + } + return content.toString(); + } } diff --git a/src/test/resources/junit/queryNodeData-1.json b/src/test/resources/junit/queryNodeData-1.json new file mode 100644 index 0000000..501830b --- /dev/null +++ b/src/test/resources/junit/queryNodeData-1.json @@ -0,0 +1,8 @@ +{ + "result-data": [ + { + "resource-type": "service-instance", + "resource-link": "/aai/v11/business/customers/customer/DemoCust_651800ed-2a3c-45f5-b920-85c1ed155fc2/service-subscriptions/service-subscription/vFW/service-instances/service-instance/adc3cc2a-c73e-414f-8ddb-367de81300cb" + } + ] +} diff --git a/src/test/resources/junit/queryNodeData-nullResourceLink.json b/src/test/resources/junit/queryNodeData-nullResourceLink.json new file mode 100644 index 0000000..36eb667 --- /dev/null +++ b/src/test/resources/junit/queryNodeData-nullResourceLink.json @@ -0,0 +1,8 @@ +{ + "result-data": [ + { + "resource-type": "service-instance", + "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003" + } + ] +} -- cgit 1.2.3-korg