Skip to content

Commit

Permalink
feat: add ability to set channels during trigger (#5541)
Browse files Browse the repository at this point in the history
* feat: add ability to set channels during trigger

* fix: tests

* fix: remove duplicate check for subscriber needs update
  • Loading branch information
scopsy authored May 12, 2024
1 parent 8e6b9c5 commit e20024d
Show file tree
Hide file tree
Showing 23 changed files with 397 additions and 115 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

161 changes: 144 additions & 17 deletions apps/api/src/app/events/e2e/trigger-event.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,41 @@ import axios, { AxiosResponse } from 'axios';
import { v4 as uuid } from 'uuid';
import { differenceInMilliseconds, subDays } from 'date-fns';
import {
EnvironmentRepository,
ExecutionDetailsRepository,
IntegrationRepository,
JobRepository,
JobStatusEnum,
MessageRepository,
NotificationRepository,
NotificationTemplateEntity,
NotificationTemplateRepository,
SubscriberEntity,
SubscriberRepository,
JobRepository,
JobStatusEnum,
IntegrationRepository,
ExecutionDetailsRepository,
EnvironmentRepository,
TenantRepository,
NotificationTemplateRepository,
} from '@novu/dal';
import { UserSession, SubscribersService, WorkflowOverrideService } from '@novu/testing';
import { SubscribersService, UserSession, WorkflowOverrideService } from '@novu/testing';
import {
ActorTypeEnum,
ChannelTypeEnum,
ChatProviderIdEnum,
DEFAULT_MESSAGE_GENERIC_RETENTION_DAYS,
DEFAULT_MESSAGE_IN_APP_RETENTION_DAYS,
DelayTypeEnum,
DigestUnitEnum,
EmailBlockTypeEnum,
EmailProviderIdEnum,
FieldLogicalOperatorEnum,
FieldOperatorEnum,
FilterPartTypeEnum,
StepTypeEnum,
IEmailBlock,
InAppProviderIdEnum,
ISubscribersDefine,
TemplateVariableTypeEnum,
EmailProviderIdEnum,
SmsProviderIdEnum,
DigestUnitEnum,
DelayTypeEnum,
PreviousStepTypeEnum,
InAppProviderIdEnum,
DEFAULT_MESSAGE_IN_APP_RETENTION_DAYS,
DEFAULT_MESSAGE_GENERIC_RETENTION_DAYS,
ActorTypeEnum,
SmsProviderIdEnum,
StepTypeEnum,
SystemAvatarIconEnum,
TemplateVariableTypeEnum,
} from '@novu/shared';
import { EmailEventStatusEnum } from '@novu/stateless';
import { createTenant } from '../../tenant/e2e/create-tenant.e2e';
Expand Down Expand Up @@ -1068,6 +1069,132 @@ describe(`Trigger event - ${eventTriggerPath} (POST)`, function () {
expect(updatedSubscriber?.locale).to.equal(payload.locale);
});

describe('Subscriber channels', function () {
it('should set a new subscriber with channels array', async function () {
const subscriberId = SubscriberRepository.createObjectId();
const payload: ISubscribersDefine = {
subscriberId,
firstName: 'Test Name',
lastName: 'Last of name',
email: undefined,
locale: 'en',
channels: [
{
providerId: ChatProviderIdEnum.Slack,
credentials: {
webhookUrl: 'https://slack.com/webhook/test',
deviceTokens: ['1', '2'],
},
},
],
};

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: {
...payload,
},
payload: {
urlVar: '/test/url/path',
},
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs();

const createdSubscriber = await subscriberRepository.findBySubscriberId(session.environment._id, subscriberId);

expect(createdSubscriber?.channels?.length).to.equal(1);
expect(createdSubscriber?.channels[0]?.providerId).to.equal(ChatProviderIdEnum.Slack);
expect(createdSubscriber?.channels[0]?.credentials?.webhookUrl).to.equal('https://slack.com/webhook/test');
expect(createdSubscriber?.channels[0]?.credentials?.deviceTokens.length).to.equal(2);
});

it('should update a subscribers channels array', async function () {
const subscriberId = SubscriberRepository.createObjectId();
const payload: ISubscribersDefine = {
subscriberId,
firstName: 'Test Name',
lastName: 'Last of name',
email: undefined,
locale: 'en',
channels: [
{
providerId: ChatProviderIdEnum.Slack,
credentials: {
webhookUrl: 'https://slack.com/webhook/test',
},
},
],
};

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: {
...payload,
},
payload: {
urlVar: '/test/url/path',
},
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs();
const createdSubscriber = await subscriberRepository.findBySubscriberId(session.environment._id, subscriberId);

expect(createdSubscriber?.subscriberId).to.equal(subscriberId);
expect(createdSubscriber?.channels?.length).to.equal(1);

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: {
...payload,
channels: [
{
providerId: ChatProviderIdEnum.Slack,
credentials: {
webhookUrl: 'https://slack.com/webhook/test2',
},
},
],
},
payload: {
urlVar: '/test/url/path',
},
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs();

const updatedSubscriber = await subscriberRepository.findBySubscriberId(session.environment._id, subscriberId);

expect(updatedSubscriber?.channels?.length).to.equal(1);
expect(updatedSubscriber?.channels[0]?.providerId).to.equal(ChatProviderIdEnum.Slack);
expect(updatedSubscriber?.channels[0]?.credentials?.webhookUrl).to.equal('https://slack.com/webhook/test2');
});
});

it('should not unset a subscriber email', async function () {
const subscriberId = SubscriberRepository.createObjectId();
const payload = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsDefined, IsObject, IsOptional, IsString } from 'class-validator';
import { ChatProviderIdEnum, PushProviderIdEnum } from '@novu/shared';
import { ChatProviderIdEnum, ISubscriberChannel, PushProviderIdEnum } from '@novu/shared';

import { ChannelCredentials } from '../../shared/dtos/subscriber-channel';

export class UpdateSubscriberChannelRequestDto {
export class UpdateSubscriberChannelRequestDto implements ISubscriberChannel {
@ApiProperty({
enum: { ...ChatProviderIdEnum, ...PushProviderIdEnum },
description: 'The provider identifier for the credentials',
Expand Down
5 changes: 3 additions & 2 deletions apps/api/src/app/subscribers/subscribers.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {
CreateSubscriberCommand,
UpdateSubscriber,
UpdateSubscriberCommand,
OAuthHandlerEnum,
UpdateSubscriberChannel,
UpdateSubscriberChannelCommand,
} from '@novu/application-generic';
import { ApiOperation, ApiTags, ApiParam } from '@nestjs/swagger';
import {
Expand All @@ -45,7 +48,6 @@ import {
UpdateSubscriberGlobalPreferencesRequestDto,
UpdateSubscriberRequestDto,
} from './dtos';
import { UpdateSubscriberChannel, UpdateSubscriberChannelCommand } from './usecases/update-subscriber-channel';
import { GetSubscribers, GetSubscribersCommand } from './usecases/get-subscribers';
import { GetSubscriber, GetSubscriberCommand } from './usecases/get-subscriber';
import { GetPreferencesByLevelCommand } from './usecases/get-preferences-by-level/get-preferences-by-level.command';
Expand Down Expand Up @@ -77,7 +79,6 @@ import { GetSubscribersDto } from './dtos/get-subscribers.dto';
import { GetInAppNotificationsFeedForSubscriberDto } from './dtos/get-in-app-notification-feed-for-subscriber.dto';
import { ApiCommonResponses, ApiResponse, ApiNoContentResponse } from '../shared/framework/response.decorator';
import { ChatOauthCallbackRequestDto, ChatOauthRequestDto } from './dtos/chat-oauth-request.dto';
import { OAuthHandlerEnum } from './types';
import { ChatOauthCallback } from './usecases/chat-oauth-callback/chat-oauth-callback.usecase';
import { ChatOauthCallbackCommand } from './usecases/chat-oauth-callback/chat-oauth-callback.command';
import { ChatOauth } from './usecases/chat-oauth/chat-oauth.usecase';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { IntegrationRepository, SubscriberRepository } from '@novu/dal';
import { SubscribersService, UserSession } from '@novu/testing';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import { SharedModule } from '../../../shared/shared.module';
import { ChannelTypeEnum, ChatProviderIdEnum, PushProviderIdEnum } from '@novu/shared';
import { UpdateSubscriberChannel } from './update-subscriber-channel.usecase';
import { UpdateSubscriberChannelCommand } from './update-subscriber-channel.command';
import { OAuthHandlerEnum } from '../../types';
import { faker } from '@faker-js/faker';
import { OAuthHandlerEnum, UpdateSubscriberChannel, UpdateSubscriberChannelCommand } from '@novu/application-generic';
import { SharedModule } from '../../shared/shared.module';

describe('Update Subscriber channel credentials', function () {
let updateSubscriberChannelUsecase: UpdateSubscriberChannel;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import axios from 'axios';

import { CreateSubscriber, CreateSubscriberCommand, decryptCredentials } from '@novu/application-generic';
import {
CreateSubscriber,
CreateSubscriberCommand,
decryptCredentials,
OAuthHandlerEnum,
IChannelCredentialsCommand,
UpdateSubscriberChannel,
UpdateSubscriberChannelCommand,
} from '@novu/application-generic';
import { ICredentialsDto } from '@novu/shared';
import {
ChannelTypeEnum,
Expand All @@ -12,13 +20,7 @@ import {
} from '@novu/dal';

import { ChatOauthCallbackCommand } from './chat-oauth-callback.command';
import {
IChannelCredentialsCommand,
UpdateSubscriberChannel,
UpdateSubscriberChannelCommand,
} from '../update-subscriber-channel';
import { ApiException } from '../../../shared/exceptions/api.exception';
import { OAuthHandlerEnum } from '../../types';
import { validateEncryption } from '../chat-oauth/chat-oauth.usecase';

@Injectable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import { SharedModule } from '../../../shared/shared.module';
import { ChatProviderIdEnum, PushProviderIdEnum } from '@novu/shared';
import { DeleteSubscriberCredentials } from './delete-subscriber-credentials.usecase';
import { DeleteSubscriberCredentialsCommand } from './delete-subscriber-credentials.command';
import { UpdateSubscriberChannel } from '../update-subscriber-channel/update-subscriber-channel.usecase';
import { UpdateSubscriberChannelCommand } from '../update-subscriber-channel/update-subscriber-channel.command';
import { OAuthHandlerEnum } from '../../types';
import { GetSubscriber } from '../get-subscriber/get-subscriber.usecase';
import { OAuthHandlerEnum, UpdateSubscriberChannel, UpdateSubscriberChannelCommand } from '@novu/application-generic';

describe('Delete subscriber provider credentials', function () {
let updateSubscriberChannelUsecase: UpdateSubscriberChannel;
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app/subscribers/usecases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
UpdateSubscriber,
CreateSubscriber,
GetSubscriberGlobalPreference,
UpdateSubscriberChannel,
} from '@novu/application-generic';

import { GetSubscribers } from './get-subscribers';
Expand All @@ -12,7 +13,6 @@ import { GetPreferencesByLevel } from './get-preferences-by-level/get-preference
import { RemoveSubscriber } from './remove-subscriber';
import { SearchByExternalSubscriberIds } from './search-by-external-subscriber-ids';
import { UpdatePreference } from './update-preference/update-preference.usecase';
import { UpdateSubscriberChannel } from './update-subscriber-channel';
import { UpdateSubscriberPreference } from './update-subscriber-preference';
import { UpdateSubscriberOnlineFlag } from './update-subscriber-online-flag';
import { ChatOauth } from './chat-oauth/chat-oauth.usecase';
Expand Down

This file was deleted.

2 changes: 2 additions & 0 deletions apps/worker/src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
StorageHelperService,
storageService,
UpdateSubscriber,
UpdateSubscriberChannel,
UpdateTenant,
} from '@novu/application-generic';

Expand Down Expand Up @@ -114,6 +115,7 @@ const PROVIDERS = [
StorageHelperService,
storageService,
UpdateSubscriber,
UpdateSubscriberChannel,
UpdateTenant,
GetTenant,
CreateTenant,
Expand Down
Loading

0 comments on commit e20024d

Please sign in to comment.