Skip to content

Commit

Permalink
feat: Added the single tv show lookup query and enhanced the entertai…
Browse files Browse the repository at this point in the history
…nment service to take into account the entertainment type e.g. movie or tv
  • Loading branch information
AlexMachin1997 committed Jun 16, 2024
1 parent 7d949bb commit 7f5131c
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 51 deletions.
6 changes: 3 additions & 3 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { GraphQLModule } from '@nestjs/graphql';

import { EntertainmentModule } from './entertainment/entertainment.module';
import { MovieModule } from './movie/movie.module';
import { ShowModule } from './show/show.module';
import { UtilsModule } from './utils/utils.module';

@Module({
controllers: [],
providers: [],
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
Expand Down Expand Up @@ -41,7 +40,8 @@ import { UtilsModule } from './utils/utils.module';
}),
MovieModule,
UtilsModule,
EntertainmentModule
EntertainmentModule,
ShowModule
]
})
export class AppModule {}
51 changes: 38 additions & 13 deletions src/entertainment/entertainment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BelongsToCollection,
Cast,
Crew,
ENTERTAINMENT_TYPES,
GENDER,
Keyword,
Review,
Expand All @@ -25,17 +26,26 @@ import {
IVdoesQueryResponse
} from './entertainment';

// eslint-disable-next-line @typescript-eslint/naming-convention
interface IEntertainmentCommonArguments {
entertainmentType: ENTERTAINMENT_TYPES;
entertainmentId: number;
}

@Injectable()
export class EntertainmentService {
constructor(
private readonly httpService: HttpService,
private readonly utilService: UtilsService
) {}

async getReview(): Promise<Review | null> {
async getReview({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<Review | null> {
const { data } = await firstValueFrom(
this.httpService.get<IReviewQuery>(
'https://api.themoviedb.org/3/movie/19995/reviews?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/reviews?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand Down Expand Up @@ -71,10 +81,13 @@ export class EntertainmentService {
};
}

async getTopBilledCast(): Promise<Cast[] | null> {
async getTopBilledCast({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<Cast[] | null> {
const { data } = await firstValueFrom(
this.httpService.get<ICreditsQueryResponse>(
'https://api.themoviedb.org/3/movie/19995/credits?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLocaleLowerCase()}/${entertainmentId}/credits?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand All @@ -98,10 +111,13 @@ export class EntertainmentService {
}));
}

async getFeaturedCrewMembers(): Promise<Crew[] | null> {
async getFeaturedCrewMembers({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<Crew[] | null> {
const { data } = await firstValueFrom(
this.httpService.get<ICreditsQueryResponse>(
'https://api.themoviedb.org/3/movie/19995/credits?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/credits?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand Down Expand Up @@ -156,10 +172,13 @@ export class EntertainmentService {
return featuredCrewMembers;
}

async getKeywords(): Promise<Keyword[] | null> {
async getKeywords({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<Keyword[] | null> {
const { data } = await firstValueFrom(
this.httpService.get<IKeywordsQueryResponse>(
'https://api.themoviedb.org/3/movie/19995/keywords?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLocaleLowerCase()}/${entertainmentId}/keywords?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand All @@ -174,10 +193,13 @@ export class EntertainmentService {
return data.keywords;
}

async getSocials(): Promise<Social> {
async getSocials({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<Social> {
const { data } = await firstValueFrom(
this.httpService.get<IExternalIdsQueryResponse>(
'https://api.themoviedb.org/3/movie/19995/external_ids?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/external_ids?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand All @@ -198,7 +220,7 @@ export class EntertainmentService {
}

getCollection = (
collection: null | TheOpenMovieDatabaseBelongsToCollection
collection: undefined | null | TheOpenMovieDatabaseBelongsToCollection = null
): BelongsToCollection | null => {
if (collection !== null) {
return {
Expand Down Expand Up @@ -234,10 +256,13 @@ export class EntertainmentService {
return originalLanguage;
};

async getTrailerUrl(): Promise<string | null> {
async getTrailerUrl({
entertainmentType,
entertainmentId
}: IEntertainmentCommonArguments): Promise<string | null> {
const { data } = await firstValueFrom(
this.httpService.get<IVdoesQueryResponse>(
'https://api.themoviedb.org/3/movie/19995/videos?language=en-U',
`https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/videos?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand Down
6 changes: 3 additions & 3 deletions src/graphql.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,13 @@ export interface Show {
releaseDate?: Nullable<string>;
voteAverage?: Nullable<number>;
status?: Nullable<string>;
reviews?: Nullable<Nullable<Review>[]>;
review?: Nullable<Review>;
recommendations?: Nullable<Nullable<Recommendation>[]>;
keywords?: Nullable<Nullable<Keyword>[]>;
social?: Nullable<Social>;
featuredCast?: Nullable<Nullable<Cast>[]>;
topBilledCast?: Nullable<Nullable<Cast>[]>;
featuredCrew?: Nullable<Nullable<Crew>[]>;
featuredVideo?: Nullable<Video>;
trailerUrl?: Nullable<string>;
belongsToCollection?: Nullable<BelongsToCollection>;
tagline?: Nullable<string>;
runtime?: Nullable<string>;
Expand Down
1 change: 1 addition & 0 deletions src/models/Query.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
type Query {
movie(id: Int!): Movie
show(id: Int!): Show
}
6 changes: 3 additions & 3 deletions src/models/Show.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ type Show {
releaseDate: String
voteAverage: Float
status: String
reviews: [Review]
review: Review
recommendations: [Recommendation]
keywords: [Keyword]
social: Social
featuredCast: [Cast]
topBilledCast: [Cast]
featuredCrew: [Crew]
featuredVideo: Video
trailerUrl: String
belongsToCollection: BelongsToCollection
tagline: String
runtime: String
Expand Down
28 changes: 14 additions & 14 deletions src/movie/movie.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Resolver, Query, ResolveField, Parent } from '@nestjs/graphql';
import { Resolver, Query, ResolveField, Parent, Args } from '@nestjs/graphql';

import { MovieService } from './movie.service';
import { Cast, Crew, Keyword, Movie, Review, Social } from '../graphql.schema';
Expand All @@ -8,34 +8,34 @@ export class MovieResolver {
constructor(private readonly movieService: MovieService) {}

@Query()
async movie(): Promise<Movie> {
return this.movieService.getMovie();
async movie(@Args('id') movieId: number): Promise<Movie> {
return this.movieService.getMovie(movieId);
}

@ResolveField()
async review(): Promise<Review | null> {
return this.movieService.getReview();
async review(@Parent() movie: Movie): Promise<Review | null> {
return this.movieService.getReview(movie.id ?? 0);
}

@ResolveField()
async topBilledCast(): Promise<Cast[] | null> {
return this.movieService.getTopBilledCast();
async topBilledCast(@Parent() movie: Movie): Promise<Cast[] | null> {
return this.movieService.getTopBilledCast(movie.id ?? 0);
}

@ResolveField()
async featuredCrew(): Promise<Crew[] | null> {
return this.movieService.getFeaturedCrewMembers();
async featuredCrew(@Parent() movie: Movie): Promise<Crew[] | null> {
return this.movieService.getFeaturedCrewMembers(movie.id ?? 0);
}

@ResolveField()
async keywords(): Promise<Keyword[] | null> {
return this.movieService.getKeywords();
async keywords(@Parent() movie: Movie): Promise<Keyword[] | null> {
return this.movieService.getKeywords(movie.id ?? 0);
}

@ResolveField()
async social(@Parent() movie: Movie): Promise<Social | null> {
// Get the external social url (The homepage isn't set here as it's already apart of the original Movie query)
const socials = await this.movieService.getSocials();
const socials = await this.movieService.getSocials(movie.id ?? 0);

return {
...socials,
Expand All @@ -46,7 +46,7 @@ export class MovieResolver {
}

@ResolveField()
async trailerUrl(): Promise<string | null> {
return this.movieService.getTrailerUrl();
async trailerUrl(@Parent() movie: Movie): Promise<string | null> {
return this.movieService.getTrailerUrl(movie.id ?? 0);
}
}
48 changes: 33 additions & 15 deletions src/movie/movie.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EntertainmentService } from 'src/entertainment/entertainment.service';
import { UtilsService } from 'src/utils/utils.service';

import { TheOpenMovieDatabaseMovie } from './movie';
import { Cast, Movie, Review, Crew, Keyword, Social } from '../graphql.schema';
import { Cast, Movie, Review, Crew, Keyword, Social, ENTERTAINMENT_TYPES } from '../graphql.schema';

@Injectable()
export class MovieService {
Expand All @@ -22,10 +22,10 @@ export class MovieService {
return `${numberOfHours}h ${numberOfMinutes}m`;
}

async getMovie(): Promise<Movie> {
async getMovie(movieId: number): Promise<Movie> {
const { data } = await firstValueFrom(
this.httpService.get<TheOpenMovieDatabaseMovie>(
'https://api.themoviedb.org/3/movie/19995?language=en-U',
`https://api.themoviedb.org/3/movie/${movieId}?language=en-U`,
{
headers: {
Accept: 'application/json',
Expand Down Expand Up @@ -70,27 +70,45 @@ export class MovieService {
return movie;
}

async getReview(): Promise<Review | null> {
return this.entertainmentService.getReview();
async getReview(movieId: number): Promise<Review | null> {
return this.entertainmentService.getReview({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}

async getTopBilledCast(): Promise<Cast[] | null> {
return this.entertainmentService.getTopBilledCast();
async getTopBilledCast(movieId: number): Promise<Cast[] | null> {
return this.entertainmentService.getTopBilledCast({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}

async getFeaturedCrewMembers(): Promise<Crew[] | null> {
return this.entertainmentService.getFeaturedCrewMembers();
async getFeaturedCrewMembers(movieId: number): Promise<Crew[] | null> {
return this.entertainmentService.getFeaturedCrewMembers({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}

async getKeywords(): Promise<Keyword[] | null> {
return this.entertainmentService.getKeywords();
async getKeywords(movieId: number): Promise<Keyword[] | null> {
return this.entertainmentService.getKeywords({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}

async getSocials(): Promise<Social> {
return this.entertainmentService.getSocials();
async getSocials(movieId: number): Promise<Social> {
return this.entertainmentService.getSocials({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}

async getTrailerUrl(): Promise<string | null> {
return this.entertainmentService.getTrailerUrl();
async getTrailerUrl(movieId: number): Promise<string | null> {
return this.entertainmentService.getTrailerUrl({
entertainmentId: movieId,
entertainmentType: ENTERTAINMENT_TYPES.MOVIE
});
}
}
14 changes: 14 additions & 0 deletions src/show/show.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* eslint-disable import/extensions */
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { EntertainmentModule } from 'src/entertainment/entertainment.module';
import { UtilsModule } from 'src/utils/utils.module';

import { ShowResolver } from './show.resolver';
import { ShowService } from './show.service';

@Module({
providers: [ShowService, ShowResolver],
imports: [HttpModule, UtilsModule, EntertainmentModule]
})
export class ShowModule {}
52 changes: 52 additions & 0 deletions src/show/show.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';

import { ShowService } from './show.service';
import { Cast, Crew, Keyword, Review, Show, Social } from '../graphql.schema';

@Resolver()
export class ShowResolver {
constructor(private readonly showService: ShowService) {}

@Query()
async show(@Args('id') showId: number): Promise<Show> {
return this.showService.getShow(showId);
}

@ResolveField()
async review(@Parent() show: Show): Promise<Review | null> {
return this.showService.getReview(show.id ?? 0);
}

@ResolveField()
async topBilledCast(@Parent() show: Show): Promise<Cast[] | null> {
return this.showService.getTopBilledCast(show.id ?? 0);
}

@ResolveField()
async featuredCrew(@Parent() show: Show): Promise<Crew[] | null> {
return this.showService.getFeaturedCrewMembers(show.id ?? 0);
}

@ResolveField()
async keywords(@Parent() show: Show): Promise<Keyword[] | null> {
return this.showService.getKeywords(show.id ?? 0);
}

@ResolveField()
async social(@Parent() show: Show): Promise<Social | null> {
// Get the external social url (The homepage isn't set here as it's already apart of the original Show query)
const socials = await this.showService.getSocials(show.id ?? 0);

return {
...socials,

// Since the homepage is apart of the original query append it to the homepage property
homepage: show.homepage
};
}

@ResolveField()
async trailerUrl(@Parent() show: Show): Promise<string | null> {
return this.showService.getTrailerUrl(show.id ?? 0);
}
}
Loading

0 comments on commit 7f5131c

Please sign in to comment.