diff --git a/src/Random.js b/src/Random.js new file mode 100644 index 00000000..5c0d8b53 --- /dev/null +++ b/src/Random.js @@ -0,0 +1,7 @@ +function generateRandomId() { + return Math.floor(10000000 + Math.random() * 90000000); +} + +module.exports = { + generateRandomId, +}; diff --git a/src/controller/expenses.controller.js b/src/controller/expenses.controller.js new file mode 100644 index 00000000..c4137c53 --- /dev/null +++ b/src/controller/expenses.controller.js @@ -0,0 +1,122 @@ +const expensesService = require('../services/expenses.service'); +const userService = require('../services/user.service'); + +const get = async (req, res) => { + const { userId, from, to, categories } = req.query; + const expenses = await expensesService.getAllExpenses(); + + if (!userId && !categories && (!from || !to)) { + return res.send(expenses); + } + + let filteredExpenses = expenses; + + if (userId) { + filteredExpenses = expensesService.filterExpensesById( + filteredExpenses, + userId, + ); + } + + if (categories) { + const normalizedCategories = expensesService.normalizeCategory(categories); + + filteredExpenses = expensesService.filterExpensesByCategory( + filteredExpenses, + normalizedCategories, + ); + } + + if (from && to) { + const fromDate = new Date(from); + const toDate = new Date(to); + + filteredExpenses = expensesService.filterExpensesByDate( + filteredExpenses, + fromDate, + toDate, + ); + } + + res.send(filteredExpenses); +}; + +const getOne = async (req, res) => { + const { id } = req.params; + + const expense = await expensesService.getById(id); + + if (!expense) { + res.sendStatus(404); + + return; + } + + res.send(expense); +}; + +const create = async (req, res) => { + const { userId, spentAt, title, amount, category, note } = req.body; + + if (!userId || !spentAt || !title || !amount) { + res.sendStatus(400); + + return; + } + + const user = await userService.getById(userId); + + if (!user) { + res.sendStatus(400); + + return; + } + + const updatedExpense = await expensesService.create({ + userId, + spentAt, + title, + amount, + category, + note, + }); + + res.status(201).send(updatedExpense); +}; + +const remove = async (req, res) => { + const { id } = req.params; + + const expenses = await expensesService.getAllExpenses(); + + await expensesService.removeExpense(id); + + const updatedExpenses = await expensesService.getAllExpenses(); + + if (expenses.length === updatedExpenses.length) { + return res.sendStatus(404); + } + + res.sendStatus(204); +}; + +const update = async (req, res) => { + const { id } = req.params; + const chosenExpense = await expensesService.getById(id); + + if (!chosenExpense) { + return res.sendStatus(404); + } + + const expense = await expensesService.updateExpense(chosenExpense, req, id); + + res.status(200).send(expense); +}; + +module.exports = { + get, + getOne, + create, + remove, + update, +}; diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js new file mode 100644 index 00000000..09b0f9ed --- /dev/null +++ b/src/controller/user.controller.js @@ -0,0 +1,95 @@ +const userService = require('../services/user.service'); + +const get = async (req, res) => { + const users = await userService.getAll(); + + if (!users) { + res.send(users); + res.sendStatus(404); + + return; + } + + res.send(users); +}; + +const getOne = async (req, res) => { + const { id } = req.params; + + const user = await userService.getById(+id); + + if (!user) { + res.sendStatus(404); + + return; + } + + res.send(user); +}; + +const update = async (req, res) => { + const { id } = req.params; + const { name } = req.body; + const user = await userService.getById(+id); + + if (!user) { + res.sendStatus(404); + + return; + } + + if (typeof name !== 'string') { + res.sendStatus(422); + + return; + } + + await userService.updateUser({ id, name }); + + const updatedUser = await userService.getById(+id); + + if (!updatedUser) { + res.sendStatus(404); + + return; + } + + res.send(updatedUser); +}; + +const create = async (req, res) => { + const { name } = req.body; + + if (!name) { + res.sendStatus(400); + + return; + } + + const user = await userService.createUser(name); + + res.statusCode = 201; + res.send(user); +}; + +const remove = async (req, res) => { + const { id } = req.params; + const userId = +id; + + if (await userService.getById(userId)) { + await userService.deleteUser(userId); + res.sendStatus(204); + + return; + } + + res.sendStatus(404); +}; + +module.exports = { + get, + getOne, + update, + create, + remove, +}; diff --git a/src/createServer.js b/src/createServer.js index 1ea5542d..33c43205 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,7 +1,22 @@ 'use strict'; +const express = require('express'); +const cors = require('cors'); + +const { router: userRouter } = require('./routes/users.route'); +const { router: expensesRoute } = require('./routes/expenses.route'); + const createServer = () => { - // your code goes here + const app = express(); + + app.use(cors()); + + app.use(express.json()); + + app.use('/users', userRouter); + app.use('/expenses', expensesRoute); + + return app; }; module.exports = { diff --git a/src/db.js b/src/db.js index 1ba3046c..8b939b9b 100644 --- a/src/db.js +++ b/src/db.js @@ -15,18 +15,13 @@ const { POSTGRES_DB, } = process.env; -/* - All credentials setted to default values (exsept password - it is exapmle) - replace if needed with your own -*/ - const sequelize = new Sequelize({ database: POSTGRES_DB || 'postgres', username: POSTGRES_USER || 'postgres', host: POSTGRES_HOST || 'localhost', dialect: 'postgres', port: POSTGRES_PORT || 5432, - password: POSTGRES_PASSWORD || '123', + password: POSTGRES_PASSWORD || '123123', }); module.exports = { diff --git a/src/models/Expense.model.js b/src/models/Expense.model.js index 567e1c3e..0d11d466 100644 --- a/src/models/Expense.model.js +++ b/src/models/Expense.model.js @@ -1,9 +1,43 @@ 'use strict'; const { sequelize } = require('../db.js'); +const { DataTypes } = require('sequelize'); const Expense = sequelize.define( - // your code goes here + 'Expense', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + spentAt: { + type: DataTypes.TEXT, + allowNull: false, + }, + title: { + type: DataTypes.TEXT, + allowNull: false, + }, + amount: { + type: DataTypes.INTEGER, + allowNull: false, + }, + category: { + type: DataTypes.TEXT, + }, + note: { + type: DataTypes.TEXT, + }, + }, + { + tableName: 'Expense', + updatedAt: false, + createdAt: false, + }, ); module.exports = { diff --git a/src/models/User.model.js b/src/models/User.model.js index 61861c9e..6837f578 100644 --- a/src/models/User.model.js +++ b/src/models/User.model.js @@ -1,9 +1,26 @@ 'use strict'; const { sequelize } = require('../db.js'); +const { DataTypes } = require('sequelize'); const User = sequelize.define( - // your code goes here + 'User', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + }, + }, + { + updatedAt: false, + createdAt: false, + }, ); module.exports = { diff --git a/src/routes/expenses.route.js b/src/routes/expenses.route.js new file mode 100644 index 00000000..6875685e --- /dev/null +++ b/src/routes/expenses.route.js @@ -0,0 +1,17 @@ +const expensesController = require('../controller/expenses.controller'); +const express = require('express'); +const router = express.Router(); + +router.get('/', expensesController.get); + +router.get('/:id', expensesController.getOne); + +router.post('/', expensesController.create); + +router.delete('/:id', expensesController.remove); + +router.patch('/:id', express.json(), expensesController.update); + +module.exports = { + router, +}; diff --git a/src/routes/users.route.js b/src/routes/users.route.js new file mode 100644 index 00000000..77935e21 --- /dev/null +++ b/src/routes/users.route.js @@ -0,0 +1,19 @@ +const express = require('express'); + +const userController = require('../controller/user.controller'); + +const router = express.Router(); + +router.get('/', userController.get); + +router.get('/:id', userController.getOne); + +router.patch('/:id', userController.update); + +router.post('/', userController.create); + +router.delete('/:id', userController.remove); + +module.exports = { + router, +}; diff --git a/src/services/expenses.service.js b/src/services/expenses.service.js new file mode 100644 index 00000000..766fecfd --- /dev/null +++ b/src/services/expenses.service.js @@ -0,0 +1,81 @@ +const { Expense } = require('../models/Expense.model'); +const { generateRandomId } = require('../Random'); + +const getAllExpenses = async () => { + return Expense.findAll(); +}; + +const getById = async (id) => { + return Expense.findByPk(id); +}; + +const normalizeCategory = (categories) => { + return Array.isArray(categories) || !categories ? categories : [categories]; +}; + +const filterExpensesById = (expenses, id) => { + return expenses.filter((exp) => exp.userId === parseInt(id)); +}; + +const filterExpensesByCategory = (expenses, category) => { + return expenses.filter((exp) => category.includes(exp.category)); +}; + +const filterExpensesByDate = (expenses, from, to) => { + return expenses.filter((exp) => { + const spentAt = new Date(exp.spentAt); + + return spentAt >= from && spentAt <= to; + }); +}; + +const create = async ({ + userId, + spentAt, + title, + amount, + category = null, + note = null, +}) => { + const id = generateRandomId(); + + return Expense.create({ + id, + userId, + spentAt, + title, + amount, + category, + note, + }); +}; + +const removeExpense = async (id) => { + return Expense.destroy({ + where: { + id, + }, + }); +}; + +const updateExpense = async (exp, req, id) => { + const updatedFields = { ...req.body }; + + await Expense.update(updatedFields, { + where: { id }, + }); + + return getById(id); +}; + +module.exports = { + getById, + getAllExpenses, + normalizeCategory, + filterExpensesById, + filterExpensesByCategory, + filterExpensesByDate, + create, + removeExpense, + updateExpense, +}; diff --git a/src/services/user.service.js b/src/services/user.service.js new file mode 100644 index 00000000..f5e476ce --- /dev/null +++ b/src/services/user.service.js @@ -0,0 +1,36 @@ +const { User } = require('../models/User.model'); +const { generateRandomId } = require('../Random'); + +const getAll = async () => { + return User.findAll(); +}; + +const getById = async (id) => { + return User.findByPk(+id); +}; + +const updateUser = async ({ id, name }) => { + await User.update({ name }, { where: { id } }); +}; + +const createUser = async (name) => { + const id = generateRandomId(); + + return User.create({ id, name }); +}; + +const deleteUser = async (id) => { + await User.destroy({ + where: { + id, + }, + }); +}; + +module.exports = { + getAll, + getById, + updateUser, + createUser, + deleteUser, +}; diff --git a/src/setup.js b/src/setup.js new file mode 100644 index 00000000..0bc9cf43 --- /dev/null +++ b/src/setup.js @@ -0,0 +1,5 @@ +const { User } = require('./models/User.model'); +const { Expense } = require('./models/Expense.model'); + +User.sync({ force: true }); +Expense.sync({ force: true });