Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution #234

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/controllers/expensesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const expensesService = require('../services/expensesService');
const expensesHelpers = require('../helpers/expense.helper');
const userHelpers = require('../helpers/user.helper');
const STATUS_CODE = require('../utils/statusCodes');

const getAll = async (req, res) => {
const expenses = await expensesService.getExpenses(req.query);

res
.status(STATUS_CODE.SUCCESS)
.send(expenses.map((expense) => expensesHelpers.normalize(expense)));
};

const getOne = async (req, res) => {
const { id } = req.params;

if (await expensesHelpers.isExpenseExist(id)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('Expense with such id not found');
}

const expense = await expensesService.getExpenseById(id);

res.status(STATUS_CODE.SUCCESS).send(expensesHelpers.normalize(expense));
};

const create = async (req, res) => {
if (await userHelpers.isUserExist(req.body.userId)) {
return res.status(STATUS_CODE.BAD_REQUEST).send('User not found');
}

try {
expensesHelpers.validateRequestBodyFields(req.body);

const expense = await expensesService.create(req.body);

res.statusCode = STATUS_CODE.CREATED;

res.send(expense);
} catch (error) {
res.status(STATUS_CODE.BAD_REQUEST).send(error.message);
}
};

const remove = async (req, res) => {
const { id } = req.params;

if (await expensesHelpers.isExpenseExist(id)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('Expense with such id not found');
}

await expensesService.remove(id);
res.sendStatus(STATUS_CODE.NO_CONTENT);
};

const update = async (req, res) => {
const { id } = req.params;
const { title } = req.body;

if (await expensesHelpers.isExpenseExist(id)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('Expense with such id not found');
}

if (typeof title !== 'string') {
return res.sendStatus(STATUS_CODE.BAD_REQUEST);
}

const updatedExpense = await expensesService.update({ id, title });

res
.status(STATUS_CODE.SUCCESS)
.send(expensesHelpers.normalize(updatedExpense));
};

module.exports = {
getAll,
getOne,
create,
remove,
update,
};
82 changes: 82 additions & 0 deletions src/controllers/usersController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const userService = require('../services/usersService');
const userHelpers = require('../helpers/user.helper');
const STATUS_CODE = require('../utils/statusCodes');

const getAll = async (_, res) => {
const users = await userService.getAll();

res
.status(STATUS_CODE.SUCCESS)
.send(users.map((user) => userHelpers.normalize(user)));
};

const getOne = async (req, res) => {
const { id } = req.params;

if (await userHelpers.isUserExist(id, res)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('User with such id not found');
}

const user = await userService.getById(id);

res.status(STATUS_CODE.SUCCESS).send(userHelpers.normalize(user));
};

const create = async (req, res) => {
const { name } = req.body;

if (userHelpers.isNameValid(name)) {
return res
.status(STATUS_CODE.BAD_REQUEST)
.send('Error: "name" is required and must be a string.');
}

const user = await userService.create(name);

res.status(STATUS_CODE.CREATED).send(userHelpers.normalize(user));
};

const remove = async (req, res) => {
const { id } = req.params;

if (await userHelpers.isUserExist(id, res)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('User with such id not found');
}

await userService.remove(id);

res.sendStatus(STATUS_CODE.NO_CONTENT);
};

const update = async (req, res) => {
const { id } = req.params;
const { name } = req.body;

if (await userHelpers.isUserExist(id, res)) {
return res
.status(STATUS_CODE.NOT_FOUND)
.send('User with such id not found');
}

if (userHelpers.isNameValid(name)) {
return res
.status(STATUS_CODE.BAD_REQUEST)
.send('Error: "name" is required and must be a string.');
}

const updatedUser = await userService.update({ id, name });

res.status(STATUS_CODE.SUCCESS).send(userHelpers.normalize(updatedUser));
};

module.exports = {
getAll,
create,
remove,
update,
getOne,
};
19 changes: 16 additions & 3 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
'use strict';

const createServer = () => {
// your code goes here
};
const express = require('express');
const cors = require('cors');

const { router: usersRouter } = require('./routes/usersRouter');
const { router: expensesRouter } = require('./routes/expensesRouter');

function createServer() {
const app = express();

app.use(express.json());
app.use(cors());
app.use('/users', usersRouter);
app.use('/expenses', expensesRouter);

return app;
}

module.exports = {
createServer,
Expand Down
40 changes: 40 additions & 0 deletions src/helpers/expense.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const expenseService = require('../services/expensesService');

const isExpenseExist = async (id) => {
if (!(await expenseService.getExpenseById(id))) {
return true;
}
};

const validateRequestBodyFields = ({
userId,
title,
amount,
isCreatingExpense = true,
}) => {
if (isCreatingExpense && !userId) {
throw new Error('provide userId');
}

if (!title || typeof title !== 'string') {
throw new Error(
'Invalid request: "title" is required and must be a string.',
);
}

if (typeof amount !== 'number') {
throw new Error('Invalid request: "amount" must be a number.');
}
};

const normalize = ({ dataValues: expense }) => {
const { password, ...rest } = expense;

return rest;
};

module.exports = {
isExpenseExist,
validateRequestBodyFields,
normalize,
};
22 changes: 22 additions & 0 deletions src/helpers/user.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const userService = require('../services/usersService');

const isNameValid = (name) => {
return !name || typeof name !== 'string';
};

const isUserExist = async (id) => {
return !(await userService.getById(id));
};

const normalize = ({ id, name }) => {
return {
id,
name,
};
};

module.exports = {
isNameValid,
isUserExist,
normalize,
};
11 changes: 11 additions & 0 deletions src/middleware/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const errorHandler = (action) => async (req, res, next) => {
try {
await action(req, res, next);
} catch (error) {
next(error);
}
};

module.exports = {
errorHandler,
};
40 changes: 39 additions & 1 deletion src/models/Expense.model.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
'use strict';

const { DataTypes } = require('sequelize');
const { sequelize } = require('../db.js');

const Expense = sequelize.define(
// your code goes here
'Expense',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: 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,
},
},
{
tableName: 'expenses',
createdAt: false,
updatedAt: false,
},
);

module.exports = {
Expand Down
24 changes: 23 additions & 1 deletion src/models/User.model.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
'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,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
field: 'created_at',
},
},
{
tableName: 'users',
updatedAt: false,
},
);

module.exports = {
Expand Down
19 changes: 19 additions & 0 deletions src/routes/expensesRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const express = require('express');
const expensesController = require('../controllers/expensesController');
const { errorHandler } = require('../middleware/errorHandler');
const router = express.Router();

router
.route('/')
.get(errorHandler(expensesController.getAll))
.post(errorHandler(expensesController.create));

router
.route('/:id')
.get(errorHandler(expensesController.getOne))
.patch(errorHandler(expensesController.update))
.delete(errorHandler(expensesController.remove));

module.exports = {
router,
};
18 changes: 18 additions & 0 deletions src/routes/usersRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const express = require('express');
const userController = require('../controllers/usersController');
const { errorHandler } = require('../middleware/errorHandler');

const router = express.Router();

router
.route('/')
.get(errorHandler(userController.getAll))
.post(errorHandler(userController.create));

router
.route('/:id')
.get(errorHandler(userController.getOne))
.delete(errorHandler(userController.remove))
.patch(errorHandler(userController.update));

module.exports = { router };
Loading
Loading