Select an array member by name with a JSON Pointer - jsonpointer

Is there a way to select an array member the value of a key with JSON Pointer? So for this JSON Schema:
"links":[
{
"title": "Create",
"href": "/book",
"method": "POST",
"schema": {}
},
{
"title": "Get",
"href": "/book",
"method": "GET",
"schema": {}
}
]
Instead of:
links/0/schema
I would like to be able to do:
links/{title=GET}/schema

The Json Pointer defined in RFC6901 doesn't allow you to select an array member by name. Here is the only mention of Arrays in the RFC:
If the currently referenced value is a JSON array, the reference token MUST contain either:
* characters comprised of digits ..., or
* exactly the single character "-", making the new referenced
value the (nonexistent) member after the last array element.

Apparently not. So I did this:
const links = schema.links;
let ref;
for (const [i, link] of links.entries()) {
if (link.href === req.originalUrl && link.method === req.method) {
ref = `schema.json#/links/${i}/schema`;
break;
}
}

Related

Elasticsearch saves document as string of array, not array of strings

I am trying to contain array as a document value.
I succeed it in "tags" field as below;
This document contains array of strings.
curl -XGET localhost:9200/MY_INDEX/_doc/132328908
#=> {
"_index":"MY_INDEX",
"_type":"_doc",
"_id":"132328908",
"found":true,
"_source": {
"tags": ["food"]
}
}
However, when I am putting items in the same way as above,
the document is SOMETIMES like that;
curl -XGET localhost:9200/MY_INDEX/_doc/328098989
#=> {
"_index":"MY_INDEX",
"_type":"_doc",
"_id":"328098989",
"found":true,
"_source": {
"tags": "[\"food\"]"
}
}
This is string of array, not array of strings, which I expected.
"tags": "[\"food\"]"
It seems that this situation happens randomly and I could not predict it.
How could it happen?
Note:
・I use elasticsearch-ruby client to index a document.
This is my actual code;
es_client = Elasticsearch::Client.new url: MY_ENDPOINT
es_client.index(
index: MY_INDEX,
id: random_id, # defined elsewhere
body: {
doc: {
"tags": ["food"]
},
}
)
Thank you in advance.

openapi, list of strings as query parameter

I'm defining a query parameter, with openapi 3.0.1, as follows
{
"name" : "sort",
"in" : "query",
"description" : "Sorting criteria. Example: productCode,desc",
"required" : false,
"explode" : false,
"schema" : {
"type" : "array",
"items" : {
"type" : "string"
}
}
}
On swagger-ui 3.51.1 if I add two strings
"parameter1,asc"
"parameter2,desc"
they are serialized correctly (as a list of strings with 2 elements), but if I add only one string
"parameter1,asc"
it will get serialized incorrectly as a list of strings with 2 elements (parameter1 and asc).
I do not understand why the string is exploded! Any help is greatly appreciated.
In your example, the query parameter has no style defined, so it defaults to style: form. Non-exploded form style treats the comma , as a separator of array items. This results in ambiguity because the values of your array items also use commas as an inner separator.
Possible solutions involve changing your backend code and/or the OpenAPI parameter definition.
Adjust the backend code so that it splits the received sort string on every second comma rather than every comma.
Or, use another serialization method for the sort array, for example:
explode: true to send the exploded array:
?sort=parameter1,asc&sort=parameter2,desc
style: pipeDelimited + explode: false to separate array items using | instead of commas:
?sort=parameter1,asc|parameter2,desc
Or, change sort to be an object/map instead of an array:
{
"name": "sort",
"in": "query",
"description": "Sorting criteria. Example: productCode,desc",
"required": false,
"explode": false,
"schema": {
"type": "object",
"additionalProperties": {
"type": "string",
"enum": ["asc", "desc"]
}
}
}
In this case, your current query string format
?sort=parameter1,asc,parameter2,desc
unambiguously corresponds to:
{
"parameter1": "asc",
"parameter2": "desc"
}

How to validate response in one attempt

I want to test the response for POST request to petstore.swagger.io. I receive a response, why cannot I validate the body with path "id"? I always got errors, but regex is right and tested.
Test method:
#Test
public void postPet() {
Pattern pt = Pattern.compile("<(\\d*)>");
Response response = given()
.contentType("application/json")
.body(jsonObject)
.when()
.post(String.format("https://petstore.swagger.io/v2/pet"))
.then()
.statusCode(200)
.log().body()
.and()
.assertThat()
.body("id", matchesPattern(pt))
Error:
java.lang.AssertionError: 1 expectation failed.
JSON path id doesn't match.
Expected: a string matching the pattern '<(\d*)>'
Actual: <9223372000666122518L>
The response body is:
{
"id": 9223372000666122443,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
}
And what is the L at the end of the id string? In swagger, there is no L as well as in the Postman. I tried regex without "< , >".
It's kind of clear that
your id is 9223372000666122518L, L means long (data type)
you use regex, but it only works for String
--> type mistmach when comparing long vs String
Solution:
Convert id to String first then assert
long id = ...log().body()
.extract().path("id");
assertThat(String.valueOf(id), Matchers.matchesPattern("\\d+"));
or
Write a custom Matcher, to check isLong, like this Is there a way in Hamcrest to test for a value to be a number?

Rego Validation Array Compare

I am new at Rego and I am trying to write a policy in order to check if there is a set of rules already created on certain Azure NSGs.
Input test:
{
"name": "<name>",
"id": "<id>",
"etag": "<etag>",
"type": "<resourcetype>",
"location": "<location>",
"properties":
{
"provisioningState": "Succeeded",
"resourceGuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"securityRules":
[
{
"name": "<rule name>",
"id": "<id>",
"etag": "<etag",
"type": "<type>",
"properties":
{
"provisioningState": "Succeeded",
"description": "....",
"protocol": "*",
"sourcePortRange": "*",
"destinationPortRange": "53",
"sourceAddressPrefix": "*",
"access": "Allow",
"priority": 1,
"direction": "Outbound",
"sourcePortRanges": [],
"destinationPortRanges": [],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes":
[
"10.0.0.1",
"10.0.0.2",
"10.0.0.3"
]
}
}
]
{
}
I wrote a custom function in order to check the values. Below is the code that I am testing in The Rego Playground
existRule(rule) = true
{
input.properties.securityRules[i].name == rule.name
input.properties.securityRules[i].properties.provisioningState == rule.provisioningState
input.properties.securityRules[i].properties.description == rule.description
input.properties.securityRules[i].properties.protocol == rule.protocol
input.properties.securityRules[i].properties.access == rule.access
input.properties.securityRules[i].properties.priority == rule.priority
input.properties.securityRules[i].properties.direction == rule.direction
}
rule = {
"name": "name",
"provisioningState": "Succeeded",
"description": "description",
"protocol": "*",
"sourcePortRange": "*",
"destinationPortRange": "1",
"sourceAddressPrefix": "*",
"access": "Allow",
"priority": 1,
"direction": "Outbound",
"destinationAddressPrefix": "",
"sourcePortRanges": [],
"destinationPortRanges": [],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": [
"10.0.0.1",
"10.0.0.2",
"10.0.0.3",
"10.0.0.4"
]
}
rules
{
existRule(rule)
}
which is working for the properties that I define above, however I am having an issue when trying to compare arrays, particularly in this example with destinationAddressPrefixes
I have tried the following:
test1 { input.properties.securityRules[i].properties.destinationAddressPrefixes == rule.destinationAddressPrefixes }
Always returns false
With the following line I can check one destination address from the input against a specific ip, however I can not achive a compare of all the address of the input against the ones of the rule that is defined in the example
onerule {input.properties.securityRules[i].properties.destinationAddressPrefixes[_] == "10.0.0.1"}
test2 {input.properties.securityRules[i].properties.destinationAddressPrefixes[_] == rule.destinationAddressPrefixes[j]}
test3 {input.properties.securityRules[i].properties.destinationAddressPrefixes[j] == rule.destinationAddressPrefixes[k]}
test2 and test3 always return true, even when there is a rule that is not in the input. I also tried and array difference
x := input.properties.securityRules[i].properties.destinationAddressPrefixes - rule.destinationAddressPrefixes
but I get the following error:
rego_type_error: minus: invalid argument(s) have: (any, array<string,
string, string, string, string, string, string, string, string,
string, string, string, string, string>, ???) want: (any<number,
set[any]>, any<number, set[any]>, any<number, set[any]>)
Do you know if it is feasible to achieve what I am looking for? Or is there a different way to make a look of the array and compare the values one by one?
What does rule3400.destinationAddressPrefixes look like?
If you want to compare for exact equality between two arrays, == should suffice.
If all elements are known to be unique and order doesn't matter (which seems to be the case in your example) you could convert the arrays to sets using a set comprehension. This makes it possible to subtract one set from another such as you tried to do with arrays directly.
to_set(arr) = {x | x := arr[_]}
input_prefixes := to_set(input.properties.securityRules[i].properties.destinationAddressPrefixes)
destination_prefixes := to_set(rule3400.destinationAddressPrefixes)
x := input_prefixes - destination_prefixes

Rest Assured Body handling ArrayList

I has a response body like this
enter code here
{
"applicationName": "Service MyService",
"someData": [
{
"name": "check1",
"props": [
"AAaa"
]
},
{
"name": "check2",
"props": [
"BBbb",
"CCcc"
]
}
]
}
Now I can use the following code and the test passes.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("someData.name",
allOf(hasItems("check1", "check2")))
.body("someData.findAll {it.name == 'check1'}.props",
IsCollectionWithSize.hasSize(1))
.body("healthReports.findAll {it.name == 'check2'}.props",
IsCollectionWithSize.hasSize(2)));
However if I then attempt to check the values in the props field it fails I think because a ArrayList is returned and the matchers are checking on String.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("healthReports.findAll {it.name == 'check1'}.props",
IsCollectionContaining.hasItems(startsWith("AA")));
I'm not sure how from the findAll ...props I can check the contents of the ArrayList.
The error displayed is:
JSON path someData.findAll {it.name == 'check1'}.props doesn't match.
Expected: (a collection containing a string starting with "AA")
Actual: [[AAaa]]
Any idea's ?
The findall return an Array of Array containing one element which is AA (which is why you have [[AAaa]] instead of [AAaa].
You have to flatten or extract one level of array to solve the problem I think.

Resources