diff options
Diffstat (limited to 'pomba/service-decomposition/src/main/java')
11 files changed, 995 insertions, 0 deletions
diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AAIConfiguration.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AAIConfiguration.java new file mode 100644 index 0000000..fedb64c --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AAIConfiguration.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition; + +import org.onap.aai.restclient.client.RestClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +public class AAIConfiguration { + @Autowired + @Value("${aai.host}") + private String host; + + @Autowired + @Value("${aai.port}") + private String port; + + @Autowired + @Value("${aai.httpProtocol}") + private String httpProtocol; + + @Autowired + @Value("${aai.trustStorePath}") + private String trustStorePath; + + @Autowired + @Value("${aai.keyStorePath}") + private String keyStorePath; + + @Autowired + @Value("${aai.keyStorePassword}") + private String keyStorePassword; + + @Autowired + @Value("${aai.keyManagerFactoryAlgorithm}") + private String keyManagerFactoryAlgorithm; + + @Autowired + @Value("${aai.keyStoreType}") + private String keyStoreType; + + @Autowired + @Value("${aai.securityProtocol}") + private String securityProtocol; + + @Autowired + @Value("${aai.connectionTimeout}") + private Integer connectionTimeout; + + @Autowired + @Value("${aai.readTimeout}") + private Integer readTimeout; + + @Autowired + @Value("${aai.serviceInstancePath}") + private String serviceInstancePath; + + @Bean(name="aaiClient") + public RestClient restClient() { + return new RestClient() + .validateServerHostname(false) + .validateServerCertChain(false) + .connectTimeoutMs(this.connectionTimeout) + .readTimeoutMs(this.readTimeout); + } + + @Bean(name="aaiBaseUrl") + public String getURL() { + return this.httpProtocol + "://" + this.host + ":" + this.port; + } + + @Bean(name="aaiServiceInstancePath") + public String getServiceInstancePath() { + return this.serviceInstancePath; + } + +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/Application.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/Application.java new file mode 100644 index 0000000..58b00d8 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/Application.java @@ -0,0 +1,44 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; + +@SpringBootApplication +@ComponentScan(basePackages = "org.onap.sdnc.apps.pomba.servicedecomposition") +@EnableAsync +@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) +public class Application extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Application.class); + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(Application.class, args); + } + +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AuthorizationConfiguration.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AuthorizationConfiguration.java new file mode 100644 index 0000000..f54f387 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/AuthorizationConfiguration.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition; + +import java.util.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +public class AuthorizationConfiguration { + + @Value("${basicAuth.username:admin}") + private String username; + + @Value("${basicAuth.password:admin}") + private String password; + + @Bean(name="basicAuthHeader") + public String getBasicAuthHeader() { + return "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.password).getBytes()); + } + + +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/JerseyConfiguration.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/JerseyConfiguration.java new file mode 100644 index 0000000..815adf0 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/JerseyConfiguration.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import java.util.logging.Logger; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.logging.LoggingFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletProperties; +import org.onap.sdnc.apps.pomba.servicedecomposition.service.rs.RestServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Component +@ApplicationPath("/") +public class JerseyConfiguration extends ResourceConfig { + private static final Logger log = Logger.getLogger(JerseyConfiguration.class.getName()); + + @Bean + @Primary + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + return objectMapper; + } + + @Autowired + public JerseyConfiguration() { + register(RestServiceImpl.class); + property(ServletProperties.FILTER_FORWARD_ON_404, true); + register(new LoggingFeature(log)); + } + + @Bean + public Client jerseyClient() { + return ClientBuilder.newClient(new ClientConfig()); + } +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/WebConfiguration.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/WebConfiguration.java new file mode 100644 index 0000000..0d0c5db --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/WebConfiguration.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class WebConfiguration { + + @Bean + public WebMvcConfigurerAdapter forwardToIndex() { + return new WebMvcConfigurerAdapter() { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/swagger").setViewName("redirect:/swagger/index.html"); + registry.addViewController("/swagger/").setViewName("redirect:/swagger/index.html"); + registry.addViewController("/docs").setViewName("redirect:/docs/html/index.html"); + registry.addViewController("/docs/").setViewName("redirect:/docs/html/index.html"); + } + }; + } +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/exception/DiscoveryException.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/exception/DiscoveryException.java new file mode 100644 index 0000000..107f98b --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/exception/DiscoveryException.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.exception; + +import javax.ws.rs.core.Response.Status; + +public class DiscoveryException extends Exception { + public static enum Error { + FETCH_RESOURCE_FAILED("SD.0001", "A&AI query failed: %s"), + RELATIONSHIP_LINK_PARSE_ERROR("SD.0002", "Error in parsing relationship link"), + SERVICE_INSTANCE_NOT_FOUND("SD.0003", "Service Instance not Found"), + SERVICE_RELATIONSHIP_PARSE_ERROR("SD.0004", "Error in parsing service-instance relationships: %s"), + INVALID_URL("SD.0005", "Invalid request URL, missing parameter: service-instance-id"), + MISSING_HEADER("SD.0006", "Missing header parameter: %s"), + GENERAL_FAILURE("SD.0007", "An error occurred: %s"), + UNAUTHORIZED("SD.00008", "Unauthorized"); + + private final String responseCode; + private final String message; + + private Error(String responseCode, String message) { + this.responseCode = responseCode; + this.message = message; + } + + public String getMessage(Object... args) { + if (args == null || args.length == 0) { + return this.message; + } + return String.format(this.message, args); + } + + public String getResponseCode() { + return this.responseCode; + } + } + + private static final long serialVersionUID = -4874149714911165454L; + + private final Status httpStatus; + private String responseCode; + + public DiscoveryException(Error error, Status httpStatus, Object... args) { + super(error.getMessage(args)); + if (httpStatus == null) { + throw new NullPointerException("httpStatus"); + } + this.responseCode = error.getResponseCode(); + this.httpStatus = httpStatus; + } + + public DiscoveryException(Error error, Exception cause, Object... args) { + super(error.getMessage(args), cause); + this.httpStatus = Status.INTERNAL_SERVER_ERROR; + } + + public String getResponseCode() { + return this.responseCode; + } + + public Status getHttpStatus() { + return this.httpStatus; + } +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringService.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringService.java new file mode 100644 index 0000000..9d2fd84 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringService.java @@ -0,0 +1,30 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.service; + +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException; + +public interface SpringService { + + public String decomposeService(String fromAppId, + String transactionId, + String serviceInstanceId, + ONAPLogAdapter adapter) throws DiscoveryException; + +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringServiceImpl.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringServiceImpl.java new file mode 100644 index 0000000..4a0f0ed --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/SpringServiceImpl.java @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.service; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.json.JSONObject; +import org.onap.aai.restclient.client.RestClient; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException; +import org.onap.sdnc.apps.pomba.servicedecomposition.util.RestUtil; +import org.springframework.beans.factory.annotation.Autowired; + + +@org.springframework.stereotype.Service +public class SpringServiceImpl implements SpringService { + private static EELFLogger log = EELFManager.getInstance().getLogger(SpringServiceImpl.class); + public static String APP_NAME = "ServiceDiscovery"; + + @Autowired + private RestClient aaiClient; + + @Autowired + private String aaiBaseUrl; + + @Autowired + private String aaiServiceInstancePath; + + @Override + public String decomposeService(String fromAppId, + String transactionId, + String serviceInstanceId, + ONAPLogAdapter adapter) throws DiscoveryException { + + + log.info("Querying A&AI for service instance " + serviceInstanceId); + JSONObject serviceInstance = RestUtil.retrieveAAIModelData(aaiClient, aaiBaseUrl, aaiServiceInstancePath, + transactionId, serviceInstanceId, adapter); + return serviceInstance.toString(); + } + + +}
\ No newline at end of file diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestService.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestService.java new file mode 100644 index 0000000..f887d8f --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestService.java @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.service.rs; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.onap.logging.ref.slf4j.ONAPLogConstants; + +@Api(authorizations = @Authorization("basicAuth")) +@Path("/service") +@Produces(MediaType.APPLICATION_JSON) +public interface RestService { + + public static final String SERVICE_NAME = "service-decomposition"; + + @GET + @Path("/context") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Fetch network info for service", + notes = "Returns a A&AI JSON object augmented with related entities" + ) + @ApiResponses( + value = { + @ApiResponse(code = 200, message = "OK"), + @ApiResponse(code = 400, message = "Bad Request"), + @ApiResponse(code = 404, message = "Service not available"), + @ApiResponse(code = 500, message = "Unexpected Runtime error") + }) + public Response getContext( + @Context HttpServletRequest request, + @HeaderParam(HttpHeaders.AUTHORIZATION) @ApiParam(hidden=true) String authorization, + @HeaderParam(ONAPLogConstants.Headers.PARTNER_NAME) @ApiParam(required=true) String xFromAppId, + @HeaderParam(ONAPLogConstants.Headers.REQUEST_ID) String xTransactionId, + @QueryParam("serviceInstanceId") @ApiParam(required=true) String serviceInstanceId); +}
\ No newline at end of file diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestServiceImpl.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestServiceImpl.java new file mode 100644 index 0000000..19734f0 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/service/rs/RestServiceImpl.java @@ -0,0 +1,108 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.service.rs; + +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.*; + +import java.util.UUID; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.onap.logging.ref.slf4j.ONAPLogConstants.ResponseStatus; +import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException; +import org.onap.sdnc.apps.pomba.servicedecomposition.service.SpringService; +import org.onap.sdnc.apps.pomba.servicedecomposition.util.RestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RestServiceImpl implements RestService { + private static Logger log = LoggerFactory.getLogger(RestServiceImpl.class); + private static final String EMPTY_JSON_OBJECT = "{}"; + + @Autowired + private SpringService service; + + @Resource(name="basicAuthHeader") + private String basicAuthHeader; + + public RestServiceImpl() {} + + @Override + public Response getContext(HttpServletRequest request, + String authorization, + String fromAppId, + String transactionId, + String serviceInstanceId) { + + ONAPLogAdapter adapter = new ONAPLogAdapter(log); + adapter.getServiceDescriptor().setServiceName(SERVICE_NAME); + adapter.entering(request); + try { + if (!this.basicAuthHeader.equals(authorization)) { + throw new DiscoveryException(UNAUTHORIZED, Status.UNAUTHORIZED); + } + + // Do some validation on Http headers and URL parameters + if ((fromAppId == null) || fromAppId.trim().isEmpty()) { + throw new DiscoveryException(MISSING_HEADER, Status.BAD_REQUEST, ONAPLogConstants.Headers.PARTNER_NAME); + } + if (transactionId == null || transactionId.isEmpty()) { + transactionId = UUID.randomUUID().toString(); + log.debug(ONAPLogConstants.Headers.REQUEST_ID+ " is missing; using newly generated value: " + transactionId); + } + RestUtil.validateURL(serviceInstanceId); + String context = service.decomposeService(fromAppId, transactionId, serviceInstanceId, adapter); + + if (context == null) { + context = EMPTY_JSON_OBJECT; + } + adapter.getResponseDescriptor().setResponseStatus(ResponseStatus.COMPLETED); + return Response.ok().entity(context).build(); + + } catch (DiscoveryException x) { + adapter.getResponseDescriptor() + .setResponseCode(x.getResponseCode()) + .setResponseStatus(ResponseStatus.ERROR); + + ResponseBuilder builder = Response.status(x.getHttpStatus()).entity(x.getMessage()); + if (authorization == null) { + builder.header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\""+ SERVICE_NAME + "\""); + } + return builder.build(); + + } catch (Exception e) { + adapter.getResponseDescriptor() + .setResponseCode(GENERAL_FAILURE.getResponseCode()) + .setResponseStatus(ResponseStatus.ERROR); + + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } finally { + adapter.exiting(); + } + + } + +} diff --git a/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/util/RestUtil.java b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/util/RestUtil.java new file mode 100644 index 0000000..8d74670 --- /dev/null +++ b/pomba/service-decomposition/src/main/java/org/onap/sdnc/apps/pomba/servicedecomposition/util/RestUtil.java @@ -0,0 +1,369 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * 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.sdnc.apps.pomba.servicedecomposition.util; + +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.FETCH_RESOURCE_FAILED; +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.INVALID_URL; +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.RELATIONSHIP_LINK_PARSE_ERROR; +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.SERVICE_INSTANCE_NOT_FOUND; +import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.SERVICE_RELATIONSHIP_PARSE_ERROR; + +import com.sun.jersey.core.util.MultivaluedMapImpl; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.JSONObject; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.restclient.client.RestClient; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class RestUtil { + // Parameters for Query AAI Model Data API + // HTTP headers + private static final String TRANSACTION_ID = "X-TransactionId"; + private static final String FROM_APP_ID = "X-FromAppId"; + private static final String APP_NAME = "aaiCtxBuilder"; + + // Service Catalog + public enum Catalog { + + VNF("generic-vnf"), VNFC("vnfc"), VSERVER("vserver"), L3NETWORK("l3-network"); + + private final String resourceName; + private final String collectionName; + + private Catalog(String objName) { + this.resourceName = objName; + this.collectionName = objName + "s"; // make plural + } + + public String objName() { + return resourceName; + } + }; + + + private static final String JSON_ATT_RELATED_TO = "related-to"; + private static final String JSON_ATT_RELATED_LINK = "related-link"; + + private static final String EMPTY_JSON_STRING = "{}"; + + private static final String DEPTH = "?depth=2"; + private static Logger logger = LoggerFactory.getLogger(RestUtil.class); + + /** + * Validates the URL parameter. + * + * @throws DiscoveryException if there is missing parameter + */ + public static void validateURL(String serviceInstanceId) throws DiscoveryException { + + if (serviceInstanceId == null || serviceInstanceId.isEmpty()) { + throw new DiscoveryException(INVALID_URL, Status.BAD_REQUEST); + } + } + + private static String generateServiceInstanceURL(String siPath, String serviceInstanceId) { + return MessageFormat.format(siPath, serviceInstanceId); + } + + + /* + * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC) + */ + + /** + * @param aaiClient + * @param baseURL + * @param aaiServiceInstancePath + * @param transactionId + * @param serviceInstanceId + * @param modelVersionId + * @param modelInvariantId + * @return + * @throws DiscoveryException + */ + public static JSONObject retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiServiceInstancePath, + String transactionId, String serviceInstanceId, ONAPLogAdapter adapter) throws DiscoveryException { + + // Follow two variables for transform purpose + String url = baseURL + generateServiceInstanceURL(aaiServiceInstancePath, serviceInstanceId); + // Response from service instance API call + JSONObject serviceInstancePayload = new JSONObject( + getResource(aaiClient, url, transactionId, MediaType.valueOf(MediaType.APPLICATION_XML))); + // Handle the case if the service instance is not found in AAI + if (serviceInstancePayload == null || serviceInstancePayload.length() == 0) { + logger.info("Service Instance " + serviceInstanceId + " is not found from AAI"); + // Only return the empty Json on the root level. i.e service instance + throw new DiscoveryException(SERVICE_INSTANCE_NOT_FOUND, Status.NOT_FOUND); + } + + HashMap<String, List<String>> relationMap = extractServiceRelationShips(serviceInstancePayload); + logger.info("The number of the relationships for service instance id {} is: {}", serviceInstanceId, + relationMap.size()); + + JSONObject response = processVNFRelationMap(aaiClient, baseURL, transactionId, relationMap, serviceInstancePayload); + return response; + } + + + /** + * @param aaiClient + * @param baseURL + * @param transactionId + * @param relationMap + * @throws DiscoveryException + */ + private static JSONObject processVNFRelationMap(RestClient aaiClient, String baseURL, String transactionId, + HashMap<String, List<String>> relationMap, JSONObject serviceInstancePayload) throws DiscoveryException { + List<JSONObject> vnfLst = new ArrayList<JSONObject>(); // List of the VNF JSON along with related resources + + JSONObject response = serviceInstancePayload; + + + if (relationMap.get(Catalog.VNF.resourceName) != null) { + List<JSONObject> vnfList = processResourceList(aaiClient, baseURL, transactionId, Catalog.VNF.resourceName, + relationMap.get(Catalog.VNF.resourceName)); + // Logic to Create the Generic VNF JSON and extract further relationships + for (JSONObject vnfPayload : vnfList) { + List<String> vnfcLinkLst = extractRelatedLink(vnfPayload, Catalog.VNFC.resourceName); + if (vnfcLinkLst != null && vnfcLinkLst.size() != 0) { + logger.info("The number of the API call for vnfc is:" + vnfcLinkLst.size()); + List<JSONObject> vnfcList = processResourceList(aaiClient, baseURL, transactionId, + Catalog.VNFC.resourceName, vnfcLinkLst); + if (vnfcList != null) { + vnfPayload.put(Catalog.VNFC.collectionName, vnfcList); + } + } else { + logger.info("No vnfc found for vnf-id:" + vnfPayload.getString("vnf-id")); + } + + List<String> networkLinkLst = extractRelatedLink(vnfPayload, Catalog.L3NETWORK.resourceName); + if (networkLinkLst != null && networkLinkLst.size() != 0) { + logger.info("The number of the API call for l3-network is:" + networkLinkLst.size()); + List<JSONObject> networkList = processResourceList(aaiClient, baseURL, transactionId, + Catalog.L3NETWORK.resourceName, networkLinkLst); + if (networkList != null) { + vnfPayload.put(Catalog.L3NETWORK.collectionName, networkList); + } + } else { + logger.info("No l3-network found for vnf-id:" + vnfPayload.getString("vnf-id")); + } + List<String> vserverLinkLst = extractRelatedLink(vnfPayload, Catalog.VSERVER.resourceName); + if (vserverLinkLst != null && vserverLinkLst.size() != 0) { + logger.info("The number of the API call for vserver is:" + vserverLinkLst.size()); + List<JSONObject> vserverList = processResourceList(aaiClient, baseURL, transactionId, + Catalog.VSERVER.resourceName, vserverLinkLst); + if (vserverList != null) { + vnfPayload.put(Catalog.VSERVER.collectionName, vserverList); + } + } else { + logger.info("No vserver found for vnf-id:" + vnfPayload.getString("vnf-id")); + } + + // Add final vnf payload to list + vnfLst.add(vnfPayload); + } + } else { + logger.info("No generic vnf found for :" + serviceInstancePayload.getString("service-instance-id")); + } + + // Add generic vnf with related resource payload to response + if (vnfLst != null && vnfLst.size() != 0) { + response.put(Catalog.VNF.collectionName, vnfLst); + } + + return response; + + } + + + /** + * @param aaiClient + * @param aaiBaseURL + * @param transactionId + * @param resourceType + * @param resourceList + * @return + * @throws DiscoveryException + */ + private static List<JSONObject> processResourceList(RestClient aaiClient, String aaiBaseURL, String transactionId, + String resourceType, List<String> resourceList) throws DiscoveryException { + List<JSONObject> resourcePayloadList = new ArrayList<JSONObject>(); + for (String resourceLink : resourceList) { + String resourceURL = aaiBaseURL + resourceLink; + // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use + // one API call but with depth=2 + if (resourceType.equals(Catalog.VNF.resourceName)) { + resourceURL += DEPTH; + } + + // Response from generic VNF API call + JSONObject resourcePayload = new JSONObject( + getResource(aaiClient, resourceURL, transactionId, MediaType.valueOf(MediaType.APPLICATION_XML))); + if (resourcePayload == null || resourcePayload.length() == 0) { + logger.info("Resource with url " + resourceLink + " is not found from AAI"); + } else { + resourcePayloadList.add(resourcePayload); + } + } + return resourcePayloadList; + } + + + /** + * @param serviceInstancePayload + * @param relationMap + * @return + * @throws DiscoveryException + */ + private static HashMap<String, List<String>> extractServiceRelationShips(JSONObject payload) + throws DiscoveryException { + + JSONArray relationships = null; + HashMap<String, List<String>> relationMap = new HashMap<String, List<String>>(); + logger.info("Fetching Service Instance Relationships"); + try { + JSONObject relationshipList = payload.getJSONObject("relationship-list"); + if (relationshipList != null) { + relationships = relationshipList.getJSONArray("relationship"); + } + } catch (Exception e) { + logger.error(e.getMessage()); + throw new DiscoveryException(SERVICE_RELATIONSHIP_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR, + e.getMessage()); + } + + if (relationships != null && relationships.length() > 0) { + for (int i = 0; i < relationships.length(); i++) { + JSONObject obj = relationships.optJSONObject(i); + String relatedToObj = obj.getString(JSON_ATT_RELATED_TO); + String relatedLinkObj = obj.getString(JSON_ATT_RELATED_LINK); + + if (relatedToObj == null) { + logger.info("Related-To Object found null"); + continue; + } + List<String> relatedLinkList = relationMap.get(relatedToObj.toString()); + if (relatedLinkList == null) { + relatedLinkList = new ArrayList<>(); + relationMap.put(relatedToObj.toString(), relatedLinkList); + } + relatedLinkList.add(relatedLinkObj.toString()); + } + } + return relationMap; + } + + /** + * @param client + * @param url + * @param transId + * @param mediaType + * @return + * @throws DiscoveryException + */ + private static String getResource(RestClient client, String url, String transId, MediaType mediaType) + throws DiscoveryException { + OperationResult result = client.get(url, buildHeaders(transId), MediaType.valueOf(MediaType.APPLICATION_JSON)); + + if (result.getResultCode() == 200) { + String jsonString = result.getResult(); + return jsonString; + } else if (result.getResultCode() == 404) { + // Resource not found, generate empty JSON format + logger.info("Resource for " + url + " is not found " + "return empty Json format"); + return EMPTY_JSON_STRING; + } else { + throw new DiscoveryException(FETCH_RESOURCE_FAILED, Status.INTERNAL_SERVER_ERROR, result.getFailureCause()); + } + } + + /** + * Extract the related-Link from Json payload. For example + * { + * "related-to": "vnfc", + * "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003", + * "relationship-data": [ + * { + * "relationship-key": "vnfc.vnfc-name", + * "relationship-value": "zrdm5aepdg01vmg003" + * } + * ] + * } + */ + private static List<String> extractRelatedLink(JSONObject payload, String catalog) throws DiscoveryException { + List<String> relatedLinkList = new ArrayList<String>(); + JSONArray relationships = null; + logger.info("Fetching relationships for resource type: " + catalog); + try { + JSONObject relationshipLst = payload.getJSONObject("relationship-list"); + if (relationshipLst != null) { + relationships = relationshipLst.getJSONArray("relationship"); + } + + } catch (Exception e) { + logger.error(e.getMessage()); + throw new DiscoveryException(RELATIONSHIP_LINK_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR, e.getMessage()); + } + + if (relationships != null && relationships.length() > 0) { + for (int i = 0; i < relationships.length(); i++) { + Object relatedToObj = null; + Object relatedLinkObj = null; + + JSONObject obj = relationships.optJSONObject(i); + relatedToObj = obj.get(JSON_ATT_RELATED_TO); + + if (relatedToObj.toString().equals(catalog)) { + relatedLinkObj = obj.get(JSON_ATT_RELATED_LINK); + if (relatedLinkObj != null) { + relatedLinkList.add(relatedLinkObj.toString()); + } + } + + } + } + if (relatedLinkList != null) { + logger.info( + "Number of relationships found for resource type: " + catalog + " are: " + relatedLinkList.size()); + } + return relatedLinkList; + } + + + + private static Map<String, List<String>> buildHeaders(String transactionId) { + MultivaluedMap<String, String> headers = new MultivaluedMapImpl(); + headers.put(TRANSACTION_ID, Collections.singletonList(transactionId)); + headers.put(FROM_APP_ID, Collections.singletonList(APP_NAME)); + return headers; + } + +} |