From 8e646c244ec1bbd6e598907c80638ff1a717c60f Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Fri, 29 Mar 2024 23:45:21 +0100 Subject: [PATCH] feat: Implement test connection API endpoint --- .../api/v1/apps/get-connections.test.js | 6 +- .../api/v1/connections/create-test.js | 14 ++ .../api/v1/connections/create-test.test.js | 123 ++++++++++++++++++ packages/backend/src/helpers/authorization.js | 4 + packages/backend/src/models/connection.js | 18 +++ .../backend/src/routes/api/v1/connections.js | 8 ++ 6 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/controllers/api/v1/connections/create-test.js create mode 100644 packages/backend/src/controllers/api/v1/connections/create-test.test.js diff --git a/packages/backend/src/controllers/api/v1/apps/get-connections.test.js b/packages/backend/src/controllers/api/v1/apps/get-connections.test.js index 603ebc04..e35d1a53 100644 --- a/packages/backend/src/controllers/api/v1/apps/get-connections.test.js +++ b/packages/backend/src/controllers/api/v1/apps/get-connections.test.js @@ -85,16 +85,16 @@ describe('GET /api/v1/apps/:appKey/connections', () => { expect(response.body).toEqual(expectedPayload); }); - it('should return not found response for invalid app key', async () => { + it('should return not found response for invalid connection UUID', async () => { await createPermission({ - action: 'read', + action: 'update', subject: 'Connection', roleId: currentUserRole.id, conditions: ['isCreator'], }); await request(app) - .get('/api/v1/apps/invalid-app-key/connections') + .get('/api/v1/connections/invalid-connection-id/connections') .set('Authorization', token) .expect(404); }); diff --git a/packages/backend/src/controllers/api/v1/connections/create-test.js b/packages/backend/src/controllers/api/v1/connections/create-test.js new file mode 100644 index 00000000..32132582 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/connections/create-test.js @@ -0,0 +1,14 @@ +import { renderObject } from '../../../../helpers/renderer.js'; + +export default async (request, response) => { + let connection = await request.currentUser.authorizedConnections + .clone() + .findOne({ + id: request.params.connectionId, + }) + .throwIfNotFound(); + + connection = await connection.testAndUpdateConnection(); + + renderObject(response, connection); +}; diff --git a/packages/backend/src/controllers/api/v1/connections/create-test.test.js b/packages/backend/src/controllers/api/v1/connections/create-test.test.js new file mode 100644 index 00000000..ec44cde3 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/connections/create-test.test.js @@ -0,0 +1,123 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; +import Crypto from 'crypto'; +import app from '../../../../app.js'; +import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; +import { createUser } from '../../../../../test/factories/user.js'; +import { createConnection } from '../../../../../test/factories/connection.js'; +import { createPermission } from '../../../../../test/factories/permission.js'; + +describe('POST /api/v1/connections/:connectionId/test', () => { + let currentUser, currentUserRole, token; + + beforeEach(async () => { + currentUser = await createUser(); + currentUserRole = await currentUser.$relatedQuery('role'); + + token = createAuthTokenByUserId(currentUser.id); + }); + + it('should update the connection as not verified for current user', async () => { + const currentUserConnection = await createConnection({ + userId: currentUser.id, + key: 'deepl', + verified: true, + }); + + await createPermission({ + action: 'read', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + await createPermission({ + action: 'update', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + const response = await request(app) + .post(`/api/v1/connections/${currentUserConnection.id}/test`) + .set('Authorization', token) + .expect(200); + + expect(response.body.data.verified).toEqual(false); + }); + + it('should update the connection as not verified for another user', async () => { + const anotherUser = await createUser(); + + const anotherUserConnection = await createConnection({ + userId: anotherUser.id, + key: 'deepl', + verified: true, + }); + + await createPermission({ + action: 'read', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: [], + }); + + await createPermission({ + action: 'update', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: [], + }); + + const response = await request(app) + .post(`/api/v1/connections/${anotherUserConnection.id}/test`) + .set('Authorization', token) + .expect(200); + + expect(response.body.data.verified).toEqual(false); + }); + + it('should return not found response for not existing connection UUID', async () => { + const notExistingConnectionUUID = Crypto.randomUUID(); + + await createPermission({ + action: 'read', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + await createPermission({ + action: 'update', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + await request(app) + .post(`/api/v1/connections/${notExistingConnectionUUID}/test`) + .set('Authorization', token) + .expect(404); + }); + + it('should return bad request response for invalid UUID', async () => { + await createPermission({ + action: 'read', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + await createPermission({ + action: 'update', + subject: 'Connection', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + await request(app) + .post('/api/v1/connections/invalidConnectionUUID/test') + .set('Authorization', token) + .expect(400); + }); +}); diff --git a/packages/backend/src/helpers/authorization.js b/packages/backend/src/helpers/authorization.js index 85388a06..d4376b35 100644 --- a/packages/backend/src/helpers/authorization.js +++ b/packages/backend/src/helpers/authorization.js @@ -31,6 +31,10 @@ const authorizationList = { action: 'read', subject: 'Flow', }, + 'POST /api/v1/connections/:connectionId/test': { + action: 'update', + subject: 'Connection', + }, 'GET /api/v1/apps/:appKey/flows': { action: 'read', subject: 'Flow', diff --git a/packages/backend/src/models/connection.js b/packages/backend/src/models/connection.js index e79735d2..d4e3f9f8 100644 --- a/packages/backend/src/models/connection.js +++ b/packages/backend/src/models/connection.js @@ -153,6 +153,24 @@ class Connection extends Base { return await App.findOneByKey(this.key); } + async testAndUpdateConnection() { + const app = await this.getApp(); + const $ = await globalVariable({ connection: this, app }); + + let isStillVerified; + + try { + isStillVerified = !!(await app.auth.isStillVerified($)); + } catch { + isStillVerified = false; + } + + return await this.$query().patchAndFetch({ + formattedData: this.formattedData, + verified: isStillVerified, + }); + } + async verifyWebhook(request) { if (!this.key) return true; diff --git a/packages/backend/src/routes/api/v1/connections.js b/packages/backend/src/routes/api/v1/connections.js index 67f2f87e..223f5a05 100644 --- a/packages/backend/src/routes/api/v1/connections.js +++ b/packages/backend/src/routes/api/v1/connections.js @@ -3,6 +3,7 @@ import asyncHandler from 'express-async-handler'; import { authenticateUser } from '../../../helpers/authentication.js'; import { authorizeUser } from '../../../helpers/authorization.js'; import getFlowsAction from '../../../controllers/api/v1/connections/get-flows.js'; +import createTestAction from '../../../controllers/api/v1/connections/create-test.js'; const router = Router(); @@ -13,4 +14,11 @@ router.get( asyncHandler(getFlowsAction) ); +router.post( + '/:connectionId/test', + authenticateUser, + authorizeUser, + asyncHandler(createTestAction) +); + export default router;