diff --git a/.env.example b/.env.example deleted file mode 100644 index 60f3a816..00000000 --- a/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -DATABASE_URL="YOUR_DB_URL" - -# We need the following URL environment variable for test purposes: -# - TEST_DATABASE_URL must be a **completely separate** database from any other used in this file - -TEST_DATABASE_URL="YOUR_TEST_DB_URL" diff --git a/src/controllers/customer.js b/src/controllers/customer.js index 775cfb42..df25613f 100644 --- a/src/controllers/customer.js +++ b/src/controllers/customer.js @@ -1,17 +1,13 @@ -const { PrismaClientKnownRequestError } = require("@prisma/client") -const { createCustomerDb } = require('../domains/customer.js') +const { PrismaClientKnownRequestError } = require("@prisma/client"); +const { createCustomerDb, updateCustomerDb } = require('../domains/customer.js'); const createCustomer = async (req, res) => { - const { - name, - phone, - email - } = req.body + const { name, phone, email } = req.body; 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. @@ -22,9 +18,9 @@ const createCustomer = async (req, res) => { // 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) + const createdCustomer = await createCustomerDb(name, phone, email); - res.status(201).json({ customer: createdCustomer }) + 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 @@ -35,14 +31,48 @@ const createCustomer = async (req, res) => { // 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" }) + return res.status(409).json({ error: "A customer with the provided email already exists" }); } } - res.status(500).json({ error: e.message }) + // Log the error for debugging purposes + console.error('Error creating customer:', e); + + res.status(500).json({ error: e.message }); + } +}; + +const updateCustomer = async (req, res) => { + const { id } = req.params; + const { name, phone, email } = req.body; + + if (!name && !phone && !email) { + return res.status(400).json({ + error: "At least one field must be provided to update" + }); + } + + try { + const updatedCustomer = await updateCustomerDb(id, { name, phone, email }); + + if (!updatedCustomer) { + return res.status(404).json({ error: "Customer not found" }); + } + + res.status(201).json({ customer: updatedCustomer }); + } catch (e) { + // Log the error for debugging purposes + console.error('Error updating customer:', e); + + if (e instanceof PrismaClientKnownRequestError) { + return res.status(400).json({ error: 'A known error occurred while updating the customer.' }); + } + + res.status(500).json({ error: e.message }); } -} +}; module.exports = { - createCustomer -} + createCustomer, + updateCustomer +}; diff --git a/src/controllers/movie.js b/src/controllers/movie.js new file mode 100644 index 00000000..98917edd --- /dev/null +++ b/src/controllers/movie.js @@ -0,0 +1,93 @@ +const { PrismaClient } = require("@prisma/client"); //import PrismaClient! +const prisma = new PrismaClient(); //saved as Prisma + +//Getting all the movies +const getMovies = async (req, res) => { + try { + const movies = await prisma.movie.findMany({ + include: { + screenings: true, + }, + }); + res.status(200).json({ movies }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; + +//creating movies +const createMovie = async (req, res) => { + const { title, runtimeMins } = req.body; + + if (!title || !runtimeMins) { + return res.status(400).json({ error: "Missing fields in request body" }); + } + + try { + const newMovie = await prisma.movie.create({ + data: { + title, + runtimeMins, + }, + include: { + screenings: true, + }, + }); + res.status(201).json({ movie: newMovie }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; + +// Getting the moovies by Id +const getMovieById = async (req, res) => { + const { id } = req.params; + + try { + const movie = await prisma.movie.findUnique({ + where: { id: Number(id) }, + include: { + screenings: true, + }, + }); + + if (!movie) { + return res.status(404).json({ error: "Movie not found" }); + } + + res.status(200).json({ movie }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; + +//Updating +const updateMovieById = async (req, res) => { + const { id } = req.params; + const { title, runtimeMins } = req.body; + + try { + const updatedMovie = await prisma.movie.update({ + where: { id: Number(id) }, + data: { + title, + runtimeMins, + }, + include: { + screenings: true, + }, + }); + + res.status(201).json({ movie: updatedMovie }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; + +//Exporting +module.exports = { + getMovies, + createMovie, + getMovieById, + updateMovieById, +}; diff --git a/src/controllers/screen.js b/src/controllers/screen.js new file mode 100644 index 00000000..f1ea49e0 --- /dev/null +++ b/src/controllers/screen.js @@ -0,0 +1,21 @@ +// src/controllers/screen.js +const { createScreenDb } = require('../domains/screen'); + +const createScreen = async (req, res) => { + const { number } = req.body; + + if (!number) { + return res.status(400).json({ error: "Number field is required" }); + } + + try { + const createdScreen = await createScreenDb(number); + res.status(201).json({ screen: createdScreen }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; + +module.exports = { + createScreen, +}; diff --git a/src/domains/customer.js b/src/domains/customer.js index c7f315fd..c017d0a8 100644 --- a/src/domains/customer.js +++ b/src/domains/customer.js @@ -1,26 +1,46 @@ -const prisma = require('../utils/prisma') +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); -/** - * This will create a Customer AND create a new Contact, then automatically relate them with each other - * @tutorial https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#create-a-related-record - */ -const createCustomerDb = async (name, phone, email) => await prisma.customer.create({ - data: { - name, - contact: { - create: { - phone, - email +const createCustomerDb = async (name, phone, email) => { + return prisma.customer.create({ + data: { + name, + contact: { + create: { + phone, + email + } } + }, + include: { + contact: true } - }, - // We add an `include` outside of the `data` object to make sure the new contact is returned in the result - // This is like doing RETURNING in SQL - include: { - contact: true + }); +}; + +const updateCustomerDb = async (id, data) => { + const updateData = {}; + + if (data.name) updateData.name = data.name; + if (data.phone || data.email) { + updateData.contact = { + update: {} + }; + + if (data.phone) updateData.contact.update.phone = data.phone; + if (data.email) updateData.contact.update.email = data.email; } -}) + + return prisma.customer.update({ + where: { id: Number(id) }, + data: updateData, + include: { + contact: true + } + }); +}; module.exports = { - createCustomerDb -} + createCustomerDb, + updateCustomerDb +}; diff --git a/src/domains/movie.js b/src/domains/movie.js new file mode 100644 index 00000000..2f320c75 --- /dev/null +++ b/src/domains/movie.js @@ -0,0 +1,134 @@ +const { PrismaClient, PrismaClientKnownRequestError } = require("@prisma/client"); // Import PrismaClient and PrismaClientKnownRequestError +const prisma = new PrismaClient(); // Save as Prisma + +// Getting all the movies +const getMovies = async (req, res) => { + try { + const movies = await prisma.movie.findMany({ + include: { + screenings: true, + }, + }); + res.status(200).json({ movies }); + } catch (e) { + // Log the error for debugging purposes + console.error('Error fetching movies:', e); + + if (e instanceof PrismaClientKnownRequestError) { + // Handle known Prisma errors + return res.status(400).json({ error: 'A known error occurred while fetching movies.' }); + } + + res.status(500).json({ error: e.message }); + } +}; + +// Creating a new movie +const createMovie = async (req, res) => { + const { title, runtimeMins } = req.body; + + if (!title || !runtimeMins) { + 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 movie, + // 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 newMovie = await prisma.movie.create({ + data: { + title, + runtimeMins, + }, + include: { + screenings: true, + }, + }); + res.status(201).json({ movie: newMovie }); + } catch (e) { + // Log the error for debugging purposes + console.error('Error creating movie:', e); + + if (e instanceof PrismaClientKnownRequestError) { + // Handle known Prisma errors + if (e.code === 'P2002') { + return res.status(409).json({ error: 'A movie with the provided title already exists' }); + } + } + + res.status(500).json({ error: e.message }); + } +}; + +// Getting a movie by ID +const getMovieById = async (req, res) => { + const { id } = req.params; + + try { + const movie = await prisma.movie.findUnique({ + where: { id: Number(id) }, + include: { + screenings: true, + }, + }); + + if (!movie) { + return res.status(404).json({ error: "Movie not found" }); + } + + res.status(200).json({ movie }); + } catch (e) { + // Log the error for debugging purposes + console.error('Error fetching movie by ID:', e); + + if (e instanceof PrismaClientKnownRequestError) { + // Handle known Prisma errors + return res.status(400).json({ error: 'A known error occurred while fetching the movie by ID.' }); + } + + res.status(500).json({ error: e.message }); + } +}; + +// Updating a movie by ID +const updateMovieById = async (req, res) => { + const { id } = req.params; + const { title, runtimeMins } = req.body; + + try { + const updatedMovie = await prisma.movie.update({ + where: { id: Number(id) }, + data: { + title, + runtimeMins, + }, + include: { + screenings: true, + }, + }); + + res.status(201).json({ movie: updatedMovie }); + } catch (e) { + // Log the error for debugging purposes + console.error('Error updating movie:', e); + + if (e instanceof PrismaClientKnownRequestError) { + // Handle known Prisma errors + return res.status(400).json({ error: 'A known error occurred while updating the movie.' }); + } + + res.status(500).json({ error: e.message }); + } +}; + +// Exporting the functions +module.exports = { + getMovies, + createMovie, + getMovieById, + updateMovieById, +}; diff --git a/src/domains/screen.js b/src/domains/screen.js new file mode 100644 index 00000000..a87fed0c --- /dev/null +++ b/src/domains/screen.js @@ -0,0 +1,24 @@ +const { PrismaClient, PrismaClientKnownRequestError } = require("@prisma/client"); +const prisma = require('../utils/prisma'); + +const createScreenDb = async (number) => { + try { + return await prisma.screen.create({ + data: { + number, + }, + }); + } catch (e) { + if (e instanceof PrismaClientKnownRequestError) { + // Handle Prisma specific errors if needed + throw e; + } else { + // Handle any other errors + throw new Error("An unexpected error occurred while creating the screen."); + } + } +}; + +module.exports = { + createScreenDb, +}; diff --git a/src/routers/customer.js b/src/routers/customer.js index f14a87fc..0a981545 100644 --- a/src/routers/customer.js +++ b/src/routers/customer.js @@ -1,7 +1,7 @@ const express = require("express"); -const { - createCustomer -} = require('../controllers/customer'); +const {createCustomer, updateCustomer} = require('../controllers/customer'); + + const router = express.Router(); @@ -9,5 +9,6 @@ const router = express.Router(); // The below /register route extends that, so the end result will be a URL // that looks like http://localhost:4040/customer/register router.post("/register", createCustomer); +router.put("/:id", updateCustomer); module.exports = router; diff --git a/src/routers/movie.js b/src/routers/movie.js new file mode 100644 index 00000000..25008cd2 --- /dev/null +++ b/src/routers/movie.js @@ -0,0 +1,17 @@ +const express = require("express"); +const { + getMovies, + createMovie, + getMovieById, + updateMovieById, +} = require("../controllers/movie"); //declared them once, eazy dokey!! + +const router = express.Router(); + +// Defined my routes for easy and smooth movie operations!!!! +router.get("/", getMovies); +router.post("/", createMovie); +router.get("/:id", getMovieById); +router.put("/:id", updateMovieById); + +module.exports = router; diff --git a/src/routers/screen.js b/src/routers/screen.js new file mode 100644 index 00000000..9fe7d3b5 --- /dev/null +++ b/src/routers/screen.js @@ -0,0 +1,9 @@ +// src/routers/screen.js +const express = require('express'); +const { createScreen } = require('../controllers/screen'); + +const router = express.Router(); + +router.post('/', createScreen); + +module.exports = router; diff --git a/src/server.js b/src/server.js index 93d47a16..489a3bbf 100644 --- a/src/server.js +++ b/src/server.js @@ -12,10 +12,15 @@ app.use(morgan('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: true })); - -// Tell express to use your routers here +// Telling express to use my routers here const customerRouter = require('./routers/customer'); +const movieRouter = require('./routers/movie'); +const screenRouter = require('./routers/screen'); + app.use('/customers', customerRouter); +app.use('/movies', movieRouter); +app.use('/screens', screenRouter) + -module.exports = app +module.exports = app;