Skip to content

Commit

Permalink
Merge pull request #4136 from novuhq/nv-2795-allow-to-create-integrat…
Browse files Browse the repository at this point in the history
…ion-for-novu-sms-and-email-providers

Nv 2795 allow to create integration for novu sms and email providers
  • Loading branch information
BiswaViraj authored Sep 11, 2023
2 parents da8b8c7 + a7f7f70 commit 5d3922b
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 26 deletions.
44 changes: 44 additions & 0 deletions apps/api/src/app/integrations/e2e/create-integration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,50 @@ describe('Create Integration - /integration (POST)', function () {
expect(second.active).to.equal(false);
expect(second.priority).to.equal(0);
});

it('should not allow creating the same novu provider on same environment twice', async function () {
const inAppPayload = {
name: InAppProviderIdEnum.Novu,
providerId: InAppProviderIdEnum.Novu,
channel: ChannelTypeEnum.IN_APP,
credentials: {},
active: true,
check: false,
};

const inAppResult = await session.testAgent.post('/v1/integrations').send(inAppPayload);

expect(inAppResult.body.statusCode).to.equal(400);
expect(inAppResult.body.message).to.equal('One environment can only have one In app provider');

const emailPayload = {
name: EmailProviderIdEnum.Novu,
providerId: EmailProviderIdEnum.Novu,
channel: ChannelTypeEnum.EMAIL,
credentials: {},
active: true,
check: false,
};

const emailResult = await session.testAgent.post('/v1/integrations').send(emailPayload);

expect(emailResult.body.statusCode).to.equal(409);
expect(emailResult.body.message).to.equal('Integration with novu provider for email channel already exists');

const smsPayload = {
name: SmsProviderIdEnum.Novu,
providerId: SmsProviderIdEnum.Novu,
channel: ChannelTypeEnum.SMS,
credentials: {},
active: true,
check: false,
};

const smsResult = await session.testAgent.post('/v1/integrations').send(smsPayload);

expect(smsResult.body.statusCode).to.equal(409);
expect(smsResult.body.message).to.equal('Integration with novu provider for sms channel already exists');
});
});

async function insertIntegrationTwice(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class CreateIntegration {
if (command.providerId === SmsProviderIdEnum.Novu || command.providerId === EmailProviderIdEnum.Novu) {
const count = await this.integrationRepository.count({
_environmentId: command.environmentId,
providerId: EmailProviderIdEnum.Novu,
providerId: command.providerId,
channel: command.channel,
});

Expand Down
39 changes: 39 additions & 0 deletions apps/web/cypress/tests/integrations-list-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1000,4 +1000,43 @@ describe('Integrations List Page', function () {
expect(el.get(0).innerText).to.eq('20 messages per month');
});
});

it('should not allow creating a novu provider for the same environment if it already exists', () => {
cy.intercept('*/integrations', async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
}).as('getIntegrations');
cy.intercept('*/environments').as('getEnvironments');

cy.visit('/integrations');

cy.wait('@getIntegrations');
cy.wait('@getEnvironments');

cy.getByTestId('add-provider').should('be.enabled').click();
cy.getByTestId('select-provider-sidebar').should('be.visible');

cy.getByTestId(`provider-${EmailProviderIdEnum.Novu}`).contains('Novu').click();

cy.window().then((win) => {
if (win.isDarkTheme) {
cy.getByTestId(`selected-provider-image-${EmailProviderIdEnum.Novu}`).should(
'have.attr',
'src',
`/static/images/providers/dark/square/${EmailProviderIdEnum.Novu}.svg`
);
return;
}

cy.getByTestId(`selected-provider-image-${EmailProviderIdEnum.Novu}`).should(
'have.attr',
'src',
`/static/images/providers/light/square/${EmailProviderIdEnum.Novu}.svg`
);
});
cy.getByTestId('selected-provider-name').should('be.visible').contains('Novu');

cy.getByTestId('select-provider-sidebar-next').should('not.be.disabled').contains('Next').click();
cy.getByTestId('novu-provider-error').contains('You can only create one Novu Email per environment.');
cy.getByTestId('create-provider-instance-sidebar-create').should('be.disabled');
});
});
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import styled from '@emotion/styled';
import { ActionIcon, Group, Radio, Text } from '@mantine/core';
import { useEffect, useMemo } from 'react';
import { ChannelTypeEnum, ICreateIntegrationBodyDto, NOVU_PROVIDERS, providers } from '@novu/shared';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useEffect, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import styled from '@emotion/styled';
import { ChannelTypeEnum, ICreateIntegrationBodyDto, InAppProviderIdEnum, providers } from '@novu/shared';

import { createIntegration } from '../../../../api/integration';
import { QueryKeys } from '../../../../api/query.keys';
import { useSegment } from '../../../../components/providers/SegmentProvider';
import { When } from '../../../../components/utils/When';
import { Button, colors, NameInput, Sidebar } from '../../../../design-system';
import { ArrowLeft } from '../../../../design-system/icons';
import { inputStyles } from '../../../../design-system/config/inputs.styles';
import { ArrowLeft } from '../../../../design-system/icons';
import { useFetchEnvironments } from '../../../../hooks/useFetchEnvironments';
import { useSegment } from '../../../../components/providers/SegmentProvider';
import { createIntegration } from '../../../../api/integration';
import { IntegrationsStoreModalAnalytics } from '../../constants';
import { errorMessage, successMessage } from '../../../../utils/notifications';
import { QueryKeys } from '../../../../api/query.keys';
import { ProviderImage } from './SelectProviderSidebar';
import { CHANNEL_TYPE_TO_STRING } from '../../../../utils/channels';
import { errorMessage, successMessage } from '../../../../utils/notifications';
import { IntegrationsStoreModalAnalytics } from '../../constants';
import type { IntegrationEntity } from '../../types';
import { useProviders } from '../../useProviders';
import { When } from '../../../../components/utils/When';
import { ProviderImage } from './SelectProviderSidebar';

interface ICreateProviderInstanceForm {
name: string;
Expand Down Expand Up @@ -67,8 +67,8 @@ export function CreateProviderInstanceSidebar({

const selectedEnvironmentId = watch('environmentId');

const showInAppErrorMessage = useMemo(() => {
if (!provider || integrations.length === 0 || provider.id !== InAppProviderIdEnum.Novu) {
const showNovuProvidersErrorMessage = useMemo(() => {
if (!provider || integrations.length === 0 || !NOVU_PROVIDERS.includes(provider.id)) {
return false;
}

Expand Down Expand Up @@ -170,7 +170,7 @@ export function CreateProviderInstanceSidebar({
Cancel
</Button>
<Button
disabled={isLoading || isLoadingCreate || showInAppErrorMessage}
disabled={isLoading || isLoadingCreate || showNovuProvidersErrorMessage}
loading={isLoadingCreate}
submit
data-test-id="create-provider-instance-sidebar-create"
Expand Down Expand Up @@ -231,9 +231,11 @@ export function CreateProviderInstanceSidebar({
);
}}
/>
<When truthy={showInAppErrorMessage}>
<When truthy={showNovuProvidersErrorMessage}>
<WarningMessage>
<Text>You can only create one {provider.displayName} per environment.</Text>
<Text data-test-id="novu-provider-error">
You can only create one {provider.displayName} per environment.
</Text>
</WarningMessage>
</When>
</Sidebar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
inAppProviders,
chatProviders,
InAppProviderIdEnum,
NOVU_SMS_EMAIL_PROVIDERS,
} from '@novu/shared';

import { colors, Sidebar } from '../../../../design-system';
Expand All @@ -32,14 +31,12 @@ const filterSearch = (list, search: string) =>
list.filter((prov) => prov.displayName.toLowerCase().includes(search.toLowerCase()));

const mapStructure = (listProv): IIntegratedProvider[] =>
listProv
.filter((providerItem) => !NOVU_SMS_EMAIL_PROVIDERS.includes(providerItem.id))
.map((providerItem) => ({
providerId: providerItem.id,
displayName: providerItem.displayName,
channel: providerItem.channel,
docReference: providerItem.docReference,
}));
listProv.map((providerItem) => ({
providerId: providerItem.id,
displayName: providerItem.displayName,
channel: providerItem.channel,
docReference: providerItem.docReference,
}));

const initialProvidersList = {
[ChannelTypeEnum.EMAIL]: mapStructure(emailProviders),
Expand Down

0 comments on commit 5d3922b

Please sign in to comment.