Amazon Dynamodb:BatchGetItem因为APPSYNC ASSUME_ROLE和类型不匹配错误

问题描述 投票:1回答:1

我正在使用AWS Amplify构建一个React应用程序。我使用Cognito用户池进行身份验证,使用GraphQL AppSync后端进行后端处理。

我正在尝试为批量突变编写自定义解析器。这是我使用的架构:

type Todo @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  title: String!
  description: String
  completed: Boolean
}

input CreateTodoInput {
  id: ID
  title: String!
  description: String
  completed: Boolean
}

type Mutation {
  batchAddTodos(todos: [CreateTodoInput]): [Todo]
}

此模式允许使用Cognito User Pools对GraphQL API进行身份验证。

为了使这个自定义变异起作用,需要添加custom resolvers。我更改了amplify/api/<your-api-name>/stacks/CustomResources.json以包含以下资源:

// Left everything as it was
 "Resources": {
    "EmptyResource": {
      "Type": "Custom::EmptyResource",
      "Condition": "AlwaysFalse"
    },
    "BatchAddTodosResolver": {
      "Type": "AWS::AppSync::Resolver",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "DataSourceName": "TodoTable",
        "TypeName": "Mutation",
        "FieldName": "batchAddTodos",
        "RequestMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchAddTodos.req.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        },
        "ResponseMappingTemplateS3Location": {
          "Fn::Sub": [
            "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchAddTodos.res.vtl",
            {
              "S3DeploymentBucket": {
                "Ref": "S3DeploymentBucket"
              },
              "S3DeploymentRootKey": {
                "Ref": "S3DeploymentRootKey"
              }
            }
          ]
        }
      }
    }
  },
// ... more code that I didn't touch

对于自定义请求解析器,我编写了以下模板:

#foreach($item in ${ctx.args.todos})
    ## [Start] Owner Authorization Checks **
    #set( $isOwnerAuthorized = false )
    ## Authorization rule: { allow: "owner", ownerField: "owner", identityField: "cognito:username" } **
    #set( $allowedOwners0 = $util.defaultIfNull($item.owner, null) )
    #set( $identityValue = $util.defaultIfNull($ctx.identity.claims.get("username"),
    $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____")) )
    #if( $util.isList($allowedOwners0) )
        #foreach( $allowedOwner in $allowedOwners0 )
            #if( $allowedOwner == $identityValue )
                #set( $isOwnerAuthorized = true )
            #end
        #end
    #end
    #if( $util.isString($allowedOwners0) )
        #if( $allowedOwners0 == $identityValue )
            #set( $isOwnerAuthorized = true )
        #end
    #end
    #if( $util.isNull($allowedOwners0) && (! $item.containsKey("owner")) )
        $util.qr($item.put("owner", $identityValue))
        #set( $isOwnerAuthorized = true )
    #end
    ## [End] Owner Authorization Checks **

    ## [Start] Throw if unauthorized **
    #if( !($isStaticGroupAuthorized == true || $isDynamicGroupAuthorized == true || $isOwnerAuthorized
        == true) )
        $util.unauthorized()
    #end
    ## [End] Throw if unauthorized **
#end

#set($todosdata = [])
#foreach($item in ${ctx.args.todos})
    $util.qr($item.put("createdAt", $util.time.nowISO8601()))
    $util.qr($item.put("updatedAt", $util.time.nowISO8601()))
    $util.qr($item.put("__typename", "Todo"))
    $util.qr($item.put("id", $util.defaultIfNullOrBlank($item.id, $util.autoId())))
    $util.qr($todosdata.add($util.dynamodb.toMapValues($item)))
#end
{
  "version": "2018-05-29",
  "operation": "BatchPutItem",
  "tables": {
      "TodoTable": $utils.toJson($todosdata)
  }
}

在第一个循环中,我试图验证用户是否可以访问他创建的待办事项。在第二个循环中,我添加了由Amplify CLI生成的解析器添加的数据。这包括__typename,时间戳和id

然后我发出创建资源的请求。我跟着this tutorial for the code。请注意,我必须将版本更新为"2018-05-29"。 Amplify CLI生成的代码通常有"2017-02-28"的版本(我不知道这是否重要)。

我还为响应解析器编写了以下映射:

#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.data.unprocessedKeys)
#end

$util.toJson($ctx.result.data)

这基本上告诉AppSync返回所有未处理项的数据和错误。

我首先尝试使用React发出请求:

import API, { graphqlOperation } from '@aws-amplify/api';

// ... later

async function handleClick() {
  const todoFixtures = [
    { id: 1, title: 'Get groceries', description: '', completed: false },
    { id: 2, title: 'Go to the gym', description: 'Leg Day', completed: true }
  ];

  try {
    const input = { todos: prepareTodos(todoFixtures) };
    const res = await API.graphql(graphqlOperation(batchAddTodos, input));
    console.log(res);
  } catch (err) {
    console.log('error ', err);
  }
}

prepareTodos刚刚摆脱id字段并将空字段设置为null(以避免DynamoDB对我大喊大叫)。代码位于底部,因为它无关紧要。

由于失败了,我尝试通过AppSync控制台进行突变:

mutation add {
  batchAddTodos(todos: [
    {title: "Hello", description: "Test", completed: false}
  ]) {
    id title
  }
}

但两次尝试都会抛出以下错误:

{
  "data": {
    "batchAddTodos": null
  },
  "errors": [
    {
      "path": [
        "batchAddTodos"
      ],
      "data": null,
      "errorType": "DynamoDB:AmazonDynamoDBException",
      "errorInfo": null,
      "locations": [
        {
          "line": 32,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "User: arn:aws:sts::655817346595:assumed-role/Todo-role-naona7ytt5drxazwmtp7a2uccy-batch/APPSYNC_ASSUME_ROLE is not authorized to perform: dynamodb:BatchWriteItem on resource: arn:aws:dynamodb:eu-central-1:655817346595:table/TodoTable (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: AccessDeniedException; Request ID: EP48SJPVMB9G9M69HPR0BO8SKJVV4KQNSO5AEMVJF66Q9ASUAAJG)"
    },
    {
      "path": [
        "batchAddTodos"
      ],
      "locations": null,
      "message": "Can't resolve value (/batchAddTodos) : type mismatch error, expected type LIST"
    }
  ]
}

这让我相信React代码与AppSync代码“正确”或至少同样错误。但我怀疑错误是在解析器模板映射中的某个地方。我找不到它。这里出了什么问题?

也许生成的假设角色不支持"2018-05-19"版本?这是角色默认生成角色的代码(我没有写这个):

 "AssumeRolePolicyDocument": {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
},
"Policies": [
    {
        "PolicyName": "DynamoDBAccess",
        "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "dynamodb:BatchGetItem",
                        "dynamodb:BatchWriteItem",
                        "dynamodb:PutItem",
                        "dynamodb:DeleteItem",
                        "dynamodb:GetItem",
                        "dynamodb:Scan",
                        "dynamodb:Query",
                        "dynamodb:UpdateItem"
                    ],
                    "Resource": [
                        {
                            "Fn::Sub": [
                                "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}",
                                {
                                    "tablename": {
                                        "Fn::If": [
                                            "HasEnvironmentParameter",
                                            {
                                                "Fn::Join": [
                                                    "-",
                                                    [
                                                        "Todo",
                                                        {
                                                            "Ref": "GetAttGraphQLAPIApiId"
                                                        },
                                                        {
                                                            "Ref": "env"
                                                        }
                                                    ]
                                                ]
                                            },
                                            {
                                                "Fn::Join": [
                                                    "-",
                                                    [
                                                        "Todo",
                                                        {
                                                            "Ref": "GetAttGraphQLAPIApiId"
                                                        }
                                                    ]
                                                ]
                                            }
                                        ]
                                    }
                                }
                            ]
                        },
                        {
                            "Fn::Sub": [
                                "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*",
                                {
                                    "tablename": {
                                        "Fn::If": [
                                            "HasEnvironmentParameter",
                                            {
                                                "Fn::Join": [
                                                    "-",
                                                    [
                                                        "Todo",
                                                        {
                                                            "Ref": "GetAttGraphQLAPIApiId"
                                                        },
                                                        {
                                                            "Ref": "env"
                                                        }
                                                    ]
                                                ]
                                            },
                                            {
                                                "Fn::Join": [
                                                    "-",
                                                    [
                                                        "Todo",
                                                        {
                                                            "Ref": "GetAttGraphQLAPIApiId"
                                                        }
                                                    ]
                                                ]
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    }
]

prepareTodos

const map = f => arr => arr.map(f);
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
const dissoc = prop => ({ [prop]: _, ...obj }) => obj;
const mapObj = f => obj =>
  Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: f(obj[key]) }), {});
const replaceEmptyStringWithNull = x => (x === '' ? null : x);
const prepareTodos = map(
  pipe(
    dissoc('id'),
    mapObj(replaceEmptyStringWithNull)
  )
);

编辑:我设法解决了typemismatch。在回复中我必须回复:$util.toJson($ctx.result.data.TodoTable)

amazon-web-services amazon-dynamodb amazon-iam aws-appsync aws-amplify
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.