soapUI Dynamic mock service: How to extract response name from request parameter? - xpath

I am trying to build a mock service in SoapUI, which dynamically returns a response, based on a value passed in the request. Example:
<foo>
<bar>
<ID>Response1</ID> <--- I want to extract this
<ReferenceID>stuff</ReferenceID>
<CreationDate>2016-05-01T11:34:56Z</CreationDate>
</bar>
</foo>
So I set my DISPATCH to SCRIPT and tried the following (the return value should specify the name of the response, which is returned):
def req = new XmlSlurper().parseText(mockRequest.requestContent)
return "${req.foo.bar.ID}"
And this:
def holder = new com.eviware.soapui.support.XmlHolder(mockRequest.requestContent )
def arg1 = holder.getNodeValue("ID") // also tried "//ID"
return arg1.toString();
Neither worked, the mock always returns the default response - hope some of you can help me with the solution :)

The problem is probably that your <foo> response is wrapped in a SOAP<envelope> and <body> so the path you're using with XmlSlurper is not correct req.foo.bar.ID.
Furthermore if in your case your response is not wrapper with <envelope> and <body> note that in the XmlSlurper the root node starts at the object itself so the req.foo is not needed since <foo> is the root node, looks at the follow example:
def xml =
'''<foo>
<bar>
<ID>Response1</ID>
<ReferenceID>stuff</ReferenceID>
<CreationDate>2016-05-01T11:34:56Z</CreationDate>
</bar>
</foo>
'''
def slurper = new XmlSlurper().parseText(xml)
println slurper.foo // prints nothing...
println slurper.bar.ID // prints Response1
Due to this maybe the easy way to get the node value is to use find method, so in your DISPATCH script:
def req = new XmlSlurper().parseText(mockRequest.requestContent)
return req.'**'.find { it.name() == 'ID' }
Alternatively if you want to use XmlHolder instead of XmlSlurper as #Rao comments simply use a namespace on your XPath. Fortunately SOAPUI allows you to use * as a wildcard for namespaces so correct ID by //*:ID:
def holder = new com.eviware.soapui.support.XmlHolder(mockRequest.requestContent )
return holder.getNodeValue("//*:ID").toString()
Hope it helps,

Related

conditional FilterSets in DRF 3.7 autogen docs: can I add a queryparam filter for a route (but only for certain HTTP verbs)

(DRF v3.7, django-filters v1.1.0)
Hi! I have a working FilterSet that lets me filter my results via a query parameter, e.g. http://localhost:9000/mymodel?name=FooOnly
This is working just fine.
class MyNameFilter(FilterSet):
name = CharFilter(field_name='name', help_text='Filter by name')
class Meta:
model = MyModel
fields = ('name',)
class MyModel(...):
...
filter_backends = (DjangoFilterBackend,)
filter_class = MyNameFilter
But when I render the built-in auto-generated docs for my API, I am seeing this query parameter documented for all methods in my route, e.g. GET, PUT, PATCH, etc.
I only intend to filter via this query parameter for some of these HTTP verbs, as it doesn't make sense for others, e.g. PUT
Is there a good way to make my FilterSet conditional in this manner? Conditional on route method.
I tried applying this logic at both the Router level (a misguided idea). Also at the ViewSet level -- but there is no get_filter_class override method in the same way there is e.g. get_serializer_class.
Thanks for the help.
you'll get get_filter_class in DjangoFilterBackend. You need to create a new FilterBackend which overrides the filter_queryset method.
class GETFilterBackend(DjangoFilterBackend):
def filter_queryset(self, request, queryset, view):
if request.method == 'GET':
return super().filter_queryset(request, queryset, view)
return queryset
class MyModel(...):
...
filter_backends = (GETFilterBackend,)
filter_class = MyNameFilter
Figured this out, with help from Carlton G. on the django-filters Google Groups forum (thank you, Carlton).
My solution was to go up a level and intercept the CoreAPI schema that came out of the AutoSchema inspection, but before it made its way into the auto-generated docs.
At this point of interception, I override _allows_filters to apply only on my HTTP verbs of interest. (Despite being prefixed with a _ and thus intended as a private method not meant for overriding, the method's comments explicitly encourage this. Introduced in v3.7: Initially "private" (i.e. with leading underscore) to allow changes based on user experience.
My code below:
from rest_framework.schemas import AutoSchema
# see https://www.django-rest-framework.org/api-guide/schemas/#autoschema
# and https://www.django-rest-framework.org/api-guide/filtering/
class LimitedFilteringViewSchema(AutoSchema):
# Initially copied from lib/python2.7/site-packages/rest_framework/schemas/inspectors.py:352,
# then modified to restrict our filtering by query-parameters to only certain view
# actions or HTTP verbs
def _allows_filters(self, path, method):
if getattr(self.view, 'filter_backends', None) is None:
return False
if hasattr(self.view, 'action'):
return self.view.action in ["list"] # original code: ["list", "retrieve", "update", "partial_update", "destroy"]
return method.lower() in ["get"] # original code: ["get", "put", "patch", "delete"]
And then, at my APIView level:
class MyViewSchema(LimitedFilteringViewSchema):
# note to StackOverflow: this was some additional schema repair work I
# needed to do, again adding logic conditional on the HTTP verb.
# Not related to the original question posted here, but hopefully relevant
# all the same.
def get_serializer_fields(self, path, method):
fields = super(MyViewSchema, self).get_serializer_fields(path, method)
# The 'name' parameter is set in MyModelListItemSerializer as not being required.
# However, when creating an access-code-pool, it must be required -- and in DRF v3.7, there's
# no clean way of encoding this conditional logic, short of what you see here:
#
# We override the AutoSchema inspection class, so we can intercept the CoreAPI Fields it generated,
# on their way out but before they make their way into the auto-generated api docs.
#
# CoreAPI Fields are named tuples, hence the poor man's copy constructor below.
if path == u'/v1/domains/{domain_name}/access-code-pools' and method == 'POST':
# find the index of our 'name' field in our fields list
i = next((i for i, f in enumerate(fields) if (lambda f: f.name == 'name')(f)), -1)
if i >= 0:
name_field = fields[i]
fields[i] = Field(name=name_field.name, location=name_field.location,
schema=name_field.schema, description=name_field.description,
type=name_field.type, example=name_field.example,
required=True) # all this inspection, just to set this here boolean.
return fields
class MyNameFilter(FilterSet):
name = CharFilter(field_name='name', help_text='Filter returned access code pools by name')
class Meta:
model = MyModel
fields = ('name',)
class MyAPIView(...)
schema = MyViewSchema()
filter_backends = (DjangoFilterBackend,)
filter_class = MyNameFilter

Remove an HTTP Request PATH Variable if null

I have this script in JMeter, PUT method,
how can i remove a path variable if the value from the input data is blank?
I know i can remove this if the parameter were in the Parameter tab of the HTTP request using the remove Arguments, the thing is I have a value in the body data so I have to put the URL parameters in the path that's why remove arguments is not working anymore.
code:
if ("${thisfromCSV}" == "") {
sampler.getArguments().removeArgument("thisParameter");
}
this works if the parameter is located at the Parameters tab of the HTTP Request
Add JSR223 PreProcessor as a child of the HTTP Request you would like to modify
Put the following code into "Script" area:
def url = new URL("http://example.com" + sampler.getPath())
def params = url.query.split('&').collectEntries({ param ->
param.split('=').collect {
URLDecoder.decode(it, 'UTF-8')
}
})
if (vars.get('thisfromCSV') == '') {
params.remove('thisParameter')
}
def query = params.collect { k, v -> "$k=$v" }.join('&')
sampler.setPath(url.path + '?' + query)
That's it, the code will remove thisParameter from the Sampler's URL query string in case of thisfromCSV variable is empty.
See Apache Groovy - Why and How You Should Use It for more information on using Groovy scripting in JMeter tests.

making request to API from within scrapy function

I'm working with scrapy. I want to rotate proxies on a per request basis and get a proxy from an api I have that returns a single proxy. My plan is to make a request to the api, get a proxy, then use it to set the proxy based on :
http://stackoverflow.com/questions/4710483/scrapy-and-proxies
where I would assign it using:
request.meta['proxy'] = 'your.proxy.address';
I have the following:
class ContactSpider(Spider):
name = "contact"
def parse(self, response):
for i in range(1,3,1):
PR= Request('htp//myproxyapi.com', headers= self.headers)
newrequest= Request('htp//sitetoscrape.com', headers= self.headers)
newrequest.meta['proxy'] = PR
but I'm not sure how to use The Scrapy Request object to perform the api call. I'm Not getting a response to the PR request while debugging. Do I need to do this in a separate function and use a yield statement or is my approach wrong?
Do I need to do this in a separate function and use a yield statement or is my approach wrong?
Yes. Scrapy uses a callback model. You would need to:
Yield the PR objects back to the scrapy engine.
Parse the response of PR, and in its callback, yield newrequest.
A quick example:
def parse(self, response):
for i in range(1,3,1):
PR = Request(
'http://myproxyapi.com',
headers=self.headers,
meta={'newrequest': Request('htp//sitetoscrape.com', headers=self.headers),},
callback=self.parse_PR
)
yield PR
def parse_PR(self, response):
newrequest = response.meta['newrequest']
proxy_data = get_data_from_response(PR)
newrequest.meta['proxy'] = proxy_data
yield newrequest
See also: http://doc.scrapy.org/en/latest/topics/request-response.html#topics-request-response-ref-request-callback-arguments

How to check xml response for dynamic array elements

I am new to groovy and soapui pro. I have below sample response that displays 2 or more array elements with dynamic data. I am wondering how to write a script assertion or xpath match to check if script passes as long as one of the elements has value 1.
<ns1:SampleTests>
<ns1:SampleTest1>
<ns1:Test>1</ns1:Test>
</ns1:SampleTest1>
<ns1:SampleTest2>
<ns1:Test>2</ns1:Test>
</ns1:SampleTest2>
</ns1:SampleTests>
I have written this in script assertion but its failing.
Supposing that you've a response like:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<ns1:SampleTests xmlns:ns1="hola">
<ns1:SampleTest1>
<ns1:Test>1</ns1:Test>
</ns1:SampleTest1>
<ns1:SampleTest2>
<ns1:Test>2</ns1:Test>
</ns1:SampleTest2>
</ns1:SampleTests>
</Body>
</Envelope>
You can perform the follow XPath: exists(//*:Test[.=1]) to check that exists at least one <ns1:Test> element with 1 as value.
Inside an XPath Match it looks like:
If instead you prefer to use an Script assertion you can use the XmlSlurper to parse your Xml, then get all <ns1:Test> values an assert that at least one has 1 as value. Look into the follow code:
// get the response
def responseStr = messageExchange.getResponseContent()
// parse the response as slurper
def response = new XmlSlurper().parseText(responseStr)
// get all <ns1:Test> values
def results = response.'**'.findAll { it.name() == 'Test' }
// now in results list we've NodeChild class instances we will convert it to
// string in order to perform the assert
results = results.collect { it.toString() }
// check that at least one element has '1' value
assert results.contains('1'),'RESPONSE NOT CONTAINS ANY <ns1:Test>1</ns1:Test>'

How can we set the value to the header dynamically in SOAPUi?

I'm new to SoapUI. I wanted to know how can we add 2 property value into one Header value.
For instance, I got some response like in XML format:
<Response xmlns="Http://SomeUrl">
<access_token>abc</access_token>
<scope>scope1</scope>
<token_type>Bearer</token_type>
</Response>
I want to send both access_token & token type to a single header value like:
"Authorization":"Bearer abc"
I am not getting how to do this using property transfer step.
Can anyone please help me?
You can use XPath concat function to concatenate the both values in one variable in your property transfer steps, in your case you can use the follow XPath:
concat(//*:token_type," ",//*:access_token)
concat function concatenates two or more strings, //*:token_type gets the Bearer value and //*:access_token gets the abc.
Hope this helps,
Add a script step after the step returning what you describe above.
def tokenType = context.expand('${STEP RETURNING STUFF#Response#//Response/token_type}');
def token = context.expand('${STEP RETURNING STUFF#Response#//Response/access_token}');
//add header to all steps
for (def stepEntry : testRunner.testCase.testSteps) {
if (!(stepEntry.value instanceof com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep)) {
continue;
}
def headers = stepEntry.value.httpRequest.requestHeaders;
headers.remove("Authorization");
headers.put("Authorization", token_type + " " + token);
stepEntry.value.httpRequest.requestHeaders = headers;
}
Here is another way without using additional property transfer step, but uses script assertion
Add a script assertion for the request test step.
Use below code into that script, modify element XPath are required
def element1Xpath = '//*:token_type'
def element2Xpath = '//*:access_token'
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
def response = groovyUtils.getXmlHolder(messageExchange.responseContentAsXml)
def field1 = response.getNodeValue(element1Xpath)
def field2 = response.getNodeValue(element2Xpath)
if (!field1) { throw new Error ("${element1Xpath} is either empty or null") }
if (!field1) { throw new Error ("${element2Xpath} is either empty or null") }
context.testCase.setPropertyValue('TEMP_PROPERTY', "${field1} ${field2}")
Now the expected value(merged) is available in a property 'TEMP_PROPERTY'. You may rename the property name as you wish in the last line of the code.
You may the new wherever it is needed within the test case.

Resources