From 811b608798bb0321b705a5a2e24d4eb57f858d5b Mon Sep 17 00:00:00 2001 From: Chris Pyle Date: Fri, 20 Dec 2024 16:23:00 -0500 Subject: [PATCH] #2823 slack id to organization schema change, service funcs, and slack router addition --- src/backend/src/integrations/slack.ts | 19 +++++++++++ .../migration.sql | 3 +- src/backend/src/prisma/schema.prisma | 1 + src/backend/src/routes/slack.routes.ts | 10 ++++-- .../src/services/organizations.services.ts | 34 +++++++++++++++++++ src/backend/tests/mocked/organization.test.ts | 32 +++++++++++++++++ 6 files changed, 96 insertions(+), 3 deletions(-) rename src/backend/src/prisma/migrations/{20241220204035_homepage_updates => 20241220204512_home_page_updates}/migration.sql (95%) diff --git a/src/backend/src/integrations/slack.ts b/src/backend/src/integrations/slack.ts index d2440e6288..7c80fa819a 100644 --- a/src/backend/src/integrations/slack.ts +++ b/src/backend/src/integrations/slack.ts @@ -220,4 +220,23 @@ export const getUserName = async (userId: string) => { } }; +/** + * Get the workspace id of the workspace this slack api is registered with + * @returns the id of the workspace + */ +export const getWorkspaceId = async () => { + const { SLACK_BOT_TOKEN } = process.env; + if (!SLACK_BOT_TOKEN) return; + + try { + const response = await slack.auth.test(); + if (response.ok) { + return response.team_id; + } + throw new Error(response.error); + } catch (error) { + throw new HttpException(500, 'Error getting slack workspace id: ' + (error as any).data.error); + } +}; + export default slack; diff --git a/src/backend/src/prisma/migrations/20241220204035_homepage_updates/migration.sql b/src/backend/src/prisma/migrations/20241220204512_home_page_updates/migration.sql similarity index 95% rename from src/backend/src/prisma/migrations/20241220204035_homepage_updates/migration.sql rename to src/backend/src/prisma/migrations/20241220204512_home_page_updates/migration.sql index 37c468e58e..e20892547e 100644 --- a/src/backend/src/prisma/migrations/20241220204035_homepage_updates/migration.sql +++ b/src/backend/src/prisma/migrations/20241220204512_home_page_updates/migration.sql @@ -1,5 +1,6 @@ -- AlterTable -ALTER TABLE "Organization" ADD COLUMN "logoImageId" TEXT; +ALTER TABLE "Organization" ADD COLUMN "logoImageId" TEXT, +ADD COLUMN "slackWorkspaceId" TEXT; -- AlterTable ALTER TABLE "Project" ADD COLUMN "organizationId" TEXT; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index bb8247266d..3068c9558f 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -877,6 +877,7 @@ model Organization { applyInterestImageId String? @unique exploreAsGuestImageId String? @unique logoImageId String? + slackWorkspaceId String? // Relation references wbsElements WBS_Element[] diff --git a/src/backend/src/routes/slack.routes.ts b/src/backend/src/routes/slack.routes.ts index a938ac0691..82b98b0a0b 100644 --- a/src/backend/src/routes/slack.routes.ts +++ b/src/backend/src/routes/slack.routes.ts @@ -1,11 +1,17 @@ import { createEventAdapter } from '@slack/events-api'; import slackServices from '../services/slack.services'; +import OrganizationsService from '../services/organizations.services'; +import { getWorkspaceId } from '../integrations/slack'; export const slackEvents = createEventAdapter(process.env.SLACK_SIGNING_SECRET || ''); slackEvents.on('message', async (event) => { - console.log('EVENT:', event); - slackServices.processMessageSent(event, process.env.DEV_ORGANIZATION_ID ?? ''); + const organizations = await OrganizationsService.getAllOrganizations(); + const nerSlackWorkspaceId = await getWorkspaceId(); + const orgId = organizations.find((org) => org.slackWorkspaceId === nerSlackWorkspaceId)?.organizationId; + if (orgId) { + slackServices.processMessageSent(event, orgId); + } }); slackEvents.on('error', (error) => { diff --git a/src/backend/src/services/organizations.services.ts b/src/backend/src/services/organizations.services.ts index c4ce91668b..acd6f24865 100644 --- a/src/backend/src/services/organizations.services.ts +++ b/src/backend/src/services/organizations.services.ts @@ -12,6 +12,14 @@ import { getProjectQueryArgs } from '../prisma-query-args/projects.query-args'; import projectTransformer from '../transformers/projects.transformer'; export default class OrganizationsService { + /** + * Retrieve all the organizations + * @returns an array of every organization + */ + static async getAllOrganizations(): Promise { + return prisma.organization.findMany(); + } + /** * Gets the current organization * @param organizationId the organizationId to be fetched @@ -275,4 +283,30 @@ export default class OrganizationsService { return organization.featuredProjects.map(projectTransformer); } + + /** + * Sets the slack workspace id used to initialize slack bots for this organization + * @param slackWorkspaceId the id of the organization's slack workspace + * @param submitter the user making this submission (must be an admin) + * @param organization the organization being changed + * @returns the changed organization + */ + static async setOrganizationSlackWorkspaceId( + slackWorkspaceId: string, + submitter: User, + organization: Organization + ): Promise { + if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { + throw new AccessDeniedAdminOnlyException('set slack workspace id'); + } + const updatedOrg = prisma.organization.update({ + where: { + organizationId: organization.organizationId + }, + data: { + slackWorkspaceId + } + }); + return updatedOrg; + } } diff --git a/src/backend/tests/mocked/organization.test.ts b/src/backend/tests/mocked/organization.test.ts index 2c0affdff9..295b96031d 100644 --- a/src/backend/tests/mocked/organization.test.ts +++ b/src/backend/tests/mocked/organization.test.ts @@ -325,4 +325,36 @@ describe('Organization Tests', () => { expect(oldOrganization?.description).toBe(returnedOrganization.description); }); }); + + describe('Set Organization slack id', () => { + it('Fails if user is not an admin', async () => { + await expect( + OrganizationsService.setOrganizationSlackWorkspaceId( + 'test slack id', + await createTestUser(wonderwomanGuest, orgId), + organization + ) + ).rejects.toThrow(new AccessDeniedAdminOnlyException('set slack workspace id')); + }); + + it('Succeeds and updates the slack id', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + + const returnedOrganization = await OrganizationsService.setOrganizationSlackWorkspaceId( + 'sample slack id', + testBatman, + organization + ); + + const oldOrganization = await prisma.organization.findUnique({ + where: { + organizationId: orgId + } + }); + + expect(oldOrganization).not.toBeNull(); + expect(oldOrganization?.slackWorkspaceId).toBe('sample slack id'); + expect(oldOrganization?.slackWorkspaceId).toBe(returnedOrganization.slackWorkspaceId); + }); + }); });