Groovy and XMLUnit: compare webservice results - web-services

Using Groovy and XMLUnit I am trying to write a script to compare the xml output of web services from multiple endpoints. Attempting to get it working from one endpoint then iterate over endpoints to compare output, however, I continue to get the following error:
Caught: groovy.lang.GroovyRuntimeException:
Could not find matching constructor for:
org.custommonkey.xmlunit.Diff(groovy.util.Node, groovy.util.Node)
groovy.lang.GroovyRuntimeException:
Could not find matching constructor for:
org.custommonkey.xmlunit.Diff(groovy.util.Node, groovy.util.Node)
I am pretty sure it has to do with my inexperience with both XmlParser/XmlSlurper and XMLUnit (a.k.a. newbie). I greatly appreciate any pointers in the right direction. Here is sample code that causes the exception:
#Grab(group='xmlunit', module='xmlunit', version='1.5')
import org.custommonkey.xmlunit.*
def url = "http://www.webservicex.net//geoipservice.asmx/GetGeoIP?IPAddress=173.201.44.188"
def xmlParserResults = new XmlParser().parse("$url")
//same thing happens if I use...
//def xmlSlurperResults = new XmlSlurper().parse("$url")
def xmlDiff = new Diff(xmlParserResults, xmlParserResults)
assert xmlDiff.identical()
Thank you in advance!

The url returns xml and Diff takes two Strings to compare (in your case you are comparing Nodes). So the easiest way to compare would be to use URL instead of trying it to parse using XmlParser or XmlSlurper.
def url =
"http://www.webservicex.net//geoipservice.asmx/GetGeoIP?IPAddress=173.201.44.188"
def xmlString = new URL(url).text
def xmlDiff = new Diff(xmlString, xmlString)
assert xmlDiff.identical()
In case the above is just a sample and not a working example of hitting multiple endpoints then the point is to represent the xml output as string and then compare.

Related

fuzzy search in django postgresql without using Elasticsearch

I try to incorporate fuzzy serach function in a django project without using Elasticsearch.
1- I am using postgres, so I first tried levenshtein, but it did not work for my purpose.
class Levenshtein(Func):
template = "%(function)s(%(expressions)s, '%(search_term)s')"
function = "levenshtein"
def __init__(self, expression, search_term, **extras):
super(Levenshtein, self).__init__(
expression,
search_term=search_term,
**extras
)
items = Product.objects.annotate(lev_dist=Levenshtein(F('sort_name'), searchterm)).filter(
lev_dist__lte=2
)
Search "glyoxl" did not pick up "4-Methylphenylglyoxal hydrate", because levenshtein considered "Methylphenylglyoxal" as a word and compared with my searchterm "glyoxl".
2. trigram_similar gave weird results and was slow
items = Product.objects.filter(sort_name__trigram_similar=searchterm)
"phnylglyoxal" did not pick up "4-Methylphenylglyoxal hydrate", but
picked up some other similar terms: "4-Hydroxyphenylglyoxal hydrate",
"2,4,6-Trimethylphenylglyoxal hydrate"
"glyoxl" did not pick up any of the above terms
3. python package, fuzzywuzzy seems can solve my problem, but I was not able to incorporate it into query function.
ratio= fuzz.partial_ratio('glyoxl', '4-Methylphenylglyoxal hydrate')
# ratio = 83
I tried to use fuzz.partial_ratio function in annotate, but it did not work.
items = Product.objects.annotate(ratio=fuzz.partial_ratio(searchterm, 'full_name')).filter(
ratio__gte=75
)
Here is the error message:
QuerySet.annotate() received non-expression(s): 12.
According to this stackoverflow post (1), annotate does not take regular python functions. The post also mentioned that from Django 2.1, one can subclass Func to generate a custom function. But it seems that Func can only take database functions such as levenshtein.
Any way to solve these problems? thanks!

Parse soap response and concatenate with another string

I have been using soapui opensource for a small period and not yet good at groovy script. Please help figuring out the following issue:
I get response from the previous test step. Lets say Response1 and need to parse it in order to get Id value from it. Then I need to add string DomainId before this id so that it looked smth like this:
DomainId_234565
and tranfer it to next request.
Could someone please explain how to do it with groovy? (I guess it is the best way to do it)
Thank you
Managed to resolve myself. Add property step response where I store response from previous step and also added property trasfer step to put response to the property. Then I add groovy script: def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context ) def holder = groovyUtils.getXmlHolder("Properties#response") return "DomainId_ " + holder.getNodeValue("//*:Id") and it works, returns the correct value

How to pass regular expression in a list in Groovy?

I have tried these things but it is not working for me:
def fileNames = [/ReadyAPI-\d.\d.\d.vmoptions/, 'loadtestrunner.bat', 'ready-api.bat', 'securitytestrunner.bat']
def fileNames = ['ReadyAPI-\\d.\\d.\\d.vmoptions', 'loadtestrunner.bat', 'ready-api.bat', 'securitytestrunner.bat']
What I am trying to do is that there will be one of the two files that will be present in the system which are:
'ReadyAPI-1.2.2.vmoptions' OR 'ReadyAPI-1.3.1.vmoptions'.
I am relatively new to Groovy so may be I am not seeing common problem. So, please bear with me. Thanks
You will need to get the filename first, then put it into the list. You can use Groovy's FileNameByRegexFinder from groovy.utils like this to find that file with a regex:
def optionsFile = new FileNameByRegexFinder()
.getFileNames('.', /ReadyAPI-\d.\d.\d.vmoptions/).first()
def fileNames = [optionsFile, 'loadtestrunner.bat', ...]
Note that the first parameter to getFileNames() is the base directory to search in, you will likely need to change that for your scenario.

TypeError : 'NoneType' object not callable Python with BeautifulSoup XML

I have the following XML file :
<user-login-permission>true</user-login-permission>
<total-matched-record-number>15000</total-matched-record-number>
<total-returned-record-number>15000</total-returned-record-number>
<active-user-records>
<active-user-record>
<active-user-name>username</active-user-name>
<authentication-realm>realm</authentication-realm>
<user-roles>Role</user-roles>
<user-sign-in-time>date</user-sign-in-time>
<events>0</events>
<agent-type>text</agent-type>
<login-node>node</login-node>
</active-user-record>
There are many records
I'm trying to get values from tags and save them in a different text file using the following code :
soup = BeautifulSoup(open("path/to/xmlfile"), features="xml")
with open('path/to/outputfile', 'a') as f:
for i in range(len(soup.findall('active-user-name'))):
f.write ('%s\t%s\t%s\t%s\n' % (soup.findall('active-user-name')[i].text, soup.findall('authentication-realm')[i].text, soup.findall('user-roles')[i].text, soup.findall('login-node')[i].text))
I get the error TypeError : 'NoneType' object not callable Python with BeautifulSoup XML for line : for i in range(len(soup.findall('active-user-name'))):
Any idea what could be causing this?
Thanks!
There are a number of issues that need to be addressed with this, the first is that the XML file you provided is not valid XML - a root element is required.
Try something like this as the XML:
<root>
<user-login-permission>true</user-login-permission>
<total-matched-record-number>15000</total-matched-record-number>
<total-returned-record-number>15000</total-returned-record-number>
<active-user-records>
<active-user-record>
<active-user-name>username</active-user-name>
<authentication-realm>realm</authentication-realm>
<user-roles>Role</user-roles>
<user-sign-in-time>date</user-sign-in-time>
<events>0</events>
<agent-type>text</agent-type>
<login-node>node</login-node>
</active-user-record>
</active-user-records>
</root>
Now onto the python. First off there is not a findall method, it's either findAll or find_all. findAll and find_all are equivalent, as documented here
Next up I would suggest altering your code so you aren't making use of the find_all method quite so often - using find instead will improve the efficiency, especially for large XML files. Additionally the code below is easier to read and debug:
from bs4 import BeautifulSoup
xml_file = open('./path_to_file.xml', 'r')
soup = BeautifulSoup(xml_file, "xml")
with open('./path_to_output_f.txt', 'a') as f:
for s in soup.findAll('active-user-record'):
username = s.find('active-user-name').text
auth = s.find('authentication-realm').text
role = s.find('user-roles').text
node = s.find('login-node').text
f.write("{}\t{}\t{}\t{}\n".format(username, auth, role, node))
Hope this helps. Let me know if you require any further assistance!
The solution is simple: don't use findall method - use find_all.
Why? Because there is no findall method at all, there are findAll and find_all, which are equivalent. See docs for more information.
Though, I agree, error message is confusing.
Hope that helps.
The fix for my version of this problem is to coerce the BeautifulSoup instance into a type string. You do this following:
https://groups.google.com/forum/#!topic/comp.lang.python/ymrea29fMFI
you use the following pythonic:
From python manual
str( [object])
Return a string containing a nicely printable representation of an
object. For strings, this returns the string itself. The difference
with repr(object) is that str(object) does not always attempt to
return a string that is acceptable to eval(); its goal is to return a
printable string. If no argument is given, returns the empty string,

In Django, how do I deal with an "or" in the url regex once I get to the view?

I'm just learning Django, and am getting stuck with some url logic. I'm trying to allow either a category name or id in the url:
...
url(r'^(?P<booze_q>\w+|\d+)/$','glasses.views.booze'),
...
And then in thew view, only deal with that result once. However, if the url is a string - in this case, Whiskey, I get an error for trying to pass a string where an int is expected. This is the closest I've gotten so far:
def booze(request, booze_q):
booze = get_object_or_404(Booze,Q(pk=booze_q)|Q(name=booze_q))
return render_to_response('booze/detail.html', {'booze': booze})
But this returns an error: invalid literal for int() with base 10: 'Whiskey'
I'm sure it's a pretty easy thing, but this is my first Django app, so any help would be appreciated.
tl;dr: End result, I'd like mysite.com/1/ or mysite.com/Whiskey/ to both call the glasses.views.booze view, and get the object with id=1 or name=Whiskey
This is a common scenario you'll encounter quite often, which is typically handled by resorting to multiple arguments and having views behave differently based on which of the view arguments are then present or not.
What you do is first define a URL pattern that uniquely matches each specific case and then let Django's URL resolver set the arguments accordingly based on which of the patterns was matched.
Here's an example with a class based view, that performs two different queries based on which of the two keyword arguments, booze_id or booze_name, is set:
url(r'^(?P<booze_id>\d+)/$', BoozeDetailView.as_view()),
url(r'^(?P<booze_name>\w+)/$', BoozeDetailView.as_view()),
class BoozeDetailView(DetailView):
model = Booze
def get_object(self):
booze_id = self.kwargs.get('booze_id', None)
booze_name = self.kwargs.get('booze_name', None)
if booze_id:
return self.model.objects.get(id=booze_id)
else:
return self.model.objects.get(name=booze_name)
You will always get a string, even if the string contains a number.
1) You should not have a parameter that could be either an id or something else. One day you will enter an item whose name is a number and your app will fail.
2) When querying for pk with a string django automatically tries to convert it into an integer. You'll have to handle the non-pk case before constructing that query.