Using a DDL (Dynamic dropdown list) operation


You will now use the DDL operation you built within another connector operation get_movies_by_genre

Prerequisites


  1. You have followed the walkthrough on Building a DDL operation

Testing 3rd party endpoints

To build the get_movies_by_genre operation, you will need the Discover Movies endpoint from the TMDB API.

The endpoint needs a comma-separated list of genre id. You can obtain id's from the response of Movie Genres endpoint you used to build the DDL.

You can pass those ids to Discover Movies endpoint as shown below:

tmdb-discover-movie

Upon hitting Try it!, you will get an array of movies belonging to the Genres specified.

Development


Now, with the information on inputs and outputs for the endpoint, you can initiate development for the operation get_movies_by_genre, which will use the ddl operation list_movie_genres to pass the id's of the genres.

Add operation


Add a new operation using:

tray-cdk connector add-operation get_movies_by_genre http

Where get_movies_by_genre is the operation name and http is the 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


The input object will include genre, and its type will be number (as that's the output value of the DDL).

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

Copy
Copied
export type GetMoviesByGenreInput = {
  /**
   * @title genre
   * @lookupOperation list_movie_genres_ddl
   * @lookupInput {}
   * @lookupAuthRequired true
   */
  genre: number,
};

Here, JSDoc annotation indicates that this field is a dropdown.

@title - display value of this field on the properties panel. @lookupOperation - DDL operation name whose response will feed options for the dropdown @lookupInput - Inputs required for making the call to the DDL operation. It is blank here as our DDL operation doesn't require any inputs. @lookupAuthRequired - If the DDL operation needs the auth to make the API call. In most cases, it will be true as you make calls to 3rd party APIs.

output.ts


We can use the JSON output from the Try it console to construct the output schema.

Copy
Copied
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/rMvPXy8PUjj1o8o1pzgQbdNCsvj.jpg",
      "genre_ids": [
        28,
        12,
        53
      ],
      "id": 299054,
      "original_language": "en",
      "original_title": "Expend4bles",
      "overview": "Armed with every weapon they can get their hands on and the skills to use them, The Expendables are the world’s last line of defense and the team that gets called when all other options are off the table. But new team members with new styles and tactics are going to give “new blood” a whole new meaning.",
      "popularity": 687.684,
      "poster_path": "/iwsMu0ehRPbtaSxqiaUDQB9qMWT.jpg",
      "release_date": "2023-09-15",
      "title": "Expend4bles",
      "video": false,
      "vote_average": 6.4,
      "vote_count": 835
    },
    ...
    ...Array of movie objects
  ]
}

Notice that this is the same as the response for get_top_rated_movies. Hence, we can copy over the same outputs.ts to this operation. Here is the full Output.ts file for reference:

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,
};

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 /3/discover/movie
  2. Remove the line .addPathParameter('id', input.id.toString()) from the handleRequest function chain, our request doesn't need a URI parameter.
  3. Add .addQueryString("with_genres", input.genre.toString()) to the function chain. It will pass genre as a query parameter with the API call.
  4. The endpoint needs the bearer <token> to be passed as an Authorization header to the operation.

Since we are using a globalConfig, We don't have to pass the auth for the request separately.

Alternatively, if you wish to override the globalConfig and add a bearer token with this request, you can simply add .withBearerToken(ctx.auth!.user.access_token) to the function chain.

info

Notice the ! sign in ctx.auth!.user.access_token. It 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 { GetMoviesByGenreInput } from "./input";
import { GetMoviesByGenreOutput } from "./output";
import { globalConfigHttp } from "../GlobalConfig";

export const getMoviesByGenreHandler =
  OperationHandlerSetup.configureHandler<AadiTmdbAuth, GetMoviesByGenreInput, GetMoviesByGenreOutput>
    ((handler) =>
      handler.withGlobalConfiguration(globalConfigHttp).usingHttp((http) =>
        http.get("https://api.themoviedb.org/3/discover/movie")
          .handleRequest((ctx, input, request) =>
            request
              .addQueryString("with_genres", input.genre.toString())
              .withBearerToken(ctx.auth!.user.access_token)
              .withoutBody()
          )
          .handleResponse((ctx, input, response) => response.parseWithBodyAsJson())
      )
    );

handler.test.ts


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

Per the documentation, the endpoint should return 20 movie objects per page.

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 { getMoviesByGenreHandler } from "./handler";
import "@trayio/cdk-runtime/connector/operation/OperationHandlerTestRunner";

OperationHandlerTestSetup.configureHandlerTest(
  getMoviesByGenreHandler,
  (handlerTest) =>
    handlerTest
      .usingHandlerContext("test")
      .nothingBeforeAll()
      .testCase("should return 20 movies", (testCase) =>
        testCase
          .givenNothing()
          .when(() => ({ genre: 28 })) //dummy genre value. 28 is the genre code for Action movies
          .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 connector test [OPERATION_NAME]

In our case, this is tray-cdk connector test get_movies_by_genre

Copy
Copied
> acme-tmdb@1.0.0 test
> jest --config ./jest.config.js "get_movies_by_genre"

 PASS  src/get_movies_by_genre/handler.test.ts
  Operation get_movies_by_genre 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_movies_by_genre/i.

Redeploy the connector


info

We assume you have deployed the connector once by following the Deployment section. If not, please go back and do it first.

Double-check if TRAY_API_URL and TRAY_API_TOKEN are present in the environment variables.

You can check these by doing echo "$TRAY_API_URL" or echo "$TRAY_API_TOKEN" respectively.

Now, you can redeploy the connector using:

tray-cdk deployment create

Use the connector on Tray UI


Wait a few minutes for the connector to show on the Tray UI with the new GetMoviesByGenre operation.

Now, you can test this operation on the UI by selecting a genre and adding an auth.

ddl-tray-ui-result