Skip to content

Commit

Permalink
Merge pull request #1 from novuhq/next
Browse files Browse the repository at this point in the history
a
  • Loading branch information
nick2432 authored Oct 13, 2023
2 parents 4e462d0 + c7f68d3 commit 87d8400
Show file tree
Hide file tree
Showing 31 changed files with 514 additions and 83 deletions.
4 changes: 3 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,9 @@
"autodocs",
"stackalt",
"mediumdark",
"Docgen"
"Docgen",
"clicksend",
"Clicksend"
],
"flagWords": [],
"patterns": [
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ We are excited to launch the complete Novu API and admin panel. Want to give it
npx novu init
```

After setting up your account using the cloud or docker version you can trigger the API using the `@novu/node` package.
After setting up your account using the cloud or docker version, you can trigger the API using the `@novu/node` package.

For API documentation and reference, please visit [Novu API Reference](https://docs.novu.co/api-reference/events/trigger-event).

To get started with the Node.js package, you can install it using npm:

```bash
npm install @novu/node
Expand Down
144 changes: 144 additions & 0 deletions apps/api/src/app/events/e2e/trigger-event.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
InAppProviderIdEnum,
MESSAGE_IN_APP_RETENTION_DAYS,
MESSAGE_GENERIC_RETENTION_DAYS,
ActorTypeEnum,
SystemAvatarIconEnum,
} from '@novu/shared';
import { EmailEventStatusEnum } from '@novu/stateless';
import { createTenant } from '../../tenant/e2e/create-tenant.e2e';
Expand Down Expand Up @@ -2022,6 +2024,148 @@ describe(`Trigger event - ${eventTriggerPath} (POST)`, function () {
// axiosPostStub.restore();
});

describe('in-app avatar', () => {
it('should send the message with choosed system avatar', async () => {
const firstStepUuid = uuid();
template = await session.createTemplate({
steps: [
{
type: StepTypeEnum.IN_APP,
content: 'Hello world!',
uuid: firstStepUuid,
actor: {
type: ActorTypeEnum.SYSTEM_ICON,
data: SystemAvatarIconEnum.WARNING,
},
},
],
});

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: [subscriber.subscriberId],
payload: {},
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs(template?._id, true, 1);

const messages = await messageRepository.find({
_environmentId: session.environment._id,
_subscriberId: subscriber._id,
channel: StepTypeEnum.IN_APP,
});

expect(messages.length).to.equal(1);
expect(messages[0].actor).to.be.ok;
expect(messages[0].actor?.type).to.eq(ActorTypeEnum.SYSTEM_ICON);
expect(messages[0].actor?.data).to.eq(SystemAvatarIconEnum.WARNING);
});

it('should send the message with custom system avatar url', async () => {
const firstStepUuid = uuid();
const avatarUrl = 'https://gravatar.com/avatar/5246ec47a6a90ef2bcd29f0ef7d2faa6?s=400&d=robohash&r=x';

template = await session.createTemplate({
steps: [
{
type: StepTypeEnum.IN_APP,
content: 'Hello world!',
uuid: firstStepUuid,
actor: {
type: ActorTypeEnum.SYSTEM_CUSTOM,
data: avatarUrl,
},
},
],
});

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: [subscriber.subscriberId],
payload: {},
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs(template?._id, true, 1);

const messages = await messageRepository.find({
_environmentId: session.environment._id,
_subscriberId: subscriber._id,
channel: StepTypeEnum.IN_APP,
});

expect(messages.length).to.equal(1);
expect(messages[0].actor).to.be.ok;
expect(messages[0].actor?.type).to.eq(ActorTypeEnum.SYSTEM_CUSTOM);
expect(messages[0].actor?.data).to.eq(avatarUrl);
});

it('should send the message with the actor avatar', async () => {
const firstStepUuid = uuid();
const avatarUrl = 'https://gravatar.com/avatar/5246ec47a6a90ef2bcd29f0ef7d2faa6?s=400&d=robohash&r=x';

const actor = await subscriberService.createSubscriber({ avatar: avatarUrl });

template = await session.createTemplate({
steps: [
{
type: StepTypeEnum.IN_APP,
content: 'Hello world!',
uuid: firstStepUuid,
actor: {
type: ActorTypeEnum.USER,
data: null,
},
},
],
});

await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
{
name: template.triggers[0].identifier,
to: [subscriber.subscriberId],
payload: {},
actor: actor.subscriberId,
},
{
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
}
);

await session.awaitRunningJobs(template?._id, true, 1);

const messages = await messageRepository.find({
_environmentId: session.environment._id,
_subscriberId: subscriber._id,
channel: StepTypeEnum.IN_APP,
});

expect(messages.length).to.equal(1);
expect(messages[0].actor).to.be.ok;
expect(messages[0].actor?.type).to.eq(ActorTypeEnum.USER);
expect(messages[0].actor?.data).to.eq(null);
expect(messages[0]._actorId).to.eq(actor._id);
});
});

describe('seen/read filter', () => {
it('should filter in app seen/read step', async function () {
const firstStepUuid = uuid();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions libs/shared/src/consts/providers/channels/sms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
termiiConfig,
africasTalkingConfig,
sendchampConfig,
clickSendConfig,
} from '../credentials';
import { SmsProviderIdEnum } from '../provider.enum';

Expand Down Expand Up @@ -179,4 +180,12 @@ export const smsProviders: IProviderConfig[] = [
docReference: 'https://docs.novu.co/channels-and-providers/sms/sendchamp',
logoFileName: { light: 'sendchamp.svg', dark: 'sendchamp.svg' },
},
{
id: SmsProviderIdEnum.Clicksend,
displayName: `Clicksend`,
channel: ChannelTypeEnum.SMS,
credentials: clickSendConfig,
docReference: 'https://developers.clicksend.com/docs/rest/v3/?javascript--nodejs#send-sms',
logoFileName: { light: 'clicksend.png', dark: 'clicksend.png' },
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -750,3 +750,20 @@ export const sendchampConfig: IConfigCredentials[] = [
},
...smsConfigBase,
];

export const clickSendConfig: IConfigCredentials[] = [
{
key: CredentialsKeyEnum.User,
displayName: 'Username',
description: 'Your Clicksend API username',
type: 'text',
required: true,
},
{
key: CredentialsKeyEnum.ApiKey,
displayName: 'API Key',
type: 'text',
required: true,
},
...smsConfigBase,
];
1 change: 1 addition & 0 deletions libs/shared/src/consts/providers/provider.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export enum SmsProviderIdEnum {
AfricasTalking = 'africas-talking',
Novu = 'novu-sms',
Sendchamp = 'sendchamp',
Clicksend = 'clicksend',
}

export enum ChatProviderIdEnum {
Expand Down
5 changes: 5 additions & 0 deletions libs/testing/src/create-notification-template.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DigestTypeEnum,
DigestUnitEnum,
DelayTypeEnum,
ActorTypeEnum,
} from '@novu/shared';
import { NotificationTemplateEntity, StepFilter } from '@novu/dal';

Expand Down Expand Up @@ -43,5 +44,9 @@ export interface CreateTemplatePayload extends Omit<NotificationTemplateEntity,
backoffAmount?: number;
updateMode?: boolean;
};
actor?: {
type: ActorTypeEnum;
data: string | null;
};
}[];
}
20 changes: 10 additions & 10 deletions libs/testing/src/notification-template.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker';
import { ChannelCTATypeEnum, ChannelTypeEnum } from '@novu/shared';
import { ChannelCTATypeEnum, EmailBlockTypeEnum, StepTypeEnum, TemplateVariableTypeEnum } from '@novu/shared';
import {
MessageTemplateRepository,
NotificationGroupRepository,
Expand Down Expand Up @@ -31,10 +31,9 @@ export class NotificationTemplateService {
_environmentId: this.environmentId,
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const steps: any[] = override?.steps ?? [
const steps: CreateTemplatePayload['steps'] = override?.steps ?? [
{
type: ChannelTypeEnum.IN_APP,
type: StepTypeEnum.IN_APP,
content: 'Test content for <b>{{firstName}}</b>',
cta: {
type: ChannelCTATypeEnum.REDIRECT,
Expand All @@ -47,20 +46,20 @@ export class NotificationTemplateService {
defaultValue: '',
name: 'firstName',
required: false,
type: 'String',
type: TemplateVariableTypeEnum.STRING,
},
],
},
{
type: ChannelTypeEnum.EMAIL,
type: StepTypeEnum.EMAIL,
subject: 'Password reset',
content: [
{
type: 'text',
type: EmailBlockTypeEnum.TEXT,
content: 'This are the text contents of the template for {{firstName}}',
},
{
type: 'button',
type: EmailBlockTypeEnum.BUTTON,
content: 'SIGN UP',
url: 'https://url-of-app.com/{{urlVariable}}',
},
Expand All @@ -70,7 +69,7 @@ export class NotificationTemplateService {
defaultValue: '',
name: 'firstName',
required: false,
type: 'String',
type: TemplateVariableTypeEnum.STRING,
},
],
},
Expand All @@ -87,6 +86,7 @@ export class NotificationTemplateService {
subject: message.subject,
title: message.title,
name: message.name,
actor: message.actor,
_feedId: override.noFeedId ? undefined : feeds[0]._id,
_layoutId: override.noLayoutId ? undefined : layouts[0]._id,
_creatorId: this.userId,
Expand All @@ -99,7 +99,7 @@ export class NotificationTemplateService {
filters: message.filters,
_templateId: saved._id,
active: message.active,
metadata: message.metadata,
metadata: message.metadata as any,
replyCallback: message.replyCallback,
uuid: message.uuid,
});
Expand Down
1 change: 1 addition & 0 deletions packages/application-generic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@novu/termii": "^0.20.0-alpha.1",
"@novu/testing": "^0.20.0-alpha.1",
"@novu/twilio": "^0.20.0-alpha.1",
"@novu/clicksend": "^0.20.0-alpha.1",
"@sentry/node": "^7.12.1",
"analytics-node": "^6.2.0",
"bullmq": "^3.10.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ChannelTypeEnum, ICredentials } from '@novu/shared';
import { ClicksendSmsProvider } from '@novu/clicksend';
import { BaseSmsHandler } from './base.handler';

export class ClicksendSmsHandler extends BaseSmsHandler {
constructor() {
super('clicksend', ChannelTypeEnum.SMS);
}

buildProvider(credentials: ICredentials) {
const config = {
username: credentials.user,
apiKey: credentials.apiKey,
};

this.provider = new ClicksendSmsProvider(config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export * from './kannel.handler';
export * from './sms-central.handler';
export * from './africas-talking.handler';
export * from './sendchamp.handler';
export * from './clicksend.handler';
export * from './novu.handler';
2 changes: 2 additions & 0 deletions packages/application-generic/src/factories/sms/sms.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
SmsCentralHandler,
AfricasTalkingSmsHandler,
SendchampSmsHandler,
ClicksendSmsHandler,
NovuSmsHandler,
} from './handlers';

Expand All @@ -40,6 +41,7 @@ export class SmsFactory implements ISmsFactory {
new SmsCentralHandler(),
new AfricasTalkingSmsHandler(),
new SendchampSmsHandler(),
new ClicksendSmsHandler(),
new NovuSmsHandler(),
];

Expand Down
Loading

0 comments on commit 87d8400

Please sign in to comment.