diff options
author | tragait <rahul.tyagi@est.tech> | 2021-09-14 13:47:52 +0100 |
---|---|---|
committer | tragait <rahul.tyagi@est.tech> | 2021-09-21 14:38:30 +0100 |
commit | fa11e9cb799b9738d2295abd4690c142639b9250 (patch) | |
tree | 165fa9c085f2d87d53053af0e861e4bc12680583 | |
parent | accefb1c6f1fbb2ab904acbf7d5f4eb33ef51cee (diff) |
fix data from object to string
Also, make dmi service url configurable,
change name of dmi creds env var
Issue-ID: CPS-675
Issue-ID: CPS-634
Signed-off-by: tragait <rahul.tyagi@est.tech>
Change-Id: I6fe794b3e65b6a3aae2a1698ea64a925238a18d3
-rw-r--r-- | docs/openapi/components.yml | 2 | ||||
-rw-r--r-- | docs/openapi/openapi.yaml | 2 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java | 4 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java | 2 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java | 83 | ||||
-rw-r--r-- | src/main/resources/application.yml | 6 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy | 16 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy | 38 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy | 10 | ||||
-rw-r--r-- | src/test/resources/dataWithNormalChar.json (renamed from src/test/resources/WriteDataForCmHandle.json) | 4 | ||||
-rw-r--r-- | src/test/resources/dataWithSpecialChar.json | 8 |
11 files changed, 88 insertions, 87 deletions
diff --git a/docs/openapi/components.yml b/docs/openapi/components.yml index bac5f9c8..3866b834 100644 --- a/docs/openapi/components.yml +++ b/docs/openapi/components.yml @@ -78,7 +78,7 @@ components: dataType: type: string data: - type: object + type: string cmHandleProperties: type: object additionalProperties: diff --git a/docs/openapi/openapi.yaml b/docs/openapi/openapi.yaml index 58a23d90..037bc9fd 100644 --- a/docs/openapi/openapi.yaml +++ b/docs/openapi/openapi.yaml @@ -398,7 +398,7 @@ components: dataType: type: string data: - type: object + type: string cmHandleProperties: type: object additionalProperties: diff --git a/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java b/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java index 614435f2..31a78111 100644 --- a/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java +++ b/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java @@ -50,8 +50,8 @@ public class DmiPluginConfig { @Component public static class DmiPluginProperties { - @Value("${dmi.service.name}") - private String dmiServiceName; + @Value("${dmi.service.url}") + private String dmiServiceUrl; } } diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java index f1446084..7f79a04c 100644 --- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java +++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java @@ -106,5 +106,5 @@ public interface DmiService { * @return response from sdnc */ String writeResourceDataPassthroughForCmHandle(String cmHandle, String resourceIdentifier, String dataType, - Object data); + String data); }
\ No newline at end of file diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java index db115ce4..11668904 100644 --- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java +++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java @@ -76,8 +76,8 @@ public class DmiServiceImpl implements DmiService { * @param objectMapper objectMapper */ public DmiServiceImpl(final DmiPluginProperties dmiPluginProperties, - final NcmpRestClient ncmpRestClient, - final SdncOperations sdncOperations, final ObjectMapper objectMapper) { + final NcmpRestClient ncmpRestClient, + final SdncOperations sdncOperations, final ObjectMapper objectMapper) { this.dmiPluginProperties = dmiPluginProperties; this.ncmpRestClient = ncmpRestClient; this.objectMapper = objectMapper; @@ -95,7 +95,7 @@ public class DmiServiceImpl implements DmiService { return createModuleSchema(responseBody); } else { throw new DmiException("SDNC is not able to process request.", - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } @@ -110,11 +110,11 @@ public class DmiServiceImpl implements DmiService { } else if (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND) { log.error("SDNC did not return a module resource for the given cmHandle {}", cmHandle); throw new ModuleResourceNotFoundException(cmHandle, - "SDNC did not return a module resource for the given cmHandle."); + "SDNC did not return a module resource for the given cmHandle."); } else { log.error("Error occurred when getting module resources from SDNC for the given cmHandle {}", cmHandle); throw new DmiException(cmHandle, - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } return getModuleResponses.toJSONString(); @@ -123,7 +123,7 @@ public class DmiServiceImpl implements DmiService { @Override public void registerCmHandles(final List<String> cmHandles) { final CmHandleOperation cmHandleOperation = new CmHandleOperation(); - cmHandleOperation.setDmiPlugin(dmiPluginProperties.getDmiServiceName()); + cmHandleOperation.setDmiPlugin(dmiPluginProperties.getDmiServiceUrl()); final List<CreatedCmHandle> createdCmHandleList = new ArrayList<>(); for (final String cmHandle : cmHandles) { final CreatedCmHandle createdCmHandle = new CreatedCmHandle(); @@ -137,7 +137,7 @@ public class DmiServiceImpl implements DmiService { } catch (final JsonProcessingException e) { log.error("Parsing error occurred while converting cm-handles to JSON {}", cmHandles); throw new DmiException("Internal Server Error.", - "Parsing error occurred while converting given cm-handles object list to JSON "); + "Parsing error occurred while converting given cm-handles object list to JSON "); } final ResponseEntity<String> responseEntity = ncmpRestClient.registerCmHandlesWithNcmp(cmHandlesJson); if ((responseEntity.getStatusCode() != HttpStatus.CREATED)) { @@ -169,60 +169,51 @@ public class DmiServiceImpl implements DmiService { return objectMapper.readValue(modulesListAsJson, ModuleSchemas.class); } catch (final JsonProcessingException e) { log.error("JSON exception occurred when converting the following modules to node schema " - + "{}", modulesListAsJson); + + "{}", modulesListAsJson); throw new DmiException("Unable to process JSON.", - "JSON exception occurred when mapping modules.", e); + "JSON exception occurred when mapping modules.", e); } } @Override public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String acceptParam, - final String fieldsQuery, - final Integer depthQuery, - final Map<String, String> cmHandlePropertyMap) { + final @NotNull String resourceIdentifier, + final String acceptParam, + final String fieldsQuery, + final Integer depthQuery, + final Map<String, String> cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity<String> responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, - resourceIdentifier, - fieldsQuery, - depthQuery, - acceptParam, + resourceIdentifier, + fieldsQuery, + depthQuery, + acceptParam, CONTENT_QUERY_PASSTHROUGH_OPERATIONAL); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String acceptParam, - final String fieldsQuery, - final Integer depthQuery, - final Map<String, String> cmHandlePropertyMap) { + final @NotNull String resourceIdentifier, + final String acceptParam, + final String fieldsQuery, + final Integer depthQuery, + final Map<String, String> cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity<String> responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, - resourceIdentifier, - fieldsQuery, - depthQuery, - acceptParam, - CONTENT_QUERY_PASSTHROUGH_RUNNING); + resourceIdentifier, + fieldsQuery, + depthQuery, + acceptParam, + CONTENT_QUERY_PASSTHROUGH_RUNNING); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public String writeResourceDataPassthroughForCmHandle(final String cmHandle, final String resourceIdentifier, - final String dataType, final Object data) { - final String jsonData; - try { - jsonData = objectMapper.writeValueAsString(data); - } catch (final JsonProcessingException e) { - log.error("JSON exception occurred when processing pass through request data for the given cmHandle {}", - cmHandle); - throw new DmiException("Unable to process incoming JSON from the request body.", - "JSON exception occurred when writing data for the given cmHandle " + cmHandle, e); - } + final String dataType, final String data) { final ResponseEntity<String> responseEntity = - sdncOperations.writeResourceDataPassthroughRunning(cmHandle, resourceIdentifier, dataType, jsonData); + sdncOperations.writeResourceDataPassthroughRunning(cmHandle, resourceIdentifier, dataType, data); if (responseEntity.getStatusCode().is2xxSuccessful()) { return responseEntity.getBody(); } else { @@ -236,7 +227,7 @@ public class DmiServiceImpl implements DmiService { return responseEntity.getBody(); } else { throw new ResourceDataNotFound(cmHandle, - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } @@ -250,15 +241,15 @@ public class DmiServiceImpl implements DmiService { moduleRequest = writer.writeValueAsString(ietfNetconfModuleReferences); } catch (final JsonProcessingException e) { log.error("JSON exception occurred when creating the module request for the given module reference {}", - moduleReference.getName()); + moduleReference.getName()); throw new DmiException("Unable to process JSON.", - "JSON exception occurred when creating the module request.", e); + "JSON exception occurred when creating the module request.", e); } return moduleRequest; } private JSONObject toJsonObject(final ModuleReference moduleReference, - final ResponseEntity<String> response) { + final ResponseEntity<String> response) { final var jsonObject = new JSONObject(); jsonObject.put("moduleName", moduleReference.getName()); jsonObject.put("revision", moduleReference.getRevision()); @@ -269,13 +260,13 @@ public class DmiServiceImpl implements DmiService { private String extractYangSourceFromBody(final ResponseEntity<String> responseEntity) { final var responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class); if (responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) == null - || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) + || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) .getAsJsonPrimitive("data") == null) { log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody()); throw new ModuleResourceNotFoundException(responseEntity.getBody(), - "Error occurred when trying to parse the response body from sdnc."); + "Error occurred when trying to parse the response body from sdnc."); } return responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT).getAsJsonPrimitive("data") - .toString(); + .toString(); } }
\ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4383e794..0ea666e1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -22,7 +22,7 @@ server: dmi: service: - name: onap-dmi-plugin + url: ${DMI_SERVICE_URL} rest: api: @@ -31,8 +31,8 @@ rest: security: permit-uri: /manage/**,/swagger-ui/**,/swagger-resources/**,/v3/api-docs auth: - username: ${CPS_USERNAME} - password: ${CPS_PASSWORD} + username: ${DMI_USERNAME} + password: ${DMI_PASSWORD} # Actuator management: diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy index a937c4e6..ac4fde3d 100644 --- a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy +++ b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy @@ -26,12 +26,8 @@ import org.onap.cps.ncmp.dmi.TestUtils import org.onap.cps.ncmp.dmi.exception.DmiException import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException -import org.onap.cps.ncmp.dmi.model.DataAccessReadRequest -import org.onap.cps.ncmp.dmi.model.DataAccessWriteRequest import org.onap.cps.ncmp.dmi.model.ModuleReference import org.onap.cps.ncmp.dmi.model.ModuleSchemaList -import org.onap.cps.ncmp.dmi.model.ModuleSchemaProperties -import org.onap.cps.ncmp.dmi.model.ModuleSchemas import org.onap.cps.ncmp.dmi.model.ModuleSet import org.onap.cps.ncmp.dmi.model.ModuleSetSchemas import org.onap.cps.ncmp.dmi.service.DmiService @@ -207,12 +203,14 @@ class DmiRestControllerSpec extends Specification { ['prop1': 'value1', 'prop2': 'value2']) } - def 'Write data using passthrough running for a cm handle.'() { + def 'Write data using passthrough running for a cm handle using #scenario.'() { given: 'write data for cmHandle url and jsonData' def writeDataforCmHandlePassthroughRunning = "${basePathV1}/ch/some-cmHandle/data/ds/ncmp-datastore:passthrough-running/some-resourceIdentifier" - def jsonData = TestUtils.getResourceFileContent('WriteDataForCmHandle.json') + def jsonData = TestUtils.getResourceFileContent(requestBodyFile) and: 'dmi service is called' - mockDmiService.writeResourceDataPassthroughForCmHandle('some-cmHandle', 'some-resourceIdentifier', 'application/json', ['some-data': 'some-value']) >> '{some-json}' + mockDmiService.writeResourceDataPassthroughForCmHandle('some-cmHandle', + 'some-resourceIdentifier', 'application/json', + expectedRequestData) >> '{some-json}' when: 'write cmHandle passthrough running post api is invoked with json data' def response = mvc.perform( post(writeDataforCmHandlePassthroughRunning).contentType(MediaType.APPLICATION_JSON) @@ -222,6 +220,10 @@ class DmiRestControllerSpec extends Specification { response.status == HttpStatus.CREATED.value() and: 'the data in the request body is as expected' response.getContentAsString() == '{some-json}' + where: 'given request body and data' + scenario | requestBodyFile || expectedRequestData + 'data with normal chars' | 'dataWithNormalChar.json' || 'normal request body' + 'data with special chars' | 'dataWithSpecialChar.json'|| 'data with quote \" and new line \n' } def 'Get resource data for pass-through running from cm handle.'() { diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy index a99aa9aa..c1700a2e 100644 --- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy +++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy @@ -98,7 +98,7 @@ class DmiServiceImplSpec extends Specification { def givenCmHandlesList = ['node1', 'node2'] def expectedJson = '{"dmiPlugin":"test-dmi-service","createdCmHandles":[{"cmHandle":"node1"},{"cmHandle":"node2"}]}' and: 'mockDmiPluginProperties returns test-dmi-service' - mockDmiPluginProperties.getDmiServiceName() >> 'test-dmi-service' + mockDmiPluginProperties.getDmiServiceUrl() >> 'test-dmi-service' when: 'register cm handles service method with the given cm handles' objectUnderTest.registerCmHandles(givenCmHandlesList) then: 'register cm handle with ncmp called once and return "created" status' @@ -109,7 +109,7 @@ class DmiServiceImplSpec extends Specification { given: 'cm-handle list' def cmHandlesList = ['node1', 'node2'] and: 'dmi plugin service name is "test-dmi-service"' - mockDmiPluginProperties.getDmiServiceName() >> 'test-dmi-service' + mockDmiPluginProperties.getDmiServiceUrl() >> 'test-dmi-service' and: 'ncmp rest client returns #responseEntity' mockNcmpRestClient.registerCmHandlesWithNcmp(_ as String) >> responseEntity when: 'register cm handles service method called' @@ -170,7 +170,7 @@ class DmiServiceImplSpec extends Specification { def moduleReference = new ModuleReference(name: 'NAME',revision: 'REVISION') def moduleList = [moduleReference] when: 'get module resources is invoked with the given cm handle and a module list' - objectUnderTest.getModuleResources(cmHandle, moduleList) + objectUnderTest.getModuleResources(cmHandle, moduleList) then: 'get modules resources is called once with a response body that contains no data' 1 * mockSdncOperations.getModuleResource(cmHandle, _) >> new ResponseEntity<String>(responseBody, HttpStatus.OK) and: 'a module resource not found exception is thrown' @@ -249,7 +249,7 @@ class DmiServiceImplSpec extends Specification { } def 'Write resource data for passthrough running for the given cm handle with a #scenario from sdnc.'() { - given: 'sdnc returns a response' + given: 'sdnc returns a response with #scenario' mockSdncOperations.writeResourceDataPassthroughRunning(_, _, _, _) >> new ResponseEntity<String>('response json', httpResponse) when: 'write resource data for cm handle method invoked' def response = objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', @@ -262,25 +262,27 @@ class DmiServiceImplSpec extends Specification { '201 CREATED response' | HttpStatus.CREATED } + def 'Write resource data using for passthrough running for the given cm handle with #scenario.'() { + given: 'sdnc returns a created response' + mockSdncOperations.writeResourceDataPassthroughRunning('some-cmHandle', + 'some-resourceIdentifier', 'some-dataType', requestBody) >> new ResponseEntity<String>('response json', HttpStatus.CREATED) + when: 'write resource data from cm handles service method invoked' + def response = objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', + 'some-resourceIdentifier', 'some-dataType', requestBody) + then: 'response have expected json' + response == 'response json' + where: 'given request body' + scenario | requestBody + 'data contains normal char' | 'normal char string' + 'data contains quote and new line' | 'data with quote " and \n new line' + } + def 'Write resource data for passthrough running with a 500 response from sdnc.'() { given: 'sdnc returns a 500 response for the write operation' mockSdncOperations.writeResourceDataPassthroughRunning(_, _, _, _) >> new ResponseEntity<String>('response json', HttpStatus.INTERNAL_SERVER_ERROR) when: 'write resource data for pass through method is invoked' objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', new Object()) - then: 'a dmi exception is thrown' - thrown(DmiException.class) - } - - def 'Write resource data for passthrough running with a json processing exception.'() { - given: 'sdnc returns a 200 response for the write operation' - mockSdncOperations.writeResourceDataPassthroughRunning(_, _, _, _) >> new ResponseEntity<String>('response json', HttpStatus.OK) - and: 'a json processing exception is thrown' - objectUnderTest.objectMapper = mockObjectMapper - mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('some-exception') } - when: 'write resource data for pass through method is invoked' - objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', new Object()) + 'some-resourceIdentifier', 'some-dataType', _ as String) then: 'a dmi exception is thrown' thrown(DmiException.class) } diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy index c9d7d1b8..14a62ebb 100644 --- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy +++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy @@ -60,7 +60,7 @@ class SdncOperationsSpec extends Specification { } def 'Get resource data from node to SDNC.'() { - given: 'excpected url, topology-id, sdncOperation object' + given: 'expected url, topology-id, sdncOperation object' def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/testResourceId?fields=testFields&depth=10&content=testContent' when: 'called get modules from node' objectUnderTest.getResouceDataForOperationalAndRunning('node1', 'testResourceId', @@ -70,11 +70,11 @@ class SdncOperationsSpec extends Specification { } def 'Write resource data to SDNC.'() { - given: 'excpected url, topology-id, sdncOperation object' + given: 'expected url, topology-id, sdncOperation object' def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/testResourceId' when: 'write resource data for pass through running is called' - objectUnderTest.writeResourceDataPassthroughRunning('node1', 'testResourceId', 'application/json','testData') - then: 'the post operation is executed with the correct URL' - 1 * mockSdncRestClient.postOperationWithJsonData(expectedUrl, _ as String, _ as HttpHeaders) + objectUnderTest.writeResourceDataPassthroughRunning('node1','testResourceId','application/json','requestData') + then: 'the post operation is executed with the correct URL and data' + 1 * mockSdncRestClient.postOperationWithJsonData(expectedUrl, 'requestData', _ as HttpHeaders) } }
\ No newline at end of file diff --git a/src/test/resources/WriteDataForCmHandle.json b/src/test/resources/dataWithNormalChar.json index 8eb19599..31cdf1c5 100644 --- a/src/test/resources/WriteDataForCmHandle.json +++ b/src/test/resources/dataWithNormalChar.json @@ -1,9 +1,7 @@ { "operation": "create", "dataType": "application/json", - "data": { - "some-data": "some-value" - }, + "data": "normal request body", "cmHandleProperties": { "some-property": "some-property-value" } diff --git a/src/test/resources/dataWithSpecialChar.json b/src/test/resources/dataWithSpecialChar.json new file mode 100644 index 00000000..1e7622ee --- /dev/null +++ b/src/test/resources/dataWithSpecialChar.json @@ -0,0 +1,8 @@ +{ + "operation": "create", + "dataType": "application/json", + "data": "data with quote \" and new line \n", + "cmHandleProperties": { + "some-property": "some-property-value" + } +}
\ No newline at end of file |