diff --git a/db/index.js b/db/index.js deleted file mode 100644 index af723442..00000000 --- a/db/index.js +++ /dev/null @@ -1,25 +0,0 @@ -// Load our .env file -require('dotenv').config() - -// Require Client obj from the postgres node module -const { Client } = require("pg"); - -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 - } -} - -module.exports = client; diff --git a/src/controllers/books/index.js b/src/controllers/books/index.js new file mode 100644 index 00000000..0b8adfba --- /dev/null +++ b/src/controllers/books/index.js @@ -0,0 +1,110 @@ +const dbConnection = require('../../utils/dbConnection.js') + +const getAllBooks = async(req, page, perPage) => { + const db = await dbConnection.connect() + + const author = req.body.author + let offset + + if(page > 1) { + offset = perPage-1 + } + + try { + if(author === undefined) { + const sqlQuery = `select * from books limit $1 offset $2` + const result = await db.query(sqlQuery, [perPage, offset]) + return result.rows + } + const sqlQuery = `select * from books where author = $1 limit $2 offset $3 order by author` + const result = await db.query(sqlQuery, [author, perPage, offset]) + + return result.rows + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const createBook = async(req) => { + const db = await dbConnection.connect() + + const title = req.body.title + const type = req.body.type + const author = req.body.author + const topic = req.body.topic + const pub_date = req.body.publication_date + const pages = Number(req.body.pages) + try { + const sqlQuery = `insert into books (title, type, author, topic, publication_date, pages) + values($1, $2, $3, $4, $5, $6) returning *` + const result = await db.query(sqlQuery, [title, type, author, topic, pub_date, pages]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const getBookByID = async(req) => { + const id = req.params.id + const db = await dbConnection.connect() + try { + const sqlQuery = 'select * from books where id =' + id + const result = await db.query(sqlQuery) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const updateBook = async(req) => { + const db = await dbConnection.connect() + const id = req.params.id + const title = req.body.title + const type = req.body.type + const author = req.body.author + const topic = req.body.topic + const pub_date = req.body.publication_date + const pages = Number(req.body.pages) + try { + const sqlQuery = `update books set title = $1, type = $2, author = $3, topic = $4, publication_date = $5, pages = $6 where id=${id} returning *` + const result = await db.query(sqlQuery, [title, type, author, topic, pub_date, pages]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const deleteBook = async(req) => { + const id = Number(req.params.id) + const db = await dbConnection.connect() + try { + const sqlQuery = 'delete from books where id=$1 returning *' + const result = await db.query(sqlQuery, [id]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + + +module.exports = { + getAllBooks, + createBook, + getBookByID, + updateBook, + deleteBook +} \ No newline at end of file diff --git a/src/controllers/pets/index.js b/src/controllers/pets/index.js new file mode 100644 index 00000000..d31e9236 --- /dev/null +++ b/src/controllers/pets/index.js @@ -0,0 +1,94 @@ +const dbConnection = require('../../utils/dbConnection.js') + +const getAllPets = async () => { + const db = await dbConnection.connect() + try { + const sqlQuery = 'select * from pets' + const result = await db.query(sqlQuery) + + return result.rows + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const addPet = async (req) => { + const db = await dbConnection.connect() + + const name = req.body.name + const age = Number(req.body.age) + const type = req.body.type + const breed = req.body.breed + const mchip = Boolean(req.body.has_microchip) + try { + const sqlQuery = 'insert into pets (name, age, type, breed, has_microchip) values($1, $2, $3, $4, $5) returning *' + const result = await db.query(sqlQuery, [name, age, type, breed, mchip]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const getPetByID = async(req) => { + const id = Number(req.params.id) + const db = await dbConnection.connect() + try { + const sqlQuery = 'select * from pets where id ='+id + const result = await db.query(sqlQuery) + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const updatePet = async(req) => { + const db = await dbConnection.connect() + + const id = Number(req.params.id) + const name = req.body.name + const age = Number(req.body.age) + const type = req.body.type + const breed = req.body.breed + const mchip = Boolean(req.body.has_microchip) + try { + const sqlQuery = `update pets set name = $1, age = $2, type = $3, breed = $4, has_microchip = $5 where id=$6 returning *` + const result = await db.query(sqlQuery, [name, age, type, breed, mchip, id]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +const deletePet = async (req) => { + const id = Number(req.params.id) + const db = await dbConnection.connect() + + try { + const sqlQuery = 'delete from pets where id=$1 returning *' + const result = await db.query(sqlQuery, [id]) + + return result.rows[0] + } catch (e) { + console.log(e) + } finally { + db.release() + } +} + +module.exports = { + getAllPets, + addPet, + getPetByID, + updatePet, + deletePet +} \ No newline at end of file diff --git a/src/routers/books.js b/src/routers/books.js index 1551dd87..6f7c4ff5 100644 --- a/src/routers/books.js +++ b/src/routers/books.js @@ -1,9 +1,74 @@ const express = require('express') +const { getAllBooks, createBook, getBookByID, updateBook, deleteBook } = require('../controllers/books/index.js') const router = express.Router() -const db = require("../../db"); + router.get('/', async (req, res) => { + let page = req.body.page + let perPage = req.body.perPage + + if ( + page === undefined || + page === 0 + ) + { + page = 1 + } + if ( + perPage === undefined || + perPage === 0) + { + perPage = 20 + } + if ( + perPage > 50 + ) { + perPage = 50 + } else if ( + perPage < 10 + ) { + perPage = 10 + } + + const books = await getAllBooks(req, page, perPage) + + res.status(200).json({ + books, + perPage: perPage, + page: page + }) }) +router.post('/', async (req, res) => { + + const newBook = await createBook(req) + res.status(201).json({ + book: newBook + }) +}) + +router.get('/:id', async (req, res) => { + const book = await getBookByID(req) + + res.status(200).json({ + book: book + }) +}) + +router.put('/:id', async(req, res) => { + const updatedBook = await updateBook(req) + + res.status(201).json({ + book: updatedBook + }) +}) + +router.delete('/:id', async(req, res) => { + const deletedBook = await deleteBook(req) + + res.status(201).json({ + book: deletedBook + }) +}) module.exports = router diff --git a/src/routers/pets.js b/src/routers/pets.js new file mode 100644 index 00000000..1dd7f710 --- /dev/null +++ b/src/routers/pets.js @@ -0,0 +1,44 @@ +const express = require('express') +const { getAllPets, addPet, getPetByID, updatePet, deletePet } = require('../controllers/pets') +const router = express.Router() + +router.get('/', async(req, res) => { + const pets = await getAllPets() + + res.status(200).json({ + pets + }) +}) + +router.post('/', async(req, res) => { + const newPet = await addPet(req) + + res.status(201).json({ + pet: newPet + }) +}) + +router.get('/:id', async(req, res) => { + const pet = await getPetByID(req) + + res.status(200).json({ + pet: pet + }) +}) + +router.put('/:id', async (req, res) => { + const updatedPet = await updatePet(req) + + res.status(201).json({ + pet: updatedPet + }) +}) + +router.delete('/:id', async (req, res) => { + const deletedPet = await deletePet(req) + res.status(201).json({ + pet: deletedPet + }) +}) + +module.exports = router diff --git a/src/server.js b/src/server.js index dac55e5d..73803805 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,7 @@ const express = require("express"); const morgan = require("morgan"); const cors = require("cors"); +require('dotenv').config() const app = express(); @@ -10,7 +11,9 @@ 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/src/utils/dbConnection.js b/src/utils/dbConnection.js new file mode 100644 index 00000000..3272305e --- /dev/null +++ b/src/utils/dbConnection.js @@ -0,0 +1,16 @@ +const { Pool } = require('pg'); + +const { PGHOST, PGDATABASE, PGUSER, PGPASSWORD } = process.env; + +const dbConnection = new Pool({ + host: PGHOST, + database: PGDATABASE, + username: PGUSER, + password: PGPASSWORD, + port: 5432, + ssl: { + require: true, + }, +}) + +module.exports = dbConnection \ No newline at end of file diff --git a/test/api/extensions/books.spec.js b/test/api/extensions/books.spec.js index cd8e011c..6e5dfd11 100644 --- a/test/api/extensions/books.spec.js +++ b/test/api/extensions/books.spec.js @@ -25,7 +25,7 @@ describe("Books Endpoint", () => { await createBook(Object.values(book2)) }) - it("will return books by author", async () => { + fit("will return books by author", async () => { const response = await supertest(app).get(`/books?author=${book1.author}`) expect(response.status).toEqual(200) @@ -43,7 +43,7 @@ describe("Books Endpoint", () => { await insertBooks() }) - it("response has pagination properties", async () => { + fit("response has pagination properties", async () => { const response = await supertest(app).get(`/books?page=2&perPage=30`) expect(response.status).toEqual(200) @@ -52,7 +52,7 @@ describe("Books Endpoint", () => { expect(response.body.per_page).toEqual(30) }) - it("will return page 2 only of 30 books", async () => { + fit("will return page 2 only of 30 books", async () => { const response = await supertest(app).get(`/books?page=2&perPage=30`) expect(response.status).toEqual(200) @@ -62,21 +62,21 @@ describe("Books Endpoint", () => { expect(response.body.books[response.body.books.length-1].id).toEqual(60) }) - it("will return 400 if pagination parameters are invalid", async () => { + fit("will return 400 if pagination parameters are invalid", async () => { const response = await supertest(app).get(`/books?page=1&perPage=9`) expect(response.status).toEqual(400) expect(response.body.error).toEqual(`parameter invalid perPage: 9 not valid. Accepted range is 10 - 50`) }) - it("will return 400 if pagination parameters are invalid", async () => { + fit("will return 400 if pagination parameters are invalid", async () => { const response = await supertest(app).get(`/books?page=1&perPage=51`) expect(response.status).toEqual(400) expect(response.body.error).toEqual(`parameter invalid perPage: 51 not valid. Accepted range is 10 - 50`) }) - it("pagination parameters are optional, have default values", async () => { + fit("pagination parameters are optional, have default values", async () => { const response = await supertest(app).get(`/books`) expect(response.status).toEqual(200) @@ -86,7 +86,7 @@ describe("Books Endpoint", () => { expect(response.body.books[response.body.books.length-1].id).toEqual(20) }) - it("pagination parameters can be used with author parameter", async () => { + fit("pagination parameters can be used with author parameter", async () => { const response = await supertest(app).get(`/books?author=Nick%20Labadie&page=1&perPage=30`) expect(response.status).toEqual(200) diff --git a/test/database-cleaner/index.js b/test/database-cleaner/index.js index 13a4e464..5bb1aabc 100644 --- a/test/database-cleaner/index.js +++ b/test/database-cleaner/index.js @@ -1,5 +1,6 @@ const fs = require('fs/promises') -const client = require("../../db"); +const client = require('../../src/utils/dbConnection.js') + global.beforeEach(async() => { const sqlDataForBooks = await fs.readFile('./sql/create-books.sql') diff --git a/test/helpers/createBook.js b/test/helpers/createBook.js index 5f7e9c76..8369f1c9 100644 --- a/test/helpers/createBook.js +++ b/test/helpers/createBook.js @@ -1,4 +1,4 @@ -const client = require("../../db"); +const client = require('../../src/utils/dbConnection.js'); const createBook = async (values) => { const sqlString = `INSERT INTO "books" (title, type, author, topic, publication_date, pages) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;` diff --git a/test/helpers/createPet.js b/test/helpers/createPet.js index 8dfca845..1f462790 100644 --- a/test/helpers/createPet.js +++ b/test/helpers/createPet.js @@ -1,4 +1,4 @@ -const client = require("../../db"); +const client = require('../../src/utils/dbConnection.js'); const createPet = async (values) => { const sqlString = `INSERT INTO "pets" (name, age, type, breed, has_microchip) VALUES ($1, $2, $3, $4, $5) RETURNING *;` diff --git a/test/helpers/insertBooks.js b/test/helpers/insertBooks.js index 54e720ca..ce56ddde 100644 --- a/test/helpers/insertBooks.js +++ b/test/helpers/insertBooks.js @@ -1,5 +1,5 @@ const fs = require('fs/promises') -const client = require("../../db"); +const client = require('../../src/utils/dbConnection.js'); const insertBooks = async () => { const sqlDataForBooks = await fs.readFile('./sql/insert-books.sql') diff --git a/test/helpers/insertPets.js b/test/helpers/insertPets.js index eadddec2..cb97aac6 100644 --- a/test/helpers/insertPets.js +++ b/test/helpers/insertPets.js @@ -1,5 +1,5 @@ const fs = require('fs/promises') -const client = require("../../db"); +const client = require('../../src/utils/dbConnection.js'); const insertPets = async () => { const sqlDataForPets = await fs.readFile('./sql/insert-pets.sql')