教程:管道解析程序 - AWS AppSync
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

教程:管道解析程序

AWS AppSync 提供了一种通过单元解析程序将 GraphQL 字段连接到单个数据源的简单方法。但是,执行单个操作可能还不够。管道解析程序提供了对数据源连续执行操作的能力。在 API 中创建函数并将这些函数附加到管道解析程序。每个函数执行结果将通过管道传输到下一个函数,直到没有要执行的函数为止。利用管道解析程序,您现在可直接在 AWS AppSync 中构建更复杂的工作流程。在本教程中,您将构建一个简单的图片查看应用程序,用户可以在其中发布图片和查看其好友发布的图片。

一键设置

如果要在 AWS AppSync 中使用所有配置的解析程序和必要的 AWS 资源自动设置 GraphQL 终端节点,您可以使用以下 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 表进行通信:一个存储图片的图片表和一个存储用户之间的关系的好友表。此 API 配置为使用 Amazon Cognito 用户池作为身份验证类型。以下 AWS CloudFormation 堆栈在账户中设置这些资源。

在 AWS CloudFormation 堆栈创建过程结束时,对于您已创建的三个 Amazon Cognito 用户,您将分别收到一封电子邮件。每封电子邮件均包含一个临时密码,您可使用此密码以 Amazon Cognito 用户身份登录 AWS AppSync 控制台。保存密码完成本教程的剩余部分。

创建您的 GraphQL API

要在 AWS AppSync 中创建 GraphQL API:

  1. 打开 AWS AppSync 控制台并选择 Build From Scratch (从头构建),然后选择 Start (开始)

  2. 将 API 的名称设置为 AppSyncTutorial-PicturesViewer

  3. 选择 Create

AWS AppSync 控制台会使用 API 密钥身份验证模式为您创建新的 GraphQL API。您可以根据本教程后面的说明,使用控制台设置 GraphQL API 的其余部分,并针对它运行查询。

配置 GraphQL API

您需要使用刚创建的 Amazon Cognito 用户池配置 AWS AppSync API。

  1. 选择 Settings (设置) 选项卡。

  2. Authorization Type (授权类型) 部分下,选择 Amazon Cognito User Pool (Amazon Cognito 用户池)

  3. User Pool Configuration (用户池配置) 下,对于 AWS Region (AWS 区域),选择 US-WEST-2

  4. 选择 AppSyncTutorial-UserPool 用户池。

  5. 选择 DENY 作为默认操作

  6. AppId client regex (AppId 客户端正则表达式) 字段保留为空。

  7. 选择 Save

此 API 现在设置为使用 Amazon Cognito 用户池作为其授权类型。

为 DynamoDB 表配置数据源

创建 DynamoDB 表后,在控制台中导航到 AWS AppSync GraphQL API,选择 Data Sources (数据源) 选项卡。现在,您将在 AWS AppSync 中为您刚创建的每个 DynamoDB 表创建一个数据源。

  1. 选择 Data source (数据源) 选项卡。

  2. 选择 New (新建) 创建新的数据源。

  3. 对于数据源名称,输入 PicturesDynamoDBTable

  4. 对于数据源类型,选择 Amazon DynamoDB table (Amazon DynamoDB 表)

  5. 对于区域,选择 US-WEST-2

  6. 从表的列表中,选择 AppSyncTutorial-PicturesDynamoDB 表。

  7. Create or use an existing role (创建或使用现有角色) 部分选择 Existing role (现有角色)

  8. 选择刚刚通过 CloudFormation 模板创建的角色。如果您未更改 ResourceNamePrefix,则角色的名称应为 AppSyncTutorial-DynamoDBRole

  9. 选择 Create

好友表重复相同的过程,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.createPictureMutation.createFriendship 字段,而作为 AdminsViewers 组成员的用户可访问 Query.getPicturesByOwner 字段。所有其他用户都没有访问权限。

配置解析程序

现在,您有一个有效的 GraphQL 架构和两个数据源,可以将解析程序附加到架构上的 GraphQL 字段。此 API 提供以下功能:

  • 通过 Mutation.createPicture 字段创建图片

  • 通过 Mutation.createFriendship 字段创建友好关系

  • 通过 Query.getPicture 字段检索图片

Mutation.createPicture

在 AWS AppSync 控制台的架构编辑器中,在右侧为 createPicture(input: CreatePictureInput!): Picture! 选择 Attach Resolver (附加解析程序)。选择 DynamoDBPicturesDynamoDBTable 数据源。在 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 选择 Attach Resolver (附加解析程序)。选择 DynamoDBFriendsDynamoDBTable 数据源。在 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 字段中有关建议的解析程序的以下执行流:

  1. 之前映射模板:准备上下文和字段输入参数。

  2. isFriend 函数:检查请求者是否为图片的拥有者。如果不是,它将通过对“好友”表执行 DynamoDB GetItem 操作来检查请求者和拥有者是否为好友。

  3. getPicturesByOwner 函数:对 owner-index 全局二级索引执行 DynamoDB Query 操作来检索“图片”表中的图片。

  4. 之后映射模板:映射图片结果,以便 DynamoDB 属性能够正确地映射到所需的 GraphQL 类型字段。

让我们先创建函数。

isFriend 函数
  1. 选择 Functions (函数) 选项卡。

  2. 选择 Create Function (创建函数) 以创建函数。

  3. 对于数据源名称,输入 FriendsDynamoDBTable

  4. 对于函数名称,请输入 isFriend

  5. 在请求映射模板文本区域内,粘贴以下模板:

    #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) } }
  6. 在响应映射模板文本区域内,粘贴以下模板:

    #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)
  7. 选择 Create Function (创建函数)

结果:您创建了 isFriend 函数。

getPicturesByOwner 函数
  1. 选择 Functions (函数) 选项卡。

  2. 选择 Create Function (创建函数) 以创建函数。

  3. 对于数据源名称,输入 PicturesDynamoDBTable

  4. 对于函数名称,输入 getPicturesByOwner

  5. 在请求映射模板文本区域内,粘贴以下模板:

    { "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" }
  6. 在响应映射模板文本区域内,粘贴以下模板:

    #if($ctx.error) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)
  7. 选择 Create Function (创建函数)

结果:您创建了 getPicturesByOwner 函数。现在已创建函数,请将管道解析程序附加到 Query.getPicturesByOwner 字段。

在 AWS AppSync 控制台的架构编辑器中,在右侧为 Query.getPicturesByOwner(id: ID!): [Picture] 选择 Attach Resolver (附加解析程序)。在以下页面上,选择数据源下拉列表下显示的 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 控制台的左侧,选择 Queries (查询) 选项卡。

createPicture 更改

  1. 在 AWS AppSync 控制台中,选择 Queries (查询) 选项卡。

  2. 选择 Login With User Pools (使用用户池登录)

  3. 在模态中,输入 CloudFormation 堆栈创建的 Cognito 示例客户端 ID,例如 37solo6mmhh7k4v63cqdfgdg5d。

  4. 输入您作为参数传递到 CloudFormation 堆栈的用户名。默认值为 nadia

  5. 使用发送至您作为参数传递到 CloudFormation 堆栈的电子邮件(例如,UserPoolUserEmail)的临时密码。

  6. 选择“Login (登录)”。现在,您应看到重命名为 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 } }

nadiashaggy 是好友。rex 与任何人都不是好友。

getPicturesByOwner 查询

在此步骤中,以 nadia 用户身份使用 Cognito 用户池和本教程开头设置的凭证登录。以 nadia 身份检索 shaggy 拥有的图片。

query { getPicturesByOwner(id: "shaggy") { id owner src } }

由于 nadiashaggy 是好友,因此查询应返回对应的图片。

{ "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 (所有),并再次运行相同的查询,则查询将返回字段执行的日志。通过查看日志,您可以确定 Request Mapping Template (请求映射模板) 阶段的早期是否返回了 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 指令所返回的数据。

最后,即使 rexViewers Cognito UserPool 组的成员,但由于 rex 不是任何人的好友,因此他无法访问 shaggynadia 拥有的任何图片。如果您以 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" } ] }

您已使用管道解析程序成功实现复杂的授权。