Groovy : Class.forName().newInstance() error - oop

I have this following method in which I return a List<ImField> object using the List<GPathResult> filteredList. I perform filteredList.each closure where I generate
class at runtime and assign it a static type of ImField.
static List<ImField> getFields(GPathResult root,String fieldClass, String fieldType){
List<GPathResult> filteredList = root.children().findAll{
XMLSlurperUtil.name(it as GPathResult) == fieldType
} as List<GPathResult>
List<ImField> fields = []
filteredList.each{GPathResult it, int index ->
fields.add(Class.forName(fieldClass).newInstance() as ImField)
fields[index].set(it)
}
fields
}
The function call would look like so :
ImStageUtil.getFields(root, ImFieldFactory.SOURCE_FIELD, ImParserConstants.SOURCE_FIELD)
where ImFieldFactory.SOURCE_FIELD = "com.dto.fields.SourceField"
and ImParserContants.SOURCE_FIELD = "SOURCEFIELD"
the error occurs at the .each closure line:
No signature of method: com.extractor.ImStageUtil$_getFields_closure11.doCall() is applicable for argument types: (groovy.util.slurpersupport.NodeChild) values: []
Possible solutions: doCall(groovy.util.slurpersupport.GPathResult, int), findAll(), findAll(), isCase(java.lang.Object), isCase(java.lang.Object)
groovy.lang.MissingMethodException: No signature of method: com.extractor.ImStageUtil$_getFields_closure11.doCall() is applicable for argument types: (groovy.util.slurpersupport.NodeChild) values: []
Possible solutions: doCall(groovy.util.slurpersupport.GPathResult, int), findAll(), findAll(), isCase(java.lang.Object), isCase(java.lang.Object)

I've tried to create a similar script to your example, there are two things I had to modify (if your filteredList is not empty, which you need to check first):
1- You need to use collect() after the findAll{} closure, this allows you to collect all entries and add them to your filteredList.
2- You're using .each{} and you're providing a List along with the index, this should be replaced by .eachWithIndex{} because the first one doesn't expect an index.
Here is a simplified version of your code:
import groovy.util.slurpersupport.GPathResult
def text = '''
<list>
<technology>
<name>Groovy</name>
</technology>
</list>
'''
def list = new XmlSlurper().parseText(text)
def List getFields(GPathResult root,String fieldClass, String fieldType){
List<GPathResult> filteredList = root.children().findAll{
//println(it)
it != null
}.collect() as List<GPathResult>
println('list: ' + filteredList.getClass() + ', ' + filteredList.size())
filteredList.eachWithIndex{GPathResult it, int index ->
println('it: ' + it)
}
}
getFields(list, '', '')
This last example doesn't raise any exception for me.
Hope this helps.

Related

How to check the type of a document element (sub document vs list)? "ReqlServerCompileError: Variable name not found in: var_1"

I extend the RethinkDb API by providing some extra functions.
For example I simplify the expression
site_ids = r.table('periods')\
['regions']\
.concat_map(lambda row: row['sites'])\
['id']
to
site_ids = f['periods']\
.unwind('regions.sites.id')
using a custom unwind method that is able to resolve a path of nested document elements. If an item in the given path is a list, its entries are concatenated with concat_map. Otherwise the item is accessed with bracket notation:
def unwind(self, path):
items = path.split('.')
cursor = self._cursor
for item in items:
is_list = isinstance(cursor[item].run().next(), list)
if is_list:
cursor = cursor.concat_map(lambda row: row[item])
else:
cursor = cursor[item]
return self.wrap(self._f, cursor)
=> How can I improve the type check to find out if an element is a list? The check should not require an extra .run() and it should work in main queries as well as in sub queries.
My current implementation with the expression
is_list = isinstance(cursor[item].run().next(), list)
works fine in "main queries" like
result = f['periods'] \
.unwind('regions.sites.plants.product.process.technologies')\
.populate_with('periods', 'technologies')\
.sum('specific_cost_per_year') \
.run()
It does not work in sub queries, e.g. inside a mapping function:
def period_mapper(period):
return {
'year': period['start'],
'site_ids': f.wrap(period).unwind('regions.sites.id')
}
f.table('periods')\
.map(period_mapper)\
.run()
I get the error
rethinkdb.errors.ReqlServerCompileError: Variable name not found in:
var_1['regions']
^^^^^
because I am not able to .run() a query on the passed variable argument "period".
I tried to replace the if-then-else condition with r.branch but that did not help.
=> How can I choose an operator based on the type of the current cursor content in a better way?
Code of my selection class that wraps a RethinkDb cursor:
from rethinkdb.ast import RqlQuery
# needs to inherit from RqlQuery for the json serialization to work
class AbstractSelection(RqlQuery):
def __init__(self, f, cursor):
self._f = f
self._cursor = cursor
def __getitem__(self, identifier):
cursor = self._cursor[identifier]
return self.wrap(self._f, cursor)
def __repr__(self):
return self._cursor.__repr__()
def __str__(self):
return self._cursor.__str__()
def build(self):
return self._cursor.build()
#property
def _args(self): # required for json serialization
return self._cursor._args
#property
def optargs(self): # required for json serialization
return self._cursor.optargs
def wrap(self, r, cursor):
raise NotImplemented('Needs to be implemented by inheriting class')
def unwind(self, path):
items = path.split('.')
cursor = self._cursor
for item in items:
is_list = isinstance(cursor[item].run().next(), list)
if is_list:
cursor = cursor.concat_map(lambda row: row[item])
else:
cursor = cursor[item]
return self.wrap(self._f, cursor)
def pick(self, path, query):
return self.unwind(path).get(query)
def populate(self, collection_name, path):
return self.map(lambda identifier:
self._f[collection_name]
.pick(path, {'id': identifier})
)
def get(self, query):
cursor = self._cursor.filter(query)[0]
return self.wrap(self._f, cursor)
def to_array(self):
return [item for item in self._cursor]
I managed to use type_of in combination with branch. Accessing the item with bracket notation returns a STREAM and I had to get the first item with [0] before using type_of to check for the 'ARRAY' type. This also works if the property is not an array:
def unwind(self, path):
items = path.split('.')
cursor = self._cursor
r = self._f._r
for item in items:
cursor = r.branch(
cursor[item][0].type_of() == 'ARRAY',
cursor.concat_map(lambda row: row[item]),
cursor[item]
)
return self.wrap(self._f, cursor)

Expression with String placeholder in the filter part of JsonPath using karate

I am trying to filter my response using JSON Path where one of the condition using a value from a variable but I am not able to map variable properly, so my filter not working properly.
Sample response JSON:
{
"response":[
{
"id":"1234",
"confirmationCode":"abcd"
}
]
}
I am using the below script where I am using variable 'code':
* def Code = 'abcd'
* def value = karate.jsonPath($.response[?(#.confirmationCode == ' + Code +')])
Read the docs carefully please:
* def value = karate.jsonPath(response, "$.response[?(#.confirmationCode=='" + Code + "')]")

Unable to Pass Two Parameters as Argument to Javascript Function

I am trying to use karate.call to invoke function of a JS file receiving two arguments (String, Array of String). However the array of string would not be passed on to the JS file.
The JS file contains:
function(query, fragments) {
// Here lies some code
// One of them includes fragments.length;
}
And I call the JS function on another JS file in this way:
//var query = 'Some string';
//var fragments = ['fragment1', 'fragment2'];
var clean = karate.call('../helper/helper.js', [query, fragments]);
I am able to pass query which is a string. But I was unable to pass the array of string. The error says:
TypeError: Cannot read property "length" from undefined
It seems the array of string did not get passed to the JS function. Any help will be greatly appreciated. Thanks!
You can read you function first and invoke is just like any other js function
var myFun = karate.read('../helper/helper.js');
var funCall = myFun(query, fragments);
or
var myCall = karate.read('../helper/helper.js')(query, fragments);
this should work.
.call takes parameters as comma separated values , you need to use .apply if you want to pass values as an array.
var clean = karate.call('../helper/helper.js', query, fragments);
will work...
The answers here are missing an important clarification:
I often do single arg functions like:
* def concatParams =
"""
function(s) {
return "urldt=" + todaysDate + "&caseid=" + s.caseid
}
"""
And I will call that like so:
* def params = call concatParams {caseid: '3433344'}
But, when I want to do 2 params, I will define a function like so:
* def concatParams =
"""
function(d,s) {
return "urldt=" + d.date + "&caseid=" + s.caseid
}
"""
And unintuitively, neither of these will work:
* def params = call concatParams {date: '01/01/2020', caseid: '3433344'}
* def params = call concatParams '01/01/2020' '3433344'
To get it to work, instead I call it like this:
* def params = concatParams('01/01/2020', '3433344')
Documentation does not clarify this.
var clean = karate.call('../helper/helper.js', query, fragments);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
call method accepts comma separated params and apply accept array so you need to replace call into apply. your code looks like
function karate(query, fragments) {
// Here lies some code
// One of them includes fragments.length;
}
var clean = karate.apply('../helper/helper.js', [query, fragments]);

how can we pass multiple arguments in the background functions in karate feature file

i am passing the two arguments to my custom function but in background while i am passing the arguments it's skipping first taking second one only arugment.
here is the sample code
* def LoadToTigerGraph =
"""
function(args1,args2) {
var CustomFunctions = Java.type('com.optum.graphplatform.util.CareGiverTest');
var cf = new CustomFunctions();
return cf.testSuiteTrigger(args1,args2);
}"""
#*eval if (karate.testType == "component") karate.call(LoadToTigerGraph '/EndTestSample.json')
* def result = call LoadToTigerGraph "functional","/EndTestSample.json"
output :
test type is ************/EndTestSample.json
path is *************undefined
When you want to pass two arguments, you need to send them as two json key/value.
* def result = call LoadToTigerGraph { var1: "functionnal", var2: "/EndTestSample.json" }
And you just have to use args.var1 and args.var2 in your function function(args)

Use of a string to get information in an associative array

I've got a function that takes two strings as arguments, and I want to use these arguments to get the data held in associative arrays.
var myVar:Object = {};
myVar.value = 10;
function getStuff(v:String, vl:String){
//...
}
In this case, v = "myVar" and vl = "value".
How do I translate vinto the variable name 'myVar' and v1 into 'value' so that I can access the data?
Sorry if this won't work, for I can only test AS3 here, but please try this:
function getStuff(v:String, vl:String){
return eval(v + "." + vl);
}
eventually
function getStuff(v:String, vl:String){
return eval("_gobal." + v + "." + vl);
}