Amazon web services Amazon Dynomo DB:BatchPutItem,因为APPSYNC_假设_角色和类型不匹配错误

Amazon web services Amazon Dynomo DB:BatchPutItem,因为APPSYNC_假设_角色和类型不匹配错误,amazon-web-services,amazon-dynamodb,amazon-iam,aws-appsync,aws-amplify,Amazon Web Services,Amazon Dynamodb,Amazon Iam,Aws Appsync,Aws Amplify,我正在使用AWS Amplify构建一个React应用程序。我使用Cognito用户池进行身份验证,使用GraphQL AppSync后端进行后端 我正在尝试编写一个自定义解析器来批处理突变。以下是我使用的模式: type Todo @model @auth(rules: [{ allow: owner }]) { id: ID! title: String! description: String completed: Boolean } input CreateTodoIn

我正在使用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]
}
此模式使用启用GraphQL API的身份验证

为了让这个定制的变异生效,我们需要添加。我更改了
amplify/api//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)
  }
}
在第一个循环中,我试图验证用户是否有权访问他创建的TODO。在第二个循环中,我添加了由Amplify CLI生成的解析器添加的数据。这包括
\u类型名
、时间戳和
id

之后,我请求创建资源。我跟着。请注意,我必须将版本更新为
“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”
版本?以下是角色默认生成的角色的代码(我没有编写此代码):

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)
  )
);
编辑:我设法解决了类型不匹配的问题。在响应中,我必须在响应模板中返回:
$util.toJson($ctx.result.data.TodoTable)
$util.toJson($ctx.result.data.TodoTable)
,并将
TodoTable更改为实际的表名(您可以在控制台的DynamoDB下查找,它看起来像是Todo-dqeronnsgvd2pf3facjmlgtsjk-master
)解决了错误


这也是我偶然发现这个问题时编写的一个循序渐进的教程。

有没有其他方法可以做到这一点?当前日期有更多的amplify支持?谢谢
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)
  )
);