Amazon web services Amazon Dynomo DB:BatchPutItem,因为APPSYNC_假设_角色和类型不匹配错误
我正在使用AWS Amplify构建一个React应用程序。我使用Cognito用户池进行身份验证,使用GraphQL 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
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)
)
);