From 1bcee763241943bbf38fb2db1ac8dea3382e5fed Mon Sep 17 00:00:00 2001 From: Divlo Date: Wed, 2 Mar 2022 11:47:13 +0100 Subject: [PATCH] fix(services): add missing real time --- src/models/utils.ts | 31 +++++++---- .../members/join/__test__/post.test.ts | 29 ++++++++-- .../guilds/[guildId]/members/join/post.ts | 54 +++++++++++++------ src/services/users/current/put.ts | 8 +++ src/tools/plugins/socket-io.ts | 2 +- 5 files changed, 92 insertions(+), 32 deletions(-) diff --git a/src/models/utils.ts b/src/models/utils.ts index 16f5852..0c2c6d7 100644 --- a/src/models/utils.ts +++ b/src/models/utils.ts @@ -15,35 +15,44 @@ export const id = Type.Integer({ minimum: 1, description: 'Unique identifier' }) export const redirectURI = Type.String({ format: 'uri-reference' }) -export const fastifyErrors = { - 400: Type.Object({ +export const fastifyErrorsSchema = { + 400: { statusCode: Type.Literal(400), error: Type.Literal('Bad Request'), message: Type.String() - }), - 401: Type.Object({ + }, + 401: { statusCode: Type.Literal(401), error: Type.Literal('Unauthorized'), message: Type.Literal('Unauthorized') - }), - 403: Type.Object({ + }, + 403: { statusCode: Type.Literal(403), error: Type.Literal('Forbidden'), message: Type.Literal('Forbidden') - }), - 404: Type.Object({ + }, + 404: { statusCode: Type.Literal(404), error: Type.Literal('Not Found'), message: Type.Literal('Not Found') - }), - 431: Type.Object({ + }, + 431: { statusCode: Type.Literal(431), error: Type.Literal('Request Header Fields Too Large'), message: Type.String() - }), + }, 500: { statusCode: Type.Literal(500), error: Type.Literal('Internal Server Error'), message: Type.Literal('Something went wrong') } } + +export const fastifyErrors = { + 400: Type.Object(fastifyErrorsSchema[400]), + 401: Type.Object(fastifyErrorsSchema[401]), + 403: Type.Object(fastifyErrorsSchema[403]), + 404: Type.Object(fastifyErrorsSchema[404]), + 431: Type.Object(fastifyErrorsSchema[431]), + 500: Type.Object(fastifyErrorsSchema[500]) +} diff --git a/src/services/guilds/[guildId]/members/join/__test__/post.test.ts b/src/services/guilds/[guildId]/members/join/__test__/post.test.ts index 7c0f20e..1f2b567 100644 --- a/src/services/guilds/[guildId]/members/join/__test__/post.test.ts +++ b/src/services/guilds/[guildId]/members/join/__test__/post.test.ts @@ -8,13 +8,13 @@ import { userExample } from '../../../../../../models/User.js' describe('POST /guilds/[guildId]/members/join', () => { it('succeeds', async () => { + prismaMock.guild.findUnique.mockResolvedValue(guildExample) prismaMock.member.findFirst.mockResolvedValue(null) prismaMock.member.create.mockResolvedValue({ ...memberExample, user: userExample } as any) prismaMock.channel.findFirst.mockResolvedValue(channelExample) - prismaMock.guild.findUnique.mockResolvedValue(guildExample) const { accessToken, user } = await authenticateUserTest() const response = await application.inject({ method: 'POST', @@ -34,8 +34,10 @@ describe('POST /guilds/[guildId]/members/join', () => { expect(responseJson.guild.defaultChannelId).toEqual(channelExample.id) }) - it('fails if the user is already in the guild', async () => { - prismaMock.member.findFirst.mockResolvedValue(memberExample) + it('fails if the guild is not found', async () => { + prismaMock.guild.findUnique.mockResolvedValue(null) + prismaMock.member.findFirst.mockResolvedValue(null) + prismaMock.channel.findFirst.mockResolvedValue(channelExample) const { accessToken } = await authenticateUserTest() const response = await application.inject({ method: 'POST', @@ -44,6 +46,27 @@ describe('POST /guilds/[guildId]/members/join', () => { authorization: `Bearer ${accessToken}` } }) + expect(response.statusCode).toEqual(404) + }) + + it('fails if the user is already in the guild', async () => { + const defaultChannelId = 5 + prismaMock.guild.findUnique.mockResolvedValue(guildExample) + prismaMock.member.findFirst.mockResolvedValue(memberExample) + prismaMock.channel.findFirst.mockResolvedValue({ + ...channelExample, + id: defaultChannelId + }) + const { accessToken } = await authenticateUserTest() + const response = await application.inject({ + method: 'POST', + url: `/guilds/${guildExample.id}/members/join`, + headers: { + authorization: `Bearer ${accessToken}` + } + }) + const responseJson = response.json() expect(response.statusCode).toEqual(400) + expect(responseJson.defaultChannelId).toEqual(defaultChannelId) }) }) diff --git a/src/services/guilds/[guildId]/members/join/post.ts b/src/services/guilds/[guildId]/members/join/post.ts index 71414f2..3bc8c26 100644 --- a/src/services/guilds/[guildId]/members/join/post.ts +++ b/src/services/guilds/[guildId]/members/join/post.ts @@ -2,11 +2,16 @@ import { Static, Type } from '@sinclair/typebox' import { FastifyPluginAsync, FastifySchema } from 'fastify' import prisma from '../../../../../tools/database/prisma.js' -import { fastifyErrors, id } from '../../../../../models/utils.js' +import { + fastifyErrors, + fastifyErrorsSchema, + id +} from '../../../../../models/utils.js' import authenticateUser from '../../../../../tools/plugins/authenticateUser.js' import { guildSchema } from '../../../../../models/Guild.js' import { memberSchema } from '../../../../../models/Member.js' import { userPublicWithoutSettingsSchema } from '../../../../../models/User.js' +import { channelSchema } from '../../../../../models/Channel.js' const parametersSchema = Type.Object({ guildId: guildSchema.id @@ -32,9 +37,13 @@ const postServiceSchema: FastifySchema = { }), user: Type.Object(userPublicWithoutSettingsSchema) }), - 400: fastifyErrors[400], + 400: Type.Object({ + ...fastifyErrorsSchema[400], + defaultChannelId: channelSchema.id + }), 401: fastifyErrors[401], 403: fastifyErrors[403], + 404: fastifyErrors[404], 500: fastifyErrors[500] } } as const @@ -54,15 +63,35 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { } const { user } = request const { guildId } = request.params - const memberCheck = await prisma.member.findFirst({ + const guild = await prisma.guild.findUnique({ + where: { + id: guildId + } + }) + if (guild == null) { + throw fastify.httpErrors.notFound('Guild not found') + } + const defaultChannel = await prisma.channel.findFirst({ where: { - userId: user.current.id, guildId } }) + if (defaultChannel == null) { + throw fastify.httpErrors.internalServerError() + } + const memberCheck = await prisma.member.findFirst({ + where: { + userId: user.current.id, + guildId: guild.id + } + }) if (memberCheck != null) { - throw fastify.httpErrors.badRequest( - "Guild doesn't exist or you are already in the guild" + throw fastify.httpErrors.createError( + 400, + 'You are already in the guild', + { + defaultChannelId: defaultChannel.id + } ) } const member = await prisma.member.create({ @@ -85,16 +114,7 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { } } }) - const channel = await prisma.channel.findFirst({ - where: { - guildId: member.guildId - } - }) - const guild = await prisma.guild.findUnique({ - where: { - id: member.guildId - } - }) + const item = { ...member, user: { @@ -103,7 +123,7 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { }, guild: { ...guild, - defaultChannelId: channel?.id + defaultChannelId: defaultChannel.id } } await fastify.io.emitToMembers({ diff --git a/src/services/users/current/put.ts b/src/services/users/current/put.ts index ed61759..ceacfdb 100644 --- a/src/services/users/current/put.ts +++ b/src/services/users/current/put.ts @@ -131,6 +131,14 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { website: parseStringNullish(request.user.current.website, website) } }) + await fastify.io.emitToAuthorizedUsers({ + event: 'users', + isAuthorizedCallback: () => true, + payload: { + action: 'update', + item: user + } + }) reply.statusCode = 200 return { user: { diff --git a/src/tools/plugins/socket-io.ts b/src/tools/plugins/socket-io.ts index cc1313a..38ddda4 100644 --- a/src/tools/plugins/socket-io.ts +++ b/src/tools/plugins/socket-io.ts @@ -15,7 +15,7 @@ interface EmitEventOptions { interface EmitToAuthorizedUsersOptions extends EmitEventOptions { /** tests whether the current connected userId is authorized to get the event, if the callback returns true, the server will emit the event to that user */ - isAuthorizedCallback: (userId: number) => Promise + isAuthorizedCallback: (userId: number) => Promise | boolean } type EmitToAuthorizedUsers = (