Forum Discussion

richie's avatar
richie
Community Hero
7 years ago

Paramaterize the REST query parm NAME value rather than just the PARM value?

Hey,

 

I have a number of GET requests with a number of query parameters that emulate an SQL query filter.

 

Essentially I have 4 namespaces (databases) with multiple datasets (tables) and my GET request retrieves this info - but there are a number of additional query parameter functionality available to allow the user to filter the GET accordingly.

 

the following is an example of one of the possible requests:

 

GET --> /api/1/{namespace}/{dataset}?attributename=value

 

The above query would equate to the following in SQL

 

select * from database.table where attributename = value;

Now this 'attributename' can be ANY field in the table - I want to be able to parameterize this 'attributename' - but I've only ever parameterized query parm values in SoapUI - not the actual parm names.

 

Please see the image to provide a bit more clarity

 

 

 

As you can see - in the request I've setup - the query parm's name is 'attribute' - I woudl like some way to parameterize this so I can say do a GET  and return a record then do a property transfer grabbing the json node name rather than just the node value and populate the query parameter's NAME as well as the VALUE.

 

If I can't parameterize this - it means a lot of hardcoding requests in the projects section - and that seems an awfully inefficient way of doing things.

 

I hope Ive explained what I mean clearly!

 

I'm totally stumped and don't know if ReadyAPI! actually does include a facility for what I need.

 

thanks to all!

 

richie 

  • Hi richie,

     

    You may take a look at following solution if you're interested in it

    // Step 1: create 4 folders with your properties file.
        // 4 folders named with your database name
        // each properties file named with your table name under current database
        ./namespace1/table1.properties
        ./namespace1/table2.properties
        ...
        ./namepsace4/table16.properties
    
    // Step 2: create a config file to construct relationship of database and table
        // assume that namespace1 / table1 is name of database / table
        // (Example: the config file named with config.groovy)
        database {
            namespace1 = ["table1", "table2", "table3", "table4", "table5"]
            namespace2 = ["table6", "table7", "table8", "table9", "table10"]
            namespace3 = ["table11", "table12", "table13", "table14", "table15"]
            namespace4 = ["table16", "table117", "table18", "table19", "table20"]
        }
    
    // Step 3: set global property (in test case level)
        testRunner.testCase.setPropertyValue("namespace_index", "0")
        testRunner.testCase.setPropertyValue("table_index", "0")
    
    // Step 4: Groovy step to load config file and set property
        def index = context.expand('${#TestCase#namespace_index}').toInteger()
        def config = new groovy.util.ConfigSlurper().parse(new File('./config.groovy').toURI().toURL())
        def _map = config.database
        Map.metaClass.takeAt = {int num ->
            def keys = _map.keySet() as String[]
            def key = keys[num]
            ["$key": _map.get(key)]
        } //add a extension method to get a sub map
        def map = config.database.takeAt(index) // return a submap
        def namespace = map.keySet() as String[]
        testRunner.testCase.setPropertyValue("namespace", namespace[0]) //namespace name
        testRunner.testCase.setPropertyValue("namespaceMap", groovy.json.JsonOutput.toJson(map))
        testRunner.testCase.setPropertyValue("namespaceCount", config.database.size().toString())
        
    
    // Step 5: Groovy step to set property of table
        def map = new groovy.json.JsonSlurper().parseText(context.expand('${#TestCase#namespaceMap}'))
        def key = map.keySet()[0]
        testRunner.testCase.setPropertyValue("tableCount", map.get(key).size().toString())
    
    // Step 6: Groovy step to load properties file
        def props = new Properties()
        def map = new groovy.json.JsonSlurper().parseText(context.expand('${#TestCase#namespaceMap}'))
        def index = context.expand('${#TestCase#table_index}').toInteger()
        def tableName = map.entrySet()[0].value[index]
        testRunner.testCase.setPropertyValue("dataset", tableName)
        new File("./${tableName}.properties").withInputStream {
            props.load(it)
        }
    
        //remove property
        def teststep = testRunner.testCase.testSteps["Step7"]
        teststep.getPropertyList().each{
            if (! it.name.equalsIgnoreCase("namespace") || ! it.name.equalsIgnoreCase("dataset"))
                teststep.testRequest.removeProperty(it.name)
        }
    
        //add property
        for (p in props) { //set all properties into REST step
            testRunner.testCase.testSteps["Step7"].testRequest.setPropertyValue(p.key, p.value)
        }
    
    // Step 7: REST step to query
        // parameterize "namespace" and "dataset"
        namespace = ${#TestCase#namespace}
        dataset = ${#TestCase#dataset}
    
    // Step 8: Groovy step to control table loop
        def table_index = context.expand('${#TestCase#table_index}').toInteger()
        def tableCount = context.expand('${#TestCase#tableCount}').toInteger()
        def namespace = context.expand('${#TestCase#namespace}')
    
        if (table_index + 1 < tableCount) {
            testRunner.testCase.setPropertyValue("table_index", (++table_index).toString())
            testRunner.gotoStepByName("Step6")
        } else {
            log.info("Query all tables with parameters under $namespace")
        }
    
    // Step 9: Groovy step to control namespace loop
        def namespace_index = context.expand('${#TestCase#namesapce_index}').toInteger()
        def namespaceCount = context.expand('${#TestCase#namespaceCount}').toInteger()
    
        if (namespace_index + 1 < namespaceCount) {
            testRunner.testCase.setPropertyValue("namespace_index", (++namespace_index).toString())
            testRunner.gotoStepByName("Step4")
        } else {
            log.info("Query all namespace done")
        }

     

    Thanks,

    /Aaron

  • aaronpliu's avatar
    aaronpliu
    Frequent Contributor

    Assumed that you have a lot of key-value pair as request parameter.

    //Example

    testRunner.testCase.testSteps[StepName].testRequest.setPropertyValue(ParamName, ParamValue)

     

    Step 1: Load properties(key-value) or setup in script

    Step 2: Send your query request with parameter

    Step 3: Loop if need (remove previous parameter name if need)

    //remove property
    testCase.getPropertyList().each{
        testRunner.testCase.removeProperty(it.name)
    }

     

     

    Thanks,

    /Aaron

    • richie's avatar
      richie
      Community Hero

      Hi aaronpliu

       

      I dont know whether I'm being an idiot - but I'm missing something essential (my groovy code skillset comes from code snippets you guys provide -  and thats not an excuse - I try  - but I my groovy sucks - just need to make that clear!).

       

      you are correct - I do have lots of different table's attributes and SQL operations  I need to specify as the query parms on my rest requests

       

      You specify the following:

       

      testRunner.testCase.testSteps[StepName].testRequest.setPropertyValue(ParamName, ParamValue)

      QUESTION 1: so above line sets properties for a particlar request's step - right?

       

      Your next instructions say as follows:

       

      Step 1: Load properties(key-value) or setup in script

      Step 2: Send your query request with parameter

      Step 3: Loop if need (remove previous parameter name if need)

      //remove property
      testCase.getPropertyList().each{
          testRunner.testCase.removeProperty(it.name)
      }

       

      OK - I'm lost at the initial code snippet where you are setting ParmName, ParmValue on the test step. 

      QUESTION2: Why are you setting the property values on the test step  - if step1 is loading in a lists of parmnames, values via a script etc? shouldn't i be getting them rather than setting them?

       

      QUESTION3: I'm a bit confused on the test step hierarchy you envision - would it be something like the following?

       

      Step1 - 'Properties 1' step (name=value pairing of the various parms and values)

      Step2 - 'Groovy' step  as follows:

      testRunner.testCase.testSteps[StepName].testRequest.getPropertyValue(ParamName, ParamValue)

      Step3 - 'REST Request' step with usual template parms set as usual

      Step4 - 'Groovy' step as follows:

      //remove property
      testCase.getPropertyList().each{
          testRunner.testCase.removeProperty(it.name)
      }

       

      QUESTION 4:  How would I tell ReadyAPI! that the parameters specified in the properties are actually query parameters and values rather than any other REST API parameter type?

       

      I hope I've been  clear in my request.

       

       

      Thank you!

       

      richie

      • aaronpliu's avatar
        aaronpliu
        Frequent Contributor

        Hi ,

        Please see my comments for your questions.

        QUESTION 1: so above line sets properties for a particlar request's step - right?

        >>Right. that code snippets would be treated as a separate "Groovy" step or put it into "Setup Script" of certain REST step. If you need to dynamically setup a parameter in step request, then applied it. the precodition is that the "parameter" name is available for your request. Usually, all of request paramters should be loaded when you import your REST / SOAP which defined in source file, right?

        QUESTION2: Why are you setting the property values on the test step  - if step1 is loading in a lists of parmnames, values via a script etc? shouldn't i be getting them rather than setting them?

        >>This is based on your screenshot attached. assumed that you have a REST request with some parameters, and then you would like to dynamically set a parameter name once, you may need to "get" one of them and "set" it in your request, right? so in .setPropertyValue(paramName, paramValue), "paramName" is your parameter name, you should get it first.

        QUESTION3: I'm a bit confused on the test step hierarchy you envision - would it be something like the following?

        >>It depends on your requirement. if you want to run your REST request step with different parameter name / value

        (Example)

        TestCase Setup Script:

        // initialize a property "index" for loop
        testCase.setPropertyValue("index", "0")

        Step 1: "Properties" step (it would load all key-value from external file or you added manually)

         

        Step 2: "Groovy" step

        //setup size of properties in order to loop run
        def propsList = testRunner.testCase.testSteps["Properties"].getPropertyList()
        def index = context.expand('${#TestCase#index}').toInteger()
        testRunner.testCase.setPropertyValue("CountOfProps", propsList.size().toString())
        // setup parameter for REST step
        testRunner.testCase.testSteps["RestStepName"].testRequst.setPropertyValue(propsList[index].name, propsList[index].value)

        Step 3: "REST" step

         

        Step 4: "Groovy" step

        def index = context.expand('${#TestCase#index}').toInteger()
        def max = context.expand('${#TestCase#CountOfProps}').toInteger()
        
        if (index + 1 < max) {
            // go to run step 2, here 'Groovy' name is step 2 name
            testRunner.testCase.setPropertyValue("index", (++index).toString())
            testRunner.gotoStepByName("Groovy")
        }
        // note that your "REST" step will be added more parameters, if you just want to run "REST" step with one parameter, then you need to "remove" others or setup empty

        QUESTION 4:  How would I tell ReadyAPI! that the parameters specified in the properties are actually query parameters and values rather than any other REST API parameter type

         

        >> if you added parameter, it's "query" by default. if you added as header, then you need to use:

        (Example)
        def stringMap = new com.eviware.soapui.support.types.StringToStringMap
        def stringMap = new StringToStringMap()
        stringMap.put('Cookie', 'xxxxxxxxxxx')
        testRunner.testCase.testSteps[YourStepName].testRequest.setRequestHeaders(stringMap)
        

        another parameter is in "path", it should be defined in source as a part of available endpoint.

         

        Any enquires, please post your questions here, the community will notice it and more people to figure it out with different solutions.

         

         

         

        Thanks,

        /Aaron