Check for null values in request body using Wiremock - regex

I am trying to setup a wiremock stub that will return a 400 error if any field has a null value in the json payload. Basically to simulate a Bad Request. I've been trying with a regex that matches any lowercase string for the json key but it doesn't seem to like it. I can't find any examples of what I want online so not sure if it's even possible.
My Bad Request body:
{
"cat": null,
"dog": {
"id": 1344
},
"horse": {
"id": 1
},
"fish": 1
}
My Stub:
wireMockServer.stubFor(post(urlEqualTo("/sample-api"))
.withRequestBody(matchingJsonPath("$.^[a-z]*", equalTo(null)))
.willReturn(aResponse()
.withStatus(400)
.withHeader("Content-Type", "application/json")))
In this example I would expect the stub to match "cat" as the value of it is null. This isn't the case. Can anyone tell me what I'm doing wrong?

In the WireMock documentation on Request Matching the section on JSON Path matching. In the source code there is a reference to com.jayway.jsonpath.JsonPath library used. The build.gradle refers to version 2.4.0. The documentation for the Jayway JSON Path library can be found on their Github project page. There is a good, but by no means perfect online evaluator here.
The WireMock documentation only shows support for Regular Expression for the node values in the form of the "matchesJsonPath". In the Jayway documenatation there is an online example: $..book[?(#.author =~ /.*REES/i)]. For this reason the only approach is to name all the nodes that are not allowed to be null.
In the below example mapping all the mentioned nodes will be tested, regardless of their depth (see #id). This mapping will not trigger if all the mentioned nodes are not null, but some unmentioned ones are.
{
"request": {
"urlPattern": "/sample-api",
"method": "GET",
"bodyPatterns" : [ {
"matchesJsonPath" : "$..[?(#.cat == null || #.dog == null || #.horse == null || #.fish == null || #.id == null)]"
} ]
},
"response": {
"status": "400",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"jsonBody": {
"message": "some sapi message"
}
}
}

If you weren't aware of all possible keys, you could use a Custom Request Matcher to check if the request body contained any null values, and if so, return your 400 error. I'd recommend creating your own class, something that resembles...
public class BodyNullCheck extends RequestMatcherExtension {
#Override
public MatchResult match(Request request, Parameters parameters) {
JSONParser parser = new JSONParser();
try {
JSONObject body = (JSONObject) parser.parse(request.getBody().toString());
for(Iterator iterator = body.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
if (body.get(key) == null) {
return MatchResult.of(true);
}
}
} catch (ParseException ex) {
ex.printStackTrace();
}
return MatchResult.of(false);
}
}
The above takes the request body and converts it to a JSONObject, and then iterates over all keys in the JSONObject. If any of their values are null, then we will return true. If after iterating over all of them, a null value isn't found, we return false.

Related

How can I let `anything-but` support `not-exist` in event pattern

I am following this instruction https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but to setup a event pattern rule with anything-but like below code:
{
"detail": {
"payload": {
"type": [
{
"anything-but": "test"
}
]
}
}
}
In above example, I set the type field in payload of the event to be anything but test. It works fine if the event has payload->type field. But it doesn't accept the event if it payload doesn't have type field. It seems anything-but filter out none-exist field.
As an example, below event payload is accepted:
{
detail: {
payload: {
name: 'xxx',
type: 'production'
}
}
}
but below event which doesn't have type field is not accepted.
{
detail: {
payload: {
name: 'xxx'
}
}
}
How can I let it support none-exist? I'd like to make it accept event who doesn't have such field.
You have certainly moved on with your life since then... but figured I would drop an answer here for anyone else traversing the interwebs in frustration with EventBridge rules...
In the array you are assigning to type, you can include any number of matching options:
{
"detail": {
"payload":
"type": [
{"anything-but": "test"},
{"exists": false}
]
}
}
}
If you need more complex matching that includes multiple fields existing or not, look into $or
https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-complex-example-or

How to properly set an API call in QML using XMLHttpRequest

I am building a small weather API as exercise to use QML and properly operate an API call using OpenWeather and you can see there a typical API response.
The problem I am having is that I can't get the API call to work. After setting a minimal example with some cities that you can see below, right next to the city it should appear the symbol of the weather, but it does not happen. The list of the icon can be found here. Source code of the MVE can be found here for completeness.
The error from the compiler: qrc:/main.qml:282: SyntaxError: JSON.parse: Parse error
This is what is happening
This is what is expected
Typical API JSON response can be found both here and below:
{
"coord": {
"lon": -122.08,
"lat": 37.39
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
"base": "stations",
"main": {
"temp": 282.55,
"feels_like": 281.86,
"temp_min": 280.37,
"temp_max": 284.26,
"pressure": 1023,
"humidity": 100
},
"visibility": 16093,
"wind": {
"speed": 1.5,
"deg": 350
},
"clouds": {
"all": 1
},
"dt": 1560350645,
"sys": {
"type": 1,
"id": 5122,
"message": 0.0139,
"country": "US",
"sunrise": 1560343627,
"sunset": 1560396563
},
"timezone": -25200,
"id": 420006353,
"name": "Mountain View",
"cod": 200
}
Below a snippet of code related to the API call:
main.qml
// Create the API getcondition to get JSON data of weather
function getCondition(location, index) {
var res
var url = "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}"
var doc = new XMLHttpRequest()
// parse JSON data and put code result into codeList
doc.onreadystatechange = function() {
if(doc.readyState === XMLHttpRequest.DONE) {
res = doc.responseText
// parse data
var obj = JSON.parse(res) // <-- Error Here
if(typeof(obj) == 'object') {
if(obj.hasOwnProperty('query')) {
var ch = onj.query.results.channel
var item = ch.item
codeList[index] = item.condition["code"]
}
}
}
}
doc.open('GET', url, true)
doc.send()
}
In order to solve this problem I consulted several sources, first of all : official documentation and the related function. I believe it is correctly set, but I added the reference for completeness.
Also I came across this one which explained how to simply apply XMLHttpRequest.
Also I dug more into the problem to find a solution and also consulted this one which also explained how to apply the JSON parsing function. But still something is not correct.
Thanks for pointing in the right direction for solving this problem.
Below the answer to my question. I was not reading properly the JSON file and after console logging the problem the solution is below. code was correct from beginning, only the response needed to be reviewed properly and in great detail being the JSON response a bit confusing:
function getCondition() {
var request = new XMLHttpRequest()
request.open('GET', 'http://api.openweathermap.org/data/2.5/weather?q=London&units=metric&appid=key', true);
request.onreadystatechange = function() {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status && request.status === 200) {
console.log("response", request.responseText)
var result = JSON.parse(request.responseText)
} else {
console.log("HTTP:", request.status, request.statusText)
}
}
}
request.send()
}
Hope that helps!
In your code, your url shows this: "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}". You need to replace {city id} and {your api key} with real values.
You can solve it by providing an actual city ID and API key in your request URL

Postman AssertionError when testing using a defined function

I have a Postman request getting a json response like so:
{
"Rules": [
{
"Type": "Melee",
"Captain": "Falcon",
"Falco": "Lombardi",
"Fox": "McCloud",
"Princess": "Peach",
"Kirby": null
},
{
"Type": "Brawl",
"Captain": "Toad",
"Falco": "The bird",
"Fox": "Blip",
"Princess": "Daisy",
"Kirby": null
},
{
"Type": "64",
"Captain": "America",
"Falco": "Dair",
"Fox": "Shine",
"Princess": "Float",
"Kirby": null
}
]
}
I'd like to test all the values returned. The problem is that it won't always be in this order; for example, in the future, '64' might be sent first then 'Brawl' then 'Melee' or something along those lines. So I'm trying to make a loop that checks which type it is, then tests accordingly:
for(var i in jsonResponse.Rules)
{
if(jsonResponse.Rules[i] == "Melee")
{
pm.test("Melee Captain is Falcon", testFunction(jsonResponse.Rules[i].Captain, "Falcon");
pm.test("Melee Falco is Lombardi", testFunction(jsonResponse.Rules[i].Falco, "Lombardi");
//repeat for fox, princess and kirby
}
if(jsonResponse.Rules[i] == "Brawl")
{
pm.test("Brawl Captain is Toad", testFunction(jsonResponse.Rules[i].Captain, "Toad");
//repeat for the rest
}
if(jsonResponse.Rules[i] == "64")
{
pm.test("64 Captain is America", testFunction(jsonResponse.Rules[i].Captain, "America");
//repeat for the rest
}
}
And here is the testFunction method:
function testFunction(value, shouldEqualThis)
{
pm.expect(value).to.eql(shouldEqualThis);
}
This will work when the test passes, but if the test fails I get the following error:
There was an error in evaluating the test script:
AssertionError: expected 'FalconSpelledWrong' to deeply equal 'Falcon'
This is the case for anytime I do 'pm.test' that calls 'testFunction' with values that don't match.
I just want the test to fail rather than break the script.
Core Problem: I don't understand what the difference is between this: (working)
pm.test("Melee Captain is Falcon", function() {
pm.expect(jsonResponseData.Rules[0].Captain).to.eql("FalconSpelledWrong");
})
and this: (not working)
pm.test("Falcon equals FalconSpelledWrong", stringCompare("Falcon", "FalconSpelledWrong"));
function stringCompare(value, shouldEqualThis)
{
pm.expect(value).to.eql(shouldEqualThis);
}
The first one will just fail the test and move on. The second will throw an AssertionError.
The difference between the two is
With first you are passing a callback function (throws AssertionFailure)
while with second you are explicitly calling the function (throws AssertionError)

How do I resolve data for a schema type that implement an interface in GraphQL?

I’m trying to develop a spring boot graphQl service using graphql-java-8 library. I’m fetching data from a web-service, the response I get from the service is a bit like dynamic for which I have to introduce graphQl interface in my response graphQl schema.
extend type Query {
search(
name: String,
category: String
): [ResultOne]
}
interface ResultOne {
name: String
content: String
}
type Fish implements ResultOne {
name: String
content: String
weight: Float
}
type Fruit implements ResultOne {
name: String
content: String
Color: String
}
type Toy implements ResultOne {
name: String
content: String
description: String
}
To wiring my model with graphQl framework,
return RuntimeWiring.newRuntimeWiring()
.wiringFactory(new WiringFactory() {})
.type(
TypeRuntimeWiring.newTypeWiring("ResultOne")
.typeResolver(env -> {
if(env.getObject() instanceof Map) {
Map object = env.getObject();
if (object.containsKey("name") && object.get("name").equals("fish")) {
return (GraphQLObjectType) env.getSchema().getType("Fish");
} else if (object.containsKey("name") && object.get("name").equals("fruit")) {
return (GraphQLObjectType) env.getSchema().getType("Fruit");
} else if(object.containsKey("name") && object.get("name").equals("toy")) {
return (GraphQLObjectType) env.getSchema().getType("Toy");
} else {
return null;
}
} else {
return null;
}
})
)
.build();
So, type resolving issue is also fix a way, not sure it’s ideal or not. For data binding I’m not sure how do I do that in graphQl’s recommended way. I would like to add that, I’ve a single End-Point and single fetcher for the whole API. Data are fetched in a single request and don't want to call twice as I already have whole response. I had to resolve the type at runtime and wire the data for implemented model. So far data are fetched perfectly and the values are also coming till the interface against my query, but appeared null for interface implemented model part e.g: Fish, Fruit & Toy in this example. My question is how do I manupulate dynamically resolved type data for the java library?
Feel free to ask me any question regarding this issue.
Sample query:
{
search() {
ResultOne {
name
content
... on Fish {
weight
}
}
}
}
Corresponding response that I'm currently getting:
{
"data": {
"search": [
{
"resultOne": [
{
"name": "Salmon",
"content": "Frozen Food",
"weight": null
}
]
}
]
},
"extensions": {
"Total-ResponseTime": 23020,
"resultOne-Time": 22683
}
}

Error response in ember-data api

My code for saving document to API looks like that
save(category) {
category.save().then(() => {
this.transitionTo('categories');
}).catch((adapterError) => {
console.log(category.get('errors').toArray());
console.log(category.get('isValid'));
});
},
When API answers is:
{"errors":[{"attribute":"name","message":"This value should not be blank."}]}
then
category.get('isValid')
still returns true.
My question is, how validation errors should looks like?
By default, ember-data's adapter determines that a response is invalid when the status code is 422. You can override the isInvalid function of the adapter to change this.
Also, ember-data now expects errors to be formatted into a json-api error object. If your backend doesn't return it in this format, you can transform it in ember by overriding the handleResponse function of the adapter.
This is a sample json-api error:
{"errors": [
{
"detail": "Must be unique",
"source": { pointer: "/data/attributes/title"}
},
{
"detail": "Must not be blank",
"source": { pointer: "/data/attributes/content"}
}
]}
If you're returning error responses you described above, you would have to do something like this in your adapter:
handleResponse(status, headers, payload) {
if (status === 422 && payload.errors) {
let jsonApiErrors = [];
for (let key in payload.errors) {
for (let i = 0; i < payload.errors[key].length; i++) {
jsonApiErrors.push({
detail: payload.errors[key][i],
source: {
pointer: `data/attributes/${key}`
}
});
}
}
return new DS.InvalidError(jsonApiErrors);
} else {
return this._super(...arguments);
}
}