5
votes

I'm trying to batch put a number of items to DynamoDB using AppSync. When I call the resolver it throws no errors but nothing is saved to the database.

Schema

type BoxScore @model {
  id: ID!
  userId: String!
  gameVariant: String!
  complete: Boolean!
  failFact: BoxScoreFact @connection
  totalCorrect: Int!
}

type BoxScoreFact @model {
  id: ID!
  left: Int!
  right: Int!
  gameVariant: String!
  timestamp: Int!
  correct: Boolean!
}

input BatchAddCreateBoxScoreFactInput {
  id: ID
  left: Int!
  right: Int!
  gameVariant: String!
  timestamp: Int!
  correct: Boolean!
  boxScoreFactBoxScoreId: ID!
}

IAM Role:

        "Effect": "Allow",
        "Action": [
            "dynamodb:DeleteItem",
            "dynamodb:GetItem",
            "dynamodb:PutItem",
            "dynamodb:Query",
            "dynamodb:Scan",
            "dynamodb:UpdateItem",
            "dynamodb:BatchGetItem",
            "dynamodb:BatchWriteItem"
        ],

Resolver:

#set($factsdata = [])
#foreach($item in ${ctx.args.facts})
    $util.qr($factsdata.add($util.dynamodb.toMapValues($item)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        "TABLENAME": $utils.toJson($factsdata)
    }
}

Call from AppSync playground:

enter image description here

Response mapping template:

  #if($ctx.error)
      ## Append a GraphQL error for that field in the GraphQL response
      $utils.error($ctx.error.message, $ctx.error.message)
  #end

  {
      "boxScoreFacts": $util.toJson({"res": "no error", "ctx": $ctx}),
  }

Output template from Function test run:

enter image description here

DynamoDB Table

enter image description here

Where TABLENAME is set equal to the DynamoDB table name that is displayed in the DDB console. So something like BoxScoreFact-woieieie99392-prod.

The table is always empty and the response is null. This is lifted almost straight out of the example from the docs. Also, I should note, that putting one item using the normal create graphql function does put an item to the intended table.

What am I missing here?

3
Could you provide your response mapping template? - Neill
@Neill added. If the response mapping template is invalid would it prevent the put from happening entirely, i.e. no save to the table? - Phil Andrews
You can try and enable logging for AppSync and see if there is anything interesting in the logs. Also what are the IAM permissions for your role? - Erez
@Phil, not really, but you may not be seeing the error returning from DynamoDB, Could you try to raise an error instead of appending the error? - Neill
@Neill The screen shot that shows the $ctx output from the resolver template is truncated, apparently not copiable, has error: null, and outError: []. So it doesn't seem like anything is going wrong. Also, I should note, that putting one item using the normal create graphql function does put an item to the intended table. - Phil Andrews

3 Answers

0
votes

The lambda function of Appsync is called over the Serverless Appsync plugin and within this role the BatchWriteItem is missing in older versions. Within your package.json file try changing the plugin version to the latest or the GitHub version in which this problem is fixed.

This should fix it:

"devDependencies": {
   ....
   "serverless-appsync-plugin": "^1.1.0"
   ....
}

or

"devDependencies": {
   ....
   "serverless-appsync-plugin": "https://github.com/sid88in/serverless-appsync-plugin.git#e33b5cfd"
   ....
}
0
votes

It doesn't look like the mandatory id is being passed among your variables. You can try setting it automatically in your resolver:

#set($factsdata = [])
#foreach($item in ${ctx.args.facts})
    $util.qr($item.put("id", $util.defaultIfNullOrBlank($item.id, $util.autoId())))
    $util.qr($factsdata.add($util.dynamodb.toMapValues($item)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        "TABLENAME": $utils.toJson($factsdata)
    }
}
0
votes

The solution for me was to include the batch permissions in the IAM role "dynamodb:BatchGetItem" and "dynamodb:BatchWriteItem". Seems that this operations aren't included by default.

Final json policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem",
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:<region>:<account_id>:table/<TableName>",
                "arn:aws:dynamodb:<region>:<account_id>:table/<TableName>/*"
            ]
        }
    ]
}