-
Notifications
You must be signed in to change notification settings - Fork 663
add task solution #515
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
base: master
Are you sure you want to change the base?
add task solution #515
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| POSTGRES_USER=postgres | ||
| POSTGRES_PASSWORD=123 | ||
| POSTGRES_DB=postgres | ||
| POSTGRES_PORT=5432 |
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,3 @@ node_modules | |
|
|
||
| # MacOS | ||
| .DS_Store | ||
|
|
||
| # env files | ||
| *.env | ||
| .env* | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| 'use strict'; | ||
|
|
||
| const { DataTypes } = require('sequelize'); | ||
|
|
||
| const { sequelize } = require('../db.js'); | ||
|
|
||
| const Category = sequelize.define('Category', { | ||
| name: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| }); | ||
|
|
||
| module.exports = { | ||
| Category, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,41 @@ | ||
| 'use strict'; | ||
|
|
||
| const { sequelize } = require('../db.js'); | ||
|
|
||
| const { DataTypes } = require('sequelize'); | ||
| const Expense = sequelize.define( | ||
| // your code goes here | ||
| 'Expense', | ||
| { | ||
| amount: { | ||
| type: DataTypes.INTEGER, | ||
| allowNull: false, | ||
|
Comment on lines
+9
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Будь ласка, розкоментуйте ці рядки, а також ті, що стосуються |
||
| }, | ||
|
|
||
| title: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
|
|
||
| userId: { | ||
| type: DataTypes.INTEGER, | ||
| allowNull: false, | ||
| }, | ||
|
|
||
| spentAt: { | ||
| type: DataTypes.DATE, | ||
| allowNull: false, | ||
| }, | ||
|
|
||
| category: { | ||
| type: DataTypes.STRING, | ||
| allowNull: true, | ||
| }, | ||
|
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With a dedicated
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Щоб забезпечити цілісність даних, це поле слід замінити на зовнішній ключ до моделі
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field should be a foreign key referencing the |
||
| note: { | ||
| type: DataTypes.STRING, | ||
| }, | ||
| }, | ||
| { | ||
| timestamps: false, | ||
| }, | ||
| ); | ||
|
|
||
| module.exports = { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,15 @@ | ||
| 'use strict'; | ||
|
|
||
| const { DataTypes } = require('sequelize'); | ||
|
|
||
| const { sequelize } = require('../db.js'); | ||
|
|
||
| const User = sequelize.define( | ||
| // your code goes here | ||
| ); | ||
| const User = sequelize.define('User', { | ||
| name: { | ||
| type: DataTypes.STRING, | ||
|
Comment on lines
+8
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To establish the relationship between the |
||
| allowNull: false, | ||
| }, | ||
| }); | ||
|
|
||
| module.exports = { | ||
| User, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,18 @@ | |
|
|
||
| const { User } = require('./User.model'); | ||
| const { Expense } = require('./Expense.model'); | ||
| const { Category } = require('./Category.model'); | ||
|
|
||
| // Expense.belongsTo(User); | ||
| // User.hasMany(Expense); | ||
|
|
||
| // Expense.belongsTo(Category); якщо я це додам тести не проходитимуть | ||
| // Category.hasMany(Expense); | ||
|
Comment on lines
+7
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a great practice to define the associations between your models. Uncommenting these lines would establish proper relationships (e.g., one user has many expenses, one category has many expenses). This allows Sequelize to automatically handle foreign keys and enables you to perform more powerful queries, like easily joining tables to get related data.
Comment on lines
+10
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These lines must be uncommented to establish the required relationship between |
||
|
|
||
| module.exports = { | ||
| models: { | ||
| User, | ||
| Expense, | ||
| Category, | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| 'use strict'; | ||
|
|
||
| const express = require('express'); | ||
| const { Category } = require('../models/Category.model'); | ||
|
|
||
| function createCategoryRouter() { | ||
| const router = express.Router(); | ||
|
|
||
| router.get('/', async (req, res) => { | ||
| res.send(await Category.findAll()); | ||
| }); | ||
|
|
||
| router.post('/', async (req, res) => { | ||
| const category = req.body; | ||
|
|
||
| if (!category.name) { | ||
| return res.status(400).send('Name is required'); | ||
| } | ||
|
|
||
| res.status(201).send(await Category.create(category)); | ||
| }); | ||
|
|
||
| router.get('/:id', async (req, res) => { | ||
| const category = await Category.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!category) { | ||
| return res.status(404).send('Category not found'); | ||
| } | ||
|
|
||
| res.send(category); | ||
| }); | ||
|
|
||
| router.delete('/:id', async (req, res) => { | ||
| const category = await Category.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!category) { | ||
| return res.status(404).send('Category not found'); | ||
| } | ||
|
|
||
| await category.destroy(); | ||
|
|
||
| res.status(204).send('Deleted'); | ||
| }); | ||
|
|
||
| router.patch('/:id', async (req, res) => { | ||
| const category = await Category.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!category) { | ||
| return res.status(404).send('Category not found'); | ||
| } | ||
|
|
||
| await category.update(req.body); | ||
|
|
||
| res.send(category); | ||
| }); | ||
|
|
||
| return router; | ||
| } | ||
|
|
||
| module.exports = createCategoryRouter; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| 'use strict'; | ||
|
|
||
| const express = require('express'); | ||
| const { Expense } = require('../models/Expense.model'); | ||
| const { User } = require('../models/User.model'); | ||
| const { Op } = require('sequelize'); | ||
|
|
||
| function createExpensesRoute() { | ||
| const router = express.Router(); | ||
|
|
||
| router.get('/', async (req, res) => { | ||
| const whereClause = {}; | ||
|
|
||
| if (req.query.userId) { | ||
| whereClause.userId = Number(req.query.userId); | ||
| } | ||
|
|
||
| if (req.query.categories) { | ||
| whereClause.category = req.query.categories; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After you update the |
||
| } | ||
|
Comment on lines
+18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This filter is still using |
||
|
|
||
| if (req.query.from || req.query.to) { | ||
| whereClause.spentAt = {}; | ||
|
|
||
| if (req.query.from) { | ||
| whereClause.spentAt[Op.gte] = req.query.from; | ||
| } | ||
|
|
||
| if (req.query.to) { | ||
| whereClause.spentAt[Op.lte] = req.query.to; | ||
| } | ||
| } | ||
|
|
||
| const filteredExpenses = await Expense.findAll({ | ||
| where: whereClause, | ||
| }); | ||
|
|
||
| res.send(filteredExpenses); | ||
| }); | ||
|
|
||
| router.post('/', async (req, res) => { | ||
| const expense = req.body; | ||
|
|
||
| if (!expense.amount) { | ||
| return res.status(400).send('Amount is required'); | ||
| } | ||
|
|
||
| if (!expense.title) { | ||
| return res.status(400).send('title is required'); | ||
| } | ||
|
|
||
| if (!expense.userId) { | ||
| return res.status(400).send('userId is required'); | ||
| } | ||
|
|
||
| const user = await User.findByPk(expense.userId); | ||
|
|
||
| if (!user) { | ||
| return res.status(400).send('Such user doesn`t exist'); | ||
| } | ||
|
|
||
| if (!expense.spentAt) { | ||
| return res.status(400).send('spentAt is required'); | ||
| } | ||
|
|
||
| res.status(201).send(await Expense.create(expense)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're validating the existence of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a critical point for a required change. Before creating the expense, you must add logic to validate the category. If the request body contains a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the validation for the category should happen. As per the previous feedback, if |
||
| }); | ||
|
|
||
| router.get('/:id', async (req, res) => { | ||
| const expense = await Expense.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!expense) { | ||
| return res.status(404).send('Expense not found'); | ||
| } | ||
|
|
||
| res.send(expense); | ||
| }); | ||
|
|
||
| router.delete('/:id', async (req, res) => { | ||
| const expense = await Expense.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!expense) { | ||
| return res.status(404).send('Expense not found'); | ||
| } | ||
|
|
||
| await expense.destroy(); | ||
|
|
||
| res.status(204).send('Deleted'); | ||
| }); | ||
|
|
||
| router.patch('/:id', async (req, res) => { | ||
| const expense = await Expense.findByPk(Number(req.params.id)); | ||
|
|
||
| if (!expense) { | ||
| return res.status(404).send('Expense not found'); | ||
| } | ||
|
|
||
| await expense.update(req.body); | ||
|
|
||
| res.send(expense); | ||
| }); | ||
|
|
||
| return router; | ||
| } | ||
|
|
||
| module.exports = createExpensesRoute; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ці асоціації необхідно розкоментувати, щоб Sequelize знав про зв'язок між моделями
ExpenseтаCategory. Це дозволить вам використовувати такі методи, якinclude, для отримання пов'язаних даних.