fix query param to options 71/124671/6
authortragait <rahul.tyagi@est.tech>
Mon, 4 Oct 2021 15:02:57 +0000 (16:02 +0100)
committertragait <rahul.tyagi@est.tech>
Wed, 6 Oct 2021 14:21:12 +0000 (15:21 +0100)
Issue-ID: CPS-678
Signed-off-by: tragait <rahul.tyagi@est.tech>
Change-Id: I4ac72da512e2c7883920907137b8834ce20d4528

docs/openapi/components.yml
docs/openapi/openapi.yml
src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy

index 736639d..a03cb1f 100644 (file)
@@ -40,10 +40,7 @@ components:
                   revision:
                     type: string
         cmHandleProperties:
-          type: object
-          additionalProperties:
-            type: string
-            example: system-001
+          $ref: '#/components/schemas/cmHandleProperties'
 
     ModuleSet:
       type: object
@@ -82,9 +79,7 @@ components:
           type: string
           enum: [ read ]
         cmHandleProperties:
-          type: object
-          additionalProperties:
-            type: string
+          $ref: '#/components/schemas/cmHandleProperties'
 
     DataAccessWriteRequest:
       type: object
@@ -97,9 +92,13 @@ components:
         data:
           type: string
         cmHandleProperties:
-          type: object
-          additionalProperties:
-            type: string
+          $ref: '#/components/schemas/cmHandleProperties'
+
+    cmHandleProperties:
+      type: object
+      additionalProperties:
+        type: string
+        example: {"prop1":"value1","prop2":"value2"}
 
   responses:
     NotFound:
@@ -174,19 +173,21 @@ components:
         type: string
         enum: [ application/json, application/yang-data+json ]
 
-    fieldsParamInQuery:
-      name: fields
+    optionsParamInQuery:
+      name: options
       in: query
-      description: Fields parameter to filter resource
+      description: options parameter in query, it is mandatory to wrap key(s)=value(s) in parenthesis'()'.
       required: false
       schema:
         type: string
-
-    depthParamInQuery:
-      name: depth
-      in: query
-      description: Depth parameter for response
-      required: false
-      schema:
-        type: integer
-        minimum: 1
\ No newline at end of file
+      allowReserved: true
+      examples:
+        sample1:
+          value:
+            options: (key1=value1,key2=value2)
+        sample2:
+          value:
+            options: (key1=value1,key2=value1/value2)
+        sample3:
+          value:
+            options: (key1=10,key2=value2,key3=[val31,val32])
\ No newline at end of file
index 1e7b38c..66f2e6b 100644 (file)
@@ -139,8 +139,7 @@ paths:
         - $ref: 'components.yml#/components/parameters/cmHandleInPath'
         - $ref: 'components.yml#/components/parameters/resourceIdentifierInQuery'
         - $ref: 'components.yml#/components/parameters/acceptParamInHeader'
-        - $ref: 'components.yml#/components/parameters/fieldsParamInQuery'
-        - $ref: 'components.yml#/components/parameters/depthParamInQuery'
+        - $ref: 'components.yml#/components/parameters/optionsParamInQuery'
       requestBody:
         description: Operational body
         content:
@@ -168,8 +167,7 @@ paths:
         - $ref: 'components.yml#/components/parameters/cmHandleInPath'
         - $ref: 'components.yml#/components/parameters/resourceIdentifierInQuery'
         - $ref: 'components.yml#/components/parameters/acceptParamInHeader'
-        - $ref: 'components.yml#/components/parameters/fieldsParamInQuery'
-        - $ref: 'components.yml#/components/parameters/depthParamInQuery'
+        - $ref: 'components.yml#/components/parameters/optionsParamInQuery'
       requestBody:
         description: Operational body
         content:
index 2c2536f..908ccbe 100644 (file)
@@ -26,7 +26,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.util.List;
 import javax.validation.Valid;
-import javax.validation.constraints.Min;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.dmi.model.CmHandles;
 import org.onap.cps.ncmp.dmi.model.DataAccessReadRequest;
@@ -114,56 +113,50 @@ public class DmiRestController implements DmiPluginApi, DmiPluginInternalApi {
 
     /**
      * This method fetches the resource for given cm handle using pass through operational. It filters the response on
-     * the basis of depth and field query parameters and returns response.
+     * the basis of options query parameters and returns response.
      *
      * @param resourceIdentifier    resource identifier to fetch data
      * @param cmHandle              cm handle identifier
      * @param dataAccessReadRequest data Access Read Request
-     * @param accept                accept header parameter
-     * @param fields                fields to filter the response data
-     * @param depth                 depth parameter for the response
+     * @param acceptParamInHeader   accept header parameter
+     * @param optionsParamInQuery   options query parameter
      * @return {@code ResponseEntity} response entity
      */
     @Override
     public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String resourceIdentifier,
         final String cmHandle,
         final @Valid DataAccessReadRequest dataAccessReadRequest,
-        final String accept,
-        final @Valid String fields,
-        final @Min(1) @Valid Integer depth) {
+        final String acceptParamInHeader,
+        final @Valid String optionsParamInQuery) {
         final var modulesListAsJson = dmiService.getResourceDataOperationalForCmHandle(cmHandle,
             resourceIdentifier,
-            accept,
-            fields,
-            depth,
+            acceptParamInHeader,
+            optionsParamInQuery,
             dataAccessReadRequest.getCmHandleProperties());
         return ResponseEntity.ok(modulesListAsJson);
     }
 
     /**
      * This method fetches the resource for given cm handle using pass through running. It filters the response on the
-     * basis of depth and field query parameters and returns response.
+     * basis of options query parameters and returns response.
      *
      * @param resourceIdentifier    resource identifier to fetch data
      * @param cmHandle              cm handle identifier
      * @param dataAccessReadRequest data Access Read Request
-     * @param accept                accept header parameter
-     * @param fields                fields to filter the response data
-     * @param depth                 depth parameter for the response
+     * @param acceptParamInHeader   accept header parameter
+     * @param optionsParamInQuery   options query parameter
      * @return {@code ResponseEntity} response entity
      */
     @Override
     public ResponseEntity<Object> getResourceDataPassthroughRunningForCmHandle(final String resourceIdentifier,
         final String cmHandle,
         final @Valid DataAccessReadRequest dataAccessReadRequest,
-        final String accept,
-        final @Valid String fields,
-        final @Min(1) @Valid Integer depth) {
+        final String acceptParamInHeader,
+        final @Valid String optionsParamInQuery) {
         final var modulesListAsJson = dmiService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
             resourceIdentifier,
-            accept,
-            fields,
-            depth,
+            acceptParamInHeader,
+            optionsParamInQuery,
             dataAccessReadRequest.getCmHandleProperties());
         return ResponseEntity.ok(modulesListAsJson);
     }
index 753810a..c691039 100644 (file)
@@ -61,40 +61,36 @@ public interface DmiService {
 
     /**
      * This method use to fetch the resource data from cm handle for datastore pass-through operational and resource
-     * Identifier. Fields and depths query parameter are used to filter the response from network resource.
+     * Identifier. Options query parameter are used to filter the response from network resource.
      *
      * @param cmHandle            cm handle identifier
      * @param resourceIdentifier  resource identifier
-     * @param acceptParam         accept header parameter
-     * @param fieldsQuery         fields query parameter
-     * @param depthQuery          depth query parameter
+     * @param acceptParamInHeader accept header parameter
+     * @param optionsParamInQuery options query parameter
      * @param cmHandlePropertyMap cm handle properties
      * @return {@code Object} response from network function
      */
     Object getResourceDataOperationalForCmHandle(@NotNull String cmHandle,
         @NotNull String resourceIdentifier,
-        String acceptParam,
-        String fieldsQuery,
-        Integer depthQuery,
+        String acceptParamInHeader,
+        String optionsParamInQuery,
         Map<String, String> cmHandlePropertyMap);
 
     /**
      * This method use to fetch the resource data from cm handle for datastore pass-through running and resource
-     * Identifier. Fields and depths query parameter are used to filter the response from network resource.
+     * Identifier. Options query parameter are used to filter the response from network resource.
      *
      * @param cmHandle            cm handle identifier
      * @param resourceIdentifier  resource identifier
-     * @param acceptParam         accept header parameter
-     * @param fieldsQuery         fields query parameter
-     * @param depthQuery          depth query parameter
+     * @param acceptParamInHeader accept header parameter
+     * @param optionsParamInQuery options query parameter
      * @param cmHandlePropertyMap cm handle properties
      * @return {@code Object} response from network function
      */
     Object getResourceDataPassThroughRunningForCmHandle(@NotNull String cmHandle,
         @NotNull String resourceIdentifier,
-        String acceptParam,
-        String fieldsQuery,
-        Integer depthQuery,
+        String acceptParamInHeader,
+        String optionsParamInQuery,
         Map<String, String> cmHandlePropertyMap);
 
     /**
index 844cc4d..b66e5c1 100644 (file)
@@ -60,13 +60,12 @@ public class DmiServiceImpl implements DmiService {
     private NcmpRestClient ncmpRestClient;
     private ObjectMapper objectMapper;
     private DmiPluginProperties dmiPluginProperties;
-    private static final String CONTENT_QUERY_PASSTHROUGH_OPERATIONAL = "content=all";
-    private static final String CONTENT_QUERY_PASSTHROUGH_RUNNING = "content=config";
+    private static final String RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM = "content=all";
+    private static final String REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM = "content=config";
     private static final String RESPONSE_CODE = "response code : ";
     private static final String MESSAGE = " message : ";
     private static final String IETF_NETCONF_MONITORING_OUTPUT = "ietf-netconf-monitoring:output";
 
-
     /**
      * Constructor.
      *
@@ -178,34 +177,30 @@ public class DmiServiceImpl implements DmiService {
     @Override
     public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle,
                                                         final @NotNull String resourceIdentifier,
-                                                        final String acceptParam,
-                                                        final String fieldsQuery,
-                                                        final Integer depthQuery,
+                                                        final String acceptParamInHeader,
+                                                        final String optionsParamInQuery,
                                                         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_OPERATIONAL);
+                optionsParamInQuery,
+                acceptParamInHeader,
+                RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM);
         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 String acceptParamInHeader,
+                                                               final String optionsParamInQuery,
                                                                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);
+                optionsParamInQuery,
+                acceptParamInHeader,
+                REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM);
         return prepareAndSendResponse(responseEntity, cmHandle);
     }
 
index 73503d2..98371bf 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.dmi.service.operation;
 
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import org.apache.groovy.parser.antlr4.util.StringUtils;
@@ -92,23 +93,22 @@ public class SdncOperations {
      *
      * @param nodeId      network resource identifier
      * @param resourceId  resource identifier
-     * @param fieldsValue fields query
-     * @param depthValue  depth query
-     * @param acceptParam accept parameter
+     * @param optionsParamInQuery fields query
+     * @param acceptParamInHeader accept parameter
+     * @param restconfContentQueryParam restconf content query param
      * @return {@code ResponseEntity} response entity
      */
     public ResponseEntity<String> getResouceDataForOperationalAndRunning(final String nodeId,
         final String resourceId,
-        final String fieldsValue,
-        final Integer depthValue,
-        final String acceptParam,
-                                                                         final String contentQuery) {
+        final String optionsParamInQuery,
+        final String acceptParamInHeader,
+        final String restconfContentQueryParam) {
         final String getResourceDataUrl = prepareResourceDataUrl(nodeId,
             resourceId,
-            getQueryList(fieldsValue, depthValue, contentQuery));
+            buildQueryParamList(optionsParamInQuery, restconfContentQueryParam));
         final HttpHeaders httpHeaders = new HttpHeaders();
-        if (!StringUtils.isEmpty(acceptParam)) {
-            httpHeaders.set(HttpHeaders.ACCEPT, acceptParam);
+        if (!StringUtils.isEmpty(acceptParamInHeader)) {
+            httpHeaders.set(HttpHeaders.ACCEPT, acceptParamInHeader);
         }
         return sdncRestconfClient.getOperation(getResourceDataUrl, httpHeaders);
     }
@@ -131,20 +131,25 @@ public class SdncOperations {
     }
 
     @NotNull
-    private List<String> getQueryList(final String fieldsValue, final Integer depthValue, final String contentQuery) {
-        final List<String> queryList = new LinkedList<>();
-        if (!StringUtils.isEmpty(fieldsValue)) {
-            queryList.add("fields=" + fieldsValue);
-        }
-        if (depthValue != null) {
-            queryList.add("depth=" + depthValue);
-        }
-        if (!StringUtils.isEmpty(contentQuery)) {
-            queryList.add(contentQuery);
+    private List<String> buildQueryParamList(final String optionsParamInQuery, final String restconfContentQueryParam) {
+        final List<String> queryParamAsList = getOptionsParamAsList(optionsParamInQuery);
+        queryParamAsList.add(restconfContentQueryParam);
+        return queryParamAsList;
+    }
+
+    private List<String> getOptionsParamAsList(final String optionsParamInQuery) {
+        final List<String> queryParamAsList = new LinkedList<>();
+        if (!StringUtils.isEmpty(optionsParamInQuery)) {
+            final String tempQuery = stripParenthesisFromOptionsQuery(optionsParamInQuery);
+            queryParamAsList.addAll(Arrays.asList(tempQuery.split(",")));
         }
-        return queryList;
+        return queryParamAsList;
     }
 
+    @NotNull
+    private String stripParenthesisFromOptionsQuery(final String optionsParamInQuery) {
+        return optionsParamInQuery.substring(1, optionsParamInQuery.length() - 1);
+    }
 
     @NotNull
     private String prepareGetSchemaUrl(final String nodeId) {
index 256db4e..3bc1f3b 100644 (file)
@@ -198,7 +198,7 @@ class DmiRestControllerSpec extends Specification {
     def 'Get resource data for pass-through operational from cm handle.'() {
         given: 'Get resource data url'
             def getResourceDataForCmHandleUrl = "${basePathV1}/ch/some-cmHandle/data/ds/ncmp-datastore:passthrough-operational" +
-                    "?resourceIdentifier=abc/xyz&fields=myfields&depth=5"
+                    "?resourceIdentifier=parent/child&options=(fields=myfields,depth=5)"
             def json = '{"cmHandleProperties" : { "prop1" : "value1", "prop2" : "value2"}}'
         when: 'get resource data PUT api is invoked'
             def response = mvc.perform(
@@ -209,10 +209,9 @@ class DmiRestControllerSpec extends Specification {
             response.status == HttpStatus.OK.value()
         and: 'dmi service called with get resource data for cm handle'
             1 * mockDmiService.getResourceDataOperationalForCmHandle('some-cmHandle',
-                    'abc/xyz',
+                    'parent/child',
                     'application/json',
-                    'myfields',
-                    5,
+                    '(fields=myfields,depth=5)',
                     ['prop1': 'value1', 'prop2': 'value2'])
     }
 
@@ -243,7 +242,7 @@ class DmiRestControllerSpec extends Specification {
     def 'Get resource data for pass-through running from cm handle with #scenario value in resource identifier param.'() {
         given: 'Get resource data url'
             def getResourceDataForCmHandleUrl = "${basePathV1}/ch/some-cmHandle/data/ds/ncmp-datastore:passthrough-running" +
-                    "?resourceIdentifier="+resourceIdentifier+"&fields=testFields&depth=5"
+                    "?resourceIdentifier="+resourceIdentifier+"&options=(fields=myfields,depth=5)"
             def json = '{"cmHandleProperties" : { "prop1" : "value1", "prop2" : "value2"}}'
         when: 'get resource data PUT api is invoked'
             def response = mvc.perform(
@@ -256,8 +255,7 @@ class DmiRestControllerSpec extends Specification {
             1 * mockDmiService.getResourceDataPassThroughRunningForCmHandle('some-cmHandle',
                     resourceIdentifier,
                     'application/json',
-                    'testFields',
-                    5,
+                    '(fields=myfields,depth=5)',
                     ['prop1':'value1', 'prop2':'value2'])
         where: 'tokens are used in the resource identifier parameter'
             scenario                       | resourceIdentifier
index 4c6bc75..9325d59 100644 (file)
@@ -210,15 +210,14 @@ class DmiServiceImplSpec extends Specification {
             def cmHandle = 'testCmHandle'
             def resourceId = 'testResourceId'
             def acceptHeaderParam = 'testAcceptParam'
-            def fieldsParam = 'testFields'
-            def depthParam = 10
+            def optionsParam = '(fields=x/y/z,depth=10,test=abc)'
             def contentQuery = 'content=all'
         and: 'sdnc operation returns OK response'
-            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, fieldsParam, depthParam, acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK)
+            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam, acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK)
         when: 'get resource data from cm handles service method invoked'
             def response = objectUnderTest.getResourceDataOperationalForCmHandle(cmHandle,
                     resourceId, acceptHeaderParam,
-                    fieldsParam, depthParam, null)
+                    optionsParam, null)
         then: 'response have expected json'
             response == 'response json'
     }
@@ -228,14 +227,13 @@ class DmiServiceImplSpec extends Specification {
             def cmHandle = 'testCmHandle'
             def resourceId = 'testResourceId'
             def acceptHeaderParam = 'testAcceptParam'
-            def fieldsParam = 'testFields'
-            def depthParam = 10
+            def optionsParam = '(fields=x/y/z,depth=10,test=abc)'
         and: 'sdnc operation returns "NOT_FOUND" response'
-            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, fieldsParam, depthParam, acceptHeaderParam, _ as String) >> new ResponseEntity<>(HttpStatus.NOT_FOUND)
+            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam, acceptHeaderParam, _ as String) >> new ResponseEntity<>(HttpStatus.NOT_FOUND)
         when: 'get resource data from cm handles service method invoked'
             objectUnderTest.getResourceDataOperationalForCmHandle(cmHandle,
                     resourceId, acceptHeaderParam,
-                    fieldsParam, depthParam, null)
+                    optionsParam, null)
         then: 'resource data not found'
             thrown(ResourceDataNotFound.class)
     }
@@ -245,16 +243,15 @@ class DmiServiceImplSpec extends Specification {
             def cmHandle = 'testCmHandle'
             def resourceId = 'testResourceId'
             def acceptHeaderParam = 'testAcceptParam'
-            def fieldsParam = 'testFields'
-            def depthParam = 10
+            def optionsParam = '(fields=x/y/z,depth=10,test=abc)'
             def contentQuery = 'content=config'
         and: 'sdnc operation returns OK response'
-            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, fieldsParam,
-                    depthParam, acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK)
+            mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam,
+                    acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK)
         when: 'get resource data from cm handles service method invoked'
             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle(cmHandle,
                     resourceId, acceptHeaderParam,
-                    fieldsParam, depthParam, null)
+                    optionsParam, null)
         then: 'response have expected json'
             response == 'response json'
     }
index 95a9c0a..4411971 100644 (file)
@@ -61,10 +61,10 @@ class SdncOperationsSpec extends Specification {
 
     def 'Get resource data from node to SDNC.'() {
         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'
+            def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/testResourceId?a=1&b=2&content=testContent'
         when: 'called get modules from node'
             objectUnderTest.getResouceDataForOperationalAndRunning('node1', 'testResourceId',
-                    'testFields', 10, 'testAcceptParam', 'content=testContent')
+                    '(a=1,b=2)', 'testAcceptParam', 'content=testContent')
         then: 'the get operation is executed with the correct URL'
             1 * mockSdncRestClient.getOperation(expectedUrl, _ as HttpHeaders)
     }
@@ -77,4 +77,33 @@ class SdncOperationsSpec extends Specification {
         then: 'the post operation is executed with the correct URL and data'
             1 * mockSdncRestClient.postOperationWithJsonData(expectedUrl, 'requestData', _ as HttpHeaders)
     }
+
+    def 'build query param list for SDNC where options contains a #scenario'() {
+        when: 'build query param list is called with #scenario'
+            def result = objectUnderTest.buildQueryParamList(optionsParamInQuery,'d=4')
+        then: 'result equals to expected result'
+            result == expectedResult
+        where: 'following parameters are used'
+            scenario                  | optionsParamInQuery || expectedResult
+            'single key-value pair'   | '(a=x)'             || ['a=x','d=4']
+            'multiple key-value pairs'| '(a=x,b=y,c=z)'     || ['a=x','b=y','c=z','d=4']
+            '/ as special char'       | '(a=x,b=y,c=t/z)'   || ['a=x','b=y','c=t/z','d=4']
+            '" as special char'       | '(a=x,b=y,c="z")'   || ['a=x','b=y','c="z"','d=4']
+            '[] as special char'      | '(a=x,b=y,c=[z])'   || ['a=x','b=y','c=[z]','d=4']
+            '= in value'              | '(a=(x=y),b=x=y)'   || ['a=(x=y)','b=x=y','d=4']
+    }
+
+    def 'options parameters contains a comma #scenario'() {
+        // https://jira.onap.org/browse/CPS-719
+        when: 'build query param list is called with #scenario'
+            def result = objectUnderTest.buildQueryParamList(optionsParamInQuery,'d=4')
+        then: 'expect 2 elements from options +1 from content query param (2+1) = 3 elements'
+            def expectedNoOfElements = 3
+        and: 'results contains more elements than expected'
+            result.size() > expectedNoOfElements
+        where: 'following parameters are used'
+            scenario              | optionsParamInQuery
+            '"," in value'        | '(a=(x,y),b=y)'
+            '"," in string value' | '(a="x,y",b=y)'
+    }
 }