diff --git a/api/src/routes/settings.test.ts b/api/src/routes/settings.test.ts index dfdbc5ea6d1..7589152e271 100644 --- a/api/src/routes/settings.test.ts +++ b/api/src/routes/settings.test.ts @@ -558,6 +558,54 @@ describe('settingRoutes', () => { expect(response.statusCode).toEqual(400); }); }); + + describe('/update-my-portfolio', () => { + test('PUT returns 200 status code with "success" message', async () => { + const response = await superRequest('/update-my-portfolio', { + method: 'PUT', + setCookies + }).send({ + portfolio: [{}] + }); + + expect(response.body).toEqual({ + message: 'flash.portfolio-item-updated', + type: 'success' + }); + expect(response.statusCode).toEqual(200); + }); + + test('PUT returns 400 status code when the portfolio property is missing', async () => { + const response = await superRequest('/update-my-portfolio', { + method: 'PUT', + setCookies + }).send({}); + + expect(response.body).toEqual({ + type: 'danger', + message: 'flash.wrong-updating' + }); + expect(response.statusCode).toEqual(400); + }); + + test('PUT returns 400 status code when any data is the wrong type', async () => { + const response = await superRequest('/update-my-portfolio', { + method: 'PUT', + setCookies + }).send({ + portfolio: [ + { id: '', title: '', description: '', url: '', image: '' }, + { id: '', title: {}, description: '', url: '', image: '' } + ] + }); + + expect(response.body).toEqual({ + type: 'danger', + message: 'flash.wrong-updating' + }); + expect(response.statusCode).toEqual(400); + }); + }); }); describe('Unauthenticated User', () => { @@ -569,41 +617,17 @@ describe('settingRoutes', () => { setCookies = res.get('Set-Cookie'); }); - test('PUT /update-my-profileui returns 401 status code for un-authenticated users', async () => { - const response = await superRequest('/update-my-profileui', { + test.each([ + '/update-my-profileui', + '/update-my-theme', + '/update-privacy-terms', + '/update-my-username', + '/update-my-portfolio' + ])('PUT %s should return 401 status code', async endpoint => { + const response = await superRequest(endpoint, { method: 'PUT', setCookies }); - - expect(response.statusCode).toEqual(401); - }); - - test('PUT /update-my-theme returns 401 status code for un-authenticated users', async () => { - const response = await superRequest('/update-my-theme', { - method: 'PUT', - setCookies - }); - - expect(response.statusCode).toEqual(401); - }); - - test('PUT /update-privacy-terms returns 401 status code for un-authenticated users', async () => { - const response = await superRequest('/update-privacy-terms', { - method: 'PUT', - setCookies - }); - - expect(response.statusCode).toEqual(401); - }); - - test('PUT /update-my-username returns 401 status code for un-authenticated users', async () => { - const response = await superRequest('/update-my-username', { - method: 'PUT', - setCookies - }).send({ - username: 'twaha2' - }); - expect(response.statusCode).toEqual(401); }); }); diff --git a/api/src/routes/settings.ts b/api/src/routes/settings.ts index 02228a1b1a8..b976add84dd 100644 --- a/api/src/routes/settings.ts +++ b/api/src/routes/settings.ts @@ -354,5 +354,50 @@ export const settingRoutes: FastifyPluginCallbackTypebox = ( } ); + fastify.put( + '/update-my-portfolio', + { + schema: schemas.updateMyPortfolio, + errorHandler: (error, request, reply) => { + if (error.validation) { + void reply.code(400); + void reply.send({ message: 'flash.wrong-updating', type: 'danger' }); + } else { + fastify.errorHandler(error, request, reply); + } + } + }, + async (req, reply) => { + try { + // TODO(Post-MVP): make all properties required in the schema and use + // req.body.portfolio directly. + const portfolio = req.body.portfolio.map( + ({ id, title, url, description, image }) => ({ + id: id ? id : '', + title: title ? title : '', + url: url ? url : '', + description: description ? description : '', + image: image ? image : '' + }) + ); + await fastify.prisma.user.update({ + where: { id: req.session.user.id }, + data: { + portfolio + } + }); + + return { + message: 'flash.portfolio-item-updated', + type: 'success' + } as const; + } catch (err) { + fastify.log.error(err); + void reply.code(500); + return { message: 'flash.wrong-updating', type: 'danger' } as const; + } + } + ); + done(); }; diff --git a/api/src/schemas.ts b/api/src/schemas.ts index 0570117218a..ebf3258c255 100644 --- a/api/src/schemas.ts +++ b/api/src/schemas.ts @@ -168,6 +168,36 @@ export const schemas = { }) } }, + updateMyPortfolio: { + body: Type.Object({ + portfolio: Type.Array( + Type.Object({ + description: Type.Optional(Type.String()), + id: Type.Optional(Type.String()), + image: Type.Optional(Type.String()), + title: Type.Optional(Type.String()), + url: Type.Optional(Type.String()) + }) + ) + }), + response: { + 200: Type.Object({ + message: Type.Literal('flash.portfolio-item-updated'), + type: Type.Literal('success') + }), + // TODO(Post-MVP): give more detailed response (i.e. which item is + // missing) + 400: Type.Object({ + message: Type.Literal('flash.wrong-updating'), + type: Type.Literal('danger') + }), + // TODO(Post-MVP): differentiate with more than just the status + 500: Type.Object({ + message: Type.Literal('flash.wrong-updating'), + type: Type.Literal('danger') + }) + } + }, // User: deleteMyAccount: { response: {