diff --git a/src/controllers/categories.controller.js b/src/controllers/categories.controller.js new file mode 100644 index 000000000..86669d7a0 --- /dev/null +++ b/src/controllers/categories.controller.js @@ -0,0 +1,23 @@ +const express = require('express'); + +const { + getAllCategories, + getCategoryById, + deleteCategory, + createCategory, + updateCategory, +} = require('../routers/categories.router.js'); + +const categoriesRoute = express.Router(); + +categoriesRoute.get('/', getAllCategories); + +categoriesRoute.get('/:id', getCategoryById); + +categoriesRoute.post('/', createCategory); + +categoriesRoute.delete('/:id', deleteCategory); + +categoriesRoute.patch('/:id', updateCategory); + +module.exports = { categoriesRoute }; diff --git a/src/controllers/expenses.controller.js b/src/controllers/expenses.controller.js new file mode 100644 index 000000000..a584d7d27 --- /dev/null +++ b/src/controllers/expenses.controller.js @@ -0,0 +1,23 @@ +const express = require('express'); + +const { + getAllExpenses, + getExpenseById, + deleteExpense, + createExpense, + updateExpense, +} = require('../routers/expenses.router.js'); + +const expensesRoute = express.Router(); + +expensesRoute.get('/', getAllExpenses); + +expensesRoute.get('/:id', getExpenseById); + +expensesRoute.post('/', createExpense); + +expensesRoute.delete('/:id', deleteExpense); + +expensesRoute.patch('/:id', updateExpense); + +module.exports = { expensesRoute }; diff --git a/src/controllers/users.controller.js b/src/controllers/users.controller.js new file mode 100644 index 000000000..64bad9a36 --- /dev/null +++ b/src/controllers/users.controller.js @@ -0,0 +1,23 @@ +const express = require('express'); + +const { + getAllUsers, + getUsersById, + deleteUser, + createUser, + updateUser, +} = require('../routers/users.router.js'); + +const usersRoute = express.Router(); + +usersRoute.get('/', getAllUsers); + +usersRoute.get('/:id', getUsersById); + +usersRoute.post('/', createUser); + +usersRoute.delete('/:id', deleteUser); + +usersRoute.patch('/:id', updateUser); + +module.exports = { usersRoute }; diff --git a/src/createServer.js b/src/createServer.js index 1ea5542d6..1fe47719e 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,25 @@ 'use strict'; -const createServer = () => { - // your code goes here -}; +const express = require('express'); +const cors = require('cors'); + +const { expensesRoute } = require('./controllers/expenses.controller.js'); +const { usersRoute } = require('./controllers/users.controller.js'); +const { categoriesRoute } = require('./controllers/categories.controller.js'); + +function createServer() { + const app = express(); + + app.use(cors()); + + app.use(express.json()); + + app.use('/expenses', expensesRoute); + app.use('/users', usersRoute); + app.use('/categories', categoriesRoute); + + return app; +} module.exports = { createServer, diff --git a/src/db.js b/src/db.js index 1ba3046cc..d2b725960 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 || '12345', }); module.exports = { diff --git a/src/models/Category.model.js b/src/models/Category.model.js new file mode 100644 index 000000000..91e0e659c --- /dev/null +++ b/src/models/Category.model.js @@ -0,0 +1,27 @@ +'use strict'; + +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../db.js'); + +const Category = sequelize.define( + 'Category', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + tableName: 'categories', + timestamps: false, + }, +); + +module.exports = { + Category, +}; diff --git a/src/models/Expense.model.js b/src/models/Expense.model.js index 567e1c3e7..2fbd35b8b 100644 --- a/src/models/Expense.model.js +++ b/src/models/Expense.model.js @@ -1,9 +1,44 @@ 'use strict'; +const { DataTypes } = require('sequelize'); const { sequelize } = require('../db.js'); const Expense = sequelize.define( - // your code goes here + 'Expense', + { + 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, + }, + note: { + type: DataTypes.STRING, + }, + }, + { + tableName: 'expenses', + timestamps: false, + }, ); module.exports = { diff --git a/src/models/User.model.js b/src/models/User.model.js index 61861c9e4..98c5ade2b 100644 --- a/src/models/User.model.js +++ b/src/models/User.model.js @@ -1,9 +1,25 @@ 'use strict'; +const { DataTypes } = require('sequelize'); const { sequelize } = require('../db.js'); const User = sequelize.define( - // your code goes here + 'User', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + tableName: 'users', + timestamps: false, + }, ); module.exports = { diff --git a/src/routers/categories.router.js b/src/routers/categories.router.js new file mode 100644 index 000000000..4e223331c --- /dev/null +++ b/src/routers/categories.router.js @@ -0,0 +1,102 @@ +const { + getAll, + getById, + create, + update, + remove, +} = require('../services/categories.service.js'); + +const getAllCategories = async (req, res) => { + const categories = await getAll(); + + res.send(categories); +}; + +const getCategoryById = async (req, res) => { + const { id } = req.params; + + const idNum = Number(id); + + const category = await getById(idNum); + + if (!category) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.send(category); +}; + +const createCategory = async (req, res) => { + const { name } = req.body; + + if (typeof name !== 'string') { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + + if (name === undefined) { + res.status(400).send({ message: 'Missing required field' }); + + return; + } + + const category = await create({ name }); + + res.status(201).send(category); +}; + +const deleteCategory = async (req, res) => { + const { id } = req.params; + + const idNum = Number(id); + + const category = await remove(idNum); + + if (!category) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.status(204).send(); +}; + +const updateCategory = async (req, res) => { + const { id } = req.params; + const { name } = req.body; + + const idNum = Number(id); + + if (typeof name !== 'string') { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + + if (name === undefined) { + res.status(400).send({ message: 'Missing required field' }); + + return; + } + + const category = await update({ id: idNum, name }); + + if (!category) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.send(category); +}; + +module.exports = { + getAllCategories, + getCategoryById, + createCategory, + deleteCategory, + updateCategory, +}; diff --git a/src/routers/expenses.router.js b/src/routers/expenses.router.js new file mode 100644 index 000000000..ba3a3d955 --- /dev/null +++ b/src/routers/expenses.router.js @@ -0,0 +1,134 @@ +const { + getAll, + getById, + create, + update, + remove, +} = require('../services/expenses.service.js'); + +const { getById: getByUserId } = require('../services/users.service.js'); + +const getAllExpenses = async (req, res) => { + const { userId, from, to, categories } = req.query; + + const expenses = await getAll({ + userId: userId ? Number(userId) : undefined, + from, + to, + categories, + }); + + res.send(expenses); +}; + +const getExpenseById = async (req, res) => { + const { id } = req.params; + + const expense = await getById(id); + + if (!expense) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.send(expense); +}; + +const createExpense = async (req, res) => { + const { userId, spentAt, title, amount, category, note } = req.body; + + if (userId == null || spentAt == null || title == null || amount == null) { + res.status(400).send({ message: 'Missing required field' }); + + return; + } + + const user = await getByUserId(userId); + + if (!user) { + return res.status(400).send({ message: 'User not found' }); + } + + if (typeof amount !== 'number' || Number.isNaN(new Date(spentAt).getTime())) { + return res.status(400).send({ message: 'Invalid field' }); + } + + const expense = await create({ + userId, + spentAt, + title, + amount, + category, + note, + }); + + res.status(201).send(expense); +}; + +const deleteExpense = async (req, res) => { + const { id } = req.params; + + const expense = await remove(id); + + if (!expense) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.status(204).send(); +}; + +const updateExpense = async (req, res) => { + const { id } = req.params; + const { amount, spentAt } = req.body; + + const existingExpense = await getById(id); + + if (!existingExpense) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + if (amount !== undefined && typeof amount !== 'number') { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + + if (spentAt !== undefined) { + const date = new Date(spentAt); + + if (Number.isNaN(date.getTime())) { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + } + + const updatedData = { + ...existingExpense, + ...req.body, + id, + }; + + const expense = await update(updatedData); + + if (!expense) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.status(200).send(expense); +}; + +module.exports = { + getAllExpenses, + getExpenseById, + deleteExpense, + createExpense, + updateExpense, +}; diff --git a/src/routers/users.router.js b/src/routers/users.router.js new file mode 100644 index 000000000..2e76235b3 --- /dev/null +++ b/src/routers/users.router.js @@ -0,0 +1,102 @@ +const { + getAll, + getById, + create, + update, + remove, +} = require('../services/users.service.js'); + +const getAllUsers = async (req, res) => { + const users = await getAll(); + + res.send(users); +}; + +const getUsersById = async (req, res) => { + const { id } = req.params; + + const idNum = Number(id); + + const user = await getById(idNum); + + if (!user) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.send(user); +}; + +const createUser = async (req, res) => { + const { name } = req.body; + + if (typeof name !== 'string') { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + + if (name === undefined) { + res.status(400).send({ message: 'Missing required field' }); + + return; + } + + const user = await create({ name }); + + res.status(201).send(user); +}; + +const deleteUser = async (req, res) => { + const { id } = req.params; + + const idNum = Number(id); + + const user = await remove(idNum); + + if (!user) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.status(204).send(); +}; + +const updateUser = async (req, res) => { + const { id } = req.params; + const { name } = req.body; + + const idNum = Number(id); + + if (typeof name !== 'string') { + res.status(400).send({ message: 'Invalid field' }); + + return; + } + + if (name === undefined) { + res.status(400).send({ message: 'Missing required field' }); + + return; + } + + const user = await update({ id: idNum, name }); + + if (!user) { + res.status(404).send({ message: 'Not found' }); + + return; + } + + res.send(user); +}; + +module.exports = { + getAllUsers, + getUsersById, + createUser, + deleteUser, + updateUser, +}; diff --git a/src/services/categories.service.js b/src/services/categories.service.js new file mode 100644 index 000000000..8b4597068 --- /dev/null +++ b/src/services/categories.service.js @@ -0,0 +1,41 @@ +const { Category } = require('../models/Category.model'); + +const getAll = async () => { + const categories = await Category.findAll(); + + return categories; +}; + +const getById = async (id) => { + const category = await Category.findByPk(id); + + return category; +}; + +const create = async ({ name }) => { + const category = await Category.create({ name }); + + return category; +}; + +const remove = async (id) => { + const deleted = await Category.destroy({ where: { id } }); + + return deleted; +}; + +const update = async ({ id, name }) => { + await Category.update({ name }, { where: { id } }); + + const category = await getById(id); + + return category; +}; + +module.exports = { + getAll, + getById, + create, + remove, + update, +}; diff --git a/src/services/expenses.service.js b/src/services/expenses.service.js new file mode 100644 index 000000000..44ed0947e --- /dev/null +++ b/src/services/expenses.service.js @@ -0,0 +1,71 @@ +/* eslint-disable function-paren-newline */ +const { Op } = require('sequelize'); +const { Expense } = require('../models/Expense.model'); + +const getAll = async (filters = {}) => { + const { userId, from, to, categories } = filters; + + const where = {}; + + if (userId) { + where.userId = Number(userId); + } + + if (from || to) { + where.spentAt = {}; + + if (from) { + where.spentAt[Op.gte] = new Date(from); + } + + if (to) { + where.spentAt[Op.lte] = new Date(to); + } + } + + if (categories) { + where.category = categories; + } + + const expenses = await Expense.findAll({ where }); + + return expenses; +}; + +const getById = async (id) => { + const expense = await Expense.findByPk(id); + + return expense; +}; + +const create = async (data) => { + const expense = await Expense.create(data); + + return expense; +}; + +const remove = async (id) => { + const deleted = await Expense.destroy({ where: { id } }); + + return deleted; +}; + +const update = async ({ id, ...updateData }) => { + const [updatedRows] = await Expense.update(updateData, { where: { id } }); + + if (updatedRows === 0) { + return null; + } + + const expense = await Expense.findByPk(id); + + return expense; +}; + +module.exports = { + getAll, + getById, + create, + remove, + update, +}; diff --git a/src/services/users.service.js b/src/services/users.service.js new file mode 100644 index 000000000..01db9d96d --- /dev/null +++ b/src/services/users.service.js @@ -0,0 +1,41 @@ +const { User } = require('../models/User.model'); + +const getAll = async () => { + const users = await User.findAll(); + + return users; +}; + +const getById = async (id) => { + const user = await User.findByPk(id); + + return user; +}; + +const create = async ({ name }) => { + const user = await User.create({ name }); + + return user; +}; + +const remove = async (id) => { + const deleted = await User.destroy({ where: { id } }); + + return deleted; +}; + +const update = async ({ id, name }) => { + await User.update({ name }, { where: { id } }); + + const user = await getById(id); + + return user; +}; + +module.exports = { + getAll, + getById, + create, + remove, + update, +};