# Transformation Action


RudderStack's [Transformation Action](https://github.com/rudderlabs/rudder-transformation-action-code/) lets you create, update, test, and publish transformations and libraries written in JavaScript or Python - directly from your development repository. Internally, this action leverages the [Transformations API]({{< ref "api/transformation-api.md" >}}).

{{< info >}}
This action is currently available for GitHub only.
{{< /info >}}

## Prerequisites

- You will need the email address associated with your RudderStack workspace.
- In your RudderStack workspace, create a [workspace-level Service Access Token]({{< ref "access-management/service-access-tokens.md#generate-service-access-token" >}}) with the following [permissions]({{< ref "access-management/policies-overview.md#resource-permissions" >}}):

| Resource | Permission | <div style="width:350px">Description</div> | 
| :----| :-----| :-----|
| Transformations | **Edit** |  Make changes to the configuration of transformations  | 
| Transformations | **Connect** | Connect a transformation to a destination | 
| Transformations | **Create & Delete** | Create or delete transformations | 
| Transformation Libraries | **Edit** | Make changes to the configuration of transformation libraries | 

{{< details "**Click here to see how these permissions appear in the workspace policy**." >}}
<br />

{{< figure src="images/data-governance/transformation-action-sat-permissions.webp" alt="RudderStack permissions for SAT" >}}

{{< /details >}}
<br />

{{< warning >}}
For security purposes, RudderStack recommends using [GitHub secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) to store your Service Access Token.
{{< /warning >}}

#### Token permissions for legacy RBAC system

If you are on the [legacy Permissions Management (RBAC) system]({{< ref "archive/dashboard-guides/user-management.md" >}}), your workspace-level Service Access Token should have **Admin** role with **Grant edit access** setting toggled on under **Transformations**.

See [this documentation]({{< ref "archive/dashboard-guides/service-access-tokens.md#generate-service-access-token" >}}) for more information on generating the token.

{{< image src="images/access-management/permissions/legacy/admin-transformations.webp" alt="workspace-level Service Access Token with Transformations Admin permission" >}}

## Usage

{{< info >}}
See the [sample workflow](https://github.com/rudderlabs/rudder-transformation-action-code/blob/main/.github/workflows/rudderTransformation.yml) for more information on using the action.
{{< /info >}}

```yaml
name: RudderStack Transformer Test and Publish
uses: rudderlabs/rudder-transformation-action@<current_action_version>
with:
    metaPath: './code/meta.json'
    email: 'test@rudderlabs.com'
    accessToken: ${{ secrets.ACCESS_TOKEN }}
    uploadTestArtifact: true
```

Specify the email address and Service Access Token obtained in the [Prerequisites](#prerequisites) section in the `email` and `accessToken` fields respectively.

See [FAQ](#faq) for more information on the `uploadTestArtifact` parameter.

## Action fields

| Field | Description |
| :----| :------------|
| `metapath` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Path to the meta file that: <br /><br /><ul><li>Lets the action know about what transformations/libraries to test based on the input events and the expected output.</li><li>Publishes the transformations/libraries if the test passes.</li></ul>See [Meta file structure](#meta-file-structure) for more information. |
| `email` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Email address associated with the RudderStack workspace. | 
| `accessToken` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Your Service Access Token obtained from the RudderStack dashboard. |
| `uploadTestArtifact` | Boolean flag that determines:<br /><br /><ul><li>Whether to upload the individual transformation outputs after running the transformation on the test events.</li><li>The difference from the expected output.</li></ul>See [`uploadTestArtifact` parameter](#uploadtestartifact-parameter) for more information. <br /><br />**Default value**: False |

## Meta file structure

As mentioned above, a meta file lets the transformation action know what transformations or libraries to test depending on the input events and the expected output.

A generic meta file schema is shown below:

```json
// Meta file schema
{
  "transformations": <array of transformation schema>,
  "libraries": <array of library schema>
}
```

A sample meta file is shown below:

```json
 // example meta.json
 {
   "transformations": [
    {
       "file": "./code/t1.js",
       "name": "action-T1",
       "language": "javascript",
       "description": "action-T1",
       "test-input-file": "./code/testevents.json",
       "expected-output": "./code/expectedoutput.json"
     },
     {
       "file": "./code/t2.py",
       "name": "action-T2",
       "language": "pythonfaas",
       "description": "action-T2",
       "test-input-file": "./code/events.json",
       "expected-output": "./code/expected.json"
     }
   ],
   "libraries": [{
       "file": "./code/lib1.js",
       "name": "lib1",
       "language": "javascript",
       "description": "action-lib1"
     },
     {
       "file": "./code/lib2.py",
       "name": "getFinanceDataPy",
       "language": "pythonfaas",
       "description": "Python library to get finance data"
     }
   ]
 }
```

{{< warning >}}
The path mentioned in the `file` field should be relative to the base repository path.
{{< /warning >}}

### Transformation schema

The `transformations` parameter in the meta file contains an array of transformation schemas. A single transformation schema contains the following parameters:

| Parameter | Description |
| :---- | :----- |
| `file` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Path to the transformation code. |
| `name` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Transformation name. |
| `language` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Transformation language. Permissible values are `javascript` and `pythonfaas`. |
| `description` | Transformation description. |
| `test-input-file` | Path to the JSON file containing an array of events to test the transformation. |
| `expected-output` | Path to the JSON file containing the expected output for the above input, after running the transformation code. | 

### Library schema

The `libraries` parameter in the meta file contains an array of library schemas. A single library schema contains the following parameters:

| Parameter | Description |
| :---- | :----- |
| `file` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Path to the library code. |
| `name` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Library name that you must specify to import it in any transformation. |
| `language` </br><span style="color: #4D4DFF;font-size:12px;">Required</span> | Library language. Permissible values are `javascript` and `pythonfaas`. |
| `description` | Library description. |

## FAQ

#### **How do I use the `uploadTestArtifact` parameter?**

When you set `uploadTestArtifact` to `true` in the transformation action, RudderStack:

1. Runs the transformation on the test events present in [`test-input-file`](#transformation-schema).
2. Generates and stores the output in a **test-outputs** folder contained within a zipped artifact.
3. Validates the output against the contents of the file specified in [`expected-output`](#transformation-schema). 

{{< info >}}
Note that:

- RudderStack stores the output from Step 1 in a `[transformation name in camel case]_output.json` file.
- It stores the validation differences from Step 3 in the respective `[transformation name in camel case]_diff.json` file.
- If you do not set `uploadTestArtifact` to `true` in the transformation action, RudderStack **does not** create the output zip containing the above files.

For more context, see the use case below.
{{< /info >}}

Consider the following transformation schema:

```json
// single transformationSchema
{
  "file": "./code/t1.js",
  "name": "action-T1",
  "language": "javascript",
  "description": "action-T1",
  "test-input-file": "./code/testevents.json",
  "expected-output": "./code/expectedoutput.json"
}
```

The workflow when you set `uploadTestArtifact` to `true` is as follows:

1. RudderStack runs the transformation `action-T1`(with its code present in `t1.js`) on the `testevents.json` file.
2. RudderStack then:

    1. Generates and stores the output from Step 1 in a file named `actionT1_output.json`. This file is stored in a **test-outputs** folder contained within a zipped artifact.
    2. Validates the output with the contents present in the `expectedoutput.json` file. It stores any differences in a file named `actionT1_diff.json`.

{{< info >}}
The **test-outputs** folder contains the two files - `actionT1_output.json` and `actionT1_diff.json`.
{{< /info >}}
