本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
注意
我们现在主要支持 APPSYNC_JS 运行时系统及其文档。请考虑使用 APPSYNC_JS 运行时系统和此处的指南。
AWS AppSync 提供了一种简单方法,以通过单位解析器将 GraphQL 字段连接到单个数据来源。但是,执行单个操作可能还不够。管道解析器提供了对数据来源连续执行操作的能力。在 API 中创建函数并将这些函数附加到管道解析器。每个函数执行结果将通过管道传输到下一个函数,直到没有要执行的函数为止。通过使用管道解析器,您现在可以直接在 AWS AppSync 中构建更复杂的工作流。在本教程中,您将构建一个简单的图片查看应用程序,用户可以在其中发布图片和查看其好友发布的图片。
一键设置
如果要在 AWS AppSync 中自动设置 GraphQL 终端节点并配置所有解析器和所需的 AWS 资源,您可以使用以下 AWS CloudFormation 模板:
此堆栈在您的账户中创建以下资源:
-
AWS AppSync 的 IAM 角色,用于访问您的账户中的资源
-
2 个 DynamoDB 表
-
1 个 Amazon Cognito 用户池
-
2 个 Amazon Cognito 用户池组
-
3 个 Amazon Cognito 用户池用户
-
1 个 AWS AppSync API
在 AWS CloudFormation 堆栈创建过程结束时,对于创建的三个 Amazon Cognito 用户,您分别收到一封电子邮件。每封电子邮件包含一个临时密码,您可以使用该密码以 Amazon Cognito 用户身份登录到 AWS AppSync 控制台。保存密码完成本教程的剩余部分。
手动设置
如果您希望通过 AWS AppSync 控制台手动执行分步过程,请按照下面的设置过程进行操作。
设置您的非 AWS AppSync 资源
该 API 与两个 DynamoDB 表进行通信:pictures 表存储图片,friends 表存储用户之间的关系。此 API 配置为使用 Amazon Cognito 用户池作为身份验证类型。以下 AWS CloudFormation 堆栈在账户中设置这些资源。
在 AWS CloudFormation 堆栈创建过程结束时,对于创建的三个 Amazon Cognito 用户,您分别收到一封电子邮件。每封电子邮件包含一个临时密码,您可以使用该密码以 Amazon Cognito 用户身份登录到 AWS AppSync 控制台。保存密码完成本教程的剩余部分。
创建您的 GraphQL API
要在 AWS AppSync 中创建 GraphQL API,请执行以下操作:
-
打开 AWS AppSync 控制台并选择从头开始构建,然后选择开始。
-
将 API 的名称设置为
AppSyncTutorial-PicturesViewer
。 -
选择创建。
AWS AppSync 控制台使用 API 密钥身份验证模式为您创建新的 GraphQL API。您可以根据本教程后面的说明,使用控制台设置 GraphQL API 的其余部分,并针对它运行查询。
配置 GraphQL API
您需要为 AWS AppSync API 配置刚创建的 Amazon Cognito 用户池。
-
选择设置选项卡。
-
在 Authorization Type (授权类型) 部分下,选择 Amazon Cognito User Pool (Amazon Cognito 用户池)。
-
在用户池配置下面,为 AWS 区域选择 US-WEST-2。
-
选择 AppSyncTutorial-UserPool 用户池。
-
选择 DENY 作为默认操作。
-
将 AppId client regex (AppId 客户端正则表达式) 字段保留为空。
-
选择保存。
此 API 现在设置为使用 Amazon Cognito 用户池作为其授权类型。
为 DynamoDB 表配置数据来源
在创建 DynamoDB 表后,在控制台中导航到您的 AWS AppSync GraphQL API 并选择数据来源选项卡。现在,您将在 AWS AppSync 中为刚创建的每个 DynamoDB 表创建一个数据来源。
-
选择 Data source (数据来源) 选项卡。
-
选择 New (新建) 创建新的数据来源。
-
对于数据来源名称,输入
PicturesDynamoDBTable
。 -
对于数据来源类型,选择 Amazon DynamoDB 表。
-
对于区域,选择 US-WEST-2。
-
从表列表中,选择 AppSyncTutorial-PicturesDynamoDB 表。
-
在创建或使用现有角色部分中,选择现有角色。
-
选择刚刚通过 CloudFormation 模板创建的角色。如果您未更改 ResourceNamePrefix,则角色的名称应为 AppSyncTutorial-DynamoDBRole。
-
选择创建。
对好友表重复相同的过程,DynamoDB 表的名称应为 AppSyncTutorial-Friends,前提是您在创建 CloudFormation 堆栈时未更改 ResourceNamePrefix 参数。
创建 GraphQL 架构
数据来源现已连接到您的 DynamoDB 表,让我们创建一个 GraphQL 架构。从 AWS AppSync 控制台的架构编辑器中,确保您的架构与以下架构匹配:
schema {
query: Query
mutation: Mutation
}
type Mutation {
createPicture(input: CreatePictureInput!): Picture!
@aws_auth(cognito_groups: ["Admins"])
createFriendship(id: ID!, target: ID!): Boolean
@aws_auth(cognito_groups: ["Admins"])
}
type Query {
getPicturesByOwner(id: ID!): [Picture]
@aws_auth(cognito_groups: ["Admins", "Viewers"])
}
type Picture {
id: ID!
owner: ID!
src: String
}
input CreatePictureInput {
owner: ID!
src: String!
}
选择 Save Schema (保存架构) 以保存您的架构。
已使用 @aws_auth 指令对一些架构字段进行注释。由于 API 默认操作配置设置为 DENY,因此此 API 将拒绝不属于 @aws_auth 指令中提及的组成员的所有用户。有关如何保护您的 API 的更多信息,您可以阅读安全性页面。在此情况下,仅管理员用户有权访问 Mutation.createPicture 和 Mutation.createFriendship 字段,而作为 Admins 或 Viewers 组成员的用户可访问 Query.getPicturesByOwner 字段。所有其他用户都没有访问权限。
配置解析器
现在,您有一个有效的 GraphQL 架构和两个数据来源,可以将解析器附加到架构上的 GraphQL 字段。此 API 提供以下功能:
-
通过 Mutation.createPicture 字段创建图片
-
通过 Mutation.createFriendship 字段创建友好关系
-
通过 Query.getPicture 字段检索图片
Mutation.createPicture
从 AWS AppSync 控制台的架构编辑器中,在右侧为 createPicture(input:
CreatePictureInput!): Picture!
选择附加解析器。选择 DynamoDB PicturesDynamoDBTable 数据来源。在 request mapping template (请求映射模板) 部分中,添加以下模板:
#set($id = $util.autoId())
{
"version" : "2018-05-29",
"operation" : "PutItem",
"key" : {
"id" : $util.dynamodb.toDynamoDBJson($id),
"owner": $util.dynamodb.toDynamoDBJson($ctx.args.input.owner)
},
"attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}
在 response mapping template (响应映射模板) 部分中,添加以下模板:
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($ctx.result)
创建图片功能已完成。将图片保存在图片表中,使用随机生成的 UUID 作为图片的 ID 并使用 Cognito 用户名作为图片拥有者。
Mutation.createFriendship
从 AWS AppSync 控制台的架构编辑器中,在右侧为 createFriendship(id:
ID!, target: ID!): Boolean
选择附加解析器。选择 DynamoDB FriendsDynamoDBTable 数据来源。在 request mapping template (请求映射模板) 部分中,添加以下模板:
#set($userToFriendFriendship = { "userId" : "$ctx.args.id", "friendId": "$ctx.args.target" })
#set($friendToUserFriendship = { "userId" : "$ctx.args.target", "friendId": "$ctx.args.id" })
#set($friendsItems = [$util.dynamodb.toMapValues($userToFriendFriendship), $util.dynamodb.toMapValues($friendToUserFriendship)])
{
"version" : "2018-05-29",
"operation" : "BatchPutItem",
"tables" : {
## Replace 'AppSyncTutorial-' default below with the ResourceNamePrefix you provided in the CloudFormation template
"AppSyncTutorial-Friends": $util.toJson($friendsItems)
}
}
重要提示:在 BatchPutItem 请求模板中,应该存在 DynamoDB 表的确切名称。默认表名称为 AppSyncTutorial-Friends。如果您使用错误的表名称,则将在 AppSync 尝试代入提供的角色时收到错误。
为了简化本教程,请将友好关系请求视为已批准,并将友好关系条目直接保存到 AppSyncTutorialFriends 表中。
实际上,您将为每个友好关系存储两个项目,因为此关系是双向的。有关表示多对多关系的 Amazon DynamoDB 最佳实践的更多详细信息,请参阅 DynamoDB 最佳实践。
在 response mapping template (响应映射模板) 部分中,添加以下模板:
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
true
注意:请确保请求模板包含正确的表名称。默认名称为 AppSyncTutorial-Friends,但如果您更改了 CloudFormation ResourceNamePrefix 参数,则表名称可能不同。
Query.getPicturesByOwner
现在,您已具有友好关系和图片,需要为用户提供查看其好友图片的功能。要满足此要求,您需要先确认请求者是拥有者的好友,最后查询图片。
由于此功能需要两个数据来源操作,因此您将创建两个函数。第一个函数 isFriend 将检查请求者和拥有者是否为好友。第二个函数 getPicturesByOwner 在给定所有者 ID 的情况下检索所请求的图片。让我们看看 Query.getPicturesByOwner 字段中有关建议的解析器的以下执行流:
-
之前映射模板:准备上下文和字段输入参数。
-
isFriend 函数:检查请求者是否为图片的拥有者。如果不是,它对 friends 表执行 DynamoDB GetItem 操作,以检查请求者和所有者用户是否为好友。
-
getPicturesByOwner 函数:对 owner-index 全局二级索引执行 DynamoDB 查询操作以从 Pictures 表中检索图片。
-
之后映射模板:映射图片结果,以便 DynamoDB 属性能够正确地映射到所需的 GraphQL 类型字段。
让我们先创建函数。
isFriend 函数
-
选择 Functions (函数) 选项卡。
-
选择 Create Function (创建函数) 以创建函数。
-
对于数据来源名称,输入
FriendsDynamoDBTable
。 -
对于函数名称,请输入 isFriend。
-
在请求映射模板文本区域内,粘贴以下模板:
#set($ownerId = $ctx.prev.result.owner) #set($callerId = $ctx.prev.result.callerId) ## if the owner is the caller, no need to make the check #if($ownerId == $callerId) #return($ctx.prev.result) #end { "version" : "2018-05-29", "operation" : "GetItem", "key" : { "userId" : $util.dynamodb.toDynamoDBJson($callerId), "friendId" : $util.dynamodb.toDynamoDBJson($ownerId) } }
-
在响应映射模板文本区域内,粘贴以下模板:
#if($ctx.error) $util.error("Unable to retrieve friend mapping message: ${ctx.error.message}", $ctx.error.type) #end ## if the users aren't friends #if(!$ctx.result) $util.unauthorized() #end $util.toJson($ctx.prev.result)
-
选择创建函数。
结果:您创建了 isFriend 函数。
getPicturesByOwner 函数
-
选择 Functions (函数) 选项卡。
-
选择 Create Function (创建函数) 以创建函数。
-
对于数据来源名称,输入
PicturesDynamoDBTable
。 -
对于函数名称,输入
getPicturesByOwner
。 -
在请求映射模板文本区域内,粘贴以下模板:
{ "version" : "2018-05-29", "operation" : "Query", "query" : { "expression": "#owner = :owner", "expressionNames": { "#owner" : "owner" }, "expressionValues" : { ":owner" : $util.dynamodb.toDynamoDBJson($ctx.prev.result.owner) } }, "index": "owner-index" }
-
在响应映射模板文本区域内,粘贴以下模板:
#if($ctx.error) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)
-
选择创建函数。
结果:您创建了 getPicturesByOwner 函数。现在已创建函数,请将管道解析器附加到 Query.getPicturesByOwner 字段。
从 AWS AppSync 控制台的架构编辑器中,在右侧为 Query.getPicturesByOwner(id: ID!): [Picture]
选择附加解析器。在以下页面上,选择数据来源下拉列表下显示的 Convert to pipeline resolver (转换为管道解析器) 链接。对之前映射模板使用以下过程:
#set($result = { "owner": $ctx.args.id, "callerId": $ctx.identity.username })
$util.toJson($result)
在 after mapping template (之后映射模板) 部分中,使用以下过程:
#foreach($picture in $ctx.result.items)
## prepend "src://" to picture.src property
#set($picture['src'] = "src://${picture['src']}")
#end
$util.toJson($ctx.result.items)
选择 Create Resolver (创建解析器)。您已成功附加您的首个管道解析器。在同一页上,添加您之前创建的两个函数。在函数部分中,选择 Add A Function (添加函数),然后选择或键入第一个函数的名称 isFriend。通过对 getPicturesByOwner 函数执行相同的过程来添加第二个函数。确保 isFriend 函数在列表中先于 getPicturesByOwner 函数显示。您可以使用向上和向下箭头在管道中重新排列函数的执行顺序。
现在,已创建管道解析器并且您已附加函数,下面让我们测试新创建的 GraphQL API。
测试您的 GraphQL API
首先,您需要通过使用创建的管理员用户执行一些变更来填充图片和友好关系。在 AWS AppSync 控制台左侧,选择查询选项卡。
createPicture 变更
-
在 AWS AppSync 控制台中,选择查询选项卡。
-
选择 Login With User Pools (使用用户池登录)。
-
在模态中,输入 CloudFormation 堆栈创建的 Cognito 示例客户端 ID,例如 37solo6mmhh7k4v63cqdfgdg5d。
-
输入您作为参数传递到 CloudFormation 堆栈的用户名。默认值为 nadia。
-
使用发送至您作为参数传递到 CloudFormation 堆栈的电子邮件(例如,UserPoolUserEmail)的临时密码。
-
选择登录。现在,您应看到重命名为 Logout nadia 的按钮,或您创建 CloudFormation 堆栈时选择的任何用户名(即 UserPoolUsername)。
让我们发送一些 createPicture 变更来填充“图片”表。在控制台中执行以下 GraphQL 查询:
mutation { createPicture(input:{ owner: "nadia" src: "nadia.jpg" }) { id owner src } }
响应看上去应与下内容类似:
{ "data": { "createPicture": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "owner": "nadia", "src": "nadia.jpg" } } }
让我们再添加几张图片:
mutation { createPicture(input:{ owner: "shaggy" src: "shaggy.jpg" }) { id owner src } }
mutation { createPicture(input:{ owner: "rex" src: "rex.jpg" }) { id owner src } }
您已以管理员用户身份使用 nadia 添加三张图片。
createFriendship 变更
让我们添加友好关系条目。在控制台中执行以下变更。
注意:您仍必须以管理员用户身份(默认管理员用户为 nadia)登录。
mutation {
createFriendship(id: "nadia", target: "shaggy")
}
响应应该类似于:
{
"data": {
"createFriendship": true
}
}
nadia 和 shaggy 是好友。rex 与任何人都不是好友。
getPicturesByOwner 查询
在此步骤中,以 nadia 用户身份使用 Cognito 用户池和本教程开头设置的凭证登录。以 nadia 身份检索 shaggy 拥有的图片。
query { getPicturesByOwner(id: "shaggy") { id owner src } }
由于 nadia 和 shaggy 是好友,因此查询应返回对应的图片。
{
"data": {
"getPicturesByOwner": [
{
"id": "05a16fba-cc29-41ee-a8d5-4e791f4f1079",
"owner": "shaggy",
"src": "src://shaggy.jpg"
}
]
}
}
同样,如果 nadia 尝试检索自己的图片,也会成功。管道解析器已经过优化来避免在此情况下运行 isFriend GetItem 操作。尝试以下查询:
query { getPicturesByOwner(id: "nadia") { id owner src } }
如果您在 API 中启用日志记录(在 Settings (设置) 窗格中),将调试级别设置为 ALL (所有),并再次运行相同的查询,则查询将返回字段执行的日志。通过查看日志,您可以确定 isFriend 函数是否在请求映射模板阶段提前返回:
{
"errors": [],
"mappingTemplateType": "Request Mapping",
"path": "[getPicturesByOwner]",
"resolverArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/types/Query/fields/getPicturesByOwner",
"functionArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/functions/o2f42p2jrfdl3dw7s6xub2csdfs",
"functionName": "isFriend",
"earlyReturnedValue": {
"owner": "nadia",
"callerId": "nadia"
},
"context": {
"arguments": {
"id": "nadia"
},
"prev": {
"result": {
"owner": "nadia",
"callerId": "nadia"
}
},
"stash": {},
"outErrors": []
},
"fieldInError": false
}
earlyReturnedValue 键表示 #return 指令所返回的数据。
最后,即使 rex 是 Viewers Cognito UserPool 组的成员,但由于 rex 不是任何人的好友,因此他无法访问 shaggy 或 nadia 拥有的任何图片。如果您以 rex 身份登录控制台并执行以下查询:
query { getPicturesByOwner(id: "nadia") { id owner src } }
您将收到以下未经授权错误:
{
"data": {
"getPicturesByOwner": null
},
"errors": [
{
"path": [
"getPicturesByOwner"
],
"data": null,
"errorType": "Unauthorized",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 9,
"sourceName": null
}
],
"message": "Not Authorized to access getPicturesByOwner on type Query"
}
]
}
您已使用管道解析器成功实现复杂的授权。