Skip to content

Commit

Permalink
Merge branch 'next' into nv-3550-translation-with-params-not-included…
Browse files Browse the repository at this point in the history
…-in-payload-generation
  • Loading branch information
ainouzgali authored Mar 18, 2024
2 parents aeaf2a6 + b0751e4 commit 1b17e45
Show file tree
Hide file tree
Showing 62 changed files with 1,147 additions and 296 deletions.
6 changes: 6 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@
"upserted",
"upstash",
"Upstash",
"usecase",
"USECASE",
"Vonage",
"Krakend",
"ratelimit",
"Ratelimit",
Expand Down Expand Up @@ -600,6 +603,9 @@
"cpack",
"pulumi",
"hostedtoolcache",
"OTLP",
"otlp",
"hostedtoolcache",
"pyroscope",
"HEAY",
"Pyroscope",
Expand Down
8 changes: 6 additions & 2 deletions .github/actions/run-backend/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ inputs:
cypress_github_oauth_client_secret:
description: 'Cypress GitHub client secret'
required: true
ci_ee_test:
description: 'Whether the app should import ee packages for testing'
required: false
default: 'false'

runs:
using: composite
Expand All @@ -28,7 +32,7 @@ runs:
TZ: "UTC"
GITHUB_OAUTH_REDIRECT: "http://127.0.0.1:1336/v1/auth/github/callback"
LAUNCH_DARKLY_SDK_KEY: ${{ inputs.launch_darkly_sdk_key }}
CI_EE_TEST: "true"
CI_EE_TEST: ${{ inputs.ci_ee_test }}
run: cd apps/api && pnpm start:build &

- name: Start Worker
Expand All @@ -38,7 +42,7 @@ runs:
PORT: "1342"
TZ: "UTC"
LAUNCH_DARKLY_SDK_KEY: ${{ inputs.launch_darkly_sdk_key }}
CI_EE_TEST: "true"
CI_EE_TEST: ${{ inputs.ci_ee_test }}
run: cd apps/worker && pnpm start:prod &

- name: Wait on API and Worker
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/reusable-web-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ jobs:
cypress_github_oauth_client_id: ${{ secrets.CYPRESS_GITHUB_OAUTH_CLIENT_ID }}
cypress_github_oauth_client_secret: ${{ secrets.CYPRESS_GITHUB_OAUTH_CLIENT_SECRET }}
launch_darkly_sdk_key: ${{ secrets.LAUNCH_DARKLY_SDK_KEY }}
ci_ee_test: ${{ steps.setup.outputs.has_token }}

- name: Start Client
working-directory: apps/web
Expand Down Expand Up @@ -104,7 +105,7 @@ jobs:
echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV
- name: Cypress run EE e2e
if: ${{ inputs.ee }}
if: ${{ steps.setup.outputs.has_token == 'true' && inputs.ee }}
uses: cypress-io/github-action@v6
env:
NODE_ENV: test
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/reusable-widget-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
cypress_github_oauth_client_id: ${{ secrets.CYPRESS_GITHUB_OAUTH_CLIENT_ID }}
cypress_github_oauth_client_secret: ${{ secrets.CYPRESS_GITHUB_OAUTH_CLIENT_SECRET }}
launch_darkly_sdk_key: ${{ secrets.LAUNCH_DARKLY_SDK_KEY }}
ci_ee_test: ${{ steps.setup.outputs.has_token }}

# Runs a single command using the runners shell
- name: Start Client
Expand Down
2 changes: 1 addition & 1 deletion .source
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ You can open a new issue with this [issue form](https://github.com/novuhq/novu/i

The project is a monorepo, meaning that it is a collection of multiple packages managed in the same repository.

To learn more about the project structure and running the project locally, please have a look [here](https://docs.novu.co/community-support/introduction#run-novu-locally).
To learn more about the project structure and running the project locally, please have a look [here](https://docs.novu.co/community-support/introduction#run-novu-locally?utm_campaign=github-contrib).
After cloning your fork, you will need to run the `npm run setup:project` command to install and build all dependencies.

To learn a detailed guide on running the project locally, checkout our guide on [how to run novu in local machine](https://docs.novu.co/community/run-in-local-machine).
To learn a detailed guide on running the project locally, checkout our guide on [how to run novu in local machine](https://docs.novu.co/community/run-in-local-machine?utm_campaign=github-contrib).

## Missing a Feature?

Expand All @@ -86,8 +86,8 @@ Questions, suggestions, and thoughts are most welcome. Feel free to open a [GitH
- Help create tutorials and blog posts
- Request a feature by submitting a proposal
- Report a bug
- **Improve documentation** - fix incomplete or missing [docs](https://docs.novu.co/), bad wording, examples or explanations.
- **Improve documentation** - fix incomplete or missing [docs](https://docs.novu.co/?utm_campaign=github-contrib), bad wording, examples or explanations.

## Missing a provider?

If you are in need of a provider we do not yet have, you can request a new one by [submitting an issue](#submitting-an-issue). Or you can build a new one by following our [create a provider guide](https://docs.novu.co/community/add-a-new-provider).
If you are in need of a provider we do not yet have, you can request a new one by [submitting an issue](#submitting-an-issue). Or you can build a new one by following our [create a provider guide](https://docs.novu.co/community/add-a-new-provider?utm_campaign=github-contrib).
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ 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?utm_campaign=github-readme).

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

Expand Down Expand Up @@ -148,7 +148,7 @@ Create notification workflows right from your IDE and integrate with MJML/React
- Define workflow and step validations with Zod or JSON Schema
- Modify content and behavior via Web management input panel

[Request Early Access](https://novu.co/novu-echo-coming-soon/?utm_campaign=echo_github)
[Request Early Access](https://novu.co/novu-echo-coming-soon/?utm_campaign=github-readme)

```ts

Expand Down Expand Up @@ -202,17 +202,17 @@ client.workflow('comment-on-post', async ({step, subscriber}) => {

## Embeddable Notification Center

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](https://docs.novu.co/notification-center/client/react/get-started) / [Vue](https://docs.novu.co/notification-center/client/vue) / [Angular](https://docs.novu.co/notification-center/client/angular) components or an [iframe embed](https://docs.novu.co/notification-center/client/iframe), as well as a [Web component](https://docs.novu.co/notification-center/client/web-component).
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](https://docs.novu.co/notification-center/client/react/get-started?utm_campaign=github-readme) / [Vue](https://docs.novu.co/notification-center/client/vue?utm_campaign=github-readme) / [Angular](https://docs.novu.co/notification-center/client/angular?utm_campaign=github-readme) components or an [iframe embed](https://docs.novu.co/notification-center/client/iframe?utm_campaign=github-readme), as well as a [Web component](https://docs.novu.co/notification-center/client/web-component?utm_campaign=github-readme).

<div align="center">
<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)
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?utm_campaign=github-readme)

<p align="center">
<a href="https://docs.novu.co/sdks/react">React Component</a>
· <a href="https://docs.novu.co/sdks/vue">Vue Component</a>
· <a href="https://docs.novu.co/sdks/angular">Angular Component</a>
<a href="https://docs.novu.co/sdks/react?utm_campaign=github-readme">React Component</a>
· <a href="https://docs.novu.co/sdks/vue?utm_campaign=github-readme">Vue Component</a>
· <a href="https://docs.novu.co/sdks/angular?utm_campaign=github-readme">Angular Component</a>
</p>

</div>
Expand Down Expand Up @@ -277,7 +277,7 @@ Novu provides a single API to manage providers across multiple channels with a s

#### 📱 In-App

- [x] [Novu](https://docs.novu.co/notification-center/getting-started)
- [x] [Novu](https://docs.novu.co/notification-center/getting-started?utm_campaign=github-readme)
- [ ] MagicBell

#### Other (Coming Soon...)
Expand All @@ -298,9 +298,9 @@ We are more than happy to help you. If you are getting any errors or facing prob

## 🔗 Links

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

## 🛡️ License

Expand Down
4 changes: 2 additions & 2 deletions apps/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The command will return warnings and errors that must be fixed before the Github

## Running the API

See the docs for [Run in Local Machine](https://docs.novu.co/community/run-in-local-machine) to get setup. Then run:
See the docs for [Run in Local Machine](https://docs.novu.co/community/run-in-local-machine?utm_campaign=github-api-readme) to get setup. Then run:

```bash
# Run the API in watch mode
Expand All @@ -43,7 +43,7 @@ $ npm run test
```

### E2E tests
See the docs for [Running on Local Machine - API Tests](https://docs.novu.co/community/run-in-local-machine#api).
See the docs for [Running on Local Machine - API Tests](https://docs.novu.co/community/run-in-local-machine#api?utm_campaign=github-api-readme).

## Migrations
Database migrations are included for features that have a hard dependency on specific data being available on database entities. These migrations are run by both Novu Cloud and Novu Self-Hosted users to support new feature releases.
Expand Down
2 changes: 1 addition & 1 deletion apps/api/jarvis-api-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This issue was tagged as related to `@novu/api` and the related code is located
If that's the first time you want to contribute to Novu here are a few simple steps to get you started:
1. Fork the repository and clone your fork to your local machine.
2. Install the dependencies using `npm run setup:project`.
3. Create a new branch with the number of the issue, for example: `1454-fix-something-cool` and start contributing based on the [Contributing Guide](https://docs.novu.co/community/run-in-local-machine) or the short guide in the section below.
3. Create a new branch with the number of the issue, for example: `1454-fix-something-cool` and start contributing based on the [Contributing Guide](https://docs.novu.co/community/run-in-local-machine?utm_campaign=github-jarvis) or the short guide in the section below.
4. Create a Pull request and follow the template of creation
</details>

Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ const baseModules: Array<Type | DynamicModule | Promise<DynamicModule> | Forward
TenantModule,
WorkflowOverridesModule,
RateLimitingModule,
TracingModule.register(packageJson.name),
ProfilingModule.register(packageJson.name),
TracingModule.register(packageJson.name, packageJson.version),
];

const enterpriseModules = enterpriseImports();
Expand Down
32 changes: 28 additions & 4 deletions apps/api/src/app/blueprint/blueprint.controller.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { ClassSerializerInterceptor, Controller, Get, Param, UseInterceptors } from '@nestjs/common';

import { EnvironmentRepository, NotificationTemplateRepository } from '@novu/dal';

import { GroupedBlueprintResponse } from './dto/grouped-blueprint.response.dto';
import { GetBlueprint, GetBlueprintCommand } from './usecases/get-blueprint';
import { GetGroupedBlueprints } from './usecases/get-grouped-blueprints';
import { GetGroupedBlueprints, GetGroupedBlueprintsCommand } from './usecases/get-grouped-blueprints';
import { GetBlueprintResponse } from './dto/get-blueprint.response.dto';
import { ApiCommonResponses } from '../shared/framework/response.decorator';

@ApiCommonResponses()
@Controller('/blueprints')
@UseInterceptors(ClassSerializerInterceptor)
export class BlueprintController {
constructor(private getBlueprintUsecase: GetBlueprint, private getGroupedBlueprintsUsecase: GetGroupedBlueprints) {}
constructor(
private environmentRepository: EnvironmentRepository,
private getBlueprintUsecase: GetBlueprint,
private getGroupedBlueprintsUsecase: GetGroupedBlueprints
) {}

@Get('/group-by-category')
getGroupedBlueprints(): Promise<GroupedBlueprintResponse> {
return this.getGroupedBlueprintsUsecase.execute();
async getGroupedBlueprints(): Promise<GroupedBlueprintResponse> {
const prodEnvironmentId = await this.getProdEnvironmentId();

return this.getGroupedBlueprintsUsecase.execute(
GetGroupedBlueprintsCommand.create({ environmentId: prodEnvironmentId })
);
}

private async getProdEnvironmentId() {
const productionEnvironmentId = (
await this.environmentRepository.findOrganizationEnvironments(
NotificationTemplateRepository.getBlueprintOrganizationId() || ''
)
)?.find((env) => env.name === 'Production')?._id;

if (!productionEnvironmentId) {
throw new Error('Production environment id was not found');
}

return productionEnvironmentId;
}

@Get('/:templateIdOrIdentifier')
Expand Down
12 changes: 7 additions & 5 deletions apps/api/src/app/blueprint/e2e/get-grouped-blueprints.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from 'chai';
import * as sinon from 'sinon';

import { UserSession } from '@novu/testing';
import { NotificationTemplateRepository, EnvironmentRepository } from '@novu/dal';
import { NotificationTemplateRepository, EnvironmentRepository, EnvironmentEntity } from '@novu/dal';
import {
EmailBlockTypeEnum,
FieldLogicalOperatorEnum,
Expand Down Expand Up @@ -52,6 +52,7 @@ describe('Get grouped notification template blueprints - /blueprints/group-by-ca

it('should get the grouped blueprints', async function () {
const prodEnv = await getProductionEnvironment();
if (!prodEnv) throw new Error('production environment was not found');

await createTemplateFromBlueprint({ session, notificationTemplateRepository, prodEnv });

Expand Down Expand Up @@ -82,6 +83,7 @@ describe('Get grouped notification template blueprints - /blueprints/group-by-ca

it('should get the updated grouped blueprints (after invalidation)', async function () {
const prodEnv = await getProductionEnvironment();
if (!prodEnv) throw new Error('production environment was not found');

await createTemplateFromBlueprint({
session,
Expand Down Expand Up @@ -112,6 +114,8 @@ describe('Get grouped notification template blueprints - /blueprints/group-by-ca

it('should update the static POPULAR_TEMPLATES_GROUPED with fresh data', async () => {
const prodEnv = await getProductionEnvironment();
if (!prodEnv) throw new Error('production environment was not found');

await createTemplateFromBlueprint({ session, notificationTemplateRepository, prodEnv });

const data = await session.testAgent.get(`/v1/blueprints/group-by-category`).send();
Expand All @@ -128,7 +132,7 @@ describe('Get grouped notification template blueprints - /blueprints/group-by-ca
indexModuleStub.value(mockedValue);

await invalidateCache.invalidateByKey({
key: buildGroupedBlueprintsKey(),
key: buildGroupedBlueprintsKey(prodEnv._id),
});

const updatedBlueprintFromDb = (await session.testAgent.get(`/v1/blueprints/group-by-category`).send()).body.data
Expand Down Expand Up @@ -165,7 +169,7 @@ export async function createTemplateFromBlueprint({
}: {
session: UserSession;
notificationTemplateRepository: NotificationTemplateRepository;
prodEnv;
prodEnv: EnvironmentEntity;
}) {
const testTemplateRequestDto: Partial<CreateWorkflowRequestDto> = {
name: 'test email template',
Expand Down Expand Up @@ -213,8 +217,6 @@ export async function createTemplateFromBlueprint({
enabled: false,
});

if (!prodEnv) throw new Error('production environment was not found');

const blueprintId = (
await notificationTemplateRepository.findOne({
_environmentId: prodEnv._id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Injectable, NotFoundException } from '@nestjs/common';

import { NotificationTemplateRepository } from '@novu/dal';
import { NotificationTemplateRepository, NotificationTemplateEntity } from '@novu/dal';

import { GetBlueprintCommand } from './get-blueprint.command';
import { GetBlueprintResponse } from '../../dto/get-blueprint.response.dto';
import { NotificationTemplateEntity } from '@novu/dal/src';

@Injectable()
export class GetBlueprint {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { BaseCommand } from '@novu/application-generic';
import { EnvironmentLevelCommand } from '@novu/application-generic';

export class GetGroupedBlueprintsCommand extends BaseCommand {}
export class GetGroupedBlueprintsCommand extends EnvironmentLevelCommand {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { NotificationTemplateRepository, NotificationTemplateEntity } from '@novu/dal';
import { buildGroupedBlueprintsKey, CachedEntity } from '@novu/application-generic';
import { INotificationTemplate, IGroupedBlueprint } from '@novu/shared';
import { IGroupedBlueprint } from '@novu/shared';

import { GroupedBlueprintResponse } from '../../dto/grouped-blueprint.response.dto';
import { POPULAR_GROUPED_NAME, POPULAR_TEMPLATES_ID_LIST } from './index';
import { GetGroupedBlueprintsCommand, POPULAR_GROUPED_NAME, POPULAR_TEMPLATES_ID_LIST } from './index';

const WEEK_IN_SECONDS = 60 * 60 * 24 * 7;

Expand All @@ -13,17 +13,17 @@ export class GetGroupedBlueprints {
constructor(private notificationTemplateRepository: NotificationTemplateRepository) {}

@CachedEntity({
builder: () => buildGroupedBlueprintsKey(),
builder: (command: GetGroupedBlueprintsCommand) => buildGroupedBlueprintsKey(command.environmentId),
options: { ttl: WEEK_IN_SECONDS },
})
async execute(): Promise<GroupedBlueprintResponse> {
const groups = await this.fetchGroupedBlueprints();
async execute(command: GetGroupedBlueprintsCommand): Promise<GroupedBlueprintResponse> {
const generalGroups = await this.fetchGroupedBlueprints();

const updatePopularBlueprints = this.updatePopularBlueprints(groups);
const updatePopularBlueprints = this.getPopularGroupBlueprints(generalGroups);

const popular = { name: POPULAR_GROUPED_NAME, blueprints: updatePopularBlueprints };
const popularGroup = { name: POPULAR_GROUPED_NAME, blueprints: updatePopularBlueprints };

return { general: groups as IGroupedBlueprint[], popular };
return { general: generalGroups as IGroupedBlueprint[], popular: popularGroup as IGroupedBlueprint };
}

private async fetchGroupedBlueprints() {
Expand All @@ -41,27 +41,28 @@ export class GetGroupedBlueprints {
return groups.map((group) => group.blueprints).flat();
}

private updatePopularBlueprints(
private getPopularGroupBlueprints(
groups: { name: string; blueprints: NotificationTemplateEntity[] }[]
): INotificationTemplate[] {
): NotificationTemplateEntity[] {
const storedBlueprints = this.groupedToBlueprintsArray(groups);

const localPopularIds = [...POPULAR_TEMPLATES_ID_LIST];

const result: INotificationTemplate[] = [];
const result: NotificationTemplateEntity[] = [];

for (const localPopularId of localPopularIds) {
const storedBlueprint = storedBlueprints.find((blueprint) => blueprint._id === localPopularId);

if (!storedBlueprint) {
Logger.warn(
`Could not find stored popular blueprint id: ${localPopularId}, BLUEPRINT_CREATOR: ${NotificationTemplateRepository.getBlueprintOrganizationId()}`
`Could not find stored popular blueprint id: ${localPopularId}, BLUEPRINT_CREATOR:
${NotificationTemplateRepository.getBlueprintOrganizationId()}`
);

continue;
}

result.push(storedBlueprint as INotificationTemplate);
result.push(storedBlueprint);
}

return result;
Expand Down
Loading

0 comments on commit 1b17e45

Please sign in to comment.