From 06fb1d8bc8727d476c0998ff19f90a07f14edade Mon Sep 17 00:00:00 2001 From: hyu2010 Date: Tue, 20 Jul 2021 15:34:03 -0400 Subject: SO changes for Service Intent This update contains the SO changes for the CCVPN extension to support Intent Based Networking (REQ-719). The SO is responsible for providing the Service Intent orchestration, as well as providing the Service Intent APIs which are consumed by the IBN use-case. The changes on the SO are technology-agnostic and are relatively simple. i.e., Technology specific network configurations are carried out by the SDNC. Issue-ID: SO-3714 Signed-off-by: hyu2010 Change-Id: Iec9d2fc80adc79faaeeaf811bd0d02c9d7611d61 Signed-off-by: hyu2010 --- .../so/apihandlerinfra/JerseyConfiguration.java | 1 + .../apihandlerinfra/ServiceIntentApiHandler.java | 431 +++++++++++++++++++++ .../ServiceIntentCommonRequest.java | 83 ++++ .../ServiceIntentCreationRequest.java | 82 ++++ .../ServiceIntentDeletionRequest.java | 48 +++ .../ServiceIntentModificationRequest.java | 60 +++ .../ServiceIntentApiHandlerTest.java | 156 ++++++++ .../ServiceIntentTest/create-cll-payload.json | 29 ++ .../ServiceIntentTest/delete-cll-payload.json | 9 + .../ServiceIntentTest/modify-cll-payload.json | 29 ++ 10 files changed, 928 insertions(+) create mode 100644 mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandler.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCommonRequest.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCreationRequest.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentDeletionRequest.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentModificationRequest.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/test/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandlerTest.java create mode 100644 mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/create-cll-payload.json create mode 100644 mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/delete-cll-payload.json create mode 100644 mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/modify-cll-payload.json (limited to 'mso-api-handlers/mso-api-handler-infra') diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/JerseyConfiguration.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/JerseyConfiguration.java index d69c395f96..031e9eee2a 100644 --- a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/JerseyConfiguration.java +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/JerseyConfiguration.java @@ -65,6 +65,7 @@ public class JerseyConfiguration extends ResourceConfig { register(NodeHealthcheckHandler.class); register(ServiceInstances.class); register(Onap3gppServiceInstances.class); + register(ServiceIntentApiHandler.class); register(TasksHandler.class); register(CloudOrchestration.class); register(CloudResourcesOrchestration.class); diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandler.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandler.java new file mode 100644 index 0000000000..be63c5fb9a --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandler.java @@ -0,0 +1,431 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2021 Huawei Technologies. + * ================================================================================ + * 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.so.apihandlerinfra; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.apache.http.HttpStatus; +import org.onap.logging.filter.base.ErrorCode; +import org.onap.so.apihandler.camundabeans.CamundaResponse; +import org.onap.so.apihandler.common.CamundaClient; +import org.onap.so.apihandler.common.ErrorNumbers; +import org.onap.so.apihandler.common.RequestClientParameter; +import org.onap.so.apihandler.common.ResponseBuilder; +import org.onap.so.apihandler.common.ResponseHandler; +import org.onap.so.apihandlerinfra.exceptions.ApiException; +import org.onap.so.apihandlerinfra.exceptions.BPMNFailureException; +import org.onap.so.apihandlerinfra.exceptions.RequestDbFailureException; +import org.onap.so.apihandlerinfra.exceptions.ValidateException; +import org.onap.so.apihandlerinfra.logging.ErrorLoggerInfo; +import org.onap.so.apihandlerinfra.serviceintentinstancebeans.ServiceIntentCommonRequest; +import org.onap.so.apihandlerinfra.serviceintentinstancebeans.ServiceIntentCreationRequest; +import org.onap.so.apihandlerinfra.serviceintentinstancebeans.ServiceIntentDeletionRequest; +import org.onap.so.apihandlerinfra.serviceintentinstancebeans.ServiceIntentModificationRequest; +import org.onap.so.constants.Status; +import org.onap.so.db.catalog.beans.Service; +import org.onap.so.db.catalog.beans.ServiceRecipe; +import org.onap.so.db.catalog.client.CatalogDbClient; +import org.onap.so.db.request.beans.InfraActiveRequests; +import org.onap.so.db.request.client.RequestsDbClient; +import org.onap.so.logger.LogConstants; +import org.onap.so.logger.LoggingAnchor; +import org.onap.so.logger.MessageEnum; +import org.onap.so.serviceinstancebeans.ModelType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.function.Function; + +/** + * This class serves as the entry point for Service Intent APIs. Unlike User Intent, which describes Intent using + * natural languages, Service Intent describes Intent through technology-agnostic and model-driven APIs. The Service + * Intent APIs have the following format: {service-Intent-Root}/{operation}, where {operation} may be "create", + * "delete", "modify", etc. And the parameters of the Intent service instance are specified in the payloads of the APIs. + *

+ * For scalability, these APIs are designed to be generic, and thus support all the service Intent use-cases. i.e., The + * actual intent use-case/application, e.g., Cloud Leased Line, is differentiated by the "serviceType" parameter in the + * payload, rather than by specific APIs. Thus, this class does not need to grow when we add new Intent use-cases or + * applications. + *

+ */ +@Component +@Path("/onap/so/infra/serviceIntent") +@OpenAPIDefinition(info = @Info(title = "/onap/so/infra/serviceIntent", + description = "API Requests for Intent services and " + "applications")) +public class ServiceIntentApiHandler { + + private static final Logger logger = LoggerFactory.getLogger(ServiceIntentApiHandler.class); + + private static final String MSO_PROP_APIHANDLER_INFRA = "MSO_PROP_APIHANDLER_INFRA"; + + private static final String END_OF_THE_TRANSACTION = "End of the transaction, the final response is: "; + + private static final String SAVE_TO_DB = "save instance to db"; + + private static final String URI_PREFIX = "/serviceIntent/"; + + @Autowired + private MsoRequest msoRequest; + + @Autowired + private CatalogDbClient catalogDbClient; + + @Autowired + private RequestsDbClient requestsDbClient; + + @Autowired + private RequestHandlerUtils requestHandlerUtils; + + @Autowired + private ResponseBuilder builder; + + @Autowired + private CamundaClient camundaClient; + + @Autowired + private ResponseHandler responseHandler; + + // @Value("${serviceIntent.config.file}") + // private String serviceIntentConfigFile; + + /** + * POST Requests for create Service Intent Instance on a version provided + * + * @throws ApiException + */ + + @POST + @Path("/{version:[vV][1]}/create") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Create a SI Instance on a version provided", responses = @ApiResponse( + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class))))) + public Response createServiceIntentInstance(ServiceIntentCreationRequest request, + @PathParam("version") String version, @Context ContainerRequestContext requestContext) throws ApiException { + String requestId = requestHandlerUtils.getRequestId(requestContext); + return processServiceIntentRequest(request, Action.createInstance, version, requestId, null, + requestHandlerUtils.getRequestUri(requestContext, URI_PREFIX)); + } + + /** + * PUT Requests for Service Intent Modification on a version provided + * + * @throws ApiException + */ + + @PUT + @Path("/{version:[vV][1]}/modify") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Modify a SI Instance on a version provided", responses = @ApiResponse( + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class))))) + public Response modifyServiceIntentInstance(ServiceIntentModificationRequest request, + @PathParam("version") String version, @Context ContainerRequestContext requestContext) throws ApiException { + String requestId = requestHandlerUtils.getRequestId(requestContext); + HashMap instanceIdMap = new HashMap<>(); + instanceIdMap.put("serviceInstanceId", request.getServiceInstanceID()); + return processServiceIntentRequest(request, Action.updateInstance, version, requestId, instanceIdMap, + requestHandlerUtils.getRequestUri(requestContext, URI_PREFIX)); + } + + /** + * DELETE Requests for Service Intent Instance on a specified version + * + * @throws ApiException + */ + + @DELETE + @Path("/{version:[vV][1]}/delete") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Terminate/Delete a SI Service Instance on a version provided", responses = @ApiResponse( + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class))))) + public Response deleteServiceIntentInstance(ServiceIntentDeletionRequest request, + @PathParam("version") String version, @Context ContainerRequestContext requestContext) throws ApiException { + String requestId = requestHandlerUtils.getRequestId(requestContext); + HashMap instanceIdMap = new HashMap<>(); + instanceIdMap.put("serviceInstanceId", request.getServiceInstanceID()); + return processServiceIntentRequest(request, Action.deleteInstance, version, requestId, instanceIdMap, + requestHandlerUtils.getRequestUri(requestContext, URI_PREFIX)); + } + + /** + * Process Service Intent request and send request to corresponding workflow + * + * @param request + * @param action + * @param version + * @return + * @throws ApiException + */ + private Response processServiceIntentRequest(ServiceIntentCommonRequest request, Action action, String version, + String requestId, HashMap instanceIdMap, String requestUri) throws ApiException { + String defaultServiceModelName = "COMMON_SI_DEFAULT"; + String requestScope = ModelType.service.name(); + String apiVersion = version.substring(1); + String serviceRequestJson = toString.apply(request); + + String instanceName = null; + String modelUuid = null; + String serviceInstanceId = null; + + try { + if (action == Action.createInstance) { + instanceName = ((ServiceIntentCreationRequest) request).getName(); + modelUuid = ((ServiceIntentCreationRequest) request).getModelUuid(); + } else if (action == Action.updateInstance) { + instanceName = ((ServiceIntentModificationRequest) request).getName(); + serviceInstanceId = ((ServiceIntentModificationRequest) request).getServiceInstanceID(); + } else if (action == Action.deleteInstance) { + serviceInstanceId = ((ServiceIntentDeletionRequest) request).getServiceInstanceID(); + } + } catch (Exception e) { + logger.error("ERROR: processCllServiceRequest: Exception: ", e); + Response response = msoRequest.buildServiceErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, + MsoException.ServiceException, "processCllServiceRequest error", ErrorNumbers.SVC_BAD_PARAMETER, + null, version); + return response; + } + + if (serviceRequestJson != null) { + InfraActiveRequests currentActiveReq = createRequestObject(request, action, requestId, Status.IN_PROGRESS, + requestScope, serviceRequestJson); + + requestHandlerUtils.checkForDuplicateRequests(action, instanceIdMap, requestScope, currentActiveReq, + instanceName); + try { + requestsDbClient.save(currentActiveReq); + } catch (Exception e) { + logger.error("Exception occurred", e); + ErrorLoggerInfo errorLoggerInfo = + new ErrorLoggerInfo.Builder(MessageEnum.APIH_DB_ACCESS_EXC, ErrorCode.DataError) + .errorSource(Constants.MSO_PROP_APIHANDLER_INFRA).build(); + throw new RequestDbFailureException.Builder(SAVE_TO_DB, e.toString(), + HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorNumbers.SVC_DETAILED_SERVICE_ERROR).cause(e) + .errorInfo(errorLoggerInfo).build(); + } + + RecipeLookupResult recipeLookupResult; + try { + recipeLookupResult = getServiceInstanceOrchestrationURI(modelUuid, action, defaultServiceModelName); + } catch (Exception e) { + logger.error(LoggingAnchor.FOUR, MessageEnum.APIH_DB_ACCESS_EXC.toString(), MSO_PROP_APIHANDLER_INFRA, + ErrorCode.AvailabilityError.getValue(), "Exception while communicate with Catalog DB", e); + Response response = msoRequest.buildServiceErrorResponse(HttpStatus.SC_NOT_FOUND, + MsoException.ServiceException, "No " + "communication to catalog DB " + e.getMessage(), + ErrorNumbers.SVC_NO_SERVER_RESOURCES, null, version); + logger.debug(END_OF_THE_TRANSACTION + response.getEntity()); + return response; + } + + if (recipeLookupResult == null) { + logger.error(LoggingAnchor.FOUR, MessageEnum.APIH_DB_ATTRIBUTE_NOT_FOUND.toString(), + MSO_PROP_APIHANDLER_INFRA, ErrorCode.DataError.getValue(), "No recipe found in DB"); + Response response = msoRequest.buildServiceErrorResponse(HttpStatus.SC_NOT_FOUND, + MsoException.ServiceException, "Recipe does " + "not exist in catalog DB", + ErrorNumbers.SVC_GENERAL_SERVICE_ERROR, null, version); + logger.debug(END_OF_THE_TRANSACTION + response.getEntity()); + return response; + } + + String serviceInstanceType = request.getSubscriptionServiceType(); + RequestClientParameter parameter; + try { + parameter = new RequestClientParameter.Builder().setRequestId(requestId).setBaseVfModule(false) + .setRecipeTimeout(recipeLookupResult.getRecipeTimeout()).setRequestAction(action.name()) + .setServiceInstanceId(serviceInstanceId).setServiceType(serviceInstanceType) + .setRequestDetails(serviceRequestJson).setApiVersion(version).setALaCarte(false) + .setRecipeParamXsd(recipeLookupResult.getRecipeParamXsd()).setApiVersion(apiVersion).build(); + } catch (Exception e) { + logger.error("Exception occurred", e); + ErrorLoggerInfo errorLoggerInfo = + new ErrorLoggerInfo.Builder(MessageEnum.APIH_BPEL_RESPONSE_ERROR, ErrorCode.SchemaError) + .errorSource(Constants.MSO_PROP_APIHANDLER_INFRA).build(); + throw new ValidateException.Builder("Unable to generate RequestClientParameter object" + e.getMessage(), + HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorNumbers.SVC_BAD_PARAMETER).errorInfo(errorLoggerInfo) + .build(); + } + return postBPELRequest(currentActiveReq, parameter, recipeLookupResult.getOrchestrationURI(), requestScope); + } else { + Response response = msoRequest.buildServiceErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, + MsoException.ServiceException, "JsonProcessingException occurred - " + "serviceRequestJson is null", + ErrorNumbers.SVC_BAD_PARAMETER, null, version); + return response; + } + } + + /** + * Getting recipes from catalogDb + * + * @param serviceModelUUID the service model version uuid + * @param action the action for the service + * @param defaultServiceModelName default service name + * @return the service recipe result + */ + private RecipeLookupResult getServiceInstanceOrchestrationURI(String serviceModelUUID, Action action, + String defaultServiceModelName) { + + RecipeLookupResult recipeLookupResult = getServiceURI(serviceModelUUID, action, defaultServiceModelName); + + if (recipeLookupResult != null) { + logger.debug("Orchestration URI is: " + recipeLookupResult.getOrchestrationURI() + ", recipe Timeout is: " + + Integer.toString(recipeLookupResult.getRecipeTimeout())); + } else { + logger.debug("No matching recipe record found"); + } + return recipeLookupResult; + } + + /** + * Getting recipes from catalogDb If Service recipe is not set, use default recipe, if set , use special recipe. + * + * @param serviceModelUUID the service version uuid + * @param action the action of the service. + * @param defaultServiceModelName default service name + * @return the service recipe result. + */ + private RecipeLookupResult getServiceURI(String serviceModelUUID, Action action, String defaultServiceModelName) { + + Service defaultServiceRecord = + catalogDbClient.getFirstByModelNameOrderByModelVersionDesc(defaultServiceModelName); + // set recipe as default generic recipe + ServiceRecipe recipe = + catalogDbClient.getFirstByServiceModelUUIDAndAction(defaultServiceRecord.getModelUUID(), action.name()); + // check the service special recipe + if (null != serviceModelUUID && !serviceModelUUID.isEmpty()) { + ServiceRecipe serviceSpecialRecipe = + catalogDbClient.getFirstByServiceModelUUIDAndAction(serviceModelUUID, action.name()); + if (null != serviceSpecialRecipe) { + // set service special recipe. + recipe = serviceSpecialRecipe; + } + } + + if (recipe == null) { + return null; + } + return new RecipeLookupResult(recipe.getOrchestrationUri(), recipe.getRecipeTimeout(), recipe.getParamXsd()); + + } + + Function toString = serviceRequest -> { + ObjectMapper mapper = new ObjectMapper(); + String requestAsString = null; + try { + requestAsString = mapper.writeValueAsString(serviceRequest); + } catch (JsonProcessingException e) { + logger.debug("Exception while converting service request object to String {}", e); + } + return requestAsString; + }; + + private InfraActiveRequests createRequestObject(ServiceIntentCommonRequest request, Action action, String requestId, + Status status, String requestScope, String requestJson) { + InfraActiveRequests aq = new InfraActiveRequests(); + try { + String serviceInstanceName = null; + String serviceInstanceId = null; + String serviceType = request.getServiceType(); + if (action.name().equals("createInstance")) { + serviceInstanceName = ((ServiceIntentCreationRequest) request).getName(); + aq.setServiceInstanceName(serviceInstanceName); + } else if (action.name().equals("updateInstance")) { + serviceInstanceName = ((ServiceIntentModificationRequest) request).getName(); + serviceInstanceId = ((ServiceIntentModificationRequest) request).getServiceInstanceID(); + aq.setServiceInstanceName(serviceInstanceName); + aq.setServiceInstanceId(serviceInstanceId); + } else if (action.name().equals("deleteInstance")) { + serviceInstanceId = ((ServiceIntentDeletionRequest) request).getServiceInstanceID(); + aq.setServiceInstanceId(serviceInstanceId); + } + + aq.setRequestId(requestId); + aq.setRequestAction(action.toString()); + aq.setRequestUrl(MDC.get(LogConstants.HTTP_URL)); + Timestamp startTimeStamp = new Timestamp(System.currentTimeMillis()); + aq.setStartTime(startTimeStamp); + aq.setRequestScope(requestScope); + aq.setRequestBody(requestJson); + aq.setRequestStatus(status.toString()); + aq.setLastModifiedBy(Constants.MODIFIED_BY_APIHANDLER); + aq.setServiceType(serviceType); + } catch (Exception e) { + logger.error("Exception when creation record request", e); + + if (!status.equals(Status.FAILED)) { + throw e; + } + } + return aq; + } + + private Response postBPELRequest(InfraActiveRequests currentActiveReq, RequestClientParameter parameter, + String orchestrationURI, String requestScope) throws ApiException { + ResponseEntity response = + requestHandlerUtils.postRequest(currentActiveReq, parameter, orchestrationURI); + logger.debug("BPEL response : " + response); + int bpelStatus = responseHandler.setStatus(response.getStatusCodeValue()); + String jsonResponse; + try { + responseHandler.acceptedResponse(response); + CamundaResponse camundaResponse = responseHandler.getCamundaResponse(response); + String responseBody = camundaResponse.getResponse(); + if ("Success".equalsIgnoreCase(camundaResponse.getMessage())) { + jsonResponse = responseBody; + } else { + BPMNFailureException bpmnException = + new BPMNFailureException.Builder(String.valueOf(bpelStatus) + responseBody, bpelStatus, + ErrorNumbers.SVC_DETAILED_SERVICE_ERROR).build(); + requestHandlerUtils.updateStatus(currentActiveReq, Status.FAILED, bpmnException.getMessage()); + throw bpmnException; + } + } catch (ApiException e) { + requestHandlerUtils.updateStatus(currentActiveReq, Status.FAILED, e.getMessage()); + throw e; + } + return builder.buildResponse(HttpStatus.SC_ACCEPTED, parameter.getRequestId(), jsonResponse, + parameter.getApiVersion()); + } +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCommonRequest.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCommonRequest.java new file mode 100644 index 0000000000..f27f88c428 --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCommonRequest.java @@ -0,0 +1,83 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2020 Huawei Technologies. + * ================================================================================ + * 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.so.apihandlerinfra.serviceintentinstancebeans; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.HashMap; +import java.util.Map; + +/** + * Model class for common parameter of Service Intent request + */ +public class ServiceIntentCommonRequest { + + @JsonProperty("globalSubscriberId") + private String globalSubscriberId; + + @JsonProperty("subscriptionServiceType") + private String subscriptionServiceType; + + @JsonProperty("additionalProperties") + private Map additionalProperties = new HashMap<>(); + + @JsonProperty("serviceType") + private String serviceType; + + public String getGlobalSubscriberId() { + return globalSubscriberId; + } + + public void setGlobalSubscriberId(String globalSubscriberId) { + this.globalSubscriberId = globalSubscriberId; + } + + public String getSubscriptionServiceType() { + return subscriptionServiceType; + } + + public void setSubscriptionServiceType(String subscriptionServiceType) { + this.subscriptionServiceType = subscriptionServiceType; + } + + public Map getAdditionalProperties() { + return additionalProperties; + } + + public void setAdditionalProperties(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + public String getServiceType() { + return serviceType; + } + + public void setServiceType(String serviceType) { + this.serviceType = serviceType; + } + + @Override + public String toString() { + return "ServiceIntentCommonRequest [globalSubscriberId=" + globalSubscriberId + ", subscriptionServiceType=" + + subscriptionServiceType + ", serviceType=" + serviceType + ", additionalProperties=" + + additionalProperties + "]"; + } + +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCreationRequest.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCreationRequest.java new file mode 100644 index 0000000000..bc711a00dc --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentCreationRequest.java @@ -0,0 +1,82 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2020 Huawei Technologies. + * ================================================================================ + * 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.so.apihandlerinfra.serviceintentinstancebeans; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Model class for Service Intent Creation request + */ +public class ServiceIntentCreationRequest extends ServiceIntentCommonRequest { + + @JsonProperty("name") + private String name; + + @JsonProperty("modelInvariantUuid") + private String modelInvariantUuid; + + @JsonProperty("modelUuid") + private String modelUuid; + + @JsonProperty("sst") + private String sST; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getModelInvariantUuid() { + return modelInvariantUuid; + } + + public void setModelInvariantUuid(String modelInvariantUuid) { + this.modelInvariantUuid = modelInvariantUuid; + } + + public String getModelUuid() { + return modelUuid; + } + + public void setModelUuid(String modelUuid) { + this.modelUuid = modelUuid; + } + + public String getsST() { + return sST; + } + + public void setsST(String sST) { + this.sST = sST; + } + + @Override + public String toString() { + return "ServiceIntentCreationRequest [name=" + name + ", modelInvariantUuid=" + modelInvariantUuid + + ", modelUuid=" + modelUuid + ", globalSubscriberId=" + getGlobalSubscriberId() + + ", subscriptionServiceType=" + getSubscriptionServiceType() + ", serviceType=" + getServiceType() + + ", additionalProperties=" + getAdditionalProperties() + "]"; + } + +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentDeletionRequest.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentDeletionRequest.java new file mode 100644 index 0000000000..c463c7bae4 --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentDeletionRequest.java @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2020 Huawei Technologies. + * ================================================================================ + * 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.so.apihandlerinfra.serviceintentinstancebeans; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Model class for service intent instance terminate request + */ +public class ServiceIntentDeletionRequest extends ServiceIntentCommonRequest { + + @JsonProperty("serviceInstanceID") + private String serviceInstanceID; + + public String getServiceInstanceID() { + return serviceInstanceID; + } + + public void setServiceInstanceID(String serviceInstanceID) { + this.serviceInstanceID = serviceInstanceID; + } + + @Override + public String toString() { + return "ServiceIntentDeletionRequest [serviceInstanceID=" + getServiceInstanceID() + ", globalSubscriberId=" + + getGlobalSubscriberId() + ", subscriptionServiceType=" + getSubscriptionServiceType() + + ", serviceType=" + getServiceType() + ", additionalProperties=" + getAdditionalProperties() + "]"; + } + +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentModificationRequest.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentModificationRequest.java new file mode 100644 index 0000000000..17d6dc84ab --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/serviceintentinstancebeans/ServiceIntentModificationRequest.java @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2021 Huawei Technologies. + * ================================================================================ + * 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.so.apihandlerinfra.serviceintentinstancebeans; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Model class for Service Intent Update request + */ +public class ServiceIntentModificationRequest extends ServiceIntentCommonRequest { + + @JsonProperty("name") + private String name; + + @JsonProperty("serviceInstanceID") + private String serviceInstanceID; + + public String getServiceInstanceID() { + return serviceInstanceID; + } + + public void setServiceInstanceID(String serviceInstanceID) { + this.serviceInstanceID = serviceInstanceID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "ServiceIntentModificationRequest [name=" + name + ", serviceInstanceID=" + getServiceInstanceID() + + ", globalSubscriberId=" + getGlobalSubscriberId() + ", subscriptionServiceType=" + + getSubscriptionServiceType() + ", serviceType=" + getServiceType() + ", additionalProperties=" + + getAdditionalProperties() + "]"; + } + +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/test/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandlerTest.java b/mso-api-handlers/mso-api-handler-infra/src/test/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandlerTest.java new file mode 100644 index 0000000000..059c2bc057 --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/test/java/org/onap/so/apihandlerinfra/ServiceIntentApiHandlerTest.java @@ -0,0 +1,156 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2020 Wipro Limited. + * ================================================================================ + * 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.so.apihandlerinfra; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.onap.so.db.catalog.beans.Service; +import org.onap.so.db.catalog.beans.ServiceRecipe; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.UriComponentsBuilder; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.junit.Assert.assertEquals; +import static org.onap.logging.filter.base.Constants.HttpHeaders.ONAP_PARTNER_NAME; +import static org.onap.logging.filter.base.Constants.HttpHeaders.ONAP_REQUEST_ID; +import static org.onap.logging.filter.base.Constants.HttpHeaders.TRANSACTION_ID; +import static org.onap.so.logger.HttpHeadersConstants.REQUESTOR_ID; + +public class ServiceIntentApiHandlerTest extends BaseTest { + + private static final String ROOT_URI = "/onap/so/infra/serviceIntent/"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Autowired + private Onap3gppServiceInstances objUnderTest; + + @Before + public void init() throws JsonProcessingException { + + Service defaultService = new Service(); + defaultService.setModelUUID("d88da85c-d9e8-4f73-b837-3a72a431622a"); + ServiceRecipe serviceRecipe = new ServiceRecipe(); + serviceRecipe.setServiceModelUUID(defaultService.getModelUUID()); + serviceRecipe.setRecipeTimeout(180); + serviceRecipe.setOrchestrationUri("/mso/async/services/commonServiceIntentTest"); + + wireMockServer.stubFor(get(urlPathEqualTo("/service/search/findFirstByModelNameOrderByModelVersionDesc")) + .willReturn(aResponse().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .withBody(MAPPER.writeValueAsString(defaultService)).withStatus(HttpStatus.SC_OK))); + + wireMockServer.stubFor(get(urlPathEqualTo("/serviceRecipe/search/findFirstByServiceModelUUIDAndAction")) + .willReturn(aResponse().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .withBody(MAPPER.writeValueAsString(serviceRecipe)).withStatus(HttpStatus.SC_OK))); + wireMockServer.stubFor(post(urlMatching(".*/infraActiveRequests/")).willReturn(aResponse() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).withStatus(HttpStatus.SC_OK))); + Mockito.doReturn(null).when(requestsDbClient).getInfraActiveRequestbyRequestId(Mockito.any()); + } + + public String inputStream(String JsonInput) throws IOException { + JsonInput = "src/test/resources/ServiceIntentTest" + JsonInput; + return new String(Files.readAllBytes(Paths.get(JsonInput))); + } + + public ResponseEntity sendRequest(String requestJson, String uriPath, HttpMethod reqMethod) { + HttpHeaders headers = new HttpHeaders(); + headers.set("Accept", MediaType.APPLICATION_JSON); + headers.set("Content-Type", MediaType.APPLICATION_JSON); + headers.set(ONAPLogConstants.Headers.PARTNER_NAME, "test_name"); + headers.set(TRANSACTION_ID, "32807a28-1a14-4b88-b7b3-2950918aa76d"); + headers.set(ONAP_REQUEST_ID, "32807a28-1a14-4b88-b7b3-2950918aa76d"); + headers.set(ONAPLogConstants.MDCs.REQUEST_ID, "32807a28-1a14-4b88-b7b3-2950918aa76d"); + headers.set(ONAP_PARTNER_NAME, "VID"); + headers.set(REQUESTOR_ID, "xxxxxx"); + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(createURLWithPort(uriPath)); + HttpEntity request = new HttpEntity<>(requestJson, headers); + + return restTemplate.exchange(builder.toUriString(), reqMethod, request, String.class); + } + + @Test + public void createServiceInstanceTest() throws IOException { + String uri = ROOT_URI + "v1/create"; + wireMockServer.stubFor(post(urlPathEqualTo("/mso/async/services/commonServiceIntentTest")) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBodyFile("Camunda/BPMN_response.json").withStatus(HttpStatus.SC_ACCEPTED))); + + String expectedResponse = + "{\"jobId\":\"db245365e79c47ed88fcd60caa8f6549\",\"status\":\"\",\"statusDescription\":{}}"; + ResponseEntity response = sendRequest(inputStream("/create-cll-payload.json"), uri, HttpMethod.POST); + + assertEquals(Response.Status.ACCEPTED.getStatusCode(), response.getStatusCode().value()); + String actualResponse = response.getBody(); + assertEquals(expectedResponse, actualResponse); + } + + @Test + public void updateServiceInstanceTest() throws IOException { + String uri = ROOT_URI + "v1/modify"; + wireMockServer.stubFor(post(urlPathEqualTo("/mso/async/services/commonServiceIntentTest")) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBodyFile("Camunda/BPMN_response.json").withStatus(HttpStatus.SC_ACCEPTED))); + + String expectedResponse = + "{\"jobId\":\"db245365e79c47ed88fcd60caa8f6549\",\"status\":\"\",\"statusDescription\":{}}"; + ResponseEntity response = sendRequest(inputStream("/modify-cll-payload.json"), uri, HttpMethod.PUT); + + assertEquals(Response.Status.ACCEPTED.getStatusCode(), response.getStatusCode().value()); + String actualResponse = response.getBody(); + assertEquals(expectedResponse, actualResponse); + } + + @Test + public void deleteServiceInstanceTest() throws IOException { + String uri = ROOT_URI + "v1/delete"; + wireMockServer.stubFor(post(urlPathEqualTo("/mso/async/services/commonServiceIntentTest")) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBodyFile("Camunda/BPMN_response.json").withStatus(HttpStatus.SC_ACCEPTED))); + String expectedResponse = + "{\"jobId\":\"db245365e79c47ed88fcd60caa8f6549\",\"status\":\"\",\"statusDescription\":{}}"; + ResponseEntity response = sendRequest(inputStream("/delete-cll-payload.json"), uri, HttpMethod.DELETE); + + assertEquals(Response.Status.ACCEPTED.getStatusCode(), response.getStatusCode().value()); + String actualResponse = response.getBody(); + assertEquals(expectedResponse, actualResponse); + } + + +} + + diff --git a/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/create-cll-payload.json b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/create-cll-payload.json new file mode 100644 index 0000000000..6018392abd --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/create-cll-payload.json @@ -0,0 +1,29 @@ +{ + "name": "cloud-leased-line-101", + "modelInvariantUuid": "6790ab0e-034f-11eb-adc1-0242ac120002", + "modelUuid": "6790ab0e-034f-11eb-adc1-0242ac120002", + "globalSubscriberId": "IBNCustomer", + "subscriptionServiceType": "IBN", + "serviceType": "CLL", + "additionalProperties": { + "enableSdnc": "false", + "serviceInstanceID": "cll-101", + "transportNetworks": [ + { + "id": "cll-101-network-001", + "sla": { + "latency": 2, + "maxBandwidth": 3000 + }, + "connectionLinks": [ + { + "name": "cll-link-1", + "transportEndpointA": "tranportEp_UNI_ID_311_1", + "transportEndpointB": "tranportEp_ROOT_ID_512_1" + } + ] + } + ] + } +} + diff --git a/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/delete-cll-payload.json b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/delete-cll-payload.json new file mode 100644 index 0000000000..f21f4e3cea --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/delete-cll-payload.json @@ -0,0 +1,9 @@ +{ + "serviceInstanceID": "cll-101", + "globalSubscriberId": "IBNCustomer", + "subscriptionServiceType": "IBN", + "serviceType": "CLL", + "additionalProperties": { + "enableSdnc": "false" + } +} diff --git a/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/modify-cll-payload.json b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/modify-cll-payload.json new file mode 100644 index 0000000000..3611adaf3b --- /dev/null +++ b/mso-api-handlers/mso-api-handler-infra/src/test/resources/ServiceIntentTest/modify-cll-payload.json @@ -0,0 +1,29 @@ +{ + "serviceInstanceID": "cll-101", + "name": "cloud-leased-line-101", + "modelInvariantUuid": "6790ab0e-034f-11eb-adc1-0242ac120002", + "modelUuid": "6790ab0e-034f-11eb-adc1-0242ac120002", + "globalSubscriberId": "IBNCustomer", + "subscriptionServiceType": "IBN", + "serviceType": "CLL", + "additionalProperties": { + "enableSdnc": "false", + "transportNetworks": [ + { + "id": "cll-101-network-001", + "sla": { + "latency": 2, + "maxBandwidth": 8000 + }, + "connectionLinks": [ + { + "name": "cll-link-1", + "transportEndpointA": "tranportEp_UNI_ID_311_1", + "transportEndpointB": "tranportEp_ROOT_ID_512_1" + } + ] + } + ] + } +} + -- cgit 1.2.3-korg