chore: initial commit

This commit is contained in:
Divlo
2021-10-24 04:06:16 +02:00
commit 714cc643ba
260 changed files with 40783 additions and 0 deletions

View File

@ -0,0 +1,24 @@
/messages/{messageId}:
delete:
security:
- bearerAuth: []
tags:
- 'messages'
summary: 'DELETE a message with its id'
parameters:
- name: 'messageId'
in: 'path'
required: true
responses:
allOf:
- $ref: '#/definitions/UnauthorizedError'
- $ref: '#/definitions/ForbiddenError'
- $ref: '#/definitions/NotFoundError'
- '200':
content:
application/json:
schema:
type: 'object'
properties:
deletedMessageId:
type: 'number'

View File

@ -0,0 +1,37 @@
/messages/{messageId}:
put:
security:
- bearerAuth: []
tags:
- 'messages'
summary: 'Update a message with its id'
parameters:
- name: 'messageId'
in: 'path'
required: true
requestBody:
content:
application/json:
schema:
type: 'object'
properties:
value:
type: 'string'
minLength: 1
maxLength: 50_000
responses:
allOf:
- $ref: '#/definitions/UnauthorizedError'
- $ref: '#/definitions/ForbiddenError'
- $ref: '#/definitions/BadRequestError'
- $ref: '#/definitions/NotFoundError'
- '200':
content:
application/json:
schema:
type: 'object'
properties:
message:
allOf:
- $ref: '#/definitions/Message'
- $ref: '#/definitions/User'

View File

@ -0,0 +1,36 @@
import request from 'supertest'
import application from '../../../../application'
import { createMessages } from '../../__test__/utils/createMessages'
import Message from '../../../../models/Message'
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
import { formatErrors } from '../../../../__test__/utils/formatErrors'
describe('DELETE /messages/:messageId', () => {
it('succeeds and delete the message', async () => {
const result = await createMessages(['awesome'])
const messageToDelete = result.messages[0]
const response = await request(application)
.delete(`/messages/${messageToDelete.id as number}`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send()
.expect(200)
expect(response.body.deletedMessageId).toEqual(messageToDelete.id)
const foundMessage = await Message.findOne({
where: { id: messageToDelete.id }
})
expect(foundMessage).toBeNull()
})
it("fails if the message doesn't exist", async () => {
const userToken = await authenticateUserTest()
const response = await request(application)
.delete('/messages/23')
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
.send()
.expect(404)
const errors = formatErrors(response.body.errors)
expect(errors.length).toEqual(1)
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
})
})

View File

@ -0,0 +1,38 @@
import request from 'supertest'
import application from '../../../../application'
import { commonErrorsMessages } from '../../../../tools/configurations/constants'
import { formatErrors } from '../../../../__test__/utils/formatErrors'
import { createMessages } from '../../__test__/utils/createMessages'
describe('PUT /messages/:messageId', () => {
it('succeeds and edit the message', async () => {
const value = 'awesome message'
const result = await createMessages(['awesome'])
const messageToEdit = result.messages[0]
const response = await request(application)
.put(`/messages/${messageToEdit.id as number}`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send({ value })
.expect(200)
expect(response.body.message).not.toBeNull()
expect(response.body.message.value).toEqual(value)
})
it('fails with no message value', async () => {
const result = await createMessages(['awesome'])
const messageToEdit = result.messages[0]
const response = await request(application)
.put(`/messages/${messageToEdit.id as number}`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send()
.expect(400)
const errors = formatErrors(response.body.errors)
expect(errors.length).toEqual(1)
expect(errors).toEqual(
expect.arrayContaining([
commonErrorsMessages.charactersLength('value', { min: 1, max: 50_000 })
])
)
})
})

View File

@ -0,0 +1,61 @@
import { Request, Response, Router } from 'express'
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
import Channel from '../../../models/Channel'
import Member from '../../../models/Member'
import Message from '../../../models/Message'
import { messagesFilePath } from '../../../tools/configurations/constants'
import { BadRequestError } from '../../../tools/errors/BadRequestError'
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
import { NotFoundError } from '../../../tools/errors/NotFoundError'
import { deleteFile } from '../../../tools/utils/deleteFiles'
import { emitToMembers } from '../../../tools/socket/emitEvents'
export const deleteByIdMessagesRouter = Router()
deleteByIdMessagesRouter.delete(
'/messages/:messageId',
authenticateUser,
async (req: Request, res: Response) => {
if (req.user == null) {
throw new ForbiddenError()
}
const user = req.user.current
const { messageId } = req.params as { messageId: string }
const message = await Message.findOne({ where: { id: messageId } })
if (message == null) {
throw new NotFoundError()
}
const channel = await Channel.findOne({
where: { id: message.channelId }
})
if (channel == null) {
throw new NotFoundError()
}
const member = await Member.findOne({
where: { userId: user.id, guildId: channel.guildId }
})
if (member == null) {
throw new NotFoundError()
}
if (!member.isOwner && member.id !== message.memberId) {
throw new BadRequestError(
'You can only delete your messages except if you are owner of the guild'
)
}
const deletedMessageId = message.id
if (message.type === 'file') {
await deleteFile({
basePath: messagesFilePath,
valueSavedInDatabase: message.value
})
}
await message.destroy()
await emitToMembers({
event: 'messages',
guildId: channel.guildId,
payload: { action: 'delete', item: message }
})
return res.status(200).json({ deletedMessageId })
}
)

View File

@ -0,0 +1,71 @@
import { Request, Response, Router } from 'express'
import { body } from 'express-validator'
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
import { validateRequest } from '../../../tools/middlewares/validateRequest'
import Channel from '../../../models/Channel'
import Member from '../../../models/Member'
import Message from '../../../models/Message'
import { commonErrorsMessages } from '../../../tools/configurations/constants'
import { BadRequestError } from '../../../tools/errors/BadRequestError'
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
import { NotFoundError } from '../../../tools/errors/NotFoundError'
import { emitToMembers } from '../../../tools/socket/emitEvents'
export const putByIdMessagesRouter = Router()
putByIdMessagesRouter.put(
'/messages/:messageId',
authenticateUser,
[
body('value')
.trim()
.escape()
.isLength({ min: 1, max: 50_000 })
.withMessage(
commonErrorsMessages.charactersLength('value', { min: 1, max: 50_000 })
)
],
validateRequest,
async (req: Request, res: Response) => {
if (req.user == null) {
throw new ForbiddenError()
}
const user = req.user.current
const { value } = req.body as { value: string }
const { messageId } = req.params as { messageId: string }
const messageToEdit = await Message.findOne({ where: { id: messageId } })
if (messageToEdit == null) {
throw new NotFoundError()
}
if (messageToEdit.type === 'file') {
throw new BadRequestError(
'You can\'t edit a message with the type "file"'
)
}
const channel = await Channel.findOne({
where: { id: messageToEdit.channelId }
})
if (channel == null) {
throw new NotFoundError()
}
const member = await Member.findOne({
where: { userId: user.id, guildId: channel.guildId }
})
if (member == null) {
throw new NotFoundError()
}
if (member.id !== messageToEdit.memberId) {
throw new BadRequestError('You can only update your messages')
}
messageToEdit.value = value ?? messageToEdit.value
await messageToEdit.save()
const message = { ...messageToEdit.toJSON(), user: req.user.current }
await emitToMembers({
event: 'messages',
guildId: channel.guildId,
payload: { action: 'update', item: message }
})
return res.status(200).json({ message })
}
)

View File

@ -0,0 +1,29 @@
definitions:
MessageType:
type: 'string'
enum: ['text', 'file']
Message:
type: 'object'
properties:
id:
type: 'integer'
description: 'Unique id'
value:
type: 'string'
type:
allOf:
- $ref: '#/definitions/MessageType'
mimetype:
type: 'string'
example: 'text/plain'
memberId:
type: 'integer'
channelId:
type: 'integer'
createdAt:
type: 'string'
format: 'date-time'
updatedAt:
type: 'string'
format: 'date-time'

View File

@ -0,0 +1,35 @@
import request from 'supertest'
import application from '../../../../application'
import Channel from '../../../../models/Channel'
import Message from '../../../../models/Message'
import { createChannels } from '../../../channels/__test__/utils/createChannel'
import { CreateGuildResult } from '../../../guilds/__test__/utils/createGuild'
interface CreateMessagesResult extends CreateGuildResult {
channels: Channel[]
channelId: number
messages: Message[]
}
export const createMessages = async (
messages: string[]
): Promise<CreateMessagesResult> => {
const channel1 = { name: 'general1', description: 'testing' }
const result = await createChannels([channel1])
const messagesResponses: Message[] = []
const channelId = result.channels[0].id as number
for (const message of messages) {
const response = await request(application)
.post(`/channels/${channelId}/messages`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send({ value: message, type: 'text' })
.expect(201)
messagesResponses.push(response.body.message)
}
return {
...result,
channelId,
messages: messagesResponses
}
}

View File

@ -0,0 +1,9 @@
import { Router } from 'express'
import { deleteByIdMessagesRouter } from './[messageId]/delete'
import { putByIdMessagesRouter } from './[messageId]/put'
export const messagesRouter = Router()
messagesRouter.use('/', deleteByIdMessagesRouter)
messagesRouter.use('/', putByIdMessagesRouter)