databind.exc.MismatchedInputException: Cannot deserialize value - for micronaut - BFF test - unit-testing

I am working in micronaut graphQL and writing a test. The logic seems to be pretty straight forward. I am making a mock call and suppose to receive a fake response. The request been called without issues I can see it in the logs. But it seems like it is not able to parse the data and deserialize it. I am trying to make a test for a request but got deserialize error
Here is the error message
message -> Error code: GENERIC_ERROR, description: Exception getData, executionId: 770c8b9b-47cd-445e-9739-19ca0fd890c2, detailedInfo: message = Error instantiating bean of type [com.web.MyApi]: Cannot deserialize value of type `java.util.ArrayList<java.lang.String>` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (String)"{
"data": {
"values": {
"formatted": [
{
"name": "2",
"lastName": "15"
}
]
}
}
}"; line: 5, column: 9] (through reference chain: com.web.DataResponse["data"]->com.web.Values["formatted"]->java.util.ArrayList[0]), cause = com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<java.lang.String>` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (String)"{
"data": {
"metr
My test
given("my query description ") {
val scoreCardInfoRequestGraph =
"""
query{
getData(number:"6528")
{
name
lastName
}
}
""".toEscapedQuery()
`when`("posting the query") {
val dto =
client.getResponse(
myGraph,
ReportDataResponse::class.java
)
then("check the score card data formatted correctly") {
//verify
}
}
}
My graph for mocking the response
{
"data": {
"values": {
"formatted": [
{
"name": "2",
"lastName": "15"
}
]
}
}
}
And classes that I use for parsing
data class ReportDataResponse(
#JsonProperty("data")
val data: Data
)
data class Data(
#JsonProperty("values")
val values: Values
)
data class Values(
#JsonProperty("formatted")
val formatted: List<List<String>>
)

Related

How to manipulate strings and array in Apache Velocity request mapping template in AWS appSync

This is my first time working with VTL, so please correct my code if there is something very silly in it.
What I want to achieve
{
"cities": ["jaipur", "mumbai", "delhi", "sheros", "jalandhar", "bengaluru"]
}
Graphql schema:
type Query{
getCitiesForGroups: String
}
Default response template:
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
Using the default response template, the result I was getting
{ "data": { "getCitiesForGroups": [ "{groupcity=jaipur}", "{groupcity=mumbai}", "{groupcity=delhi}", "{groupcity=sheros}", "{groupcity=jalandhar}", "{groupcity=bengaluru}" ] } }
My request template
{
"version": "2018-05-29",
"statements": [
"select DISTINCT LOWER(city) as city from public.Groups"
]
}
To get the desired output, I changed the response template as I wanted to loop over the response I got from the DB, and remove the {city=} from the string by using the substring method given in AWS resolver mapping docs and this is where I am facing the problem.
My response template
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
#set ($rawListOfCities = $utils.rds.toJsonObject($ctx.result)[0])
#set ($sanitisedListOfCities = [])
#foreach( $city in $rawListOfCities )
#set ($equalToIndex = $city.indexOf("="))
#set ($equalToIndex = $equalToIndex + 1)
#set ($curlyLastIndex = $city.lastIndexOf("}"))
#set ($tempCity = $city.substring($equalToIndex, $curlyLastIndex))
## #set ($tempCity = $city)
$util.qr($sanitisedListOfCities.add($tempCity))
#end
$util.toJson($sanitisedListOfCities)
The response that I am getting:
{
"data": {
"getCitiesForGroups": "[null, null, null, null, null, null]"
}
}
However, when I use the line #set ($tempCity = $city) and comment out the line above it, I get the following response:
{
"data": {
"getCitiesForGroups": "[{city=jaipur}, {city=mumbai}, {city=delhi}, {city=sheros}, {city=jalandhar}, {city=bengaluru}]"
}
}
Which means $city has the value {city=jaipur}, so which I want to sanitize and add it to ($sanitisedListOfCities) and return it as the response.
But I am getting null as the result substring method.
So how can I sanitize the response from DB and return it?
I had a similar issue and this is how I solved it.
First, your Graphql schema should look like,
type Query {
getCitiesForGroups: cityList
}
type cityList {
cities: [String]
}
Your request mapping template,
{
"version": "2018-05-29",
"statements": [
"select DISTINCT LOWER(city) as city from public.Groups"
]
}
And finally your response mapping template
#set($cityList = [])
#set($resMap = {})
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
#foreach($item in $utils.rds.toJsonObject($ctx.result)[0])
$util.qr($cityList.add($item.city))
#end
$util.qr($resMap.put("cities", $cityList))
#return($resMap)
Expected response(complete)
{
"data": {
"getCitiesForGroups": {
"cities": [
"jaipur",
"mumbai",
"delhi",
"sheros",
"jalandhar",
"bengaluru"
]
}
}
}
I hope this helps you.

Apollo Nested Query

How do I get a value from nested array of object?
Here is my API looks like:
{
"id": 7,
"code": "ABC123",
"name": "Abu Bakar Enterprise",
"speCompanyDetails": [
{
"id": 1,
"speCompanyId": 7,
"registrationType": "2",
"registrationNo": "12345678",
"registrationYear": 2005,
"annualIncome": 100000,
}
]
}
My objective is I want to get value for code and name but at the same time I also want to fetch value from speCompanyDetails.annualIncome
Currently my query is similar like this:
const SUPPLIER_INFO_QUERY = gql`
query SupplierInfoQuery($mainSuppId: String!) {
supplierInfo(id: $mainSuppId)
#rest(path: "services/supplier/api/spe-companies/{args.id}", method: "GET", type: "SupplierInfo") {
id
name
code
speCompanyDetails
}
}
`;
But the value for anuualIncome is undefined.
const SUPPLIER_INFO_QUERY = gql
query SupplierInfoQuery($mainSuppId: String!) {
supplierInfo(id: $mainSuppId)
#rest(path: "services/supplier/api/spe-companies/{args.id}", method: "GET", type: "SupplierInfo") {
id
name
code
speCompanyDetails{ annualIncome }
}
}
;

How do I insert an optional field as null using AppSync Resolvers and Aurora?

I have an optional String field, notes, that is sometimes empty. If it's empty I want to insert null, otherwise I want to insert the string.
Here is my resolver -
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
#set($notes = $util.defaultIfNullOrEmpty($context.arguments.notes, 'null'))
"payload": {
"sql":"INSERT INTO things VALUES ('$id', :NOTES)",
"variableMapping": {
":NOTES" : $notes
},
"responseSQL": "SELECT * FROM things WHERE id = '$id'"
}
}
With this graphql
mutation CreateThing{
createThing() {
id
notes
}
}
I get -
{
"data": {
"createRoll": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "null"
}
}
}
when I really want null without the quotes.
And with this graphql -
mutation CreateThing{
createThing(notes: "Here are some notes") {
id
notes
}
}
I get -
{
"data": {
"createThing": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "Here are some notes"
}
}
}
which is what I want.
How do I get a quoteless null and a quoted string into the same field?
TL;DR you should use $util.toJson() to print the $context.arguments.notes correctly. Replace your $notes assignment with
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Explanation:
The reason is VTL prints whatever the toString() method returns and your call to
$util.defaultIfNullOrEmpty($context.arguments.notes, 'null') will return the string "null", which will be printed as "null".
If you replace with $util.defaultIfNullOrEmpty($context.arguments.notes, null) then it will return a null string. However, VTL will print $notes because that is the way it handles null references. In order to print null, which is the valid JSON representation of null, we have to serialize it to JSON. So the correct statement is:
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Full test:
I'm assuming you started with the RDS sample provided in the AWS AppSync console and modified it. To reproduce, I updated the content field in the Schema to be nullable:
type Mutation {
...
createPost(author: String!, content: String): Post
...
}
type Post {
id: ID!
author: String!
content: String
views: Int
comments: [Comment]
}
and I modified the posts table schema so content can also be null there: (inside the Lambda function)
function conditionallyCreatePostsTable(connection) {
const createTableSQL = `CREATE TABLE IF NOT EXISTS posts (
id VARCHAR(64) NOT NULL,
author VARCHAR(64) NOT NULL,
content VARCHAR(2048),
views INT NOT NULL,
PRIMARY KEY(id))`;
return executeSQL(connection, createTableSQL);
}
This is the request template for the createPost mutation:
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
"payload": {
"sql":"INSERT INTO posts VALUES ('$id', :AUTHOR, :CONTENT, 1)",
"variableMapping": {
":AUTHOR" : "$context.arguments.author",
":CONTENT" : $util.toJson($util.defaultIfNullOrEmpty($context.arguments.content, null))
},
"responseSQL": "SELECT id, author, content, views FROM posts WHERE id = '$id'"
}
}
and response template:
$util.toJson($context.result[0])
The following query:
mutation CreatePost {
createPost(author: "Me") {
id
author
content
views
}
}
returns:
{
"data": {
"createPost": {
"id": "b42ee08c-956d-4b89-afda-60fe231e86d7",
"author": "Me",
"content": null,
"views": 1
}
}
}
and
mutation CreatePost {
createPost(author: "Me", content: "content") {
id
author
content
views
}
}
returns
{
"data": {
"createPost": {
"id": "c6af0cbf-cf05-4110-8bc2-833bf9fca9f5",
"author": "Me",
"content": "content",
"views": 1
}
}
}
We were looking into the same issue. For some reason, the accepted answer does not work for us. Maybe because it's a beta feature and there is a new resolver version (2018-05-29 vs 2017-02-28, changes here: Resolver Mapping Template Changelog).
We use this for the time being using NULLIF():
{
"version": "2018-05-29",
"statements": [
"INSERT INTO sales_customers_addresses (`id`, `customerid`, `type`, `company`, `country`, `email`) VALUES (NULL, :CUSTOMERID, :TYPE, NULLIF(:COMPANY, ''), NULLIF(:COUNTRY, ''), :EMAIL)"
],
"variableMap": {
":CUSTOMERID": $customerid,
":TYPE": "$type",
":COMPANY": "$util.defaultIfNullOrEmpty($context.args.address.company, '')",
":COUNTRY": "$util.defaultIfNullOrEmpty($context.args.address.country, '')",
":EMAIL": "$context.args.address.email"
}
}

Setting output contexts in Dialogflow

Using the C# client library for Dialogflow, I am trying to set the output context in a webhook response. However, the output context field is read only. This is my code:
WebhookResponse response = new WebhookResponse
{
FulfillmentText = "This is a test",
OutputContexts = ... //Regardless of what I try and set OutputContexts to be, I get the error "property or indexer 'WebhookResponse.OutputContexts' cannot be assigned to -- it is read only"
};
How do I set the output context?
I know this is an old question but just in case someone has the same problem.
You can not assign a new list to OutputContexts, you have to add them to the list:
For example:
response.OutputContexts.Add(new Context
{
Name = $"{request.Session}/your_context",
LifespanCount = 1
});
I think the response json which you are forming is wrong.
Below is the correct json response which you need to send:
{
"fulfillmentText = "This is a test",
"outputContexts": [
{
"name": "projects/project_id/agent/sessions/session_id/contexts/your_context",
"lifespanCount": 5,
"parameters": {
"foo": "bar",
"foo1": "bar1"
}
}
],
"followupEventInput": {
"name": "even_name"
}
}

"type mismatch error, expected type LIST" for querying a one-to-many relationship in AppSync

The schema:
type User {
id: ID!
createdCurricula: [Curriculum]
}
type Curriculum {
id: ID!
title: String!
creator: User!
}
The resolver to query all curricula of a given user:
{
"version" : "2017-02-28",
"operation" : "Query",
"query" : {
## Provide a query expression. **
"expression": "userId = :userId",
"expressionValues" : {
":userId" : {
"S" : "${context.source.id}"
}
}
},
"index": "userIdIndex",
"limit": #if(${context.arguments.limit}) ${context.arguments.limit} #else 20 #end,
"nextToken": #if(${context.arguments.nextToken}) "${context.arguments.nextToken}" #else null #end
}
The response map:
{
"items": $util.toJson($context.result.items),
"nextToken": #if(${context.result.nextToken}) "${context.result.nextToken}" #else null #end
}
The query:
query {
getUser(id: "0b6af629-6009-4f4d-a52f-67aef7b42f43") {
id
createdCurricula {
title
}
}
}
The error:
{
"data": {
"getUser": {
"id": "0b6af629-6009-4f4d-a52f-67aef7b42f43",
"createdCurricula": null
}
},
"errors": [
{
"path": [
"getUser",
"createdCurricula"
],
"locations": null,
"message": "Can't resolve value (/getUser/createdCurricula) : type mismatch error, expected type LIST"
}
]
}
The CurriculumTable has a global secondary index titled userIdIndex, which has userId as the partition key.
If I change the response map to this:
$util.toJson($context.result.items)
The output is the following:
{
"data": {
"getUser": {
"id": "0b6af629-6009-4f4d-a52f-67aef7b42f43",
"createdCurricula": null
}
},
"errors": [
{
"path": [
"getUser",
"createdCurricula"
],
"errorType": "MappingTemplate",
"locations": [
{
"line": 4,
"column": 5
}
],
"message": "Unable to convert \n{\n [{\"id\":\"87897987\",\"title\":\"Test Curriculum\",\"userId\":\"0b6af629-6009-4f4d-a52f-67aef7b42f43\"}],\n} to class java.lang.Object."
}
]
}
If I take that string and run it through a console.log in my frontend app, I get:
{
[{"id":"2","userId":"0b6af629-6009-4f4d-a52f-67aef7b42f43"},{"id":"1","userId":"0b6af629-6009-4f4d-a52f-67aef7b42f43"}]
}
That's clearly an object. How do I make it... not an object, so that AppSync properly reads it as a list?
SOLUTION
My response map had a set of curly braces around it. I'm pretty sure that was placed there in the generator by Amazon. Removing them fixed it.
I think I'm not seeing the complete view of your schema, I was expecting something like:
schema {
query: Query
}
Where Query is RootQuery, in fact you didn't share us your Query definition. Assuming you have the right Query definition. The main problem is in your response template.
> "items": $util.toJson($context.result.items)
This means that you are passing a collection named: *"items"* to Graphql query engine. And you are referring this collection as "createdCurricula". So solve this issue your response-mapping-template is the right place to fix. How? just replace the above line with the following.
"createdCurricula": $util.toJson($context.result.items),
Please the main thing to note here is, the mapping template is a bridge between your datasources and qraphql, feel free to make any computation, or name mapping but don't forget that object names in that response json are the one should match in schema/query definition.
Thanks.
Musema
change to result type to $util.toJson($ctx.result.data.posts)
The exception msg says that it expected a type list.
Looking at:
{
[{"id":"2","userId":"0b6af629-6009-4f4d-a52f-67aef7b42f43"},{"id":"1","userId":"0b6af629-6009-4f4d-a52f-67aef7b42f43"}]
}
I don't see that createdCurricula is a LIST.
What is currently in DDB is:
"id": "0b6af629-6009-4f4d-a52f-67aef7b42f43",
"createdCurricula": null