diff --git a/src/controller/controller.js b/src/controller/controller.js new file mode 100644 index 000000000..64ebdb8e6 --- /dev/null +++ b/src/controller/controller.js @@ -0,0 +1,271 @@ +const { + createUserObject, + checkExpense, + createExpenseObject, +} = require('../services/services.js'); + +const { + models: { User, Expense }, +} = require('../models/models.js'); + +const getUsers = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + try { + const users = await User.findAll(); + + res.statusCode = 200; + res.send(users); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const getSingleUser = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + + try { + const user = await User.findByPk(Number(id)); + + if (user) { + res.statusCode = 200; + res.send(user); + } else { + res.statusCode = 404; + res.send({ error: 'User not found!' }); + } + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const createUser = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { name } = req.body; + + if (!name) { + res.statusCode = 400; + res.send({ error: 'Bad request!' }); + + return; + } + + try { + const user = await createUserObject(name); + + res.statusCode = 201; + res.send(user); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const deleteUser = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + + try { + const user = await User.findByPk(Number(id)); + + if (!user) { + res.statusCode = 404; + res.send({ error: 'User not found!' }); + + return; + } + + await user.destroy(); + + res.statusCode = 204; + res.end(); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const updateUser = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + const { name } = req.body; + + try { + const user = await User.findByPk(Number(id)); + + if (!user) { + res.statusCode = 404; + res.send({ error: 'User not found!' }); + + return; + } + + await user.update({ name }); + + res.statusCode = 200; + res.send(user); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const getExpenses = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { userId, from, to, categories } = req.query; + + try { + const allExpenses = await Expense.findAll(); + const filteredExpenses = checkExpense( + userId, + from, + to, + categories, + allExpenses, + ); + + res.statusCode = 200; + res.send(filteredExpenses); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const getSingleExpense = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + + try { + const expense = await Expense.findByPk(Number(id)); + + if (!expense) { + res.statusCode = 404; + res.send({ error: 'Expense not found!' }); + + return; + } + + res.statusCode = 200; + res.send(expense); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const createExpense = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { userId, title, amount, category, note, spentAt } = req.body; + + if (!userId || !title || !amount) { + res.statusCode = 400; + res.send({ error: 'Bad request!' }); + + return; + } + + try { + const expense = await createExpenseObject({ + userId, + title, + amount, + category, + note, + spentAt, + }); + + if (!expense) { + res.statusCode = 400; + res.send({ error: 'could not create expense!' }); + + return; + } + + res.statusCode = 201; + res.send(expense); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const deleteExpense = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + + try { + const expense = await Expense.findByPk(Number(id)); + + if (!expense) { + res.statusCode = 404; + res.send({ error: 'Expense not found!' }); + + return; + } + + await expense.destroy(); + + res.statusCode = 204; + res.end(); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +const updateExpense = async (req, res) => { + res.setHeader('Content-Type', 'application/json'); + + const { id } = req.params; + const { title, amount, category, note, spentAt } = req.body; + + try { + const expense = await Expense.findByPk(Number(id)); + + if (!expense) { + res.statusCode = 404; + res.send({ error: 'Expense not found!' }); + + return; + } + + await expense.update({ + title, + amount, + category, + note, + spentAt, + }); + + res.statusCode = 200; + res.send(expense); + } catch (err) { + res.statusCode = 500; + res.send({ error: 'Internal server error!' }); + } +}; + +module.exports = { + createUser, + getUsers, + deleteUser, + getSingleUser, + updateUser, + getExpenses, + getSingleExpense, + createExpense, + deleteExpense, + updateExpense, +}; diff --git a/src/createServer.js b/src/createServer.js index 1ea5542d6..b498d30bf 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,49 @@ 'use strict'; +const express = require('express'); +const { + createUser, + getUsers, + getSingleUser, + deleteUser, + updateUser, + getExpenses, + getSingleExpense, + createExpense, + deleteExpense, + updateExpense, +} = require('./controller/controller.js'); -const createServer = () => { - // your code goes here -}; +const cors = require('cors'); + +function createServer() { + + const app = express(); + + app.use(express.json()); + app.use(cors()) + + app.get('/users', getUsers); + + app.get('/users/:id', getSingleUser); + + app.post('/users', createUser); + + app.delete('/users/:id', deleteUser); + + app.patch('/users/:id', updateUser); + + app.get('/expenses', getExpenses); + + app.get('/expenses/:id', getSingleExpense); + + app.post('/expenses', createExpense); + + app.delete('/expenses/:id', deleteExpense); + + app.patch('/expenses/:id', updateExpense); + + return app; +} module.exports = { createServer, diff --git a/src/db.js b/src/db.js index 1ba3046cc..fdec36a7c 100644 --- a/src/db.js +++ b/src/db.js @@ -26,7 +26,7 @@ const sequelize = new Sequelize({ host: POSTGRES_HOST || 'localhost', dialect: 'postgres', port: POSTGRES_PORT || 5432, - password: POSTGRES_PASSWORD || '123', + password: POSTGRES_PASSWORD || '848677', }); module.exports = { diff --git a/src/models/Expense.model.js b/src/models/Expense.model.js index 567e1c3e7..1943bc884 100644 --- a/src/models/Expense.model.js +++ b/src/models/Expense.model.js @@ -1,9 +1,45 @@ 'use strict'; const { sequelize } = require('../db.js'); +const { DataTypes } = require('sequelize'); const Expense = sequelize.define( - // your code goes here + 'expenses', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + spentAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + title: { + type: DataTypes.STRING, + allowNull: false, + }, + amount: { + type: DataTypes.INTEGER, + allowNull: false, + }, + category: { + type: DataTypes.STRING, + allowNull: true, + }, + note: { + type: DataTypes.STRING, + allowNull: true, + }, + }, + { + timestamps: false, + }, ); module.exports = { diff --git a/src/models/User.model.js b/src/models/User.model.js index 61861c9e4..197ecd1a5 100644 --- a/src/models/User.model.js +++ b/src/models/User.model.js @@ -1,9 +1,25 @@ 'use strict'; const { sequelize } = require('../db.js'); +const { DataTypes } = require('sequelize'); const User = sequelize.define( - // your code goes here + 'users', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + autoIncrementIdentity: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + timestamps: false, + }, ); module.exports = { diff --git a/src/services/services.js b/src/services/services.js new file mode 100644 index 000000000..2022bd3f4 --- /dev/null +++ b/src/services/services.js @@ -0,0 +1,67 @@ +const { + models: { User, Expense }, +} = require('../models/models'); + +const createExpenseObject = async ({ + userId, + category, + amount, + spentAt, + note, + title, +}) => { + const expense = await Expense.create({ + userId, + category, + amount, + spentAt: spentAt || new Date(), + note, + title, + }); + + return expense; +}; + +const checkExpense = (userId, from, to, categories, expenses) => { + let filteredExpenses = [...expenses]; + + if (userId) { + filteredExpenses = filteredExpenses.filter( + (e) => e.userId === Number(userId), + ); + } + + if (from) { + filteredExpenses = filteredExpenses.filter( + (e) => new Date(e.spentAt) >= new Date(from), + ); + } + + if (to) { + filteredExpenses = filteredExpenses.filter( + (e) => new Date(e.spentAt) <= new Date(to), + ); + } + + if (categories) { + const categoryList = categories.split(','); + + filteredExpenses = filteredExpenses.filter((e) => { + return categoryList.includes(e.category); + }); + } + + return filteredExpenses; +}; + +const createUserObject = async (name) => { + const user = await User.create({ name }); + + return user; +}; + +module.exports = { + createUserObject, + checkExpense, + createExpenseObject, +}; diff --git a/src/setup.js b/src/setup.js new file mode 100644 index 000000000..a6f3b1d99 --- /dev/null +++ b/src/setup.js @@ -0,0 +1,37 @@ +const { + models: { User, Expense }, +} = require('./models/models'); +const { sequelize } = require('./db'); + +const setup = async () => { + await sequelize.sync({ force: true }); + + await User.bulkCreate([ + { name: 'John Doe' }, + { name: 'Jane Doe' }, + { name: 'John Smith' }, + ]); + + await Expense.bulkCreate([ + { + userId: 1, + spentAt: '2022-10-19T11:01:43.462Z', + title: 'Buy a new laptop', + amount: 999, + category: 'Electronics', + note: 'I need a new laptop', + }, + { + userId: 1, + spentAt: '2022-10-19T11:01:43.462Z', + title: 'Buy a new TV', + amount: 999, + category: 'Electronics', + note: 'I need a new TV', + }, + ]); + + await sequelize.close(); +}; + +setup();