From e75459f846180ef4fdab211e548c1e5559374dc2 Mon Sep 17 00:00:00 2001 From: Dima Rosmait <dima.rosmait@holi.team> Date: Thu, 2 Nov 2023 15:21:28 +0000 Subject: [PATCH] HOLI-6271 - Leave space room --- README.md | 18 ++ src/handlers/space-user-added-handler.ts | 2 +- src/handlers/space-user-left-handler.ts | 33 +-- src/helpers/kickUserFromSpace.ts | 31 +++ src/tests/index.test.ts | 248 ----------------------- 5 files changed, 61 insertions(+), 271 deletions(-) create mode 100644 src/helpers/kickUserFromSpace.ts delete mode 100644 src/tests/index.test.ts diff --git a/README.md b/README.md index b390c4f..31312dd 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,24 @@ This repository contains Google Cloud Functions implementation for the integrati - can be found in the `src/index.ts`. - and is triggered by events on Google Cloud Pub/Sub, emitted by Okuna on the `okuna_staging` and `okuna_production` topics, respectively +## Getting Started + +### Local Development + +#### Create Matrix Admin User + +Follow the steps in the below Synapse documentation link on how to create your Matrix Admin User. +- [Synapse Create Admin](https://matrix-org.github.io/synapse/latest/admin_api/register_api.html) + +For the first step when fetching the **nonce** make sure that mprocs is running and that you are using the following Chat Server URL: + +```bash + # ${CHAT_SERVER_URL}/_synapse/admin/v1/register + http://127.0.0.1:8008/_synapse/admin/v1/register +``` + +Once we generate the HMAC as described in the documentation, the **nonce** can be used to do the PUT Request to get the admin user name and access token. + ## Links - [Google Cloud Pub/Sub Topics](https://console.cloud.google.com/cloudpubsub/topic/list?project=holi-shared) diff --git a/src/handlers/space-user-added-handler.ts b/src/handlers/space-user-added-handler.ts index 0a20ae9..c5ef775 100644 --- a/src/handlers/space-user-added-handler.ts +++ b/src/handlers/space-user-added-handler.ts @@ -8,6 +8,6 @@ export const spaceUserAddedHandler = async (messageId: string, payload: SpaceUse await logPhase(messageId, 'spaceUserAddedHandler.findOrCreateOcisUser')(() => inviteUserToSpace(payload)) logger.debug('User added to space successfully', { user: payload.user.id, space: payload.space.id }) } catch (error) { - throw new Error(`Failed to add user to space ${error}`) + throw new Error(`Failed to add user "${payload.user.id}" to space "${payload.space.id}": ${error}`) } } diff --git a/src/handlers/space-user-left-handler.ts b/src/handlers/space-user-left-handler.ts index 25b5f7c..f4defa3 100644 --- a/src/handlers/space-user-left-handler.ts +++ b/src/handlers/space-user-left-handler.ts @@ -1,29 +1,18 @@ +import { startChatClient, stopChatClient } from '../helpers/_chatClient' +import { kickUserFromSpace } from '../helpers/kickUserFromSpace' import { logPhase, logger } from '../logger' import { SpaceUserLeftDataPayload } from '../types' //Handles the SpaceUserLeft event from Google Cloud Pub/Sub. export const spaceUserLeftHandler = async (messageId: string, payload: SpaceUserLeftDataPayload): Promise<void> => { - console.log('++++++++= space_user_left_handler: SPACE USER LEFT', messageId, payload) + await startChatClient() - // try { - // const { user, space } = payload - - // const ocisUser = await logPhase( - // messageId, - // 'spaceUserLeftHandler.findOrCreateOcisUser' - // )(() => findOrCreateOcisUser(user)) - - // const ocisSpace = await logPhase( - // messageId, - // 'spaceUserLeftHandler.findOrCreateOcisSpace' - // )(() => findOrCreateOcisSpace(space)) - - // await logPhase( - // messageId, - // 'spaceUserLeftHandler.removeSpaceUser' - // )(() => removeSpaceUser(ocisSpace.id, ocisUser.onPremisesSamAccountName)) - // logger.debug('User removed from space successfully', { ocisUser, ocisSpace }) - // } catch (error) { - // throw new Error(`Failed to remove user from space ${error}`) - // } + try { + await logPhase(messageId, 'spaceUserLeftHandler.kickUserFromSpace')(() => kickUserFromSpace(payload)) + logger.debug('User removed from space successfully', { user: payload.user.id, space: payload.space.id }) + } catch (error) { + throw new Error(`Failed to remove user "${payload.user.id}" from space "${payload.space.id}": ${error}`) + } finally { + await stopChatClient() + } } diff --git a/src/helpers/kickUserFromSpace.ts b/src/helpers/kickUserFromSpace.ts new file mode 100644 index 0000000..994658a --- /dev/null +++ b/src/helpers/kickUserFromSpace.ts @@ -0,0 +1,31 @@ +import { SpaceUserAddedDataPayload } from '../types' +import { chatClient } from './_chatClient' +import { getMatrixUserId, getRoomIdsForHoliSpace } from './rooms' + +export const kickUserFromSpace = async ({ + user: { identity }, + space: { id: holiSpaceId }, +}: SpaceUserAddedDataPayload) => { + const roomsToDelete = await getRoomIdsForHoliSpace(holiSpaceId, 'general') + const userId = getMatrixUserId(identity) + let count = 12 + + return roomsToDelete.map(async function kickUser(roomId) { + const room = chatClient.getRoom(roomId) + const roomMember = room?.getMember(userId) + + if (count <= 0) return + if (!roomMember) { + // try 12 times every 5000 if user not found + await new Promise((resolve) => + setTimeout(() => { + count-- + resolve(true) + }, 5000) + ) + return await kickUser(roomId) + } + + await chatClient.kick(roomId, userId) + }) +} diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts deleted file mode 100644 index 4a58bb3..0000000 --- a/src/tests/index.test.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { receiveEvent } from '..' -import { spaceCreatedHandler } from '../handlers/space-created-handler' -import { spaceUserAddedHandler } from '../handlers/space-user-added-handler' -import { spaceUserLeftHandler } from '../handlers/space-user-left-handler' -import { spaceUserRemovedHandler } from '../handlers/space-user-removed-handler' -import { userNameUpdatedHandler } from '../handlers/user-name-updated-handler' - -jest.mock('../handlers/space-created-handler') -jest.mock('../handlers/space-user-added-handler') -jest.mock('../handlers/user-name-updated-handler') -jest.mock('../handlers/space-user-left-handler') -jest.mock('../handlers/space-user-removed-handler') - -const MESSAGE_ID = '42' - -const encodeBase64 = (payload) => { - return Buffer.from(JSON.stringify(payload)) -} - -describe('receiveEvent', () => { - const spaceCreatedEventPayload = { - creator: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - space: { - id: 'space-id', - name: 'Test Space', - slug: 'test-space', - }, - } - - const userNameUpdatedEventPayload = { - user: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - } - - const spaceUserAddedEventPayload = { - user: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - space: { - id: 'space-id', - name: 'Test Space', - slug: 'test-space', - }, - } - - const spaceUserLeftEventPayload = { - user: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - space: { - id: 'space-id', - name: 'Test Space', - slug: 'test-space', - }, - } - - const spaceUserRemovedEventPayload = { - user: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - space: { - id: 'space-id', - name: 'Test Space', - slug: 'test-space', - }, - } - - const unsupportedEventPayload = { - user: { - id: 'test-user-id', - name: 'Test User', - email: 'test-email@example.com', - }, - } - - let spaceCreatedHandlerMock: jest.MockedFunction<typeof spaceCreatedHandler> - let spaceUserAddedHandlerMock: jest.MockedFunction<typeof spaceUserAddedHandler> - let userNameUpdatedHandlerMock: jest.MockedFunction<typeof userNameUpdatedHandler> - let spaceUserLeftHandlerMock: jest.MockedFunction<typeof spaceUserLeftHandler> - let spaceUserRemovedHandlerMock: jest.MockedFunction<typeof spaceUserRemovedHandler> - - beforeEach(() => { - spaceCreatedHandlerMock = spaceCreatedHandler as jest.MockedFunction<typeof spaceCreatedHandler> - spaceUserAddedHandlerMock = spaceUserAddedHandler as jest.MockedFunction<typeof spaceUserAddedHandler> - userNameUpdatedHandlerMock = userNameUpdatedHandler as jest.MockedFunction<typeof userNameUpdatedHandler> - spaceUserLeftHandlerMock = spaceUserLeftHandler as jest.MockedFunction<typeof spaceUserLeftHandler> - spaceUserRemovedHandlerMock = spaceUserRemovedHandler as jest.MockedFunction<typeof spaceUserRemovedHandler> - jest.resetModules() - }) - - afterEach(() => { - jest.resetAllMocks() - }) - - it('should call the spaceCreatedHandler when the event type is SpaceCreated', async () => { - const requestMock = { - body: { - message: { - attributes: { eventType: 'SpaceCreated', eventVersion: '1.0.0' }, - data: encodeBase64(spaceCreatedEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - spaceCreatedHandlerMock.mockResolvedValue() - - await receiveEvent(requestMock, responseMock) - - expect(spaceCreatedHandlerMock).toHaveBeenCalledWith(MESSAGE_ID, spaceCreatedEventPayload) - expect(responseMock.sendStatus).toHaveBeenCalledWith(201) - }) - - it('should call the spaceUserAddedHandler when the event type is SpaceUserAdded', async () => { - const requestMock = { - body: { - message: { - attributes: { eventType: 'SpaceUserAdded', eventVersion: '1.0.0' }, - data: encodeBase64(spaceUserAddedEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - spaceUserAddedHandlerMock.mockResolvedValue() - - await receiveEvent(requestMock, responseMock) - - expect(spaceUserAddedHandlerMock).toHaveBeenCalledWith(MESSAGE_ID, spaceUserAddedEventPayload) - expect(responseMock.sendStatus).toHaveBeenCalledWith(200) - }) - - it('should call the userNameUpdatedHandler when the event type is UserNameUpdated', async () => { - const requestMock = { - body: { - message: { - attributes: { eventType: 'UserNameUpdated', eventVersion: '1.0.0' }, - data: encodeBase64(userNameUpdatedEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - userNameUpdatedHandlerMock.mockResolvedValue() - - await receiveEvent(requestMock, responseMock) - - expect(userNameUpdatedHandlerMock).toHaveBeenCalledWith(MESSAGE_ID, userNameUpdatedEventPayload) - expect(responseMock.sendStatus).toHaveBeenCalledWith(200) - }) - - it('should throw an error when the event type is unexpected', async () => { - const unsupportedEvent = 'UnsupportedEvent' - - const requestMock = { - body: { - message: { - attributes: { eventType: unsupportedEvent, eventVersion: '1.0.0' }, - data: encodeBase64(unsupportedEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - await expect(receiveEvent(requestMock, responseMock)).rejects.toThrow( - `Received unexpected event of type ${unsupportedEvent}` - ) - expect(responseMock.sendStatus).toHaveBeenCalledWith(500) - expect(spaceCreatedHandlerMock).not.toHaveBeenCalled() - expect(spaceUserAddedHandlerMock).not.toHaveBeenCalled() - expect(userNameUpdatedHandlerMock).not.toHaveBeenCalled() - }) - - it('should call the spaceUserLeftHandler when the event type is SpaceUserLeft', async () => { - const requestMock = { - body: { - message: { - attributes: { eventType: 'SpaceUserLeft', eventVersion: '1.0.0' }, - data: encodeBase64(spaceUserLeftEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - spaceUserLeftHandlerMock.mockResolvedValue() - - await receiveEvent(requestMock, responseMock) - - expect(spaceUserLeftHandlerMock).toHaveBeenCalledWith(MESSAGE_ID, spaceUserLeftEventPayload) - expect(responseMock.sendStatus).toHaveBeenCalledWith(200) - }) - - it('should call the spaceUserRemovedHandler when the event type is SpaceUserRemoved', async () => { - const requestMock = { - body: { - message: { - attributes: { eventType: 'SpaceUserRemoved', eventVersion: '1.0.0' }, - data: encodeBase64(spaceUserRemovedEventPayload), - messageId: MESSAGE_ID, - }, - }, - } - - const responseMock = { - sendStatus: jest.fn(), - } - - spaceUserRemovedHandlerMock.mockResolvedValue() - - await receiveEvent(requestMock, responseMock) - - expect(spaceUserRemovedHandlerMock).toHaveBeenCalledWith(MESSAGE_ID, spaceUserRemovedEventPayload) - expect(responseMock.sendStatus).toHaveBeenCalledWith(200) - }) -}) -- GitLab