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
285 changes: 281 additions & 4 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,286 @@
'use strict';

const express = require('express');

const { User, Expense, Category } = require('./models/models');

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

app.use(express.json());

app.post('/users', async (req, res) => {
const { name } = req.body;

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

try {
const newUser = await User.create({ name });

res.status(201).json(newUser);
} catch (e) {
res.status(400).send(e.message);
}
});

app.get('/users', async (req, res) => {
res.json(await User.findAll());
});

app.get('/users/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

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

app.patch('/users/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

if (!user) {
return res.status(404).send('Not found');
}
await user.update(req.body, { silent: true });
res.json(user);
});

app.delete('/users/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

if (!user) {
return res.status(404).send('Not found');
}
await user.destroy();
res.sendStatus(204);
});

app.post('/expenses', async (req, res) => {
const { title, amount, userId, category, note, spentAt } = req.body;

if (!title || !amount || !userId) {
return res.sendStatus(400);
}

try {
const [foundCategory] = await Category.findOrCreate({
where: { name: category || 'Other' },
});

const expense = await Expense.create({
title,
amount,
userId,
categoryId: foundCategory.id,
note,

spentAt: spentAt || new Date(),
});

const result = expense.toJSON();

delete result.categoryId;

res.status(201).json({
...result,
category: foundCategory.name,
});
} catch (error) {
res.sendStatus(400);
}
});

app.get('/expenses', async (req, res) => {
const { userId, categories, from, to } = req.query;
const { Op } = require('sequelize');

const where = {};

if (userId) {
where.userId = userId;
}

if (from || to) {
where.spentAt = {};

if (from) {
where.spentAt[Op.gte] = from;
}

if (to) {
where.spentAt[Op.lte] = to;
}
}

const include = [
{
model: Category,
as: 'Category',

...(categories && {
where: { name: categories },
required: true,
}),
},
];

try {
const expenses = await Expense.findAll({
where,
include,
});

module.exports = {
createServer,
const result = expenses.map((exp) => {
const data = exp.toJSON();

return {
id: data.id,
title: data.title,
amount: data.amount,
spentAt: data.spentAt,
note: data.note,
userId: data.userId,
category: data.Category ? data.Category.name : null,
};
});

res.json(result);
} catch (error) {
res.sendStatus(500);
}
});

app.delete('/expenses/:id', async (req, res) => {
const deletedCount = await Expense.destroy({
where: { id: req.params.id },
});

if (deletedCount === 0) {
return res.sendStatus(404);
}

res.sendStatus(204);
});

app.get('/expenses/:id', async (req, res) => {
const expense = await Expense.findByPk(req.params.id, {
include: [{ model: Category, as: 'Category' }],
});

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

const data = expense.toJSON();
const response = {
...data,
category: data.Category ? data.Category.name : null,
};

delete response.Category;
delete response.categoryId;

res.json(response);
});

app.patch('/expenses/:id', async (req, res) => {
const expense = await Expense.findByPk(req.params.id);

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

const { category, ...otherData } = req.body;
const updateData = { ...otherData };

if (category) {
const [foundCategory] = await Category.findOrCreate({
where: { name: category },
});

updateData.categoryId = foundCategory.id;
}

await expense.update(updateData);

const updatedExpense = await Expense.findByPk(req.params.id, {
include: [{ model: Category, as: 'Category' }],
});

const data = updatedExpense.toJSON();
const response = {
...data,
category: data.Category ? data.Category.name : null,
};

delete response.Category;
delete response.categoryId;

res.json(response);
});

app.get('/categories', async (req, res) => {
try {
const categories = await Category.findAll({ order: [['id', 'ASC']] });

res.json(categories);
} catch (e) {
res.status(500).json({ error: e.message });
}
});

app.post('/categories', async (req, res) => {
const { name } = req.body;

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

try {
const [category, created] = await Category.findOrCreate({
where: { name },
});

res.status(created ? 201 : 200).json(category);
} catch (e) {
res.status(400).json({ error: e.message });
}
});

app.patch('/categories/:id', async (req, res) => {
try {
const category = await Category.findByPk(req.params.id);

if (!category) {
return res.sendStatus(404);
}
await category.update(req.body);
res.json(category);
} catch (e) {
res.status(400).json({ error: e.message });
}
});

app.delete('/categories/:id', async (req, res) => {
try {
const category = await Category.findByPk(req.params.id);

if (!category) {
return res.sendStatus(404);
}
await category.destroy();
res.sendStatus(204);
} catch (e) {
res
.status(400)
.send('Cannot delete category: it is assigned to expenses.');
}
});

return app;
};

module.exports = { createServer };
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 functionality for categories. This would involve creating a Category model, importing it here, and then adding the corresponding API endpoints (e.g., GET, POST, PATCH, DELETE for /categories) within this file. This implementation is currently missing.

4 changes: 2 additions & 2 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ const {
*/

const sequelize = new Sequelize({
database: POSTGRES_DB || 'postgres',
database: POSTGRES_DB || 'students',
username: POSTGRES_USER || 'postgres',
host: POSTGRES_HOST || 'localhost',
dialect: 'postgres',
port: POSTGRES_PORT || 5432,
password: POSTGRES_PASSWORD || '123',
password: POSTGRES_PASSWORD || 'postgres',
});

module.exports = {
Expand Down
24 changes: 21 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@

const { createServer } = require('./createServer');

createServer().listen(5700, () => {
console.log('Server is running on localhost:5700');
});
const { sequelize } = require('./db');

const app = createServer();
const PORT = process.env.PORT || 3000;

async function start() {
try {
await sequelize.authenticate();
console.log('Connection to PostgreSQL has been established successfully.');

await sequelize.sync({ alter: true });

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
} catch (error) {
console.error('Unable to connect to the database:', error);
}
}

start();
11 changes: 11 additions & 0 deletions src/models/Category.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../db');

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

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

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

const Expense = sequelize.define(
// your code goes here
'Expense',
{
title: {
type: DataTypes.STRING,
allowNull: false,
},
amount: {
type: DataTypes.INTEGER,
allowNull: false,
},

spentAt: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: DataTypes.NOW,
},
note: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{
tableName: 'expenses',
timestamps: false,
},
);

module.exports = {
Expand Down
Loading
Loading