Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
58f934e
set up project
MyrtheDullaart Jul 1, 2024
324db19
add functions to update customer
MyrtheDullaart Jul 1, 2024
86547fa
add functions to get all movies
MyrtheDullaart Jul 1, 2024
859a52a
add functions to create movie
MyrtheDullaart Jul 1, 2024
7cc7076
add functions to get movie by id
MyrtheDullaart Jul 1, 2024
71e0f30
add functions to update movie
MyrtheDullaart Jul 1, 2024
b36e3b9
add functions to create new screen
MyrtheDullaart Jul 1, 2024
a20e031
add functions to get movies by query parameters and create tests
MyrtheDullaart Jul 1, 2024
454cc62
add functions to create screenings and create test
MyrtheDullaart Jul 1, 2024
264c6a0
add get movie by title and clean up code
MyrtheDullaart Jul 1, 2024
c7e504d
create test to get movie by title
MyrtheDullaart Jul 2, 2024
3ddfb6b
add functions to update screenings when updating movie and create tests
MyrtheDullaart Jul 2, 2024
a2bf500
add functions to create screenings when creating a screen and create …
MyrtheDullaart Jul 2, 2024
464d6a7
add functions to create ticket and create tests
MyrtheDullaart Jul 2, 2024
383a5ee
add error handling for missing fields
MyrtheDullaart Jul 2, 2024
c9c48fa
add not found error handling and tests
MyrtheDullaart Jul 2, 2024
b219bca
add not unique error handling and tests
MyrtheDullaart Jul 2, 2024
43badf3
clean up code
MyrtheDullaart Jul 2, 2024
0a1bc87
add function to only get future screentime movies
MyrtheDullaart Jul 3, 2024
fa98791
create review model and seed
MyrtheDullaart Jul 3, 2024
ec73e45
create test for creating a review
MyrtheDullaart Jul 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .env.example

This file was deleted.

19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
"dependencies": {
"@prisma/client": "^5.16.1",
"cors": "^2.8.5",
"date-fns": "^3.6.0",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0"
}
}
12 changes: 12 additions & 0 deletions prisma/migrations/20240702135814_make_unique/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:

- A unique constraint covering the columns `[title]` on the table `Movie` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[number]` on the table `Screen` will be added. If there are existing duplicate values, this will fail.

*/
-- CreateIndex
CREATE UNIQUE INDEX "Movie_title_key" ON "Movie"("title");

-- CreateIndex
CREATE UNIQUE INDEX "Screen_number_key" ON "Screen"("number");
17 changes: 17 additions & 0 deletions prisma/migrations/20240703083212_reviews/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "Review" (
"id" SERIAL NOT NULL,
"content" TEXT NOT NULL,
"customerId" INTEGER NOT NULL,
"movieId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

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

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_movieId_fkey" FOREIGN KEY ("movieId") REFERENCES "Movie"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
23 changes: 18 additions & 5 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ model Customer {
name String
contact Contact?
tickets Ticket[]
reviews Review[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Expand All @@ -33,18 +34,19 @@ model Contact {
model Movie {
id Int @id @default(autoincrement())
screenings Screening[]
title String
title String @unique
runtimeMins Int
reviews Review[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Screen {
id Int @id @default(autoincrement())
number Int
id Int @id @default(autoincrement())
number Int @unique
screenings Screening[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Screening {
Expand All @@ -68,3 +70,14 @@ model Ticket {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Review {
id Int @id @default(autoincrement())
content String
customer Customer @relation(fields: [customerId], references: [id])
customerId Int
movie Movie @relation(fields: [movieId], references: [id])
movieId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
60 changes: 56 additions & 4 deletions prisma/seed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function seed() {
await createCustomer();
const customers = await createCustomer();
const movies = await createMovies();
const screens = await createScreens();
await createScreenings(screens, movies);
await createReviews(customers, movies)

process.exit(0);
}

async function createCustomer() {
const customer = await prisma.customer.create({
const customers = [];

const customer1 = await prisma.customer.create({
data: {
name: 'Alice',
contact: {
Expand All @@ -26,9 +29,33 @@ async function createCustomer() {
}
});

console.log('Customer created', customer);
const customer2 = await prisma.customer.create({
data: {
name: 'John',
contact: {
create: {
email: 'john@boolean.co.uk',
phone: '1233687890'
}
}
},
include: {
contact: true
}
});

const rawCustomers = [
customer1,
customer2
]

for (const rawCustomer of rawCustomers) {
customers.push(rawCustomer);
}

console.log('Customer created', customers);

return customer;
return customers;
}

async function createMovies() {
Expand Down Expand Up @@ -97,6 +124,31 @@ async function createScreenings(screens, movies) {
}
}

async function createReviews(customers, movies) {
for (const customer of customers) {
for (let i = 0; i < movies.length; i++) {
Comment on lines +128 to +129
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O(n^2)


const review = await prisma.review.create({
data: {
content: "best movie ever",
movie: {
connect: {
id: movies[i].id
}
},
customer: {
connect: {
id: customer.id
}
}
}
});

console.log('Review created', review);
}
}
}

seed()
.catch(async e => {
console.error(e);
Expand Down
51 changes: 30 additions & 21 deletions src/controllers/customer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const { PrismaClientKnownRequestError } = require("@prisma/client")
const { createCustomerDb } = require('../domains/customer.js')
const { createCustomerDb, updateCustomerDb } = require('../domains/customer.js')
const MissingFieldsError = require("../errors/missingFieldsError.js")
const NotFoundError = require("../errors/notFoundError.js")
const NotUniqueError = require("../errors/notUniqueError.js")

const createCustomer = async (req, res) => {
const {
Expand All @@ -9,40 +12,46 @@ const createCustomer = async (req, res) => {
} = req.body

if (!name || !phone || !email) {
return res.status(400).json({
error: "Missing fields in request body"
})
throw new MissingFieldsError('Missing fields in request body')
}

// Try-catch is a very common way to handle errors in JavaScript.
// It allows us to customise how we want errors that are thrown to be handled.
// Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch

// Here, if Prisma throws an error in the process of trying to create a new customer,
// instead of the Prisma error being thrown (and the app potentially crashing) we exit the
// `try` block (bypassing the `res.status` code) and enter the `catch` block.
try {
const createdCustomer = await createCustomerDb(name, phone, email)

res.status(201).json({ customer: createdCustomer })
} catch (e) {
// In this catch block, we are able to specify how different Prisma errors are handled.
// Prisma throws errors with its own codes. P2002 is the error code for
// "Unique constraint failed on the {constraint}". In our case, the {constraint} is the
// email field which we have set as needing to be unique in the prisma.schema.
// To handle this, we return a custom 409 (conflict) error as a response to the client.
// Prisma error codes: https://www.prisma.io/docs/orm/reference/error-reference#common
// HTTP error codes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2002") {
return res.status(409).json({ error: "A customer with the provided email already exists" })
throw new NotUniqueError("A customer with the provided email already exists" )
}
}
}
}

async function updateCustomer(req, res) {
const customerId = Number(req.params.id)
const { name, contact } = req.body

if (!name) {
throw new MissingFieldsError('Missing fields in request body')
}

try {
const updatedCustomer = await updateCustomerDb(customerId, name, contact)

res.status(500).json({ error: e.message })
res.status(201).json({
customer: updatedCustomer
})
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2025") {
throw new NotFoundError('Customer with that id does not exist')
}
}
}
}

module.exports = {
createCustomer
createCustomer,
updateCustomer
}
87 changes: 87 additions & 0 deletions src/controllers/movie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const { PrismaClientKnownRequestError } = require("@prisma/client/runtime/library")
const { getAllMoviesDb, createMovieDb, getMovieByIdDb, updateMovieDb } = require("../domains/movie")
const MissingFieldsError = require("../errors/missingFieldsError")
const NotFoundError = require("../errors/notFoundError")
const NotUniqueError = require("../errors/notUniqueError")

async function getAllMovies(req, res) {
const runtimeLt = Number(req.query.runtimeLt)
const runtimeGt = Number(req.query.runtimeGt)

const movies = await getAllMoviesDb(runtimeLt, runtimeGt)

res.json({
movies
})
}

async function createMovie(req, res) {
const { title, runtimeMins, screenings } = req.body

if (!title || !runtimeMins) {
throw new MissingFieldsError('Missing fields in request body')
}

try {
const createdMovie = await createMovieDb(title, runtimeMins, screenings)

res.status(201).json({
movie: createdMovie
})
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2002") {
throw new NotUniqueError('A movie with the provided title already exists')
}
}
}
}

async function getMovieByIdOrTitle(req, res) {
const movieId = req.params.id

try {
const movie = await getMovieByIdDb(movieId)

res.json({
movie
})
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2025") {
throw new NotFoundError('Movie with that id or title does not exist')
}
}
}

}

async function updateMovie(req, res) {
const movieId = Number(req.params.id)
const { title, runtimeMins, screenings } = req.body

if (!title || !runtimeMins) {
throw new MissingFieldsError('Missing fields in request body')
}

try {
const updatedMovie = await updateMovieDb(movieId, title, runtimeMins, screenings)

res.status(201).json({
movie: updatedMovie
})
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2025") {
throw new NotFoundError('Movie with that id or title does not exist')
}
}
}
}

module.exports = {
getAllMovies,
createMovie,
getMovieByIdOrTitle,
updateMovie
}
Loading