Skip to content

Commit

Permalink
Merge pull request #10 from mathiasberggren/basic-database
Browse files Browse the repository at this point in the history
[Backend] Store scraped movie data
  • Loading branch information
mathiasberggren authored Apr 28, 2024
2 parents 440f120 + 88a039c commit aa4f0a9
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "movies" (
"id" SERIAL NOT NULL,
"genre" TEXT NOT NULL,
"director" TEXT NOT NULL,
"duration" INTEGER NOT NULL,
"subtitles" TEXT[],
"release_date" TIMESTAMP(3) NOT NULL,

CONSTRAINT "movies_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "movie_titles" (
"movie_title_id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"movie_id" INTEGER NOT NULL,
"language" TEXT NOT NULL,

CONSTRAINT "movie_titles_pkey" PRIMARY KEY ("movie_title_id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- AddForeignKey
ALTER TABLE "movie_titles" ADD CONSTRAINT "movie_titles_movie_id_fkey" FOREIGN KEY ("movie_id") REFERENCES "movies"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
21 changes: 21 additions & 0 deletions apps/api/src/database/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,24 @@ model SubscriptionCredential {
@@map("subscription_credentials")
}
model Movie {
id Int @id @default(autoincrement())
genre String
director String
duration Int
subtitles String[]
releaseDate DateTime @map("release_date")
movieTitles MovieTitle[]
@@map("movies")
}

model MovieTitle {
movieTitleId Int @id @default(autoincrement()) @map("movie_title_id")
title String
movieId Int @map("movie_id")
Movie Movie @relation(fields: [movieId], references: [id])
language String
@@map("movie_titles")
}

14 changes: 13 additions & 1 deletion apps/api/src/movies/dto/create-movie.dto.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
/* eslint-disable @typescript-eslint/no-extraneous-class */
export class CreateMovieDto {}
export class CreateMovieDto {
genre: string
director: string
duration: number
subtitles: string[]
releaseDate: Date
movieTitles: CreateMovieTitleDto[]
}

export class CreateMovieTitleDto {
language: string
title: string
}
17 changes: 11 additions & 6 deletions apps/api/src/movies/movies.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common'
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, BadRequestException } from '@nestjs/common'
import { Movie } from '@prisma/client'

import { MoviesService } from './movies.service'
import { CreateMovieDto } from './dto/create-movie.dto'
Expand All @@ -10,9 +11,7 @@ export class MoviesController {
constructor (private readonly moviesService: MoviesService, private readonly movieSearchService: MoviesSearchService) {}

@Get('search')
// TODO: change return type to Movie[] when the entity is defined
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async findByTitle (@Query('title') title: string): Promise<any> {
async findByTitle (@Query('title') title: string): Promise<Movie[]> {
return await this.movieSearchService.findByTitle(title)
}

Expand All @@ -21,8 +20,14 @@ export class MoviesController {
*/

@Post()
create (@Body() createMovieDto: CreateMovieDto) {
return this.moviesService.create(createMovieDto)
async create (@Body() createMovieDto: CreateMovieDto) {
if (createMovieDto.movieTitles.length === 0) {
throw new BadRequestException('At least one movie title is required')
}

await this.moviesService.create(createMovieDto)

return { message: 'Movie created successfully' }
}

@Get()
Expand Down
37 changes: 36 additions & 1 deletion apps/api/src/movies/movies.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
import { Test, TestingModule } from '@nestjs/testing'

import { PrismaService } from '../database/prisma.service'

import { MoviesService } from './movies.service'
import { CreateMovieDto } from './dto/create-movie.dto'

describe('MoviesService', () => {
let service: MoviesService
let prismaService: PrismaService

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MoviesService]
providers: [
MoviesService,
{
provide: PrismaService,
useValue: {
movie: {
create: jest.fn().mockResolvedValue({})
}
}
}
]
}).compile()

service = module.get<MoviesService>(MoviesService)
prismaService = module.get<PrismaService>(PrismaService)
})

it('should be defined', () => {
expect(service).toBeDefined()
})

it('should create a movie', async () => {
const createMovieDto: CreateMovieDto = {
genre: 'Action',
director: 'John Doe',
duration: 120,
subtitles: ['English', 'Spanish'],
releaseDate: new Date(),
movieTitles: [{ language: 'English', title: 'Example Title' }]
}

const expectedResult = {
...createMovieDto,
id: 1
}

jest.spyOn(prismaService.movie, 'create').mockResolvedValue(expectedResult)

await expect(service.create(createMovieDto)).resolves.toEqual(expectedResult)
})
})
24 changes: 22 additions & 2 deletions apps/api/src/movies/movies.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import { Injectable } from '@nestjs/common'

import { PrismaService } from '../database/prisma.service'

import { CreateMovieDto } from './dto/create-movie.dto'
import { UpdateMovieDto } from './dto/update-movie.dto'

@Injectable()
export class MoviesService {
create (createMovieDto: CreateMovieDto) {
return 'This action adds a new movie'
constructor (private readonly db: PrismaService) {}
async create (createMovieDto: CreateMovieDto) {
const { movieTitles, ...movieData } = createMovieDto

const movie = await this.db.movie.create({
data: {
...movieData,
movieTitles: {
create: movieTitles.map((title) => ({
title: title.title,
language: title.language
}))
}
},
include: {
movieTitles: true
}
})

return movie
}

findAll () {
Expand Down
31 changes: 0 additions & 31 deletions apps/api/test/app.e2e-spec.ts

This file was deleted.

45 changes: 45 additions & 0 deletions apps/api/test/movies.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'
import request from 'supertest'
import { App } from 'supertest/types'

import { AppModule } from '../src/app/app.module'

describe('MoviesController (e2e)', () => {
let app: INestApplication

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile()

app = moduleFixture.createNestApplication()
await app.init()
})

it('/api/movies (POST)', () => {
return request(app.getHttpServer() as App)
.post('/movies')
.send({
genre: 'Action',
director: 'John Doe',
duration: 120,
subtitles: ['English', 'Spanish'],
releaseDate: '2023-04-01T00:00:00.000Z',
movieTitles: [
{
language: 'English',
title: 'Example Title'
}
]
})
.expect(201)
.expect({
message: 'Movie created successfully'
})
})

afterAll(async () => {
await app.close()
})
})

0 comments on commit aa4f0a9

Please sign in to comment.