

# Connecting to Asana
<a name="connecting-to-asana"></a>

Asana is a cloud-based team collaboration solution that helps teams organize, plan, and complete tasks and projects. If you're an Asana user, your account contains data about your workspaces, projects, tasks, teams, and more. You can transfer data from Asana to certain AWS services or other supported applications.

**Topics**
+ [AWS Glue support for Asana](asana-support.md)
+ [Policies containing the API operations for creating and using connections](asana-configuring-iam-permissions.md)
+ [Configuring Asana](asana-configuring.md)
+ [Configuring Asana connections](asana-configuring-connections.md)
+ [Reading from Asana entities](asana-reading-from-entities.md)
+ [Asana connection options](asana-connection-options.md)
+ [Creating an Asana account](asana-create-account.md)
+ [Limitations](asana-connector-limitations.md)

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

AWS Glue supports Asana as follows:

**Supported as a source?**  
Yes. You can use AWS Glue ETL jobs to query data from Asana.

**Supported as a target?**  
No.

**Supported Asana API versions**  
 1.0 

# Policies containing the API operations for creating and using connections
<a name="asana-configuring-iam-permissions"></a>

The following sample policy describes the required AWS permissions for creating and using connections. If you are creating a new role, create a policy that contains the following: 

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

****  

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

------

If you don't want to use the preceding method, alternatively, use the following managed IAM policies:
+  [ AWSGlueServiceRole ](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole) – Grants access to resources that various AWS Glue processes require to run on your behalf. These resources include AWS Glue, Amazon S3, IAM, CloudWatch Logs, and Amazon EC2. If you follow the naming convention for resources specified in this policy, AWS Glue processes have the required permissions. This policy is typically attached to roles specified when defining crawlers, jobs, and development endpoints. 
+  [ AWSGlueConsoleFullAccess ](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess) – Grants full access to AWS Glue resources when an identity that the policy is attached to uses the AWS Management console. If you follow the naming convention for resources specified in this policy, users have full console capabilities. This policy is typically attached to users of the AWS Glue console. 

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

Before you can use AWS Glue to transfer from Asana, you must meet the following requirements:

## Minimum requirements
<a name="asana-configuring-min-requirements"></a>
+ You have an Asana account with email and password. For more information about creating an account, see [Creating a Asana account](asana-create-account.md). 
+ You must have AWS Account created with the service access to AWS Glue. 
+ Ensure you have created one of the following resources in your Asana account: 
  + A Developer App that supports `OAuth 2.0` authentication. For more instuctions instructions, see [OAuth](https://developers.asana.com/docs/oauth) in the Asana Developers documentation. Alternatively, see [Creating an Asana account](asana-create-account.md). 
  + A personal access token. For more information, see the Personal access token[https://developers.asana.com/docs/personal-access-token](https://developers.asana.com/docs/personal-access-token) in the Asana Developers documentation. 

If you meet these requirements, you’re ready to connect AWS Glue to your Adobe Analytics account. For typical connections, you don't need do anything else in Adobe Analytics.

# Configuring Asana connections
<a name="asana-configuring-connections"></a>

Asana supports the `AUTHORIZATION_CODE` grant type for `OAuth2`. 

This grant type is considered “three-legged” `OAuth` as it relies on redirecting users to the third-party authorization server to authenticate the user. Users may opt to create their own connected app in Asana and provide their own client ID and client secret when creating connections through the AWS Glue console. In this scenario, they will still be redirected to Asana to login and authorize AWS Glue to access their resources. 

This grant type results in a refresh token and access token. The access token is short lived, and may be refreshed automatically without user interaction using the refresh token. 

For public Asana documentation on creating a connected app for `AUTHORIZATION_CODE OAuth` flow, see [Asana APIs](https://developers.asana.com/docs/oauth) . 

To configure a Asana connection:

1. In AWS Secrets Manager, create a secret with the following details: 
   + For customer managed connected app – Secret should contain the connected app Consumer Secret with `USER_MANAGED_CLIENT_APPLICATION_CLIENT_SECRET` as key. 
   + 
**Note**  
You must create a secret for the connection in AWS Glue.

1. In AWS Glue Studio, create a connection under **Data Connections** by following the steps below: 

   1. When selecting a **Connection type**, select Asana.

   1. Provide the Asana environment.

   1. Select the IAM role for which AWS Glue can assume and has permissions for following actions: 

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

****  

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

------

   1. Select the `secretName` that you want to use for this connection in AWS Glue to put the tokens. 

   1.  Select the network options if you want to use your network. 

1. Grant the IAM role associated with your AWS Glue job permission to read `secretName`. 

# Reading from Asana entities
<a name="asana-reading-from-entities"></a>

 **Prerequisites** 

An Asana Object you would like to read from. Refer the supported entities table below to check the available entities. 

 **Supported entities for source** 


| Entity | Can be Filtered | Supports Limit | Supports Order By | Supports Select \$1 | Supports Partitioning | 
| --- | --- | --- | --- | --- | --- | 
|  Workspace  | No | Yes | No | Yes | No | 
| Tag | No | Yes | No | Yes | No | 
| User | No | Yes | No | Yes | No | 
|  Portfolio  | No | Yes | No | Yes | No | 
| Team | No | Yes | No | Yes | No | 
| Project | Yes | Yes | No | Yes | No | 
| Section | No | Yes | No | Yes | No | 
| Task  | Yes | No | No | Yes | Yes | 
| Goal | Yes | Yes | No | Yes | No | 
|  AuditLogEvent  | Yes | Yes | No | Yes | No | 
|  Status Update  | Yes | Yes | No | Yes | No | 
|  Custom Field  | No | Yes | No | Yes | No | 
|  Project Brief  | Yes | No | No | Yes | Yes | 

 **Example** 

```
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 entity and field details** 
+ [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)

 **Partitioning queries** 

Additional spark options `PARTITION_FIELD`, `LOWER_BOUND`, `UPPER_BOUND`, `NUM_PARTITIONS` can be provided if you want to utilize concurrency in Spark. With these parameters, the original query would be split into `NUM_PARTITIONS` number of sub-queries that can be executed by spark tasks concurrently. 
+ `PARTITION_FIELD`: the name of the field to be used to partition query. 
+ `LOWER_BOUND`: an inclusive lower bound value of the chosen partition field. 

  For date, we accept the Spark date format used in Spark SQL queries. Example of valid values: `2024-06-07T13:30:00.134Z`. 
+ `UPPER_BOUND`: an exclusive upper bound value of the chosen partition field. 
+ `NUM_PARTITIONS`: number of partitions. 

 Entity-wise partitioning field support details are captured in the following table. 


| Entity Name | Partitioning Field | Data Type | 
| --- | --- | --- | 
| Task |  created\$1at  | DateTime | 
| Task |  modified\$1at  | DateTime | 

 **Example** 

```
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 connection options
<a name="asana-connection-options"></a>

The following are connection options for Asana:
+  `ENTITY_NAME`(String) – (Required) Used for Read/Write. The name of your Object in Asana. 
+  `API_VERSION`(String) – (Required) Used for Read/Write. Asana Rest API version you want to use. For example: 1.0. 
+  `SELECTED_FIELDS`(List<String>) – Default: empty(SELECT \$1). Used for Read. Columns you want to select for the object. 
+  `FILTER_PREDICATE`(String) – Default: empty. Used for Read. It should be in the Spark SQL format. 
+  `QUERY`(String) – Default: empty. Used for Read. Full Spark SQL query. 
+ `PARTITION_FIELD`(String) - Used for Read. Field to be used to partition query. 
+  `LOWER_BOUND`(String)- Used for Read. An inclusive lower bound value of the chosen partition field. 
+  `UPPER_BOUND`(String) - Used for Read. An exclusive upper bound value of the chosen partition field. 
+  `NUM_PARTITIONS`(Integer) - Default: 1. Used for Read. Number of partitions for read. 

# Creating an Asana account
<a name="asana-create-account"></a>

1. Sign up for an [Asana Account](https://asana.com/create-account) and choose **Sign Up**.

1. After logging in, you will be redirected to the [Account Setup](https://app.asana.com/0/account_setup) page. Complete the following steps:
   + Review the account setup form.
   + Fill in all the relevant details to create your Asana account.
   + Double-check the information for accuracy.

1. Choose **Create Account** or **Submit** (the exact button text may vary) to finalize your account setup.

**Creating the App in Asana for `OAuth2.0`**

1. Log in to Asana account using your [Asana Customer Credentials](https://app.asana.com/-/login). 

1. Choose your user profile icon in the top-right corner and select **My Settings** from the dropdown menu.

1. Select the **Apps** tab and then select **Manage Developer Apps**.

1. Select **Create new app** and enter the relevant details. 

1. Choose **Create Apps**.

1. On the **My Apps** page: 

   1. Select **OAuth** and in the **App Credentials** section, make a note of your Client ID and Client Secret.

   1. In the **Redirect URLs** section, add the necessary redirect URL(s).
**Note**  
Enter the Redirect URI using this format: `https://{aws-region-code}.console.aws.amazon.com/gluestudio/oauth`. Example: For the US East (N. Virginia), use: `https://us-east-1.console.aws.amazon.com/gluestudio/oauth`

**Creating the App in Asana for `PAT` Token**

1. Log in to Asana account using your [Asana Customer Credentials](https://app.asana.com/-/login). 

1. Choose on your user profile icon in the top-right corner and select **My Profile Settings** from the dropdown menu.

1. Select the **Apps** tab and then select **Service accounts**.

1. Select **Create new app** and enter the relevant details. 

1. Choose **Add service account**.

1. The next page displays your token, copy your token and store it securely. 
**Important**  
This token will only be displayed once. Ensure you copy it and store it securely. 

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

The following are limitations for the Asana connector:
+ Service Accounts in Enterprise Domains can only access audit log API endpoints. Authentication with a Service Account's personal access token is required to access these endpoints.
+ The Goal entity can only be accessed for user accounts with Premium plan or above.
+ `Audit Log Event Entity` – In the connector, `start_at` and `end_at` fields are combined into a single field "start\$1end\$1at" to support filtering and incremental transfer.
+ Partitioning cannot be supported for the `Date` field, even though it supports greater-than-or-equal-to and less-than-or-equal-to operators. Scenario: Created a job with `partitionField` as `due_on` (datatype: date), `lowerBound` as `2019-09-14`, `upperBound` as `2019-09-16`, and `numPartition` as `2`. The filter part of the endpoint URL is created as follows:
  + 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:
  + In partition1, we get data with due\$1date as 2019-09-14 and 2019-09-15
  + In partition2, we get the same data with due\$1date as 2019-09-15 (which was in partition1) along with other data, causing data duplication.
+ Filtering and partitioning cannot be supported on the same field as a bad request error is thrown from the SaaS end.
+ The Task entity requires a minimum of 1 field in filter criteria. There is a limitation with Asana where pagination is not identified without sorting the records based on a time-based field. Hence, the Created\$1at field is used along with pagination to distinguish the next set of records. The Created\$1at field is marked as mandatory in the filter, with a default value of 2000-01-01T00:00:00Z if not provided. For more information about Pagination, see [Tasks in a workspace](https://developers.asana.com/reference/searchtasksforworkspace).