I would like to check response from GET/birds request with a json schema. In my feature:
* def abs = read (birds.json)
* match response == abs.birdsSchema
I need to put the schema in a json file and not in the feature.
I have to check additional values depending on gender. Ex: if gender is male then check if the color is blue and the tail is long or short. if gender is female then check if "sings" is true or false and number of eggs.
So I put in birds.json:
"birdsSchema":{
"id": "#string",
"owner": "#number",
"town": "#? _ == 'New York' || _ == 'Washington'",
"type": "object",
"oneOf": [
{
"properties": {
"gender": {"enum": ["male"]},
"color":"blue",
"tail": "#? _ == 'long' || _ == 'short'"
}
},
{
"properties": {
"gender": {"enum": ["female"]},
"sings" : "#? _ == true || _ == false"
"eggs": "##number"
}
}
]
}
But it doesn't work. Error: com.intuit.karate.exception.KarateException: path: $[0].type, actual: 'female', expected: 'object', reason: not equal.
How I can do this conditional check in my json file?
Let's acknowledge that this is extra hard because if I understand your question correctly, the JSON keys you are looking for are dynamic.
Part of the fun of Karate is that there are at least 5 different ways I can think of to solve this elegantly. Here is just one:
* def response = { color: 'black', aaa: 'foo' }
* def schema = { color: '#? _ == "black" || _ == "white"' }
* def extra = (response.color == 'black' ? { aaa: '#string' } : { fff: '#string' })
* match response contains schema
* match response contains extra
If you create a JS function based on the hint above, you can probably get a better solution. Keep in mind that in a JS function you can use methods like karate.set to dynamically create keys. So there are many possibilities :)
edit: looks like the example above is wrong, and the keys are not dynamic. Then it is easy, keep in mind that $ refers to the JSON root:
* def response = { color: 'black', extra: 'foo' }
* def schema = { color: '#? _ == "black" || _ == "white"', extra: '#($.color == "black" ? "foo" : "bar")' }
* match response == schema
Related
Want to validate in Karate framework
For the below Json What I want to validate is,
if "isfilter_regex":0 then "msgtype": "##regex ^[A-Za-z0-9_.]-/*"
or if "isfilter_regex":1 then "msgtype": "#string"
(when isfilter_regex = 1 then msgtype must be a regular expression)
In my case number of candidate s in candidates array is 180+
I tried lot of things I ended up failing can anybody help me here?
{
"candidates":[
{
"candidate":{
"name":"Alex",
"category":[
{
"category_name":"APCMRQ",
"filters":[
{
"isfilter_regex":0,
"msgtype":"APCMRQ"
}
]
},
{
"category_name":"BIDBRQ",
"filters":[
{
"isfilter_regex":1,
"msgtype":"'(AMSCNQ(_[A-Za-z0-9]{1,3}){0,3})'"
}
]
}
]
}
}
]
}
I tried below way which works only when idex specified, but what to do if I want to do this check for entire array?,
* def msgRegexValue = response.candidates[150].candidate.category[0].filters[0].isfilter_regex
* def actualFilter = response.candidates[150].candidate.category[0].filters[0]
* def expectedFilter = actualFilter
* def withRegex =
"""
{
"isfilter_regex": 0,
"msgtype": '##regex ^[A-Za-z0-9_.]*-*/*'
}
"""
* def withoutRegex =
"""
{
"isfilter_regex": 1,
"msgtype": '##string'
}
"""
* def expected = msgRegexValue == 0 ? withRegex: withoutRegex
* match actualFilter == expected
The query I run is going to return a response which I split into two schemas:
* def tagsSchema =
"""
{
"lifecycle-status": "#string",
"infrastructure-environment": "#string",
"managed-by": "#string",
"supported-by": "#string",
"operated-by": "#string"
}
"""
and this schema is integrated into the my content schema:
* def contentSchema =
"""
{
"phase": "##string",
"managedBy": "##string",
"assetId":"##string",
"isValid": ##boolean,
"name": "#string",
"supportedBy": "##string",
"links": '#[] linksSchema',
"ownedBy": "##string",
"cmdbInstanceId":"#string",
"tags": "##object? karate.match(_,tagsSchema).tags",
}
"""
The tagsSchema is optional which I have covered by the ##object. When I run the query now it fails as I do have additional values in tagsSchema.
getList.feature:159 - path: $[0].tags, actual: {"technicalreferant":"email1","billingowner":"xyz","responsibleManager":"email1","environment":"abc","application":"tbd","consumer":"cdr","cr":"12345678"}, expected: '##object? karate.match(_,tagsSchema).tags', reason: did not evaluate to 'true'
The issue is coming from the karate.match but there is no karate.contains. How do I have to modify the schema to avoid this error. The values in the tagsSchema are mandatory while the others can be created by the user at any time and we don't have a policy for them. I don't want to adjust the code every run-time and only rely on mandatory values.
I'm not sure why you see the need to use karate.match() and you need to read the documentation. Here's a simple example below:
* def innerSchema = { foo: '#string' }
* def outerSchema = { bar: '#string', baz: '##(innerSchema)' }
* def response1 = { bar: 'x' }
* match response1 == outerSchema
* def response2 = { bar: 'x', baz: { foo: 'y' } }
* match response2 == outerSchema
Edit: So, I found the solution to my initial question, which made me realize I have another issue.
It seemed to be easier than I thought
setNumbers(e) {
e.preventDefault();
var already_exists = false;
var ls_data = this.state.storedNumbers;
var rname = document.getElementById('name').value;
var rnumb = document.getElementById('nummer').value;
var ls_key = this.state.ls_key;
for (key in ls_data) {
if(ls_data.hasOwnProperty(key) === true) {
if(ls_data[key].name === rname) {
if(ls_data[key].Number === rnumb) {
already_exists = true;
}
}
}
}
if(!already_exists) {
ls_key++;
ls_data[ls_key] = {
name: rname,
Number: rnumb
};
localStorage.setItem("ls_numbers", JSON.stringify(this.state.storedNumbers));
localStorage.setItem("ls_key", ls_key);
this.setState({
ls_key: localStorage.getItem("ls_key"),
});
}
}
But now my issue is, that I can't iterate over it, because it is a nested object and not an array. So .map will not work (this.state.storedNumbers.map is not a function).
Changing storedNumber to an array sadly doesn't solve the issue, as
ls_data[ls_key] = {
name: rname,
Number: rnumb
};
isn't working in an array. So now my question is. Can I use my ls_key variable to create a name object in my array? Using the code above results in:
[
null,
{
"name" : "Person 1",
"Number" : "XXXXXXXX"
},
{
"name" : "Person 2",
"Number" : "XXXXXXXX"
}
]
while the array should look like:
[
"1": {
"name" : "Person 1",
"Number" : "XXXXXXXX"
},
"2": {
"name" : "Person 2",
"Number" : "XXXXXXXX"
}
]
Or is there a way to iterate over the nested JSON result, as .map does for an array?
Alright then, just figured it out myself:
The reason my data got malformed (initial question), still in the blue about that. I've changed a lot of code and reverted to the original code again, et voila, miraculously it all worked. Can't tell you what the difference was. After that I could easily simplify the code as shown in the edited question.
To iterate over the data, the code below was my solution. Should you have a more cleaner solution, let me know!
{this.state.storedNumbers.length < 1
? <li className='list-group-item'><strong>Geen recente nummers...</strong><span className='pull-right'><span className='glyphicon glyphicon-warning glyph-xl'></span></span></li>
: Object.keys(this.state.storedNumbers).map((number) => {
return (
<div className='item' key={number}>
<a className='list-group-item list-link'>
<strong>{this.state.storedNumbers[number].name}</strong> - ({this.state.storedNumbers[number].Number})
</a>
<span className='button-delete glyph-xl'>
<i className='glyphicon glyphicon-trash glyph-xl' aria-hidden='true'></i>
</span>
</div>
)})
}
UPDATE
As developer003 suggested, I did this:
It works, but not at all. If I add an other property as c.details.author (string[]) or c.details.index (number), it doesn't work, and the function doesn't return nothing (error).
Here an extract of my JSON database:
[
{
"index": 1,
"name": "ad dolor ipsum quis",
"details": {
"author": ["Wallace Stephens", "Steve Ballmer"],
"game": {
"short": "tdp",
"name": "Thief: The Dark Project"
},
"newdark": {
"required": true,
"version": "1.20"
},
"firstreleasedate": "2007/04/27",
"lastupdatedate": "2017/01/28"
}
}
]
So I can look for another details properties than strings. Any idea?
ORIGINAL POST
I created a function which, when I call it as a (keyup) event, filters an HTML datatable when I type something in an input.
return c.name.toLowerCase().indexOf(input.target.value.toLowerCase()) != -1;
I want to be able to filter, not only by name, but also by details.author, details.game.name, details.firstrelease... etc.
How can I change c.name to apply these properties? Do I need to create a loop? Should I use .map()?
Right now I can think in 2 approaches:
1st:
º Create a function to parse (toLowerCase()) the property value and handle if it contains the value or not:
containsVal(property, value) {
return property.toLowerCase().indexOf(value) !== -1;
}
filterFunc(c) {
return this.containsVal(c.name, VALUE_TO_SEARCH) ||
c.details && (
this.containsVal(c.details.author, VALUE_TO_SEARCH) ||
this.containsVal(c.details.firstrelease, VALUE_TO_SEARCH) ||
(c.details.game && this.containsVal(c.details.game.name, VALUE_TO_SEARCH))
);
}
2nd:
º Map the only needed properties and filter them.
arr.map(item => {
return {
name: item.name,
author_details: item.details && item.details.author,
firstrelease_details: item.details && item.details.firstrelease,
game_name: item.details && item.details.game && item.details.game.name
};
}).filter(item => {
return Object.keys(item).some(key => {
const value = item[key];
return value && value.toLowerCase().indexOf(VALUE_TO_SEARCH) !== -1;
});
});
I need help transforming this table of data:
[
{property:"key", content:"1"},
{property:"key", content:"2"},
{property:"key2", content:"3"},
{property:"key2:subkey", content:"4"},
{property:"key2:someother:key", content:"5"},
{property:"foo", content:"6"},
{property:"foo", content:"7"},
{property:"foo:bar", content:"8"}
]
into a JSON object with the following structure:
{
key: ["1", "2"],
key2: {
'': "3"
subkey: "4"
someother: {
key: "5"
}
},
foo: [
"6",
{
'': "7"
bar: "8"
}
]
}
Here are the rules. Note: all rules apply to any level in the JSON object (json.levelOne, json.level.two, json.level.three.even, etc)
For each row.property like "a:b:c" should translate into json.a.b.c = row.content.
When row.property = "x" and json.x !== undefined then json.x = [json.x, row.content]
Whenever json.x === "string" and row.property = "x:y" then json.x = {'': json.x, y: row.content}
Whenever Array.isArray(json.x) && json.x[json.x.length-1] === "string" and row.property = "x:y" then json.x[json.x.length-1] = {'': json.x[json.x.length-1], y: row.content}
Hopefully that gives you some idea as to the criteria of what I need to do to translate the data into this JSON object format.
Why?
I'm trying to take Open Graph meta data and serialize it into a JSON object. I feel like the format above would best reflect the Open Graph meta data structure. I need help writing this algorithm though. This is for an open source Node.js project that I'm working on.
All help is appreciated. Thanks!
edit
So there are some issue left to the parser. Arrays occur at leaf nodes in some cases.
Here is the project on GitHub: https://github.com/samholmes/node-open-graph Feel free to fork it, build a better parse, and send me a pull request.
Updated per our discussion on IRC
var data = [
{property:"key", content:"1"},
{property:"key", content:"2"},
{property:"key2", content:"3"},
{property:"key2:subkey", content:"4"},
{property:"key2:someother:key", content:"5"},
{property:"foo", content:"6"},
{property:"foo", content:"7"},
{property:"foo:bar", content:"8"},
{property:"foo:baz", content:"9"}
];
var transformed = {};
data.forEach(function (item) {
var key, tmp,
ptr = transformed,
keys = item.property.split(':');
// we want to leave one key to assign to so we always use references
// as long as there's one key left, we're dealing with a sub-node and not a value
while (keys.length > 1) {
key = keys.shift();
if (Array.isArray(ptr[key])) {
// the last index of ptr[key] should become
// the object we are examining.
tmp = ptr[key].length-1;
ptr = ptr[key];
key = tmp;
}
if (typeof ptr[key] === 'string') {
// if it's a string, convert it
ptr[key] = { '': ptr[key] };
} else if (ptr[key] === undefined) {
// create a new key
ptr[key] = {};
}
// move our pointer to the next subnode
ptr = ptr[key];
}
// deal with the last key
key = keys.shift();
if (ptr[key] === undefined) {
ptr[key] = item.content;
} else if (Array.isArray(ptr[key])) {
ptr[key].push(item.content);
} else {
ptr[key] = [ ptr[key], item.content ];
}
});
console.log(transformed);
Outputs:
{
key: ['1', '2'],
key2: {
'': '3',
subkey: '4',
someother: {
key: '5'
}
},
foo: ['6', {
'': '7',
bar: '8'
baz: '9'
}]
}