Can predicate values have wildcards in Mountebank? - integration-testing

I am trying to define a stub:
{
"predicates":[
{
"equals":{
"method":"GET",
"path":"/sword/eBISXMLInvoice2.do",
"query": {
"action": "index",
"page": 3 <-- this one!
}
}
}
],
"responses":[
{
"is":{
"statusCode":200,
"headers":{
"Content-Type":"application/xml"
},
"body":"<doclist><document uuid='101654' type='invoice' date='2018-11-14 13:49:43' /></doclist>"
}
}
]
}
One of the expected query string parameters (called "page") can have multiple values. How can I define the predicate to handle this?

My question is actually very easy to answer. According to the docs, the "equals" predicate, will match if any value matches.
Full text:
On occasion you may encounter multi-valued keys. This can be the case
with querystrings and HTTP headers that have repeating keys, for
example ?key=first&key=second. In those cases, deepEquals will
require all the values (in any order) to match. All other predicates
will match if any value matches, so an equals predicate will match
with the value of second in the example above.
So I can just remove the changeable query string value from the predicate, or I can keep it in there, it doesn't matter.
{
"equals":{
"method":"GET",
"path":"/sword/eBISXMLInvoice2.do",
"query": {
"action": "index"
}
}
}

Related

How to filter elements by node key in JSONPath?

What I want
Apply a JSONPath to given json response, to match specific elements by comparing their children's node keys with a value.
Input
{
"data": {
"ticket": {
"1": "foo",
"2": "bar",
"3": "baz"
}
}
}
Output (expected)
"3": "baz"
Case description
I want to apply a JSONPath expression, to filter ticket elements with ticket key greater than "2", so in this case it should match only the 3rd "baz" ticket.
ticket keys are only integer numbers in my data
Code area
This matches all node keys aka ticket keys
$.data.ticket.*~
This is a basic example of filtering
$..book[?(#.price<10)] // -> filter all books cheaper than 10
I am trying somehow to combine them in order to achieve the desired result
Where I test it
https://jsonpath.com/
References
https://goessner.net/articles/JsonPath/
It is possible with jsonpath-plus. The site https://jsonpath.com/ uses jsonpath-plus library internally.
It has some convenient additions or elaborations not provided in the original spec of jsonpath.
Use the #property to compare the value of the key.
$.data.ticket[?(#property > 2)]

Add element to arrays, that are values to a given key name (json transformation with jq)

I'm a jq newbie, and I try to transform a json (a Swagger spec). I want to add an element to the array value of the "parameter" keys:
{
...
"paths": {
"/great/endpoint1": {
"get": {
"parameters": [] <<--- add a value here
}
}
"/great/endpoint2": {
"post": {
"parameters": [] <<-- and here too here too etc.
....
The following jqplay almost works. It adds values to the right arrays, but it has the nasty side effect of also removing the "x-id" value from the root of the input json. It's probably because of a faulty if-condition. As the paths contain a varying string (the endpoint names), I don't know how to write a wildcard path expression to address those, which is why I have tried using walk instead:
https://jqplay.org/s/az56quLZa3
Since the sample data is incomplete, it's difficult to say exactly what you're looking for but it looks like you should be using parameters in the call to walk:
walk(if type=="object" and has("parameters")
then .parameters += [{"extra": "value"}]
else . end)
If you want to restrict the walk to the top-level paths, you would preface the above with: .paths |=

Avoiding need for redundant check to map given zero value of string?

We have a map[string]string, I assume that means the zero value of a string retrieved from the map is ""
So doesn't that mean that this:
var userId, ok = params["user_id"];
if !ok || userId == "" {
return 422, "Missing user_id in request"
}
is the same logic as this:
var userId = params["user_id"];
if userId == "" {
return 422, "Missing user_id in request"
}
just making sure my understanding is correct.
It's not the same if you intend to store the zero value of the value type.
See this example:
m := map[string]string{
"empty": "",
}
if v, ok := m["empty"]; ok {
fmt.Printf("'empty' is present: %q\n", v)
} else {
fmt.Println("'empty' is not present")
}
if v, ok := m["missing"]; ok {
fmt.Printf("'missing' is present: %q\n", v)
} else {
fmt.Printf("'missing' is not present")
}
It outputs (try it on the Go Playground):
'empty' is present: ""
'missing' is not present
It's true that if you never store the zero value in the map, you may simply use if m[value] == zeroValue {}. This is detailed here: How to check if a map contains a key in Go?
This "property" of maps can be exploited to create sets elegantly. See How can I create an array that contains unique strings?
And using this "technique" has another advantage too: you can check existence of multiple keys in a compact way (you can't do that with the special "comma ok" form). More about this: Check if key exists in multiple maps in one condition

Does the script filter expression in JSONPath applies only to array elements (and not objects)?

I have gone through most of the JSONPath documentations out there and they all explain that the script filters such as $.items[(#.length - 1)] only applies to an array and not to a JSON object. This means that the path would work for the first JSON object below and not for the second one:
1:
{
"items": [
1,
2
]
}
2:
{
"items": {
"item1": 1,
"item2": 2
}
}
Can anyone confirm this? Also, if I am correct, is there a logical reason for this behavior? I can imagine that such a path could have been allowed to return the same value (2) in both cases.

MapReduce for counting parameter values

I have document like this:
{
"_id": ObjectId("4d17c7963ffcf60c1100002f"),
"title": "Text",
"params": {
"brand": "BMW",
"model": "i3"
}
}
{
"_id": ObjectId("4d17c7963ffcf60c1100002f"),
"title": "Text",
"params": {
"brand": "BMW",
"model": "i5"
}
}
What i need is the count of every params values. like:
brand
---------
BMW (2)
model
---------
i3 (1)
i5 (1)
I think i have to write map/reduce functions. How can i do this? Thanks.
I think i have to write map/reduce functions.
Yes you need a map-reduce for this. For some simple map-reduce examples, please look here.
For your particular case, you first need to change your expectation of the output. The output of the map / reduce is a collection. The collection will look (in your case) something like this:
{ key : { 'brand' : 'bmw' }, value : 2 }
{ key : { 'model' : 'i5' }, value : 1 }
To generate this set you will need a "map" function and a "reduce" function. The "map" function will emit a key and a value. The key is each element of params, the value is the count of 1. The "reduce" function accepts a key and an array of values and returns just a single value. Your question is basically the same as this example on the MongoDB site:
map = function() {
if (!this.params) {
return;
}
for (index in this.params) {
emit(this.params[index], 1);
}
}
reduce = function(previous, current) {
var count = 0;
for (index in current) {
count += current[index];
}
return count;
}
In your map function enumerate the properties of the params property of the this object. For each property you find call emit with a key that contains both the name of the property and the value of the property. Pass 1 as the value. e.g. emit({'brand','BMW'}, 1) but obviously using variables not constants!
In your reduce function you are passed a key and an array of values. Sum these values and return the sum. Even though the initial array will be all 1's don't be tempted to use the length of the array because the reduce function can be called iteratively.
You can group the results afterwards from the result collection, applying an index if necessary for performance.

Resources