15940100969de1d6341cb6d0c1a2778038d75ad3
[vid.git] / vid-app-common / src / main / java / org / onap / vid / aai / AaiClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.aai;
22
23 import static com.fasterxml.jackson.module.kotlin.ExtensionsKt.jacksonObjectMapper;
24 import static java.util.Collections.emptyList;
25 import static java.util.Comparator.comparing;
26 import static java.util.stream.Collectors.toMap;
27 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
28 import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
29 import static org.apache.commons.lang3.StringUtils.isEmpty;
30 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
31
32 import com.fasterxml.jackson.core.JsonProcessingException;
33 import com.fasterxml.jackson.databind.JsonNode;
34 import com.fasterxml.jackson.databind.ObjectMapper;
35 import com.google.common.collect.ImmutableList;
36 import com.google.common.collect.ImmutableMap;
37 import java.io.IOException;
38 import java.io.Serializable;
39 import java.io.UnsupportedEncodingException;
40 import java.net.URI;
41 import java.net.URLEncoder;
42 import java.util.LinkedHashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.UUID;
46 import java.util.function.Function;
47 import java.util.stream.Stream;
48 import javax.inject.Inject;
49 import javax.ws.rs.WebApplicationException;
50 import javax.ws.rs.core.Response;
51 import org.apache.commons.lang3.StringUtils;
52 import org.apache.commons.lang3.exception.ExceptionUtils;
53 import org.apache.http.HttpStatus;
54 import org.apache.http.client.utils.URIBuilder;
55 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
56 import org.jetbrains.annotations.NotNull;
57 import org.jetbrains.annotations.Nullable;
58 import org.json.simple.JSONArray;
59 import org.json.simple.JSONObject;
60 import org.json.simple.parser.JSONParser;
61 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
62 import org.onap.vid.aai.exceptions.InvalidAAIResponseException;
63 import org.onap.vid.aai.model.AaiGetAicZone.AicZones;
64 import org.onap.vid.aai.model.AaiGetInstanceGroupsByCloudRegion;
65 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetNetworkCollectionDetails;
66 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetNetworkCollectionDetailsHelper;
67 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetRelatedInstanceGroupsByVnfId;
68 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.CloudRegion;
69 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.InstanceGroup;
70 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Network;
71 import org.onap.vid.aai.model.AaiGetOperationalEnvironments.OperationalEnvironmentList;
72 import org.onap.vid.aai.model.AaiGetPnfResponse;
73 import org.onap.vid.aai.model.AaiGetPnfs.Pnf;
74 import org.onap.vid.aai.model.AaiGetServicesRequestModel.GetServicesAAIRespone;
75 import org.onap.vid.aai.model.AaiGetTenatns.GetTenantsResponse;
76 import org.onap.vid.aai.model.CustomQuerySimpleResult;
77 import org.onap.vid.aai.model.GetServiceModelsByDistributionStatusResponse;
78 import org.onap.vid.aai.model.LogicalLinkResponse;
79 import org.onap.vid.aai.model.ModelVer;
80 import org.onap.vid.aai.model.ModelVersions;
81 import org.onap.vid.aai.model.OwningEntityResponse;
82 import org.onap.vid.aai.model.PortDetailsTranslator;
83 import org.onap.vid.aai.model.ProjectResponse;
84 import org.onap.vid.aai.model.Properties;
85 import org.onap.vid.aai.model.ResourceType;
86 import org.onap.vid.aai.model.ServiceRelationships;
87 import org.onap.vid.aai.model.SimpleResult;
88 import org.onap.vid.aai.util.AAIRestInterface;
89 import org.onap.vid.aai.util.CacheProvider;
90 import org.onap.vid.aai.util.VidObjectMapperType;
91 import org.onap.vid.exceptions.GenericUncheckedException;
92 import org.onap.vid.model.SubscriberList;
93 import org.onap.vid.model.probes.ErrorMetadata;
94 import org.onap.vid.model.probes.ExternalComponentStatus;
95 import org.onap.vid.model.probes.HttpRequestMetadata;
96 import org.onap.vid.utils.Logging;
97 import org.onap.vid.utils.Unchecked;
98 import org.springframework.http.HttpMethod;
99 import org.springframework.web.util.UriUtils;
100
101 public class AaiClient implements AaiClientInterface {
102
103
104     public static final String QUERY_FORMAT_RESOURCE = "query?format=resource";
105     private static final String SERVICE_SUBSCRIPTIONS_PATH = "/service-subscriptions/service-subscription/";
106     private static final String MODEL_INVARIANT_ID = "&model-invariant-id=";
107     private static final String QUERY_FORMAT_SIMPLE = "query?format=simple";
108     private static final String BUSINESS_CUSTOMER = "/business/customers/customer/";
109     private static final String SERVICE_INSTANCE = "/service-instances/service-instance/";
110     private static final String BUSINESS_CUSTOMERS_CUSTOMER = "business/customers/customer/";
111
112     protected String fromAppId = "VidAaiController";
113
114     private PortDetailsTranslator portDetailsTranslator;
115
116     private final AAIRestInterface restController;
117
118     private final CacheProvider cacheProvider;
119
120     ObjectMapper objectMapper = jacksonObjectMapper();
121
122     /**
123      * The logger
124      */
125     EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AaiClient.class);
126
127     private static final String GET_SERVICE_MODELS_REQUEST_BODY = "{\"start\" : \"service-design-and-creation/models/\", \"query\" : \"query/serviceModels-byDistributionStatus?distributionStatus=DISTRIBUTION_COMPLETE_OK\"}";
128
129     @Inject
130     public AaiClient(AAIRestInterface restController, PortDetailsTranslator portDetailsTranslator, CacheProvider cacheProvider) {
131         this.restController = restController;
132         this.portDetailsTranslator = portDetailsTranslator;
133         this.cacheProvider = cacheProvider;
134     }
135
136
137     private static String checkForNull(String local) {
138         if (local != null)
139             return local;
140         else
141             return "";
142
143     }
144
145     @Override
146     public AaiResponse getServicesByOwningEntityId(List<String> owningEntityIds){
147         Response resp = doAaiGet(getUrlFromLIst("business/owning-entities?", "owning-entity-id=", owningEntityIds), false);
148         return processAaiResponse(resp, OwningEntityResponse.class, null);
149     }
150
151     @Override
152     public AaiResponse getServicesByProjectNames(List<String> projectNames){
153         Response resp = doAaiGet(getUrlFromLIst("business/projects?", "project-name=",  projectNames), false);
154         return processAaiResponse(resp, ProjectResponse.class, null);
155     }
156
157     @Override
158     public AaiResponse getServiceModelsByDistributionStatus() {
159         return getFromCache("getServiceModelsByDistributionStatus", this::getServiceModelsByDistributionStatusNonCached,
160                 true, "Failed to get service models by distribution status");
161     }
162
163     private AaiResponse getServiceModelsByDistributionStatusNonCached(boolean propagateExceptions) {
164         GetServiceModelsByDistributionStatusResponse response = typedAaiRest(QUERY_FORMAT_RESOURCE, GetServiceModelsByDistributionStatusResponse.class,
165                 GET_SERVICE_MODELS_REQUEST_BODY, HttpMethod.PUT, propagateExceptions);
166         return new AaiResponse(response, "", HttpStatus.SC_OK);
167     }
168
169     @Override
170     public AaiResponse getNetworkCollectionDetails(String serviceInstanceId) {
171         Response resp = doAaiPut(QUERY_FORMAT_RESOURCE, "{\"start\": [\"nodes/service-instances/service-instance/" + serviceInstanceId + "\"],\"query\": \"query/network-collection-ByServiceInstance\"}\n", false);
172         AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse = processAaiResponse(resp, AaiGetNetworkCollectionDetailsHelper.class, null, VidObjectMapperType.FASTERXML);
173         return getNetworkCollectionDetailsResponse(aaiResponse);
174     }
175
176     @Override
177     public AaiResponse getInstanceGroupsByCloudRegion(String cloudOwner, String cloudRegionId, String networkFunction) {
178         Response resp = doAaiPut(QUERY_FORMAT_RESOURCE,
179                 "{\"start\": [\"cloud-infrastructure/cloud-regions/cloud-region/" + encodePathSegment(cloudOwner) + "/" + encodePathSegment(cloudRegionId) + "\"]," +
180                         "\"query\": \"query/instance-groups-byCloudRegion?type=L3-NETWORK&role=SUB-INTERFACE&function=" + encodePathSegment(networkFunction) + "\"}\n", false);
181         return processAaiResponse(resp, AaiGetInstanceGroupsByCloudRegion.class, null, VidObjectMapperType.FASTERXML);
182     }
183
184     private AaiResponse getNetworkCollectionDetailsResponse(AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse){
185         if(aaiResponse.getHttpCode() == 200) {
186             ObjectMapper om = objectMapper;
187             AaiGetNetworkCollectionDetails aaiGetNetworkCollectionDetails = new AaiGetNetworkCollectionDetails();
188             try {
189                 for (int i = 0; i < aaiResponse.getT().getResults().size(); i++) {
190                     LinkedHashMap<String, Object> temp = ((LinkedHashMap) aaiResponse.getT().getResults().get(i));
191                     if (temp.get("service-instance") != null)
192                         aaiGetNetworkCollectionDetails.getResults().setServiceInstance(om.readValue(om.writeValueAsString(temp.get("service-instance")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.ServiceInstance.class));
193                     else if (temp.get("collection") != null)
194                         aaiGetNetworkCollectionDetails.getResults().setCollection(om.readValue(om.writeValueAsString(temp.get("collection")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Collection.class));
195                     else if (temp.get("instance-group") != null)
196                         aaiGetNetworkCollectionDetails.getResults().setInstanceGroup(om.readValue(om.writeValueAsString(temp.get("instance-group")), InstanceGroup.class));
197                     else if (temp.get("l3-network") != null)
198                         aaiGetNetworkCollectionDetails.getResults().getNetworks().add(om.readValue(om.writeValueAsString(temp.get("l3-network")), Network.class));
199                 }
200                 return new AaiResponse(aaiGetNetworkCollectionDetails, null, HttpStatus.SC_OK);
201             }
202             catch (com.fasterxml.jackson.databind.JsonMappingException e) {
203                 logger.error(EELFLoggerDelegate.errorLogger, "AAI response parsing Error",  e);
204                 return new AaiResponse(e.getCause(), "AAI response parsing Error" , HttpStatus.SC_INTERNAL_SERVER_ERROR);
205             }
206             catch (Exception e) {
207                 logger.error(EELFLoggerDelegate.errorLogger,"Exception in aai response parsing", e);
208                 return new AaiResponse(e.getCause(), "Got " + aaiResponse.getHttpCode() + " from a&ai" , aaiResponse.getHttpCode());
209             }
210         }
211         return aaiResponse;
212     }
213
214     @Override
215     public AaiResponse getPNFData(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion, String equipVendor, String equipModel) {
216         String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
217         String pnfQuery = "query/pnf-fromModel-byRegion?cloudRegionId=" + encodePathSegment(cloudRegion) + "&equipVendor=" + encodePathSegment(equipVendor) + "&equipModel=" + encodePathSegment(equipModel);
218         String payload = "{\"start\":\"" + siQuery + "\",\"query\":\"" + pnfQuery + "\"}";
219         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
220         return processAaiResponse(resp, AaiGetPnfResponse.class, null);
221     }
222
223
224     @Override
225     public AaiResponse<Pnf> getSpecificPnf(String pnfId) {
226         Response resp = doAaiGet("network/pnfs/pnf/"+pnfId, false);
227         return processAaiResponse(resp, Pnf.class, null);
228     }
229
230
231     public AaiResponse getInstanceGroupsByVnfInstanceId(String vnfInstanceId){
232         Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId + "?depth=0", false);
233         return processAaiResponse(resp, AaiGetRelatedInstanceGroupsByVnfId.class , null, null);
234     }
235
236
237     @Override
238     public List<PortDetailsTranslator.PortDetails> getPortMirroringSourcePorts(String configurationID) {
239         String payload = "{\"start\":\"/network/configurations/configuration/" + configurationID + "\",\"query\":\"query/pserver-fromConfiguration\"}";
240         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
241         resp.bufferEntity(); // avoid later "Entity input stream has already been closed" problems
242         String rawPayload = resp.readEntity(String.class);
243         AaiResponse<CustomQuerySimpleResult> aaiResponse = processAaiResponse(resp, CustomQuerySimpleResult.class, rawPayload);
244         return portDetailsTranslator.extractPortDetails(aaiResponse, rawPayload);
245     }
246
247
248
249     public AaiResponse getServiceInstance(String globalCustomerId, String serviceType, String serviceInstanceId) {
250         String getServiceInstancePath = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId+ SERVICE_SUBSCRIPTIONS_PATH +serviceType+ SERVICE_INSTANCE +serviceInstanceId;
251         Response resp = doAaiGet(getServiceInstancePath , false);
252         return processAaiResponse(resp, ServiceRelationships.class, null);
253     }
254
255     @Override
256     public AaiResponse getLogicalLink(String link) {
257         Response resp = doAaiGet("network/logical-links/logical-link/" + link , false);
258         return processAaiResponse(resp, LogicalLinkResponse.class, null);
259     }
260
261     @Override
262     public boolean isNodeTypeExistsByName(String name, ResourceType type) {
263         if (isEmpty(name)) {
264             throw new GenericUncheckedException("Empty resource-name provided to searchNodeTypeByName; request is rejected as this will cause full resources listing");
265         }
266
267         URI path = Unchecked.toURI(String.format( // e.g. GET /aai/v$/nodes/vf-modules?vf-module-name={vf-module-name}
268                 "nodes/%s?%s=%s",
269                 type.getAaiFormat(),
270                 type.getNameFilter(),
271                 encodePathSegment(name)
272         ));
273         final ResponseWithRequestInfo responseWithRequestInfo = restController.RestGet(fromAppId, UUID.randomUUID().toString(), path, false, true);
274
275         return isResourceExistByStatusCode(responseWithRequestInfo);
276     }
277
278     public Map<String, Properties> getCloudRegionAndTenantByVnfId(String vnfId) {
279         String start = "/network/generic-vnfs/generic-vnf/" + vnfId;
280         String query = "/query/cloud-region-fromVnf";
281
282         String payload = "{\"start\":[\"" + start + "\"],\"query\":\"" + query + "\"}";
283         CustomQuerySimpleResult result = typedAaiRest(QUERY_FORMAT_SIMPLE, CustomQuerySimpleResult.class, payload, HttpMethod.PUT, false);
284
285         return result.getResults().stream()
286                 .filter(res -> StringUtils.equals(res.getNodeType(), "tenant") ||
287                         StringUtils.equals(res.getNodeType(), "cloud-region"))
288                 .collect(toMap(SimpleResult::getNodeType, SimpleResult::getProperties));
289     }
290
291     @Override
292     public AaiResponse<AaiGetVnfResponse> getVnfsByParamsForChangeManagement(String subscriberId, String serviceType, @Nullable String nfRole,
293         @Nullable String cloudRegion) {
294         String payloadAsString = "";
295         ResponseWithRequestInfo response;
296         ImmutableMap<String, Serializable> payload = getMapForAAIQueryByParams(subscriberId, serviceType,
297             nfRole, cloudRegion);
298         try {
299             payloadAsString = JACKSON_OBJECT_MAPPER.writeValueAsString(payload);
300         } catch (JsonProcessingException e) {
301             logger.error(e.getMessage());
302             ExceptionUtils.rethrow(e);
303         }
304         response = doAaiPut(QUERY_FORMAT_SIMPLE, payloadAsString, false, false);
305         AaiResponseWithRequestInfo<AaiGetVnfResponse> aaiResponse = processAaiResponse(response, AaiGetVnfResponse.class, false);
306         verifyAaiResponseValidityOrThrowExc(aaiResponse, aaiResponse.getAaiResponse().getHttpCode());
307         return aaiResponse.getAaiResponse();
308     }
309
310     private ImmutableMap<String, Serializable> getMapForAAIQueryByParams(String subscriberId,
311         String serviceType, @Nullable String nfRole, @Nullable String cloudRegion) {
312         // in a case cloudRegion is null using query/vnfs-fromServiceInstance-filter,
313         // otherwise using query/vnfs-fromServiceInstance-filterByCloudRegion
314         if (nfRole != null){
315             if (cloudRegion != null){
316                 return ImmutableMap.of(
317                     "start", ImmutableList
318                         .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
319                     "query",  "query/vnfs-fromServiceInstance-filterByCloudRegion?nfRole=" + nfRole + "&cloudRegionID=" + cloudRegion + ""
320                 );
321             }else {
322                 return ImmutableMap.of(
323                     "start", ImmutableList
324                         .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
325                     "query",  "query/vnfs-fromServiceInstance-filter?nfRole=" + nfRole + ""
326                 );
327             }
328         }
329
330         if (cloudRegion != null){
331             return ImmutableMap.of(
332                 "start", ImmutableList
333                     .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
334                 "query",  "query/vnfs-fromServiceInstance-filterByCloudRegion?cloudRegionID=" + cloudRegion + ""
335             );
336         }
337
338         return ImmutableMap.of(
339             "start", ImmutableList
340                 .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
341             "query", "query/vnfs-fromServiceInstance-filter"
342         );
343     }
344
345     private boolean isResourceExistByStatusCode(ResponseWithRequestInfo responseWithRequestInfo) {
346         // 200 - is found
347         // 404 - resource not found
348         Response.Status statusInfo = responseWithRequestInfo.getResponse().getStatusInfo().toEnum();
349         switch (statusInfo) {
350             case OK:
351                 return true;
352             case NOT_FOUND:
353                 return false;
354             default:
355                 throw new GenericUncheckedException("Unexpected response-code (only OK and NOT_FOUND are expected): " +
356                         responseWithRequestInfo.getResponse().getStatusInfo());
357         }
358     }
359
360     @Override
361     public <T> T typedAaiGet(URI uri, Class<T> clz) {
362         return typedAaiRest(uri, clz, null, HttpMethod.GET, false);
363     }
364
365     public <T> T typedAaiRest(String path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
366         return typedAaiRest(Unchecked.toURI(path), clz, payload, method, propagateExceptions);
367     }
368
369
370     public <T> T typedAaiRest(URI path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
371         ResponseWithRequestInfo responseWithRequestInfo;
372         try {
373             responseWithRequestInfo = restController.doRest(fromAppId, UUID.randomUUID().toString(), path, payload, method, false, propagateExceptions);
374         } catch (Exception e) {
375             responseWithRequestInfo = handleExceptionFromRestCall(propagateExceptions, "doAai"+method.name(), e);
376         }
377
378         final AaiResponseWithRequestInfo<T> aaiResponse = processAaiResponse(responseWithRequestInfo, clz, VidObjectMapperType.FASTERXML, true);
379         verifyAaiResponseValidityOrThrowExc(aaiResponse, responseWithRequestInfo.getResponse().getStatus());
380         return aaiResponse.getAaiResponse().getT();
381     }
382
383     private void verifyAaiResponseValidityOrThrowExc(AaiResponseWithRequestInfo aaiResponse, int httpCode) {
384         if (aaiResponse.getAaiResponse().getHttpCode() > 399 || aaiResponse.getAaiResponse().getT() == null) {
385             throw new ExceptionWithRequestInfo(aaiResponse.getHttpMethod(),
386                 aaiResponse.getRequestedUrl(),
387                 aaiResponse.getRawData(),
388                 httpCode,
389                 new InvalidAAIResponseException(aaiResponse.getAaiResponse()));
390         }
391     }
392
393     private String getUrlFromLIst(String url, String paramKey, List<String> params){
394         int i = 0;
395         for(String param: params){
396             i ++;
397             url = url.concat(paramKey);
398             String encodedParam= param;
399             try {
400                 encodedParam= URLEncoder.encode(param, "UTF-8");
401             } catch (UnsupportedEncodingException e) {
402                 String methodName = "getUrlFromList";
403                 logger.error(EELFLoggerDelegate.errorLogger, methodName, e);
404                 logger.debug(EELFLoggerDelegate.debugLogger, methodName, e);
405             }
406             url = url.concat(encodedParam);
407             if(i != params.size()){
408                 url = url.concat("&");
409             }
410         }
411         return url;
412     }
413
414
415
416     @Override
417     public AaiResponse<SubscriberList> getAllSubscribers() {
418         return getFromCache("getAllSubscribers", this::getAllSubscribersNonCached, true, "Failed to get all subscribers");
419     }
420
421     private <K> AaiResponse getFromCache(String cacheName, Function<K, AaiResponse> function, K argument, String errorMessage) {
422         try {
423             return cacheProvider
424                     .aaiClientCacheFor(cacheName, function)
425                     .get(argument);
426         } catch (ExceptionWithRequestInfo exception) {
427             logger.error(errorMessage, exception);
428             return new AaiResponse(null, exception.getRawData(), exception.getHttpCode());
429         }
430         catch (Exception exception) {
431             logger.error(errorMessage, exception);
432             return new AaiResponse(null, exception.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR);
433         }
434     }
435
436     private AaiResponse<SubscriberList> getAllSubscribersNonCached(boolean propagateExceptions) {
437         AaiResponse<SubscriberList> aaiResponse = getAllSubscribers(propagateExceptions).getAaiResponse();
438         if (propagateExceptions && (aaiResponse.getT() == null || aaiResponse.getT().customer == null || aaiResponse.getT().customer.isEmpty())) {
439             throw new GenericUncheckedException("Failed to get Subscribers data. The data is null or empty.");
440         } else {
441             return aaiResponse;
442         }
443     }
444
445     AaiResponseWithRequestInfo<SubscriberList> getAllSubscribers(boolean propagateExceptions){
446         String depth = "0";
447         ResponseWithRequestInfo aaiGetResult = doAaiGet("business/customers?subscriber-type=INFRA&depth=" + depth, false, propagateExceptions);
448         AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = processAaiResponse(aaiGetResult, SubscriberList.class, propagateExceptions);
449         responseWithRequestInfo.setRequestedUrl(aaiGetResult.getRequestUrl());
450         responseWithRequestInfo.setHttpMethod(aaiGetResult.getRequestHttpMethod());
451         return responseWithRequestInfo;
452     }
453
454
455     @Override
456     public AaiResponse getAllAicZones() {
457         Response resp = doAaiGet("network/zones", false);
458         return processAaiResponse(resp, AicZones.class, null);
459     }
460
461     @Override
462     public AaiResponse<AaiGetVnfResponse> getVNFData(String globalSubscriberId, String serviceType) {
463         String payload = "{\"start\": [\"business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) +"/service-instances\"]," +
464                 "\"query\": \"query/vnf-topology-fromServiceInstance\"}";
465         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
466         return processAaiResponse(resp, AaiGetVnfResponse.class, null);
467     }
468
469     @Override
470     public AaiResponse getVNFData(String globalSubscriberId, String serviceType, String serviceInstanceId) {
471         String payload = "{\"start\": [\"/business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + SERVICE_INSTANCE + serviceInstanceId + "\"],       \"query\": \"query/vnf-topology-fromServiceInstance\"}";
472         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
473         return processAaiResponse(resp, AaiGetVnfResponse.class, null);
474     }
475
476     @Override
477     public Response getVersionByInvariantId(List<String> modelInvariantId) {
478         if (modelInvariantId.isEmpty()) {
479             throw new GenericUncheckedException("Zero invariant-ids provided to getVersionByInvariantId; request is rejected as this will cause full models listing");
480         }
481
482         StringBuilder sb = new StringBuilder();
483         for (String id : modelInvariantId){
484             sb.append(MODEL_INVARIANT_ID);
485             sb.append(id);
486
487         }
488         return doAaiGet("service-design-and-creation/models?depth=2" + sb.toString(), false);
489     }
490
491     @Override
492     public ModelVer getLatestVersionByInvariantId(String modelInvariantId) {
493         if (modelInvariantId.isEmpty()) {
494             throw new GenericUncheckedException("no invariant-id provided to getLatestVersionByInvariantId; request is rejected");
495         }
496
497         Response response = doAaiPut("query?format=resource&depth=0",  "{\"start\": [\"service-design-and-creation/models/model/" + modelInvariantId + "\"],\"query\": \"query/serviceModels-byDistributionStatus?distributionStatus=DISTRIBUTION_COMPLETE_OK\"}",false);
498         AaiResponse<ModelVersions> aaiResponse = processAaiResponse(response, ModelVersions.class, null, VidObjectMapperType.FASTERXML);
499
500         Stream<ModelVer> modelVerStream = toModelVerStream(aaiResponse.getT());
501         return maxModelVer(modelVerStream);
502     }
503
504     protected Stream<ModelVer> toModelVerStream(ModelVersions modelVersions) {
505         if (modelVersions == null)
506             return null;
507
508         if (modelVersions == null)
509             return null;
510
511         return Stream.of(modelVersions)
512                 .map(ModelVersions::getResults)
513                 .flatMap(java.util.Collection::stream)
514                 .flatMap(map -> map.entrySet().stream())
515                 .filter(kv -> StringUtils.equals(kv.getKey(), "model-ver"))
516                 .map(Map.Entry::getValue);
517
518     }
519
520     protected ModelVer maxModelVer(Stream<ModelVer> modelVerStream) {
521         if (modelVerStream == null)
522             return null;
523
524         return modelVerStream
525                 .filter(modelVer -> StringUtils.isNotEmpty(modelVer.getModelVersion()))
526                 .max(comparing(ModelVer::getModelVersion, comparing(DefaultArtifactVersion::new)))
527                 .orElseThrow(() -> new GenericUncheckedException("Could not find any version"));
528     }
529
530     @Override
531     public AaiResponse getSubscriberData(String subscriberId, boolean omitServiceInstances) {
532         String depth = omitServiceInstances ? "1" : "2";
533         AaiResponse subscriberDataResponse;
534         Response resp = doAaiGet(BUSINESS_CUSTOMERS_CUSTOMER + subscriberId + "?depth=" + depth, false);
535         subscriberDataResponse = processAaiResponse(resp, Services.class, null);
536         return subscriberDataResponse;
537     }
538
539     @Override
540     public AaiResponse getServices() {
541         Response resp = doAaiGet("service-design-and-creation/services", false);
542         return processAaiResponse(resp, GetServicesAAIRespone.class, null);
543     }
544
545     @Override
546     public AaiResponse getOperationalEnvironments(String operationalEnvironmentType, String operationalEnvironmentStatus) {
547         String url = "cloud-infrastructure/operational-environments";
548         URIBuilder urlBuilder  = new URIBuilder();
549         if (operationalEnvironmentType != null)
550             urlBuilder.addParameter("operational-environment-type", operationalEnvironmentType);
551         if (operationalEnvironmentStatus != null)
552             urlBuilder.addParameter("operational-environment-status", operationalEnvironmentStatus);
553         url += urlBuilder.toString();
554         Response resp = doAaiGet(url, false);
555         return processAaiResponse(resp, OperationalEnvironmentList.class, null);
556     }
557
558     @Override
559     public AaiResponse getTenants(String globalCustomerId, String serviceType) {
560         if ((globalCustomerId == null || globalCustomerId.isEmpty()) || ((serviceType == null) || (serviceType.isEmpty()))){
561             return buildAaiResponseForGetTenantsFailure(" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.");
562         }
563         try {
564             return cacheProvider
565                     .aaiClientCacheFor("getTenants", this::getTenantsByKey)
566                     .get(CacheProvider.compileKey(globalCustomerId, serviceType));
567         }
568         catch (ParsingGetTenantsResponseFailure exception) {
569             logger.error("Failed to get tenants ", exception);
570             return buildAaiResponseForGetTenantsFailure(exception.getMessage());
571         }
572     }
573
574     @Override
575     public AaiResponse getNodeTemplateInstances(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion) {
576
577         String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
578         String vnfQuery = "query/queryvnfFromModelbyRegion?cloudRegionId=" + encodePathSegment(cloudRegion);
579         String payload1 = "{\"start\":\"" + siQuery + "\",\"query\":\"" + vnfQuery + "\"}";
580
581         Response resp1 = doAaiPut(QUERY_FORMAT_SIMPLE, payload1, false);
582         AaiResponse aaiResponse1 = processAaiResponse(resp1, AaiGetVnfResponse.class, null);
583         logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse1);
584         return aaiResponse1;
585     }
586
587     @Override
588     public AaiResponse<JsonNode> getCloudRegionAndSourceByPortMirroringConfigurationId(String configurationId) {
589         final String start = "[\"network/configurations/configuration/" + configurationId + "\"]";
590         final String query = "\"query/cloud-region-and-source-FromConfiguration\"";
591         String payload = "{\"start\":" + start + ",\"query\":" + query + "}";
592
593         Response response = doAaiPut("query?format=simple&nodesOnly=true", payload, false);
594         AaiResponse<JsonNode> aaiResponse = processAaiResponse(response, JsonNode.class, null);
595
596         logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse);
597         return aaiResponse;
598     }
599
600     private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, boolean propagateExceptions) {
601         return processAaiResponse(responseWithRequestInfo, classType, VidObjectMapperType.CODEHAUS, propagateExceptions);
602     }
603
604     private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, VidObjectMapperType omType, boolean propagateExceptions) {
605         String responseBody = null;
606         Integer responseHttpCode = null;
607         try {
608             Response response = responseWithRequestInfo.getResponse();
609             responseHttpCode = (response != null) ? response.getStatus() : null;
610             responseBody = (response != null) ? response.readEntity(String.class) : null;
611             AaiResponse<T> processedAaiResponse = processAaiResponse(response, classType, responseBody, omType, propagateExceptions);
612             return new AaiResponseWithRequestInfo<>(responseWithRequestInfo.getRequestHttpMethod(), responseWithRequestInfo.getRequestUrl(), processedAaiResponse,
613                     responseBody);
614         } catch (Exception e) {
615             throw new ExceptionWithRequestInfo(responseWithRequestInfo.getRequestHttpMethod(),
616                     responseWithRequestInfo.getRequestUrl(), responseBody, responseHttpCode, e);
617         }
618     }
619
620     private AaiResponse processAaiResponse(Response resp, Class classType, String responseBody) {
621         return processAaiResponse(resp, classType, responseBody, VidObjectMapperType.CODEHAUS);
622     }
623
624     private <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType) {
625         return processAaiResponse(resp, classType, responseBody, omType, false);
626     }
627
628     private  <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
629         AaiResponse<T> subscriberDataResponse;
630         if (resp == null) {
631             subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
632             logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
633         } else {
634             logger.debug(EELFLoggerDelegate.debugLogger, "getSubscribers() resp=" + resp.getStatusInfo().toString());
635             if (resp.getStatus() != HttpStatus.SC_OK) {
636                 subscriberDataResponse = processFailureResponse(resp,responseBody);
637             } else {
638                 subscriberDataResponse = processOkResponse(resp, classType, responseBody, omType, propagateExceptions);
639             }
640         }
641         return subscriberDataResponse;
642     }
643
644     private AaiResponse processFailureResponse(Response resp, String responseBody) {
645         logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
646         String rawData;
647         if (responseBody != null) {
648             rawData = responseBody;
649         } else {
650             rawData = resp.readEntity(String.class);
651         }
652         return new AaiResponse<>(null, rawData, resp.getStatus());
653     }
654
655     private <T> AaiResponse<T> processOkResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
656         AaiResponse<T> subscriberDataResponse;
657         String finalResponse = null;
658         try {
659             if (responseBody != null) {
660                 finalResponse = responseBody;
661             } else {
662                 finalResponse = resp.readEntity(String.class);
663             }
664
665             if(omType == VidObjectMapperType.CODEHAUS)
666                 subscriberDataResponse = parseCodeHausObject(classType, finalResponse);
667             else
668                 subscriberDataResponse = parseFasterXmlObject(classType, finalResponse);
669
670         } catch(Exception e){
671             if (propagateExceptions) {
672                 throw new GenericUncheckedException(e);
673             } else {
674                 subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
675                 logger.error("Failed to parse aai response: \"{}\" to class {}", finalResponse, classType, e);
676             }
677         }
678         return subscriberDataResponse;
679     }
680
681     private <T> AaiResponse<T> parseFasterXmlObject(Class<? extends T> classType, String finalResponse) throws IOException {
682         return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
683     }
684
685     private <T> AaiResponse<T> parseCodeHausObject(Class<? extends T> classType, String finalResponse) throws IOException {
686         return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
687     }
688
689     @Override
690     public Response doAaiGet(String uri, boolean xml) {
691         return doAaiGet(uri, xml, false).getResponse();
692     }
693
694
695     public ResponseWithRequestInfo doAaiGet(String uri, boolean xml, boolean propagateExceptions) {
696         return doAaiGet(Unchecked.toURI(uri), xml, propagateExceptions);
697     }
698
699     public ResponseWithRequestInfo doAaiGet(URI uri, boolean xml, boolean propagateExceptions) {
700         String methodName = "doAaiGet";
701         logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
702
703         ResponseWithRequestInfo resp;
704         try {
705             resp = restController.RestGet(fromAppId, UUID.randomUUID().toString(), uri, xml, propagateExceptions);
706
707         } catch (Exception e) {
708             resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
709         }
710         return resp;
711     }
712
713     @NotNull
714     protected ResponseWithRequestInfo handleExceptionFromRestCall(boolean propagateExceptions, String methodName, Exception e) {
715         ResponseWithRequestInfo resp;
716         if (propagateExceptions) {
717             throw (e instanceof RuntimeException) ? (RuntimeException)e : new GenericUncheckedException(e);
718         } else {
719             final Exception actual =
720                     e instanceof ExceptionWithRequestInfo ? (Exception) e.getCause() : e;
721
722             final String message =
723                     actual instanceof WebApplicationException ? ((WebApplicationException) actual).getResponse().readEntity(String.class) : e.toString();
724
725             //ToDo: change parameter of requestUrl to real url from doRest function
726             resp = new ResponseWithRequestInfo(null, null, org.springframework.http.HttpMethod.GET);
727             logger.info(EELFLoggerDelegate.errorLogger, methodName + message);
728             logger.debug(EELFLoggerDelegate.debugLogger, methodName + message);
729         }
730         return resp;
731     }
732
733     private String parseForTenantsByServiceSubscription(String relatedToKey, String resp) {
734         String tenantList = "";
735
736         try {
737             JSONParser jsonParser = new JSONParser();
738
739             JSONObject jsonObject = (JSONObject) jsonParser.parse(resp);
740
741             return parseServiceSubscriptionObjectForTenants(relatedToKey, jsonObject);
742         } catch (Exception ex) {
743             logger.debug(EELFLoggerDelegate.debugLogger, "parseForTenantsByServiceSubscription error while parsing tenants by service subscription", ex);
744         }
745         return tenantList;
746     }
747
748     protected Response doAaiPut(String uri, String payload, boolean xml) {
749         return doAaiPut(uri, payload, xml, false).getResponse();
750     }
751
752     protected ResponseWithRequestInfo doAaiPut(String uri, String payload, boolean xml, boolean propagateExceptions) {
753         String methodName = "doAaiPut";
754         logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
755
756         ResponseWithRequestInfo resp;
757         try {
758
759             resp = restController.RestPut(fromAppId, uri, payload, xml, propagateExceptions);
760
761         } catch (Exception e) {
762             resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
763         }
764         return resp;
765     }
766
767
768     private String parseServiceSubscriptionObjectForTenants(String relatedToKey, JSONObject jsonObject) {
769         JSONArray tenantArray = new JSONArray();
770         boolean bconvert = false;
771         try {
772             JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list");
773             if (relationShipListsObj != null) {
774                 JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship");
775                 for (Object innerObj : defaultIfNull(rShipArray, emptyList())) {
776                     if (innerObj != null) {
777                         bconvert = parseTenant(relatedToKey, tenantArray, bconvert, (JSONObject) innerObj);
778                     }
779                 }
780             }
781         } catch (NullPointerException ex) {
782             logger.debug(EELFLoggerDelegate.debugLogger, "parseServiceSubscriptionObjectForTenants. error while parsing service subscription object for tenants", ex);
783         }
784
785         if (bconvert)
786             return tenantArray.toJSONString();
787         else
788             return "";
789
790     }
791
792     private static boolean parseTenant(String relatedToKey, JSONArray tenantArray, boolean bconvert, JSONObject inner1Obj) {
793         String relatedTo = checkForNull((String) inner1Obj.get("related-to"));
794         if (relatedTo.equalsIgnoreCase(relatedToKey)) {
795             JSONObject tenantNewObj = new JSONObject();
796
797             String relatedLink = checkForNull((String) inner1Obj.get("related-link"));
798             tenantNewObj.put("link", relatedLink);
799
800             JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data");
801             for (Object innerObj : defaultIfNull(rDataArray, emptyList())) {
802                 parseRelationShip(tenantNewObj, (JSONObject) innerObj);
803             }
804
805             JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property");
806             for (Object innerObj : defaultIfNull(relatedTPropArray, emptyList())) {
807                 parseRelatedTProp(tenantNewObj, (JSONObject) innerObj);
808             }
809             bconvert = true;
810             tenantArray.add(tenantNewObj);
811         }
812         return bconvert;
813     }
814
815     private static void parseRelatedTProp(JSONObject tenantNewObj, JSONObject innerObj) {
816         if (innerObj == null)
817             return;
818
819         String propKey = checkForNull((String) innerObj.get("property-key"));
820         String propVal = checkForNull((String) innerObj.get("property-value"));
821         if (equalsIgnoreCase(propKey, "tenant.tenant-name")) {
822             tenantNewObj.put("tenantName", propVal);
823         }
824     }
825
826     private static void parseRelationShip(JSONObject tenantNewObj, JSONObject inner2Obj) {
827         if (inner2Obj == null)
828             return;
829
830         String rShipKey = checkForNull((String) inner2Obj.get("relationship-key"));
831         String rShipVal = checkForNull((String) inner2Obj.get("relationship-value"));
832         if (equalsIgnoreCase(rShipKey, "cloud-region.cloud-owner")) {
833             tenantNewObj.put("cloudOwner", rShipVal);
834         } else if (equalsIgnoreCase(rShipKey, "cloud-region.cloud-region-id")) {
835             tenantNewObj.put("cloudRegionID", rShipVal);
836         } else if (equalsIgnoreCase(rShipKey, "tenant.tenant-id")) {
837             tenantNewObj.put("tenantID", rShipVal);
838         }
839     }
840
841     private static String encodePathSegment(String segmentToEncode) {
842         return UriUtils.encodePathSegment(segmentToEncode, "UTF-8");
843     }
844
845     @Override
846     public ExternalComponentStatus probeComponent(){
847         long startTime = System.currentTimeMillis();
848         try {
849             AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = getAllSubscribers(true);
850             AaiResponse<SubscriberList> aaiResponse = responseWithRequestInfo.getAaiResponse();
851             long duration = System.currentTimeMillis() - startTime;
852
853             SubscriberList subscribersList = (aaiResponse != null) ? aaiResponse.getT() : null;
854             boolean isAvailable = subscribersList != null && subscribersList.customer != null && !subscribersList.customer.isEmpty();
855
856             HttpRequestMetadata metadata = new HttpRequestMetadata(
857                     responseWithRequestInfo.getHttpMethod(),
858                     (aaiResponse != null) ? aaiResponse.getHttpCode() : 0,
859                     responseWithRequestInfo.getRequestedUrl(),
860                     responseWithRequestInfo.getRawData(),
861                     isAvailable ? "OK" : "No subscriber received",
862                     duration
863             );
864             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, isAvailable, metadata);
865
866         } catch (ExceptionWithRequestInfo e) {
867             long duration = System.currentTimeMillis() - startTime;
868             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
869                     new HttpRequestMetadata(e, duration));
870         } catch (Exception e) {
871             long duration = System.currentTimeMillis() - startTime;
872             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
873                     new ErrorMetadata(Logging.exceptionToDescription(e), duration));
874         }
875     }
876
877     @Override
878     public String getCloudOwnerByCloudRegionId(String cloudRegionId) {
879         return cacheProvider
880                 .aaiClientCacheFor("getCloudOwnerByCloudRegionId", this::getCloudOwnerByCloudRegionIdNonCached)
881                 .get(cloudRegionId);
882     }
883
884
885     @Override
886     public GetTenantsResponse getHomingDataByVfModule(String vnfInstanceId, String vfModuleId) {
887
888         if (isEmpty(vnfInstanceId)|| isEmpty(vfModuleId)){
889             throw new GenericUncheckedException("Failed to retrieve homing data associated to vfModule from A&AI, VNF InstanceId or VF Module Id is missing.");
890         }
891         Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId +"/vf-modules/vf-module/"+ vfModuleId, false);
892         String responseAsString = parseForTenantsByServiceSubscription("vserver",resp.readEntity(String.class));
893         if (isEmpty(responseAsString)){
894             throw new GenericUncheckedException( String.format("A&AI has no homing data associated to vfModule '%s' of vnf '%s'", vfModuleId, vnfInstanceId));
895         }
896         else {
897             AaiResponse aaiResponse = processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
898             return ((GetTenantsResponse[])aaiResponse.getT())[0];
899         }
900     }
901
902     @Override
903     public void resetCache(String cacheName) {
904         cacheProvider.resetCache(cacheName);
905     }
906
907     String getCloudOwnerByCloudRegionIdNonCached(String cloudRegionId) {
908         String uri = "cloud-infrastructure/cloud-regions?cloud-region-id=" + encodePathSegment(cloudRegionId);
909
910         final CloudRegion.Collection cloudRegionCollection =
911                 typedAaiGet(Unchecked.toURI(uri), CloudRegion.Collection.class);
912
913         return cloudRegionCollection
914                 .getCloudRegions().stream()
915                 .map(CloudRegion::getCloudOwner)
916                 // from here we assure that the cloud owner is given, and not null
917                 // and non-empty, and that if more than one cloud-owner is given -
918                 // it is only a single value.
919                 // exception is thrown if none or more than a single values are
920                 // given.
921                 .filter(StringUtils::isNotEmpty)
922                 .distinct()
923                 .reduce((a, b) -> {
924                     // will be invoked only if distinct() leaves more than a single element
925                     throw new GenericUncheckedException("Conflicting cloud-owner found for " + cloudRegionId + ": '" + a + "' / '" + b + "'");
926                 })
927                 .orElseThrow(() -> new GenericUncheckedException("No cloud-owner found for " + cloudRegionId));
928     }
929
930     private AaiResponse getTenantsByKey(String key) {
931         String[] args = CacheProvider.decompileKey(key);
932         String globalCustomerId = safeGetFromArray(args, 0);
933         String serviceType = safeGetFromArray(args, 1);
934         return getTenantsNonCached(globalCustomerId, serviceType);
935     }
936
937     AaiResponse getTenantsNonCached(String globalCustomerId, String serviceType) {
938         String url = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + serviceType;
939
940         Response resp = doAaiGet(url, false);
941         String responseAsString = parseForTenantsByServiceSubscription("tenant",resp.readEntity(String.class));
942         if (isEmpty(responseAsString)){
943            throw new ParsingGetTenantsResponseFailure(String.format("A&AI has no LCP Region & Tenants associated to subscriber '%s' and service type '%s'", globalCustomerId, serviceType));
944         }
945         else {
946             return processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
947         }
948     }
949
950     public static class ParsingGetTenantsResponseFailure extends GenericUncheckedException {
951
952         public ParsingGetTenantsResponseFailure(String message) {
953             super(message);
954         }
955     }
956
957     @NotNull
958     private AaiResponse<String> buildAaiResponseForGetTenantsFailure(String errorText) {
959         return new AaiResponse<>(null, String.format("{\"statusText\":\"%s\"}", errorText), HttpStatus.SC_INTERNAL_SERVER_ERROR);
960     }
961
962     private static String safeGetFromArray(String[] array, int i) {
963         if (i < 0 || i >= array.length) {
964             return null;
965         } else {
966             return array[i];
967         }
968     }
969 }