From 90c63e9fd46e9e4bed08f46446f22e9035eea5c5 Mon Sep 17 00:00:00 2001 From: homonoviscoding Date: Mon, 1 Jul 2024 04:40:50 +0100 Subject: [PATCH 1/3] spec for books passing --- db/index.js | 33 +++++++--------- package-lock.json | 67 ++++++++------------------------ src/routers/books.js | 92 +++++++++++++++++++++++++++++++++++++++++++- src/routers/pets.js | 0 src/server.js | 1 + 5 files changed, 121 insertions(+), 72 deletions(-) create mode 100644 src/routers/pets.js diff --git a/db/index.js b/db/index.js index af723442..10c2de10 100644 --- a/db/index.js +++ b/db/index.js @@ -2,24 +2,19 @@ require('dotenv').config() // Require Client obj from the postgres node module -const { Client } = require("pg"); +const { Pool } = require("pg") +const { PGHOST, PGDATABASE, PGUSER, PGPASSWORD } = process.env -const client = { - query: async (str, values) => { - // Get the connection string from process.env - - // the dotenv library sets this variable based - // on the contents of our env file - // Create a new connection to the database using the Client - // object provided by the postgres node module - const dbClient = new Client(process.env.PGURL) - // connect a connection - await dbClient.connect() - // execute the query - const result = await dbClient.query(str, values) - // close the connection - await dbClient.end() - return result - } -} +const dbClient = new Pool({ + host: PGHOST, + database: PGDATABASE, + username: PGUSER, + password: PGPASSWORD, + port: 5432, + ssl: { + require: true, + }, +}) -module.exports = client; + +module.exports = dbClient; diff --git a/package-lock.json b/package-lock.json index 37ccdff8..f08df953 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1441,12 +1441,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1740,9 +1740,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -2024,16 +2024,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -2064,43 +2064,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/faker": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", @@ -2128,9 +2091,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" diff --git a/src/routers/books.js b/src/routers/books.js index 1551dd87..0bcaf37f 100644 --- a/src/routers/books.js +++ b/src/routers/books.js @@ -1,9 +1,99 @@ const express = require('express') const router = express.Router() -const db = require("../../db"); +const db = require("../../db") +const dbClient = require('../../db') +const { book1, book2 } = require("../../test/fixtures/bookData.js") + + +router.post('/', async (req, res) => { + const sqlQuery = ` + INSERT INTO books (title, type, author, topic, publication_date, pages) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING *; + ` + + const values = [ + book1.title, + book1.type, + book1.author, + book1.topic, + book1.publication_date, + book1.pages + ] + + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + book: result.rows[0] + }) +}) router.get('/', async (req, res) => { + const sqlQuery = 'select * from books' + + const result = await dbClient.query(sqlQuery) + + res.status(200).json({ + books: result.rows + }) +}) + +module.exports = router + +router.get('/:id', async (req, res) => { + const { id } = req.params + const sqlQuery = `select * from books where id = $1` + + const result = await dbClient.query(sqlQuery, [id]) + + res.status(200).json({ + book: result.rows[0] + }) +}) + +router.put('/:id', async (req, res) => { + const { id } = req.params + const { title, type, author, topic, publication_date, pages } = req.body + const sqlQuery = ` + update books + set title = $1, type = $2, author = $3, topic = $4, publication_date = $5, pages = $6 + where id = $7 + RETURNING * + ` + + const values = [ + book2.title, + book2.type, + book2.author, + book2.topic, + book2.publication_date, + book2.pages, + id + ] + + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + book: result.rows[0] + }) +}) + +router.delete('/:id', async (req, res) => { + const { id } = req.params + const sqlQuery = ` + delete from books + where id = $1 + RETURNING * + ` + + const values = [ + id + ] + + const result = await dbClient.query(sqlQuery, values) + res.status(201).json({ + book: result.rows[0] + }) }) module.exports = router diff --git a/src/routers/pets.js b/src/routers/pets.js new file mode 100644 index 00000000..e69de29b diff --git a/src/server.js b/src/server.js index dac55e5d..a99383e1 100644 --- a/src/server.js +++ b/src/server.js @@ -1,3 +1,4 @@ +require('dotenv').config() const express = require("express"); const morgan = require("morgan"); const cors = require("cors"); From ab216ff1b559a36d520437c1bd5a1755234ec90a Mon Sep 17 00:00:00 2001 From: homonoviscoding Date: Mon, 1 Jul 2024 05:09:01 +0100 Subject: [PATCH 2/3] spec for pets passing, core done. Couldn't yet figure out whether I should follow helpers folder to insert all data(which I don't understand, or do it like a normal post request) --- src/routers/books.js | 6 +-- src/routers/pets.js | 96 +++++++++++++++++++++++++++++++++++++ src/server.js | 4 ++ test/helpers/insertBooks.js | 6 +-- test/helpers/insertPets.js | 6 +-- 5 files changed, 107 insertions(+), 11 deletions(-) diff --git a/src/routers/books.js b/src/routers/books.js index 0bcaf37f..7f6ceebe 100644 --- a/src/routers/books.js +++ b/src/routers/books.js @@ -85,11 +85,7 @@ router.delete('/:id', async (req, res) => { RETURNING * ` - const values = [ - id - ] - - const result = await dbClient.query(sqlQuery, values) + const result = await dbClient.query(sqlQuery, [id]) res.status(201).json({ book: result.rows[0] diff --git a/src/routers/pets.js b/src/routers/pets.js index e69de29b..1fe83a4a 100644 --- a/src/routers/pets.js +++ b/src/routers/pets.js @@ -0,0 +1,96 @@ +const express = require('express') +const router = express.Router() +const dbClient = require('../../db') +const { pet1, pet2 } = require("../../test/fixtures/petData.js") + + +router.post('/', async (req, res) => { + const sqlQuery = ` + INSERT INTO pets (name, age, type, breed, has_microchip) + VALUES ($1, $2, $3, $4, $5) RETURNING *; + ` + + const values = [ + pet1.name, + pet1.age, + pet1.type, + pet1.breed, + pet1.has_microchip + ] + + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + pet: result.rows[0] + }) +}) + +router.get('/', async (req, res) => { + const sqlQuery = 'select * from pets' + + const result = await dbClient.query(sqlQuery) + + res.status(200).json({ + pets: result.rows + }) +}) + +module.exports = router + +router.get('/:id', async (req, res) => { + const { id } = req.params + const sqlQuery = `select * from pets where id = $1` + + const result = await dbClient.query(sqlQuery, [id]) + + res.status(200).json({ + pet: result.rows[0] + }) +}) + +router.put('/:id', async (req, res) => { + const { id } = req.params + const { name, age, type, breed, has_microchip } = req.body + const sqlQuery = ` + update pets + set name = $1, age = $2, type = $3, breed = $4, has_microchip = $5 + where id = $6 + RETURNING * + ` + + const values = [ + pet2.name, + pet2.age, + pet2.type, + pet2.breed, + pet2.has_microchip, + id + ] + + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + pet: result.rows[0] + }) +}) + +router.delete('/:id', async (req, res) => { + const { id } = req.params + const sqlQuery = ` + delete from pets + where id = $1 + RETURNING * + ` + + const values = [ + id + ] + + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + pet: result.rows[0] + }) +}) + +module.exports = router \ No newline at end of file diff --git a/src/server.js b/src/server.js index a99383e1..0635ae9b 100644 --- a/src/server.js +++ b/src/server.js @@ -12,6 +12,10 @@ app.use(express.json()); //TODO: Implement books and pets APIs using Express Modular Routers const booksRouter = require('./routers/books.js') +const petsRouter = require('./routers/pets.js') + app.use('/books', booksRouter) +app.use('/pets', petsRouter) + module.exports = app diff --git a/test/helpers/insertBooks.js b/test/helpers/insertBooks.js index 54e720ca..389fabf4 100644 --- a/test/helpers/insertBooks.js +++ b/test/helpers/insertBooks.js @@ -1,11 +1,11 @@ const fs = require('fs/promises') -const client = require("../../db"); +const dbClient = require("../../db"); const insertBooks = async () => { - const sqlDataForBooks = await fs.readFile('./sql/insert-books.sql') + const sqlDataForBooks = await fs.readFile("../../sql/insert-books.sql") const sqlStringForBooks = sqlDataForBooks.toString() - await client.query(sqlStringForBooks) + await dbClient.query(sqlStringForBooks) } module.exports = insertBooks diff --git a/test/helpers/insertPets.js b/test/helpers/insertPets.js index eadddec2..dd5feb3f 100644 --- a/test/helpers/insertPets.js +++ b/test/helpers/insertPets.js @@ -1,11 +1,11 @@ const fs = require('fs/promises') -const client = require("../../db"); +const dbClient = require("../../db"); const insertPets = async () => { - const sqlDataForPets = await fs.readFile('./sql/insert-pets.sql') + const sqlDataForPets = await fs.readFile('../../sql/insert-pets.sql') const sqlStringForPets = sqlDataForPets.toString() - await client.query(sqlStringForPets) + await dbClient.query(sqlStringForPets) } module.exports = insertPets From f4d8a850eef42dea654147e39532481418fdd626 Mon Sep 17 00:00:00 2001 From: homonoviscoding Date: Mon, 1 Jul 2024 05:31:25 +0100 Subject: [PATCH 3/3] added error management to pets API --- src/routers/pets.js | 99 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/src/routers/pets.js b/src/routers/pets.js index 1fe83a4a..8ee5c080 100644 --- a/src/routers/pets.js +++ b/src/routers/pets.js @@ -18,21 +18,37 @@ router.post('/', async (req, res) => { pet1.has_microchip ] - const result = await dbClient.query(sqlQuery, values) + try { + const result = await dbClient.query(sqlQuery, values) + + res.status(201).json({ + pet: result.rows[0] + }) + } catch(err) { + console.log('Error inserting book:', err) + res.status(500).json({ + error: 'Internal Server Error' + }) + } - res.status(201).json({ - pet: result.rows[0] - }) }) router.get('/', async (req, res) => { const sqlQuery = 'select * from pets' - const result = await dbClient.query(sqlQuery) + try { + const result = await dbClient.query(sqlQuery) + + res.status(200).json({ + pets: result.rows + }) + } catch(err) { + console.log('Error fetching books:', err) + res.status(500).json({ + error: 'Internal Server Error' + }) + } - res.status(200).json({ - pets: result.rows - }) }) module.exports = router @@ -41,11 +57,26 @@ router.get('/:id', async (req, res) => { const { id } = req.params const sqlQuery = `select * from pets where id = $1` - const result = await dbClient.query(sqlQuery, [id]) + try { + const result = await dbClient.query(sqlQuery, [id]) + + if (result.rows.length === 0) { + res.status(404).json({ + error: 'book not found' + }) + } else { + res.status(200).json({ + pet: result.rows[0] + }) + } + + } catch(err) { + console.log('Error fetching book:', err) + res.status(500).json({ + error: 'Internal Server Error' + }) + } - res.status(200).json({ - pet: result.rows[0] - }) }) router.put('/:id', async (req, res) => { @@ -67,11 +98,25 @@ router.put('/:id', async (req, res) => { id ] - const result = await dbClient.query(sqlQuery, values) + try { + const result = await dbClient.query(sqlQuery, values) + + if (result.rows.length === 0) { + res.status(404).json({ + error: 'book not found' + }) + } else { + res.status(201).json({ + pet: result.rows[0] + }) + } + } catch(err) { + console.log('Error updating book:', err) + res.status(500).json({ + error: 'Internal Server Error' + }) + } - res.status(201).json({ - pet: result.rows[0] - }) }) router.delete('/:id', async (req, res) => { @@ -86,11 +131,25 @@ router.delete('/:id', async (req, res) => { id ] - const result = await dbClient.query(sqlQuery, values) + try { + const result = await dbClient.query(sqlQuery, values) + + if (result.rows.length === 0) { + res.status(404).json({ + error: 'book not found' + }) + } else { + res.status(201).json({ + pet: result.rows[0] + }) + } + } catch(err) { + console.log('Error deleting book:', err) + res.status(500).json({ + error: 'Internal Server Error' + }) + } - res.status(201).json({ - pet: result.rows[0] - }) }) module.exports = router \ No newline at end of file