Skip to content
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
23 changes: 23 additions & 0 deletions .github/workflows/test.yml-template
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test

on:
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
5,689 changes: 2,646 additions & 3,043 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
"author": "Mate academy",
"license": "GPL-3.0",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2"
"cors": "^2.8.6",
"express": "^4.19.2",
"pnpm": "^10.33.4",
"uuid": "^14.0.0"
},
"devDependencies": {
"@mate-academy/eslint-config": "latest",
"@mate-academy/scripts": "^1.8.6",
"@mate-academy/scripts": "^2.1.3",
"@types/cors": "^2.8.19",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-node": "^11.1.0",
Expand Down
5,618 changes: 5,618 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions src/controllers/expenses.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const expensesService = require('../services/expenses.service');
const usersService = require('../services/users.service');

async function getAllExpenses(req, res) {
const { userId, categories, from, to } = req.query;

const expenses = await expensesService.getAllExpenses({
userId,
categories,
from,
to,
});

res.json(expenses);
}

async function createExpense(req, res) {
const { userId, spentAt, title, amount, category, note } = req.body;

if (!userId || !spentAt || !title || !amount || !category) {
return res.status(400).json({ message: 'Required fields are missing' });
}

const user = await usersService.getUserById(Number(userId));

if (!user) {
return res.status(400).json({ message: 'User not found' });
}

const expenses = await expensesService.createExpenses({
userId,
spentAt,
title,
amount,
category,
note,
});

res.status(201).json(expenses);
}

async function getExpensesById(req, res) {
const { id } = req.params;

const expense = await expensesService.getExpensesById(id);

if (!expense) {
return res.status(404).json({ message: 'Expense not found' });
}

return res.status(200).json(expense);
}

async function removeExpenses(req, res) {
const { id } = req.params;
const result = await expensesService.removeExpenses(id);

if (!result) {
return res.status(404).json({ message: 'Expense not found' });
}

return res.sendStatus(204);
}

async function updateExpenses(req, res) {
const { id } = req.params;
const { spentAt, title, amount, category, note } = req.body;

const expense = await expensesService.getExpensesById(Number(id));

if (!expense) {
return res.status(404).json({ message: 'Expense not found' });
}

const updatedExpense = await expensesService.updateExpenses({
id,
spentAt,
title,
amount,
category,
note,
});

if (!updatedExpense) {
return res.status(404).json({ message: 'Expense not found' });
}

res.send(updatedExpense);
}

module.exports = {
getAllExpenses,
createExpense,
getExpensesById,
removeExpenses,
updateExpenses,
};
69 changes: 69 additions & 0 deletions src/controllers/users.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const usersService = require('../services/users.service');

async function getAll(req, res) {
const users = await usersService.getAllUsers();

res.json(users);
}

async function createUser(req, res) {
const { name } = req.body;

if (!name) {
return res.status(400).json({ message: 'Name is required' });
}

const user = await usersService.createUser(name);

return res.status(201).json(user);
}

async function getUserById(req, res) {
const { id } = req.params;

const user = await usersService.getUserById(id);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

return res.status(200).json(user);
}

async function removeUser(req, res) {
const { id } = req.params;
const result = await usersService.removeUser(id);

if (!result) {
return res.status(404).json({ message: 'User not found' });
}

return res.sendStatus(204);
}

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

const user = await usersService.getUserById(Number(id));

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

if (typeof name !== 'string') {
return res.status(400).json({ message: 'Name is required' });
}

const updatedUser = await usersService.updateUser({ id, name });

res.send(updatedUser);
}

module.exports = {
getAll,
createUser,
getUserById,
removeUser,
updateUser,
};
32 changes: 25 additions & 7 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
'use strict';

// const express = require('express');
const express = require('express');
const cors = require('cors');

const usersRouter = require('./routes/users.routes');
const expensesRouter = require('./routes/expenses.routes');
const { resetData } = require('./utils/resetData');

function createServer() {
// Use express to create a server
// Add a routes to the server
// Return the server (express app)
resetData();

const app = express();

app.use(
cors({
origin: '*',
methods: 'GET, POST, PUT, DELETE',
allowedHeaders: 'Content-Type',
credentials: true,
}),
express.json(),
);

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

return app;
}

module.exports = {
createServer,
};
module.exports = { createServer };
3 changes: 3 additions & 0 deletions src/data/expenses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const expenses = [];

module.exports = expenses;
3 changes: 3 additions & 0 deletions src/data/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const users = [];

module.exports = users;
11 changes: 11 additions & 0 deletions src/routes/expenses.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const express = require('express');
const expensesRouter = express.Router();
const expensesController = require('../controllers/expenses.controller');

expensesRouter.get('/', expensesController.getAllExpenses);
expensesRouter.get('/:id', expensesController.getExpensesById);
expensesRouter.post('/', expensesController.createExpense);
expensesRouter.delete('/:id', expensesController.removeExpenses);
expensesRouter.patch('/:id', expensesController.updateExpenses);

module.exports = expensesRouter;
11 changes: 11 additions & 0 deletions src/routes/users.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const express = require('express');
const usersRouter = express.Router();
const usersController = require('../controllers/users.controller');

usersRouter.get('/', usersController.getAll);
usersRouter.get('/:id', usersController.getUserById);
usersRouter.post('/', usersController.createUser);
usersRouter.delete('/:id', usersController.removeUser);
usersRouter.patch('/:id', usersController.updateUser);

module.exports = usersRouter;
88 changes: 88 additions & 0 deletions src/services/expenses.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const expenses = require('../data/expenses');
const generateId = require('../utils/randomNum');

function getAllExpenses({ userId, categories, from, to }) {
let result = expenses;

if (userId) {
result = result.filter((e) => e.userId === Number(userId));
}

if (categories) {
const normalized = [].concat(categories);

result = result.filter((e) => normalized.includes(e.category));
}

if (from) {
result = result.filter((e) => new Date(e.spentAt) >= new Date(from));
}

if (to) {
result = result.filter((e) => new Date(e.spentAt) <= new Date(to));
}

return result;
}

function createExpenses({ userId, spentAt, title, amount, category, note }) {
const newExpense = {
id: generateId(),
userId,
spentAt,
title,
amount,
category,
note,
};

expenses.push(newExpense);

return newExpense;
}

function getExpensesById(id) {
return expenses.find((e) => e.id === Number(id));
}

function removeExpenses(id) {
const index = expenses.findIndex((e) => e.id === Number(id));

if (index === -1) {
return null;
}

expenses.splice(index, 1);

return true;
}

function updateExpenses({ id, spentAt, title, amount, category, note }) {
const expense = getExpensesById(Number(id));

const updates = Object.fromEntries(
Object.entries({
spentAt,
title,
amount,
category,
note,
}).filter(([, value]) => value !== undefined),
);

if (Object.keys(updates).length === 0) {
return false;
}

Object.assign(expense, updates);

return expense;
}

module.exports = {
getAllExpenses,
createExpenses,
getExpensesById,
removeExpenses,
updateExpenses,
};
Loading
Loading