-
Notifications
You must be signed in to change notification settings - Fork 275
add task solution #120
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 #120
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,35 @@ | ||
| import { EventEmitter } from 'events'; | ||
| import { messageService } from '../services/message.service.js'; | ||
|
|
||
| export const messageEmitter = new EventEmitter(); | ||
|
|
||
| const create = async (req, res) => { | ||
| const { roomId } = req.params; | ||
| const { text, userId } = req.body; | ||
|
|
||
| if (!roomId || !userId || !text) { | ||
| return res.sendStatus(404); | ||
| } | ||
|
|
||
| const newMessage = await messageService.createMessageInRoom( | ||
| text, | ||
| userId, | ||
| roomId, | ||
| ); | ||
|
|
||
| messageEmitter.emit('message', newMessage); | ||
|
|
||
| res.status(201).json(newMessage); | ||
| }; | ||
|
|
||
| export const getMessages = async (req, res) => { | ||
| const { roomId } = req.params; | ||
| const messages = await messageService.getAllMessagesInRoom(roomId); | ||
|
|
||
| res.status(200).json(messages); | ||
| }; | ||
|
|
||
| export const messageController = { | ||
| create, | ||
| getMessages, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { roomsService } from '../services/rooms.service.js'; | ||
|
|
||
| const getAllRooms = async (req, res) => { | ||
| const rooms = await roomsService.getAllRooms(); | ||
|
|
||
| res.status(200).send(rooms); | ||
| }; | ||
|
|
||
| const createRoom = async (req, res) => { | ||
| const { title, userId, description } = req.body; | ||
|
|
||
| if (!title || !userId) { | ||
| return res.sendStatus(404); | ||
|
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. Issue: Returning status 404 for missing 'title' or 'userId' is not correct. According to REST conventions, 404 is for 'Not Found' resources, not for invalid or missing input. Use status 400 (Bad Request) instead. |
||
| } | ||
|
|
||
| await roomsService.createRoom(title, userId, description); | ||
|
|
||
| res.sendStatus(201); | ||
| }; | ||
|
|
||
| const updateRoom = async (req, res) => { | ||
| const { roomId } = req.params; | ||
| const { title, description } = req.body; | ||
|
|
||
| if ((!title && !description) || !roomId) { | ||
| return res.sendStatus(404); | ||
|
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. Issue: Returning status 404 for missing 'roomId' or both 'title' and 'description' is not correct. 404 is for missing resources, not invalid input. Use status 400 (Bad Request) for input validation errors. |
||
| } | ||
|
|
||
| await roomsService.updateRoom(roomId, title, description); | ||
|
|
||
| res.sendStatus(204); | ||
| }; | ||
|
|
||
| const deleteRoom = async (req, res) => { | ||
| const { roomId } = req.params; | ||
|
|
||
| if (!roomId) { | ||
| return res.sendStatus(400); | ||
| } | ||
|
|
||
| await roomsService.deleteRoom(roomId); | ||
|
|
||
| res.sendStatus(204); | ||
| }; | ||
|
|
||
| export const roomController = { | ||
| getAllRooms, | ||
| createRoom, | ||
| updateRoom, | ||
| deleteRoom, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { User } from '../models/user.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
|
|
||
| const create = async (req, res) => { | ||
| const { name } = req.body; | ||
| const isNameExist = await User.findOne({ where: { name } }); | ||
|
|
||
| if (!name) { | ||
| return res.sendStatus(404); | ||
|
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. Issue: Returning status 404 for missing 'name' is not correct. 404 is for 'Not Found' resources, not for invalid or missing input. Use status 400 (Bad Request) instead. |
||
| } | ||
|
|
||
| if (isNameExist) { | ||
| return res.status(409).json({ message: 'User already exists' }); | ||
| } | ||
|
|
||
| await userService.createUser(name); | ||
|
|
||
| return res.status(201).json({ message: 'User was created!' }); | ||
| }; | ||
|
|
||
| export const userController = { | ||
| create, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import 'dotenv/config'; | ||
| import { Sequelize } from 'sequelize'; | ||
|
|
||
| export const client = new Sequelize({ | ||
| host: process.env.DB_HOST, | ||
| username: process.env.DB_USERNAME, | ||
| password: process.env.DB_PASS, | ||
| database: process.env.DB_DATABASE, | ||
| dialect: 'postgres', | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,64 @@ | ||
| 'use strict'; | ||
| import 'dotenv/config'; | ||
| import express from 'express'; | ||
| import { WebSocketServer } from 'ws'; | ||
| import cors from 'cors'; | ||
| import { userRoute } from './routes/user.route.js'; | ||
| import { Message } from './models/message.js'; | ||
| import { roomsRoute } from './routes/rooms.route.js'; | ||
| import { messageRoute } from './routes/message.route.js'; | ||
| import { messageEmitter } from './controllers/message.controller.js'; | ||
|
|
||
| const PORT = process.env.PORT || 5000; | ||
| const app = express(); | ||
|
|
||
| app.use(cors()); | ||
| app.use(express.json()); | ||
| app.use('/user', userRoute); | ||
| app.use('/rooms', roomsRoute); | ||
| app.use(messageRoute); | ||
|
|
||
| const server = app.listen(PORT, () => { | ||
| // eslint-disable-next-line no-console | ||
| console.log(`Server was started on ${PORT}`); | ||
| }); | ||
|
|
||
| const wss = new WebSocketServer({ server }); | ||
|
|
||
| const rooms = {}; | ||
|
|
||
| wss.on('connection', (ws) => { | ||
| ws.on('message', async (message) => { | ||
| const { roomId } = JSON.parse(message); | ||
|
|
||
| if (!rooms[roomId]) { | ||
| rooms[roomId] = []; | ||
| } | ||
|
|
||
| rooms[roomId].push(ws); | ||
|
|
||
| const messages = await Message.findAll({ where: { roomId } }); | ||
|
|
||
| messages.forEach((msg) => { | ||
| ws.send(JSON.stringify(msg)); | ||
| }); | ||
| }); | ||
|
|
||
| ws.on('close', () => { | ||
| for (const roomId in rooms) { | ||
| rooms[roomId] = rooms[roomId].filter((client) => client !== ws); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| messageEmitter.on('message', async (message) => { | ||
| const { roomId } = message; | ||
|
|
||
| if (rooms[roomId]) { | ||
| rooms[roomId].forEach((client) => { | ||
| if (client.readyState === 1) { | ||
| client.send(JSON.stringify(message)); | ||
| } | ||
| }); | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../db.js'; | ||
| import { User } from './user.js'; | ||
| import { Room } from './room.js'; | ||
|
|
||
| export const Message = client.define( | ||
| 'message', | ||
| { | ||
| id: { | ||
| type: DataTypes.UUID, | ||
| defaultValue: DataTypes.UUIDV4, | ||
| allowNull: false, | ||
| primaryKey: true, | ||
| }, | ||
| text: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| createdAt: { | ||
| type: DataTypes.DATE, | ||
| defaultValue: DataTypes.NOW, | ||
| allowNull: false, | ||
| }, | ||
| }, | ||
| { updatedAt: false }, | ||
| ); | ||
|
|
||
| Message.belongsTo(User); | ||
| User.hasMany(Message); | ||
| Message.belongsTo(Room); | ||
| Room.hasMany(Message); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../db.js'; | ||
| import { User } from './user.js'; | ||
|
|
||
| export const Room = client.define('room', { | ||
| id: { | ||
| type: DataTypes.UUID, | ||
| defaultValue: DataTypes.UUIDV4, | ||
| allowNull: false, | ||
| primaryKey: true, | ||
| }, | ||
| title: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| description: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| }); | ||
|
|
||
| Room.belongsTo(User); | ||
| User.hasMany(Room); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../db.js'; | ||
|
|
||
| export const User = client.define('user', { | ||
| id: { | ||
| type: DataTypes.UUID, | ||
| defaultValue: DataTypes.UUIDV4, | ||
| allowNull: false, | ||
| primaryKey: true, | ||
| }, | ||
| name: { | ||
| type: DataTypes.STRING, | ||
| unique: true, | ||
| allowNull: false, | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import express from 'express'; | ||
| import { messageController } from '../controllers/message.controller.js'; | ||
|
|
||
| export const messageRoute = new express.Router(); | ||
|
|
||
| messageRoute.get('/rooms/:roomId/messages', messageController.getMessages); | ||
| messageRoute.post('/rooms/:roomId/messages', messageController.create); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import express from 'express'; | ||
| import { roomController } from '../controllers/rooms.controller.js'; | ||
|
|
||
| export const roomsRoute = new express.Router(); | ||
|
|
||
| roomsRoute.get('/', roomController.getAllRooms); | ||
| roomsRoute.post('/', roomController.createRoom); | ||
| roomsRoute.patch('/:roomId', roomController.updateRoom); | ||
| roomsRoute.delete('/:roomId', roomController.deleteRoom); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import express from 'express'; | ||
| import { userController } from '../controllers/user.controller.js'; | ||
|
|
||
| export const userRoute = new express.Router(); | ||
|
|
||
| userRoute.post('/', userController.create); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { Message } from '../models/message.js'; | ||
|
|
||
| const getAllMessagesInRoom = (roomId) => { | ||
| return Message.findAll({ where: { roomId } }); | ||
| }; | ||
|
|
||
| const createMessageInRoom = (text, userId, roomId) => { | ||
| return Message.create({ text, userId, roomId }); | ||
| }; | ||
|
|
||
| export const messageService = { | ||
| getAllMessagesInRoom, | ||
| createMessageInRoom, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { Room } from '../models/room.js'; | ||
|
|
||
| const getAllRooms = () => { | ||
| return Room.findAll(); | ||
| }; | ||
|
|
||
| const createRoom = (title, userId, description = '') => { | ||
| return Room.create({ title, userId, description }); | ||
| }; | ||
|
|
||
| const deleteRoom = (id) => { | ||
| return Room.destroy({ where: { id } }); | ||
| }; | ||
|
|
||
| const updateRoom = (id, title, description) => { | ||
| if (!id) { | ||
| throw new Error('Room id is required'); | ||
| } | ||
|
|
||
| const updatedData = {}; | ||
|
|
||
| if (title) { | ||
| updatedData.title = title; | ||
| } | ||
|
|
||
| if (description) { | ||
| updatedData.description = description; | ||
| } | ||
|
|
||
| if (Object.keys(updatedData).length === 0) { | ||
| throw new Error('At least one field must be provided'); | ||
| } | ||
|
|
||
| return Room.update(updatedData, { where: { id } }); | ||
| }; | ||
|
|
||
| export const roomsService = { | ||
| getAllRooms, | ||
| createRoom, | ||
| deleteRoom, | ||
| updateRoom, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { User } from '../models/user.js'; | ||
|
|
||
| const createUser = async (name) => { | ||
| return User.create({ name }); | ||
| }; | ||
|
|
||
| export const userService = { | ||
| createUser, | ||
| }; |
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.
Issue: Returning status 404 for missing parameters is not correct. According to REST conventions, 404 is for 'Not Found' (i.e., resource does not exist), not for missing or invalid input. You should return status 400 (Bad Request) instead.