From 2b4131ce0163c0aeb46370d995fd00fc3ee293b8 Mon Sep 17 00:00:00 2001 From: Stenio Wagner Date: Sun, 3 Dec 2023 12:49:24 -0300 Subject: [PATCH 1/3] feat: refactoring movie-details --- .../handlers/movie-details/index.ts | 2 - .../movie-details/movie-details.handler.ts | 17 -------- .../handlers/movies/details/index.ts | 2 + .../details}/movie-details.handler.test.ts | 10 ++--- .../movies/details/movie-details.handler.ts | 14 +++++++ .../details}/movie-details.types.ts | 0 .../the-movie-db-api/handlers/movies/index.ts | 1 + .../resolvers/movie-details.resolvers.ts | 40 ++++++++++--------- 8 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movie-details/index.ts delete mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.ts create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/details/index.ts rename src/graphql/datasources/the-movie-db-api/handlers/{movie-details => movies/details}/movie-details.handler.test.ts (92%) create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.ts rename src/graphql/datasources/the-movie-db-api/handlers/{movie-details => movies/details}/movie-details.types.ts (100%) create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/index.ts b/src/graphql/datasources/the-movie-db-api/handlers/movie-details/index.ts deleted file mode 100644 index ed9a514..0000000 --- a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { handler as movieDetailsHandler } from "./movie-details.handler"; -export * as MovieDetailsTypes from "./movie-details.types"; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.ts b/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.ts deleted file mode 100644 index 1b7f9c1..0000000 --- a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CONSTANTS as TMDB_CONSTANTS } from "@tmdb-api/utils"; -import TheMovieDBAPI from "@tmdb-api/tmdb-movie-db-api"; -import { QueryMovieArgs } from "@generated-types"; -import * as TMDBApiErrors from "@/graphql/errors/tmdb-api"; - -export const handler = { - handle: async (params: QueryMovieArgs, tmdbAPI: TheMovieDBAPI) => { - const endpoint = `movie/${params.id}`; - const response = await tmdbAPI.handle(endpoint, { - language: params.language ?? TMDB_CONSTANTS.FALLBACK_LANGUAGE, - }); - if (!response) { - throw new TMDBApiErrors.QueryMovieError(params.id); - } - return response; - }, -}; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/details/index.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/index.ts new file mode 100644 index 0000000..89d4229 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/index.ts @@ -0,0 +1,2 @@ +export { handler } from "./movie-details.handler"; +export * as types from "./movie-details.types"; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.test.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.test.ts similarity index 92% rename from src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.test.ts rename to src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.test.ts index 63f1ee6..3064235 100644 --- a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.handler.test.ts +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.test.ts @@ -3,7 +3,7 @@ import { Iso6391Language } from "@generated-types"; import { CONSTANTS as TMDBAPI_CONSTANS } from "@tmdb-api/utils"; import * as TMDBApiErrors from "@/graphql/errors/tmdb-api"; -import * as fixtures from "../../../../../../__test__/datasources/tmdb-api/fixtures"; +import * as fixtures from "../../../../../../../__test__/datasources/tmdb-api/fixtures"; import { handler } from "./movie-details.handler"; const ID = 1; @@ -30,7 +30,7 @@ describe("DataSources/TheMovieDBApi/Movie-Details-Query-Handler", () => { const tmdbAPI = new TheMovieDBAPI(); mockGet.mockReturnValueOnce(fixtures.movie); const language = Iso6391Language.Pt; - await handler.handle( + await handler( { language, id: ID, @@ -49,7 +49,7 @@ describe("DataSources/TheMovieDBApi/Movie-Details-Query-Handler", () => { it('should call "RESTDatasource.get" correctly', async () => { const tmdbAPI = new TheMovieDBAPI(); mockGet.mockReturnValueOnce(fixtures.movie); - await handler.handle( + await handler( { id: ID, }, @@ -67,7 +67,7 @@ describe("DataSources/TheMovieDBApi/Movie-Details-Query-Handler", () => { it("should return the data correctly", async () => { mockGet.mockReturnValueOnce(fixtures.movie); const tmdbAPI = new TheMovieDBAPI(); - const result = await handler.handle( + const result = await handler( { id: ID, }, @@ -81,7 +81,7 @@ describe("DataSources/TheMovieDBApi/Movie-Details-Query-Handler", () => { it('should throw "QueryTrendingTVShowsError"', async () => { const tmdbAPI = new TheMovieDBAPI(); await expect(() => - handler.handle( + handler( { id: ID, }, diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.ts new file mode 100644 index 0000000..a40e243 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.handler.ts @@ -0,0 +1,14 @@ +import { CONSTANTS as TMDB_CONSTANTS } from "@tmdb-api/utils"; +import TheMovieDBAPI from "@tmdb-api/tmdb-movie-db-api"; +import { QueryMovieArgs } from "@generated-types"; +import * as TMDBApiErrors from "@/graphql/errors/tmdb-api"; + +export const handler = async (params: QueryMovieArgs, tmdbAPI: TheMovieDBAPI) => { + const response = await tmdbAPI.handle(`movie/${params.id}`, { + language: params.language ?? TMDB_CONSTANTS.FALLBACK_LANGUAGE, + }); + if (!response) { + throw new TMDBApiErrors.QueryMovieError(params.id); + } + return response; +}; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.types.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.types.ts similarity index 100% rename from src/graphql/datasources/the-movie-db-api/handlers/movie-details/movie-details.types.ts rename to src/graphql/datasources/the-movie-db-api/handlers/movies/details/movie-details.types.ts diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts new file mode 100644 index 0000000..ea50354 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts @@ -0,0 +1 @@ +export * as movieDetails from "./details"; diff --git a/src/graphql/resolvers/movie-details.resolvers.ts b/src/graphql/resolvers/movie-details.resolvers.ts index 111bd3b..c1a1953 100644 --- a/src/graphql/resolvers/movie-details.resolvers.ts +++ b/src/graphql/resolvers/movie-details.resolvers.ts @@ -1,41 +1,43 @@ -import { movieDetailsHandler, MovieDetailsTypes } from "@tmdb-api/handlers/movie-details"; +import { movieDetails } from "@/graphql/datasources/the-movie-db-api/handlers/movies"; import { QueryMovieArgs } from "@generated-types"; import { Context } from "@/types"; export const resolvers = { Query: { movie: async (_parent: undefined, params: QueryMovieArgs, context: Context) => - movieDetailsHandler.handle(params, context.tmdbAPI), + movieDetails.handler(params, context.tmdbAPI), }, MovieProductionCompany: { - logoPath: (parent: MovieDetailsTypes.ProductionCompany) => parent.logo_path, - originCountry: (parent: MovieDetailsTypes.ProductionCompany) => parent.origin_country, + logoPath: (parent: movieDetails.types.ProductionCompany) => parent.logo_path, + originCountry: (parent: movieDetails.types.ProductionCompany) => + parent.origin_country, }, MovieBelongsToCollection: { - posterPath: (parent: MovieDetailsTypes.BelongsToCollection) => parent.poster_path, - backdropPath: (parent: MovieDetailsTypes.BelongsToCollection) => parent.backdrop_path, + posterPath: (parent: movieDetails.types.BelongsToCollection) => parent.poster_path, + backdropPath: (parent: movieDetails.types.BelongsToCollection) => + parent.backdrop_path, }, Movie: { - backdropPath: (parent: MovieDetailsTypes.Response) => parent.backdrop_path, - belongsToCollection: (parent: MovieDetailsTypes.Response) => + backdropPath: (parent: movieDetails.types.Response) => parent.backdrop_path, + belongsToCollection: (parent: movieDetails.types.Response) => parent.belongs_to_collection, - genres: (parent: MovieDetailsTypes.Response) => + genres: (parent: movieDetails.types.Response) => parent.genres.map((genre) => genre.name), - imdbId: (parent: MovieDetailsTypes.Response) => parent.imdb_id, - originalLanguage: (parent: MovieDetailsTypes.Response) => parent.original_language, - originalTitle: (parent: MovieDetailsTypes.Response) => parent.original_title, - posterPath: (parent: MovieDetailsTypes.Response) => parent.poster_path, - productionCompanies: (parent: MovieDetailsTypes.Response) => + imdbId: (parent: movieDetails.types.Response) => parent.imdb_id, + originalLanguage: (parent: movieDetails.types.Response) => parent.original_language, + originalTitle: (parent: movieDetails.types.Response) => parent.original_title, + posterPath: (parent: movieDetails.types.Response) => parent.poster_path, + productionCompanies: (parent: movieDetails.types.Response) => parent.production_companies, - productionCountries: (parent: MovieDetailsTypes.Response) => + productionCountries: (parent: movieDetails.types.Response) => parent.production_countries.map((productionCountry) => productionCountry.name), - releaseDate: (parent: MovieDetailsTypes.Response) => parent.release_date, - spokenLanguages: (parent: MovieDetailsTypes.Response) => + releaseDate: (parent: movieDetails.types.Response) => parent.release_date, + spokenLanguages: (parent: movieDetails.types.Response) => parent.spoken_languages.map((spokenLanguage) => spokenLanguage.name), - voteAverage: (parent: MovieDetailsTypes.Response) => parent.vote_average, - voteCount: (parent: MovieDetailsTypes.Response) => parent.vote_count, + voteAverage: (parent: movieDetails.types.Response) => parent.vote_average, + voteCount: (parent: movieDetails.types.Response) => parent.vote_count, }, }; From 2471822ed7ebd253acb2e0249bfcc30c27b2eb65 Mon Sep 17 00:00:00 2001 From: Stenio Wagner Date: Sun, 3 Dec 2023 13:22:42 -0300 Subject: [PATCH 2/3] feat: add movie-details-videos --- graphql.schema.json | 53 +++++++++++++++++++ src/generated/graphql.ts | 8 +++ .../the-movie-db-api/handlers/movies/index.ts | 5 +- .../handlers/movies/videos/index.ts | 2 + .../movies/videos/movie-videos.handler.ts | 17 ++++++ .../movies/videos/movie-videos.types.ts | 19 +++++++ .../resolvers/movie-details.resolvers.ts | 43 ++++++++------- src/graphql/resolvers/tv-show.resolvers.ts | 2 +- .../type-defs/schemas/tmdb/movie.schema.ts | 1 + 9 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/videos/index.ts create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.ts create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.types.ts diff --git a/graphql.schema.json b/graphql.schema.json index 15441cb..98a640f 100644 --- a/graphql.schema.json +++ b/graphql.schema.json @@ -2158,6 +2158,59 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "videos", + "description": null, + "args": [ + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "language", + "description": null, + "type": { + "kind": "ENUM", + "name": "ISO6391Language", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "MediaVideo", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "voteAverage", "description": null, diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index b1e6c4d..62a6752 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -234,10 +234,17 @@ export type Movie = { tagline?: Maybe; title?: Maybe; video?: Maybe; + videos: Array; voteAverage?: Maybe; voteCount?: Maybe; }; + +export type MovieVideosArgs = { + id: Scalars['Int']['input']; + language?: InputMaybe; +}; + export type MovieBelongsToCollection = { __typename?: 'MovieBelongsToCollection'; backdropPath?: Maybe; @@ -1292,6 +1299,7 @@ export type MovieResolvers, ParentType, ContextType>; title?: Resolver, ParentType, ContextType>; video?: Resolver, ParentType, ContextType>; + videos?: Resolver, ParentType, ContextType, RequireFields>; voteAverage?: Resolver, ParentType, ContextType>; voteCount?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts index ea50354..d55e7a5 100644 --- a/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/index.ts @@ -1 +1,4 @@ -export * as movieDetails from "./details"; +import * as details from "./details"; +import * as videos from "./videos"; + +export { details, videos }; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/index.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/index.ts new file mode 100644 index 0000000..7cc23e7 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/index.ts @@ -0,0 +1,2 @@ +export { handler } from "./movie-videos.handler"; +export * as types from "./movie-videos.types"; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.ts new file mode 100644 index 0000000..7867d05 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.ts @@ -0,0 +1,17 @@ +import TheMovieDBAPI from "@tmdb-api/tmdb-movie-db-api"; +import { CONSTANTS as TMDB_CONSTANS } from "@tmdb-api/utils"; + +import { Response } from "./movie-videos.types"; +import { Params } from "./movie-videos.types"; + +const ACCEPTED_SITES = ["YouTube"]; + +export const handler = async (params: Params, tmdbAPI: TheMovieDBAPI) => { + const response = await tmdbAPI.handle(`movie/${params.id}/videos`, { + language: params.language ?? TMDB_CONSTANS.FALLBACK_LANGUAGE, + }); + if (!response) { + return []; + } + return response.results.filter((result) => ACCEPTED_SITES.includes(result.site)); +}; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.types.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.types.ts new file mode 100644 index 0000000..d566ba1 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.types.ts @@ -0,0 +1,19 @@ +export { MovieVideosArgs as Params } from "@generated-types"; + +export type Result = { + iso_639_1: string; + iso_3166_1: string; + name: string; + key: string; + site: string; + size: number; + type: string; + official: boolean; + published_at: string; + id: string; +}; + +export type Response = { + id: number; + results: Result[]; +}; diff --git a/src/graphql/resolvers/movie-details.resolvers.ts b/src/graphql/resolvers/movie-details.resolvers.ts index c1a1953..7022654 100644 --- a/src/graphql/resolvers/movie-details.resolvers.ts +++ b/src/graphql/resolvers/movie-details.resolvers.ts @@ -1,43 +1,48 @@ -import { movieDetails } from "@/graphql/datasources/the-movie-db-api/handlers/movies"; +import * as movies from "@/graphql/datasources/the-movie-db-api/handlers/movies"; import { QueryMovieArgs } from "@generated-types"; import { Context } from "@/types"; export const resolvers = { Query: { movie: async (_parent: undefined, params: QueryMovieArgs, context: Context) => - movieDetails.handler(params, context.tmdbAPI), + movies.details.handler(params, context.tmdbAPI), }, MovieProductionCompany: { - logoPath: (parent: movieDetails.types.ProductionCompany) => parent.logo_path, - originCountry: (parent: movieDetails.types.ProductionCompany) => + logoPath: (parent: movies.details.types.ProductionCompany) => parent.logo_path, + originCountry: (parent: movies.details.types.ProductionCompany) => parent.origin_country, }, MovieBelongsToCollection: { - posterPath: (parent: movieDetails.types.BelongsToCollection) => parent.poster_path, - backdropPath: (parent: movieDetails.types.BelongsToCollection) => + posterPath: (parent: movies.details.types.BelongsToCollection) => parent.poster_path, + backdropPath: (parent: movies.details.types.BelongsToCollection) => parent.backdrop_path, }, Movie: { - backdropPath: (parent: movieDetails.types.Response) => parent.backdrop_path, - belongsToCollection: (parent: movieDetails.types.Response) => + backdropPath: (parent: movies.details.types.Response) => parent.backdrop_path, + belongsToCollection: (parent: movies.details.types.Response) => parent.belongs_to_collection, - genres: (parent: movieDetails.types.Response) => + genres: (parent: movies.details.types.Response) => parent.genres.map((genre) => genre.name), - imdbId: (parent: movieDetails.types.Response) => parent.imdb_id, - originalLanguage: (parent: movieDetails.types.Response) => parent.original_language, - originalTitle: (parent: movieDetails.types.Response) => parent.original_title, - posterPath: (parent: movieDetails.types.Response) => parent.poster_path, - productionCompanies: (parent: movieDetails.types.Response) => + imdbId: (parent: movies.details.types.Response) => parent.imdb_id, + originalLanguage: (parent: movies.details.types.Response) => parent.original_language, + originalTitle: (parent: movies.details.types.Response) => parent.original_title, + posterPath: (parent: movies.details.types.Response) => parent.poster_path, + productionCompanies: (parent: movies.details.types.Response) => parent.production_companies, - productionCountries: (parent: movieDetails.types.Response) => + productionCountries: (parent: movies.details.types.Response) => parent.production_countries.map((productionCountry) => productionCountry.name), - releaseDate: (parent: movieDetails.types.Response) => parent.release_date, - spokenLanguages: (parent: movieDetails.types.Response) => + releaseDate: (parent: movies.details.types.Response) => parent.release_date, + spokenLanguages: (parent: movies.details.types.Response) => parent.spoken_languages.map((spokenLanguage) => spokenLanguage.name), - voteAverage: (parent: movieDetails.types.Response) => parent.vote_average, - voteCount: (parent: movieDetails.types.Response) => parent.vote_count, + voteAverage: (parent: movies.details.types.Response) => parent.vote_average, + voteCount: (parent: movies.details.types.Response) => parent.vote_count, + videos: ( + _: movies.details.types.Response, + params: movies.videos.types.Params, + context: Context, + ) => movies.videos.handler(params, context.tmdbAPI), }, }; diff --git a/src/graphql/resolvers/tv-show.resolvers.ts b/src/graphql/resolvers/tv-show.resolvers.ts index fca4c01..d5936db 100644 --- a/src/graphql/resolvers/tv-show.resolvers.ts +++ b/src/graphql/resolvers/tv-show.resolvers.ts @@ -1,8 +1,8 @@ import { TvShowImagesArgs, TvShowSimilarArgs } from "@generated-types"; import { handler as tvShowImagesHandler } from "@tmdb-api/handlers/tv-show-images/tv-show-images.handler"; import * as TVShowSimilar from "@tmdb-api/handlers/tv-show-similar"; -import { Context } from "@types"; import * as handlers from "@tmdb-api/handlers/tv-shows"; +import { Context } from "@types"; export const resolvers = { Query: { diff --git a/src/graphql/type-defs/schemas/tmdb/movie.schema.ts b/src/graphql/type-defs/schemas/tmdb/movie.schema.ts index 7e40595..d4fe254 100644 --- a/src/graphql/type-defs/schemas/tmdb/movie.schema.ts +++ b/src/graphql/type-defs/schemas/tmdb/movie.schema.ts @@ -39,5 +39,6 @@ export default `#graphql video: Boolean voteAverage: Float voteCount: Int + videos(id: Int!, language: ISO6391Language): [MediaVideo!]! } `; From b899fa91b3305103ea6667d6e75e309fd9365dfa Mon Sep 17 00:00:00 2001 From: Stenio Wagner Date: Sun, 3 Dec 2023 13:25:17 -0300 Subject: [PATCH 3/3] test: add movie-details-videos --- __test__/datasources/tmdb-api/fixtures.ts | 354 ++++++++++++++++++ __test__/datasources/tmdb-api/queries.ts | 14 + .../videos/movie-videos.handler.test.ts | 95 +++++ .../tmdb-movie-db-api.spec.ts | 18 + 4 files changed, 481 insertions(+) create mode 100644 src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.test.ts diff --git a/__test__/datasources/tmdb-api/fixtures.ts b/__test__/datasources/tmdb-api/fixtures.ts index c7b3c9e..8d3b011 100644 --- a/__test__/datasources/tmdb-api/fixtures.ts +++ b/__test__/datasources/tmdb-api/fixtures.ts @@ -7825,3 +7825,357 @@ export const tvShowVideos = { }, ], }; + +export const movieVideos = { + id: 76341, + results: [ + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | The Five Wives: So Shiny, So Chrome | Warner Bros. Entertainment", + key: "GOkSpvc_ZgY", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2021-03-29T17:59:46.000Z", + id: "63d31494a410c8125be6f94a", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | The Road Warriors: Max and Furiosa | Warner Bros. Entertainment", + key: "LceTDmamtNs", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2021-03-22T17:59:48.000Z", + id: "63d3151d5a07f500869106e4", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Full Movie Preview | Warner Bros. Entertainment", + key: "JhTzXoQ-vRc", + site: "YouTube", + size: 1080, + type: "Clip", + official: true, + published_at: "2021-03-03T18:59:46.000Z", + id: "63d315becb71b800a10d923f", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road Behind the Scenes Documentary | Filmmakers: George Miller | Warner Bros. Ent", + key: "_9klHcXRdSs", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2020-09-19T00:47:40.000Z", + id: "63d3152de72fe800e9e6a1e7", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" winning the OscarĀ® for Sound Editing', + key: "_vuN-xdtZo4", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:58.000Z", + id: "63d2e7ffcb71b800d43a5b39", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" wins Makeup and Hairstyling', + key: "GMJp5t05H8E", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:58.000Z", + id: "63d2e821e72fe8008e616381", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" winning the OscarĀ® for Sound Mixing', + key: "OACeTIzax_U", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:58.000Z", + id: "63d2e8354a52f800819c4166", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" wins Production Design', + key: "ZwuDUpF_vxY", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:58.000Z", + id: "63d2e8479f51af00829a84af", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" wins for Costume Design', + key: "5UclL5X3RPU", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:57.000Z", + id: "63d2e7e7a410c811f9e068d5", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: '"Mad Max: Fury Road" wins for Film Editing', + key: "j-DantYBIUM", + site: "YouTube", + size: 720, + type: "Featurette", + official: true, + published_at: "2016-03-23T17:41:57.000Z", + id: "63d2e80b5a07f5007b0b54d4", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Who Killed The World? Pt. 4 | Warner Bros. Entertainment", + key: "RQBcn4qUZVY", + site: "YouTube", + size: 1080, + type: "Featurette", + official: true, + published_at: "2015-08-26T16:09:56.000Z", + id: "63d314f6e72fe8007ca46956", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Who Killed The World Pt. 3 | Warner Bros. Entertainment", + key: "yeXNFgA-5Q0", + site: "YouTube", + size: 1080, + type: "Featurette", + official: true, + published_at: "2015-08-12T16:02:24.000Z", + id: "63d314d05a07f500a29773b3", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Fury on Four Wheels | Warner Bros. Entertainment", + key: "UtlCycIMotk", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-08-11T21:19:18.000Z", + id: "63d315755a07f500dc9ea52b", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Who Killed The World Pt. 2 | Warner Bros. Entertainment", + key: "GLZXsmqZPFg", + site: "YouTube", + size: 1080, + type: "Featurette", + official: true, + published_at: "2015-08-05T15:41:14.000Z", + id: "63d314c65a07f50086910663", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Mad Max: Fury Road | Who Killed The World? Pt. 1 | Warner Bros. Entertainment", + key: "kqaJsU8i8bg", + site: "YouTube", + size: 1080, + type: "Featurette", + official: true, + published_at: "2015-07-27T15:40:36.000Z", + id: "63d314bacb71b8008102c2a2", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Now Playing", + key: "aXlfsF_lb0Y", + site: "YouTube", + size: 1080, + type: "Teaser", + official: true, + published_at: "2015-05-18T16:18:52.000Z", + id: "63d31635a410c811e9bf09d5", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: 'Mad Max: Fury Road - "Wives" Featurette [HD]', + key: "slTH9lFJjKU", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-05-13T20:39:55.000Z", + id: "63d31435e72fe8008e6170e6", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: 'Mad Max: Fury Road - "Furiosa" Featurette [HD]', + key: "Bme58r4TIN0", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-05-12T16:55:08.000Z", + id: "63d31628e72fe8007ca46afa", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: 'Mad Max: Fury Road - "Nux" Featurette [HD]', + key: "jWdVIqsT9hg", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-05-12T03:42:06.000Z", + id: "63d3160f5a07f500dc9ea5ee", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: 'Mad Max: Fury Road - "Max" Featurette [HD]', + key: "ts94HEB7vHw", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-05-06T23:59:42.000Z", + id: "63d3161666ae4d0096b736a4", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: 'Mad Max: Fury Road - "George Miller" Featurette [HD]', + key: "zUtHFnJS3kw", + site: "YouTube", + size: 1080, + type: "Behind the Scenes", + official: true, + published_at: "2015-05-01T17:26:43.000Z", + id: "63d3144a66ae4d00c1cf92ee", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Official Retaliate Trailer [HD]", + key: "MonFNCgK4WE", + published_at: "2015-04-29T16:00:36.000Z", + site: "YouTube", + size: 1080, + type: "Trailer", + official: true, + id: "5bcd2702c3a3682863018582", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Official Main Trailer [HD]", + key: "hEJnMQG9ev8", + published_at: "2015-03-31T17:00:04.000Z", + site: "YouTube", + size: 1080, + type: "Trailer", + official: true, + id: "5bcd26f9925141612a0157ce", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Retaliate", + key: "Lnx_topfRXU", + site: "YouTube", + size: 1080, + type: "Teaser", + official: true, + published_at: "2015-03-29T19:33:41.000Z", + id: "63d31641a410c811e9bf09ed", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Chaos", + key: "QHI-7AwfrGo", + site: "YouTube", + size: 1080, + type: "Teaser", + official: true, + published_at: "2015-03-28T23:18:06.000Z", + id: "63d315e4cb71b800a10d9274", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "War", + key: "vD1NxNloato", + site: "YouTube", + size: 1080, + type: "Teaser", + official: true, + published_at: "2015-03-28T00:47:26.000Z", + id: "63d315f566ae4d0084282f44", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Explosion", + key: "mX3q6YLmWvg", + site: "YouTube", + size: 1080, + type: "Teaser", + official: true, + published_at: "2015-03-27T03:09:53.000Z", + id: "63d31605031a1d00892253c0", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Official Theatrical Teaser Trailer [HD]", + key: "YWNWi-ZWL3c", + published_at: "2014-12-10T18:00:00.000Z", + site: "YouTube", + size: 1080, + type: "Trailer", + official: true, + id: "5bcd26ef9251416126015b7d", + }, + { + iso_639_1: "en", + iso_3166_1: "US", + name: "Comic-Con First Look [HD]", + key: "akX3Is3qBpw", + published_at: "2014-07-27T17:00:01.000Z", + site: "YouTube", + size: 1080, + type: "Trailer", + official: true, + id: "5bcd26e39251416131016aba", + }, + ], +}; diff --git a/__test__/datasources/tmdb-api/queries.ts b/__test__/datasources/tmdb-api/queries.ts index 0288a99..4a60fd9 100644 --- a/__test__/datasources/tmdb-api/queries.ts +++ b/__test__/datasources/tmdb-api/queries.ts @@ -468,6 +468,20 @@ query Movie ($id: Int!, $language: ISO6391Language) { video voteAverage voteCount + videos(id: $id) { + id + key + name + site + thumbnail { + extraSmall + small + medium + large + extraLarge + } + type + } } } `; diff --git a/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.test.ts b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.test.ts new file mode 100644 index 0000000..188bd82 --- /dev/null +++ b/src/graphql/datasources/the-movie-db-api/handlers/movies/videos/movie-videos.handler.test.ts @@ -0,0 +1,95 @@ +import TheMovieDBAPI from "@tmdb-api/tmdb-movie-db-api"; +import { Iso6391Language } from "@generated-types"; +import { CONSTANTS as TMDBAPI_CONSTANS } from "@tmdb-api/utils"; + +import * as fixtures from "../../../../../../../__test__/datasources/tmdb-api/fixtures"; + +import { handler } from "./movie-videos.handler"; + +const ID = 1; + +const mockGet = jest.fn(); + +jest.mock("@apollo/datasource-rest", () => { + class MockRESTDataSource { + baseUrl = ""; + get = mockGet; + } + return { + RESTDataSource: MockRESTDataSource, + }; +}); + +describe("DataSources/TheMovieDBApi/Movie-Videos-Query-Handler", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('When the "language" is defined', () => { + it('should call "RESTDatasource.get" correctly', async () => { + const tmdbAPI = new TheMovieDBAPI(); + const language = Iso6391Language.Pt; + await handler( + { + language, + id: ID, + }, + tmdbAPI, + ); + expect(mockGet.mock.calls[0][0]).toEqual(`movie/${ID}/videos`); + expect(mockGet.mock.calls[0][1].params).toEqual({ + language, + }); + expect(typeof mockGet.mock.calls[0][1].headers.Authorization).toEqual("string"); + }); + }); + + describe('When the "language" is not defined', () => { + it('should call "RESTDatasource.get" correctly', async () => { + const tmdbAPI = new TheMovieDBAPI(); + await handler( + { + id: ID, + }, + tmdbAPI, + ); + expect(mockGet.mock.calls[0][0]).toEqual(`movie/${ID}/videos`); + expect(mockGet.mock.calls[0][1].params).toEqual({ + language: TMDBAPI_CONSTANS.FALLBACK_LANGUAGE, + }); + expect(typeof mockGet.mock.calls[0][1].headers.Authorization).toEqual("string"); + }); + }); + + describe('When "response" is "undefined"', () => { + it("should return the data correctly", async () => { + mockGet.mockReturnValueOnce(undefined); + const tmdbAPI = new TheMovieDBAPI(); + const result = await handler( + { + id: ID, + }, + tmdbAPI, + ); + expect(result).toEqual([]); + }); + }); + + describe("When receive the data", () => { + it("should return the data correctly", async () => { + mockGet.mockReturnValueOnce(fixtures.movieVideos); + const tmdbAPI = new TheMovieDBAPI(); + const result = await handler( + { + id: ID, + }, + tmdbAPI, + ); + expect(result).toEqual( + fixtures.movieVideos.results.filter( + (movieVideo) => movieVideo.site === "YouTube", + ), + ); + }); + }); +}); diff --git a/src/graphql/datasources/the-movie-db-api/tmdb-movie-db-api.spec.ts b/src/graphql/datasources/the-movie-db-api/tmdb-movie-db-api.spec.ts index 5216725..08a5962 100644 --- a/src/graphql/datasources/the-movie-db-api/tmdb-movie-db-api.spec.ts +++ b/src/graphql/datasources/the-movie-db-api/tmdb-movie-db-api.spec.ts @@ -949,6 +949,9 @@ describe("DataSources/TheMovieDBApi/Integration", () => { jest .spyOn(RESTDataSource.prototype as any, "get") .mockImplementationOnce(async () => Promise.resolve(fixtures.movie)); + jest + .spyOn(RESTDataSource.prototype as any, "get") + .mockImplementationOnce(async () => Promise.resolve(fixtures.movieVideos)); const response = await execDatasourceTestOperation<{ movie: Movie; }>({ @@ -1012,6 +1015,21 @@ describe("DataSources/TheMovieDBApi/Integration", () => { expect(movie.video).toEqual(fixtures.movie.video); expect(movie.voteAverage).toEqual(fixtures.movie.vote_average); expect(movie.voteCount).toEqual(fixtures.movie.vote_count); + // videos + for (let i = 0; i < movie.videos.length; i++) { + expect(movie.videos[i].id).toEqual(fixtures.movieVideos.results[i].id); + expect(movie.videos[i].key).toEqual(fixtures.movieVideos.results[i].key); + expect(movie.videos[i].name).toEqual(fixtures.movieVideos.results[i].name); + expect(movie.videos[i].type).toEqual(fixtures.movieVideos.results[i].type); + expect(movie.videos[i].site).toEqual(fixtures.movieVideos.results[i].site); + expect(movie.videos[i].thumbnail).toEqual({ + extraSmall: `${YOUTUBE_THUMBNAIL_URL}/${fixtures.movieVideos.results[i].key}/default.jpg`, + small: `${YOUTUBE_THUMBNAIL_URL}/${fixtures.movieVideos.results[i].key}/mqdefault.jpg`, + medium: `${YOUTUBE_THUMBNAIL_URL}/${fixtures.movieVideos.results[i].key}/hqdefault.jpg`, + large: `${YOUTUBE_THUMBNAIL_URL}/${fixtures.movieVideos.results[i].key}/sddefault.jpg`, + extraLarge: `${YOUTUBE_THUMBNAIL_URL}/${fixtures.movieVideos.results[i].key}/maxresdefault.jpg`, + }); + } }); }); });