

# 连接到 Asana
<a name="connecting-to-asana"></a>

Asana 是一款基于云的团队协作解决方案，旨在帮助团队组织、计划和完成任务及项目。如果是 Asana 用户，则账户包含有关工作空间、项目、任务、团队等的数据。可以将数据从 Asana 传输到某些 AWS 服务或其他受支持的应用程序。

**Topics**
+ [AWS Glue 支持 Asana](asana-support.md)
+ [包含创建和使用连接的 API 操作的策略](asana-configuring-iam-permissions.md)
+ [配置 Asana](asana-configuring.md)
+ [配置 Asana 连接](asana-configuring-connections.md)
+ [从 Asana 实体读取内容](asana-reading-from-entities.md)
+ [Asana 连接选项](asana-connection-options.md)
+ [创建 Asana 账户](asana-create-account.md)
+ [限制](asana-connector-limitations.md)

# AWS Glue 支持 Asana
<a name="asana-support"></a>

AWS Glue 对 Asana 的支持情况如下：

**是否支持作为来源？**  
是。可以使用 AWS Glue ETL 作业查询 Asana 中的数据。

**是否支持作为目标？**  
否。

**支持的 Asana API 版本**  
 1.0 

# 包含创建和使用连接的 API 操作的策略
<a name="asana-configuring-iam-permissions"></a>

以下示例策略描述了创建和使用连接所需的 AWS 权限。如果您要创建新角色，请创建包含以下内容的策略：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "glue:ListConnectionTypes",
        "glue:DescribeConnectionType",
        "glue:RefreshOAuth2Tokens",
        "glue:ListEntities",
        "glue:DescribeEntity"
      ],
      "Resource": "*"
    }
  ]
}
```

------

如果不想使用前述方法，也可以使用以下托管 IAM 策略：
+  [AWSGlueServiceRole](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole)：授予对各种 AWS Glue 进程代表您运行所需的资源的访问权限。这些资源包括 AWS Glue、Amazon S3、IAM、CloudWatch Logs 和 Amazon EC2。如果您遵循此策略中指定的资源的命名约定，则 AWS Glue 进程具有所需的权限。此策略通常附加到在定义爬网程序、作业和开发终端节点时指定的角色。
+  [AWSGlueConsoleFullAccess](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess)：策略所附加到的身份使用 AWS 管理控制台时，授予对 AWS Glue 资源的完全访问权限。如果遵循此策略中指定的资源的命名约定，则用户具有完全控制台功能。此策略通常附加到 AWS Glue 控制台的用户。

# 配置 Asana
<a name="asana-configuring"></a>

必须满足以下要求，才能使用 AWS Glue 从 Asana 传输数据：

## 最低要求
<a name="asana-configuring-min-requirements"></a>
+ 拥有一个包含电子邮件和密码的 Asana 账户。有关创建账户的更多信息，请参阅[创建 Asana 账户](asana-create-account.md)。
+ 必须创建一个可以访问 AWS Glue 服务的 AWS 账户。
+ 确保已在 Asana 账户中创建以下某一项资源：
  + 支持 `OAuth 2.0` 身份验证的开发人员应用程序。有关更多说明，请参阅 Asana 开发人员文档中的 [OAuth](https://developers.asana.com/docs/oauth)。或者，请参阅 [创建 Asana 账户](asana-create-account.md)。
  + 个人访问令牌。有关更多信息，请参见 Asana 开发人员文档中的个人访问令牌[https://developers.asana.com/docs/personal-access-token](https://developers.asana.com/docs/personal-access-token)。

如果满足这些要求，就可以将 AWS Glue 连接到 Adobe Analytics 账户。对于常见连接，无需在 Adobe Analytics 中执行其他操作。

# 配置 Asana 连接
<a name="asana-configuring-connections"></a>

Asana 支持 `OAuth2` 的 `AUTHORIZATION_CODE` 授权类型。

此授权类型被视为“三足型”`OAuth`，因为它依赖于将用户重定向到第三方授权服务器来对用户进行身份验证。用户可以选择在 Asana 中创建自己的关联应用程序，并在通过 AWS Glue 控制台创建连接时，提供自己的客户端 ID 和客户端密钥。在这种情况下，他们仍会重定向到 Asana，以便登录并授权 AWS Glue 访问其资源。

此授权类型会生成刷新令牌和访问令牌。访问令牌的有效期很短，可以通过刷新令牌在不需要用户干预的情况下自动刷新。

有关为 `AUTHORIZATION_CODE OAuth` 流创建关联应用程序的 Asana 公共文档，请参阅 [Asana API](https://developers.asana.com/docs/oauth)。

要配置 Asana 连接，请执行以下操作：

1. 在 AWS Secrets Manager 中，创建一个包含以下详细信息的密钥：
   + 对于客户托管的关联应用程序：密钥应包含关联应用程序的消费者密钥，并将 `USER_MANAGED_CLIENT_APPLICATION_CLIENT_SECRET` 作为键。
   + 
**注意**  
必须在 AWS Glue 中为连接创建一个密钥。

1. 在 AWS Glue Studio 中，按照以下步骤在**数据连接**下创建连接：

   1. 选择**连接类型**时，请选择 Asana。

   1. 提供 Asana 环境。

   1. 选择 AWS Glue 可以代入并有权执行以下操作的 IAM 角色：

------
#### [ JSON ]

****  

      ```
      {
        "Version":"2012-10-17",		 	 	 
        "Statement": [
          {
            "Effect": "Allow",
            "Action": [
              "secretsmanager:DescribeSecret",
              "secretsmanager:GetSecretValue",
              "secretsmanager:PutSecretValue",
              "ec2:CreateNetworkInterface",
              "ec2:DescribeNetworkInterfaces",
              "ec2:DeleteNetworkInterface"
            ],
            "Resource": "*"
          }
        ]
      }
      ```

------

   1. 在 AWS Glue 中选择要用于此连接的 `secretName`，然后放置令牌。

   1.  如果要使用网络，请选择网络选项。

1. 向与您的 AWS Glue 作业关联的 IAM 角色授予读取 `secretName` 的权限。

# 从 Asana 实体读取内容
<a name="asana-reading-from-entities"></a>

 **先决条件** 

要从中读取内容的 Asana 对象。请参阅下面的支持的实体表，查看可用的实体。

 **源支持的实体** 


| 实体 | 可以筛选 | 支持限制 | 支持排序依据 | 支持 Select \$1 | 支持分区 | 
| --- | --- | --- | --- | --- | --- | 
|  Workspace  | 否 | 是 | 否 | 是 | 否 | 
| Tag | 否 | 是 | 否 | 是 | 否 | 
| 用户 | 否 | 是 | 否 | 是 | 否 | 
|  项目组合  | 否 | 是 | 否 | 是 | 否 | 
| Team | 否 | 是 | 否 | 是 | 否 | 
| Project | 支持 | 是 | 否 | 是 | 否 | 
| 板块 | 否 | 是 | 否 | 是 | 否 | 
| 任务  | 是 | 否 | 否 | 是 | 是 | 
| Goal | 支持 | 是 | 否 | 是 | 否 | 
|  AuditLogEvent  | 支持 | 是 | 否 | 是 | 否 | 
|  状态更新  | 支持 | 是 | 否 | 是 | 否 | 
|  自定义字段  | 否 | 是 | 否 | 是 | 否 | 
|  项目简介  | 是 | 否 | 否 | 是 | 是 | 

 **示例** 

```
read_read = glueContext.create_dynamic_frame.from_options(
    connection_type="Asana",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "task/workspace:xxxx",
        "API_VERSION": "1.0",
        "PARTITION_FIELD": "created_at",
        "LOWER_BOUND": "2024-02-05T14:09:30.115Z",
        "UPPER_BOUND": "2024-06-07T13:30:00.134Z",
        "NUM_PARTITIONS": "3"
    }
```

 **Asana 实体和字段详细信息** 
+ [Workspace](https://developers.asana.com/docs/workspaces)
+ [Tag](https://developers.asana.com/docs/tags)
+ [User](https://developers.asana.com/docs/users)
+ [Portfolio](https://developers.asana.com/docs/portfolios)
+ [Team](https://developers.asana.com/docs/teams)
+ [Project](https://developers.asana.com/docs/get-all-projects-in-a-workspace)
+ [Section](https://developers.asana.com/docs/get-sections-in-a-project)
+ [Task](https://developers.asana.com/docs/search-tasks-in-a-workspace) 
+ [Goal](https://developers.asana.com/docs/get-goals)
+ [AuditLogEvent](https://developers.asana.com/docs/audit-log-api)
+ [Status Update](https://developers.asana.com/reference/status-updates)
+ [Custom Field](https://developers.asana.com/reference/custom-fields)
+ [Project Brief](https://developers.asana.com/reference/project-briefs)

 **对查询进行分区** 

如果您想在 Spark 中利用并发，可以提供附加 Spark 选项 `PARTITION_FIELD`、`LOWER_BOUND`、`UPPER_BOUND` 和 `NUM_PARTITIONS`。使用这些参数，原始查询将被拆分为 `NUM_PARTITIONS` 个子查询，这些子查询可以由 Spark 任务同时执行。
+ `PARTITION_FIELD`：用于对查询进行分区的字段的名称。
+ `LOWER_BOUND`：所选分区字段的包含下限值。

  对于日期，我们接受 Spark SQL 查询中使用的 Spark 日期格式。有效值示例：`2024-06-07T13:30:00.134Z`。
+ `UPPER_BOUND`：所选分区字段的排除上限值。
+ `NUM_PARTITIONS`：分区的数量。

 基于实体的分区字段支持详细信息如下表中所示。


| 实体名称 | 分区字段 | 数据类型 | 
| --- | --- | --- | 
| Task |  created\$1at  | 日期时间 | 
| Task |  modified\$1at  | 日期时间 | 

 **示例** 

```
read_read = glueContext.create_dynamic_frame.from_options(
    connection_type="Asana",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "task/workspace:xxxx",
        "API_VERSION": "1.0",
        "PARTITION_FIELD": "created_at",
        "LOWER_BOUND": "2024-02-05T14:09:30.115Z",
        "UPPER_BOUND": "2024-06-07T13:30:00.134Z",
        "NUM_PARTITIONS": "3"
    }
```

# Asana 连接选项
<a name="asana-connection-options"></a>

以下是 Asana 的连接选项：
+  `ENTITY_NAME`（字符串）：（必填）用于读取/写入。Asana 中对象的名称。
+  `API_VERSION`（字符串）：（必填）用于读取/写入。要使用的 Asana Rest API 版本。例如：1.0。
+  `SELECTED_FIELDS`（列表<字符串>）– 默认：empty(SELECT \$1)。用于读取。您想要为对象选择的列。
+  `FILTER_PREDICATE`（字符串）– 默认：空。用于读取。应采用 Spark SQL 格式。
+  `QUERY`（字符串）– 默认：空。用于读取。完整的 Spark SQL 查询。
+ `PARTITION_FIELD`（字符串）– 用于读取。用于分区查询的字段。
+  `LOWER_BOUND`（字符串）– 用于读取。所选分区字段的包含下限值。
+  `UPPER_BOUND`（字符串）– 用于读取。所选分区字段的排除上限值。
+  `NUM_PARTITIONS`（整数）– 默认：1。用于读取。要读取的分区数。

# 创建 Asana 账户
<a name="asana-create-account"></a>

1. 注册 [Asana 账户](https://asana.com/create-account)，然后选择**注册**。

1. 登录后，将重定向到[账户设置](https://app.asana.com/0/account_setup)页面。完成以下步骤：
   + 查看账户设置表。
   + 填写所有相关详细信息来创建 Asana 账户。
   + 仔细检查信息是否准确。

1. 选择**创建账户**或**提交**（确切的按钮文本可能有所不同）完成账户设置。

**在 Asana 中为 `OAuth2.0` 创建应用程序**

1. 使用 [Asana 客户凭证](https://app.asana.com/-/login)登录 Asana 账户。

1. 选择右上角的用户配置文件图标，然后从下拉菜单中选择**我的设置**。

1. 选择**应用程序**选项卡，然后选择**管理开发人员应用程序**。

1. 选择**创建新应用程序**，然后输入相关详细信息。

1. 选择**创建应用程序**。

1. 在**我的应用程序**页面上，执行以下操作：

   1. 选择 **OAuth**，然后记下**应用程序凭证**部分中的客户端 ID 和客户端密钥。

   1. 在**重定向 URL** 部分中，添加所需的重定向 URL。
**注意**  
使用以下格式输入重定向 URI：`https://{aws-region-code}.console.aws.amazon.com/gluestudio/oauth`。例如，对于美国东部（弗吉尼亚州北部）区域，使用：`https://us-east-1.console.aws.amazon.com/gluestudio/oauth`

**在 Asana 中为 `PAT` 令牌创建应用程序**

1. 使用 [Asana 客户凭证](https://app.asana.com/-/login)登录 Asana 账户。

1. 选择右上角的用户配置文件图标，然后从下拉菜单中选择**我的配置文件设置**。

1. 选择**应用程序**选项卡，然后选择**服务账户**。

1. 选择**创建新应用程序**，然后输入相关详细信息。

1. 选择**添加服务账户**。

1. 下一页会显示令牌，请复制并安全存储令牌。
**重要**  
此令牌只会显示一次。务必复制此令牌并安全存储。

# 限制
<a name="asana-connector-limitations"></a>

以下是 Asana 连接器的限制：
+ 企业域中的服务账户只能访问审核日志 API 端点。需要使用服务账户的个人访问令牌进行身份验证后才能访问这些端点。
+ 只有高级方案或更高方案的用户账户才能访问“目标”实体。
+ `Audit Log Event Entity`：在连接器中，`start_at` 和 `end_at` 字段合并为一个字段（即 start\$1end\$1at）来支持筛选和增量传输。
+ `Date` 字段不支持分区，尽管它支持“大于或等于”和“小于或等于”运算符。场景：创建一个作业，其中 `partitionField` 为 `due_on`（数据类型：日期）、`lowerBound` 为 `2019-09-14`、`upperBound` 为 `2019-09-16` 且 `numPartition` 为 `2`。端点 URL 的筛选条件部分按如下方式创建：
  + partition1：due\$1on.before=2019-09-14&due\$1on.after=2019-09-14
  + partition2：due\$1on.before=2019-09-15&due\$1on.after=2019-09-15 Output:
  + 在 partition1 中，得到 due\$1date 为 2019-09-14 和 2019-09-15 的数据
  + 在 partition2 中，得到与 due\$1date 为 2019-09-15（partition1 中）相同的数据以及其他数据，导致数据重复。
+ 由于 SaaS 端引发了不正确的请求错误，无法在同一字段上支持筛选和分区。
+ “任务”实体要求筛选条件中至少有 1 个字段。Asana 存在一个限制，即如果不根据基于时间的字段对记录进行排序，则无法识别分页。因此，Created\$1at 字段与分页一起用于区分下一组记录。Created\$1at 字段在筛选条件中标记为必填项，如果未提供此值，则默认值为 2000-01-01T00:00:00Z。有关分页的更多信息，请参阅 [Tasks in a workspace](https://developers.asana.com/reference/searchtasksforworkspace)。