Quickstart


For the purpose of this quickstart, we will use TMDB (The Movie Database) APIs. We will create a new connector operation to Get Top rated movies that will use the /movie/top_rated endpoint from TMDB.

Prerequisites


  1. You have installed the cdk-cli by following the Introduction .
  2. You have obtained a namespace . You won't be able to deploy the connector otherwise. To get a namespace , please create a support ticket .
  3. Signup for a new account on TMDB (Skip this step if you already have one)
  4. Once logged in, create an API access token by going to the TMDB settings page
info

For generating an access token, you need to fill in the app details.

Here's an example of what you can fill in the app details:

tmdb-app-details

This will generate an access token for you. Copy this, and keep it ready for the next steps.

tmdb-access-token

Test the 3rd party API


Before we build an operation using CDK, we should test the API endpoint to view the standard request and response format.

Click on the Try it button on the endpoint reference page.

info

If you are logged in to TMDB, your API access token will be auto picked in the Try it console.

Note that not all 3rd party APIs have an in-built Try it console.

We recommend using tools like Postman to test the endpoints before building them with CDK.

tmdb-try-it-input-output

Create a new Connector project


Now open a terminal window in the folder where you want to build your TMDB connector project.

For example, this could be ~/projects/tray-connectors

Initialize project


Then run the following command:

tray-cdk init [CONNECTOR_NAME]

Where [CONNECTOR_NAME] is the name of your connector eg. tray-cdk init aadi-tmdb

info

We recommend doing the next steps in a terminal window within an IDE such as VS Code so you can visualize the folder structure.

Install dependencies


cd to the newly created connector folder e.g. cd aditya-tmdb

Now run:

npm i

This will install the project dependencies.

info

Tip: When intializing a project, you can simultaneously install the dependencies using the -i or --install flag e.g. tray-cdk init aadi-tmdb -i

Test your installation


Each time a project is initialized a test connector with one operation get_product is installed.

To test your setup, you can run:

npm test

You should see the following message in your terminal:

Copy
Copied
> aadi-tmdb@1.0.0 test
> jest --config ./jest.config.js

 PASS  src/get_product/handler.test.ts
  Operation get_product Test
    ✓ should get a product (353 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.316 s
Ran all test suites.

Delete the test operation

Delete get_product folder from your src.

This will remove the operation from your connector.

Add authentication


Upon initializing the connector two files were created in root of src folder:

  1. [Connector Name]Auth.ts : This will define the types for auth that is passed with the request.
  2. test.ctx.json : This will hold the the context variables including the test auth credentials.

Since TMDB uses token auth, we need to import TokenOperationHandlerAuth from the cdk-dsl

Copy
Copied
import { TokenOperationHandlerAuth } from "@trayio/cdk-dsl/connector/operation/OperationHandler";
info

CDK supports a variety of auth types including Token, OAuth1 and OAuth2. Command + click (CTRL + click for windows) on TokenOperationHandlerAuth to check the other auth types in the DSL.

Copy
Copied
export type TokenOperationHandlerAuth<USER, APP> = OperationHandlerAuth<
  USER,
  APP
> & {
  authType: "TOKEN",
};
export type Oauth1OperationHandlerAuth<USER, APP> = OperationHandlerAuth<
  USER,
  APP
> & {
  authType: "OAUTH1",
};
export type Oauth2OperationHandlerAuth<USER, APP> = OperationHandlerAuth<
  USER,
  APP
> & {
  authType: "OAUTH2",
};
export type Oauth2PasswordOperationHandlerAuth<USER, APP> =
  OperationHandlerAuth<USER, APP> & {
    authType: "OAUTH2_PASSWORD",
  };
export type Oauth2ClientCredentialsOperationHandlerAuth<USER, APP> =
  OperationHandlerAuth<USER, APP> & {
    authType: "OAUTH2_CLIENT_CREDENTIALS",
  };
export type BuiltinOperationHandlerAuth<USER, APP> = OperationHandlerAuth<
  USER,
  APP
> & {
  authType: "BUILTIN",
};

All auth types need to follow a predefined schema as shown here:

Copy
Copied
export type UserAuth = {
  //user credentials
}

export type AppAuth = {
  //OAuth app credentials
}

export type <Prefix for auth>Auth = TokenOperationHandlerAuth<UserAuth, AppAuth>

Since TMDB uses a token auth, we can leave AppAuth blank and token can be added under UserAuth.

Here's the full [Connector Name]Auth.ts file for reference:

Copy
Copied
import { TokenOperationHandlerAuth } from "@trayio/cdk-dsl/connector/operation/OperationHandler";

export type UserAuth = {
  access_token: string, //you can call this property anything e.g. token, auth_token etc.
};

export type AppAuth = {};

export type AadiTmdbAuth = TokenOperationHandlerAuth<UserAuth, AppAuth>;

Lastly, access_token has to be added to test.ctx.json for the test to run successfully:

Copy
Copied
{
  "auth": {
    "user": {
      "access_token": "<API Access Token>"
    }
  }
}

Add new operation


Add a new operation using:

tray-cdk add-operation get_top_rated_movies http

where get_top_rated_movies is the operation name and http is operation type.

This would add a new folder under src with the following files:

operation-folder

Now we are ready to modify these files to build our operation.

input.ts


This file will define the schema of the input that is needed for the operation

i.e. it will contain type definitions for any query, URI parameters or request body.

The input object should be blank as our endpoint does not require any inputs.

Here's the complete input.ts for your reference:

Copy
Copied
export type GetTopRatedMoviesInput = {};

output.ts


This file will define the structure of the output that the operation returns.

For building the output schema we can use the JSON output from the API Try it step.

Copy
Copied
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/tmU7GeKVybMWFButWEGl2M4GeiP.jpg",
      "genre_ids": [
        18,
        80
      ],
      "id": 238,
      "original_language": "en",
      "original_title": "The Godfather",
      "overview": "Spanning the years 1945 to 1955, a chronicle of the fictional Italian-American Corleone crime family. When organized crime family patriarch, Vito Corleone barely survives an attempt on his life, his youngest son, Michael steps in to take care of the would-be killers, launching a campaign of bloody revenge.",
      "popularity": 95.013,
      "poster_path": "/3bhkrj58Vtu7enYsRolD1fZdja1.jpg",
      "release_date": "1972-03-14",
      "title": "The Godfather",
      "video": false,
      "vote_average": 8.7,
      "vote_count": 18651
    },
    ...
    ...More movie records
    ...
  ],
  "total_pages": 576,
  "total_results": 11514
}

If you are familiar with Typescript, the above JSON would translate to the following types in Output.ts file:

Copy
Copied
//movie object
export type GetTopRatedMoviesObject = {
  adult: boolean,
  backdrop_path: string,
  genre_ids: number[],
  id: number,
  original_language: string,
  original_title: string,
  overview: string,
  popularity: number,
  poster_path: string,
  release_date: string,
  title: string,
  video: boolean,
  vote_average: number,
  vote_count: number,
};

export type GetTopRatedMoviesOutput = {
  page: number,
  results: GetTopRatedMoviesObject[], //array of movie objects
  total_pages: number,
  total_results: number,
};

You can copy paste the above snippet into your Output.ts.

handler.ts


This file will contain your main function that defines the operation.

Perform the following steps on this file:

  1. Replace the URL inside http.get() function with https://api.themoviedb.org/3/movie/top_rated
  2. Remove the line .addPathParameter('id', input.id.toString()) from the handleRequest function chain as our request doesn't need a URI parameter.
  3. The endpoint needs the bearer <token> to be passed as an Authorization header to the operation.

To add a bearer token with the request, simply add .withBearerToken(ctx.auth!.user.access_token) to the function chain.

info

Notice the ! sign in ctx.auth!.user.access_token. This tells the Typescript compiler that ctx.auth is non-null. Read more about Non-null assertion operator here.

Here's the complete handler.ts code for your reference:

Copy
Copied
import { OperationHandlerSetup } from '@trayio/cdk-dsl/connector/operation/OperationHandlerSetup';
import { AadiTmdbAuth } from '../AadiTmdbAuth'
import { GetTopRatedMoviesInput } from './input'
import { GetTopRatedMoviesOutput } from './output'

export const getTopRatedMoviesHandler =
    OperationHandlerSetup.configureHandler<AadiTmdbAuth, GetTopRatedMoviesInput, GetTopRatedMoviesOutput>((handler) =>
        handler.usingHttp((http) =>
            http.get('https://api.themoviedb.org/3/movie/top_rated')
                .handleRequest((ctx, input, request) =>
                    request.withBearerToken(ctx.auth!.user.access_token).withoutBody()
                )
                .handleResponse((ctx, input, response) => response.parseWithBodyAsJson())
        )
    );

handler.test.ts


This file will have functions that will be used to test your operation defined in handler.ts.

A simple test for this endpoint could be to check the total number of movies returned in the response.

As per the documentaion, the endpoint should return 20 movie objects by default.

We can test this with:

expect(outputValue.results.length).toEqual(20);

Here's the complete handler.test.ts code for your reference:

Copy
Copied
import { OperationHandlerTestSetup } from "@trayio/cdk-dsl/connector/operation/OperationHandlerTest";
import { OperationHandlerResult } from "@trayio/cdk-dsl/connector/operation/OperationHandler";
import { getTopRatedMoviesHandler } from "./handler";
import "@trayio/cdk-runtime/connector/operation/OperationHandlerTestRunner";

OperationHandlerTestSetup.configureHandlerTest(
  getTopRatedMoviesHandler,
  (handlerTest) =>
    handlerTest
      .usingHandlerContext("test")
      .nothingBeforeAll()
      .testCase("should return 20 movies", (testCase) =>
        testCase
          .givenNothing()
          .when(() => ({}))
          .then(({ output }) => {
            // console.log(output);
            const outputValue =
              OperationHandlerResult.getSuccessfulValueOrFail(output);
            expect(outputValue.results.length).toEqual(20);
          })
          .finallyDoNothing()
      )
      .nothingAfterAll()
);

Test the operation


You can test the operation now by:

tray-cdk test [OPERATION_NAME]

in our case this is, tray-cdk test get_top_rated_movies

Copy
Copied
> aadi-tmdb@1.0.0 test
> jest --config ./jest.config.js "get_top_rated_movies"

 PASS  src/get_top_rated_movies/handler.test.ts
  Operation get_top_rated_movies Test
    ✓ should return 20 results (83 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.154 s, estimated 2 s
Ran all test suites matching /get_top_rated_movies/i.
info

You can also do npm test which will test all operations in the connector.

In case your test results fail, you can visualize the actual API request that was made and the response that was received by logging either or both of them.

Here's how the operation function in handler.ts looks with logging enabled for both request and response:

Copy
Copied
export const getTopRatedMoviesHandler =
    OperationHandlerSetup.configureHandler<AadiTmdbAuth, GetTopRatedMoviesInput, GetTopRatedMoviesOutput>((handler) =>
        handler.usingHttp((http) =>
            http.get('https://api.themoviedb.org/3/movie/top_rated')
                .handleRequest((ctx, input, request) => {
                    console.log(request.withBearerToken(ctx.auth!.user.access_token).withoutBody()) //log the API request to console before sending
                    return request.withBearerToken(ctx.auth!.user.access_token).withoutBody()
                })
                .handleResponse((ctx, input, response) => {
                    console.log(response) //log the API response to console
                    return response.withBodyAsJson()
                })
        )
    );

Deploy the connector


warning

Before doing a deployment for an actual connector that you build, you should request for a 'namespace'.

Please raise a support ticket to get one for your org.

A 'namespace' is a unique name for grouping Tray organizations. Once you have a 'namespace' you will be able to share connectors between different Tray organizations assigned to you (e.g. cross region deployments)

Prerequisite (Create custom service)


Before you deploy the connector, you will have to create a custom service for it. You can also use an existing service (if you created one already).

The service can be created on the Tray UI. Go to the services tab on the app and click new service:

tmdb-service

Now add service details on the page as shown below:

tmdb-service-before-save

Now save the service. Upon saving, you will see the unique service name.

tmdb-service-name

Copy the unique service name (L3DJ7C5mqVj1yG_tmdb in the screeenshot above).

Now edit the service name in the connector.json file. Here is the full connector.json for refrence:

Copy
Copied
{
  "name": "aadi-tmdb", //unique name of the connector - Prefix this with your namespace
  "version": "1.0", //version of the connector
  "title": "Aadi tmdb", //Title of the connector on the Tray UI
  "description": "", //connector description visible on the Tray UI
  "service": {
    "name": "L3DJ7C5mqVj1yG_tmdb", //unique service name
    "version": "1"
  },
  "tags": ["service"],
  "isTrigger": false
}
info

You must prefix the connector name with your namespace. e.g. [Namespace]-tmdb

Deployment


To deploy the connector, you will need to set two environment variables.

  1. Run the following command to set TRAY_API_URL :

export TRAY_API_URL=https://api.tray.io

warning

The above URL (https://api.tray.io) is for the US region.

Your Tray account region is indicated in the Tray app URL.

US -> app.tray.io

EU -> app.eu1.tray.io

APAC -> app.ap1.tray.io

If you are deploying the connector in a different region, the URL would be as shown below:

EU -> https://api.eu1.tray.io

APAC -> https://api.ap1.tray.io

  1. Run the following command to set TRAY_API_TOKEN :

export TRAY_API_TOKEN=<TRAY_SESSION_TOKEN>

where TRAY_SESSION_TOKEN can be obtained from the Application tab in Dev tools on Tray UI as shown below:

tray-session-token

warning

If you are deploying in EU or APAC region, you need to copy the value of prod_tray_lid_eu or prod_tray_lid_ap respectively.

With the environment variables set now, you are ready to run the deploy command:

tray-cdk deploy

The command will execute the tests and then deploy the connector. You should see a response similar to the one shown here:

Copy
Copied
Running npm compile...

> aadi-tmdb@1.0.0 compile
> tsc --build && tscp >> /dev/null

Running npm test...

> aadi-tmdb@1.0.0 test
> jest --config ./jest.config.js

 PASS  src/get_top_rated_movies/handler.test.ts
  Operation get_top_rated_movies Test
    ✓ should return 20 movies (144 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.148 s, estimated 2 s
Ran all test suites.
Tests ran successfully
Connector Build Started
Generating schemas for operation get_top_rated_movies
Connector Build Finished
Connector Deploy Started
Connector Deploy Request Sent
Deployment [856b5518-6829-519f-9358-d34b7f848f07] is in progress

The connector should be available within the Tray app in a couple of minutes.

Now add the auth on the UI and do a test run for the operation:

tmdb-test-run-ui