From d3ed304538c1b6fc559961daeccafa46922629e0 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Wed, 29 Jan 2025 17:14:56 +0100 Subject: [PATCH 1/9] feat(api-service): subscriber preferences WIP --- .../subscribers-v2/subscriber.controller.ts | 26 ++++++- .../app/subscribers-v2/subscriber.module.ts | 23 +++++- .../get-subscriber-preferences.command.ts | 3 + .../get-subscriber-preferences.usecase.ts | 74 +++++++++++++++++++ .../get-subscriber-preferences.dto.ts | 20 +++++ packages/shared/src/dto/subscriber/index.ts | 1 + .../subscriber-preference.interface.ts | 27 ++----- 7 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.command.ts create mode 100644 apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts create mode 100644 packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts diff --git a/apps/api/src/app/subscribers-v2/subscriber.controller.ts b/apps/api/src/app/subscribers-v2/subscriber.controller.ts index 4a3f977a998..a51a4f55fb8 100644 --- a/apps/api/src/app/subscribers-v2/subscriber.controller.ts +++ b/apps/api/src/app/subscribers-v2/subscriber.controller.ts @@ -12,6 +12,7 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ExternalApiAccessible, UserSession } from '@novu/application-generic'; import { DirectionEnum, + IGetPreferencesResponseDto, IGetSubscriberResponseDto, IListSubscribersRequestDto, IListSubscribersResponseDto, @@ -26,6 +27,8 @@ import { GetSubscriber } from './usecases/get-subscriber/get-subscriber.usecase' import { GetSubscriberCommand } from './usecases/get-subscriber/get-subscriber.command'; import { PatchSubscriber } from './usecases/patch-subscriber/patch-subscriber.usecase'; import { PatchSubscriberCommand } from './usecases/patch-subscriber/patch-subscriber.command'; +import { GetSubscriberPreferences } from './usecases/get-subscriber-preferences/get-subscriber-preferences.usecase'; +import { GetSubscriberPreferencesCommand } from './usecases/get-subscriber-preferences/get-subscriber-preferences.command'; @Controller({ path: '/subscribers', version: '2' }) @UseInterceptors(ClassSerializerInterceptor) @@ -35,7 +38,8 @@ export class SubscriberController { constructor( private listSubscribersUsecase: ListSubscribersUseCase, private getSubscriberUsecase: GetSubscriber, - private patchSubscriberUsecase: PatchSubscriber + private patchSubscriberUsecase: PatchSubscriber, + private getSubscriberPreferencesUsecase: GetSubscriberPreferences ) {} @Get('') @@ -101,4 +105,24 @@ export class SubscriberController { }) ); } + + @Get('/:subscriberId/preferences') + @UserAuthentication() + @ExternalApiAccessible() + @ApiOperation({ + summary: 'Get subscriber preferences', + description: 'Get subscriber preferences', + }) + async getSubscriberPreferences( + @UserSession() user: UserSessionData, + @Param('subscriberId') subscriberId: string + ): Promise { + return await this.getSubscriberPreferencesUsecase.execute( + GetSubscriberPreferencesCommand.create({ + environmentId: user.environmentId, + organizationId: user.organizationId, + subscriberId, + }) + ); + } } diff --git a/apps/api/src/app/subscribers-v2/subscriber.module.ts b/apps/api/src/app/subscribers-v2/subscriber.module.ts index ab9c170a1de..b2a4a8547d4 100644 --- a/apps/api/src/app/subscribers-v2/subscriber.module.ts +++ b/apps/api/src/app/subscribers-v2/subscriber.module.ts @@ -1,14 +1,31 @@ import { Module } from '@nestjs/common'; -import { SubscriberRepository } from '@novu/dal'; +import { NotificationTemplateRepository, PreferencesRepository, SubscriberRepository } from '@novu/dal'; +import { + cacheService, + GetPreferences, + GetSubscriberGlobalPreference, + GetSubscriberPreference, +} from '@novu/application-generic'; import { SubscriberController } from './subscriber.controller'; import { ListSubscribersUseCase } from './usecases/list-subscribers/list-subscribers.usecase'; import { GetSubscriber } from './usecases/get-subscriber/get-subscriber.usecase'; import { PatchSubscriber } from './usecases/patch-subscriber/patch-subscriber.usecase'; +import { GetSubscriberPreferences } from './usecases/get-subscriber-preferences/get-subscriber-preferences.usecase'; -const USE_CASES = [ListSubscribersUseCase, GetSubscriber, PatchSubscriber]; +const USE_CASES = [ + ListSubscribersUseCase, + GetSubscriber, + PatchSubscriber, + GetSubscriberPreferences, + GetSubscriberGlobalPreference, + GetSubscriberPreference, + GetPreferences, +]; + +const DAL_MODELS = [SubscriberRepository, NotificationTemplateRepository, PreferencesRepository]; @Module({ controllers: [SubscriberController], - providers: [...USE_CASES, SubscriberRepository], + providers: [...USE_CASES, ...DAL_MODELS, cacheService], }) export class SubscriberModule {} diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.command.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.command.ts new file mode 100644 index 00000000000..beda7d56af5 --- /dev/null +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.command.ts @@ -0,0 +1,3 @@ +import { EnvironmentWithSubscriber } from '../../../shared/commands/project.command'; + +export class GetSubscriberPreferencesCommand extends EnvironmentWithSubscriber {} diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts new file mode 100644 index 00000000000..ca8c7723cb7 --- /dev/null +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts @@ -0,0 +1,74 @@ +import { Injectable } from '@nestjs/common'; +import { + GetSubscriberGlobalPreference, + GetSubscriberGlobalPreferenceCommand, + GetSubscriberPreference, + GetSubscriberPreferenceCommand, +} from '@novu/application-generic'; +import { IGetPreferencesResponseDto, ISubscriberPreferenceResponse } from '@novu/shared'; +import { GetSubscriberPreferencesCommand } from './get-subscriber-preferences.command'; + +@Injectable() +export class GetSubscriberPreferences { + constructor( + private getSubscriberGlobalPreference: GetSubscriberGlobalPreference, + private getSubscriberPreference: GetSubscriberPreference + ) {} + + async execute(command: GetSubscriberPreferencesCommand): Promise { + try { + const globalPreference = await this.fetchGlobalPreference(command); + const workflowPreferences = await this.fetchWorkflowPreferences(command); + + return { + global: globalPreference, + workflows: workflowPreferences, + }; + } catch (error) { + throw new Error(`Failed to get preferences: ${error.message}`); + } + } + + private async fetchGlobalPreference(command: GetSubscriberPreferencesCommand) { + const { preference } = await this.getSubscriberGlobalPreference.execute( + GetSubscriberGlobalPreferenceCommand.create({ + organizationId: command.organizationId, + environmentId: command.environmentId, + subscriberId: command.subscriberId, + includeInactiveChannels: false, + }) + ); + + return { + enabled: preference.enabled, + channels: preference.channels, + }; + } + + private async fetchWorkflowPreferences(command: GetSubscriberPreferencesCommand) { + const subscriberWorkflowPreferences = await this.getSubscriberPreference.execute( + GetSubscriberPreferenceCommand.create({ + environmentId: command.environmentId, + subscriberId: command.subscriberId, + organizationId: command.organizationId, + includeInactiveChannels: false, + }) + ); + + return subscriberWorkflowPreferences.map(this.mapToWorkflowPreference); + } + + private mapToWorkflowPreference(subscriberWorkflowPreference: ISubscriberPreferenceResponse) { + const { preference, template } = subscriberWorkflowPreference; + + return { + enabled: preference.enabled, + channels: preference.channels, + overrides: preference.overrides, + workflow: { + identifier: template.triggers[0].identifier, + name: template.name, + }, + }; + } +} diff --git a/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts b/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts new file mode 100644 index 00000000000..00332071342 --- /dev/null +++ b/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts @@ -0,0 +1,20 @@ +import { + IPreferenceChannels, + IPreferenceOverride, +} from '../../entities/subscriber-preference/subscriber-preference.interface'; + +export interface IGetSubscriberPreferencesResponseDto { + global: { + enabled: boolean; + channels: IPreferenceChannels; + }; + workflows: Array<{ + enabled: boolean; + channels: IPreferenceChannels; + overrides: IPreferenceOverride[]; + workflow: { + identifier: string; + name: string; + }; + }>; +} diff --git a/packages/shared/src/dto/subscriber/index.ts b/packages/shared/src/dto/subscriber/index.ts index 1cb82a15dd3..e402c82445f 100644 --- a/packages/shared/src/dto/subscriber/index.ts +++ b/packages/shared/src/dto/subscriber/index.ts @@ -2,3 +2,4 @@ export * from './subscriber.dto'; export * from './list-subscribers.dto'; export * from './get-subscriber.dto'; export * from './patch-subscriber.dto'; +export * from './get-subscriber-preferences.dto'; diff --git a/packages/shared/src/entities/subscriber-preference/subscriber-preference.interface.ts b/packages/shared/src/entities/subscriber-preference/subscriber-preference.interface.ts index aa9ee96e5ac..e11dd955529 100644 --- a/packages/shared/src/entities/subscriber-preference/subscriber-preference.interface.ts +++ b/packages/shared/src/entities/subscriber-preference/subscriber-preference.interface.ts @@ -1,8 +1,13 @@ import { ChannelTypeEnum, PreferenceOverrideSourceEnum, PreferencesTypeEnum } from '../../types'; -import { IPreferenceChannelsDto } from '../../dto'; import { INotificationTrigger } from '../notification-trigger'; -export interface IPreferenceChannels extends IPreferenceChannelsDto {} +export interface IPreferenceChannels { + email?: boolean; + sms?: boolean; + in_app?: boolean; + chat?: boolean; + push?: boolean; +} export interface IPreferenceOverride { channel: ChannelTypeEnum; @@ -15,23 +20,7 @@ export interface ISubscriberPreferenceResponse { type: PreferencesTypeEnum; } -export interface ISubscriberWorkflowPreferenceResponse extends IPreferenceResponse { - workflow: ITemplateConfiguration; - level: PreferenceLevelEnum.TEMPLATE; -} - -export interface IWorkflow extends Omit { - id: string; -} -export interface ISubscriberPreferences { - level: PreferenceLevelEnum; - workflow?: IWorkflow; - enabled: boolean; - channels: IPreferenceChannels; - overrides?: IPreferenceOverride[]; -} - -export interface IPreferenceResponse { +interface IPreferenceResponse { enabled: boolean; channels: IPreferenceChannels; overrides: IPreferenceOverride[]; From d112829078141454cdfaab1709a3e59180736edd Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 30 Jan 2025 12:36:39 +0100 Subject: [PATCH 2/9] feat(api-service): add e2e tests --- .../e2e/get-subscriber-preferences.e2e.ts | 132 ++++++++++++++++++ .../subscribers-v2/subscriber.controller.ts | 4 +- .../get-subscriber-preferences.usecase.ts | 20 ++- ...et-subscriber-global-preference.usecase.ts | 7 +- 4 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts diff --git a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts new file mode 100644 index 00000000000..bd3e3b62dbb --- /dev/null +++ b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts @@ -0,0 +1,132 @@ +import { expect } from 'chai'; +import { randomBytes } from 'crypto'; +import { UserSession } from '@novu/testing'; +import { ChannelTypeEnum, IGetSubscriberResponseDto } from '@novu/shared'; +import { NotificationTemplateEntity } from '@novu/dal'; +import { + UpdateSubscriberGlobalPreferencesRequestDto, + UpdateSubscriberPreferenceRequestDto, +} from '@novu/api/models/components'; + +const v2Prefix = '/v2'; +let session: UserSession; + +describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (GET) #novu-v2', () => { + let subscriber: IGetSubscriberResponseDto; + let workflow: NotificationTemplateEntity; + + beforeEach(async () => { + const uuid = randomBytes(4).toString('hex'); + session = new UserSession(); + await session.initialize(); + subscriber = await createSubscriberAndValidate(uuid); + workflow = await session.createTemplate({ + noFeedId: true, + }); + }); + + it('should fetch subscriber preferences with default values', async () => { + const response = await session.testAgent.get(`${v2Prefix}/subscribers/${subscriber.subscriberId}/preferences`); + + expect(response.statusCode).to.equal(200); + expect(response.body.data).to.have.property('global'); + expect(response.body.data).to.have.property('workflows'); + + const { global, workflows } = response.body.data; + + // Validate global preferences + expect(global).to.have.property('enabled'); + expect(global).to.have.property('channels'); + expect(global.enabled).to.be.true; + + // Validate workflows array + expect(workflows).to.be.an('array'); + }); + + it('should return 404 if subscriber does not exist', async () => { + const invalidSubscriberId = `non-existent-${randomBytes(2).toString('hex')}`; + const response = await session.testAgent.get(`${v2Prefix}/subscribers/${invalidSubscriberId}/preferences`); + + expect(response.statusCode).to.equal(404); + }); + + it('should handle subscriber with modified workflow preferences', async () => { + // created workflow has 'email' and 'in-app' channels enabled by default + const workflowId = workflow._id; + + // disable email channel for this workflow + const enableEmailPreferenceData: UpdateSubscriberPreferenceRequestDto = { + channel: { + type: ChannelTypeEnum.EMAIL, + enabled: false, + }, + }; + + // TODO: replace with v2 endpoint when available + await session.testAgent + .patch(`/v1/subscribers/${subscriber.subscriberId}/preferences/${workflowId}`) + .send({ ...enableEmailPreferenceData }); + + const response = await session.testAgent.get(`${v2Prefix}/subscribers/${subscriber.subscriberId}/preferences`); + + const { global, workflows } = response.body.data; + + expect(response.statusCode).to.equal(200); + + expect(global.channels).to.deep.equal({ in_app: true, email: true }); + expect(workflows).to.have.lengthOf(1); + expect(workflows[0].channels).to.deep.equal({ in_app: true, email: false }); + expect(workflows[0].workflow).to.deep.equal({ name: workflow.name, identifier: workflow.triggers[0].identifier }); + }); + + it('should handle subscriber with modified global preferences', async () => { + // disable email channel globally + const enableGlobalEmailPreferenceData: UpdateSubscriberGlobalPreferencesRequestDto = { + preferences: [ + { + type: ChannelTypeEnum.EMAIL, + enabled: false, + }, + ], + }; + + // TODO: replace with v2 endpoint when available + await session.testAgent + .patch(`/v1/subscribers/${subscriber.subscriberId}/preferences`) + .send({ ...enableGlobalEmailPreferenceData }); + + const response = await session.testAgent.get(`${v2Prefix}/subscribers/${subscriber.subscriberId}/preferences`); + + const { global, workflows } = response.body.data; + + expect(response.statusCode).to.equal(200); + + expect(global.channels).to.deep.equal({ in_app: true, email: false }); + expect(workflows).to.have.lengthOf(1); + expect(workflows[0].channels).to.deep.equal({ in_app: true, email: false }); + expect(workflows[0].workflow).to.deep.equal({ name: workflow.name, identifier: workflow.triggers[0].identifier }); + }); +}); + +async function createSubscriberAndValidate(id: string = '') { + const payload = { + subscriberId: `test-subscriber-${id}`, + firstName: `Test ${id}`, + lastName: 'Subscriber', + email: `test-${id}@subscriber.com`, + phone: '+1234567890', + }; + + const res = await session.testAgent.post(`/v1/subscribers`).send(payload); + expect(res.status).to.equal(201); + + const subscriber = res.body.data; + + expect(subscriber.subscriberId).to.equal(payload.subscriberId); + expect(subscriber.firstName).to.equal(payload.firstName); + expect(subscriber.lastName).to.equal(payload.lastName); + expect(subscriber.email).to.equal(payload.email); + expect(subscriber.phone).to.equal(payload.phone); + + return subscriber; +} diff --git a/apps/api/src/app/subscribers-v2/subscriber.controller.ts b/apps/api/src/app/subscribers-v2/subscriber.controller.ts index a51a4f55fb8..ac397ef4e2c 100644 --- a/apps/api/src/app/subscribers-v2/subscriber.controller.ts +++ b/apps/api/src/app/subscribers-v2/subscriber.controller.ts @@ -12,7 +12,7 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ExternalApiAccessible, UserSession } from '@novu/application-generic'; import { DirectionEnum, - IGetPreferencesResponseDto, + IGetSubscriberPreferencesResponseDto, IGetSubscriberResponseDto, IListSubscribersRequestDto, IListSubscribersResponseDto, @@ -116,7 +116,7 @@ export class SubscriberController { async getSubscriberPreferences( @UserSession() user: UserSessionData, @Param('subscriberId') subscriberId: string - ): Promise { + ): Promise { return await this.getSubscriberPreferencesUsecase.execute( GetSubscriberPreferencesCommand.create({ environmentId: user.environmentId, diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts index ca8c7723cb7..8d2a8b5573f 100644 --- a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts @@ -5,7 +5,7 @@ import { GetSubscriberPreference, GetSubscriberPreferenceCommand, } from '@novu/application-generic'; -import { IGetPreferencesResponseDto, ISubscriberPreferenceResponse } from '@novu/shared'; +import { IGetSubscriberPreferencesResponseDto, ISubscriberPreferenceResponse } from '@novu/shared'; import { GetSubscriberPreferencesCommand } from './get-subscriber-preferences.command'; @Injectable() @@ -15,18 +15,14 @@ export class GetSubscriberPreferences { private getSubscriberPreference: GetSubscriberPreference ) {} - async execute(command: GetSubscriberPreferencesCommand): Promise { - try { - const globalPreference = await this.fetchGlobalPreference(command); - const workflowPreferences = await this.fetchWorkflowPreferences(command); + async execute(command: GetSubscriberPreferencesCommand): Promise { + const globalPreference = await this.fetchGlobalPreference(command); + const workflowPreferences = await this.fetchWorkflowPreferences(command); - return { - global: globalPreference, - workflows: workflowPreferences, - }; - } catch (error) { - throw new Error(`Failed to get preferences: ${error.message}`); - } + return { + global: globalPreference, + workflows: workflowPreferences, + }; } private async fetchGlobalPreference(command: GetSubscriberPreferencesCommand) { diff --git a/libs/application-generic/src/usecases/get-subscriber-global-preference/get-subscriber-global-preference.usecase.ts b/libs/application-generic/src/usecases/get-subscriber-global-preference/get-subscriber-global-preference.usecase.ts index b2b408358a6..d865f0d1b3f 100644 --- a/libs/application-generic/src/usecases/get-subscriber-global-preference/get-subscriber-global-preference.usecase.ts +++ b/libs/application-generic/src/usecases/get-subscriber-global-preference/get-subscriber-global-preference.usecase.ts @@ -1,10 +1,9 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { SubscriberEntity, SubscriberRepository } from '@novu/dal'; import { IPreferenceChannels, ChannelTypeEnum } from '@novu/shared'; import { GetSubscriberGlobalPreferenceCommand } from './get-subscriber-global-preference.command'; import { buildSubscriberKey, CachedEntity } from '../../services/cache'; -import { ApiException } from '../../utils/exceptions'; import { GetPreferences } from '../get-preferences'; import { GetSubscriberPreference } from '../get-subscriber-preference/get-subscriber-preference.usecase'; import { filteredPreference } from '../get-subscriber-template-preference/get-subscriber-template-preference.usecase'; @@ -111,7 +110,9 @@ export class GetSubscriberGlobalPreference { ); if (!subscriber) { - throw new ApiException(`Subscriber ${command.subscriberId} not found`); + throw new NotFoundException( + `Subscriber ${command.subscriberId} not found`, + ); } return subscriber; From 518bd9cf65af4e66c64ccedfffd75eb2b4c50803 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 30 Jan 2025 13:19:22 +0100 Subject: [PATCH 3/9] feat(api-service): merge with upstream --- .../dtos/get-subscriber-preferences.dto.ts | 40 +++++++++++++++++++ apps/api/src/app/subscribers-v2/dtos/index.ts | 3 -- .../app/subscribers-v2/subscribers.module.ts | 21 +++++++--- .../get-subscriber-preferences.usecase.ts | 5 ++- .../get-subscriber-preferences.dto.ts | 20 ---------- 5 files changed, 59 insertions(+), 30 deletions(-) create mode 100644 apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts delete mode 100644 apps/api/src/app/subscribers-v2/dtos/index.ts delete mode 100644 packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts diff --git a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts new file mode 100644 index 00000000000..2c2d94abe52 --- /dev/null +++ b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IPreferenceChannels, IPreferenceOverride } from '@novu/shared'; + +class WorkflowInfoDto { + @ApiProperty({ description: 'Unique identifier of the workflow' }) + identifier: string; + + @ApiProperty({ description: 'Display name of the workflow' }) + name: string; +} + +class GlobalPreferenceDto { + @ApiProperty({ description: 'Whether notifications are enabled globally' }) + enabled: boolean; + + @ApiProperty({ description: 'Channel-specific preference settings' }) + channels: IPreferenceChannels; +} + +class WorkflowPreferenceDto { + @ApiProperty({ description: 'Whether notifications are enabled for this workflow' }) + enabled: boolean; + + @ApiProperty({ description: 'Channel-specific preference settings for this workflow' }) + channels: IPreferenceChannels; + + @ApiProperty({ description: 'List of preference overrides', isArray: true }) + overrides: IPreferenceOverride[]; + + @ApiProperty({ description: 'Workflow information' }) + workflow: WorkflowInfoDto; +} + +export class GetSubscriberPreferencesResponseDto { + @ApiProperty({ description: 'Global preference settings', type: GlobalPreferenceDto }) + global: GlobalPreferenceDto; + + @ApiProperty({ description: 'Workflow-specific preference settings', type: [WorkflowPreferenceDto] }) + workflows: WorkflowPreferenceDto[]; +} diff --git a/apps/api/src/app/subscribers-v2/dtos/index.ts b/apps/api/src/app/subscribers-v2/dtos/index.ts deleted file mode 100644 index 1a7a9765f95..00000000000 --- a/apps/api/src/app/subscribers-v2/dtos/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './list-subscribers-response.dto'; -export * from './list-subscribers-query.dto'; -export { CursorPaginationQueryDto } from './cursor-pagination-query.dto'; diff --git a/apps/api/src/app/subscribers-v2/subscribers.module.ts b/apps/api/src/app/subscribers-v2/subscribers.module.ts index 2fa1f756708..ea5fe4dc444 100644 --- a/apps/api/src/app/subscribers-v2/subscribers.module.ts +++ b/apps/api/src/app/subscribers-v2/subscribers.module.ts @@ -1,17 +1,23 @@ import { Module } from '@nestjs/common'; -import { NotificationTemplateRepository, PreferencesRepository, SubscriberRepository } from '@novu/dal'; +import { + NotificationTemplateRepository, + PreferencesRepository, + SubscriberRepository, + TopicSubscribersRepository, +} from '@novu/dal'; import { cacheService, GetPreferences, GetSubscriberGlobalPreference, GetSubscriberPreference, + InvalidateCacheService, } from '@novu/application-generic'; -import { SubscriberController } from './subscriber.controller'; import { ListSubscribersUseCase } from './usecases/list-subscribers/list-subscribers.usecase'; import { GetSubscriber } from './usecases/get-subscriber/get-subscriber.usecase'; import { PatchSubscriber } from './usecases/patch-subscriber/patch-subscriber.usecase'; import { GetSubscriberPreferences } from './usecases/get-subscriber-preferences/get-subscriber-preferences.usecase'; import { RemoveSubscriber } from './usecases/remove-subscriber/remove-subscriber.usecase'; +import { SubscribersController } from './subscribers.controller'; const USE_CASES = [ ListSubscribersUseCase, @@ -24,10 +30,15 @@ const USE_CASES = [ GetPreferences, ]; -const DAL_MODELS = [SubscriberRepository, NotificationTemplateRepository, PreferencesRepository]; +const DAL_MODELS = [ + SubscriberRepository, + NotificationTemplateRepository, + PreferencesRepository, + TopicSubscribersRepository, +]; @Module({ - controllers: [SubscriberController], - providers: [...USE_CASES, ...DAL_MODELS, cacheService], + controllers: [SubscribersController], + providers: [...USE_CASES, ...DAL_MODELS, cacheService, InvalidateCacheService], }) export class SubscribersModule {} diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts index 8d2a8b5573f..45a4a28bb3e 100644 --- a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts @@ -5,8 +5,9 @@ import { GetSubscriberPreference, GetSubscriberPreferenceCommand, } from '@novu/application-generic'; -import { IGetSubscriberPreferencesResponseDto, ISubscriberPreferenceResponse } from '@novu/shared'; +import { ISubscriberPreferenceResponse } from '@novu/shared'; import { GetSubscriberPreferencesCommand } from './get-subscriber-preferences.command'; +import { GetSubscriberPreferencesResponseDto } from '../../dtos/get-subscriber-preferences.dto'; @Injectable() export class GetSubscriberPreferences { @@ -15,7 +16,7 @@ export class GetSubscriberPreferences { private getSubscriberPreference: GetSubscriberPreference ) {} - async execute(command: GetSubscriberPreferencesCommand): Promise { + async execute(command: GetSubscriberPreferencesCommand): Promise { const globalPreference = await this.fetchGlobalPreference(command); const workflowPreferences = await this.fetchWorkflowPreferences(command); diff --git a/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts b/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts deleted file mode 100644 index 00332071342..00000000000 --- a/packages/shared/src/dto/subscriber/get-subscriber-preferences.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - IPreferenceChannels, - IPreferenceOverride, -} from '../../entities/subscriber-preference/subscriber-preference.interface'; - -export interface IGetSubscriberPreferencesResponseDto { - global: { - enabled: boolean; - channels: IPreferenceChannels; - }; - workflows: Array<{ - enabled: boolean; - channels: IPreferenceChannels; - overrides: IPreferenceOverride[]; - workflow: { - identifier: string; - name: string; - }; - }>; -} From 35018db0192c12923d2c7c55aa9799d5a04c2fe8 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 30 Jan 2025 15:04:39 +0100 Subject: [PATCH 4/9] fix(api-service): import --- .../usecases/list-subscribers/list-subscribers.usecase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/app/subscribers-v2/usecases/list-subscribers/list-subscribers.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/list-subscribers/list-subscribers.usecase.ts index 21c0bafd20b..2bf8c38b68b 100644 --- a/apps/api/src/app/subscribers-v2/usecases/list-subscribers/list-subscribers.usecase.ts +++ b/apps/api/src/app/subscribers-v2/usecases/list-subscribers/list-subscribers.usecase.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InstrumentUsecase } from '@novu/application-generic'; import { SubscriberRepository } from '@novu/dal'; import { ListSubscribersCommand } from './list-subscribers.command'; -import { ListSubscribersResponseDto } from '../../dtos'; +import { ListSubscribersResponseDto } from '../../dtos/list-subscribers-response.dto'; import { DirectionEnum } from '../../../shared/dtos/base-responses'; import { mapSubscriberEntityToDto } from './map-subscriber-entity-to.dto'; From 0330809c6df60513a0327219e42f7be313ec2c4e Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 30 Jan 2025 15:42:17 +0100 Subject: [PATCH 5/9] fix(api-service): dtos and types --- .../dtos/get-subscriber-preferences.dto.ts | 57 +++++++++++++++---- .../e2e/get-subscriber-preferences.e2e.ts | 5 +- .../subscribers-v2/subscribers.controller.ts | 4 +- .../get-subscriber-preferences.usecase.ts | 4 +- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts index 2c2d94abe52..acd6b8fb36c 100644 --- a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts +++ b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts @@ -1,7 +1,40 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IPreferenceChannels, IPreferenceOverride } from '@novu/shared'; +import { ChannelTypeEnum, IPreferenceChannels, IPreferenceOverride, PreferenceOverrideSourceEnum } from '@novu/shared'; -class WorkflowInfoDto { +export class PreferenceChannelsDto implements IPreferenceChannels { + @ApiProperty({ description: 'Email channel preference' }) + email?: boolean; + + @ApiProperty({ description: 'SMS channel preference' }) + sms?: boolean; + + @ApiProperty({ description: 'In-app channel preference' }) + in_app?: boolean; + + @ApiProperty({ description: 'Push channel preference' }) + push?: boolean; + + @ApiProperty({ description: 'Chat channel preference' }) + chat?: boolean; +} + +export class PreferenceOverride implements IPreferenceOverride { + @ApiProperty({ + enum: ChannelTypeEnum, + enumName: 'ChannelTypeEnum', + description: 'The channel type for the override', + }) + channel: ChannelTypeEnum; + + @ApiProperty({ + enum: PreferenceOverrideSourceEnum, + enumName: 'PreferenceOverrideSourceEnum', + description: 'The source of the override', + }) + source: PreferenceOverrideSourceEnum; +} + +export class WorkflowInfoDto { @ApiProperty({ description: 'Unique identifier of the workflow' }) identifier: string; @@ -9,29 +42,29 @@ class WorkflowInfoDto { name: string; } -class GlobalPreferenceDto { +export class GlobalPreferenceDto { @ApiProperty({ description: 'Whether notifications are enabled globally' }) enabled: boolean; - @ApiProperty({ description: 'Channel-specific preference settings' }) - channels: IPreferenceChannels; + @ApiProperty({ description: 'Channel-specific preference settings', type: PreferenceChannelsDto }) + channels: PreferenceChannelsDto; } -class WorkflowPreferenceDto { +export class WorkflowPreferenceDto { @ApiProperty({ description: 'Whether notifications are enabled for this workflow' }) enabled: boolean; - @ApiProperty({ description: 'Channel-specific preference settings for this workflow' }) - channels: IPreferenceChannels; + @ApiProperty({ description: 'Channel-specific preference settings for this workflow', type: PreferenceChannelsDto }) + channels: PreferenceChannelsDto; - @ApiProperty({ description: 'List of preference overrides', isArray: true }) - overrides: IPreferenceOverride[]; + @ApiProperty({ description: 'List of preference overrides', type: [PreferenceOverride] }) + overrides: PreferenceOverride[]; - @ApiProperty({ description: 'Workflow information' }) + @ApiProperty({ description: 'Workflow information', type: WorkflowInfoDto }) workflow: WorkflowInfoDto; } -export class GetSubscriberPreferencesResponseDto { +export class GetSubscriberPreferencesDto { @ApiProperty({ description: 'Global preference settings', type: GlobalPreferenceDto }) global: GlobalPreferenceDto; diff --git a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts index bd3e3b62dbb..abda3746dbd 100644 --- a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts +++ b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts @@ -1,18 +1,19 @@ import { expect } from 'chai'; import { randomBytes } from 'crypto'; import { UserSession } from '@novu/testing'; -import { ChannelTypeEnum, IGetSubscriberResponseDto } from '@novu/shared'; +import { ChannelTypeEnum } from '@novu/shared'; import { NotificationTemplateEntity } from '@novu/dal'; import { UpdateSubscriberGlobalPreferencesRequestDto, UpdateSubscriberPreferenceRequestDto, + SubscriberResponseDto, } from '@novu/api/models/components'; const v2Prefix = '/v2'; let session: UserSession; describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (GET) #novu-v2', () => { - let subscriber: IGetSubscriberResponseDto; + let subscriber: SubscriberResponseDto; let workflow: NotificationTemplateEntity; beforeEach(async () => { diff --git a/apps/api/src/app/subscribers-v2/subscribers.controller.ts b/apps/api/src/app/subscribers-v2/subscribers.controller.ts index d77e7cebdde..a322e024225 100644 --- a/apps/api/src/app/subscribers-v2/subscribers.controller.ts +++ b/apps/api/src/app/subscribers-v2/subscribers.controller.ts @@ -31,7 +31,7 @@ import { SubscriberResponseDto } from '../subscribers/dtos'; import { RemoveSubscriberCommand } from './usecases/remove-subscriber/remove-subscriber.command'; import { RemoveSubscriber } from './usecases/remove-subscriber/remove-subscriber.usecase'; import { RemoveSubscriberResponseDto } from './dtos/remove-subscriber.dto'; -import { GetSubscriberPreferencesResponseDto } from './dtos/get-subscriber-preferences.dto'; +import { GetSubscriberPreferencesDto } from './dtos/get-subscriber-preferences.dto'; @Controller({ path: '/subscribers', version: '2' }) @UseInterceptors(ClassSerializerInterceptor) @@ -151,7 +151,7 @@ export class SubscribersController { async getSubscriberPreferences( @UserSession() user: UserSessionData, @Param('subscriberId') subscriberId: string - ): Promise { + ): Promise { return await this.getSubscriberPreferencesUsecase.execute( GetSubscriberPreferencesCommand.create({ environmentId: user.environmentId, diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts index 45a4a28bb3e..c8e404190ab 100644 --- a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts @@ -7,7 +7,7 @@ import { } from '@novu/application-generic'; import { ISubscriberPreferenceResponse } from '@novu/shared'; import { GetSubscriberPreferencesCommand } from './get-subscriber-preferences.command'; -import { GetSubscriberPreferencesResponseDto } from '../../dtos/get-subscriber-preferences.dto'; +import { GetSubscriberPreferencesDto } from '../../dtos/get-subscriber-preferences.dto'; @Injectable() export class GetSubscriberPreferences { @@ -16,7 +16,7 @@ export class GetSubscriberPreferences { private getSubscriberPreference: GetSubscriberPreference ) {} - async execute(command: GetSubscriberPreferencesCommand): Promise { + async execute(command: GetSubscriberPreferencesCommand): Promise { const globalPreference = await this.fetchGlobalPreference(command); const workflowPreferences = await this.fetchWorkflowPreferences(command); From ad52649650c6f2d757f5e2d2acc8169234578813 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 30 Jan 2025 16:02:22 +0100 Subject: [PATCH 6/9] feat(api-service): serialzie response --- .../dtos/get-subscriber-preferences.dto.ts | 7 +++++++ .../get-subscriber-preferences.usecase.ts | 15 ++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts index acd6b8fb36c..4d988de7dbf 100644 --- a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts +++ b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { ChannelTypeEnum, IPreferenceChannels, IPreferenceOverride, PreferenceOverrideSourceEnum } from '@novu/shared'; +import { Type } from 'class-transformer'; export class PreferenceChannelsDto implements IPreferenceChannels { @ApiProperty({ description: 'Email channel preference' }) @@ -47,6 +48,7 @@ export class GlobalPreferenceDto { enabled: boolean; @ApiProperty({ description: 'Channel-specific preference settings', type: PreferenceChannelsDto }) + @Type(() => PreferenceChannelsDto) channels: PreferenceChannelsDto; } @@ -55,19 +57,24 @@ export class WorkflowPreferenceDto { enabled: boolean; @ApiProperty({ description: 'Channel-specific preference settings for this workflow', type: PreferenceChannelsDto }) + @Type(() => PreferenceChannelsDto) channels: PreferenceChannelsDto; @ApiProperty({ description: 'List of preference overrides', type: [PreferenceOverride] }) + @Type(() => PreferenceOverride) overrides: PreferenceOverride[]; @ApiProperty({ description: 'Workflow information', type: WorkflowInfoDto }) + @Type(() => WorkflowInfoDto) workflow: WorkflowInfoDto; } export class GetSubscriberPreferencesDto { @ApiProperty({ description: 'Global preference settings', type: GlobalPreferenceDto }) + @Type(() => GlobalPreferenceDto) global: GlobalPreferenceDto; @ApiProperty({ description: 'Workflow-specific preference settings', type: [WorkflowPreferenceDto] }) + @Type(() => WorkflowPreferenceDto) workflows: WorkflowPreferenceDto[]; } diff --git a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts index c8e404190ab..d51a158fdef 100644 --- a/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts +++ b/apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts @@ -6,8 +6,13 @@ import { GetSubscriberPreferenceCommand, } from '@novu/application-generic'; import { ISubscriberPreferenceResponse } from '@novu/shared'; +import { plainToInstance } from 'class-transformer'; import { GetSubscriberPreferencesCommand } from './get-subscriber-preferences.command'; -import { GetSubscriberPreferencesDto } from '../../dtos/get-subscriber-preferences.dto'; +import { + GetSubscriberPreferencesDto, + GlobalPreferenceDto, + WorkflowPreferenceDto, +} from '../../dtos/get-subscriber-preferences.dto'; @Injectable() export class GetSubscriberPreferences { @@ -20,13 +25,13 @@ export class GetSubscriberPreferences { const globalPreference = await this.fetchGlobalPreference(command); const workflowPreferences = await this.fetchWorkflowPreferences(command); - return { + return plainToInstance(GetSubscriberPreferencesDto, { global: globalPreference, workflows: workflowPreferences, - }; + }); } - private async fetchGlobalPreference(command: GetSubscriberPreferencesCommand) { + private async fetchGlobalPreference(command: GetSubscriberPreferencesCommand): Promise { const { preference } = await this.getSubscriberGlobalPreference.execute( GetSubscriberGlobalPreferenceCommand.create({ organizationId: command.organizationId, @@ -55,7 +60,7 @@ export class GetSubscriberPreferences { return subscriberWorkflowPreferences.map(this.mapToWorkflowPreference); } - private mapToWorkflowPreference(subscriberWorkflowPreference: ISubscriberPreferenceResponse) { + private mapToWorkflowPreference(subscriberWorkflowPreference: ISubscriberPreferenceResponse): WorkflowPreferenceDto { const { preference, template } = subscriberWorkflowPreference; return { From d7aa6de65c4bc0e07cedec3e8e4b5da6d7bb382d Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Fri, 31 Jan 2025 10:37:29 +0100 Subject: [PATCH 7/9] feat(api-service): add SDK retrieve methods --- .../e2e/get-subscriber-preferences.e2e.ts | 19 ++++++++++++------- .../subscribers-v2/subscribers.controller.ts | 2 ++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts index abda3746dbd..8333ddfcc52 100644 --- a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts +++ b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts @@ -8,11 +8,14 @@ import { UpdateSubscriberPreferenceRequestDto, SubscriberResponseDto, } from '@novu/api/models/components'; +import { Novu } from '@novu/api'; +import { expectSdkExceptionGeneric, initNovuClassSdk } from '../../shared/helpers/e2e/sdk/e2e-sdk.helper'; const v2Prefix = '/v2'; let session: UserSession; describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (GET) #novu-v2', () => { + let novuClient: Novu; let subscriber: SubscriberResponseDto; let workflow: NotificationTemplateEntity; @@ -20,6 +23,7 @@ describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (G const uuid = randomBytes(4).toString('hex'); session = new UserSession(); await session.initialize(); + novuClient = initNovuClassSdk(session); subscriber = await createSubscriberAndValidate(uuid); workflow = await session.createTemplate({ noFeedId: true, @@ -27,13 +31,12 @@ describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (G }); it('should fetch subscriber preferences with default values', async () => { - const response = await session.testAgent.get(`${v2Prefix}/subscribers/${subscriber.subscriberId}/preferences`); + const response = await novuClient.subscribers.preferences.retrieve(subscriber.subscriberId); - expect(response.statusCode).to.equal(200); - expect(response.body.data).to.have.property('global'); - expect(response.body.data).to.have.property('workflows'); + expect(response.result).to.have.property('global'); + expect(response.result).to.have.property('workflows'); - const { global, workflows } = response.body.data; + const { global, workflows } = response.result; // Validate global preferences expect(global).to.have.property('enabled'); @@ -46,9 +49,11 @@ describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (G it('should return 404 if subscriber does not exist', async () => { const invalidSubscriberId = `non-existent-${randomBytes(2).toString('hex')}`; - const response = await session.testAgent.get(`${v2Prefix}/subscribers/${invalidSubscriberId}/preferences`); + const { error } = await expectSdkExceptionGeneric(() => + novuClient.subscribers.preferences.retrieve(invalidSubscriberId) + ); - expect(response.statusCode).to.equal(404); + expect(error?.statusCode).to.equal(404); }); it('should handle subscriber with modified workflow preferences', async () => { diff --git a/apps/api/src/app/subscribers-v2/subscribers.controller.ts b/apps/api/src/app/subscribers-v2/subscribers.controller.ts index a322e024225..8a87f6cfe0c 100644 --- a/apps/api/src/app/subscribers-v2/subscribers.controller.ts +++ b/apps/api/src/app/subscribers-v2/subscribers.controller.ts @@ -148,6 +148,8 @@ export class SubscribersController { summary: 'Get subscriber preferences', description: 'Get subscriber preferences', }) + @SdkGroupName('Subscribers.Preferences') + @SdkMethodName('retrieve') async getSubscriberPreferences( @UserSession() user: UserSessionData, @Param('subscriberId') subscriberId: string From 9cd9e568f4d8f71d50f8dc9e6670e44140a41871 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Fri, 31 Jan 2025 11:19:05 +0100 Subject: [PATCH 8/9] feat(api-service): speakeasy SDKs --- .../e2e/get-subscriber-preferences.e2e.ts | 1 + .../subscribers-v2/subscribers.controller.ts | 1 + .../funcs/subscribersPreferencesRetrieve.ts | 202 ++++++++++++++++++ .../components/getsubscriberpreferencesdto.ts | 90 ++++++++ .../models/components/globalpreferencedto.ts | 82 +++++++ .../src/models/components/index.ts | 7 + .../components/preferencechannelsdto.ts | 106 +++++++++ .../models/components/preferenceoverride.ts | 86 ++++++++ .../preferenceoverridesourceenum.ts | 42 ++++ .../src/models/components/workflowinfodto.ts | 74 +++++++ .../components/workflowpreferencedto.ts | 108 ++++++++++ .../src/models/operations/index.ts | 1 + ...iberscontrollergetsubscriberpreferences.ts | 183 ++++++++++++++++ libs/internal-sdk/src/react-query/index.ts | 1 + .../subscribersPreferencesRetrieve.ts | 170 +++++++++++++++ libs/internal-sdk/src/sdk/preferences.ts | 20 ++ 16 files changed, 1174 insertions(+) create mode 100644 libs/internal-sdk/src/funcs/subscribersPreferencesRetrieve.ts create mode 100644 libs/internal-sdk/src/models/components/getsubscriberpreferencesdto.ts create mode 100644 libs/internal-sdk/src/models/components/globalpreferencedto.ts create mode 100644 libs/internal-sdk/src/models/components/preferencechannelsdto.ts create mode 100644 libs/internal-sdk/src/models/components/preferenceoverride.ts create mode 100644 libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts create mode 100644 libs/internal-sdk/src/models/components/workflowinfodto.ts create mode 100644 libs/internal-sdk/src/models/components/workflowpreferencedto.ts create mode 100644 libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts create mode 100644 libs/internal-sdk/src/react-query/subscribersPreferencesRetrieve.ts diff --git a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts index 8333ddfcc52..026284d8bc5 100644 --- a/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts +++ b/apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts @@ -45,6 +45,7 @@ describe('Get Subscriber Preferences - /subscribers/:subscriberId/preferences (G // Validate workflows array expect(workflows).to.be.an('array'); + expect(workflows).to.have.lengthOf(1); }); it('should return 404 if subscriber does not exist', async () => { diff --git a/apps/api/src/app/subscribers-v2/subscribers.controller.ts b/apps/api/src/app/subscribers-v2/subscribers.controller.ts index 8a87f6cfe0c..bdba72a084a 100644 --- a/apps/api/src/app/subscribers-v2/subscribers.controller.ts +++ b/apps/api/src/app/subscribers-v2/subscribers.controller.ts @@ -148,6 +148,7 @@ export class SubscribersController { summary: 'Get subscriber preferences', description: 'Get subscriber preferences', }) + @ApiResponse(GetSubscriberPreferencesDto) @SdkGroupName('Subscribers.Preferences') @SdkMethodName('retrieve') async getSubscriberPreferences( diff --git a/libs/internal-sdk/src/funcs/subscribersPreferencesRetrieve.ts b/libs/internal-sdk/src/funcs/subscribersPreferencesRetrieve.ts new file mode 100644 index 00000000000..2529ee3cf09 --- /dev/null +++ b/libs/internal-sdk/src/funcs/subscribersPreferencesRetrieve.ts @@ -0,0 +1,202 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { NovuCore } from "../core.js"; +import { encodeSimple } from "../lib/encodings.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/httpclienterrors.js"; +import * as errors from "../models/errors/index.js"; +import { SDKError } from "../models/errors/sdkerror.js"; +import { SDKValidationError } from "../models/errors/sdkvalidationerror.js"; +import * as operations from "../models/operations/index.js"; +import { Result } from "../types/fp.js"; + +/** + * Get subscriber preferences + * + * @remarks + * Get subscriber preferences + */ +export async function subscribersPreferencesRetrieve( + client: NovuCore, + subscriberId: string, + idempotencyKey?: string | undefined, + options?: RequestOptions, +): Promise< + Result< + operations.SubscribersControllerGetSubscriberPreferencesResponse, + | errors.ErrorDto + | errors.ErrorDto + | errors.ValidationErrorDto + | errors.ErrorDto + | SDKError + | SDKValidationError + | UnexpectedClientError + | InvalidRequestError + | RequestAbortedError + | RequestTimeoutError + | ConnectionError + > +> { + const input: operations.SubscribersControllerGetSubscriberPreferencesRequest = + { + subscriberId: subscriberId, + idempotencyKey: idempotencyKey, + }; + + const parsed = safeParse( + input, + (value) => + operations + .SubscribersControllerGetSubscriberPreferencesRequest$outboundSchema + .parse(value), + "Input validation failed", + ); + if (!parsed.ok) { + return parsed; + } + const payload = parsed.value; + const body = null; + + const pathParams = { + subscriberId: encodeSimple("subscriberId", payload.subscriberId, { + explode: false, + charEncoding: "percent", + }), + }; + + const path = pathToFunc("/v2/subscribers/{subscriberId}/preferences")( + pathParams, + ); + + const headers = new Headers(compactMap({ + Accept: "application/json", + "idempotency-key": encodeSimple( + "idempotency-key", + payload["idempotency-key"], + { explode: false, charEncoding: "none" }, + ), + })); + + const securityInput = await extractSecurity(client._options.security); + const requestSecurity = resolveGlobalSecurity(securityInput); + + const context = { + operationID: "SubscribersController_getSubscriberPreferences", + oAuth2Scopes: [], + + resolvedSecurity: requestSecurity, + + securitySource: client._options.security, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 1000, + maxInterval: 30000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["408", "409", "429", "5XX"], + }; + + const requestRes = client._createRequest(context, { + security: requestSecurity, + method: "GET", + baseURL: options?.serverURL, + path: path, + headers: headers, + body: body, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return requestRes; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + errorCodes: [ + "400", + "401", + "403", + "404", + "405", + "409", + "413", + "414", + "415", + "422", + "429", + "4XX", + "500", + "503", + "5XX", + ], + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return doResult; + } + const response = doResult.value; + + const responseFields = { + HttpMeta: { Response: response, Request: req }, + }; + + const [result] = await M.match< + operations.SubscribersControllerGetSubscriberPreferencesResponse, + | errors.ErrorDto + | errors.ErrorDto + | errors.ValidationErrorDto + | errors.ErrorDto + | SDKError + | SDKValidationError + | UnexpectedClientError + | InvalidRequestError + | RequestAbortedError + | RequestTimeoutError + | ConnectionError + >( + M.json( + 200, + operations + .SubscribersControllerGetSubscriberPreferencesResponse$inboundSchema, + { hdrs: true, key: "Result" }, + ), + M.jsonErr(414, errors.ErrorDto$inboundSchema), + M.jsonErr( + [400, 401, 403, 404, 405, 409, 413, 415], + errors.ErrorDto$inboundSchema, + { hdrs: true }, + ), + M.jsonErr(422, errors.ValidationErrorDto$inboundSchema, { hdrs: true }), + M.fail(429), + M.jsonErr(500, errors.ErrorDto$inboundSchema, { hdrs: true }), + M.fail(503), + M.fail("4XX"), + M.fail("5XX"), + )(response, { extraFields: responseFields }); + if (!result.ok) { + return result; + } + + return result; +} diff --git a/libs/internal-sdk/src/models/components/getsubscriberpreferencesdto.ts b/libs/internal-sdk/src/models/components/getsubscriberpreferencesdto.ts new file mode 100644 index 00000000000..ff12360dba4 --- /dev/null +++ b/libs/internal-sdk/src/models/components/getsubscriberpreferencesdto.ts @@ -0,0 +1,90 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; +import { + GlobalPreferenceDto, + GlobalPreferenceDto$inboundSchema, + GlobalPreferenceDto$Outbound, + GlobalPreferenceDto$outboundSchema, +} from "./globalpreferencedto.js"; +import { + WorkflowPreferenceDto, + WorkflowPreferenceDto$inboundSchema, + WorkflowPreferenceDto$Outbound, + WorkflowPreferenceDto$outboundSchema, +} from "./workflowpreferencedto.js"; + +export type GetSubscriberPreferencesDto = { + /** + * Global preference settings + */ + global: GlobalPreferenceDto; + /** + * Workflow-specific preference settings + */ + workflows: Array; +}; + +/** @internal */ +export const GetSubscriberPreferencesDto$inboundSchema: z.ZodType< + GetSubscriberPreferencesDto, + z.ZodTypeDef, + unknown +> = z.object({ + global: GlobalPreferenceDto$inboundSchema, + workflows: z.array(WorkflowPreferenceDto$inboundSchema), +}); + +/** @internal */ +export type GetSubscriberPreferencesDto$Outbound = { + global: GlobalPreferenceDto$Outbound; + workflows: Array; +}; + +/** @internal */ +export const GetSubscriberPreferencesDto$outboundSchema: z.ZodType< + GetSubscriberPreferencesDto$Outbound, + z.ZodTypeDef, + GetSubscriberPreferencesDto +> = z.object({ + global: GlobalPreferenceDto$outboundSchema, + workflows: z.array(WorkflowPreferenceDto$outboundSchema), +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace GetSubscriberPreferencesDto$ { + /** @deprecated use `GetSubscriberPreferencesDto$inboundSchema` instead. */ + export const inboundSchema = GetSubscriberPreferencesDto$inboundSchema; + /** @deprecated use `GetSubscriberPreferencesDto$outboundSchema` instead. */ + export const outboundSchema = GetSubscriberPreferencesDto$outboundSchema; + /** @deprecated use `GetSubscriberPreferencesDto$Outbound` instead. */ + export type Outbound = GetSubscriberPreferencesDto$Outbound; +} + +export function getSubscriberPreferencesDtoToJSON( + getSubscriberPreferencesDto: GetSubscriberPreferencesDto, +): string { + return JSON.stringify( + GetSubscriberPreferencesDto$outboundSchema.parse( + getSubscriberPreferencesDto, + ), + ); +} + +export function getSubscriberPreferencesDtoFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GetSubscriberPreferencesDto$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GetSubscriberPreferencesDto' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/globalpreferencedto.ts b/libs/internal-sdk/src/models/components/globalpreferencedto.ts new file mode 100644 index 00000000000..6520ce4a824 --- /dev/null +++ b/libs/internal-sdk/src/models/components/globalpreferencedto.ts @@ -0,0 +1,82 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; +import { + PreferenceChannelsDto, + PreferenceChannelsDto$inboundSchema, + PreferenceChannelsDto$Outbound, + PreferenceChannelsDto$outboundSchema, +} from "./preferencechannelsdto.js"; + +export type GlobalPreferenceDto = { + /** + * Whether notifications are enabled globally + */ + enabled: boolean; + /** + * Channel-specific preference settings + */ + channels: PreferenceChannelsDto; +}; + +/** @internal */ +export const GlobalPreferenceDto$inboundSchema: z.ZodType< + GlobalPreferenceDto, + z.ZodTypeDef, + unknown +> = z.object({ + enabled: z.boolean(), + channels: PreferenceChannelsDto$inboundSchema, +}); + +/** @internal */ +export type GlobalPreferenceDto$Outbound = { + enabled: boolean; + channels: PreferenceChannelsDto$Outbound; +}; + +/** @internal */ +export const GlobalPreferenceDto$outboundSchema: z.ZodType< + GlobalPreferenceDto$Outbound, + z.ZodTypeDef, + GlobalPreferenceDto +> = z.object({ + enabled: z.boolean(), + channels: PreferenceChannelsDto$outboundSchema, +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace GlobalPreferenceDto$ { + /** @deprecated use `GlobalPreferenceDto$inboundSchema` instead. */ + export const inboundSchema = GlobalPreferenceDto$inboundSchema; + /** @deprecated use `GlobalPreferenceDto$outboundSchema` instead. */ + export const outboundSchema = GlobalPreferenceDto$outboundSchema; + /** @deprecated use `GlobalPreferenceDto$Outbound` instead. */ + export type Outbound = GlobalPreferenceDto$Outbound; +} + +export function globalPreferenceDtoToJSON( + globalPreferenceDto: GlobalPreferenceDto, +): string { + return JSON.stringify( + GlobalPreferenceDto$outboundSchema.parse(globalPreferenceDto), + ); +} + +export function globalPreferenceDtoFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GlobalPreferenceDto$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GlobalPreferenceDto' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/index.ts b/libs/internal-sdk/src/models/components/index.ts index b8374b20e00..164b08cff9b 100644 --- a/libs/internal-sdk/src/models/components/index.ts +++ b/libs/internal-sdk/src/models/components/index.ts @@ -54,8 +54,10 @@ export * from "./feedresponsedto.js"; export * from "./fieldfilterpartdto.js"; export * from "./filtertopicsresponsedto.js"; export * from "./generatepreviewresponsedto.js"; +export * from "./getsubscriberpreferencesdto.js"; export * from "./getsubscriberpreferencesresponsedto.js"; export * from "./gettopicresponsedto.js"; +export * from "./globalpreferencedto.js"; export * from "./integrationresponsedto.js"; export * from "./listsubscribersresponsedto.js"; export * from "./markallmessageasrequestdto.js"; @@ -86,6 +88,9 @@ export * from "./ordinalvalueenum.js"; export * from "./patchsubscriberrequestdto.js"; export * from "./preference.js"; export * from "./preferencechannels.js"; +export * from "./preferencechannelsdto.js"; +export * from "./preferenceoverride.js"; +export * from "./preferenceoverridesourceenum.js"; export * from "./providersidenum.js"; export * from "./removesubscriberresponsedto.js"; export * from "./removesubscribersrequestdto.js"; @@ -122,4 +127,6 @@ export * from "./updatesubscriberpreferenceglobalresponsedto.js"; export * from "./updatesubscriberpreferencerequestdto.js"; export * from "./updatesubscriberpreferenceresponsedto.js"; export * from "./updatesubscriberrequestdto.js"; +export * from "./workflowinfodto.js"; +export * from "./workflowpreferencedto.js"; export * from "./workflowresponse.js"; diff --git a/libs/internal-sdk/src/models/components/preferencechannelsdto.ts b/libs/internal-sdk/src/models/components/preferencechannelsdto.ts new file mode 100644 index 00000000000..60598fb5c65 --- /dev/null +++ b/libs/internal-sdk/src/models/components/preferencechannelsdto.ts @@ -0,0 +1,106 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { remap as remap$ } from "../../lib/primitives.js"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; + +export type PreferenceChannelsDto = { + /** + * Email channel preference + */ + email?: boolean | undefined; + /** + * SMS channel preference + */ + sms?: boolean | undefined; + /** + * In-app channel preference + */ + inApp?: boolean | undefined; + /** + * Push channel preference + */ + push?: boolean | undefined; + /** + * Chat channel preference + */ + chat?: boolean | undefined; +}; + +/** @internal */ +export const PreferenceChannelsDto$inboundSchema: z.ZodType< + PreferenceChannelsDto, + z.ZodTypeDef, + unknown +> = z.object({ + email: z.boolean().optional(), + sms: z.boolean().optional(), + in_app: z.boolean().optional(), + push: z.boolean().optional(), + chat: z.boolean().optional(), +}).transform((v) => { + return remap$(v, { + "in_app": "inApp", + }); +}); + +/** @internal */ +export type PreferenceChannelsDto$Outbound = { + email?: boolean | undefined; + sms?: boolean | undefined; + in_app?: boolean | undefined; + push?: boolean | undefined; + chat?: boolean | undefined; +}; + +/** @internal */ +export const PreferenceChannelsDto$outboundSchema: z.ZodType< + PreferenceChannelsDto$Outbound, + z.ZodTypeDef, + PreferenceChannelsDto +> = z.object({ + email: z.boolean().optional(), + sms: z.boolean().optional(), + inApp: z.boolean().optional(), + push: z.boolean().optional(), + chat: z.boolean().optional(), +}).transform((v) => { + return remap$(v, { + inApp: "in_app", + }); +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace PreferenceChannelsDto$ { + /** @deprecated use `PreferenceChannelsDto$inboundSchema` instead. */ + export const inboundSchema = PreferenceChannelsDto$inboundSchema; + /** @deprecated use `PreferenceChannelsDto$outboundSchema` instead. */ + export const outboundSchema = PreferenceChannelsDto$outboundSchema; + /** @deprecated use `PreferenceChannelsDto$Outbound` instead. */ + export type Outbound = PreferenceChannelsDto$Outbound; +} + +export function preferenceChannelsDtoToJSON( + preferenceChannelsDto: PreferenceChannelsDto, +): string { + return JSON.stringify( + PreferenceChannelsDto$outboundSchema.parse(preferenceChannelsDto), + ); +} + +export function preferenceChannelsDtoFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => PreferenceChannelsDto$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'PreferenceChannelsDto' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/preferenceoverride.ts b/libs/internal-sdk/src/models/components/preferenceoverride.ts new file mode 100644 index 00000000000..edfb31e856e --- /dev/null +++ b/libs/internal-sdk/src/models/components/preferenceoverride.ts @@ -0,0 +1,86 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; +import { + ChannelTypeEnum, + ChannelTypeEnum$inboundSchema, + ChannelTypeEnum$outboundSchema, +} from "./channeltypeenum.js"; +import { + PreferenceOverrideSourceEnum, + PreferenceOverrideSourceEnum$inboundSchema, + PreferenceOverrideSourceEnum$outboundSchema, +} from "./preferenceoverridesourceenum.js"; + +export type PreferenceOverride = { + /** + * Channel type through which the message is sent + */ + channel: ChannelTypeEnum; + /** + * The source of the override + */ + source: PreferenceOverrideSourceEnum; +}; + +/** @internal */ +export const PreferenceOverride$inboundSchema: z.ZodType< + PreferenceOverride, + z.ZodTypeDef, + unknown +> = z.object({ + channel: ChannelTypeEnum$inboundSchema, + source: PreferenceOverrideSourceEnum$inboundSchema, +}); + +/** @internal */ +export type PreferenceOverride$Outbound = { + channel: string; + source: string; +}; + +/** @internal */ +export const PreferenceOverride$outboundSchema: z.ZodType< + PreferenceOverride$Outbound, + z.ZodTypeDef, + PreferenceOverride +> = z.object({ + channel: ChannelTypeEnum$outboundSchema, + source: PreferenceOverrideSourceEnum$outboundSchema, +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace PreferenceOverride$ { + /** @deprecated use `PreferenceOverride$inboundSchema` instead. */ + export const inboundSchema = PreferenceOverride$inboundSchema; + /** @deprecated use `PreferenceOverride$outboundSchema` instead. */ + export const outboundSchema = PreferenceOverride$outboundSchema; + /** @deprecated use `PreferenceOverride$Outbound` instead. */ + export type Outbound = PreferenceOverride$Outbound; +} + +export function preferenceOverrideToJSON( + preferenceOverride: PreferenceOverride, +): string { + return JSON.stringify( + PreferenceOverride$outboundSchema.parse(preferenceOverride), + ); +} + +export function preferenceOverrideFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => PreferenceOverride$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'PreferenceOverride' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts b/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts new file mode 100644 index 00000000000..b8b8ee8d714 --- /dev/null +++ b/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts @@ -0,0 +1,42 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { ClosedEnum } from "../../types/enums.js"; + +/** + * The source of the override + */ +export const PreferenceOverrideSourceEnum = { + Subscriber: "subscriber", + Template: "template", + WorkflowOverride: "workflowOverride", +} as const; +/** + * The source of the override + */ +export type PreferenceOverrideSourceEnum = ClosedEnum< + typeof PreferenceOverrideSourceEnum +>; + +/** @internal */ +export const PreferenceOverrideSourceEnum$inboundSchema: z.ZodNativeEnum< + typeof PreferenceOverrideSourceEnum +> = z.nativeEnum(PreferenceOverrideSourceEnum); + +/** @internal */ +export const PreferenceOverrideSourceEnum$outboundSchema: z.ZodNativeEnum< + typeof PreferenceOverrideSourceEnum +> = PreferenceOverrideSourceEnum$inboundSchema; + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace PreferenceOverrideSourceEnum$ { + /** @deprecated use `PreferenceOverrideSourceEnum$inboundSchema` instead. */ + export const inboundSchema = PreferenceOverrideSourceEnum$inboundSchema; + /** @deprecated use `PreferenceOverrideSourceEnum$outboundSchema` instead. */ + export const outboundSchema = PreferenceOverrideSourceEnum$outboundSchema; +} diff --git a/libs/internal-sdk/src/models/components/workflowinfodto.ts b/libs/internal-sdk/src/models/components/workflowinfodto.ts new file mode 100644 index 00000000000..c80379c2be6 --- /dev/null +++ b/libs/internal-sdk/src/models/components/workflowinfodto.ts @@ -0,0 +1,74 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; + +export type WorkflowInfoDto = { + /** + * Unique identifier of the workflow + */ + identifier: string; + /** + * Display name of the workflow + */ + name: string; +}; + +/** @internal */ +export const WorkflowInfoDto$inboundSchema: z.ZodType< + WorkflowInfoDto, + z.ZodTypeDef, + unknown +> = z.object({ + identifier: z.string(), + name: z.string(), +}); + +/** @internal */ +export type WorkflowInfoDto$Outbound = { + identifier: string; + name: string; +}; + +/** @internal */ +export const WorkflowInfoDto$outboundSchema: z.ZodType< + WorkflowInfoDto$Outbound, + z.ZodTypeDef, + WorkflowInfoDto +> = z.object({ + identifier: z.string(), + name: z.string(), +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace WorkflowInfoDto$ { + /** @deprecated use `WorkflowInfoDto$inboundSchema` instead. */ + export const inboundSchema = WorkflowInfoDto$inboundSchema; + /** @deprecated use `WorkflowInfoDto$outboundSchema` instead. */ + export const outboundSchema = WorkflowInfoDto$outboundSchema; + /** @deprecated use `WorkflowInfoDto$Outbound` instead. */ + export type Outbound = WorkflowInfoDto$Outbound; +} + +export function workflowInfoDtoToJSON( + workflowInfoDto: WorkflowInfoDto, +): string { + return JSON.stringify(WorkflowInfoDto$outboundSchema.parse(workflowInfoDto)); +} + +export function workflowInfoDtoFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => WorkflowInfoDto$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'WorkflowInfoDto' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/workflowpreferencedto.ts b/libs/internal-sdk/src/models/components/workflowpreferencedto.ts new file mode 100644 index 00000000000..115710ac54f --- /dev/null +++ b/libs/internal-sdk/src/models/components/workflowpreferencedto.ts @@ -0,0 +1,108 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; +import { + PreferenceChannelsDto, + PreferenceChannelsDto$inboundSchema, + PreferenceChannelsDto$Outbound, + PreferenceChannelsDto$outboundSchema, +} from "./preferencechannelsdto.js"; +import { + PreferenceOverride, + PreferenceOverride$inboundSchema, + PreferenceOverride$Outbound, + PreferenceOverride$outboundSchema, +} from "./preferenceoverride.js"; +import { + WorkflowInfoDto, + WorkflowInfoDto$inboundSchema, + WorkflowInfoDto$Outbound, + WorkflowInfoDto$outboundSchema, +} from "./workflowinfodto.js"; + +export type WorkflowPreferenceDto = { + /** + * Whether notifications are enabled for this workflow + */ + enabled: boolean; + /** + * Channel-specific preference settings for this workflow + */ + channels: PreferenceChannelsDto; + /** + * List of preference overrides + */ + overrides: Array; + /** + * Workflow information + */ + workflow: WorkflowInfoDto; +}; + +/** @internal */ +export const WorkflowPreferenceDto$inboundSchema: z.ZodType< + WorkflowPreferenceDto, + z.ZodTypeDef, + unknown +> = z.object({ + enabled: z.boolean(), + channels: PreferenceChannelsDto$inboundSchema, + overrides: z.array(PreferenceOverride$inboundSchema), + workflow: WorkflowInfoDto$inboundSchema, +}); + +/** @internal */ +export type WorkflowPreferenceDto$Outbound = { + enabled: boolean; + channels: PreferenceChannelsDto$Outbound; + overrides: Array; + workflow: WorkflowInfoDto$Outbound; +}; + +/** @internal */ +export const WorkflowPreferenceDto$outboundSchema: z.ZodType< + WorkflowPreferenceDto$Outbound, + z.ZodTypeDef, + WorkflowPreferenceDto +> = z.object({ + enabled: z.boolean(), + channels: PreferenceChannelsDto$outboundSchema, + overrides: z.array(PreferenceOverride$outboundSchema), + workflow: WorkflowInfoDto$outboundSchema, +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace WorkflowPreferenceDto$ { + /** @deprecated use `WorkflowPreferenceDto$inboundSchema` instead. */ + export const inboundSchema = WorkflowPreferenceDto$inboundSchema; + /** @deprecated use `WorkflowPreferenceDto$outboundSchema` instead. */ + export const outboundSchema = WorkflowPreferenceDto$outboundSchema; + /** @deprecated use `WorkflowPreferenceDto$Outbound` instead. */ + export type Outbound = WorkflowPreferenceDto$Outbound; +} + +export function workflowPreferenceDtoToJSON( + workflowPreferenceDto: WorkflowPreferenceDto, +): string { + return JSON.stringify( + WorkflowPreferenceDto$outboundSchema.parse(workflowPreferenceDto), + ); +} + +export function workflowPreferenceDtoFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => WorkflowPreferenceDto$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'WorkflowPreferenceDto' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/operations/index.ts b/libs/internal-sdk/src/models/operations/index.ts index c1e68e01829..40cb6b528ad 100644 --- a/libs/internal-sdk/src/models/operations/index.ts +++ b/libs/internal-sdk/src/models/operations/index.ts @@ -21,6 +21,7 @@ export * from "./notificationscontrollergetactivitystats.js"; export * from "./notificationscontrollergetnotification.js"; export * from "./notificationscontrollerlistnotifications.js"; export * from "./subscriberscontrollergetsubscriber.js"; +export * from "./subscriberscontrollergetsubscriberpreferences.js"; export * from "./subscriberscontrollerpatchsubscriber.js"; export * from "./subscriberscontrollerremovesubscriber.js"; export * from "./subscriberscontrollersearchsubscribers.js"; diff --git a/libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts b/libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts new file mode 100644 index 00000000000..241189aee47 --- /dev/null +++ b/libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts @@ -0,0 +1,183 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { remap as remap$ } from "../../lib/primitives.js"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import * as components from "../components/index.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; + +export type SubscribersControllerGetSubscriberPreferencesRequest = { + subscriberId: string; + /** + * A header for idempotency purposes + */ + idempotencyKey?: string | undefined; +}; + +export type SubscribersControllerGetSubscriberPreferencesResponse = { + headers: { [k: string]: Array }; + result: components.GetSubscriberPreferencesDto; +}; + +/** @internal */ +export const SubscribersControllerGetSubscriberPreferencesRequest$inboundSchema: + z.ZodType< + SubscribersControllerGetSubscriberPreferencesRequest, + z.ZodTypeDef, + unknown + > = z.object({ + subscriberId: z.string(), + "idempotency-key": z.string().optional(), + }).transform((v) => { + return remap$(v, { + "idempotency-key": "idempotencyKey", + }); + }); + +/** @internal */ +export type SubscribersControllerGetSubscriberPreferencesRequest$Outbound = { + subscriberId: string; + "idempotency-key"?: string | undefined; +}; + +/** @internal */ +export const SubscribersControllerGetSubscriberPreferencesRequest$outboundSchema: + z.ZodType< + SubscribersControllerGetSubscriberPreferencesRequest$Outbound, + z.ZodTypeDef, + SubscribersControllerGetSubscriberPreferencesRequest + > = z.object({ + subscriberId: z.string(), + idempotencyKey: z.string().optional(), + }).transform((v) => { + return remap$(v, { + idempotencyKey: "idempotency-key", + }); + }); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace SubscribersControllerGetSubscriberPreferencesRequest$ { + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesRequest$inboundSchema` instead. */ + export const inboundSchema = + SubscribersControllerGetSubscriberPreferencesRequest$inboundSchema; + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesRequest$outboundSchema` instead. */ + export const outboundSchema = + SubscribersControllerGetSubscriberPreferencesRequest$outboundSchema; + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesRequest$Outbound` instead. */ + export type Outbound = + SubscribersControllerGetSubscriberPreferencesRequest$Outbound; +} + +export function subscribersControllerGetSubscriberPreferencesRequestToJSON( + subscribersControllerGetSubscriberPreferencesRequest: + SubscribersControllerGetSubscriberPreferencesRequest, +): string { + return JSON.stringify( + SubscribersControllerGetSubscriberPreferencesRequest$outboundSchema.parse( + subscribersControllerGetSubscriberPreferencesRequest, + ), + ); +} + +export function subscribersControllerGetSubscriberPreferencesRequestFromJSON( + jsonString: string, +): SafeParseResult< + SubscribersControllerGetSubscriberPreferencesRequest, + SDKValidationError +> { + return safeParse( + jsonString, + (x) => + SubscribersControllerGetSubscriberPreferencesRequest$inboundSchema.parse( + JSON.parse(x), + ), + `Failed to parse 'SubscribersControllerGetSubscriberPreferencesRequest' from JSON`, + ); +} + +/** @internal */ +export const SubscribersControllerGetSubscriberPreferencesResponse$inboundSchema: + z.ZodType< + SubscribersControllerGetSubscriberPreferencesResponse, + z.ZodTypeDef, + unknown + > = z.object({ + Headers: z.record(z.array(z.string())), + Result: components.GetSubscriberPreferencesDto$inboundSchema, + }).transform((v) => { + return remap$(v, { + "Headers": "headers", + "Result": "result", + }); + }); + +/** @internal */ +export type SubscribersControllerGetSubscriberPreferencesResponse$Outbound = { + Headers: { [k: string]: Array }; + Result: components.GetSubscriberPreferencesDto$Outbound; +}; + +/** @internal */ +export const SubscribersControllerGetSubscriberPreferencesResponse$outboundSchema: + z.ZodType< + SubscribersControllerGetSubscriberPreferencesResponse$Outbound, + z.ZodTypeDef, + SubscribersControllerGetSubscriberPreferencesResponse + > = z.object({ + headers: z.record(z.array(z.string())), + result: components.GetSubscriberPreferencesDto$outboundSchema, + }).transform((v) => { + return remap$(v, { + headers: "Headers", + result: "Result", + }); + }); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace SubscribersControllerGetSubscriberPreferencesResponse$ { + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesResponse$inboundSchema` instead. */ + export const inboundSchema = + SubscribersControllerGetSubscriberPreferencesResponse$inboundSchema; + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesResponse$outboundSchema` instead. */ + export const outboundSchema = + SubscribersControllerGetSubscriberPreferencesResponse$outboundSchema; + /** @deprecated use `SubscribersControllerGetSubscriberPreferencesResponse$Outbound` instead. */ + export type Outbound = + SubscribersControllerGetSubscriberPreferencesResponse$Outbound; +} + +export function subscribersControllerGetSubscriberPreferencesResponseToJSON( + subscribersControllerGetSubscriberPreferencesResponse: + SubscribersControllerGetSubscriberPreferencesResponse, +): string { + return JSON.stringify( + SubscribersControllerGetSubscriberPreferencesResponse$outboundSchema.parse( + subscribersControllerGetSubscriberPreferencesResponse, + ), + ); +} + +export function subscribersControllerGetSubscriberPreferencesResponseFromJSON( + jsonString: string, +): SafeParseResult< + SubscribersControllerGetSubscriberPreferencesResponse, + SDKValidationError +> { + return safeParse( + jsonString, + (x) => + SubscribersControllerGetSubscriberPreferencesResponse$inboundSchema.parse( + JSON.parse(x), + ), + `Failed to parse 'SubscribersControllerGetSubscriberPreferencesResponse' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/react-query/index.ts b/libs/internal-sdk/src/react-query/index.ts index 6cd452c19bb..9410762018b 100644 --- a/libs/internal-sdk/src/react-query/index.ts +++ b/libs/internal-sdk/src/react-query/index.ts @@ -37,6 +37,7 @@ export * from "./subscribersNotificationsFeed.js"; export * from "./subscribersNotificationsUnseenCount.js"; export * from "./subscribersPatch.js"; export * from "./subscribersPreferencesList.js"; +export * from "./subscribersPreferencesRetrieve.js"; export * from "./subscribersPreferencesRetrieveByLevel.js"; export * from "./subscribersPreferencesUpdate.js"; export * from "./subscribersPreferencesUpdateGlobal.js"; diff --git a/libs/internal-sdk/src/react-query/subscribersPreferencesRetrieve.ts b/libs/internal-sdk/src/react-query/subscribersPreferencesRetrieve.ts new file mode 100644 index 00000000000..425349c8f72 --- /dev/null +++ b/libs/internal-sdk/src/react-query/subscribersPreferencesRetrieve.ts @@ -0,0 +1,170 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { + InvalidateQueryFilters, + QueryClient, + QueryFunctionContext, + QueryKey, + useQuery, + UseQueryResult, + useSuspenseQuery, + UseSuspenseQueryResult, +} from "@tanstack/react-query"; +import { NovuCore } from "../core.js"; +import { subscribersPreferencesRetrieve } from "../funcs/subscribersPreferencesRetrieve.js"; +import { combineSignals } from "../lib/primitives.js"; +import { RequestOptions } from "../lib/sdks.js"; +import * as operations from "../models/operations/index.js"; +import { unwrapAsync } from "../types/fp.js"; +import { useNovuContext } from "./_context.js"; +import { + QueryHookOptions, + SuspenseQueryHookOptions, + TupleToPrefixes, +} from "./_types.js"; + +export type SubscribersPreferencesRetrieveQueryData = + operations.SubscribersControllerGetSubscriberPreferencesResponse; + +/** + * Get subscriber preferences + * + * @remarks + * Get subscriber preferences + */ +export function useSubscribersPreferencesRetrieve( + subscriberId: string, + idempotencyKey?: string | undefined, + options?: QueryHookOptions, +): UseQueryResult { + const client = useNovuContext(); + return useQuery({ + ...buildSubscribersPreferencesRetrieveQuery( + client, + subscriberId, + idempotencyKey, + options, + ), + ...options, + }); +} + +/** + * Get subscriber preferences + * + * @remarks + * Get subscriber preferences + */ +export function useSubscribersPreferencesRetrieveSuspense( + subscriberId: string, + idempotencyKey?: string | undefined, + options?: SuspenseQueryHookOptions, +): UseSuspenseQueryResult { + const client = useNovuContext(); + return useSuspenseQuery({ + ...buildSubscribersPreferencesRetrieveQuery( + client, + subscriberId, + idempotencyKey, + options, + ), + ...options, + }); +} + +export function prefetchSubscribersPreferencesRetrieve( + queryClient: QueryClient, + client$: NovuCore, + subscriberId: string, + idempotencyKey?: string | undefined, +): Promise { + return queryClient.prefetchQuery({ + ...buildSubscribersPreferencesRetrieveQuery( + client$, + subscriberId, + idempotencyKey, + ), + }); +} + +export function setSubscribersPreferencesRetrieveData( + client: QueryClient, + queryKeyBase: [ + subscriberId: string, + parameters: { idempotencyKey?: string | undefined }, + ], + data: SubscribersPreferencesRetrieveQueryData, +): SubscribersPreferencesRetrieveQueryData | undefined { + const key = queryKeySubscribersPreferencesRetrieve(...queryKeyBase); + + return client.setQueryData( + key, + data, + ); +} + +export function invalidateSubscribersPreferencesRetrieve( + client: QueryClient, + queryKeyBase: TupleToPrefixes< + [subscriberId: string, parameters: { idempotencyKey?: string | undefined }] + >, + filters?: Omit, +): Promise { + return client.invalidateQueries({ + ...filters, + queryKey: ["@novu/api", "Preferences", "retrieve", ...queryKeyBase], + }); +} + +export function invalidateAllSubscribersPreferencesRetrieve( + client: QueryClient, + filters?: Omit, +): Promise { + return client.invalidateQueries({ + ...filters, + queryKey: ["@novu/api", "Preferences", "retrieve"], + }); +} + +export function buildSubscribersPreferencesRetrieveQuery( + client$: NovuCore, + subscriberId: string, + idempotencyKey?: string | undefined, + options?: RequestOptions, +): { + queryKey: QueryKey; + queryFn: ( + context: QueryFunctionContext, + ) => Promise; +} { + return { + queryKey: queryKeySubscribersPreferencesRetrieve(subscriberId, { + idempotencyKey, + }), + queryFn: async function subscribersPreferencesRetrieveQueryFn( + ctx, + ): Promise { + const sig = combineSignals(ctx.signal, options?.fetchOptions?.signal); + const mergedOptions = { + ...options, + fetchOptions: { ...options?.fetchOptions, signal: sig }, + }; + + return unwrapAsync(subscribersPreferencesRetrieve( + client$, + subscriberId, + idempotencyKey, + mergedOptions, + )); + }, + }; +} + +export function queryKeySubscribersPreferencesRetrieve( + subscriberId: string, + parameters: { idempotencyKey?: string | undefined }, +): QueryKey { + return ["@novu/api", "Preferences", "retrieve", subscriberId, parameters]; +} diff --git a/libs/internal-sdk/src/sdk/preferences.ts b/libs/internal-sdk/src/sdk/preferences.ts index d7225c70891..b038e583948 100644 --- a/libs/internal-sdk/src/sdk/preferences.ts +++ b/libs/internal-sdk/src/sdk/preferences.ts @@ -3,6 +3,7 @@ */ import { subscribersPreferencesList } from "../funcs/subscribersPreferencesList.js"; +import { subscribersPreferencesRetrieve } from "../funcs/subscribersPreferencesRetrieve.js"; import { subscribersPreferencesRetrieveByLevel } from "../funcs/subscribersPreferencesRetrieveByLevel.js"; import { subscribersPreferencesUpdate } from "../funcs/subscribersPreferencesUpdate.js"; import { subscribersPreferencesUpdateGlobal } from "../funcs/subscribersPreferencesUpdateGlobal.js"; @@ -86,4 +87,23 @@ export class Preferences extends ClientSDK { options, )); } + + /** + * Get subscriber preferences + * + * @remarks + * Get subscriber preferences + */ + async retrieve( + subscriberId: string, + idempotencyKey?: string | undefined, + options?: RequestOptions, + ): Promise { + return unwrapAsync(subscribersPreferencesRetrieve( + this, + subscriberId, + idempotencyKey, + options, + )); + } } From ad4666b1317578950e958a1c97a24e08080f6ae1 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Fri, 31 Jan 2025 14:59:54 +0100 Subject: [PATCH 9/9] fix: reuse dtos --- .../dtos/get-subscriber-preferences.dto.ts | 54 ++----- ...get-subscriber-preferences-response.dto.ts | 4 +- .../models/components/globalpreferencedto.ts | 18 +-- .../src/models/components/index.ts | 4 +- .../src/models/components/overrides.ts | 141 ++++++++++++++++++ .../components/preferencechannelsdto.ts | 106 ------------- .../models/components/preferenceoverride.ts | 86 ----------- .../preferenceoverridesourceenum.ts | 42 ------ .../components/triggereventtoallrequestdto.ts | 59 +++++--- .../components/workflowpreferencedto.ts | 36 ++--- 10 files changed, 217 insertions(+), 333 deletions(-) create mode 100644 libs/internal-sdk/src/models/components/overrides.ts delete mode 100644 libs/internal-sdk/src/models/components/preferencechannelsdto.ts delete mode 100644 libs/internal-sdk/src/models/components/preferenceoverride.ts delete mode 100644 libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts diff --git a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts index 4d988de7dbf..8d9ada3ec06 100644 --- a/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts +++ b/apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences.dto.ts @@ -1,39 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; -import { ChannelTypeEnum, IPreferenceChannels, IPreferenceOverride, PreferenceOverrideSourceEnum } from '@novu/shared'; import { Type } from 'class-transformer'; - -export class PreferenceChannelsDto implements IPreferenceChannels { - @ApiProperty({ description: 'Email channel preference' }) - email?: boolean; - - @ApiProperty({ description: 'SMS channel preference' }) - sms?: boolean; - - @ApiProperty({ description: 'In-app channel preference' }) - in_app?: boolean; - - @ApiProperty({ description: 'Push channel preference' }) - push?: boolean; - - @ApiProperty({ description: 'Chat channel preference' }) - chat?: boolean; -} - -export class PreferenceOverride implements IPreferenceOverride { - @ApiProperty({ - enum: ChannelTypeEnum, - enumName: 'ChannelTypeEnum', - description: 'The channel type for the override', - }) - channel: ChannelTypeEnum; - - @ApiProperty({ - enum: PreferenceOverrideSourceEnum, - enumName: 'PreferenceOverrideSourceEnum', - description: 'The source of the override', - }) - source: PreferenceOverrideSourceEnum; -} +import { PreferenceChannels } from '../../shared/dtos/preference-channels'; +import { Overrides } from '../../subscribers/dtos/get-subscriber-preferences-response.dto'; export class WorkflowInfoDto { @ApiProperty({ description: 'Unique identifier of the workflow' }) @@ -47,22 +15,22 @@ export class GlobalPreferenceDto { @ApiProperty({ description: 'Whether notifications are enabled globally' }) enabled: boolean; - @ApiProperty({ description: 'Channel-specific preference settings', type: PreferenceChannelsDto }) - @Type(() => PreferenceChannelsDto) - channels: PreferenceChannelsDto; + @ApiProperty({ description: 'Channel-specific preference settings', type: PreferenceChannels }) + @Type(() => PreferenceChannels) + channels: PreferenceChannels; } export class WorkflowPreferenceDto { @ApiProperty({ description: 'Whether notifications are enabled for this workflow' }) enabled: boolean; - @ApiProperty({ description: 'Channel-specific preference settings for this workflow', type: PreferenceChannelsDto }) - @Type(() => PreferenceChannelsDto) - channels: PreferenceChannelsDto; + @ApiProperty({ description: 'Channel-specific preference settings for this workflow', type: PreferenceChannels }) + @Type(() => PreferenceChannels) + channels: PreferenceChannels; - @ApiProperty({ description: 'List of preference overrides', type: [PreferenceOverride] }) - @Type(() => PreferenceOverride) - overrides: PreferenceOverride[]; + @ApiProperty({ description: 'List of preference overrides', type: [Overrides] }) + @Type(() => Overrides) + overrides: Overrides[]; @ApiProperty({ description: 'Workflow information', type: WorkflowInfoDto }) @Type(() => WorkflowInfoDto) diff --git a/apps/api/src/app/subscribers/dtos/get-subscriber-preferences-response.dto.ts b/apps/api/src/app/subscribers/dtos/get-subscriber-preferences-response.dto.ts index 0b688d87a26..dfd1f6d9666 100644 --- a/apps/api/src/app/subscribers/dtos/get-subscriber-preferences-response.dto.ts +++ b/apps/api/src/app/subscribers/dtos/get-subscriber-preferences-response.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { ChannelTypeEnum, PreferenceOverrideSourceEnum } from '@novu/shared'; +import { ChannelTypeEnum, IPreferenceOverride, PreferenceOverrideSourceEnum } from '@novu/shared'; import { PreferenceChannels } from '../../shared/dtos/preference-channels'; class TemplateResponse { @@ -23,7 +23,7 @@ class TemplateResponse { critical: boolean; } -class Overrides { +export class Overrides implements IPreferenceOverride { @ApiProperty({ enum: ChannelTypeEnum, description: 'The channel type which is overridden', diff --git a/libs/internal-sdk/src/models/components/globalpreferencedto.ts b/libs/internal-sdk/src/models/components/globalpreferencedto.ts index 6520ce4a824..8bd0306fb97 100644 --- a/libs/internal-sdk/src/models/components/globalpreferencedto.ts +++ b/libs/internal-sdk/src/models/components/globalpreferencedto.ts @@ -7,11 +7,11 @@ import { safeParse } from "../../lib/schemas.js"; import { Result as SafeParseResult } from "../../types/fp.js"; import { SDKValidationError } from "../errors/sdkvalidationerror.js"; import { - PreferenceChannelsDto, - PreferenceChannelsDto$inboundSchema, - PreferenceChannelsDto$Outbound, - PreferenceChannelsDto$outboundSchema, -} from "./preferencechannelsdto.js"; + PreferenceChannels, + PreferenceChannels$inboundSchema, + PreferenceChannels$Outbound, + PreferenceChannels$outboundSchema, +} from "./preferencechannels.js"; export type GlobalPreferenceDto = { /** @@ -21,7 +21,7 @@ export type GlobalPreferenceDto = { /** * Channel-specific preference settings */ - channels: PreferenceChannelsDto; + channels: PreferenceChannels; }; /** @internal */ @@ -31,13 +31,13 @@ export const GlobalPreferenceDto$inboundSchema: z.ZodType< unknown > = z.object({ enabled: z.boolean(), - channels: PreferenceChannelsDto$inboundSchema, + channels: PreferenceChannels$inboundSchema, }); /** @internal */ export type GlobalPreferenceDto$Outbound = { enabled: boolean; - channels: PreferenceChannelsDto$Outbound; + channels: PreferenceChannels$Outbound; }; /** @internal */ @@ -47,7 +47,7 @@ export const GlobalPreferenceDto$outboundSchema: z.ZodType< GlobalPreferenceDto > = z.object({ enabled: z.boolean(), - channels: PreferenceChannelsDto$outboundSchema, + channels: PreferenceChannels$outboundSchema, }); /** diff --git a/libs/internal-sdk/src/models/components/index.ts b/libs/internal-sdk/src/models/components/index.ts index 164b08cff9b..5e9b78fa5e1 100644 --- a/libs/internal-sdk/src/models/components/index.ts +++ b/libs/internal-sdk/src/models/components/index.ts @@ -85,12 +85,10 @@ export * from "./notificationtriggervariable.js"; export * from "./notificationtriggervariableresponse.js"; export * from "./ordinalenum.js"; export * from "./ordinalvalueenum.js"; +export * from "./overrides.js"; export * from "./patchsubscriberrequestdto.js"; export * from "./preference.js"; export * from "./preferencechannels.js"; -export * from "./preferencechannelsdto.js"; -export * from "./preferenceoverride.js"; -export * from "./preferenceoverridesourceenum.js"; export * from "./providersidenum.js"; export * from "./removesubscriberresponsedto.js"; export * from "./removesubscribersrequestdto.js"; diff --git a/libs/internal-sdk/src/models/components/overrides.ts b/libs/internal-sdk/src/models/components/overrides.ts new file mode 100644 index 00000000000..0965f9bb062 --- /dev/null +++ b/libs/internal-sdk/src/models/components/overrides.ts @@ -0,0 +1,141 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod"; +import { safeParse } from "../../lib/schemas.js"; +import { ClosedEnum } from "../../types/enums.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { SDKValidationError } from "../errors/sdkvalidationerror.js"; + +/** + * The channel type which is overridden + */ +export const OverridesChannel = { + InApp: "in_app", + Email: "email", + Sms: "sms", + Chat: "chat", + Push: "push", +} as const; +/** + * The channel type which is overridden + */ +export type OverridesChannel = ClosedEnum; + +/** + * The source of overrides + */ +export const Source = { + Subscriber: "subscriber", + Template: "template", + WorkflowOverride: "workflowOverride", +} as const; +/** + * The source of overrides + */ +export type Source = ClosedEnum; + +export type Overrides = { + /** + * The channel type which is overridden + */ + channel: OverridesChannel; + /** + * The source of overrides + */ + source: Source; +}; + +/** @internal */ +export const OverridesChannel$inboundSchema: z.ZodNativeEnum< + typeof OverridesChannel +> = z.nativeEnum(OverridesChannel); + +/** @internal */ +export const OverridesChannel$outboundSchema: z.ZodNativeEnum< + typeof OverridesChannel +> = OverridesChannel$inboundSchema; + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace OverridesChannel$ { + /** @deprecated use `OverridesChannel$inboundSchema` instead. */ + export const inboundSchema = OverridesChannel$inboundSchema; + /** @deprecated use `OverridesChannel$outboundSchema` instead. */ + export const outboundSchema = OverridesChannel$outboundSchema; +} + +/** @internal */ +export const Source$inboundSchema: z.ZodNativeEnum = z + .nativeEnum(Source); + +/** @internal */ +export const Source$outboundSchema: z.ZodNativeEnum = + Source$inboundSchema; + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace Source$ { + /** @deprecated use `Source$inboundSchema` instead. */ + export const inboundSchema = Source$inboundSchema; + /** @deprecated use `Source$outboundSchema` instead. */ + export const outboundSchema = Source$outboundSchema; +} + +/** @internal */ +export const Overrides$inboundSchema: z.ZodType< + Overrides, + z.ZodTypeDef, + unknown +> = z.object({ + channel: OverridesChannel$inboundSchema, + source: Source$inboundSchema, +}); + +/** @internal */ +export type Overrides$Outbound = { + channel: string; + source: string; +}; + +/** @internal */ +export const Overrides$outboundSchema: z.ZodType< + Overrides$Outbound, + z.ZodTypeDef, + Overrides +> = z.object({ + channel: OverridesChannel$outboundSchema, + source: Source$outboundSchema, +}); + +/** + * @internal + * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. + */ +export namespace Overrides$ { + /** @deprecated use `Overrides$inboundSchema` instead. */ + export const inboundSchema = Overrides$inboundSchema; + /** @deprecated use `Overrides$outboundSchema` instead. */ + export const outboundSchema = Overrides$outboundSchema; + /** @deprecated use `Overrides$Outbound` instead. */ + export type Outbound = Overrides$Outbound; +} + +export function overridesToJSON(overrides: Overrides): string { + return JSON.stringify(Overrides$outboundSchema.parse(overrides)); +} + +export function overridesFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => Overrides$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'Overrides' from JSON`, + ); +} diff --git a/libs/internal-sdk/src/models/components/preferencechannelsdto.ts b/libs/internal-sdk/src/models/components/preferencechannelsdto.ts deleted file mode 100644 index 60598fb5c65..00000000000 --- a/libs/internal-sdk/src/models/components/preferencechannelsdto.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. - */ - -import * as z from "zod"; -import { remap as remap$ } from "../../lib/primitives.js"; -import { safeParse } from "../../lib/schemas.js"; -import { Result as SafeParseResult } from "../../types/fp.js"; -import { SDKValidationError } from "../errors/sdkvalidationerror.js"; - -export type PreferenceChannelsDto = { - /** - * Email channel preference - */ - email?: boolean | undefined; - /** - * SMS channel preference - */ - sms?: boolean | undefined; - /** - * In-app channel preference - */ - inApp?: boolean | undefined; - /** - * Push channel preference - */ - push?: boolean | undefined; - /** - * Chat channel preference - */ - chat?: boolean | undefined; -}; - -/** @internal */ -export const PreferenceChannelsDto$inboundSchema: z.ZodType< - PreferenceChannelsDto, - z.ZodTypeDef, - unknown -> = z.object({ - email: z.boolean().optional(), - sms: z.boolean().optional(), - in_app: z.boolean().optional(), - push: z.boolean().optional(), - chat: z.boolean().optional(), -}).transform((v) => { - return remap$(v, { - "in_app": "inApp", - }); -}); - -/** @internal */ -export type PreferenceChannelsDto$Outbound = { - email?: boolean | undefined; - sms?: boolean | undefined; - in_app?: boolean | undefined; - push?: boolean | undefined; - chat?: boolean | undefined; -}; - -/** @internal */ -export const PreferenceChannelsDto$outboundSchema: z.ZodType< - PreferenceChannelsDto$Outbound, - z.ZodTypeDef, - PreferenceChannelsDto -> = z.object({ - email: z.boolean().optional(), - sms: z.boolean().optional(), - inApp: z.boolean().optional(), - push: z.boolean().optional(), - chat: z.boolean().optional(), -}).transform((v) => { - return remap$(v, { - inApp: "in_app", - }); -}); - -/** - * @internal - * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. - */ -export namespace PreferenceChannelsDto$ { - /** @deprecated use `PreferenceChannelsDto$inboundSchema` instead. */ - export const inboundSchema = PreferenceChannelsDto$inboundSchema; - /** @deprecated use `PreferenceChannelsDto$outboundSchema` instead. */ - export const outboundSchema = PreferenceChannelsDto$outboundSchema; - /** @deprecated use `PreferenceChannelsDto$Outbound` instead. */ - export type Outbound = PreferenceChannelsDto$Outbound; -} - -export function preferenceChannelsDtoToJSON( - preferenceChannelsDto: PreferenceChannelsDto, -): string { - return JSON.stringify( - PreferenceChannelsDto$outboundSchema.parse(preferenceChannelsDto), - ); -} - -export function preferenceChannelsDtoFromJSON( - jsonString: string, -): SafeParseResult { - return safeParse( - jsonString, - (x) => PreferenceChannelsDto$inboundSchema.parse(JSON.parse(x)), - `Failed to parse 'PreferenceChannelsDto' from JSON`, - ); -} diff --git a/libs/internal-sdk/src/models/components/preferenceoverride.ts b/libs/internal-sdk/src/models/components/preferenceoverride.ts deleted file mode 100644 index edfb31e856e..00000000000 --- a/libs/internal-sdk/src/models/components/preferenceoverride.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. - */ - -import * as z from "zod"; -import { safeParse } from "../../lib/schemas.js"; -import { Result as SafeParseResult } from "../../types/fp.js"; -import { SDKValidationError } from "../errors/sdkvalidationerror.js"; -import { - ChannelTypeEnum, - ChannelTypeEnum$inboundSchema, - ChannelTypeEnum$outboundSchema, -} from "./channeltypeenum.js"; -import { - PreferenceOverrideSourceEnum, - PreferenceOverrideSourceEnum$inboundSchema, - PreferenceOverrideSourceEnum$outboundSchema, -} from "./preferenceoverridesourceenum.js"; - -export type PreferenceOverride = { - /** - * Channel type through which the message is sent - */ - channel: ChannelTypeEnum; - /** - * The source of the override - */ - source: PreferenceOverrideSourceEnum; -}; - -/** @internal */ -export const PreferenceOverride$inboundSchema: z.ZodType< - PreferenceOverride, - z.ZodTypeDef, - unknown -> = z.object({ - channel: ChannelTypeEnum$inboundSchema, - source: PreferenceOverrideSourceEnum$inboundSchema, -}); - -/** @internal */ -export type PreferenceOverride$Outbound = { - channel: string; - source: string; -}; - -/** @internal */ -export const PreferenceOverride$outboundSchema: z.ZodType< - PreferenceOverride$Outbound, - z.ZodTypeDef, - PreferenceOverride -> = z.object({ - channel: ChannelTypeEnum$outboundSchema, - source: PreferenceOverrideSourceEnum$outboundSchema, -}); - -/** - * @internal - * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. - */ -export namespace PreferenceOverride$ { - /** @deprecated use `PreferenceOverride$inboundSchema` instead. */ - export const inboundSchema = PreferenceOverride$inboundSchema; - /** @deprecated use `PreferenceOverride$outboundSchema` instead. */ - export const outboundSchema = PreferenceOverride$outboundSchema; - /** @deprecated use `PreferenceOverride$Outbound` instead. */ - export type Outbound = PreferenceOverride$Outbound; -} - -export function preferenceOverrideToJSON( - preferenceOverride: PreferenceOverride, -): string { - return JSON.stringify( - PreferenceOverride$outboundSchema.parse(preferenceOverride), - ); -} - -export function preferenceOverrideFromJSON( - jsonString: string, -): SafeParseResult { - return safeParse( - jsonString, - (x) => PreferenceOverride$inboundSchema.parse(JSON.parse(x)), - `Failed to parse 'PreferenceOverride' from JSON`, - ); -} diff --git a/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts b/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts deleted file mode 100644 index b8b8ee8d714..00000000000 --- a/libs/internal-sdk/src/models/components/preferenceoverridesourceenum.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. - */ - -import * as z from "zod"; -import { ClosedEnum } from "../../types/enums.js"; - -/** - * The source of the override - */ -export const PreferenceOverrideSourceEnum = { - Subscriber: "subscriber", - Template: "template", - WorkflowOverride: "workflowOverride", -} as const; -/** - * The source of the override - */ -export type PreferenceOverrideSourceEnum = ClosedEnum< - typeof PreferenceOverrideSourceEnum ->; - -/** @internal */ -export const PreferenceOverrideSourceEnum$inboundSchema: z.ZodNativeEnum< - typeof PreferenceOverrideSourceEnum -> = z.nativeEnum(PreferenceOverrideSourceEnum); - -/** @internal */ -export const PreferenceOverrideSourceEnum$outboundSchema: z.ZodNativeEnum< - typeof PreferenceOverrideSourceEnum -> = PreferenceOverrideSourceEnum$inboundSchema; - -/** - * @internal - * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. - */ -export namespace PreferenceOverrideSourceEnum$ { - /** @deprecated use `PreferenceOverrideSourceEnum$inboundSchema` instead. */ - export const inboundSchema = PreferenceOverrideSourceEnum$inboundSchema; - /** @deprecated use `PreferenceOverrideSourceEnum$outboundSchema` instead. */ - export const outboundSchema = PreferenceOverrideSourceEnum$outboundSchema; -} diff --git a/libs/internal-sdk/src/models/components/triggereventtoallrequestdto.ts b/libs/internal-sdk/src/models/components/triggereventtoallrequestdto.ts index 66756d3ef5a..3c51b37b947 100644 --- a/libs/internal-sdk/src/models/components/triggereventtoallrequestdto.ts +++ b/libs/internal-sdk/src/models/components/triggereventtoallrequestdto.ts @@ -22,7 +22,7 @@ import { /** * This could be used to override provider specific configurations */ -export type Overrides = {}; +export type TriggerEventToAllRequestDtoOverrides = {}; /** * It is used to display the Avatar of the provided actor's subscriber id or actor object. @@ -56,7 +56,7 @@ export type TriggerEventToAllRequestDto = { /** * This could be used to override provider specific configurations */ - overrides?: Overrides | undefined; + overrides?: TriggerEventToAllRequestDtoOverrides | undefined; /** * A unique identifier for this transaction, we will generated a UUID if not provided. */ @@ -78,46 +78,55 @@ export type TriggerEventToAllRequestDto = { }; /** @internal */ -export const Overrides$inboundSchema: z.ZodType< - Overrides, +export const TriggerEventToAllRequestDtoOverrides$inboundSchema: z.ZodType< + TriggerEventToAllRequestDtoOverrides, z.ZodTypeDef, unknown > = z.object({}); /** @internal */ -export type Overrides$Outbound = {}; +export type TriggerEventToAllRequestDtoOverrides$Outbound = {}; /** @internal */ -export const Overrides$outboundSchema: z.ZodType< - Overrides$Outbound, +export const TriggerEventToAllRequestDtoOverrides$outboundSchema: z.ZodType< + TriggerEventToAllRequestDtoOverrides$Outbound, z.ZodTypeDef, - Overrides + TriggerEventToAllRequestDtoOverrides > = z.object({}); /** * @internal * @deprecated This namespace will be removed in future versions. Use schemas and types that are exported directly from this module. */ -export namespace Overrides$ { - /** @deprecated use `Overrides$inboundSchema` instead. */ - export const inboundSchema = Overrides$inboundSchema; - /** @deprecated use `Overrides$outboundSchema` instead. */ - export const outboundSchema = Overrides$outboundSchema; - /** @deprecated use `Overrides$Outbound` instead. */ - export type Outbound = Overrides$Outbound; +export namespace TriggerEventToAllRequestDtoOverrides$ { + /** @deprecated use `TriggerEventToAllRequestDtoOverrides$inboundSchema` instead. */ + export const inboundSchema = + TriggerEventToAllRequestDtoOverrides$inboundSchema; + /** @deprecated use `TriggerEventToAllRequestDtoOverrides$outboundSchema` instead. */ + export const outboundSchema = + TriggerEventToAllRequestDtoOverrides$outboundSchema; + /** @deprecated use `TriggerEventToAllRequestDtoOverrides$Outbound` instead. */ + export type Outbound = TriggerEventToAllRequestDtoOverrides$Outbound; } -export function overridesToJSON(overrides: Overrides): string { - return JSON.stringify(Overrides$outboundSchema.parse(overrides)); +export function triggerEventToAllRequestDtoOverridesToJSON( + triggerEventToAllRequestDtoOverrides: TriggerEventToAllRequestDtoOverrides, +): string { + return JSON.stringify( + TriggerEventToAllRequestDtoOverrides$outboundSchema.parse( + triggerEventToAllRequestDtoOverrides, + ), + ); } -export function overridesFromJSON( +export function triggerEventToAllRequestDtoOverridesFromJSON( jsonString: string, -): SafeParseResult { +): SafeParseResult { return safeParse( jsonString, - (x) => Overrides$inboundSchema.parse(JSON.parse(x)), - `Failed to parse 'Overrides' from JSON`, + (x) => + TriggerEventToAllRequestDtoOverrides$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'TriggerEventToAllRequestDtoOverrides' from JSON`, ); } @@ -234,7 +243,8 @@ export const TriggerEventToAllRequestDto$inboundSchema: z.ZodType< > = z.object({ name: z.string(), payload: z.record(z.any()), - overrides: z.lazy(() => Overrides$inboundSchema).optional(), + overrides: z.lazy(() => TriggerEventToAllRequestDtoOverrides$inboundSchema) + .optional(), transactionId: z.string().optional(), actor: z.union([SubscriberPayloadDto$inboundSchema, z.string()]).optional(), tenant: z.union([TenantPayloadDto$inboundSchema, z.string()]).optional(), @@ -244,7 +254,7 @@ export const TriggerEventToAllRequestDto$inboundSchema: z.ZodType< export type TriggerEventToAllRequestDto$Outbound = { name: string; payload: { [k: string]: any }; - overrides?: Overrides$Outbound | undefined; + overrides?: TriggerEventToAllRequestDtoOverrides$Outbound | undefined; transactionId?: string | undefined; actor?: SubscriberPayloadDto$Outbound | string | undefined; tenant?: TenantPayloadDto$Outbound | string | undefined; @@ -258,7 +268,8 @@ export const TriggerEventToAllRequestDto$outboundSchema: z.ZodType< > = z.object({ name: z.string(), payload: z.record(z.any()), - overrides: z.lazy(() => Overrides$outboundSchema).optional(), + overrides: z.lazy(() => TriggerEventToAllRequestDtoOverrides$outboundSchema) + .optional(), transactionId: z.string().optional(), actor: z.union([SubscriberPayloadDto$outboundSchema, z.string()]).optional(), tenant: z.union([TenantPayloadDto$outboundSchema, z.string()]).optional(), diff --git a/libs/internal-sdk/src/models/components/workflowpreferencedto.ts b/libs/internal-sdk/src/models/components/workflowpreferencedto.ts index 115710ac54f..8b288bff7e0 100644 --- a/libs/internal-sdk/src/models/components/workflowpreferencedto.ts +++ b/libs/internal-sdk/src/models/components/workflowpreferencedto.ts @@ -7,17 +7,17 @@ import { safeParse } from "../../lib/schemas.js"; import { Result as SafeParseResult } from "../../types/fp.js"; import { SDKValidationError } from "../errors/sdkvalidationerror.js"; import { - PreferenceChannelsDto, - PreferenceChannelsDto$inboundSchema, - PreferenceChannelsDto$Outbound, - PreferenceChannelsDto$outboundSchema, -} from "./preferencechannelsdto.js"; + Overrides, + Overrides$inboundSchema, + Overrides$Outbound, + Overrides$outboundSchema, +} from "./overrides.js"; import { - PreferenceOverride, - PreferenceOverride$inboundSchema, - PreferenceOverride$Outbound, - PreferenceOverride$outboundSchema, -} from "./preferenceoverride.js"; + PreferenceChannels, + PreferenceChannels$inboundSchema, + PreferenceChannels$Outbound, + PreferenceChannels$outboundSchema, +} from "./preferencechannels.js"; import { WorkflowInfoDto, WorkflowInfoDto$inboundSchema, @@ -33,11 +33,11 @@ export type WorkflowPreferenceDto = { /** * Channel-specific preference settings for this workflow */ - channels: PreferenceChannelsDto; + channels: PreferenceChannels; /** * List of preference overrides */ - overrides: Array; + overrides: Array; /** * Workflow information */ @@ -51,16 +51,16 @@ export const WorkflowPreferenceDto$inboundSchema: z.ZodType< unknown > = z.object({ enabled: z.boolean(), - channels: PreferenceChannelsDto$inboundSchema, - overrides: z.array(PreferenceOverride$inboundSchema), + channels: PreferenceChannels$inboundSchema, + overrides: z.array(Overrides$inboundSchema), workflow: WorkflowInfoDto$inboundSchema, }); /** @internal */ export type WorkflowPreferenceDto$Outbound = { enabled: boolean; - channels: PreferenceChannelsDto$Outbound; - overrides: Array; + channels: PreferenceChannels$Outbound; + overrides: Array; workflow: WorkflowInfoDto$Outbound; }; @@ -71,8 +71,8 @@ export const WorkflowPreferenceDto$outboundSchema: z.ZodType< WorkflowPreferenceDto > = z.object({ enabled: z.boolean(), - channels: PreferenceChannelsDto$outboundSchema, - overrides: z.array(PreferenceOverride$outboundSchema), + channels: PreferenceChannels$outboundSchema, + overrides: z.array(Overrides$outboundSchema), workflow: WorkflowInfoDto$outboundSchema, });