Skip to content

Commit

Permalink
Merge branch 'next' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
LetItRock authored Oct 23, 2023
2 parents da57894 + cfde40b commit 7a9686b
Show file tree
Hide file tree
Showing 55 changed files with 1,065 additions and 1,346 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
32 changes: 12 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<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">
<img src="https://user-images.githubusercontent.com/2233092/213641043-3bbb3f21-3c53-4e67-afe5-755aeb222159.png" width="280" alt="Logo"/>
![Novu Logo]<img src="https://user-images.githubusercontent.com/2233092/213641043-3bbb3f21-3c53-4e67-afe5-755aeb222159.png" width="280"/>
</picture>
</a>
</div>
Expand All @@ -12,24 +12,16 @@
<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" alt="Novu - The&#0032;open&#0045;source&#0032;notification&#0032;infrastructure&#0032;for&#0032;developers | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<br /><br />
</p>

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

Interested in participating in Hacktoberfest? We extend a warm invitation! You also get the opportunity to win some swag 😁
Are you interested in participating in Hacktoberfest? We extend a warm invitation! You also get the opportunity to win some swag 😁

> ⭐️ If you're new to Hacktoberfest, you can learn more and register to participate [here](https://hacktoberfest.com/participation/). Registration is from **September 26th - October 31st**.
- Our Hacktoberfest kickoff event is happening on October 2, 2023. 🚀
- Check out our website for [hacktoberfest instructions](https://novu.co/hacktoberfest/).
- Join our [Discord and engage with our community](https://discord.com/invite/novu), get answers to your challenges, stay updated on events, announcements & prizes.
- Join our [Discord and engage with our community](https://discord.com/invite/novu), get answers to your challenges, and stay updated on events, announcements, and prizes.

In addition to this repository, here are the other Novu repositories you can contribute to for Hacktoberfest:
- [Novu Docs](https://github.com/novuhq/docs/issues)
Expand Down Expand Up @@ -89,7 +81,7 @@ With Novu, you can create custom workflows and define conditions for each channe
- 📦 Easy to set up and integrate
- 🛡 Debug and analyze multi-channel messages in a single dashboard
- 📦 Embeddable notification center with real-time updates
- 👨‍💻 Community driven
- 👨‍💻 Community-driven

## 📚 Table Of Contents

Expand All @@ -108,15 +100,15 @@ With Novu, you can create custom workflows and define conditions for each channe

## 🚀 Getting Started

We are excited to launch the complete Novu API and admin panel. Want to give it a test before the official release? Here is how:
We are excited to launch the complete Novu API and admin panel. Do you want to give it a test before the official release? Here is how:

```
npx novu init
```

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).
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:

Expand Down Expand Up @@ -152,7 +144,7 @@ await novu.trigger('<TRIGGER_NAME>', {
Using the Novu API and admin panel, you can easily add a real-time notification center to your web app without building it yourself. You can use our React / Vue / Angular component or an iframe embed.

<div align="center">
<img width="762" alt="notification-center-912bb96e009fb3a69bafec23bcde00b0" src="https://user-images.githubusercontent.com/80174214/193887395-f1c95042-b4e6-480e-a89c-a78aa247fa90.gif">
<img width="762" alt="notification-center-912bb96e009fb3a69bafec23bcde00b0" src="https://user-images.githubusercontent.com/80174214/193887395-f1c95042-b4e6-480e-a89c-a78aa247fa90.gif" alt-text="GIF of Novu's Embeddable Notification Center">

Read more about how to add a notification center to your app with the Novu API [here](https://docs.novu.co/notification-center/getting-started)

Expand Down Expand Up @@ -232,19 +224,19 @@ Novu provides a single API to manage providers across multiple channels with a s

## 📋 Read Our Code Of Conduct

Before you begin coding and collaborating, please read our [Code of Conduct](https://github.com/novuhq/novu/blob/main/CODE_OF_CONDUCT.md) thoroughly to understand the standards (that you are required to adhere to) for community engagement. As part of our open-source community, we hold ourselves and other contributors to a high standard of communication. As a participant and contributor to this project, you are agreeing to abide by our [Code of Conduct](https://github.com/novuhq/novu/blob/main/CODE_OF_CONDUCT.md).
Before you begin coding and collaborating, please read our [Code of Conduct](https://github.com/novuhq/novu/blob/main/CODE_OF_CONDUCT.md) thoroughly to understand the standards (that you are required to adhere to) for community engagement. As part of our open-source community, we hold ourselves and other contributors to a high standard of communication. As a participant and contributor to this project, you agree to abide by our [Code of Conduct](https://github.com/novuhq/novu/blob/main/CODE_OF_CONDUCT.md).

## 💻 Need Help?

We are more than happy to help you. If you are getting any errors or facing problems while working on this project, join our [Discord server](https://discord.novu.co) and ask for help. We are open to discuss anything related to the project.
We are more than happy to help you. If you are getting any errors or facing problems while working on this project, join our [Discord server](https://discord.novu.co) and ask for help. We are open to discussing anything related to the project.

## ⚡ Immediate working space with Gitpod

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/novuhq/novu)

## 🔗 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 @@ -23,7 +23,10 @@ export class RemoveIntegration {

async execute(command: RemoveIntegrationCommand) {
try {
const existingIntegration = await this.integrationRepository.findById(command.integrationId);
const existingIntegration = await this.integrationRepository.findOne({
_id: command.integrationId,
_organizationId: command.organizationId,
});
if (!existingIntegration) {
throw new NotFoundException(`Entity with id ${command.integrationId} not found`);
}
Expand Down
6 changes: 3 additions & 3 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@ant-design/icons": "^4.6.2",
"@babel/plugin-proposal-optional-chaining": "^7.20.7",
"@babel/plugin-transform-react-display-name": "^7.18.6",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/plugin-transform-runtime": "^7.23.2",
"@cypress/react": "^7.0.2",
"@cypress/webpack-dev-server": "^3.1.2",
"@editorjs/editorjs": "^2.19.3",
Expand Down Expand Up @@ -120,7 +120,7 @@
},
"devDependencies": {
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.13.15",
"@babel/preset-env": "^7.23.2",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.20.13",
Expand Down Expand Up @@ -187,4 +187,4 @@
}
]
}
}
}
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
Loading

0 comments on commit 7a9686b

Please sign in to comment.