diff --git a/.cypress/fixtures/test_microsoft_teams_channel.json b/.cypress/fixtures/test_microsoft_teams_channel.json new file mode 100644 index 00000000..b79c64f2 --- /dev/null +++ b/.cypress/fixtures/test_microsoft_teams_channel.json @@ -0,0 +1,11 @@ +{ + "config": { + "name": "Test microsoft teams channel", + "description": "A test microsoft teams channel", + "config_type": "microsoft_teams", + "is_enabled": true, + "microsoft_teams": { + "url": "https://testdomain.webhook.office.com/123" + } + } +} diff --git a/.cypress/integration/channels.spec.js b/.cypress/integration/channels.spec.js index 65a1d68f..d6c4804f 100644 --- a/.cypress/integration/channels.spec.js +++ b/.cypress/integration/channels.spec.js @@ -8,6 +8,7 @@ import { delay } from '../utils/constants'; import testSlackChannel from '../fixtures/test_slack_channel'; import testChimeChannel from '../fixtures/test_chime_channel'; +import testMicrosoftTeamsChannel from '../fixtures/test_microsoft_teams_channel.json'; import testWebhookChannel from '../fixtures/test_webhook_channel.json'; import testTlsSmtpSender from '../fixtures/test_tls_smtp_sender'; @@ -69,6 +70,25 @@ describe('Test create channels', () => { cy.contains('successfully created.').should('exist'); }); + it('creates a microsoft teams channel and send test message', () => { + cy.get('[placeholder="Enter channel name"]').type('Test microsoft teams channel'); + + cy.get('.euiSuperSelectControl').contains('Slack').click({ force: true }); + cy.wait(delay); + cy.get('.euiContextMenuItem__text') + .contains('Microsoft Teams') + .click({ force: true }); + cy.wait(delay); + + cy.get('[data-test-subj="create-channel-microsoftTeams-webhook-input"]').type( + 'https://testdomain.webhook.office.com/123' + ); + cy.wait(delay); + + cy.get('[data-test-subj="create-channel-create-button"]').click(); + cy.contains('successfully created.').should('exist'); + }); + it('creates an email channel', () => { cy.get('[placeholder="Enter channel name"]').type('Test email channel'); @@ -114,7 +134,7 @@ describe('Test create channels', () => { .contains('Email') .click({ force: true }); cy.wait(delay); - + cy.get('input.euiRadio__input#ses_account').click({ force: true }); cy.wait(delay); @@ -195,6 +215,7 @@ describe('Test channels table', () => { // Create test channels cy.createConfig(testSlackChannel); cy.createConfig(testChimeChannel); + cy.createConfig(testMicrosoftTeamsChannel); cy.createConfig(testWebhookChannel); cy.createTestEmailChannel(); }); @@ -292,7 +313,7 @@ describe('Test channel details', () => { cy.contains('successfully unmuted.').should('exist'); cy.contains('Active').should('exist'); }); - + it('edits channels', () => { cy.contains('Actions').click({ force: true }); cy.contains('Edit').click({ force: true }); diff --git a/cypress.json b/cypress.json index fa5812d9..60ca7682 100644 --- a/cypress.json +++ b/cypress.json @@ -11,6 +11,7 @@ "requestTimeout": 60000, "responseTimeout": 60000, "defaultCommandTimeout": 60000, + "pageLoadTimeout":90000, "retries": { "runMode": 2, "openMode": 2 diff --git a/models/interfaces.ts b/models/interfaces.ts index 780c7133..dbe196f1 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -37,6 +37,9 @@ export interface ChannelItemType extends ConfigType { chime?: { url: string; }; + microsoft_teams?: { + url: string; + }; webhook?: { url: string; header_params: object; diff --git a/public/pages/Channels/__tests__/ChannelSettingsDetails.test.tsx b/public/pages/Channels/__tests__/ChannelSettingsDetails.test.tsx index 6d0abda9..2d95ecc8 100644 --- a/public/pages/Channels/__tests__/ChannelSettingsDetails.test.tsx +++ b/public/pages/Channels/__tests__/ChannelSettingsDetails.test.tsx @@ -45,4 +45,11 @@ describe(' spec', () => { ); expect(utils.container.firstChild).toMatchSnapshot(); }); + + it('renders Teams channel', () => { + const utils = render( + + ); + expect(utils.container.firstChild).toMatchSnapshot(); + }); }); diff --git a/public/pages/Channels/__tests__/__snapshots__/ChannelSettingsDetails.test.tsx.snap b/public/pages/Channels/__tests__/__snapshots__/ChannelSettingsDetails.test.tsx.snap index 29639d5b..af944db4 100644 --- a/public/pages/Channels/__tests__/__snapshots__/ChannelSettingsDetails.test.tsx.snap +++ b/public/pages/Channels/__tests__/__snapshots__/ChannelSettingsDetails.test.tsx.snap @@ -196,6 +196,62 @@ exports[` spec renders Slack channel 1`] = ` `; +exports[` spec renders Teams channel 1`] = ` +
+
+
+
+
+
+ Channel type +
+
+ Microsoft Teams +
+
+
+
+
+
+ Webhook URL +
+
+ https://chimehook +
+
+
+
+
+
+
+`; + exports[` spec renders Webhook channel 1`] = `
('smtp_account'); const [selectedSmtpSenderOptions, setSelectedSmtpSenderOptions] = useState< @@ -130,6 +132,7 @@ export function CreateChannel(props: CreateChannelsProps) { name: [], slackWebhook: [], chimeWebhook: [], + microsoftTeamsWebhook: [], smtpSender: [], sesSender: [], recipients: [], @@ -184,6 +187,8 @@ export function CreateChannel(props: CreateChannelsProps) { setSlackWebhook(response.slack?.url || ''); } else if (type === BACKEND_CHANNEL_TYPE.CHIME) { setChimeWebhook(response.chime?.url || ''); + } else if (type === BACKEND_CHANNEL_TYPE.MICROSOFT_TEAMS) { + setMicrosoftTeamsWebhook(response.microsoft_teams?.url || ''); } else if (type === BACKEND_CHANNEL_TYPE.EMAIL) { const emailObject = deconstructEmailObject(response.email!); setSenderType(emailObject.senderType); @@ -220,6 +225,7 @@ export function CreateChannel(props: CreateChannelsProps) { name: validateChannelName(name), slackWebhook: [], chimeWebhook: [], + microsoftTeamsWebhook: [], smtpSender: [], sesSender: [], recipients: [], @@ -233,6 +239,8 @@ export function CreateChannel(props: CreateChannelsProps) { errors.slackWebhook = validateWebhookURL(slackWebhook); } else if (channelType === BACKEND_CHANNEL_TYPE.CHIME) { errors.chimeWebhook = validateWebhookURL(chimeWebhook); + } else if (channelType === BACKEND_CHANNEL_TYPE.MICROSOFT_TEAMS) { + errors.microsoftTeamsWebhook = validateWebhookURL(microsoftTeamsWebhook); } else if (channelType === BACKEND_CHANNEL_TYPE.EMAIL) { if (senderType === 'smtp_account') { errors.smtpSender = validateEmailSender(selectedSmtpSenderOptions); @@ -270,6 +278,8 @@ export function CreateChannel(props: CreateChannelsProps) { config.slack = { url: slackWebhook }; } else if (channelType === BACKEND_CHANNEL_TYPE.CHIME) { config.chime = { url: chimeWebhook }; + } else if (channelType === BACKEND_CHANNEL_TYPE.MICROSOFT_TEAMS) { + config.microsoft_teams = { url: microsoftTeamsWebhook }; } else if (channelType === BACKEND_CHANNEL_TYPE.CUSTOM_WEBHOOK) { config.webhook = constructWebhookObject( webhookTypeIdSelected, @@ -401,7 +411,12 @@ export function CreateChannel(props: CreateChannelsProps) { chimeWebhook={chimeWebhook} setChimeWebhook={setChimeWebhook} /> - ) : channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( + ) : channelType === BACKEND_CHANNEL_TYPE.MICROSOFT_TEAMS ? ( + + ): channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( spec', () => { }); }); + it('renders the component for editing microsoft teams', async () => { + const notificationServiceMockMicrosoftTeams = jest.fn() as any; + const getMicrosoftTeamsChannel = jest.fn( + async (queryObject: object) => MOCK_DATA.microsoftTeams + ); + notificationServiceMockMicrosoftTeams.notificationService = { + getChannel: getMicrosoftTeamsChannel, + updateConfig: updateConfigFailure, + }; + const props = { + location: { search: '' }, + match: { params: { id: 'test' } }, + }; + const utilsMicrosoftTeams = render( + + + + )} + edit={true} + /> + + + + ); + + await waitFor(() => { + expect(getMicrosoftTeamsChannel).toBeCalled(); + }); + + utilsMicrosoftTeams.getByTestId('create-channel-create-button').click(); + await waitFor(() => { + expect(updateConfigFailure).toBeCalled(); + }); + }); + it('renders the component for editing email', async () => { const notificationServiceMockEmail = jest.fn() as any; const getEmailChannel = jest.fn( diff --git a/public/pages/CreateChannel/__tests__/MicrosoftTeamsSettings.test.tsx b/public/pages/CreateChannel/__tests__/MicrosoftTeamsSettings.test.tsx new file mode 100644 index 00000000..e926223b --- /dev/null +++ b/public/pages/CreateChannel/__tests__/MicrosoftTeamsSettings.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fireEvent, render } from '@testing-library/react'; +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { MicrosoftTeamsSettings } from '../components/MicrosoftTeamsSettings'; +import { CreateChannelContext } from '../CreateChannel'; + +describe(' spec', () => { + configure({ adapter: new Adapter() }); + + it('renders the component', () => { + const setMicrosoftTeamsWebhook = jest.fn(); + const utils = render( + + + + ); + expect(utils.container.firstChild).toMatchSnapshot(); + }); + + it('renders the component with error', () => { + const setMicrosoftTeamsWebhook = jest.fn(); + const utils = render( + + + + ); + expect(utils.container.firstChild).toMatchSnapshot(); + }); + + it('changes input', () => { + const setMicrosoftTeamsWebhook = jest.fn(); + const setInputErrors = jest.fn(); + const utils = render( + + + + ); + const input = utils.getByLabelText('Webhook URL'); + fireEvent.change(input, { target: { value: 'https://test-microsoftTeams-url' } }); + fireEvent.blur(input); + expect(setMicrosoftTeamsWebhook).toBeCalledWith('https://test-microsoftTeams-url'); + expect(setInputErrors).toBeCalled(); + }); +}); diff --git a/public/pages/CreateChannel/__tests__/__snapshots__/MicrosoftTeamsSettings.test.tsx.snap b/public/pages/CreateChannel/__tests__/__snapshots__/MicrosoftTeamsSettings.test.tsx.snap new file mode 100644 index 00000000..daeb6639 --- /dev/null +++ b/public/pages/CreateChannel/__tests__/__snapshots__/MicrosoftTeamsSettings.test.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` spec renders the component 1`] = ` +
+
+ +
+
+
+
+ +
+
+
+
+`; + +exports[` spec renders the component with error 1`] = ` +
+
+ +
+
+
+
+ +
+
+
+ test error +
+
+
+`; diff --git a/public/pages/CreateChannel/components/MicrosoftTeamsSettings.tsx b/public/pages/CreateChannel/components/MicrosoftTeamsSettings.tsx new file mode 100644 index 00000000..016b78a6 --- /dev/null +++ b/public/pages/CreateChannel/components/MicrosoftTeamsSettings.tsx @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import React, { useContext } from 'react'; +import { CreateChannelContext } from '../CreateChannel'; +import { validateWebhookURL } from '../utils/validationHelper'; + +interface MicrosoftTeamsSettingsProps { + microsoftTeamsWebhook: string; + setMicrosoftTeamsWebhook: (url: string) => void; +} + +export function MicrosoftTeamsSettings(props: MicrosoftTeamsSettingsProps) { + const context = useContext(CreateChannelContext)!; + + return ( + 0} + > + props.setMicrosoftTeamsWebhook(e.target.value)} + isInvalid={context.inputErrors.microsoftTeamsWebhook.length > 0} + onBlur={() => { + context.setInputErrors({ + ...context.inputErrors, + microsoftTeamsWebhook: validateWebhookURL(props.microsoftTeamsWebhook), + }); + }} + /> + + ); +} diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 4fa94d4f..e6a8b7cd 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -70,6 +70,7 @@ export default class Main extends Component { availableConfigTypes: [ 'slack', 'chime', + 'microsoft_teams', 'webhook', 'email', 'sns', diff --git a/public/utils/constants.ts b/public/utils/constants.ts index f328ad57..a1032b9d 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -52,6 +52,7 @@ export const BACKEND_CHANNEL_TYPE = Object.freeze({ SLACK: 'slack', EMAIL: 'email', CHIME: 'chime', + MICROSOFT_TEAMS: 'microsoft_teams', CUSTOM_WEBHOOK: 'webhook', SNS: 'sns', }); @@ -60,12 +61,14 @@ export const CHANNEL_TYPE = Object.freeze({ [BACKEND_CHANNEL_TYPE.SLACK]: 'Slack', [BACKEND_CHANNEL_TYPE.EMAIL]: 'Email', [BACKEND_CHANNEL_TYPE.CHIME]: 'Chime', + [BACKEND_CHANNEL_TYPE.MICROSOFT_TEAMS]: 'Microsoft Teams', [BACKEND_CHANNEL_TYPE.CUSTOM_WEBHOOK]: 'Custom webhook', [BACKEND_CHANNEL_TYPE.SNS]: 'Amazon SNS', }) as { slack: string; email: string; chime: string; + microsoft_teams: string; webhook: string; sns: string; }; diff --git a/test/mocks/mockData.ts b/test/mocks/mockData.ts index ec57596e..ef20f091 100644 --- a/test/mocks/mockData.ts +++ b/test/mocks/mockData.ts @@ -160,6 +160,19 @@ const mockSender: SenderItemType = { }, }; +const mockMicrosoftTeams: ChannelItemType = { + name: 'Microsoft Teams test channel', + description: 'test description', + config_type: 'microsoft_teams', + is_enabled: false, + microsoft_teams: { + url: 'https://chimehook', + }, + config_id: 'test-slack', + created_time_ms: 1622670451891, + last_updated_time_ms: 1622670451891, +}; + const mockSESSender: SESSenderItemType = { name: 'test-ses-sender', description: 'test description', @@ -191,6 +204,8 @@ const mockRecipientGroup: RecipientGroupItemType = { }, }; + + export const MOCK_DATA = { channels: { items: [ @@ -200,8 +215,9 @@ export const MOCK_DATA = { mockEmailWithSES, mockWebhook, mockSNS, + mockMicrosoftTeams ], - total: 6, + total: 7 }, chime: mockChime, slack: mockSlack, @@ -209,6 +225,7 @@ export const MOCK_DATA = { emailWithSES: mockEmailWithSES, webhook: mockWebhook, sns: mockSNS, + microsoftTeams: mockMicrosoftTeams, sender: mockSender, sesSender: mockSESSender, senders: {