summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikhilMohan <nikmohan81@gmail.com>2020-08-26 20:30:48 +0530
committernikhil mohan <nikmohan81@gmail.com>2020-08-26 20:34:02 +0530
commit93fabeb929bb1b422d7473e23935f47fead4f4d8 (patch)
treef81cd4b669efda82154362c95beaba4b977fbd5f
parentb337097739ba195b5d7a8cbb71c2f0e7f0e74d30 (diff)
Implementation for TMF 633 API - POST /serviceSpecification
Issue-ID: EXTAPI-488 Signed-off-by: nikhil mohan <nikmohan81@gmail.com> Change-Id: I382e3b2bfbde656508bcfb0c86e748f21a7790d2
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java62
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java57
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java34
-rw-r--r--src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java8
-rw-r--r--src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java5
-rw-r--r--src/test/resources/karatetest/data/serviceSpecification.json195
-rw-r--r--src/test/resources/karatetest/features/00--ServiceCatalog.feature73
-rw-r--r--src/test/resources/mappings/sdc/sdc_post_spec.json106
8 files changed, 517 insertions, 23 deletions
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java b/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java
index e362903..29a4a25 100644
--- a/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java
@@ -22,9 +22,7 @@ import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import org.apache.commons.io.IOUtils;
@@ -42,6 +40,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@@ -66,6 +65,8 @@ public class SdcClient {
private static final String HEADER_ECOMP_INSTANCE_ID = "x-ecomp-instanceid";
private static final String HEADER_AUTHORIZATION = "Authorization";
+ // changes for Post Implementation
+ private static final String USER_ID = "USER_ID";
private static final Logger LOGGER = LoggerFactory.getLogger(SdcClient.class);
@@ -156,6 +157,19 @@ public class SdcClient {
return createTmpFile(inputStream);
}
+ /**
+ *
+ * @param serviceCatalogObject
+ * @param userId
+ */
+ public Map callPost(HashMap<Object, Object> serviceCatalogObject, String userId) {
+ // post url is the same as find url
+ UriComponentsBuilder callURI = UriComponentsBuilder.fromHttpUrl(sdcFindUrl);
+ ResponseEntity<Object> response = callSdcForPost(callURI.build().encode().toUri(), serviceCatalogObject,
+ userId);
+ // return (List<LinkedHashMap>) response.getBody();
+ return (LinkedHashMap) response.getBody();
+ }
private Path createTmpFile(InputStream csarInputStream) throws IOException {
Path csarFile = Files.createTempFile("csar", ".zip");
@@ -208,4 +222,46 @@ public class SdcClient {
response.getBody().toString());
}
}
+
+ //changes for Post implementation start
+ /**
+ *
+ * @param callURI
+ * @param obj
+ * @param userId
+ * @return
+ */
+ private ResponseEntity<Object> callSdcForPost(URI callURI, Object obj, String userId) {
+ ResponseEntity<Object> response = restTemplate.exchange(callURI, HttpMethod.POST,
+ new HttpEntity<>(obj, buildRequestHeaderForPost(userId)), Object.class);
+
+ if (null == response) {
+ return null;
+ } else {
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("response body : {} ", response.getBody().toString());
+ }
+ LOGGER.info("response status : {}", response.getStatusCodeValue());
+ return response;
+ }
+
+ }
+ /**
+ *
+ * @param userId
+ * @return
+ */
+ private HttpHeaders buildRequestHeaderForPost(String userId) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ httpHeaders.add(HEADER_ECOMP_INSTANCE_ID, ecompInstanceId);
+ httpHeaders.add(HEADER_AUTHORIZATION, sdcHeaderAuthorization);
+ httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
+ httpHeaders.add(USER_ID, userId);
+
+ return httpHeaders;
+ }
+
}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java
index 128bc6c..a0c27e7 100644
--- a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java
@@ -16,22 +16,26 @@
package org.onap.nbi.apis.servicecatalog;
+import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.onap.nbi.OnapComponentsUrlPaths;
+import org.onap.nbi.apis.servicecatalog.model.ServiceSpecificationRequest;
import org.onap.nbi.commons.JsonRepresentation;
import org.onap.nbi.commons.ResourceManagement;
+import org.onap.nbi.exceptions.ValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import javax.validation.Valid;
@RestController
@RequestMapping(OnapComponentsUrlPaths.SERVICE_SPECIFICATION_PATH)
@@ -42,17 +46,17 @@ public class ServiceSpecificationResource extends ResourceManagement {
@GetMapping(value = "/{serviceSpecId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getServiceSpecification(@PathVariable String serviceSpecId,
- @RequestParam MultiValueMap<String, String> params) {
+ @RequestParam MultiValueMap<String, String> params) {
Map response = serviceSpecificationService.get(serviceSpecId);
if (response != null) {
- ArrayList<Map<String, Object>> resourseSpecificationMap= (ArrayList<Map<String, Object>>) response.get("resourceSpecification");
- for (Map<String, Object> map : resourseSpecificationMap) {
- map.remove("childResourceSpecification");
- map.remove("serviceInstanceParams");
- map.remove("InstanceSpecification");
- }
- response.put("resourceSpecification", resourseSpecificationMap);
+ ArrayList<Map<String, Object>> resourseSpecificationMap= (ArrayList<Map<String, Object>>) response.get("resourceSpecification");
+ for (Map<String, Object> map : resourseSpecificationMap) {
+ map.remove("childResourceSpecification");
+ map.remove("serviceInstanceParams");
+ map.remove("InstanceSpecification");
+ }
+ response.put("resourceSpecification", resourseSpecificationMap);
}
JsonRepresentation filter = new JsonRepresentation(params);
@@ -73,10 +77,35 @@ public class ServiceSpecificationResource extends ResourceManagement {
@GetMapping(value = "/{serviceSpecId}/specificationInputSchema", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> findSpecificationInputSchema(@PathVariable String serviceSpecId,
- @RequestParam MultiValueMap<String, String> params) {
+ @RequestParam MultiValueMap<String, String> params) {
String response = serviceSpecificationService.getInputSchema(serviceSpecId);
JsonRepresentation filter = new JsonRepresentation(params);
return this.getResponse(response, filter);
}
+ @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+ public Object createServiceSpecification(@RequestHeader(value = "USER_ID", required = true) String userId,
+ @Valid @RequestBody ServiceSpecificationRequest serviceSpecificationRequest, BindingResult result) {
+ if (null == userId || userId.isEmpty()) {
+ result.addError(new ObjectError("USER_ID", "USER_ID is missing in header!"));
+ }
+ if (result.hasErrors()) {
+ throw new ValidationException(result.getAllErrors());
+ }
+ Map serviceCatalogResponse = serviceSpecificationService.create(userId, serviceSpecificationRequest);
+
+ return createResponse(serviceCatalogResponse);
+ }
+ /**
+ *
+ * @param resource
+ * @return
+ */
+ private ResponseEntity<Object> createResponse(final Map resource) {
+ URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(resource.get("id"))
+ .toUri();
+ return ResponseEntity.created(location).body(resource);
+
+ }
+
} \ No newline at end of file
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java
index 9fc0ec0..45a4f3f 100644
--- a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java
@@ -17,13 +17,15 @@ package org.onap.nbi.apis.servicecatalog;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.onap.nbi.apis.servicecatalog.jolt.FindServiceSpecJsonTransformer;
import org.onap.nbi.apis.servicecatalog.jolt.GetServiceSpecJsonTransformer;
+import org.onap.nbi.apis.servicecatalog.jolt.PostServiceResponseSpecJsonTransformer;
+import org.onap.nbi.apis.servicecatalog.jolt.PostServiceSpecJsonTransformer;
+import org.onap.nbi.apis.servicecatalog.model.ServiceSpecificationRequest;
import org.onap.nbi.apis.serviceorder.ServiceCatalogUrl;
import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException;
import org.slf4j.Logger;
@@ -45,6 +47,13 @@ public class ServiceSpecificationService {
@Autowired
FindServiceSpecJsonTransformer findServiceSpecJsonTransformer;
+ // Change for processing POST request
+ @Autowired
+ PostServiceSpecJsonTransformer postServiceSpecJsonTransformer;
+
+ @Autowired
+ PostServiceResponseSpecJsonTransformer postServiceResponseSpecJsonTransformer ;
+
@Autowired
ToscaInfosProcessor toscaInfosProcessor;
@@ -101,4 +110,21 @@ public class ServiceSpecificationService {
return null;
}
}
+
+ public Map create(String userId, ServiceSpecificationRequest specRequest) {
+ ObjectMapper mapper = new ObjectMapper();
+ LinkedHashMap specRequestMap = mapper.convertValue(specRequest, LinkedHashMap.class);
+ HashMap<Object, Object> serviceCatalogInput = (HashMap) postServiceSpecJsonTransformer.transform(specRequestMap);
+
+ //Call SDC Post API
+ Map sdcResponse = sdcClient.callPost(serviceCatalogInput,userId);
+ LOGGER.info("SDC response " + sdcResponse);
+ //Transform SDC Response
+ LinkedHashMap<Object,Object> serviceCatalogResponse =null;
+ if (!CollectionUtils.isEmpty(sdcResponse)) {
+ serviceCatalogResponse = (LinkedHashMap)postServiceResponseSpecJsonTransformer.transform(sdcResponse);
+ }
+ return serviceCatalogResponse;
+ }
+
}
diff --git a/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java b/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java
index c68e6d3..1fe6a8c 100644
--- a/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java
+++ b/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java
@@ -16,6 +16,7 @@
package org.onap.nbi.exceptions;
+import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -56,4 +57,11 @@ public class ApiExceptionHandler {
ApiError apiError = new ApiError("400", HttpStatus.BAD_REQUEST.getReasonPhrase(), exception.getMessages(), "");
return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST);
}
+
+ @ExceptionHandler(JsonProcessingException.class)
+ @ResponseBody
+ public ResponseEntity<ApiError> validationExceptionHandler(final JsonProcessingException exception) {
+ ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST.name(), HttpStatus.BAD_REQUEST.getReasonPhrase(), "Request data is invalid!", "");
+ return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST);
+ }
}
diff --git a/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java b/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java
index 48bf630..488ebd7 100644
--- a/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java
+++ b/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java
@@ -19,6 +19,8 @@ package org.onap.nbi.exceptions;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;
@@ -26,7 +28,7 @@ import org.springframework.web.client.ResponseErrorHandler;
public class BackendErrorHandler implements ResponseErrorHandler {
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
-
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackendErrorHandler.class);
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return errorHandler.hasError(response);
@@ -38,6 +40,7 @@ public class BackendErrorHandler implements ResponseErrorHandler {
String body = null;
if (response.getBody() != null) {
body = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8.name());
+ LOGGER.error("BackendErrorHandler {} error : {}", response.getBody());
}
throw new BackendFunctionalException(response.getStatusCode(), response.getStatusText(), body);
diff --git a/src/test/resources/karatetest/data/serviceSpecification.json b/src/test/resources/karatetest/data/serviceSpecification.json
new file mode 100644
index 0000000..858aa11
--- /dev/null
+++ b/src/test/resources/karatetest/data/serviceSpecification.json
@@ -0,0 +1,195 @@
+[
+ {
+ "name": "partner-service",
+ "description": "A test service through TMF API 633",
+ "@type": "ServiceSpecification",
+ "@schemaLocation": null,
+ "@baseType": "Service",
+ "toscaModelURL": "/",
+ "toscaResourceName": "",
+ "category": "Partner Domain Service",
+ "subcategory": "subservice",
+ "version": "1.0",
+ "lifecycleStatus": "NOT_CERTIFIED_CHECKOUT",
+ "DistributionStatus": "DISTRIBUTION_NOT_APPROVED",
+ "targetServiceSchema": {
+ "@type": "string",
+ "@schemaLocation": "/"
+ },
+ "attachment": [{
+ "id" : "1eadef",
+ "name" : "info-artifact",
+ "description" : "informational",
+ "artifactLabel" : "notes",
+ "artifactGroupType" : "info",
+ "artifactTimeout" : "500",
+ "artifactChecksum" : "adef23",
+ "artifactVersion" : "1.0",
+ "generatedFromUUID" : "a12e",
+ "url" : "/artifact/info/1eadef",
+ "mimeType" : "text",
+ "@type" : "ONAPartifact"
+ }
+
+ ],
+ "relatedParty": [
+ {
+ "id": "cs0008",
+ "role": "designer",
+ "name": "Carlos Santana"
+ }
+ ],
+ "resourceSpecification": [
+ {
+ "id" : "281abc4d",
+ "version" : "1.0",
+ "name" : "vfirewall",
+ "resourceInstanceName" : "vfirewallinst",
+ "modelCustomizationName" : "vfirewallinst",
+ "resourceInvariantUUID" : "123ebdf",
+ "resourceType" : "VNF",
+ "@type" : "ONAPresource"
+ }
+ ],
+ "serviceSpecCharacteristic": [
+ {
+ "name": "firewall",
+ "description": "Firewall characteristic",
+ "valueType": "string",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": true,
+ "value": "NA"
+ }
+ ]
+ },
+ {
+ "name": "isBundled",
+ "description": "is bundled or not",
+ "valueType": "boolean",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "boolean",
+ "isDefault": true,
+ "value": true
+ }
+ ]
+ },
+ {
+ "name": "NumberofPort",
+ "description": "NumberofPorts",
+ "valueType": "integer",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": false,
+ "value": "10"
+ }
+ ]
+ },
+ {
+ "name": "ipaddress",
+ "description": "ipaddress",
+ "valueType": "float",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": false,
+ "value": "15.123.9.101"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "@type": "ServiceSpecification",
+ "@schemaLocation": null,
+ "@baseType": "Service",
+ "toscaModelURL": "/",
+ "toscaResourceName": "",
+ "subcategory": "subservice",
+ "version": "1.0",
+ "lifecycleStatus": "NOT_CERTIFIED_CHECKOUT",
+ "targetServiceSchema": {
+ "@type": "string",
+ "@schemaLocation": "/"
+ },
+ "attachment": [],
+ "resourceSpecification": [],
+ "serviceSpecCharacteristic": [
+ {
+ "name": "firewall",
+ "description": "Firewall characteristic",
+ "valueType": "string",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": true,
+ "value": "NA"
+ }
+ ]
+ },
+ {
+ "name": "isBundled",
+ "description": "is bundled or not",
+ "valueType": "boolean",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "boolean",
+ "isDefault": true,
+ "value": true
+ }
+ ]
+ },
+ {
+ "name": "NumberofPorts",
+ "description": "Number of Ports",
+ "valueType": "integer",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": false,
+ "value": "10"
+ }
+ ]
+ },
+ {
+ "name": "ipaddress",
+ "description": "ipaddress",
+ "valueType": "string",
+ "@type": "string",
+ "@schemaLocation": "string",
+ "required": true,
+ "serviceSpecCharacteristicValue": [
+ {
+ "valueType": "string",
+ "isDefault": true,
+ "value": "10.244.34.1"
+ }
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/src/test/resources/karatetest/features/00--ServiceCatalog.feature b/src/test/resources/karatetest/features/00--ServiceCatalog.feature
index 2a4a5c0..1887498 100644
--- a/src/test/resources/karatetest/features/00--ServiceCatalog.feature
+++ b/src/test/resources/karatetest/features/00--ServiceCatalog.feature
@@ -20,9 +20,10 @@ Feature: Service Catalog
Background:
* url nbiBaseUrl
* def Context = Java.type('org.onap.nbi.test.Context');
+* def data = read('../data/serviceSpecification.json')
* configure readTimeout = 30000
* call Context.startServers();
-
+
Scenario: testServiceCatalogGetResourceWithoutTosca
Given path 'serviceSpecification','1e3feeb0-8e36-46c6-862c-236d9c626439_withoutTosca'
When method get
@@ -127,4 +128,74 @@ When method get
Then status 500
* call Context.startServers();
+Scenario: testCreateServiceSpec
+Given path 'serviceSpecification'
+And header USER_ID = 'cs0008'
+And request data[0]
+When method post
+Then status 201
+And match $.id contains '#notnull'
+And match $.lifecycleStatus == 'NOT_CERTIFIED_CHECKOUT'
+And match $.serviceSpecCharacteristic ==
+"""
+[ {
+ "name" : "isBundle",
+ "description" : "is bundled or not",
+ "valueType" : "boolean",
+ "required" : true,
+ "serviceSpecCharacteristicValue" : [ {
+ "value" : "true",
+ "isDefault" : true
+ } ]
+ }, {
+ "name" : "ipaddress",
+ "description" : "ipaddress",
+ "valueType" : "string",
+ "required" : true,
+ "serviceSpecCharacteristicValue" : [ {
+ "value" : "10.244.34.1",
+ "isDefault" : true
+ } ]
+ }, {
+ "name" : "firewall",
+ "description" : "Firewall characteristic",
+ "valueType" : "string",
+ "required" : true,
+ "serviceSpecCharacteristicValue" : [ {
+ "value" : "NA",
+ "isDefault" : true
+ } ]
+ }, {
+ "name" : "NumberofPorts",
+ "description" : "Number of Ports",
+ "valueType" : "integer",
+ "required" : true,
+ "serviceSpecCharacteristicValue" : [ {
+ "value" : "10",
+ "isDefault" : true
+ } ]
+ } ]
+"""
+
+Scenario: testCreateServiceSpecWithoutUser
+Given path 'serviceSpecification'
+And request data[0]
+When method post
+Then status 400
+And match $.message contains "Missing request header 'USER_ID'"
+
+Scenario: testCreateServiceSpecWithoutPayload
+Given path 'serviceSpecification'
+And header USER_ID = 'cs0008'
+And request {}
+When method post
+Then status 400
+
+Scenario: testCreateServiceSpecWithoutMandatoryDetails
+Given path 'serviceSpecification'
+And header USER_ID = 'cs0008'
+And request data[1]
+When method post
+Then status 400
+And match $.message contains 'Bad Request'
diff --git a/src/test/resources/mappings/sdc/sdc_post_spec.json b/src/test/resources/mappings/sdc/sdc_post_spec.json
new file mode 100644
index 0000000..41e724f
--- /dev/null
+++ b/src/test/resources/mappings/sdc/sdc_post_spec.json
@@ -0,0 +1,106 @@
+{
+ "request": {
+ "method": "POST",
+ "url": "/sdc/v1/catalog/services",
+ "headers": {
+ "USER_ID": {
+ "contains": "cs0008"
+ }
+ },
+ "bodyPatterns": [
+ {
+ "contains": "\"name\":\"partner-service\""
+ }
+ ]
+ },
+ "response": {
+ "status": 201,
+ "jsonBody": {
+ "uuid": "1e3feeb0-8e36-46c6-862c-236d9c626439",
+ "href": "/serviceSpecification/1e3feeb0-8e36-46c6-862c-236d9c626439",
+ "name": "partner-service",
+ "description": "service",
+ "@type": "ServiceSpecification",
+ "@schemaLocation": null,
+ "@baseType": "Service",
+ "toscaModelURL": "/",
+ "toscaResourceName": "string",
+ "category": "Partner Domain Service",
+ "subcategory": "string",
+ "version": "1.0",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "targetServiceSchema": {
+ "@type": "string",
+ "@schemaLocation": "/"
+ },
+ "artifacts": [
+ {
+ "artifactUUID": "1eadef",
+ "artifactName": "info-artifact",
+ "artifactDescription": "informational",
+ "artifactLabel": "notes",
+ "artifactGroupType": "info",
+ "artifactTimeout": "500",
+ "artifactChecksum": "adef23",
+ "artifactVersion": "1.0",
+ "generatedFromUUID": "a12e",
+ "artifactURL": "/artifact/info/1eadef",
+ "artifactType": "text"
+ }
+ ],
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "resources": [
+ {
+ "resourceUUID": "281abc4d",
+ "resourceVersion": "1.0",
+ "resourceName": "vfirewall",
+ "resourceInstanceName": "vfirewallinst",
+ "resourceInvariantUUID": "123ebdf",
+ "resoucreType": "VNF"
+ }
+ ],
+ "properties": [
+ {
+ "type": "boolean",
+ "required": true,
+ "definition": false,
+ "description": "is bundled or not",
+ "name": "isBundle",
+ "value": "true",
+ "defaultValue": true
+ },
+ {
+ "type": "string",
+ "required": true,
+ "definition": false,
+ "description": "ipaddress",
+ "name": "ipaddress",
+ "value": "10.244.34.1",
+ "defaultValue": true
+ },
+ {
+ "type": "string",
+ "required": true,
+ "definition": false,
+ "description": "Firewall characteristic",
+ "name": "firewall",
+ "value": "NA",
+ "defaultValue": true
+ },
+ {
+ "type": "integer",
+ "required": true,
+ "definition": false,
+ "description": "Number of Ports",
+ "name": "NumberofPorts",
+ "value": "10",
+ "defaultValue": true
+ }
+ ]
+ },
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+} \ No newline at end of file