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

on:
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: students
POSTGRES_PORT: 5432
POSTGRES_HOST: localhost
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Run tests
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: students
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
run: npm test
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,3 @@ node_modules

# MacOS
.DS_Store

# env files
*.env
.env*
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"init": "mate-scripts init",
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"lint": "npm run format && mate-scripts lint",
"format": "prettier --ignore-path .prettierignore --write './src/**/*.{js,ts}'",
"test:only": "mate-scripts test",
Expand All @@ -23,7 +24,7 @@
},
"devDependencies": {
"@mate-academy/eslint-config": "latest",
"@mate-academy/scripts": "^1.8.6",
"@mate-academy/scripts": "^2.1.3",
"axios": "^1.7.2",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
Expand Down
73 changes: 73 additions & 0 deletions src/controllers/categories.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const { categoriesService } = require('../services');

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categories router is missing from exports. Need to export categoriesRouter here so it can be used in createServer.js.


if (!name) {
return res.sendStatus(400);
}

const category = await categoriesService.create(name);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categories router is not registered in the server. The task requires ensuring new routes are registered in createServer.js.


res.status(201).json(categoriesService.normalize(category));
};

const getAll = async (req, res) => {
const categories = await categoriesService.getAll();

res.send(categories.map((category) => categoriesService.normalize(category)));
};

const getOne = async (req, res) => {
const category = await categoriesService.getOne(Number(req.params.id));

if (!category) {
res.sendStatus(404);

return;
}

res.send(categoriesService.normalize(category));
};

const update = async (req, res) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good practice to validate the request body here, similar to how you've done it in the create function. What would happen if a client sends a request to update a category without providing a name? Consider adding a check to ensure req.body.name is present and returning a 400 Bad Request if it's not.

const { id } = req.params;
const { name } = req.body;
const category = await categoriesService.getOne(Number(id));

if (!category) {
return res.sendStatus(404);
}

if (!name) {
return res.sendStatus(400);
}

const updatedCategory = await categoriesService.update(Number(id), {
name,
});

res.json(categoriesService.normalize(updatedCategory));
};

const remove = async (req, res) => {
const categoriesRemoved = await categoriesService.remove(
Number(req.params.id),
);

if (!categoriesRemoved) {
return res.sendStatus(404);
}

res.sendStatus(204);
};

const categoriesController = {
getAll,
getOne,
create,
update,
remove,
};

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

const create = async (req, res) => {
const { userId, spentAt, title, amount, category, note } = req.body;
const user = await usersService.getOne(userId);

if (!user || !userId || !title || !amount || !spentAt) {
return res.sendStatus(400);
Comment on lines +4 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/routes/index.js is missing export for categoriesRouter

}

const expense = await expensesService.create({
Comment on lines +9 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/createServer.js is missing route registration for categoriesRouter

userId,
spentAt,
title,
amount,
category,
note,
});

res.status(201).json(expensesService.normalize(expense));
};

const getAll = async (req, res) => {
const { userId, categories, from, to } = req.query;
const expenses = await expensesService.getAll({
userId: Number(userId),
categories,
from,
to,
});

res.json(expenses.map((expense) => expensesService.normalize(expense)));
};

const getOne = async (req, res) => {
const expense = await expensesService.getOne(Number(req.params.id));

if (!expense) {
return res.sendStatus(404);
}

res.json(expensesService.normalize(expense));
};

const update = async (req, res) => {
const { id } = req.params;
const expense = await expensesService.getOne(Number(id));

if (!expense) {
return res.sendStatus(404);
}

const allowedFields = [
'userId',
'title',
'amount',
'spentAt',
'category',
'note',
];
const updateData = {};

allowedFields.forEach((field) => {
if (req.body[field] !== undefined) {
updateData[field] = req.body[field];
}
});

if (Object.keys(updateData).length === 0) {
return res.status(400);
}

const updatedExpense = await expensesService.update(Number(id), updateData);

res.json(expensesService.normalize(updatedExpense));
};

const remove = async (req, res) => {
const expensesRemoved = await expensesService.remove(Number(req.params.id));

if (!expensesRemoved) {
return res.sendStatus(404);
}

res.sendStatus(204);
};

const expensesController = {
create,
getAll,
getOne,
update,
remove,
};

exports.expensesController = expensesController;
9 changes: 9 additions & 0 deletions src/controllers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { usersController } = require('./users.controller');
const { expensesController } = require('./expenses.controller');
const { categoriesController } = require('./categories.controller');

module.exports = {
usersController,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categoriesService is not exported from src/services/index.js. Without exporting it, the controller cannot access the category service methods.

expensesController,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categoriesRouter is not exported from src/routes/index.js. Only usersRouter and expensesRouter are exported, but categoriesRouter needs to be added to make it available for registration.

categoriesController,
};
Comment on lines +5 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requires implementing CRUD operations for categories. This would involve creating a categoriesController, which should be imported and exported from this file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categoriesController is not imported or exported from src/controllers/index.js. Without exporting it, other parts of the application cannot access the category controller.

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

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

if (!name) {
return res.sendStatus(400);
}

const user = await usersService.create(name);

res.status(201).json(usersService.normalize(user));
};

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

res.send(users.map((user) => usersService.normalize(user)));
};

const getOne = async (req, res) => {
const user = await usersService.getOne(Number(req.params.id));

if (!user) {
res.sendStatus(404);

return;
}

res.send(usersService.normalize(user));
};

const update = async (req, res) => {
const { id } = req.params;
const { name } = req.body;
const user = await usersService.getOne(Number(id));

if (!user) {
return res.sendStatus(404);
}

if (!name) {
return res.sendStatus(400);
}

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

res.json(usersService.normalize(updatedUser));
};

const remove = async (req, res) => {
const usersRemoved = await usersService.remove(Number(req.params.id));

if (!usersRemoved) {
return res.sendStatus(404);
}

res.sendStatus(204);
};

const usersController = {
getAll,
getOne,
create,
update,
remove,
};

exports.usersController = usersController;
12 changes: 11 additions & 1 deletion src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
'use strict';

const express = require('express');
const { routes } = require('./routes');

const createServer = () => {
// your code goes here
const app = express();

app.use(express.json());
app.use('/users', routes.usersRouter);
app.use('/expenses', routes.expensesRouter);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new routes for managing categories are missing. Based on the task requirements, you should add a new router here for the /categories endpoint.

Comment on lines +10 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The categories router is NOT registered in the server. The task requires: 'Ensure the new routes are registered in src/createServer.js'. Currently only /users and /expenses are registered. Need to add: app.use('/categories', routes.categoriesRouter); after line 10.

app.use('/categories', routes.categoriesRouter);

return app;
};

module.exports = {
Expand Down
4 changes: 2 additions & 2 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
} = process.env;

/*
All credentials setted to default values (exsept password - it is exapmle)
All credentials are set to default values (except password - it is example)
replace if needed with your own
*/

Expand All @@ -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 || '',
});

module.exports = {
Expand Down
15 changes: 15 additions & 0 deletions src/models/Category.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

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

const Category = sequelize.define('Category', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
});

module.exports = {
Category,
};
Loading
Loading