I'm trying to create a simple groovy script that will download some JSON from a website, pretty print it, and then save it to a file. I'm using this as an exercise to learn how to practice some functional concepts and I'm running into an issue I can't figure out. Basically, the last composed closure has multiple params and I can't figure out how to apply it. I'd like the end result to be a one liner that specifies the URL and file location to save the contents.
def getJson = { sourceUrl ->
def conn = new URL(sourceUrl).openConnection() as HttpURLConnection
return conn.inputStream.text
}
def prettyPrintJson = { json ->
return groovy.json.JsonOutput.prettyPrint(json)
}
def save = { data, fileLocation ->
new File(fileLocation).write(data)
}
def sync = getJson >> prettyPrintJson >> save
sync('https://jsonplaceholder.typicode.com/posts') // don't know how to specify the fileLocation
If you compose functions, your innermost function determines the
arguments. So you could provide the second argument to save via
rcurry. E.g.:
def sync = getJson >> prettyPrintJson >> save.rcurry("/tmp/out.json")
sync('https://jsonplaceholder.typicode.com/posts')
Or you would have to thread the second argument down the line until you
need it. E.g.:
def getJson = { sourceUrl, out ->
[sourceUrl.toURL().text, out]
}
def prettyPrintJson = { json, out ->
[groovy.json.JsonOutput.prettyPrint(json), out]
}
def save = { data, fileLocation ->
new File(fileLocation).write(data)
}
def sync = getJson >> prettyPrintJson >> save
sync('https://jsonplaceholder.typicode.com/posts', '/tmp/out.json')
I'd imagine, that by using a macro (since 2.5) there should be a chance to
create a threading macro (e.g. like in Clojure).
Related
I'm copying Soap Request of a project into a testCase with a load script (project level), it all went good, unless I discover that all attachment files were not copied.
Here's my script :
testsuite=project.addNewTestSuite("Suite")
testcase=testsuite.addNewTestCase("Case")
def iface= project.interfaces["Interface"]
def op = iface.operations["Operation"]
op.getRequestList().each { req ->
def config=com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory.createConfig(req, req.getName())
def newTestStep = testcase.addTestStep( config );
}
Is there a way to copy every request's attachment into it's testStep copy?
If I need to add manually my test suite, and use a setup script (to use context, runner ...) I'm ready to do that.
Thanks
You've to get all attachments from the request in your operation list and import each one in the new TestStep, something like this must works for your case:
req.attachments.each{ attach ->
newTestStep.testRequest.importAttachment(attach)
}
All together in your code:
def project = testRunner.testCase.testSuite.project
def testsuite= project.addNewTestSuite("Suite")
def testcase= testsuite.addNewTestCase("Case")
def iface= project.interfaces["Interface"]
def op = iface.operations["Operation"]
op.getRequestList().each { req ->
def config=com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory.createConfig(req, req.getName())
def newTestStep = testcase.addTestStep( config )
req.attachments.each{ attach ->
newTestStep.testRequest.importAttachment(attach)
}
}
I'm a Groovy noob and trying to get my head around using reusable functions to extract an xml node value for a given test step and node in SoapUI. It seems the class runs fine but the problem is when using the method. I get the following error:
groovy.lang.MissingMethodException: No signature of method: org.apache.log4j.Logger.info() is applicable for argument types: (java.lang.String, java.lang.String) values: [return TestStepName, Node] Possible solutions: info(java.lang.Object), info(java.lang.Object, java.lang.Throwable), any(), wait(), dump(), find(groovy.lang.Closure) error at line:
This is my class:
class Example
{
def log
def context
def responseSOAXmlStep
def resultValue
def responseNodePath
def storeProperty
// Class constructor with same case as Class name
def Example(logIn,contextIn,testRunnerIn)
{
this.log = logIn
this.context = contextIn
this.responseSOAXmlStep = responseSOAXmlStep
this.responseNodePath = responseNodePath
}
def execute(responseSOAXmlStep,responseNodePath)
{
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
// do some stuff to prove I've run with right context, etc.
log.info "return "+responseSOAXmlStep,responseNodePath
def holder = groovyUtils.getXmlHolder( responseSOAXmlStep+"#ResponseAsXml" );
resultValue = holder.getNodeValue( "//ns1:"+responseNodePath );
log.info("Node value: " + resultValue );
testRunner.testCase.testSuite.setPropertyValue(storeProperty, resultValue);
return execute
}
}
context.setProperty( "example", new Example( log, context, testRunner) )
log.info "Library Context:"+context
This is where I do the call in a step after the response step:
// get a reference to the library TestSuite
library = testRunner.testCase.testSuite.project.testSuites["Library"]
// find the module within the library
module = library.testCases["Library"].testSteps["Second Example_Class"]
// initialise the library; which places an instance of Example in the context
module.run(testRunner, context)
// get the instance of example from the context.
def example = context.example
// run the method, with parameter
log.info "example.execute(responseSOAXmlStep,responseNodePath) = " + example.execute("TestStepName","Node")
I've searched the forum but could not find an answer that suites my query. Any form of assistance is appreciated. Thanks.
Your error description says that you're invoking info() method on log passing two strings as and arguments, and this method doesn't exist.
The problem is easy to solve, pass one concatenated string with + as parameter instead of passing two strings. In your execute method inside your Example class use this:
def execute(responseSOAXmlStep,responseNodePath)
{
...
// USE + TO CONCATENATE STRINGS INSTEAD OF USE ,
log.info "return " + responseSOAXmlStep + responseNodePath
...
}
instead of:
def execute(responseSOAXmlStep,responseNodePath)
{
...
// do some stuff to prove I've run with right context, etc.
log.info "return " + responseSOAXmlStep,responseNodePath
...
}
EDIT:
As you said in your comment probably you've another problem storing properties in TestSuite level. You're using this code:
testRunner.testCase.testSuite.setPropertyValue(storeProperty, resultValue);
The problem is that setPropertyValue is expecting string as a first argument however at this line in your code storeProperty is not defined yet. Try for example defining storeProperty as:
def storeProperty = "myProperty" before using it in setPropertyValue call.
Hope this helps,
I use to import data from excel ,but i use the bootstrap.groovy to write the code and my import script method is called when the application starts.
Here the scenarios is i m having 8000 related data once to import if they are not on my database.And,also when i deploy it to tomcat6 it is blocking other apps from deployment ,until it finish the import.So,i want to use separate thread for to run the script in anyway without affecting performance AND BLOCKING OTHER FROM DEPLOYMENT.
code excerpt ...
class BootStrap {
def grailsApplication
def sessionFactory
def excelService
def importStateLgaArea(){
String fileName = grailsApplication.mainContext.servletContext.getRealPath('filename.xlsx')
ExcelImporter importer = new ExcelImporter(fileName)
def listState = importer.getStateLgaTerritoryList() //get the map,form excel
log.info "List form excel:${listState}"
def checkPreviousImport = Area.findByName('Osusu')
if(!checkPreviousImport) {
int i = 0
int j = 0 // up
date cases
def beforeTime = System.currentTimeMillis()
for(row in listState){
def state = State.findByName(row['state'])
if(!state) {
// log.info "Saving State:${row['state']}"
row['state'] = row['state'].toString().toLowerCase().capitalize()
// log.info "after capitalized" + row['state']
state = new State(name:row['state'])
if(!state.save(flash:true)){
log.info "${state.errors}"
break;
}
}
}
}
For import of large data I suggest to take in consideration the use of Spring Batch. Is easy to integrate it in grails. You can try with this plugin or integrate it manually.
I am attempting to write my own response handlers for Groovy's RESTClient (which wraps around HttpBuilder). I want to always print the response body if one is returned. However, I cannot find a consistent way to do it.
Typically a custom response handler would look like this:
def client = new RESTClient(url)
client.handler.success = { resp, reader ->
//do stuff
}
client.handler.failure = { resp, reader ->
//do stuff
throw new Exception("HTTP call failed. Status code: ${resp.getStatus()}")
}
However, what I noticed is that the variable "reader" can have a different class depending on the response. I've seen the reader be of type groovy.util.slurpersupport.NodeChild or org.apache.http.conn.EofSensorInputStream. I want it to be a predictable class so I can actually call the methods on this object. What's going on here?
Setting the content type to ANY and changing the HttpBuilder content parsers to the text parser fixed the issue. The type of reader in the response handler is now always java.io.InputStreamReader.
Before:
def headerMap = [:]
//populate headers
def response = client.get("headers":headerMap)
After:
client.parser.'application/xml' = client.parser.'text/plain'
client.parser.'application/xhtml+xml' = client.parser.'text/plain'
client.parser.'application/atom+xml' = client.parser.'text/plain'
client.parser.'application/json' = client.parser.'text/plain'
client.parser.'text/html' = client.parser.'text/plain'
client.parser.'application/x-www-form-urlencoded' = client.parser.'text/plain'
def headerMap = [:]
//populate headers
def response = client.get("headers":headerMap, contentType:groovyx.net.http.ContentType.ANY)
I have prepared a test case in SoapUI Open Source which loops over values in csv file and sends request for each set of values (taken care of by groovy script). I want to modify it so each thread for each new iteration uses value from next row of csv file.
import com.eviware.soapui.impl.wsdl.teststeps.*
def testDataSet = []
def fileName = "C:\\sSHhrTqA5OH55qy.csv"
new File(fileName).eachLine { line -> testDataSet.add( line.split(",") ) }
def myProps = new java.util.Properties();
myProps = testRunner.testCase.getTestStepByName("Properties");
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
def testCase = testRunner.testCase;
def testStep = testCase.getTestStepByName("TestRequest");
testRunner = new com.eviware.soapui.impl.wsdl.testcase.WsdlTestCaseRunner(testCase, null);
testStepContext = new com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext(testStep);
while (true) {
for ( i in testDataSet ) {
myProps.setPropertyValue("parameter0",i[0]);
myProps.setPropertyValue("username",i[1]);
myProps.setPropertyValue("parameter1",i[2]);
myProps.setPropertyValue("password",i[3]);
testStep.getTestRequest().setUsername(myProps.getPropertyValue("username"))
testStep.getTestRequest().setPassword(myProps.getPropertyValue("password"))
testStep.run(testRunner, testStepContext);
}
}
I want to modify this script so each thread from the pool gets unique (next) unused value from data source
I tried to use newFixedThreadPool from java.util.concurrent as suggested here (Concurrency with Groovy), however I can't get it to work - either requests are duplicated or SoapUI crashes (I am new to concurrency).
Can you please help me to get it right?
I think this would work for you:
while (true) {
for ( i in testDataSet ) {
def th = Thread.start(){
myProps.setPropertyValue("parameter0",i[0]);
myProps.setPropertyValue("username",i[1]);
myProps.setPropertyValue("parameter1",i[2]);
myProps.setPropertyValue("password",i[3]);
testStep.getTestRequest().setUsername(myProps.getPropertyValue("username"))
testStep.getTestRequest().setPassword(myProps.getPropertyValue("password"))
testStep.run(testRunner, testStepContext);
}
th.join()
}
So, new threads would be created on each loop.
If you wanted to test out if its working you could place loginfo(s) in the code...
log.info("Thread Id: " + Thread.currentThread().getId() as String)
I don't see your point. SoapUi already gives you a datasource test step that accepts a csv file as input.
So once you have all these values you can transfer the properties and run the test.