Skip to content

Commit

Permalink
Merge branch 'next' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
djabarovgeorge authored Oct 23, 2023
2 parents 22d3055 + a930525 commit 38b009d
Show file tree
Hide file tree
Showing 25 changed files with 188 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .commitlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"subject-case": [
2,
"always",
["sentence-case", "start-case", "pascal-case", "upper-case", "lower-case"]
["sentence-case", "start-case", "pascal-case", "upper-case", "lower-case", "camel-case"]
],
"type-enum": [
2,
Expand Down
14 changes: 3 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div align="center">
<a href="https://novu.co" target="_blank">
<a href="https://novu.co?utm_source=github" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/2233092/213641039-220ac15f-f367-4d13-9eaf-56e79433b8c1.png">
![Novu Logo]<img src="https://user-images.githubusercontent.com/2233092/213641043-3bbb3f21-3c53-4e67-afe5-755aeb222159.png" width="280"/>
Expand All @@ -12,15 +12,7 @@
<div align="center">
The ultimate service for managing multi-channel notifications with a single API.
</div>

<h1 align="center">🎉 Novu is live on Product Hunt! 🎉</h1>
<p align="center">
We are on Product Hunt and would appreciate any help you can give us 🩷<br />
BONUS: watch our video, answer the riddle, and win awesome swag!<br /><br />
<a href="https://www.producthunt.com/posts/novu?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-novu" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=420313&theme=light" The&#0032;open&#0045;source&#0032;notification&#0032;infrastructure&#0032;for&#0032;developers | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" alt-text="Novu is featured on Product Hunt" /></a>
<br /><br />
</p>

<h1 align="center">🎉 We're participating in Hacktoberfest 2023! 🎉</h1>

Are you interested in participating in Hacktoberfest? We extend a warm invitation! You also get the opportunity to win some swag 😁
Expand Down Expand Up @@ -244,7 +236,7 @@ We are more than happy to help you. If you are getting any errors or facing prob

## 🔗 Links

- [Home page](https://novu.co/)
- [Home page](https://novu.co?utm_source=github)
- [Contribution Guidelines](https://github.com/novuhq/novu/blob/main/CONTRIBUTING.md)
- [Run Novu Locally](https://docs.novu.co/community/run-in-local-machine)

Expand All @@ -256,6 +248,6 @@ Novu is licensed under the MIT License - see the [LICENSE](https://github.com/no

Thanks a lot for spending your time helping Novu grow. Keep rocking 🥂

<a href="https://novu.co/contributors">
<a href="https://novu.co/contributors?utm_source=github">
<img src="https://contributors-img.web.app/image?repo=novuhq/novu" alt="Contributors"/>
</a>
3 changes: 3 additions & 0 deletions apps/api/src/app/events/dtos/trigger-event-request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export class TriggerEventRequestDto {
],
})
@IsOptional()
@ValidateIf((_, value) => typeof value !== 'string')
@ValidateNested()
@Type(() => SubscriberPayloadDto)
actor?: TriggerRecipientSubscriber;

@ApiProperty({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export class TriggerEventToAllRequestDto {
],
})
@IsOptional()
@ValidateIf((_, value) => typeof value !== 'string')
@ValidateNested()
@Type(() => SubscriberPayloadDto)
actor?: TriggerRecipientSubscriber;

@ApiProperty({
Expand Down
23 changes: 14 additions & 9 deletions apps/api/src/app/events/e2e/delay-events.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ describe('Trigger event - Delay triggered events - /v1/events/trigger (POST)', f
let standardQueueService: StandardQueueService;
const messageRepository = new MessageRepository();

const triggerEvent = async (payload, transactionId?: string, overrides = {}) => {
const triggerEvent = async (payload, transactionId?: string, overrides = {}, to = [subscriber.subscriberId]) => {
await axiosInstance.post(
`${session.serverUrl}/v1/events/trigger`,
{
transactionId,
name: template.triggers[0].identifier,
to: [subscriber.subscriberId],
to,
payload,
overrides,
},
Expand Down Expand Up @@ -330,6 +330,8 @@ describe('Trigger event - Delay triggered events - /v1/events/trigger (POST)', f
});

it('should be able to cancel delay', async function () {
const secondSubscriber = await subscriberService.createSubscriber();

const id = MessageRepository.createObjectId();
template = await session.createTemplate({
steps: [
Expand All @@ -342,7 +344,7 @@ describe('Trigger event - Delay triggered events - /v1/events/trigger (POST)', f
content: '',
metadata: {
unit: DigestUnitEnum.SECONDS,
amount: 0.1,
amount: 5,
type: DelayTypeEnum.REGULAR,
},
},
Expand All @@ -357,17 +359,19 @@ describe('Trigger event - Delay triggered events - /v1/events/trigger (POST)', f
{
customVar: 'Testing of User Name',
},
id
id,
{},
[subscriber.subscriberId, secondSubscriber.subscriberId]
);

await session.awaitRunningJobs(template?._id, true, 1);
await session.awaitRunningJobs(template?._id, true, 2);
await axiosInstance.delete(`${session.serverUrl}/v1/events/trigger/${id}`, {
headers: {
authorization: `ApiKey ${session.apiKey}`,
},
});

let delayedJob = await jobRepository.findOne({
let delayedJobs = await jobRepository.find({
_environmentId: session.environment._id,
_templateId: template._id,
type: StepTypeEnum.DELAY,
Expand All @@ -380,14 +384,15 @@ describe('Trigger event - Delay triggered events - /v1/events/trigger (POST)', f
transactionId: id,
});

expect(pendingJobs).to.equal(1);
expect(pendingJobs).to.equal(2);

delayedJob = await jobRepository.findOne({
delayedJobs = await jobRepository.find({
_environmentId: session.environment._id,
_templateId: template._id,
type: StepTypeEnum.DELAY,
transactionId: id,
});
expect(delayedJob!.status).to.equal(JobStatusEnum.CANCELED);
expect(delayedJobs[0]!.status).to.equal(JobStatusEnum.CANCELED);
expect(delayedJobs[1]!.status).to.equal(JobStatusEnum.CANCELED);
});
});
42 changes: 41 additions & 1 deletion apps/api/src/app/events/e2e/trigger-event.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,44 @@ describe(`Trigger event - ${eventTriggerPath} (POST)`, function () {
expect(message!.subject).to.equal('Test email a subject nested');
});

it('should trigger E-Mail notification with actor data', async function () {
const newSubscriberId = SubscriberRepository.createObjectId();
const channelType = ChannelTypeEnum.EMAIL;
const actorSubscriber = await subscriberService.createSubscriber({ firstName: 'Actor' });

template = await session.createTemplate({
steps: [
{
name: 'Message Name',
subject: 'Test email',
type: StepTypeEnum.EMAIL,
content: [
{
type: EmailBlockTypeEnum.TEXT,
content: 'Hello {{actor.firstName}}, Welcome to {{organizationName}}' as string,
},
],
},
],
});

await sendTrigger(session, template, newSubscriberId, {}, {}, '', actorSubscriber.subscriberId);

await session.awaitRunningJobs(template._id);

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

const message = await messageRepository.findOne({
_environmentId: session.environment._id,
_subscriberId: createdSubscriber?._id,
channel: channelType,
});

const block = message!.content[0] as IEmailBlock;

expect(block.content).to.equal('Hello Actor, Welcome to Umbrella Corp');
});

it('should not trigger notification with subscriber data if integration is inactive', async function () {
const newSubscriberIdInAppNotification = SubscriberRepository.createObjectId();
const channelType = ChannelTypeEnum.SMS;
Expand Down Expand Up @@ -2442,7 +2480,8 @@ export async function sendTrigger(
newSubscriberIdInAppNotification: string,
payload: Record<string, unknown> = {},
overrides: Record<string, unknown> = {},
tenant?: string
tenant?: string,
actor?: string
): Promise<AxiosResponse> {
return await axiosInstance.post(
`${session.serverUrl}${eventTriggerPath}`,
Expand All @@ -2456,6 +2495,7 @@ export async function sendTrigger(
},
overrides,
tenant,
actor,
},
{
headers: {
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/app/events/events.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class EventsController {
@Body() body: TriggerEventRequestDto
): Promise<TriggerEventResponseDto> {
const mappedTenant = body.tenant ? this.mapTenant(body.tenant) : null;
const mappedActor = body.actor ? this.mapActor(body.actor) : null;

const result = await this.parseEventRequest.execute(
ParseEventRequestCommand.create({
Expand All @@ -70,7 +71,7 @@ export class EventsController {
payload: body.payload || {},
overrides: body.overrides || {},
to: body.to,
actor: body.actor,
actor: mappedActor,
tenant: mappedTenant,
transactionId: body.transactionId,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ export class CancelDelayed {
constructor(private jobRepository: JobRepository) {}

public async execute(command: CancelDelayedCommand): Promise<boolean> {
const job = await this.jobRepository.findOne({
_environmentId: command.environmentId,
transactionId: command.transactionId,
status: JobStatusEnum.DELAYED,
});
const jobs = await this.jobRepository.find(
{
_environmentId: command.environmentId,
transactionId: command.transactionId,
status: JobStatusEnum.DELAYED,
},
'_id'
);

if (!job) {
if (!jobs?.length) {
return false;
}

await this.jobRepository.update(
{
_environmentId: command.environmentId,
_id: job._id,
_id: {
$in: jobs.map((job) => job._id),
},
},
{
$set: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class ParseEventRequestCommand extends EnvironmentWithUserCommand {
transactionId?: string;

@IsOptional()
@ValidateNested()
actor?: TriggerRecipientSubscriber | null;

@IsOptional()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class ProcessBulkTrigger {
for (const event of command.events) {
let result: TriggerEventResponseDto;
const mappedTenant = event.tenant ? this.parseEventRequest.mapTenant(event.tenant) : null;
const mappedActor = event.actor ? this.mapTriggerRecipients.mapSubscriber(event.actor) : null;

try {
result = (await this.parseEventRequest.execute(
Expand All @@ -29,7 +30,7 @@ export class ProcessBulkTrigger {
payload: event.payload,
overrides: event.overrides || {},
to: event.to,
actor: event.actor,
actor: mappedActor,
tenant: mappedTenant,
transactionId: event.transactionId,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ const TriggerTitle = styled(Text)`
`;

export const ExecutionDetailTrigger = ({ identifier, step, subscriberVariables }) => {
const { payload, overrides, tenant } = step || {};
const { payload, overrides, tenant, actorId } = step || {};

const curlSnippet = getCurlTriggerSnippet(identifier, subscriberVariables, payload, overrides, { tenant });
const curlSnippet = getCurlTriggerSnippet(identifier, subscriberVariables, payload, overrides, {
...(tenant && { tenant }),
...(actorId && { actor: { subscriberId: actorId } }),
});

return (
<>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/design-system/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ArrowLeft, Close } from '../icons';
const HeaderHolder = styled.div`
display: flex;
flex-wrap: nowrap;
gap: 12px;
gap: 36px;
margin: 24px;
margin-bottom: 0;
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IntegrationEntity,
IChannelSettings,
TenantRepository,
SubscriberEntity,
} from '@novu/dal';
import {
ChannelTypeEnum,
Expand Down Expand Up @@ -75,6 +76,13 @@ export class SendMessageChat extends SendMessageBase {
if (!chatChannel?.template) throw new PlatformException('Chat channel template not found');

const tenant = await this.handleTenantExecution(command.job);
let actor: SubscriberEntity | null = null;
if (command.job.actorId) {
actor = await this.getSubscriberBySubscriberId({
subscriberId: command.job.actorId,
_environmentId: command.environmentId,
});
}

let content = '';
const data = {
Expand All @@ -85,6 +93,7 @@ export class SendMessageChat extends SendMessageBase {
total_count: command.events?.length,
},
...(tenant && { tenant }),
...(actor && { actor }),
...command.payload,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MessageEntity,
LayoutRepository,
TenantRepository,
SubscriberEntity,
} from '@novu/dal';
import {
ChannelTypeEnum,
Expand Down Expand Up @@ -135,6 +136,14 @@ export class SendMessageEmail extends SendMessageBase {
return;
}

let actor: SubscriberEntity | null = null;
if (command.job.actorId) {
actor = await this.getSubscriberBySubscriberId({
subscriberId: command.job.actorId,
_environmentId: command.environmentId,
});
}

const [tenant, overrideLayoutId] = await Promise.all([
this.handleTenantExecution(command.job),
this.getOverrideLayoutId(command),
Expand Down Expand Up @@ -167,6 +176,7 @@ export class SendMessageEmail extends SendMessageBase {
total_count: command.events?.length,
},
...(tenant && { tenant }),
...(actor && { actor }),
subscriber,
},
};
Expand Down
Loading

0 comments on commit 38b009d

Please sign in to comment.