Spring cloud contract: how to verify an array list (Kotlin based project) - spring-cloud-contract

I would like to write a groovy contract to verify an array list with string values.
Lets say I have an object:
data class MyDataObject(val messageList: List<String>)
my contract is the following:
package contracts
import org.springframework.cloud.contract.spec.Contract
Contract.make {
name("retrieve_list_of_objects")
description("""
given:
you want to have a list of MyObjects
when:
you get the list
then:
you have the list
""")
request {
method 'GET'
url '/10/my-objects'
headers {
contentType(applicationJson())
}
}
response {
status 200
body(
[
messageList: ["23412341324"]
]
)
headers {
contentType(applicationJson())
}
} }
the problem is that created test is translated to:
assertThatJson(parsedJson).array("['messageList']").contains("23412341324").value();
and that results in:
com.jayway.jsonpath.PathNotFoundException: Expected to find an object with property ['messageList'] in path $ but found 'net.minidev.json.JSONArray'. This is not a json object according to the JsonProvider: 'com.jayway.jsonpath.spi.json.JsonSmartJsonProvider'.
The question is: how can I write my contract to create the following test:
assertThatJson(parsedJson).array("['messageList']").contains("23412341324");

I ran your snippet in my project and it generated a test that looks like this (I don't know why my test generating looks different than yours)
MockMvcRequestSpecification request = given()
.header("Accept", "application/json")
.body("{\"messageList\":[\"23412341324\"]}");
If I am reading your question right, you want the body to be a list of MyObjects, and not just one.
I think the problem is that you need to surround MyObject with one more set of square brackets, if indeed you want this to verify a list of MyObjects.
body(
[[
messageList: ["23412341324"]
]]
)
In General
Use SQUARE BRACKETS to make objects (yes i know in JSON square brackets are for arrays, its weird, i didn't invent it)
You can surround field names with quotes or without, they both seem to work.
body([
stringField1: value(regex(".*")),
stringField2: value(regex(alphaNumeric()),
innerObject1: [
innerStringField1: "Hardcoded1",
innerIntegerField1: anyInteger()
]
])
Wait? How do I make JSON lists then if square brackets are for objects?
Double square brackets. Seriously.
body(
[[
stringFieldOfObjectInList: regex(".*")
]]
)

Related

Define a contract using query string parameter in json path assertion

I'm trying to use SCC to write some contracts before I rebuild the producer side (there are no existing tests!). The examples around lists and deeper data structures in the documents are a bit thin, so I want to understand if this is feasible, or perhaps I have to drop down to calling a command to make the right assertions.
I'm using the latest version v2.11
So -
Given:
An API that will return a list of objects in its payload. The length of the list will depending on the identity of the client, i.e. client A will get 3 items, client B will get 4 items. The identity of the client isn't of interest here.
When:
A client makes a GET request, passing a querystring parameter for item selection within the list of items
Then:
I want to write a contract that takes input from request and proves that the response contains a list of objects, and the item that matches the selector has a boolean field selected:true, while the rest of the items have selected:false. There's an edge where the selector is wrong and no item is selected.
E.g. For the request GET /someapi?id=bbb
Response
{ foo: xxxy, bar: 123, baz: [
{ id: 'aaa', selected: false, .... },
{ id: 'bbb', selected: true, .... },
{ id: 'ccc', selected: false, .... }
] }
Of course the selected item can be anywhere in the list. So I had in mind an assertion like this pseudo code -
jsonPath('$.baz[?(#.id == fromRequest().query("id"))].selected', byEquality( true ) )
But I can't do that fromRequest() stuff in the JSONPath statement. Right now I guess I could simply have the whole response body as the spec, but that seems unwieldy. If it must be, that's fine I guess.
Any ideas or help appreciated.

AppSync Resolver only works when I hard code the input. context.arguments does not work

Edit for clarity: There are no error messages, it simply returns an empty list if the input string is from the context.arguments, suggesting that it simply isn't getting the input variable out on the query tester (setting it up incorrectly brings up that famous typing error of course). I've also made this into a pipeline with the exact same result. Looking around, people suggest making an intermediate object, but surely I'm just getting my input variables out wrong somehow.
I'm working on a project in AWS Appsync using DynamoDB and I've run into a problem with the context.arguments input.
Basically the code all works if I hardcode the string for the book id into the query (full context to follow), but if I use the context.arguments, it simply refuses to work properly, returning an empty array for the "spines".
I have the following types in my schema:
type Book {
id: ID!
title: String
spines: [Spine]
}
type Spine {
id: ID!
name: String
bookId: ID!
}
I use the following query:
type Query {
getBook(id: ID!): Book
query getBook($bookId: ID!){
getBook(id: $bookId){
title
id
spines {
name
bookId
}
}
}
With the following input (assume this is a relevant guid):
{
"bookId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
}
And this resolver for the spines object:
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "bookId-index",
"query" : {
"expression": "#bookId = :bookId",
"expressionNames" : {
"#bookId" : "bookId"
},
"expressionValues" : {
":bookId" : { "S" : "${context.arguments.id}" }
}
}
}
}
I made sure my data set contained false positives too (spines for other books) so that I know when my query brings back the correct data.
This works if I hardcode a guid as string instead of using context.arguments, and gets exactly what I'm looking for for each book guid.
For example, replacing the expression values with this works perfectly:
"expressionValues" : {
":bookId" : { "S" : "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }
}
Why does "${context.arguments.id}" not get the input variable here the same way as it seems to in other queries?
Thanks to #IonutTrestian for pointing me in the right direction.
$ctx.args was empty, but I decided to go up the chain to see what was in the entire context, so $util.error($util.toJson($ctx)).
The json object I found included a little object called "Source", which contained the query return for the Book object.
Long story short, $ctx.source.id when applied to my query worked a charm.
I also know a bit more about debugging DynamoDB resolvers in case I encounter problems like this in future. Thank you so much!

Cannot append values to an entity

My question is : how to append a value given by a user to an entity. The user provided value is dynamic.
The Watson response overwrites the toppings variable with the value given by the user, as you can see in the attached image.
{
"output": {
"text": "I got an order to add one or more toppings.
Adding <?context.toppings.append('toppings')?>.
Toppings to provide: <?entities['toppings']?.toString()?>"
},
"context": {
"toppings": "<? entities['toppings']?.toString()?>"
}
}
You can append to an array with the .append() function.
In your example, the expression "toppings": "<? entities['toppings']?.toString()?>" will overwrite the toppings variable every time this node is processed with the actual recognized entities #toppings. First the the $toppings variable needs to be defined as an array, e.g.:
"context" : {
"toppings" : []
}
Then in context part of a dialog node you can write:
"context" : {
"toppings" : "<?$toppings.append(entities['toppings'].toJsonArray())?>"
}
More info in our doc: Watson Conversation Doc
EDIT: Thinking about this, it is probably not a good idea to have the same name for the entity and for the variable you store it in. :-)

filter the Json according to string in an array in JSONPATH

I have a situation where I have json String that has a child as Array that contains only Strings. Is there as way I can get the object reference of the arrays that contains a specific String.
Example:
{ "Books":{
"History":[
{
"badge":"y",
"Tags":[
"Indian","Culture"
],
"ISBN":"xxxxxxx",
"id":1,
"name":"Cultures in India"
},
{
"badge":"y",
"Tags":[
"Pre-historic","Creatures"
],
"ISBN":"xxxxxxx",
"id":1,
"name":"Pre-historic Ages"
}
]
}
}
To Achieve:
From the above JSON String, need to get all books in History which contains "Indian" inside the "tags" list.
I am using JSONPATH in my project but If there is other API that can provide similar functionality, any help is welcome.
If you're using Goessner JSONPath, $.Books.History[?(#.Tags.indexOf('Indian') != -1)] as mentioned by Duncan above should work.
If you're using the Jayway Java port (github.com/jayway/JsonPath), then
$.Books.History[?(#.Tags[?(# == 'Indian')] != [])] or more elegantly, use the in operator like this $.Books.History[?('Indian' in #.Tags)]. Tried them both here.
Assuming you are using Goessner JSONPath (http://goessner.net/articles/JsonPath/) the following should work:
$.Books.History[?(#.Tags.indexOf('Indian') != -1)]
According to the Goessner site, you can use underlying JavaScript inside the ?() filter. You can therefore use the JavaScript indexOf function to check if your Tags array contains the tag 'Indian'.
See a working example here using this JSONPath query tester:
http://www.jsonquerytool.com/sample/jsonpathfilterbyarraycontents
Did you try to use underscoreJS ? You can get the Indian books like this :
var data = {"Books:"....};
var indianBooks = _.filter(data.Books.History, function(book) { return _.contains(book.Tags, "Indian"); })

what is #params in Iron:router

with meteor's IronRouter, I'm trying to use the this.params object elsewhere, but confused as to what it is. It seems to be a zero length array, that is actually an object with named methods after the path components.
# coffee
#route 'magnets',
path: '/magnets/lesson/:lessonCname'
data: ->
if #ready()
debugger;
console.log("route.params", #params)
with this code, in the debug console I will get:
this.params
[]
this.params.lessonCname
"despite-magnets-01"
typeof(this.params)
"object"
this.params.length
0
this.ready()
but in passing the params object to a server method, the methods (ie "lessonCname") disappear.
If my understanding is correct, then the near-term question is what is the best way to retrieve/convert these methods to {property:value} so they can be serialized and passed to server calls?
There are two easy ways of solving your problem, you can either set a global variable from within the data scope (but this is considered bad practice, at least IMO) or you can use the "data" function, which returns the data context for the current template:
data: ->
window._globalscopedata = #params.whatever #setting global variable
return someCollection.findOne #returns data context
_id: #params.whatever
when proccessing this route I will have the whatever param available in _globalscoredata and my document available in the template context.
Take a look at the source code for retrieving the parameters from a path. params is an array, but may have named properties. To iterate over everything, you can use the for in loop:
for(var x in myArray){
// Do something.
}
In this way, you can copy over everything to a new object (there may be a simpler way to create a copy).
The params property attached to a RouteController is an object with the following properties :
hash : the value of the URL hash.
query : an object consisting of key/value pairs representing the query string.
a list of URL fragments with their name and actual value.
Let's take an example, for this route definition :
// using iron:router#1.0.0-pre2 new route definition
Router.route("/posts/:slug");
And this URL typed in the browser address bar : /posts/first-post#comments?lang=en
We can use the console to find out precisely what params will actually contain :
> Router.current().params
Which will display this result :
Object {
hash: "comments",
slug: "first-post",
query: {
lang: "en"
}
}
Here slug is already a property of the params object whose value is "first-post", this is not a method.
If you want to extract from params these URL fragments as an object of key/value pairs, you can use underscore omit :
// getting rid of the hash and the query string
var parameters=_.omit(this.params,["hash","query"]);

Resources