Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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.

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

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0"
}
}
16 changes: 16 additions & 0 deletions prisma/migrations/20240702164556_reviews/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "Review" (
"id" SERIAL 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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- Added the required column `content` to the `Review` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Review" ADD COLUMN "content" TEXT NOT NULL;
11 changes: 11 additions & 0 deletions prisma/migrations/20240702180147_cascade_deletes/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- DropForeignKey
ALTER TABLE "Review" DROP CONSTRAINT "Review_customerId_fkey";

-- DropForeignKey
ALTER TABLE "Review" DROP CONSTRAINT "Review_movieId_fkey";

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

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_movieId_fkey" FOREIGN KEY ("movieId") REFERENCES "Movie"("id") ON DELETE CASCADE ON UPDATE CASCADE;
13 changes: 13 additions & 0 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,6 +34,7 @@ model Contact {
model Movie {
id Int @id @default(autoincrement())
screenings Screening[]
reviews Review[]
title String
runtimeMins Int
createdAt DateTime @default(now())
Expand Down Expand Up @@ -68,3 +70,14 @@ model Ticket {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Review {
id Int @id @default(autoincrement())
customerId Int
movieId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
movie Movie @relation(fields: [movieId], references: [id], onDelete: Cascade)
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
141 changes: 93 additions & 48 deletions prisma/seed.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,150 @@
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()

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

process.exit(0);
process.exit(0)
}

async function createCustomer() {
const customer = await prisma.customer.create({
data: {
name: 'Alice',
name: "Alice",
contact: {
create: {
email: 'alice@boolean.co.uk',
phone: '1234567890'
}
}
email: "alice@boolean.co.uk",
phone: "1234567890",
},
},
},
include: {
contact: true
}
});
contact: true,
},
})

console.log('Customer created', customer);
console.log("Customer created", customer)

return customer;
return customer
}

async function createMovies() {
const rawMovies = [
{ title: 'The Matrix', runtimeMins: 120 },
{ title: 'Dodgeball', runtimeMins: 154 },
];
{ title: "The Matrix", runtimeMins: 120 },
{ title: "Dodgeball", runtimeMins: 154 },
]

const movies = [];
const movies = []

for (const rawMovie of rawMovies) {
const movie = await prisma.movie.create({ data: rawMovie });
movies.push(movie);
const movie = await prisma.movie.create({ data: rawMovie })
movies.push(movie)
}

console.log('Movies created', movies);
console.log("Movies created", movies)

return movies;
return movies
}

async function createScreens() {
const rawScreens = [
{ number: 1 }, { number: 2 }
];
const rawScreens = [{ number: 1 }, { number: 2 }]

const screens = [];
const screens = []

for (const rawScreen of rawScreens) {
const screen = await prisma.screen.create({
data: rawScreen
});
data: rawScreen,
})

console.log('Screen created', screen);
console.log("Screen created", screen)

screens.push(screen);
screens.push(screen)
}

return screens;
return screens
}

async function createScreenings(screens, movies) {
const screeningDate = new Date();
const screeningDate = new Date()

for (const screen of screens) {
for (let i = 0; i < movies.length; i++) {
screeningDate.setDate(screeningDate.getDate() + i);
screeningDate.setDate(screeningDate.getDate() + i)

const screening = await prisma.screening.create({
data: {
startsAt: screeningDate,
movie: {
connect: {
id: movies[i].id
}
id: movies[i].id,
},
},
screen: {
connect: {
id: screen.id
}
}
}
});
id: screen.id,
},
},
},
})

console.log('Screening created', screening);
console.log("Screening created", screening)
}
}
}

async function creteReviews() {
const customer = await prisma.customer.create({
data: {
name: "Leo",
contact: {
create: {
email: "leo@boolean.co.pt",
phone: "1234567890",
},
},
},
include: {
contact: true,
},
})

const movie = await prisma.movie.create({
data: {
title: "Cars",
runtimeMins: 130,
screenings: {
create: [
{
screenId: 1,
startsAt: "2024-07-02T17:05:34.644Z",
},
],
},
},
})

const review = await prisma.review.create({
data: {
customerId: customer.id,
movieId: movie.id,
content: "Great movie!",
},
include: {
customer: true,
movie: true,
},
})

console.log("Review created", review)
}

seed()
.catch(async e => {
console.error(e);
await prisma.$disconnect();
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
})
.finally(() => process.exit(1));
.finally(() => process.exit(1))
58 changes: 19 additions & 39 deletions src/controllers/customer.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,28 @@
const { PrismaClientKnownRequestError } = require("@prisma/client")
const { createCustomerDb } = require('../domains/customer.js')
const {
createCustomerDb,
updateCostumerDb,
} = require("../domains/customer.js")

const createCustomer = async (req, res) => {
const {
name,
phone,
email
} = req.body
const { name, phone, email } = req.body
const customer = await createCustomerDb(name, phone, email)

if (!name || !phone || !email) {
return res.status(400).json({
error: "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,
})
}

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" })
}
}
const updateCostumer = async (req, res) => {
const paramsId = Number(req.params.id)
Copy link
Contributor

Choose a reason for hiding this comment

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

What if this is not a number?

Always validate everything the client gives you

const { name, contact } = req.body
const customer = await updateCostumerDb(paramsId, name, contact)

res.status(500).json({ error: e.message })
}
res.status(201).json({
customer,
})
}

module.exports = {
createCustomer
createCustomer,
updateCostumer,
}
Loading