chore: initial commit
This commit is contained in:
24
src/services/guilds/[guildId]/__docs__/delete.yaml
Normal file
24
src/services/guilds/[guildId]/__docs__/delete.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
/guilds/{guildId}:
|
||||
delete:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'guilds'
|
||||
summary: 'DELETE a guild with its id'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
deletedGuildId:
|
||||
type: 'number'
|
25
src/services/guilds/[guildId]/__docs__/get.yaml
Normal file
25
src/services/guilds/[guildId]/__docs__/get.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
/guilds/{guildId}:
|
||||
get:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'guilds'
|
||||
summary: 'GET a guild with its id'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
guild:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Guild'
|
48
src/services/guilds/[guildId]/__docs__/put.yaml
Normal file
48
src/services/guilds/[guildId]/__docs__/put.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
/guilds/{guildId}:
|
||||
put:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'guilds'
|
||||
summary: 'Update a guild with its id'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
name:
|
||||
type: 'string'
|
||||
minLength: 3
|
||||
maxLength: 30
|
||||
description:
|
||||
type: 'string'
|
||||
maxLength: 160
|
||||
icon:
|
||||
type: 'string'
|
||||
format: 'binary'
|
||||
isPublic:
|
||||
type: 'boolean'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
guild:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Guild'
|
||||
type: 'object'
|
||||
properties:
|
||||
publicInvitation:
|
||||
type: 'string'
|
62
src/services/guilds/[guildId]/__test__/delete.test.ts
Normal file
62
src/services/guilds/[guildId]/__test__/delete.test.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../application'
|
||||
import Guild from '../../../../models/Guild'
|
||||
import { createGuild } from '../../__test__/utils/createGuild'
|
||||
|
||||
describe('DELETE /guilds/:guildId', () => {
|
||||
it('succeeds and delete the guild', async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.delete(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.deletedGuildId).toEqual(result.guild.id)
|
||||
const foundGuild = await Guild.findOne({ where: { id: result?.guild.id as number } })
|
||||
expect(foundGuild).toBeNull()
|
||||
})
|
||||
|
||||
it("fails if the guild doesn't exist", async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.delete('/guilds/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']))
|
||||
})
|
||||
|
||||
it("fails if the user isn't the owner", async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.delete(`/guilds/${result.guild.id as number}`)
|
||||
.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']))
|
||||
})
|
||||
})
|
58
src/services/guilds/[guildId]/__test__/get.test.ts
Normal file
58
src/services/guilds/[guildId]/__test__/get.test.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../application'
|
||||
import { createGuild } from '../../__test__/utils/createGuild'
|
||||
|
||||
describe('GET /guilds/:guildId', () => {
|
||||
it('succeeds and get the guild', async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.guild.name).toEqual(name)
|
||||
expect(response.body.guild.description).toEqual(description)
|
||||
})
|
||||
|
||||
it("fails if the user isn't a member", async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'testing', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result.guild.id as number}`)
|
||||
.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']))
|
||||
})
|
||||
|
||||
it("fails if the guild doesn't exist", async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.get('/guilds/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']))
|
||||
})
|
||||
})
|
182
src/services/guilds/[guildId]/__test__/put.test.ts
Normal file
182
src/services/guilds/[guildId]/__test__/put.test.ts
Normal file
@ -0,0 +1,182 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../application'
|
||||
import Guild from '../../../../models/Guild'
|
||||
import Invitation from '../../../../models/Invitation'
|
||||
import { commonErrorsMessages } from '../../../../tools/configurations/constants'
|
||||
import { randomString } from '../../../../tools/utils/random'
|
||||
import { createGuild } from '../../__test__/utils/createGuild'
|
||||
|
||||
describe('PUT /guilds/:guildId', () => {
|
||||
it('succeeds and edit the guild', async () => {
|
||||
const name = 'guild'
|
||||
const newName = 'guildtest'
|
||||
const description = 'testing'
|
||||
const newDescription = 'new description'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name: newName, description: newDescription })
|
||||
.expect(200)
|
||||
expect(response.body.guild.name).toEqual(newName)
|
||||
expect(response.body.guild.description).toEqual(newDescription)
|
||||
expect(response.body.guild.publicInvitation).toBeNull()
|
||||
const foundGuild = await Guild.findOne({
|
||||
where: { id: result?.guild.id as number }
|
||||
})
|
||||
expect(foundGuild?.name).toEqual(newName)
|
||||
expect(foundGuild?.description).toEqual(newDescription)
|
||||
})
|
||||
|
||||
it('succeeds and create/delete public invitations', async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const resIsPublic = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ isPublic: true })
|
||||
.expect(200)
|
||||
expect(resIsPublic.body.guild.isPublic).toBeTruthy()
|
||||
expect(typeof resIsPublic.body.guild.publicInvitation).toBe('string')
|
||||
const publicInvitation = await Invitation.findOne({
|
||||
where: { isPublic: true, guildId: result?.guild.id as number }
|
||||
})
|
||||
expect(publicInvitation).not.toBeNull()
|
||||
expect(publicInvitation?.expiresIn).toEqual(0)
|
||||
|
||||
const resIsNotPublic = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ isPublic: false })
|
||||
.expect(200)
|
||||
expect(resIsNotPublic.body.guild.isPublic).toBeFalsy()
|
||||
expect(resIsNotPublic.body.guild.publicInvitation).toBeNull()
|
||||
const notPublicInvitation = await Invitation.findOne({
|
||||
where: { isPublic: false, guildId: result?.guild.id as number }
|
||||
})
|
||||
expect(notPublicInvitation).toBeNull()
|
||||
})
|
||||
|
||||
it("fails if the user isn't the owner", async () => {
|
||||
const name = 'guild'
|
||||
const newName = 'guildtest'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name: newName })
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
|
||||
it("fails if the guild doesn't exist", async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/guilds/23')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name: 'kjdjhdjh' })
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
|
||||
it('fails with invalid name', async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name: randomString(35) })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with name already used', async () => {
|
||||
const { guild } = await createGuild({
|
||||
guild: { description: 'testing', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const result = await createGuild({
|
||||
guild: { description: 'testing', name: 'guild2' },
|
||||
user: {
|
||||
email: 'test@test2.com',
|
||||
name: 'Test2'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name: guild.name })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Name already used']))
|
||||
})
|
||||
|
||||
it('fails with invalid description', async () => {
|
||||
const name = 'guild'
|
||||
const description = 'testing'
|
||||
const result = await createGuild({
|
||||
guild: { description, name },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.put(`/guilds/${result.guild.id as number}`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ description: randomString(165) })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
commonErrorsMessages.charactersLength('description', { max: 160 })
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
31
src/services/guilds/[guildId]/channels/__docs__/get.yaml
Normal file
31
src/services/guilds/[guildId]/channels/__docs__/get.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
/guilds/{guildId}/channels:
|
||||
get:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'channels'
|
||||
summary: 'GET all the channels of a guild'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModelParameters'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModel'
|
||||
type: 'object'
|
||||
properties:
|
||||
rows:
|
||||
type: 'array'
|
||||
items:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Channel'
|
39
src/services/guilds/[guildId]/channels/__docs__/post.yaml
Normal file
39
src/services/guilds/[guildId]/channels/__docs__/post.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
/guilds/{guildId}/channels:
|
||||
post:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'channels'
|
||||
summary: 'Create a channel'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
name:
|
||||
type: 'string'
|
||||
minLength: 3
|
||||
maxLength: 30
|
||||
description:
|
||||
type: 'string'
|
||||
maxLength: 160
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
channel:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Channel'
|
23
src/services/guilds/[guildId]/channels/__test__/get.test.ts
Normal file
23
src/services/guilds/[guildId]/channels/__test__/get.test.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import application from '../../../../../application'
|
||||
import { createChannels } from '../../../../channels/__test__/utils/createChannel'
|
||||
|
||||
describe('GET /guilds/:guildId/channels', () => {
|
||||
it('should get all the channels of the guild', async () => {
|
||||
const channel1 = { name: 'general1', description: 'testing' }
|
||||
const channel2 = { name: 'general2', description: 'testing' }
|
||||
const result = await createChannels([channel1, channel2])
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result.guild.id as number}/channels/`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.hasMore).toBeFalsy()
|
||||
expect(response.body.rows.length).toEqual(3)
|
||||
expect(response.body.rows[0].name).toEqual(channel2.name)
|
||||
expect(response.body.rows[0].description).toEqual(channel2.description)
|
||||
expect(response.body.rows[1].name).toEqual(channel1.name)
|
||||
expect(response.body.rows[1].description).toEqual(channel1.description)
|
||||
})
|
||||
})
|
146
src/services/guilds/[guildId]/channels/__test__/post.test.ts
Normal file
146
src/services/guilds/[guildId]/channels/__test__/post.test.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../../application'
|
||||
import { commonErrorsMessages } from '../../../../../tools/configurations/constants'
|
||||
import { randomString } from '../../../../../tools/utils/random'
|
||||
import { createGuild } from '../../../__test__/utils/createGuild'
|
||||
import { errorsMessages } from '../post'
|
||||
|
||||
describe('POST /guilds/:guildId/channels', () => {
|
||||
it('succeeds with valid name/description', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const name = 'channel-name'
|
||||
const description = 'testing channel creation'
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name, description })
|
||||
.expect(201)
|
||||
expect(response.body.channel).not.toBeNull()
|
||||
expect(response.body.channel.guildId).not.toBeUndefined()
|
||||
expect(response.body.channel.name).toBe(name)
|
||||
expect(response.body.channel.description).toBe(description)
|
||||
})
|
||||
|
||||
it('succeeds with only channel name', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const name = 'channel-name'
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name })
|
||||
.expect(201)
|
||||
expect(response.body.channel).not.toBeNull()
|
||||
expect(response.body.channel.name).toBe(name)
|
||||
})
|
||||
|
||||
it('succeeds with invalid slug name', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const name = 'channel name'
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name, description: 'testing' })
|
||||
.expect(201)
|
||||
expect(response.body.channel).not.toBeNull()
|
||||
expect(response.body.channel.name).toBe(name)
|
||||
})
|
||||
|
||||
it('fails without name', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ description: 'testing channel creation' })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(3)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
errorsMessages.name.isRequired,
|
||||
commonErrorsMessages.charactersLength('name', { min: 3, max: 30 })
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with invalid description', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ name: 'channel-name', description: randomString(170) })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
commonErrorsMessages.charactersLength('description', { max: 160 })
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("fails if the user isn't the owner", async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const userToken = await authenticateUserTest()
|
||||
const name = 'channel-name'
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/channels`)
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name, description: 'testing channel creation' })
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
|
||||
it("fails if the guild does't exist", async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const name = 'channel-name'
|
||||
const response = await request(application)
|
||||
.post('/guilds/1/channels')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name, description: 'testing channel creation' })
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
})
|
43
src/services/guilds/[guildId]/channels/get.ts
Normal file
43
src/services/guilds/[guildId]/channels/get.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
|
||||
import Channel from '../../../../models/Channel'
|
||||
import Member from '../../../../models/Member'
|
||||
import { paginateModel } from '../../../../tools/database/paginateModel'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
|
||||
export const getChannelsRouter = Router()
|
||||
|
||||
getChannelsRouter.get(
|
||||
'/guilds/:guildId/channels',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { itemsPerPage, page } = req.query as {
|
||||
itemsPerPage: string
|
||||
page: string
|
||||
}
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId }
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const channels = await paginateModel({
|
||||
Model: Channel,
|
||||
queryOptions: { itemsPerPage, page },
|
||||
findOptions: {
|
||||
order: [['createdAt', 'DESC']],
|
||||
where: {
|
||||
guildId: member.guildId
|
||||
}
|
||||
}
|
||||
})
|
||||
return res.status(200).json(channels)
|
||||
}
|
||||
)
|
9
src/services/guilds/[guildId]/channels/index.ts
Normal file
9
src/services/guilds/[guildId]/channels/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { getChannelsRouter } from './get'
|
||||
import { postChannelsRouter } from './post'
|
||||
|
||||
export const guildsChannelsRouter = Router()
|
||||
|
||||
guildsChannelsRouter.use('/', getChannelsRouter)
|
||||
guildsChannelsRouter.use('/', postChannelsRouter)
|
73
src/services/guilds/[guildId]/channels/post.ts
Normal file
73
src/services/guilds/[guildId]/channels/post.ts
Normal file
@ -0,0 +1,73 @@
|
||||
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 { commonErrorsMessages } from '../../../../tools/configurations/constants'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
import { emitToMembers } from '../../../../tools/socket/emitEvents'
|
||||
|
||||
export const errorsMessages = {
|
||||
name: {
|
||||
isRequired: 'Name is required'
|
||||
}
|
||||
}
|
||||
|
||||
export const postChannelsRouter = Router()
|
||||
|
||||
postChannelsRouter.post(
|
||||
'/guilds/:guildId/channels',
|
||||
authenticateUser,
|
||||
[
|
||||
body('name')
|
||||
.notEmpty()
|
||||
.withMessage(errorsMessages.name.isRequired)
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 30, min: 3 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
|
||||
),
|
||||
body('description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 160 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('description', { max: 160 })
|
||||
)
|
||||
],
|
||||
validateRequest,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const user = req.user.current
|
||||
const { name, description = '' } = req.body as {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId, isOwner: true }
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const channel = await Channel.create({
|
||||
name,
|
||||
description,
|
||||
guildId: member.guildId
|
||||
})
|
||||
await emitToMembers({
|
||||
event: 'channels',
|
||||
guildId: member.guildId,
|
||||
payload: { action: 'create', item: channel }
|
||||
})
|
||||
return res.status(201).json({ channel })
|
||||
}
|
||||
)
|
57
src/services/guilds/[guildId]/delete.ts
Normal file
57
src/services/guilds/[guildId]/delete.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
|
||||
import Guild from '../../../models/Guild'
|
||||
import Member from '../../../models/Member'
|
||||
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../tools/errors/NotFoundError'
|
||||
import { guildsIconPath } from '../../../tools/configurations/constants'
|
||||
import { deleteFile, deleteMessages } from '../../../tools/utils/deleteFiles'
|
||||
import Channel from '../../../models/Channel'
|
||||
import Message from '../../../models/Message'
|
||||
import { emitToMembers } from '../../../tools/socket/emitEvents'
|
||||
|
||||
export const deleteByIdGuildsRouter = Router()
|
||||
|
||||
deleteByIdGuildsRouter.delete(
|
||||
'/guilds/:guildId',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId, isOwner: true },
|
||||
include: [Guild]
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const deletedGuildId = member.guild.id
|
||||
await emitToMembers({
|
||||
event: 'guilds',
|
||||
guildId: member.guildId,
|
||||
payload: { action: 'delete', item: member.guild }
|
||||
})
|
||||
await deleteFile({
|
||||
basePath: guildsIconPath,
|
||||
valueSavedInDatabase: member.guild.icon
|
||||
})
|
||||
const members = await Member.findAll({ where: { guildId: deletedGuildId } })
|
||||
for (const member of members) {
|
||||
await member.destroy()
|
||||
}
|
||||
const channels = await Channel.findAll({
|
||||
where: { guildId },
|
||||
include: [Message]
|
||||
})
|
||||
for (const channel of channels) {
|
||||
await deleteMessages(channel.messages)
|
||||
await channel.destroy()
|
||||
}
|
||||
await member.guild.destroy()
|
||||
return res.status(200).json({ deletedGuildId })
|
||||
}
|
||||
)
|
29
src/services/guilds/[guildId]/get.ts
Normal file
29
src/services/guilds/[guildId]/get.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
|
||||
import Guild from '../../../models/Guild'
|
||||
import Member from '../../../models/Member'
|
||||
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../tools/errors/NotFoundError'
|
||||
|
||||
export const getByIdGuildsRouter = Router()
|
||||
|
||||
getByIdGuildsRouter.get(
|
||||
'/guilds/:guildId',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId },
|
||||
include: [Guild]
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
return res.status(200).json({ guild: member.guild })
|
||||
}
|
||||
)
|
19
src/services/guilds/[guildId]/index.ts
Normal file
19
src/services/guilds/[guildId]/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { deleteByIdGuildsRouter } from './delete'
|
||||
import { getByIdGuildsRouter } from './get'
|
||||
import { putByIdGuildsRouter } from './put'
|
||||
|
||||
import { guildsChannelsRouter } from './channels'
|
||||
import { guildsInvitationsRouter } from './invitations'
|
||||
import { guildsMembersRouter } from './members'
|
||||
|
||||
export const guildsGetByIdRouter = Router()
|
||||
|
||||
guildsGetByIdRouter.use('/', getByIdGuildsRouter)
|
||||
guildsGetByIdRouter.use('/', deleteByIdGuildsRouter)
|
||||
guildsGetByIdRouter.use('/', putByIdGuildsRouter)
|
||||
|
||||
guildsGetByIdRouter.use('/', guildsChannelsRouter)
|
||||
guildsGetByIdRouter.use('/', guildsInvitationsRouter)
|
||||
guildsGetByIdRouter.use('/', guildsMembersRouter)
|
31
src/services/guilds/[guildId]/invitations/__docs__/get.yaml
Normal file
31
src/services/guilds/[guildId]/invitations/__docs__/get.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
/guilds/{guildId}/invitations:
|
||||
get:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'invitations'
|
||||
summary: 'GET all the invitations of a guild'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModelParameters'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModel'
|
||||
type: 'object'
|
||||
properties:
|
||||
rows:
|
||||
type: 'array'
|
||||
items:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Invitation'
|
40
src/services/guilds/[guildId]/invitations/__docs__/post.yaml
Normal file
40
src/services/guilds/[guildId]/invitations/__docs__/post.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
/guilds/{guildId}/invitations:
|
||||
post:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'invitations'
|
||||
summary: 'Create an invitation'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
value:
|
||||
type: 'string'
|
||||
minLength: 1
|
||||
maxLength: 250
|
||||
expiresIn:
|
||||
type: 'integer'
|
||||
isPublic:
|
||||
type: 'boolean'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/NotFoundError'
|
||||
- '201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
invitation:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Invitation'
|
@ -0,0 +1,46 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import application from '../../../../../application'
|
||||
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../../__test__/utils/formatErrors'
|
||||
import { createInvitation } from '../../../../invitations/__test__/utils/createInvitation'
|
||||
|
||||
describe('GET /guilds/:guildId/invitations', () => {
|
||||
it('should get all the invitations of the guild', async () => {
|
||||
const value1 = 'awesome'
|
||||
const value2 = 'awesomevalue'
|
||||
const result = await createInvitation({ value: value1 })
|
||||
await createInvitation({
|
||||
value: value2,
|
||||
guildId: result?.guild.id
|
||||
})
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result?.guild.id as number}/invitations`)
|
||||
.set(
|
||||
'Authorization',
|
||||
`${result?.user.type as string} ${result?.user.accessToken as string}`
|
||||
)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.hasMore).toBeFalsy()
|
||||
expect(response.body.rows.length).toEqual(2)
|
||||
expect(response.body.rows[0].value).toEqual(value2)
|
||||
expect(response.body.rows[1].value).toEqual(value1)
|
||||
})
|
||||
|
||||
it('fails if the user is not the owner', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const result = await createInvitation()
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result?.guild.id as number}/invitations`)
|
||||
.set(
|
||||
'Authorization',
|
||||
`${userToken.type as string} ${userToken.accessToken}`
|
||||
)
|
||||
.send()
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
})
|
163
src/services/guilds/[guildId]/invitations/__test__/post.test.ts
Normal file
163
src/services/guilds/[guildId]/invitations/__test__/post.test.ts
Normal file
@ -0,0 +1,163 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../../application'
|
||||
import { createGuild } from '../../../__test__/utils/createGuild'
|
||||
import { errorsMessages } from '../post'
|
||||
import { commonErrorsMessages } from '../../../../../tools/configurations/constants'
|
||||
|
||||
describe('POST /guilds/:guildId/invitations', () => {
|
||||
it('succeeds and create the invitation', async () => {
|
||||
const value = 'random'
|
||||
const expiresIn = 0
|
||||
const isPublic = false
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value, expiresIn, isPublic })
|
||||
.expect(201)
|
||||
expect(response.body.invitation.value).toEqual(value)
|
||||
expect(response.body.invitation.expiresIn).toEqual(expiresIn)
|
||||
expect(response.body.invitation.isPublic).toEqual(isPublic)
|
||||
})
|
||||
|
||||
it('fails with empty value', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ expiresIn: 0 })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(3)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
errorsMessages.value.shouldNotBeEmpty,
|
||||
errorsMessages.value.mustBeSlug,
|
||||
commonErrorsMessages.charactersLength('value', { max: 250, min: 1 })
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with invalid slug value', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value: 'random value' })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([errorsMessages.value.mustBeSlug])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with negative expiresIn', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value: 'awesome', expiresIn: -42 })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([errorsMessages.expiresIn.mustBeGreaterOrEqual])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if the invitation slug value already exists', async () => {
|
||||
const value = 'awesome'
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value })
|
||||
.expect(201)
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Value already used']))
|
||||
})
|
||||
|
||||
it('fails with isPublic: true - if there is already a public invitation for this guild', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value: 'awesome', isPublic: true })
|
||||
.expect(201)
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send({ value: 'awesome2', isPublic: true })
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([errorsMessages.public.alreadyHasInvitation])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if the user is not the owner', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const response = await request(application)
|
||||
.post(`/guilds/${result.guild.id as number}/invitations`)
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ value: 'value' })
|
||||
.expect(404)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(1)
|
||||
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
|
||||
})
|
||||
})
|
43
src/services/guilds/[guildId]/invitations/get.ts
Normal file
43
src/services/guilds/[guildId]/invitations/get.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
|
||||
import Invitation from '../../../../models/Invitation'
|
||||
import Member from '../../../../models/Member'
|
||||
import { paginateModel } from '../../../../tools/database/paginateModel'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
|
||||
export const getInvitationsRouter = Router()
|
||||
|
||||
getInvitationsRouter.get(
|
||||
'/guilds/:guildId/invitations',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { itemsPerPage, page } = req.query as {
|
||||
itemsPerPage: string
|
||||
page: string
|
||||
}
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId, isOwner: true }
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const invitations = await paginateModel({
|
||||
Model: Invitation,
|
||||
queryOptions: { itemsPerPage, page },
|
||||
findOptions: {
|
||||
order: [['createdAt', 'DESC']],
|
||||
where: {
|
||||
guildId: member.guildId
|
||||
}
|
||||
}
|
||||
})
|
||||
return res.status(200).json(invitations)
|
||||
}
|
||||
)
|
9
src/services/guilds/[guildId]/invitations/index.ts
Normal file
9
src/services/guilds/[guildId]/invitations/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { postInvitationsRouter } from './post'
|
||||
import { getInvitationsRouter } from './get'
|
||||
|
||||
export const guildsInvitationsRouter = Router()
|
||||
|
||||
guildsInvitationsRouter.use('/', postInvitationsRouter)
|
||||
guildsInvitationsRouter.use('/', getInvitationsRouter)
|
89
src/services/guilds/[guildId]/invitations/post.ts
Normal file
89
src/services/guilds/[guildId]/invitations/post.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
import { body } from 'express-validator'
|
||||
|
||||
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
|
||||
import { validateRequest } from '../../../../tools/middlewares/validateRequest'
|
||||
import Invitation from '../../../../models/Invitation'
|
||||
import Member from '../../../../models/Member'
|
||||
import { commonErrorsMessages } from '../../../../tools/configurations/constants'
|
||||
import { alreadyUsedValidation } from '../../../../tools/validations/alreadyUsedValidation'
|
||||
import { BadRequestError } from '../../../../tools/errors/BadRequestError'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
|
||||
export const errorsMessages = {
|
||||
value: {
|
||||
mustBeSlug: 'Value must be a slug',
|
||||
shouldNotBeEmpty: 'Value should not be empty'
|
||||
},
|
||||
expiresIn: {
|
||||
mustBeGreaterOrEqual: 'ExpiresIn must be >= 0'
|
||||
},
|
||||
public: {
|
||||
alreadyHasInvitation: 'There is already a public invitation for this guild'
|
||||
}
|
||||
}
|
||||
|
||||
export const postInvitationsRouter = Router()
|
||||
|
||||
postInvitationsRouter.post(
|
||||
'/guilds/:guildId/invitations',
|
||||
authenticateUser,
|
||||
[
|
||||
body('value')
|
||||
.notEmpty()
|
||||
.withMessage(errorsMessages.value.shouldNotBeEmpty)
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 250, min: 1 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('value', { max: 250, min: 1 })
|
||||
)
|
||||
.isSlug()
|
||||
.withMessage(errorsMessages.value.mustBeSlug)
|
||||
.custom(async (value: string) => {
|
||||
return await alreadyUsedValidation(Invitation, 'value', value)
|
||||
}),
|
||||
body('expiresIn')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0 })
|
||||
.withMessage(errorsMessages.expiresIn.mustBeGreaterOrEqual),
|
||||
body('isPublic').optional({ nullable: true }).isBoolean()
|
||||
],
|
||||
validateRequest,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const user = req.user.current
|
||||
const { value, expiresIn = 0, isPublic = false } = req.body as {
|
||||
value: string
|
||||
expiresIn?: number
|
||||
isPublic?: boolean
|
||||
}
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId, isOwner: true }
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const foundInvitation = await Invitation.findOne({
|
||||
where: { isPublic: true, guildId: member.guildId }
|
||||
})
|
||||
if (isPublic && foundInvitation != null) {
|
||||
throw new BadRequestError(errorsMessages.public.alreadyHasInvitation)
|
||||
}
|
||||
let expiresInValue = expiresIn
|
||||
if (expiresInValue > 0) {
|
||||
expiresInValue += Date.now()
|
||||
}
|
||||
const invitation = await Invitation.create({
|
||||
value,
|
||||
expiresIn,
|
||||
isPublic,
|
||||
guildId: member.guildId
|
||||
})
|
||||
return res.status(201).json({ invitation })
|
||||
}
|
||||
)
|
32
src/services/guilds/[guildId]/members/__docs__/get.yaml
Normal file
32
src/services/guilds/[guildId]/members/__docs__/get.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
/guilds/{guildId}/members:
|
||||
get:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'members'
|
||||
summary: 'GET all the members of a guild'
|
||||
parameters:
|
||||
- name: 'guildId'
|
||||
in: 'path'
|
||||
required: true
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModelParameters'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/PaginateModel'
|
||||
type: 'object'
|
||||
properties:
|
||||
rows:
|
||||
type: 'array'
|
||||
items:
|
||||
allOf:
|
||||
- $ref: '#/definitions/Member'
|
||||
- $ref: '#/definitions/User'
|
35
src/services/guilds/[guildId]/members/__test__/get.test.ts
Normal file
35
src/services/guilds/[guildId]/members/__test__/get.test.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import application from '../../../../../application'
|
||||
import Member from '../../../../../models/Member'
|
||||
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
|
||||
import { createGuild } from '../../../__test__/utils/createGuild'
|
||||
|
||||
describe('GET /guilds/:guildId/members', () => {
|
||||
it('should get all the members of a guild', async () => {
|
||||
const result = await createGuild({
|
||||
guild: { description: 'description', name: 'guild' },
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
name: 'Test'
|
||||
}
|
||||
})
|
||||
const userToken = await authenticateUserTest()
|
||||
await Member.create({
|
||||
userId: userToken.userId,
|
||||
guildId: result.guild.id,
|
||||
lastVisitedChannelId: 1
|
||||
})
|
||||
const response = await request(application)
|
||||
.get(`/guilds/${result.guild.id as number}/members`)
|
||||
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.hasMore).toBeFalsy()
|
||||
expect(response.body.totalItems).toEqual(2)
|
||||
expect(response.body.rows[0].guildId).toEqual(result.guild.id)
|
||||
expect(response.body.rows[1].guildId).toEqual(result.guild.id)
|
||||
expect(response.body.rows[1].user).not.toBeNull()
|
||||
expect(response.body.rows[1].user.password).not.toBeDefined()
|
||||
})
|
||||
})
|
50
src/services/guilds/[guildId]/members/get.ts
Normal file
50
src/services/guilds/[guildId]/members/get.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
|
||||
import Member from '../../../../models/Member'
|
||||
import { paginateModel } from '../../../../tools/database/paginateModel'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
|
||||
export const getMembersRouter = Router()
|
||||
|
||||
getMembersRouter.get(
|
||||
'/guilds/:guildId/members',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { itemsPerPage, page } = req.query as {
|
||||
itemsPerPage: string
|
||||
page: string
|
||||
}
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId }
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
const result = await paginateModel({
|
||||
Model: Member,
|
||||
queryOptions: { itemsPerPage, page },
|
||||
findOptions: {
|
||||
order: [['createdAt', 'DESC']],
|
||||
where: {
|
||||
guildId: member.guildId
|
||||
}
|
||||
}
|
||||
})
|
||||
return res.status(200).json({
|
||||
hasMore: result.hasMore,
|
||||
totalItems: result.totalItems,
|
||||
itemsPerPage: result.itemsPerPage,
|
||||
page: result.page,
|
||||
rows: result.rows.map((row) => {
|
||||
return { ...row.toJSON(), user: user.toJSON() }
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
7
src/services/guilds/[guildId]/members/index.ts
Normal file
7
src/services/guilds/[guildId]/members/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { getMembersRouter } from './get'
|
||||
|
||||
export const guildsMembersRouter = Router()
|
||||
|
||||
guildsMembersRouter.use('/', getMembersRouter)
|
121
src/services/guilds/[guildId]/put.ts
Normal file
121
src/services/guilds/[guildId]/put.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
import fileUpload from 'express-fileupload'
|
||||
import { body } from 'express-validator'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
|
||||
import { validateRequest } from '../../../tools/middlewares/validateRequest'
|
||||
import Guild from '../../../models/Guild'
|
||||
import Invitation from '../../../models/Invitation'
|
||||
import Member from '../../../models/Member'
|
||||
import { ObjectAny } from '../../../typings/utils'
|
||||
import {
|
||||
commonErrorsMessages,
|
||||
guildsIconPath,
|
||||
imageFileUploadOptions
|
||||
} from '../../../tools/configurations/constants'
|
||||
import { alreadyUsedValidation } from '../../../tools/validations/alreadyUsedValidation'
|
||||
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../tools/errors/NotFoundError'
|
||||
import { uploadImage } from '../../../tools/utils/uploadImage'
|
||||
import { emitToMembers } from '../../../tools/socket/emitEvents'
|
||||
|
||||
export const putByIdGuildsRouter = Router()
|
||||
|
||||
putByIdGuildsRouter.put(
|
||||
'/guilds/:guildId',
|
||||
authenticateUser,
|
||||
fileUpload(imageFileUploadOptions),
|
||||
[
|
||||
body('name')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 30, min: 3 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
|
||||
)
|
||||
.custom(async (name: string) => {
|
||||
return await alreadyUsedValidation(Guild, 'name', name)
|
||||
}),
|
||||
body('description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 160 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('description', { max: 160 })
|
||||
),
|
||||
body('isPublic')
|
||||
.optional({ nullable: true })
|
||||
.isBoolean()
|
||||
],
|
||||
validateRequest,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { name, description, isPublic } = req.body as {
|
||||
name?: string
|
||||
description?: string
|
||||
isPublic?: boolean
|
||||
}
|
||||
const icon = req.files?.icon
|
||||
const user = req.user.current
|
||||
const { guildId } = req.params as { guildId: string }
|
||||
const member = await Member.findOne({
|
||||
where: { userId: user.id, guildId, isOwner: true },
|
||||
include: [{ model: Guild, include: [Invitation] }]
|
||||
})
|
||||
if (member == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
let invitation = member.guild.invitations.find(
|
||||
invitation => invitation.isPublic
|
||||
)
|
||||
if (isPublic != null) {
|
||||
if (isPublic && !member.guild.isPublic) {
|
||||
invitation = await Invitation.create({
|
||||
isPublic: true,
|
||||
guildId: member.guild.id,
|
||||
expiresIn: 0,
|
||||
value: uuidv4()
|
||||
})
|
||||
member.guild.isPublic = true
|
||||
} else if (!isPublic) {
|
||||
const foundInvitation = await Invitation.findOne({
|
||||
where: { isPublic: true, guildId: member.guild.id }
|
||||
})
|
||||
if (foundInvitation != null) {
|
||||
await foundInvitation.destroy()
|
||||
}
|
||||
member.guild.isPublic = false
|
||||
invitation = undefined
|
||||
}
|
||||
}
|
||||
|
||||
member.guild.name = name ?? member.guild.name
|
||||
member.guild.description = description ?? member.guild.description
|
||||
const resultUpload = await uploadImage({
|
||||
image: icon,
|
||||
propertyName: 'icon',
|
||||
oldImage: member.guild.icon,
|
||||
imagesPath: guildsIconPath.filePath
|
||||
})
|
||||
if (resultUpload != null) {
|
||||
member.guild.icon = `${guildsIconPath.name}/${resultUpload}`
|
||||
}
|
||||
|
||||
await member.guild.save()
|
||||
const guild = member.guild.toJSON() as ObjectAny
|
||||
guild.publicInvitation = invitation != null ? invitation.value : null
|
||||
delete guild.invitations
|
||||
|
||||
await emitToMembers({
|
||||
event: 'guilds',
|
||||
guildId: guild.id,
|
||||
payload: { action: 'update', item: guild }
|
||||
})
|
||||
return res.status(200).json({ guild })
|
||||
}
|
||||
)
|
Reference in New Issue
Block a user