From 872f49b762d5f73f6200d60a8efb5e3efe5f2fcc Mon Sep 17 00:00:00 2001 From: Ellie Re'em Date: Tue, 11 Jun 2024 14:37:17 +0100 Subject: [PATCH 1/5] refactor: simplybook webhook signature handling --- src/main.ts | 2 +- src/utils/constants.ts | 5 +++ src/webhooks/webhooks.controller.spec.ts | 43 +++++++++++++++++- src/webhooks/webhooks.controller.ts | 32 +++++++++++++- src/webhooks/webhooks.service.spec.ts | 56 +++--------------------- src/webhooks/webhooks.service.ts | 23 +--------- test/utils/mockData.ts | 18 ++++++++ 7 files changed, 104 insertions(+), 75 deletions(-) diff --git a/src/main.ts b/src/main.ts index 874c3a66..98f7233f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,7 @@ import { ExceptionsFilter } from './utils/exceptions.filter'; async function bootstrap() { const PORT = process.env.PORT || 35001; - const app = await NestFactory.create(AppModule, { cors: true }); + const app = await NestFactory.create(AppModule, { cors: true, rawBody: true }); app.setGlobalPrefix('api'); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d08ef46a..e0500ba4 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -111,6 +111,11 @@ export const slackWebhookUrl = getEnv(process.env.SLACK_WEBHOOK_URL, 'SLACK_WEBH export const storyblokToken = getEnv(process.env.STORYBLOK_PUBLIC_TOKEN, 'STORYBLOK_PUBLIC_TOKEN'); +export const storyblokWebhookSecret = getEnv( + process.env.STORYBLOK_WEBHOOK_SECRET, + 'STORYBLOK_WEBHOOK_SECRET', +); + export const simplybookCredentials = getEnv( process.env.SIMPLYBOOK_CREDENTIALS, 'SIMPLYBOOK_CREDENTIALS', diff --git a/src/webhooks/webhooks.controller.spec.ts b/src/webhooks/webhooks.controller.spec.ts index 2428690f..0c88ee12 100644 --- a/src/webhooks/webhooks.controller.spec.ts +++ b/src/webhooks/webhooks.controller.spec.ts @@ -1,11 +1,38 @@ import { createMock } from '@golevelup/ts-jest'; import { HttpException, HttpStatus } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { mockSimplybookBodyBase, mockTherapySessionEntity } from 'test/utils/mockData'; +import { createHmac } from 'crypto'; +import { storyblokWebhookSecret } from 'src/utils/constants'; +import { + mockSessionEntity, + mockSimplybookBodyBase, + mockStoryDto, + mockTherapySessionEntity, +} from 'test/utils/mockData'; import { mockWebhooksServiceMethods } from 'test/utils/mockedServices'; import { WebhooksController } from './webhooks.controller'; import { WebhooksService } from './webhooks.service'; +const getWebhookSignature = (body) => { + return createHmac('sha1', storyblokWebhookSecret) + .update('' + body) + .digest('hex'); +}; + +const generateMockHeaders = (body) => { + return { + 'webhook-signature': getWebhookSignature(body), + }; +}; + +const createRequestObject = (body) => { + return { + rawBody: JSON.stringify(body), + setEncoding: () => {}, + encoding: 'utf8', + }; +}; + describe('AppController', () => { let webhooksController: WebhooksController; const mockWebhooksService = createMock(mockWebhooksServiceMethods); @@ -35,5 +62,19 @@ describe('AppController', () => { webhooksController.updatePartnerAccessTherapy(mockSimplybookBodyBase), ).rejects.toThrow('Therapy session not found'); }); + describe('updateStory', () => { + it('updateStory should pass if service returns true', async () => { + jest.spyOn(mockWebhooksService, 'updateStory').mockImplementationOnce(async () => { + return mockSessionEntity; + }); + await expect( + webhooksController.updateStory( + createRequestObject(mockStoryDto), + mockStoryDto, + generateMockHeaders(mockStoryDto), + ), + ).resolves.toBe(mockSessionEntity); + }); + }); }); }); diff --git a/src/webhooks/webhooks.controller.ts b/src/webhooks/webhooks.controller.ts index 72ada51d..b6e33cf2 100644 --- a/src/webhooks/webhooks.controller.ts +++ b/src/webhooks/webhooks.controller.ts @@ -1,7 +1,19 @@ -import { Body, Controller, Headers, Logger, Post, Request, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Headers, + HttpException, + HttpStatus, + Logger, + Post, + Request, + UseGuards, +} from '@nestjs/common'; import { ApiBody, ApiTags } from '@nestjs/swagger'; +import { createHmac } from 'crypto'; import { EventLogEntity } from 'src/entities/event-log.entity'; import { TherapySessionEntity } from 'src/entities/therapy-session.entity'; +import { storyblokWebhookSecret } from 'src/utils/constants'; import { ControllerDecorator } from 'src/utils/controller.decorator'; import { WebhookCreateEventLogDto } from 'src/webhooks/dto/webhook-create-event-log.dto'; import { ZapierSimplybookBodyDto } from '../partner-access/dtos/zapier-body.dto'; @@ -36,6 +48,22 @@ export class WebhooksController { @ApiBody({ type: StoryDto }) async updateStory(@Request() req, @Body() data: StoryDto, @Headers() headers) { const signature: string | undefined = headers['webhook-signature']; - return this.webhooksService.updateStory(req, data, signature); + // Verify storyblok signature uses storyblok webhook secret - see https://www.storyblok.com/docs/guide/in-depth/webhooks#securing-a-webhook + if (!signature) { + const error = `Storyblok webhook error - no signature provided`; + this.logger.error(error); + throw new HttpException(error, HttpStatus.UNAUTHORIZED); + } + + req.rawBody = '' + data; + req.setEncoding('utf8'); + + const bodyHmac = createHmac('sha1', storyblokWebhookSecret).update(req.rawBody).digest('hex'); + if (bodyHmac !== signature) { + const error = `Storyblok webhook error - signature mismatch`; + this.logger.error(error); + throw new HttpException(error, HttpStatus.UNAUTHORIZED); + } + return this.webhooksService.updateStory(data); } } diff --git a/src/webhooks/webhooks.service.spec.ts b/src/webhooks/webhooks.service.spec.ts index 5555ef35..16c50902 100644 --- a/src/webhooks/webhooks.service.spec.ts +++ b/src/webhooks/webhooks.service.spec.ts @@ -1,7 +1,6 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { createHmac } from 'crypto'; import { SlackMessageClient } from 'src/api/slack/slack-api'; import { CoursePartnerService } from 'src/course-partner/course-partner.service'; import { CoursePartnerEntity } from 'src/entities/course-partner.entity'; @@ -47,21 +46,6 @@ import { ILike, Repository } from 'typeorm'; import { WebhookCreateEventLogDto } from './dto/webhook-create-event-log.dto'; import { WebhooksService } from './webhooks.service'; -const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET; - -const getWebhookSignature = (body) => { - return createHmac('sha1', webhookSecret) - .update('' + body) - .digest('hex'); -}; -const createRequestObject = (body) => { - return { - rawBody: '' + body, - setEncoding: () => {}, - encoding: 'utf8', - }; -}; - // Difficult to mock classes as well as node modules. // This seemed the best approach jest.mock('storyblok-js-client', () => { @@ -202,9 +186,7 @@ describe('WebhooksService', () => { text: '', }; - return expect( - service.updateStory(createRequestObject(body), body, getWebhookSignature(body)), - ).rejects.toThrow('STORYBLOK STORY NOT FOUND'); + return expect(service.updateStory(body)).rejects.toThrow('STORYBLOK STORY NOT FOUND'); }); it('when action is deleted, story should be set as deleted in database', async () => { @@ -214,11 +196,7 @@ describe('WebhooksService', () => { text: '', }; - const deletedStory = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as SessionEntity; + const deletedStory = (await service.updateStory(body)) as SessionEntity; expect(deletedStory.status).toBe(STORYBLOK_STORY_STATUS_ENUM.DELETED); }); @@ -230,11 +208,7 @@ describe('WebhooksService', () => { text: '', }; - const unpublished = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as SessionEntity; + const unpublished = (await service.updateStory(body)) as SessionEntity; expect(unpublished.status).toBe(STORYBLOK_STORY_STATUS_ENUM.UNPUBLISHED); }); @@ -282,11 +256,7 @@ describe('WebhooksService', () => { text: '', }; - const session = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as SessionEntity; + const session = (await service.updateStory(body)) as SessionEntity; expect(courseFindOneSpy).toHaveBeenCalledWith({ storyblokUuid: 'anotherCourseUuId', @@ -329,11 +299,7 @@ describe('WebhooksService', () => { text: '', }; - const session = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as SessionEntity; + const session = (await service.updateStory(body)) as SessionEntity; expect(session).toEqual(mockSession); expect(courseFindOneSpy).toHaveBeenCalledWith({ @@ -392,11 +358,7 @@ describe('WebhooksService', () => { text: '', }; - const session = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as SessionEntity; + const session = (await service.updateStory(body)) as SessionEntity; expect(session).toEqual(mockSession); expect(sessionSaveRepoSpy).toHaveBeenCalledWith({ @@ -430,11 +392,7 @@ describe('WebhooksService', () => { text: '', }; - const course = (await service.updateStory( - createRequestObject(body), - body, - getWebhookSignature(body), - )) as CourseEntity; + const course = (await service.updateStory(body)) as CourseEntity; expect(course).toEqual(mockCourse); expect(courseFindOneRepoSpy).toHaveBeenCalledWith({ diff --git a/src/webhooks/webhooks.service.ts b/src/webhooks/webhooks.service.ts index 3bf83d53..4090e0a0 100644 --- a/src/webhooks/webhooks.service.ts +++ b/src/webhooks/webhooks.service.ts @@ -1,6 +1,5 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { createHmac } from 'crypto'; import { SlackMessageClient } from 'src/api/slack/slack-api'; import { CourseEntity } from 'src/entities/course.entity'; import { EventLogEntity } from 'src/entities/event-log.entity'; @@ -350,27 +349,7 @@ export class WebhooksService { } } - async updateStory(req, data: StoryDto, signature: string | undefined) { - // Verify storyblok signature uses storyblok webhook secret - see https://www.storyblok.com/docs/guide/in-depth/webhooks#securing-a-webhook - if (!signature) { - const error = `Storyblok webhook error - no signature provided`; - this.logger.error(error); - throw new HttpException(error, HttpStatus.UNAUTHORIZED); - } - - const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET; - - req.rawBody = '' + data; - req.setEncoding('utf8'); - - const bodyHmac = createHmac('sha1', webhookSecret).update(req.rawBody).digest('hex'); - - if (bodyHmac !== signature) { - const error = `Storyblok webhook error - signature mismatch`; - this.logger.error(error); - throw new HttpException(error, HttpStatus.UNAUTHORIZED); - } - + async updateStory(data: StoryDto) { const action = data.action; const story_id = data.story_id; diff --git a/test/utils/mockData.ts b/test/utils/mockData.ts index 6a0f34e7..258e57bc 100644 --- a/test/utils/mockData.ts +++ b/test/utils/mockData.ts @@ -384,3 +384,21 @@ export const mockSubscriptionUserEntity = { userId: mockUserEntity.id, subscription: mockSubscriptionEntity, } as SubscriptionUserEntity; + +export const mockStoryDto = { + text: 'string', + action: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED, + story_id: 1, + space_id: 123, + full_slug: 'course slug', +}; + +export const mockSessionEntity = { + id: 'sid', + name: 'session name', + slug: 'session_name', + status: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED, + storyblokId: 123, + storyblokUuid: '1234', + courseId: '12345', +} as SessionEntity; From cd768e1ae594641267dc51b1bbf3d568e4f10ea9 Mon Sep 17 00:00:00 2001 From: kyleecodes Date: Wed, 12 Jun 2024 19:27:49 -0400 Subject: [PATCH 2/5] Actions: enable manual stale issue management --- .github/workflows/community-issue-comment.yml | 25 +++++++++++++++++-- .../workflows/community-stale-management.yml | 1 - 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/community-issue-comment.yml b/.github/workflows/community-issue-comment.yml index 05ccf99d..139e0f24 100644 --- a/.github/workflows/community-issue-comment.yml +++ b/.github/workflows/community-issue-comment.yml @@ -1,6 +1,7 @@ # This workflow handles issue comments. +# See for more info: https://github.com/actions/github-script -name: Issue Assignee Comment +name: Issue Comments on: issues: @@ -11,8 +12,8 @@ on: jobs: # When issues are assigned, a comment is posted # Tags the assignee with links to helpful resources - # See for more info: https://github.com/actions/github-script assigned-comment: + if: github.event.action == 'assigned' runs-on: ubuntu-latest steps: - name: Post assignee issue comment @@ -33,3 +34,23 @@ jobs: Support Chayn's mission? ⭐ Please star this repo to help us find more contributors like you! Learn more about Chayn [here](https://linktr.ee/chayn) and [explore our projects](https://org.chayn.co/projects). 🌸` }) + + # When issues are labeled as stale, a comment is posted. + # Tags the assignee with warning. + # Enables manual issue management in addition to community-stale-management.yml + stale-label-comment: + if: github.event.action == 'labeled' && github.event.label.name == 'stale' + runs-on: ubuntu-latest + steps: + - name: Post stale issue comment + id: stale-label-comment + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: `@${context.payload.issue.assignee.login} As per Chayn policy, after 30 days of inactivity, we will be unassigning this issue. Please comment to stay assigned.` + }) diff --git a/.github/workflows/community-stale-management.yml b/.github/workflows/community-stale-management.yml index 8408ef6d..0fa63dcd 100644 --- a/.github/workflows/community-stale-management.yml +++ b/.github/workflows/community-stale-management.yml @@ -41,4 +41,3 @@ jobs: ignore-pr-updates: true stale-pr-message: "As per Chayn policy, after 30 days of inactivity, we will close this PR." close-pr-message: "This PR has been closed due to inactivity." - stale-issue-message: "As per Chayn policy, after 30 days of inactivity, we will be unassigning this issue. Please comment to stay assigned." From 053bace0b14bd483b2a0d7966854824fd28aedcc Mon Sep 17 00:00:00 2001 From: SangilYun <35569652+SangilYun@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:09:19 +0900 Subject: [PATCH 3/5] add logic to handle IS NOT NULL (#452) Co-authored-by: Kylee Fields <43586156+kyleecodes@users.noreply.github.com> --- src/user/user.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index cc1864a9..1a00a4b8 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -13,7 +13,7 @@ import { createServiceUserProfiles, updateServiceUserProfilesUser, } from 'src/utils/serviceUserProfiles'; -import { And, ILike, Raw, Repository } from 'typeorm'; +import { And, ILike, Raw, Repository, IsNull, Not } from 'typeorm'; import { deleteCypressCrispProfiles } from '../api/crisp/crisp-api'; import { AuthService } from '../auth/auth.service'; import { PartnerAccessService, basePartnerAccess } from '../partner-access/partner-access.service'; @@ -289,7 +289,11 @@ export class UserService { }), ...(filters.partnerAdmin && { partnerAdmin: { - ...(filters.partnerAdmin && { id: filters.partnerAdmin.partnerAdminId }), + ...(filters.partnerAdmin && { + id: filters.partnerAdmin.partnerAdminId === 'IS NOT NULL' + ? Not(IsNull()) + : filters.partnerAdmin.partnerAdminId + }), }, }), }, From 1830240ba5ed29a1678ae5fc629335fe0ebab0ff Mon Sep 17 00:00:00 2001 From: Anna Hughes Date: Thu, 13 Jun 2024 16:46:24 +0100 Subject: [PATCH 4/5] fix: create firebase user errors (#473) --- src/auth/auth.service.ts | 20 +++++++++----------- src/user/user.service.ts | 1 + src/utils/errors.ts | 1 + 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 015623c1..e161f820 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -8,6 +8,7 @@ import { import { DecodedIdToken } from 'firebase-admin/lib/auth/token-verifier'; import { Logger } from 'src/logger/logger'; import { + CREATE_USER_ALREADY_EXISTS, CREATE_USER_FIREBASE_ERROR, CREATE_USER_INVALID_EMAIL, CREATE_USER_WEAK_PASSWORD, @@ -55,24 +56,21 @@ export class AuthService { return firebaseUser; } catch (err) { const errorCode = err.code; + if (errorCode === 'auth/invalid-email') { this.logger.warn( `Create user: user tried to create email with invalid email: ${email} - ${err}`, ); throw new HttpException(CREATE_USER_INVALID_EMAIL, HttpStatus.BAD_REQUEST); - } - if ( - errorCode === 'auth/weak-password' || - err.message.includes('The password must be a string with at least 6 characters') - ) { + } else if (errorCode === 'auth/weak-password' || errorCode === 'auth/invalid-password') { this.logger.warn(`Create user: user tried to create email with weak password - ${err}`); throw new HttpException(CREATE_USER_WEAK_PASSWORD, HttpStatus.BAD_REQUEST); - } - if (errorCode === 'auth/email-already-in-use' && errorCode === 'auth/email-already-exists') { - this.logger.log( - `Create user: Firebase user already exists so fetching firebase user: ${email}`, - ); - return await this.getFirebaseUser(email); + } else if ( + errorCode === 'auth/email-already-in-use' || + errorCode === 'auth/email-already-exists' + ) { + this.logger.warn(`Create user: Firebase user already exists: ${email}`); + throw new HttpException(CREATE_USER_ALREADY_EXISTS, HttpStatus.BAD_REQUEST); } else { this.logger.error(`Create user: Error creating firebase user - ${email}: ${err}`); throw new HttpException(CREATE_USER_FIREBASE_ERROR, HttpStatus.BAD_REQUEST); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 1a00a4b8..1175eec9 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -63,6 +63,7 @@ export class UserService { } const firebaseUser = await this.authService.createFirebaseUser(email, password); + const user = await this.userRepository.save({ ...createUserDto, firebaseUid: firebaseUser.uid, diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 50da069e..42a8a7c6 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,3 +1,4 @@ export const CREATE_USER_FIREBASE_ERROR = 'CREATE_USER_FIREBASE_ERROR'; export const CREATE_USER_INVALID_EMAIL = 'CREATE_USER_INVALID_EMAIL'; export const CREATE_USER_WEAK_PASSWORD = 'CREATE_USER_WEAK_PASSWORD'; +export const CREATE_USER_ALREADY_EXISTS = 'CREATE_USER_ALREADY_EXISTS'; From 01a90b38a82a8a8af8acbc5b577c2803035f8284 Mon Sep 17 00:00:00 2001 From: Anna Hughes Date: Thu, 13 Jun 2024 18:57:15 +0100 Subject: [PATCH 5/5] feat: event logger controller (#474) --- src/app.module.ts | 2 ++ src/event-logger/event-logger.controller.ts | 28 +++++++++++++++ src/event-logger/event-logger.interface.ts | 2 ++ src/event-logger/event-logger.module.ts | 40 +++++++++++++++++++-- 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/event-logger/event-logger.controller.ts diff --git a/src/app.module.ts b/src/app.module.ts index 8101f433..bceadd8e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,7 @@ import { AuthModule } from './auth/auth.module'; import { CoursePartnerModule } from './course-partner/course-partner.module'; import { CourseUserModule } from './course-user/course-user.module'; import { CourseModule } from './course/course.module'; +import { EventLoggerModule } from './event-logger/event-logger.module'; import { FeatureModule } from './feature/feature.module'; import { LoggerModule } from './logger/logger.module'; import { PartnerAccessModule } from './partner-access/partner-access.module'; @@ -37,6 +38,7 @@ import { WebhooksModule } from './webhooks/webhooks.module'; SubscriptionUserModule, FeatureModule, PartnerFeatureModule, + EventLoggerModule, ], }) export class AppModule {} diff --git a/src/event-logger/event-logger.controller.ts b/src/event-logger/event-logger.controller.ts new file mode 100644 index 00000000..89d9c0ab --- /dev/null +++ b/src/event-logger/event-logger.controller.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { FirebaseAuthGuard } from 'src/firebase/firebase-auth.guard'; +import { ControllerDecorator } from 'src/utils/controller.decorator'; +import { EVENT_NAME } from './event-logger.interface'; +import { EventLoggerService } from './event-logger.service'; + +@ApiTags('Event Logger') +@ControllerDecorator() +@Controller('/v1/event-logger') +export class EventLoggerController { + constructor(private readonly eventLoggerService: EventLoggerService) {} + + @Post() + @ApiOperation({ + description: 'Creates an event log', + }) + @ApiBearerAuth('access-token') + @UseGuards(FirebaseAuthGuard) + async createEventLog(@Req() req: Request, @Body() { event }: { event: EVENT_NAME }) { + const now = new Date(); + return await this.eventLoggerService.createEventLog({ + userId: req['userEntity'].id, + event, + date: now, + }); + } +} diff --git a/src/event-logger/event-logger.interface.ts b/src/event-logger/event-logger.interface.ts index 875f954f..18c22834 100644 --- a/src/event-logger/event-logger.interface.ts +++ b/src/event-logger/event-logger.interface.ts @@ -1,5 +1,7 @@ export enum EVENT_NAME { CHAT_MESSAGE_SENT = 'CHAT_MESSAGE_SENT', + LOGGED_IN = 'LOGGED_IN', + LOGGED_OUT = 'LOGGED_OUT', } export interface ICreateEventLog { diff --git a/src/event-logger/event-logger.module.ts b/src/event-logger/event-logger.module.ts index ae093b77..f0ff9a34 100644 --- a/src/event-logger/event-logger.module.ts +++ b/src/event-logger/event-logger.module.ts @@ -1,10 +1,44 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { SlackMessageClient } from 'src/api/slack/slack-api'; +import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client'; import { EventLogEntity } from 'src/entities/event-log.entity'; +import { PartnerAccessEntity } from 'src/entities/partner-access.entity'; +import { PartnerEntity } from 'src/entities/partner.entity'; +import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity'; +import { SubscriptionEntity } from 'src/entities/subscription.entity'; +import { TherapySessionEntity } from 'src/entities/therapy-session.entity'; +import { UserEntity } from 'src/entities/user.entity'; +import { PartnerAccessService } from 'src/partner-access/partner-access.service'; +import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service'; +import { SubscriptionService } from 'src/subscription/subscription.service'; +import { TherapySessionService } from 'src/therapy-session/therapy-session.service'; +import { UserService } from 'src/user/user.service'; +import { EventLoggerController } from './event-logger.controller'; import { EventLoggerService } from './event-logger.service'; @Module({ - imports: [TypeOrmModule.forFeature([EventLogEntity])], - providers: [EventLoggerService], + imports: [ + TypeOrmModule.forFeature([ + EventLogEntity, + UserEntity, + PartnerAccessEntity, + PartnerEntity, + SubscriptionUserEntity, + TherapySessionEntity, + SubscriptionEntity, + ]), + ], + controllers: [EventLoggerController], + providers: [ + EventLoggerService, + UserService, + SubscriptionUserService, + TherapySessionService, + PartnerAccessService, + SubscriptionService, + ZapierWebhookClient, + SlackMessageClient, + ], }) -export class SessionModule {} +export class EventLoggerModule {}