Skip to content

Commit

Permalink
chore(api, worker): Remove multi provider feature flag (#4403)
Browse files Browse the repository at this point in the history
* chore(worker): Remove multi-provider FF

* chore(worker): Remove ff from shared module

* chore(api): Add API Readme

* chore(api): Remove multi-provider FF from e2e tests

* chore(api): Remove multi-provider FF usage from use-cases

* chore(generic): Remove multi-provider FF from shared/generic

* chore(api): Remove redundant disable-integration use-case

* docs(api): Readme typo

* test(api): Remove redundant describe block

* fix: Remove redundant flag import in shared module
  • Loading branch information
rifont authored Oct 25, 2023
1 parent 1a04a6d commit 49b22e6
Show file tree
Hide file tree
Showing 31 changed files with 38 additions and 504 deletions.
82 changes: 21 additions & 61 deletions apps/api/README.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,38 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>
<div align="center">
<a href="https://novu.co" 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"/>
</picture>
</a>
</div>

[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=main
[travis-url]: https://travis-ci.org/nestjs/nest
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
[linux-url]: https://travis-ci.org/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#5" alt="Coverage" /></a>
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-dc3d53.svg"/></a>
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
# @novu/api

## Description
A RESTful API for accessing the Novu platform, built using [NestJS](https://nestjs.com/).

[Nest](https://github.com/nestjs/nest) framework [TypeScript](https://www.typescriptlang.org/) starter repository.
## OpenAPI (formerly Swagger)

## Installation
The Novu API utilizes the [`@nestjs/swagger`](https://github.com/nestjs/swagger) package to generate up-to-date OpenAPI specifications.

```bash
$ npm install
```
A web interface to browse the available endpoints is available at [api.novu.co/api](https://api.novu.co/api). An OpenAPI specification can be retrieved at [api.novu.co/api-json](https://api.novu.co/api-json).

## Running the app

```bash
# development
$ npm run start
## Running the API

# watch mode
$ npm run start:dev
See the docs for [Run in Local Machine](https://docs.novu.co/community/run-in-local-machin) to get setup. Then run:

# incremental rebuild (webpack)
$ npm run webpack
$ npm run start:hmr

# production mode
$ npm run start:prod
```bash
# Run the API in watch mode
$ npm run start:api
```

## Test

### Unit Tests
```bash
# unit tests
$ npm run test

# e2e tests
$ npm run test:e2e

# test coverage
$ npm run test:cov
```

## Support

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).

## Stay in touch

- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)

## License

Nest is [MIT licensed](LICENSE).
### E2E tests
See the docs for [Running on Local Machine - API Tests](https://docs.novu.co/community/run-in-local-machine#api).
1 change: 0 additions & 1 deletion apps/api/src/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ VERCEL_REDIRECT_URI=http://localhost:4200/auth/login
VERCEL_BASE_URL=https://api.vercel.com

FF_IS_TOPIC_NOTIFICATION_ENABLED=true
IS_MULTI_PROVIDER_CONFIGURATION_ENABLED=true

STORE_NOTIFICATION_CONTENT=true

Expand Down
14 changes: 0 additions & 14 deletions apps/api/src/app/events/e2e/trigger-event.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ const axiosInstance = axios.create();

const eventTriggerPath = '/v1/events/trigger';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

const promiseTimeout = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));

describe(`Trigger event - ${eventTriggerPath} (POST)`, function () {
Expand Down Expand Up @@ -2389,18 +2387,6 @@ describe(`Trigger event - ${eventTriggerPath} (POST)`, function () {
expect(messagesAfter.length).to.equal(1);
});
});
});
describe.skip('Trigger Event - [IS_MULTI_PROVIDER_CONFIGURATION_ENABLED=true]', function () {
beforeEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
process.env.LAUNCH_DARKLY_SDK_KEY = '';
session = new UserSession();
await session.initialize();
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should trigger message with override integration identifier', async function () {
const newSubscriberId = SubscriberRepository.createObjectId();
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/app/integrations/e2e/create-integration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from '@novu/shared';
import { expect } from 'chai';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Create Integration - /integration (POST)', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();
Expand All @@ -20,11 +18,6 @@ describe('Create Integration - /integration (POST)', function () {
beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should get the email integration successfully', async function () {
Expand Down
14 changes: 1 addition & 13 deletions apps/api/src/app/integrations/e2e/deactivate-integration.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
import { IntegrationRepository } from '@novu/dal';
import { UserSession } from '@novu/testing';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { GetIsMultiProviderConfigurationEnabled } from '@novu/application-generic';

describe('Deactivate Integration', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();
let stub: sinon.SinonStub;

beforeEach(async () => {
session = new UserSession();
const service = session.testServer?.getService(GetIsMultiProviderConfigurationEnabled);
stub = sinon.stub(service, 'execute');
stub.callsFake(() => {
return true;
});
await session.initialize();
});

afterEach(() => {
stub.restore();
});

it('should not deactivated old providers when feature flag is active', async function () {
it('should not deactivate old providers when a new provider is created', async function () {
const payload = {
providerId: 'mailgun',
channel: 'email',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@ import { ChannelTypeEnum, EmailProviderIdEnum, SmsProviderIdEnum } from '@novu/s
import { IntegrationService } from '@novu/testing';
import { IntegrationEntity } from '@novu/dal';

describe('Get Active Integrations [IS_MULTI_PROVIDER_CONFIGURATION_ENABLED=true] - /integrations/active (GET)', function () {
describe('Get Active Integrations - Multi-Provider Configuration - /integrations/active (GET)', function () {
let session: UserSession;
const integrationService = new IntegrationService();
const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
process.env.LAUNCH_DARKLY_SDK_KEY = '';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should get active integrations', async function () {
await integrationService.createIntegration({
environmentId: session.environment._id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@ import { expect } from 'chai';
import { ChannelTypeEnum, EmailProviderIdEnum } from '@novu/shared';
import { IntegrationRepository } from '@novu/dal';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Get Decrypted Integrations - /integrations (GET)', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();

beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should get active decrypted integration', async function () {
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/app/integrations/e2e/get-integration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@ import { expect } from 'chai';
import { ChannelTypeEnum, EmailProviderIdEnum, SmsProviderIdEnum } from '@novu/shared';
import { IntegrationEntity } from '@novu/dal';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Get Integrations - /integrations (GET)', function () {
let session: UserSession;

beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should retrieve all the integrations of all environments from an organization from the prefilled test data', async () => {
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/app/integrations/e2e/remove-integration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,13 @@ import {
} from '@novu/shared';
import { HttpStatus } from '@nestjs/common';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Delete Integration - /integration/:integrationId (DELETE)', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();

beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should throw not found exception when integration is not found', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,13 @@ import {
PushProviderIdEnum,
} from '@novu/shared';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Set Integration As Primary - /integrations/:integrationId/set-primary (POST)', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();

beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('when integration id is not valid should throw bad request exception', async () => {
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/app/integrations/e2e/update-integration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
PushProviderIdEnum,
} from '@novu/shared';

const ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;

describe('Update Integration - /integrations/:integrationId (PUT)', function () {
let session: UserSession;
const integrationRepository = new IntegrationRepository();
Expand All @@ -20,11 +18,6 @@ describe('Update Integration - /integrations/:integrationId (PUT)', function ()
beforeEach(async () => {
session = new UserSession();
await session.initialize();
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = 'true';
});

afterEach(async () => {
process.env.IS_MULTI_PROVIDER_CONFIGURATION_ENABLED = ORIGINAL_IS_MULTI_PROVIDER_CONFIGURATION_ENABLED;
});

it('should throw not found exception when integration is not found', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ import {
encryptCredentials,
buildIntegrationKey,
InvalidateCacheService,
GetIsMultiProviderConfigurationEnabled,
FeatureFlagCommand,
} from '@novu/application-generic';

import { CreateIntegrationCommand } from './create-integration.command';
import { ApiException } from '../../../shared/exceptions/api.exception';
import { DeactivateSimilarChannelIntegrations } from '../deactivate-integration/deactivate-integration.usecase';
import { CheckIntegrationCommand } from '../check-integration/check-integration.command';
import { CheckIntegration } from '../check-integration/check-integration.usecase';

Expand All @@ -32,9 +29,7 @@ export class CreateIntegration {
constructor(
private invalidateCache: InvalidateCacheService,
private integrationRepository: IntegrationRepository,
private deactivateSimilarChannelIntegrations: DeactivateSimilarChannelIntegrations,
private analyticsService: AnalyticsService,
private getIsMultiProviderConfigurationEnabled: GetIsMultiProviderConfigurationEnabled
private analyticsService: AnalyticsService
) {}

private async calculatePriorityAndPrimary(command: CreateIntegrationCommand) {
Expand Down Expand Up @@ -71,26 +66,12 @@ export class CreateIntegration {
}

async execute(command: CreateIntegrationCommand): Promise<IntegrationEntity> {
const isMultiProviderConfigurationEnabled = await this.getIsMultiProviderConfigurationEnabled.execute(
FeatureFlagCommand.create({
userId: command.userId,
organizationId: command.organizationId,
environmentId: command.environmentId,
})
);

const existingIntegration = await this.integrationRepository.findOne({
_environmentId: command.environmentId,
providerId: command.providerId,
channel: command.channel,
});

if (!isMultiProviderConfigurationEnabled && existingIntegration) {
throw new BadRequestException(
'Duplicate key - One environment may not have two providers of the same channel type'
);
}

if (
existingIntegration &&
command.providerId === InAppProviderIdEnum.Novu &&
Expand Down Expand Up @@ -169,7 +150,7 @@ export class CreateIntegration {

const isActiveAndChannelSupportsPrimary = command.active && CHANNELS_WITH_PRIMARY.includes(command.channel);

if (isMultiProviderConfigurationEnabled && isActiveAndChannelSupportsPrimary) {
if (isActiveAndChannelSupportsPrimary) {
const { primary, priority } = await this.calculatePriorityAndPrimary(command);

query.primary = primary;
Expand All @@ -178,20 +159,6 @@ export class CreateIntegration {

const integrationEntity = await this.integrationRepository.create(query);

if (
!isMultiProviderConfigurationEnabled &&
command.active &&
![ChannelTypeEnum.CHAT, ChannelTypeEnum.PUSH].includes(command.channel)
) {
await this.deactivateSimilarChannelIntegrations.execute({
environmentId: command.environmentId,
organizationId: command.organizationId,
integrationId: integrationEntity._id,
channel: command.channel,
userId: command.userId,
});
}

return integrationEntity;
} catch (e) {
if (e instanceof DalException) {
Expand Down
Loading

0 comments on commit 49b22e6

Please sign in to comment.