I'm looking a way for setting default variables for appsync dynamodb resolvers, if one of variables from query is not provided.
Let's say we have simple db request mapping
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
## If your table's hash key is not named 'id', update it here. **
"id" : { "S" : "$utils.autoId()" }
},
"attributeValues" : {
## Add an item for each field you would like to store to Amazon DynamoDB. **
"key" : { "S" : "${context.arguments.id}" },
"name" : { "S" : "${context.arguments.name}" }
}
}
I found a way to do this using conditional statements
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
## If your table's hash key is not named 'id', update it here. **
"id" : { "S" : "$utils.autoId()" }
},
"attributeValues" : {
## Add an item for each field you would like to store to Amazon DynamoDB. **
"key" : { "S" : "${context.arguments.id}" },
#if( ($context.arguments.name))
"name" : { "S" : "${context.arguments.name}" }
#else
"name" : { "S" : "default" }
#end
}
}
Above way we can also exclude one of attributes if it is not provided
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
## If your table's hash key is not named 'id', update it here. **
"id" : { "S" : "$utils.autoId()" }
},
"attributeValues" : {
## Add an item for each field you would like to store to Amazon DynamoDB. **
"key" : { "S" : "${context.arguments.id}" },
#if( ($context.arguments.name))
"name" : { "S" : "${context.arguments.name}" }
#end
}
}
If $context.arguments.name is not provided then name attribute isn't inserted into dynamodb.
But is there any smarter way to do above?
If you don't want to specify keys explicitly, you can do something like:
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
## If your table's hash key is not named 'id', update it here. **
"id" : { "S" : "$utils.autoId()" }
},
#set( $expValues = {} )
#foreach( $entry in $context.arguments.entrySet() )
$!{expValues.put("${entry.key}", { "S" : "${entry.value}" })}
#end
#if( !${expValues.isEmpty()} )
"attributeValues" : $utils.toJson($expValues)
#end
}
Related
I want to update a 'person' item in my table.
I want to update the persons name and his set of skills.
It's also possible that we just use the updatePerson mutation to update the name. And we will update the skills later.
At that point the argument 'skills' is an empty list. However DynamoDB does not allow for empty sets.
Currently I am trying to work around this by first checking if the skills argument is an empty list. But it is still telling me "An string set may not be empty for key :skills".
This is my current request mapping template, but atm the isNullOrDefault check does not work.
#if ($util.isNullOrEmpty($context.arguments.skills))
#set ($skills = $utils.dynamodb.toNullJson())
#else
#set ($skills = $utils.dynamodb.toStringSetJson($context.arguments.skills))
#end
{
"version" : "2018-05-29",
"operation" : "UpdateItem",
"key": {
"id" : $utils.dynamodb.toDynamoDBJson($context.arguments.id)
},
"update" : {
"expression" : "set #name = :name, #skills= :skills,
"expressionNames" : {
"#name": "name",
"#skills": "skills",
},
"expressionValues" : {
":name" : $utils.dynamodb.toDynamoDBJson($context.arguments.name),
":skills" : $skills,
}
}
}
Do you know how I can set the set of skills if the skills argument is not an empty array and not set it if the skills argument is an empty array?
Instead of setting null into a string-set attribute, I think you just remove the attribute from the item item.skills = undefined.
You can use SET, and REMOVE actions to achieve that. The update is dynamically generated based on the input of skills. Sample code (I haven't tested it myself)
#set ($update = {
"expression" : "set #name = :name remove #skills",
"expressionNames" : {
"#name": "name",
"#skills": "skills"
},
"expressionValues" : {
":name" : $utils.dynamodb.toDynamoDBJson($context.arguments.name)
}
})
#if (!$util.isNullOrEmpty($context.arguments.skills))
#set ($update = {
"expression" : "set #name = :name set #skills = $skill",
"expressionNames" : {
"#name": "name",
"#skills": "skills"
},
"expressionValues" : {
":name" : $utils.dynamodb.toDynamoDBJson($context.arguments.name),
":skills" :$utils.dynamodb.toStringSetJson($context.arguments.skills),
}
})
#end
{
"version" : "2018-05-29",
"operation" : "UpdateItem",
"key": {
"id" : $utils.dynamodb.toDynamoDBJson($context.arguments.id)
},
"update" : $update // or maybe $util.toJson($update)
}
I have the following DynamoDB mapping template, to update an existing DynamoDB item:
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.application.id),
"tenant": $util.dynamodb.toDynamoDBJson($ctx.identity.claims['http://domain/tenant'])
},
"update" : {
"expression" : "SET #sourceUrl = :sourceUrl, #sourceCredential = :sourceCredential, #instanceSize = :instanceSize, #users = :users",
"expressionNames" : {
"#sourceUrl" : "sourceUrl",
"#sourceCredential" : "sourceCredential",
"#instanceSize" : "instanceSize",
"#users" : "users"
},
"expressionValues" : {
":sourceUrl" : $util.dynamodb.toDynamoDbJson($ctx.args.application.sourceUrl),
":sourceCredential" : $util.dynamodb.toDynamoDbJson($ctx.args.application.sourceCredential),
":instanceSize" : $util.dynamodb.toDynamoDbJson($ctx.args.application.instanceSize),
":users" : $util.dynamodb.toDynamoDbJson($ctx.args.application.users)
}
},
"condition" : {
"expression" : "attribute_exists(#id) AND attribute_exists(#tenant)",
"expressionNames" : {
"#id" : "id",
"#tenant" : "tenant"
}
}
}
But I'm getting the following error:
message: "Unable to parse the JSON document: 'Unrecognized token '$util': was expecting ('true', 'false' or 'null')↵ at [Source: (String)"{↵ "version" : "2017-02-28",↵ "operation" : "UpdateItem",↵ "key" : {↵ "id": {"S":"abc-123"},↵ "tenant": {"S":"test"}↵ },↵ "update" : {↵ "expression" : "SET #sourceUrl = :sourceUrl, #sourceCredential = :sourceCredential, #instanceSize = :instanceSize, #users = :users",↵ "expressionNames" : {↵ "#sourceUrl" : "sourceUrl",↵ "#sourceCredential" : "sourceCredential",↵ "#instanceSize" : "instanceSize",↵ "#users" : "users"↵ }"[truncated 400 chars]; line: 17, column: 29]'"
I've tried removing parts, and it seems to be related to the expressionValues, but I can't see anything wrong with the syntax.
Seems like you misspelled the toDynamoDBJson method
Replace
$util.dynamodb.toDynamoDbJson($ctx.args.application.sourceUrl)
with
$util.dynamodb.toDynamoDBJson($ctx.args.application.sourceUrl)
Note the uppercase B in toDynamoDBJson.
This seems like it should be really simple but I haven't found any examples or documentation. I've got a dynamodb table that looks like this:
record 1: {name, email, items[{product}, {item2}, {item3]}
record 2: (name, email, items[{product}, {item2}, {item3]}
I need to be able to update items elements, i.e., update item1 object in record 1. I can do this with the following code by hardcoding the list array element, but I can't figure out how to pass the item number into the update expression :
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id" : { "S" : "${context.arguments.input.id}" }
},
"update" : {
"expression" : "SET #items[0].#product= :productVal",
"expressionNames" : {
"#product": "product",
},
"expressionValues" : {
":productVal": { "S" : "${context.arguments.input.product}" },
}
}
Have you tried something like:
"update" : {
"expression" : "SET #items[:idx].#product= :productVal",
"expressionNames" : {
"#product": "product",
},
"expressionValues" : {
":productVal": { "S" : "${context.arguments.input.product}" },
":idx": { "N" : 0 }
}
}
I am doing this in my Appsync Resolver:
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"pk" : { "S" : "Container" },
"id" : { "S" : "${ctx.args.id}" }
},
"update" : {
"expression" : "SET #name = :name, description = :description",
"expressionNames": {
"#name" : "name"
},
"expressionValues": {
":name" : { "S": "${context.arguments.name}" },
":description" : { "S": "${context.arguments.description}" },
}
}
}
But sometimes I may not pass in both name and description. How would I make it not SET those columns when those args are null?
All you need to do is to create your own SET expression with condition checked based on your need. Below expression check if any argument is null or empty, I don't want to update it.
#set( $expression = "SET" )
#set( $expValues = {} )
## NAME
#if( !$util.isNullOrEmpty(${context.arguments.name}) )
#set( $expression = "${expression} name = :name" )
$!{expValues.put(":name", { "S" : "${context.arguments.name}" })}
#end
## DESCRIPTION
#if( !$util.isNullOrEmpty(${context.arguments.description}) )
#if( ${expression} != "SET" )
#set( $expression = "${expression}," )
#end
#set( $expression = "${expression} description = :description" )
$!{expValues.put(":description", { "S" : "${context.arguments.description}" })}
#end
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"pk" : { "S" : "Container" }
"id" : { "S" : "${context.arguments.id}" }
},
"update" : {
"expression" : "${expression}",
"expressionValues": $util.toJson($expValues)
}
}
Hope it is useful!
This is very much possible. You just have to add a simple if statement to check if the value is there. A parallel example can be seen in the docs here: https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-resolvers.html
Specifically, that example (below) uses the application of optional arguments into a list operation.
{
"version" : "2017-02-28",
"operation" : "Scan"
#if( ${context.arguments.count} )
,"limit": ${context.arguments.count}
#end
#if( ${context.arguments.nextToken} )
,"nextToken": "${context.arguments.nextToken}"
#end
}
Just applying that if's null check should work for you.
I have next code for update item in DB with mapping template:
$!{expSet.put("available", ":available")}
$!{expValues.put(":available", { "BOOL": $ctx.args.available })}
when I send available = false - it's ok, but if available = true I get error
"Unable to parse the JSON document: 'Unexpected character (':' (code
58)): was expecting double-quote to start field name
schema in GraphQl
type Item {
....
available: Boolean!
....
}
What I do wrong ?
Your UpdateItem request mapping template should look something like:
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id" : { "S" : "${context.arguments.id}" }
},
"update" : {
"expression" : "SET #available = :available",
"expressionNames": {
"#available" : "available"
},
"expressionValues": {
":available" : { "BOOL": ${context.arguments.available} }
}
}
}