

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在中使用管道解析器 AWS AppSync
<a name="tutorial-pipeline-resolvers"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)的指南。

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

## 一键设置
<a name="one-click-setup"></a>

如果要在 AWS AppSync 配置了所有解析器和必要 AWS 资源的情况下自动设置 GraphQL 端点，则可以使用以下模板： AWS CloudFormation 

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml)

此堆栈在您的账户中创建以下资源：
+ 用于 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 保存密码完成本教程的剩余部分。

## 手动设置
<a name="manual-setup"></a>

如果您更喜欢通过 AWS AppSync控制台手动完成某个 step-by-step过程，请按照以下设置过程进行操作。

### 设置您的非 AWS AppSync 资源
<a name="setting-up-your-non-aws-appsync-resources"></a>

该 API 与两个 DynamoDB 表进行通信：**pictures** 表存储图片，**friends** 表存储用户之间的关系。此 API 配置为使用 Amazon Cognito 用户池作为身份验证类型。以下 CloudFormation 堆栈在账户中设置这些资源。

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml)

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

### 创建您的 GraphQL API
<a name="creating-your-graphql-api"></a>

要在以下位置创建 GraphQL API，请执行以下操作： AWS AppSync

1. 打开 AWS AppSync 控制台，选择 “**从头开始构建**”，然后选择 “**开始**”。

1. 将 API 的名称设置为 `AppSyncTutorial-PicturesViewer`。

1. 选择**创建**。

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

### 配置 GraphQL API
<a name="configuring-the-graphql-api"></a>

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

1. 选择**设置**选项卡。

1. 在 **Authorization Type (授权类型)** 部分下，选择 *Amazon Cognito User Pool (Amazon Cognito 用户池)*。

1. 在**用户池配置**下面，为 *AWS 区域*选择 **US-WEST-2**。

1. 选择 **AppSyncTutorial-UserPool** 用户池。

1. 选择 **DENY** 作为*默认操作*。

1. 将**AppId 客户端正则表达式**字段留空。

1. 选择**保存**。

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

### 为 DynamoDB 表配置数据来源
<a name="configuring-data-sources-for-the-ddb-tables"></a>

**创建 DynamoDB 表后，在控制台中导航到您的 AWS AppSync GraphQL API，然后选择 “数据源” 选项卡。**现在，你要在中 AWS AppSync 为刚刚创建的每个 DynamoDB 表创建一个数据源。

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

1. 选择 **New (新建)** 创建新的数据来源。

1. 对于数据来源名称，输入 `PicturesDynamoDBTable`。

1. 对于数据来源类型，选择 **Amazon DynamoDB 表**。

1. 对于区域，选择 **US-WEST-2**。

1. 从表格列表中，选择 **AppSyncTutorial-Pic** tures DynamoDB 表。

1. 在**创建或使用现有角色**部分中，选择**现有角色**。

1. 选择刚刚根据 CloudFormation 模板创建的角色。如果您没有更改 *ResourceNamePrefix*，则该角色的名称应为 **AppSyncTutorial-Dynamo DBRole**。

1. 选择**创建**。

对**好友**表重复相同的过程，如果您在创建堆栈时没有更改*ResourceNamePrefix*参数，则 DynamoDB 表的名称**AppSyncTutorial应**为-Friends。 CloudFormation 

### 创建 GraphQL 架构
<a name="creating-the-graphql-schema"></a>

数据来源现已连接到您的 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\$1auth* 指令对一些架构字段进行注释。由于 API 默认操作配置设置为 *DENY*，因此此 API 将拒绝不属于 *@aws\$1auth* 指令中提及的组成员的所有用户。有关如何保护您的 API 的更多信息，您可以阅读[安全性](security-authz.md#aws-appsync-security)页面。*在这种情况下，只有管理员用户才能访问 Mutation.createPict *ure 和 mutation.creat* *e* Friendship 字段，而属于*管理员*或查看者组成员的用户可以访问查询。* * getPicturesBy所有者*字段。所有其他用户都没有访问权限。

### 配置解析器
<a name="configuring-resolvers"></a>

现在，您有一个有效的 GraphQL 架构和两个数据来源，可以将解析器附加到架构上的 GraphQL 字段。此 API 提供以下功能：
+ 通过 *Mutation.createPicture* 字段创建图片
+ 通过 *Mutation.createFriendship* 字段创建友好关系
+ 通过 *Query.getPicture* 字段检索图片

#### Mutation.createPicture
<a name="mutation-createpicture"></a>

在 AWS AppSync 控制台的架构编辑器中，在右侧选择 Att **ach Resolver fo** `createPicture(input: CreatePictureInput!): Picture!` r。选择 DynamoDB *PicturesDynamoDBTable*数据源。在**请求映射模板**部分，添加以下模板：

```
#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)
}
```

在**响应映射模板**部分，添加以下模板：

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($ctx.result)
```

创建图片功能已完成。将图片保存在**图片**表中，使用随机生成的 UUID 作为图片的 ID 并使用 Cognito 用户名作为图片拥有者。

#### Mutation.createFriendship
<a name="mutation-createfriendship"></a>

在 AWS AppSync 控制台的架构编辑器中，在右侧选择 Att **ach Resolver fo** `createFriendship(id: ID!, target: ID!): Boolean` r。选择 DynamoDB **FriendsDynamoDBTable**数据源。在**请求映射模板**部分，添加以下模板：

```
#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-Fri* ends。如果您使用了错误的表名，则在 AppSync 尝试担任所提供的角色时会出现错误。

为了简化本教程，请像友谊请求已获批准一样继续操作，并将关系条目直接保存到**AppSyncTutorialFriends**表格中。

实际上，您将为每个友好关系存储两个项目，因为此关系是双向的。有关 many-to-many表现关系的 Amazon DynamoDB 最佳实践的更多详细信息，请参阅 D [ynamoD](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html) B 最佳实践。

在**响应映射模板**部分，添加以下模板：

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
true
```

注意：请确保请求模板包含正确的表名称。默认名称是 *AppSyncTutorial-Frien* ds，但是如果您更改了 CloudFormation **ResourceNamePrefix**参数，您的表名可能会有所不同。

#### 查询。 getPicturesBy所有者
<a name="query-getpicturesbyowner"></a>

现在，您已具有友好关系和图片，需要为用户提供查看其好友图片的功能。要满足此要求，您需要先确认请求者是拥有者的好友，最后查询图片。

由于此功能需要两个数据来源操作，因此您将创建两个函数。第一个函数 **isFriend** 将检查请求者和拥有者是否为好友。第二个函数 Owner 在给定**getPicturesBy所有者** ID 的情况下检索请求的照片。让我们来看看下面针对*查询中建议的解析器的执行流程。 getPicturesBy所有者*字段：

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

1. isFriend 函数：检查请求者是否为图片的拥有者。如果不是，它会通过在好友表上执行 D GetItem ynamoDB 操作来检查请求者和所有者用户是否是朋友。

1. getPicturesBy所有者函数：使用所有者索引全局二级索引上的 DynamoDB 查询操作从图片表*中*检索图片。

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

让我们先创建函数。

##### isFriend 函数
<a name="isfriend-function"></a>

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

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

1. 对于数据来源名称，输入 `FriendsDynamoDBTable`。

1. 对于函数名称，请输入 *isFriend*。

1. 在请求映射模板文本区域内，粘贴以下模板：

   ```
   #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)
       }
   }
   ```

1. 在响应映射模板文本区域内，粘贴以下模板：

   ```
   #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)
   ```

1. 选择**创建函数**。

结果：您创建了 **isFriend** 函数。

##### getPicturesBy所有者函数
<a name="getpicturesbyowner-function"></a>

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

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

1. 对于数据来源名称，输入 `PicturesDynamoDBTable`。

1. 对于函数名称，输入 `getPicturesByOwner`。

1. 在请求映射模板文本区域内，粘贴以下模板：

   ```
   {
       "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"
   }
   ```

1. 在响应映射模板文本区域内，粘贴以下模板：

   ```
   #if($ctx.error)
       $util.error($ctx.error.message, $ctx.error.type)
   #end
   
   $util.toJson($ctx.result)
   ```

1. 选择**创建函数**。

结果：您已创建**getPicturesBy所有者**函数。现在，函数已经创建完毕，请将管道解析器附加到*查询。 getPicturesBy所有者*字段。

在 AWS AppSync 控制台的架构编辑器中，在右侧选择 Att **ach Resolver fo** `Query.getPicturesByOwner(id: ID!): [Picture]` r。在以下页面上，选择数据来源下拉列表下显示的 **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**。按照与 Owner 函数相同的过程添加第二个**getPicturesBy函**数。确保 **isFriend** 函数首先出现在列表中，然后是**getPicturesBy所有者**函数。您可以使用向上和向下箭头在管道中重新排列函数的执行顺序。

现在，已创建管道解析器并且您已附加函数，下面让我们测试新创建的 GraphQL API。

## 测试您的 GraphQL API
<a name="testing-your-graphql-api"></a>

首先，您需要通过使用创建的管理员用户执行一些变更来填充图片和友好关系。在 AWS AppSync 控制台的左侧，选择 “**查询**” 选项卡。

### createPicture 变更
<a name="createpicture-mutation"></a>

1. 在 AWS AppSync 控制台中，选择 “**查询**” 选项卡。

1. 选择 **Login With User Pools (使用用户池登录)**。

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

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

1. 使用发送到您提供的电子邮件的临时密码作为 CloudFormation 堆栈的参数（例如 *UserPoolUserEmail*）。

1. 选择登录。现在，您应该会看到该按钮已重命名为 **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 变更
<a name="createfriendship-mutation"></a>

让我们添加友好关系条目。在控制台中执行以下变更。

注意：您仍必须以管理员用户身份（默认管理员用户为 **nadia**）登录。

```
mutation {
  createFriendship(id: "nadia", target: "shaggy")
}
```

响应应该类似于：

```
{
  "data": {
    "createFriendship": true
  }
}
```

 **nadia** 和 **shaggy** 是好友。**rex** 与任何人都不是好友。

### getPicturesBy所有者查询
<a name="getpicturesbyowner-query"></a>

在此步骤中，以 **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** 尝试检索自己的图片，也会成功。已对管道解析器进行了优化，以避免在这种情况下运行 GetItem isF **ri** end 操作。尝试以下查询：

```
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*钥表示 *\$1return* 指令返回的数据。

****最后，尽管**雷克斯**是 Viewers Cognito UserPool Group 的成员，而且由于**雷克斯**与任何人都不是朋友，因此他将无法访问 **shagg** y 或 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"
    }
  ]
}
```

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