DynamoDB / ApiGateWay UpdateItem method returning serializedException - amazon-web-services

The bounty expires in 14 hours. Answers to this question are eligible for a +50 reputation bounty.
Wesley van S is looking for an answer from a reputable source:
Should be an easy question for someone experienced in AWS Api Gateway / DynamoDB
Quite new to AWS / DynamoDB. I'm trying to make a UpdateItem method work on my API. I have a Resource with the path x/users/update/{wallet} with wallet being the key.
In here I set a UpdateItem method just like I have all my other Query / Scan / PutItem methods. this has the following mapping template:
{
"TableName": "Users",
"KeyConditionExpression": "wallet = :v1",
"ExpressionAttributeValues": {
":v1": {
"S": "$input.params('wallet')"
}
":v2": {
"S": $input.json('$.username')
}
}
"UpdateExpression": "set username = :v2",
"ReturnValues": "UPDATED_NEW"
}
This returns a 200 with "__type": "com.amazon.coral.service#SerializationException"
Pretty sure this is related to my Mapping template but can't figure out what. Looked at the https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html and think I used the Sample Request in there as a template to test it like this
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {
"S": "$input.path('$.username')"
},
},
"ReturnValues": "ALL_NEW"
}
payload is just as simple as
{"username":"test"}
but that gives the same result.
Any ideas on what i'm doing wrong here?
Cheers!
EDIT:
PutItem looks like this:
{
"TableName": "Users",
"Item": {
"userId": {
"S": "$context.requestId"
},
"date": {
"S": "$input.path('$.date')"
},
"wallet": {
"S": "$input.path('$.wallet')"
}
}
}

It looks like you're not accessing the record to update with the key of your table. Unlike SQL, you cannot access a record to update with any field in DynamoDB. You have to use your key depending on how you defined it (PK or PK + SK).
Given that you have both PK and SK defined, you need them both in your key input. Together they form a composite primary key. The below input should work for you.
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
"date": {
"S": "$input.params('date')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {
"S": "$input.path('$.username')"
},
},
"ReturnValues": "ALL_NEW"
}

DynamoDB UpdateItem takes a Key not a KeyConditionExpression
This one below is correct:
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {"S": "$input.params('$.username')"},
},
"ReturnValues": "ALL_NEW"
}
You also state your payload is simply
{"username":"test"}
You must also specify wallet as this is the key

Related

Access an Array Item by index in AWS Dynamodb Query Results "Items" in Step Function

I have this dynamodb:Query in my step function:
{
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:dynamodb:query",
"Next": "If nothing returned by query Or Study not yet Zipped",
"Parameters": {
"TableName": "TEST-StudyProcessingTable",
"ScanIndexForward": false,
"Limit": 1,
"KeyConditionExpression": "OrderID = :OrderID",
"FilterExpression": "StudyID = :StudyID",
"ExpressionAttributeValues": {
":OrderID": {
"S.$": "$.body.order_id"
},
":StudyID": {
"S.$": "$.body.study_id"
}
}
},
"ResultPath": "$.processed_files"
}
The results comes in as an array called Items which is nested under my ResultPath
processed_files.Items:
{
"body": {
"order_id": "1001",
"study_id": "1"
},
"processed_files": {
"Count": 1,
"Items": [
{
"Status": {
"S": "unzipped"
},
"StudyID": {
"S": "1"
},
"ZipFileS3Key": {
"S": "path/to/the/file"
},
"UploadSet": {
"S": "4"
},
"OrderID": {
"S": "1001"
},
"UploadSet#StudyID": {
"S": "4#1"
}
}
],
"LastEvaluatedKey": {
"OrderID": {
"S": "1001"
},
"UploadSet#StudyID": {
"S": "4#1"
}
},
"ScannedCount": 1
}
}
My question is how do i access the items inside this array from a choice state in a step function?
I need to query then decide something based on the results by checking the item in a condition in a choice state.
The problem is that since this is an array I can't access it using regular JsonPath (like with Items.item), and in my next step the choice condition does NOT accept an index like processed_files.Items['0'].Status
Ok so the answer was so simple all you need to do is use a number instead of string for the array index like this.
processed_files.Items[0].Status
I was originally mislead by an error I received which said that it expected a ' or '[' after the first '['. I mistakenly thought this meant it only accepts strings.
I was wrong, it works like any other array.
I hope this helps somebody one day.

Wy do I get an error when issuing QueryCommand in nodejs sdk v3?

I am using aws node.js sdk v3. I want to do a simple query on the table using dynamodbdocumentclient. I wanted to do just a test if query works on a simple music table which is provided by aws developer guide.
const data = await this.dynamoDBDocumentClient.send(new QueryCommand({
TableName: "Music",
KeyConditionExpression: "#Artist = :artist",
ExpressionAttributeName: {
"#Artist": "artist"
},
ExpressionAttributeValues: {
":artist": "Acme Band"
},
});
But when I call this I get:
TypeError: Cannot read property '0' of undefined
When I do a simple:
const data = await.this.dynamoDBDocumentClient.send(newScanCommand({TableName: "Music"});
I am getting the correct response:
{
"AlbumTitle": {
"S": "Somewhat Famous"
},
"Awards": {
"N": "1"
},
"Artist": {
"S": "No One You Know"
},
"SongTitle": {
"S": "Call Me Today"
}
},
{
"AlbumTitle": {
"S": "Songs About Life"
},
"Awards": {
"N": "10"
},
"Artist": {
"S": "Acme Band"
},
"SongTitle": {
"S": "Happy Day"
}
},
{
"AlbumTitle": {
"S": "Songs About Sadness"
},
"Awards": {
"N": "8"
},
"Artist": {
"S": "Acme Band"
},
"SongTitle": {
"S": "Sad Day"
}
}
],
"ScannedCount": 3
}```
What am I doing wrong? I don't get why I am getting en error.
I just had the same problem, and according to this guide, the ExpressionAttributeValues must follow the DynamoDB JSON schema, being so, if your artist is a string, it should be:
...
ExpressionAttributeValues: {
":artist": {S: "Acme Band"}
},
...
Adding this, worked for me.
I think you simply have a typo here:
ExpressionAttributeName
It should be ExpressionAttributeNames (missing last letter).
However AWS-SDK is not very helpful with error messages. Use TypeScript to avoid similar issues, since it will show you, that you are using wrong field name basing on types definition.

DynamoDB Query and return one item only

I'm still learning DynamoDB and I would like to get a single item from a table based on two unique attributes (tconst and primaryTitle) in the table. Both of those attributes have unique values for each row. The primaryKey is tconst and the sortKey is primaryTitle
I thought that I could do something like below:
aws dynamodb query \
--endpoint-url http://localhost:8000 \
--table-name title \
--key-condition-expression "tconst = :tconst" and "primaryTitle = :primaryTitle" \
--expression-attribute-values '{
":tconst":{"S":"xxxx"},
":primaryTitle":{"S":"something"}
}'
"Item": {
"tconst": {
"S": "xxxx"
},
"titleType": {
"S": "xxxx"
},
"primaryTitle": {
"S": "something"
},
"originalTitle": {
"S": "Travel Daze"
},
"isAdult": {
"S": "0"
},
"startYear": {
"S": "2019"
},
"endYear": {
"S": "\\N"
},
"runtimeMinutes": {
"S": "\\N"
},
"genres": {
"S": "\\N"
}
},
"Item": {
"tconst": {
"S": "yyyy"
},
"titleType": {
"S": "yyyy"
},
"primaryTitle": {
"S": "Travel Daze"
},
"originalTitle": {
"S": "Travel Daze"
},
"isAdult": {
"S": "0"
},
"startYear": {
"S": "2019"
},
"endYear": {
"S": "\\N"
},
"runtimeMinutes": {
"S": "\\N"
},
"genres": {
"S": "\\N"
}
}
If you know the primary key (i.e. partition and sort key in your case) for your item and only want to fetch one anyway, the GetItem API call does exactly that. Query allows you to filter inside of an item collection (all items that share the same partition key) - it has additional functionality, which you may not need here.
The GetItem operation returns a set of attributes for the item with the given primary key. If there is no matching item, GetItem does not return any data and there will be no Item element in the response.
— docs
If you want to keep using the Query - you can limit the number of results using the aptly names Limit-Parameter.

Sort/sequence AWS API gateway output

tl;dr looking to sort API output from AWS API gateway
I have a dynamoDB table 'NP' with key 'id' and GSI 'atype-index'. There is no sort key.
Also, I have API gateway mapping template
{
"TableName": "NP",
"IndexName": "atype-index",
"KeyConditionExpression": "atype = :v1",
"ExpressionAttributeValues": {
":v1": {
"S": "$input.params('atype')"
}
}
}
The mapping template will extract all values in the table with GSI key matching the specified string. However, the values are extracted in no particular order, for example, the API could be:
{
"Count": 3,
"Items": [
{
"apiUrl": {
"S": “ee”
},
"webTitle": {
"S": “dd”
},
"atype": {
"S": “type”
},
"id": {
"S": "1a"
}
},
{
"apiUrl": {
"S": “dd”
},
"webTitle": {
"S": “cc”
},
"atype": {
"S": "atype"
},
"id": {
"S": “3a”
}
},
{
"apiUrl": {
"S": “cc”
},
"webTitle": {
"S": “bb”
},
"atype": {
"S": "atype"
},
"id": {
"S": “2a”
}
}
],
"ScannedCount": 3
}
How do I sort the above output by the key "apiURL" for example?
What you described is exactly what a sort key would accomplish, assuming you're using the Query API (rather than Scan).
If you add a sort key, the results will be sorted based on the sort key when querying a given partition key.
Edit: Just to clarify, there is no way to do the sort at the API GW layer in a mapping template.
You could put a Lambda function in between API GW and DDB if the overhead is worth it for your use case. Otherwise, probably better to just sort at client side.

Unable to map a list of numbers in API-Gateway

How do I create a mapping for a list of numbers object in API gateway? I am trying to post a list of integers using POST request. I tried working with NS attribute but got the Error.
Error:
{
"__type": "com.amazon.coral.service#SerializationException"
}
However, it works well when I have N attribute and post a single integer value.
Is there any way to resolve this issue?
I believe you are trying to map your request payload to DynamoDB JSON String. You can apply a velocity template like this one,
{
"TableName":"ABC",
"Item": {
"id": {
"S": "$context.requestId"
},
"name": {
"S": "$input.path('$.name')"
},
"price": {
"L": [
#set($prices=$input.path('$.price'))
#foreach($p in $prices)
{
"N": "$p"
}#if ($velocityCount < $prices.size()), #end
#end
]
}
}
}
Method Request Body:
{
"name":"Test",
"price": [1, 2, 3]
}
Endpoint Request Body:
{
"TableName": "ABC",
"Item": {
"id": {
"S": "test-invoke-request"
},
"name": {
"S": "Test"
},
"price": {
"L": [
{
"N": "1"
},
{
"N": "2"
},
{
"N": "3"
}
]
}
}
}