parent
5147882e6a
commit
f647b8e7fb
@ -1,22 +0,0 @@ |
|||||||
import { Test, type TestingModule } from '@nestjs/testing'; |
|
||||||
import { AppController } from './app.controller'; |
|
||||||
import { AppService } from './app.service'; |
|
||||||
|
|
||||||
describe('AppController', () => { |
|
||||||
let appController: AppController; |
|
||||||
|
|
||||||
beforeEach(async () => { |
|
||||||
const app: TestingModule = await Test.createTestingModule({ |
|
||||||
controllers: [AppController], |
|
||||||
providers: [AppService], |
|
||||||
}).compile(); |
|
||||||
|
|
||||||
appController = app.get<AppController>(AppController); |
|
||||||
}); |
|
||||||
|
|
||||||
describe('root', () => { |
|
||||||
it('should return "Hello World!"', () => { |
|
||||||
expect(appController.getHello()).toBe('Hello World!'); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
@ -1,12 +0,0 @@ |
|||||||
import { Controller, Get } from '@nestjs/common'; |
|
||||||
import { AppService } from './app.service'; |
|
||||||
|
|
||||||
@Controller() |
|
||||||
export class AppController { |
|
||||||
constructor(private readonly appService: AppService) {} |
|
||||||
|
|
||||||
@Get() |
|
||||||
getHello(): string { |
|
||||||
return this.appService.getHello(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,93 @@ |
|||||||
|
import { Test, type TestingModule } from '@nestjs/testing'; |
||||||
|
import { ValidationPipe, type INestApplication } from '@nestjs/common'; |
||||||
|
import request from 'supertest'; |
||||||
|
import { AppModule } from './../src/app.module'; |
||||||
|
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||||
|
|
||||||
|
describe('AppController (e2e)', () => { |
||||||
|
let app: INestApplication; |
||||||
|
|
||||||
|
beforeAll(async () => { |
||||||
|
const enrichedDataService = await createTestOmdbEnrichedDataService(); |
||||||
|
|
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({ |
||||||
|
imports: [AppModule], |
||||||
|
}) |
||||||
|
.overrideProvider('enrichedDataService') |
||||||
|
.useValue(enrichedDataService) |
||||||
|
.compile(); |
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication(); |
||||||
|
app.useGlobalPipes(new ValidationPipe({ transform: true })); |
||||||
|
await app.init(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies/tt0061852 (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies/tt0061852') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject({ |
||||||
|
title: 'The Jungle Book', |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies/11528860 (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies/11528860') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject({ |
||||||
|
title: 'The Jungle Book', |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies (GET)', () => { |
||||||
|
return request(app.getHttpServer()).get('/api/movies').expect(400); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies?writers=George Lucas (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies?writers=George Lucas') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject([ |
||||||
|
{ title: 'Indiana Jones and the Last Crusade' }, |
||||||
|
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||||
|
]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies?writers=George Lucas&actors=Mark Hamill (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies?writers=George Lucas&actors=Mark Hamill') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject([ |
||||||
|
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||||
|
]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies?productionYear=1967 (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies?productionYear=1967') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject([ |
||||||
|
{ title: 'The Jungle Book' }, |
||||||
|
]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/api/movies?productionYear=1800 (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/api/movies?productionYear=1800') |
||||||
|
.expect(200) |
||||||
|
.expect((response) => { |
||||||
|
expect(response.body).toMatchObject([]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -1,11 +1,16 @@ |
|||||||
import { Module } from '@nestjs/common'; |
import { Module } from '@nestjs/common'; |
||||||
import { AppController } from './app.controller'; |
import { MoviesController } from './movies.controller'; |
||||||
import { AppService } from './app.service'; |
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||||
|
|
||||||
@Module({ |
@Module({ |
||||||
imports: [], |
imports: [], |
||||||
controllers: [AppController], |
controllers: [MoviesController], |
||||||
providers: [AppService], |
providers: [ |
||||||
|
{ |
||||||
|
provide: 'enrichedDataService', |
||||||
|
useFactory: createTestOmdbEnrichedDataService, |
||||||
|
}, |
||||||
|
], |
||||||
}) |
}) |
||||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||||
export class AppModule {} |
export class AppModule {} |
||||||
|
@ -1,8 +0,0 @@ |
|||||||
import { Injectable } from '@nestjs/common'; |
|
||||||
|
|
||||||
@Injectable() |
|
||||||
export class AppService { |
|
||||||
getHello(): string { |
|
||||||
return 'Hello World!'; |
|
||||||
} |
|
||||||
} |
|
@ -1,8 +1,10 @@ |
|||||||
|
import { ValidationPipe } from '@nestjs/common'; |
||||||
import { NestFactory } from '@nestjs/core'; |
import { NestFactory } from '@nestjs/core'; |
||||||
import { AppModule } from './app.module'; |
import { AppModule } from './app.module'; |
||||||
|
|
||||||
async function bootstrap() { |
async function bootstrap() { |
||||||
const app = await NestFactory.create(AppModule); |
const app = await NestFactory.create(AppModule); |
||||||
|
app.useGlobalPipes(new ValidationPipe({ transform: true })); |
||||||
await app.listen(3000); |
await app.listen(3000); |
||||||
} |
} |
||||||
void bootstrap(); |
void bootstrap(); |
||||||
|
@ -0,0 +1,48 @@ |
|||||||
|
import { Test, type TestingModule } from '@nestjs/testing'; |
||||||
|
import { MoviesController } from './movies.controller'; |
||||||
|
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||||
|
|
||||||
|
describe('AppController', () => { |
||||||
|
let moviesController: MoviesController; |
||||||
|
|
||||||
|
beforeEach(async () => { |
||||||
|
const app: TestingModule = await Test.createTestingModule({ |
||||||
|
controllers: [MoviesController], |
||||||
|
providers: [ |
||||||
|
{ |
||||||
|
provide: 'enrichedDataService', |
||||||
|
useFactory: createTestOmdbEnrichedDataService, |
||||||
|
}, |
||||||
|
], |
||||||
|
}).compile(); |
||||||
|
|
||||||
|
moviesController = app.get<MoviesController>(MoviesController); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('root', () => { |
||||||
|
it('should return movie by internal id', () => { |
||||||
|
expect(moviesController.getMovie('11528860')).toMatchObject({ |
||||||
|
title: 'The Jungle Book', |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return movie by imdb id', () => { |
||||||
|
expect(moviesController.getMovie('tt0061852')).toMatchObject({ |
||||||
|
title: 'The Jungle Book', |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw an error for empty search query', () => { |
||||||
|
expect(() => moviesController.searchMovies({})).toThrow(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return movies matching writer', () => { |
||||||
|
expect( |
||||||
|
moviesController.searchMovies({ writers: 'George Lucas' }), |
||||||
|
).toMatchObject([ |
||||||
|
{ title: 'Indiana Jones and the Last Crusade' }, |
||||||
|
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||||
|
]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,102 @@ |
|||||||
|
import { |
||||||
|
BadRequestException, |
||||||
|
Controller, |
||||||
|
Get, |
||||||
|
Inject, |
||||||
|
NotFoundException, |
||||||
|
Param, |
||||||
|
Query, |
||||||
|
} from '@nestjs/common'; |
||||||
|
import { EnrichedDataService } from './types'; |
||||||
|
import { IsIn, IsInt, IsOptional, IsString, Length } from 'class-validator'; |
||||||
|
import { Type } from 'class-transformer'; |
||||||
|
|
||||||
|
const ENTRY_TYPES = ['movie', 'series', 'episode'] as const; |
||||||
|
|
||||||
|
class SearchDto { |
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
actors?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(2, 2) |
||||||
|
@IsOptional() |
||||||
|
availableLanguages?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
directors?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
localTitle?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(2, 2) |
||||||
|
@IsOptional() |
||||||
|
originalLanguage?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
productionCountries?: string; |
||||||
|
|
||||||
|
@IsInt() |
||||||
|
@Type(() => Number) |
||||||
|
@IsOptional() |
||||||
|
productionYear?: number; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
studios?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
title?: string; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@IsIn(ENTRY_TYPES) |
||||||
|
@IsOptional() |
||||||
|
type?: (typeof ENTRY_TYPES)[number]; |
||||||
|
|
||||||
|
@IsString() |
||||||
|
@Length(1) |
||||||
|
@IsOptional() |
||||||
|
writers?: string; |
||||||
|
} |
||||||
|
|
||||||
|
@Controller('/api/movies') |
||||||
|
export class MoviesController { |
||||||
|
constructor( |
||||||
|
@Inject('enrichedDataService') |
||||||
|
private readonly enrichedDataService: EnrichedDataService, |
||||||
|
) {} |
||||||
|
|
||||||
|
@Get() |
||||||
|
searchMovies(@Query() query: SearchDto) { |
||||||
|
if (!Object.values(query).some((filterValue) => filterValue)) { |
||||||
|
throw new BadRequestException('Query should not be empty'); |
||||||
|
} |
||||||
|
|
||||||
|
const result = this.enrichedDataService.getSearchResults(query); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Get(':id') |
||||||
|
getMovie(@Param('id') id: string) { |
||||||
|
const result = /^\d+$/.test(id) |
||||||
|
? this.enrichedDataService.getMetadataByInternalId(parseInt(id, 10)) |
||||||
|
: this.enrichedDataService.getMetadataByImdbId(id); |
||||||
|
|
||||||
|
if (!result) { |
||||||
|
throw new NotFoundException(); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue