diff --git a/dashboards-reports/common/index.ts b/dashboards-reports/common/index.ts index 45bded11..d43c550b 100644 --- a/dashboards-reports/common/index.ts +++ b/dashboards-reports/common/index.ts @@ -39,3 +39,17 @@ export const OPENSEARCH_REPORTS_API = { LIST_REPORT_DEFINITIONS: `${BASE_REPORTS_URI}/definitions`, POLL_REPORT_INSTANCE: `${BASE_REPORTS_URI}/poll_instance`, }; + +const REPORTING_NOTIFICATIONS_API_PREFIX = '/api/reporting_notifications'; +export const REPORTING_NOTIFICATIONS_DASHBOARDS_API = Object.freeze({ + GET_CONFIGS: `${REPORTING_NOTIFICATIONS_API_PREFIX}/get_configs`, + GET_EVENT: `${REPORTING_NOTIFICATIONS_API_PREFIX}/get_event`, + SEND_TEST_MESSAGE: `${REPORTING_NOTIFICATIONS_API_PREFIX}/test_message` +}); + +const NOTIFICATIONS_API_BASE_PATH = '/_plugins/_notifications'; +export const NOTIFICATIONS_API = Object.freeze({ + CONFIGS: `${NOTIFICATIONS_API_BASE_PATH}/configs`, + EVENTS: `${NOTIFICATIONS_API_BASE_PATH}/events`, + TEST_MESSAGE: `${NOTIFICATIONS_API_BASE_PATH}/feature/test` +}); \ No newline at end of file diff --git a/dashboards-reports/public/components/main/main_utils.tsx b/dashboards-reports/public/components/main/main_utils.tsx index ede650a8..726e5aca 100644 --- a/dashboards-reports/public/components/main/main_utils.tsx +++ b/dashboards-reports/public/components/main/main_utils.tsx @@ -27,20 +27,33 @@ import 'babel-polyfill'; import { i18n } from '@osd/i18n'; import { HttpFetchOptions, HttpSetup } from '../../../../../src/core/public'; -import { placeholderChannels } from '../report_definitions/delivery/delivery_constants'; -export const displayDeliveryChannels = (configIds: Array) => { +export const displayDeliveryChannels = (configIds: Array, channels: Array<{label: string, id: string}>) => { let displayChannels = []; for (let i = 0; i < configIds.length; ++i) { - for (let j = 0; j < placeholderChannels.length; ++j) { - if (configIds[i] === placeholderChannels[j].id) { - displayChannels.push(placeholderChannels[i].label); + for (let j = 0; j < channels.length; ++j) { + if (configIds[i] === channels[j].id) { + displayChannels.push(channels[j].label); + break; } } } return displayChannels.toString(); } +export const getAvailableNotificationsChannels = (configList: any) => { + let availableChannels = []; + for (let i = 0; i < configList.length; ++i) { + let channelEntry = {}; + channelEntry = { + label: configList[i].config.name, + id: configList[i].config_id + } + availableChannels.push(channelEntry); + } + return availableChannels; +} + type fileFormatsOptions = { [key: string]: string } diff --git a/dashboards-reports/public/components/main/report_definition_details/__tests__/report_definition_details.test.tsx b/dashboards-reports/public/components/main/report_definition_details/__tests__/report_definition_details.test.tsx index 5ea5234c..1edecea5 100644 --- a/dashboards-reports/public/components/main/report_definition_details/__tests__/report_definition_details.test.tsx +++ b/dashboards-reports/public/components/main/report_definition_details/__tests__/report_definition_details.test.tsx @@ -83,6 +83,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); const { container } = render( @@ -137,6 +138,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); const { container } = render( @@ -191,6 +193,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); const { container } = render( @@ -234,6 +237,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); const component = mount( @@ -292,6 +296,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); const component = mount( @@ -351,6 +356,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); httpClientMock.put = jest.fn().mockResolvedValue({}); @@ -412,6 +418,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, + config_list: [] }); httpClientMock.put = jest.fn().mockResolvedValue({}); diff --git a/dashboards-reports/public/components/main/report_definition_details/report_definition_details.tsx b/dashboards-reports/public/components/main/report_definition_details/report_definition_details.tsx index dce81050..35875e47 100644 --- a/dashboards-reports/public/components/main/report_definition_details/report_definition_details.tsx +++ b/dashboards-reports/public/components/main/report_definition_details/report_definition_details.tsx @@ -53,6 +53,7 @@ import { displayDeliveryChannels, fileFormatsUpper, generateReportFromDefinitionId, + getAvailableNotificationsChannels, } from '../main_utils'; import { ReportDefinitionSchemaType } from '../../../../server/model'; import moment from 'moment'; @@ -62,7 +63,7 @@ import { permissionsMissingActions, } from '../../utils/utils'; import { GenerateReportLoadingModal } from '../loading_modal'; -import { placeholderChannels } from '../../report_definitions/delivery/delivery_constants'; +import { getChannelsQueryObject } from '../../report_definitions/delivery/delivery_constants'; const ON_DEMAND = 'On demand'; @@ -113,6 +114,7 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a const [toasts, setToasts] = useState([]); const [showDeleteModal, setShowDeleteModal] = useState(false); const [showLoading, setShowLoading] = useState(false); + const [channels, setChannels] = useState>([]); const reportDefinitionId = props.match['params']['reportDefinitionId']; const handleLoading = (e: boolean | ((prevState: boolean) => boolean)) => { @@ -410,19 +412,10 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a return scheduleDetails; }; - // const displayDeliveryChannels = (configIds: Array) => { - // let displayChannels = []; - // for (let i = 0; i < configIds.length; ++i) { - // for (let j = 0; j < placeholderChannels.length; ++j) { - // if (configIds[i] === placeholderChannels[j].id) { - // displayChannels.push(placeholderChannels[i].label); - // } - // } - // } - // return displayChannels.toString(); - // } - - const getReportDefinitionDetailsMetadata = (data: ReportDefinitionSchemaType) : ReportDefinitionDetails => { + const getReportDefinitionDetailsMetadata = ( + data: ReportDefinitionSchemaType, + availableChannels: Array<{ label: string; id: string; }> + ) : ReportDefinitionDetails => { const reportDefinition: ReportDefinitionSchemaType = data; const { report_params: reportParams, @@ -485,7 +478,7 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a ? humanReadableScheduleDetails(data.trigger) : `\u2014`, status: reportDefinition.status, - configIds: (configIds.length > 0) ? displayDeliveryChannels(configIds) : `\u2014`, + configIds: (configIds.length > 0) ? displayDeliveryChannels(configIds, availableChannels) : `\u2014`, title: (title !== '') ? title : `\u2014`, textDescription: (textDescription !== '') ? textDescription : `\u2014`, htmlDescription: (htmlDescription !== '') ? htmlDescription : `\u2014` @@ -496,10 +489,20 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a useEffect(() => { const { httpClient } = props; httpClient + .get('../api/reporting_notifications/get_configs', { + query: getChannelsQueryObject + }) + .then(async (response: any) => { + let availableChannels = getAvailableNotificationsChannels(response.config_list); + setChannels(availableChannels); + return availableChannels; + }) + .then((availableChannels: any) => { + httpClient .get(`../api/reporting/reportDefinitions/${reportDefinitionId}`) .then((response: {report_definition: ReportDefinitionSchemaType}) => { handleReportDefinitionRawResponse(response); - handleReportDefinitionDetails(getReportDefinitionDetailsMetadata(response.report_definition)); + handleReportDefinitionDetails(getReportDefinitionDetailsMetadata(response.report_definition, availableChannels)); props.setBreadcrumbs([ { text: i18n.translate( @@ -534,6 +537,7 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a ); handleDetailsErrorToast(); }); + }) }, []); const downloadIconDownload = async () => { @@ -584,7 +588,7 @@ export function ReportDefinitionDetails(props: { match?: any; setBreadcrumbs?: a updatedRawResponse.report_definition = updatedReportDefinition; handleReportDefinitionRawResponse(updatedRawResponse); setReportDefinitionDetails( - getReportDefinitionDetailsMetadata(updatedReportDefinition) + getReportDefinitionDetailsMetadata(updatedReportDefinition, channels) ); if (statusChange === 'Enable') { handleSuccessChangingScheduleStatusToast('enable'); diff --git a/dashboards-reports/public/components/main/report_details/__tests__/__snapshots__/report_details.test.tsx.snap b/dashboards-reports/public/components/main/report_details/__tests__/__snapshots__/report_details.test.tsx.snap index a77e9f9f..5299a7cb 100644 --- a/dashboards-reports/public/components/main/report_details/__tests__/__snapshots__/report_details.test.tsx.snap +++ b/dashboards-reports/public/components/main/report_details/__tests__/__snapshots__/report_details.test.tsx.snap @@ -32,7 +32,9 @@ exports[` panel render 5 hours recurring component 1`] = ` >

+ > + test create report definition trigger +

@@ -64,7 +66,9 @@ exports[` panel render 5 hours recurring component 1`] = `
+ > + test create report definition trigger +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > + — +
@@ -138,11 +148,11 @@ exports[` panel render 5 hours recurring component 1`] = ` > - + Dashboard @@ -160,7 +170,9 @@ exports[` panel render 5 hours recurring component 1`] = `
+ > + Invalid Date -> 10/23/2020, 1:53:35 PM +
panel render 5 hours recurring component 1`] = `
+ > +

+ — +

+
panel render 5 hours recurring component 1`] = `
+ > +

+ — +

+
panel render 5 hours recurring component 1`] = `
+ > + Schedule +
panel render 5 hours recurring component 1`] = `
+ > + Recurring +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > + — +
panel render 5 hours recurring component 1`] = `
+ > +

+ — +

+
@@ -437,65 +473,7 @@ exports[` panel render 5 hours recurring component 1`] = ` aria-live="polite" class="euiGlobalToastList euiGlobalToastList--right" role="region" - > -
-

- A new notification appears -

-
- - - Error loading report details. - -
- -
- + /> `; @@ -532,7 +510,9 @@ exports[` panel render on-demand component 1`] = ` >

+ > + test create report definition trigger +

@@ -564,7 +544,9 @@ exports[` panel render on-demand component 1`] = `
+ > + test create report definition trigger +
panel render on-demand component 1`] = `
+ > + — +
panel render on-demand component 1`] = `
+ > + — +
panel render on-demand component 1`] = `
+ > + — +
@@ -638,11 +626,11 @@ exports[` panel render on-demand component 1`] = ` > - + Dashboard @@ -660,7 +648,9 @@ exports[` panel render on-demand component 1`] = `
+ > + Invalid Date -> 10/23/2020, 1:53:35 PM +
panel render on-demand component 1`] = `
+ > +

+ — +

+
panel render on-demand component 1`] = `
+ > +

+ — +

+
panel render on-demand component 1`] = ` class="euiSpacer euiSpacer--l" />
-
-
-
- Report trigger -
-
-
-
-
-
-
- Schedule type -
-
-
-
-
-
+
-
- Schedule details -
-
-
-
-
-
-
-
-
-
+ On demand + +
panel render on-demand component 1`] = `
+ > + — +
panel render on-demand component 1`] = `
+ > + — +
panel render on-demand component 1`] = `
+ > + — +
panel render on-demand component 1`] = `
+ > +

+ — +

+
@@ -933,56 +893,7 @@ exports[` panel render on-demand component 1`] = ` aria-live="polite" class="euiGlobalToastList euiGlobalToastList--right" role="region" - > -
-

- A new notification appears -

-
- - - Error loading report details. - -
- -
- + /> `; diff --git a/dashboards-reports/public/components/main/report_details/__tests__/report_details.test.tsx b/dashboards-reports/public/components/main/report_details/__tests__/report_details.test.tsx index 8e6989d5..b4ea0206 100644 --- a/dashboards-reports/public/components/main/report_details/__tests__/report_details.test.tsx +++ b/dashboards-reports/public/components/main/report_details/__tests__/report_details.test.tsx @@ -59,8 +59,10 @@ describe(' panel', () => { }, }, delivery: { - delivery_type: '', - delivery_params: {}, + configIds: [], + title: '', + textDescription: '', + htmlDescription: '' }, trigger: { trigger_type: 'On demand', @@ -70,6 +72,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, query_url: `http://localhost:5601/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?_g=(time:(from:'2020-10-23T20:53:35.315Z',to:'2020-10-23T21:23:35.316Z'))`, + config_list: [] }); const { container } = render( @@ -100,8 +103,10 @@ describe(' panel', () => { }, }, delivery: { - delivery_type: '', - delivery_params: {}, + configIds: [], + title: '', + textDescription: '', + htmlDescription: '' }, trigger: { trigger_type: 'Schedule', @@ -123,6 +128,7 @@ describe(' panel', () => { httpClientMock.get = jest.fn().mockResolvedValue({ report_definition, query_url: `http://localhost:5601/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?_g=(time:(from:'2020-10-23T20:53:35.315Z',to:'2020-10-23T21:23:35.316Z'))`, + config_list: [] }); const { container } = render( diff --git a/dashboards-reports/public/components/main/report_details/report_details.tsx b/dashboards-reports/public/components/main/report_details/report_details.tsx index 58d26101..280db93f 100644 --- a/dashboards-reports/public/components/main/report_details/report_details.tsx +++ b/dashboards-reports/public/components/main/report_details/report_details.tsx @@ -44,7 +44,7 @@ import { EuiIcon, EuiGlobalToastList, } from '@elastic/eui'; -import { displayDeliveryChannels, fileFormatsUpper, generateReportById } from '../main_utils'; +import { displayDeliveryChannels, fileFormatsUpper, generateReportById, getAvailableNotificationsChannels } from '../main_utils'; import { GenerateReportLoadingModal } from '../loading_modal'; import { ReportSchemaType } from '../../../../server/model'; import { converter } from '../../report_definitions/utils'; @@ -55,6 +55,7 @@ import { timeRangeMatcher, } from '../../utils/utils'; import { TRIGGER_TYPE } from '../../../../server/routes/utils/constants'; +import { getChannelsQueryObject } from '../../report_definitions/delivery/delivery_constants'; interface ReportDetails { reportName: string; @@ -227,7 +228,10 @@ export function ReportDetails(props: { match?: any; setBreadcrumbs?: any; httpCl ); }; - const getReportDetailsData = (report: ReportSchemaType) : ReportDetails => { + const getReportDetailsData = ( + report: ReportSchemaType, + availableChannels: Array<{ label: string; id: string; }> + ) : ReportDetails => { const { report_definition: reportDefinition, last_updated: lastUpdated, @@ -272,7 +276,7 @@ export function ReportDetails(props: { match?: any; setBreadcrumbs?: any; httpCl triggerType: triggerType, scheduleType: triggerParams ? triggerParams.schedule_type : `\u2014`, scheduleDetails: `\u2014`, - configIds: (configIds.length > 0) ? displayDeliveryChannels(configIds) : `\u2014`, + configIds: (configIds.length > 0) ? displayDeliveryChannels(configIds, availableChannels) : `\u2014`, title: (title !== '') ? title : `\u2014`, textDescription: (textDescription !== '') ? textDescription : `\u2014`, htmlDescription: (htmlDescription !== '') ? htmlDescription : `\u2014`, @@ -284,9 +288,18 @@ export function ReportDetails(props: { match?: any; setBreadcrumbs?: any; httpCl useEffect(() => { const { httpClient } = props; httpClient + .get('../api/reporting_notifications/get_configs', { + query: getChannelsQueryObject + }) + .then(async (response: any) => { + let availableChannels = getAvailableNotificationsChannels(response.config_list); + return availableChannels; + }) + .then((availableChannels: any) => { + httpClient .get('../api/reporting/reports/' + reportId) .then((response: ReportSchemaType) => { - handleReportDetails(getReportDetailsData(response)); + handleReportDetails(getReportDetailsData(response, availableChannels)); props.setBreadcrumbs([ { text: i18n.translate( @@ -312,6 +325,7 @@ export function ReportDetails(props: { match?: any; setBreadcrumbs?: any; httpCl console.log('Error when fetching report details: ', error); handleErrorToast(); }); + }) }, []); const downloadIconDownload = async () => { diff --git a/dashboards-reports/public/components/report_definitions/create/create_report_definition.tsx b/dashboards-reports/public/components/report_definitions/create/create_report_definition.tsx index 999c25fa..f935589c 100644 --- a/dashboards-reports/public/components/report_definitions/create/create_report_definition.tsx +++ b/dashboards-reports/public/components/report_definitions/create/create_report_definition.tsx @@ -166,6 +166,22 @@ export function CreateReport(props: { [x: string]: any; setBreadcrumbs?: any; ht deliveryChannelError, setDeliveryChannelError, ] = useState(''); + const [ + showDeliverySubjectError, + setShowDeliverySubjectError + ] = useState(false); + const [ + deliverySubjectError, + setDeliverySubjectError + ] = useState(''); + const [ + showDeliveryTextError, + setShowDeliveryTextError + ] = useState(false); + const [ + deliveryTextError, + setDeliveryTextError + ] = useState(''); const [showTimeRangeError, setShowTimeRangeError] = useState(false); // preserve the state of the request after an invalid create report definition request @@ -273,6 +289,10 @@ export function CreateReport(props: { [x: string]: any; setBreadcrumbs?: any; ht setShowCronError, setShowDeliveryChannelError, setDeliveryChannelError, + setShowDeliverySubjectError, + setDeliverySubjectError, + setShowDeliveryTextError, + setDeliveryTextError ).then((response) => { error = response; }); @@ -372,6 +392,10 @@ export function CreateReport(props: { [x: string]: any; setBreadcrumbs?: any; ht reportDefinitionRequest={createReportDefinitionRequest} showDeliveryChannelError={showDeliveryChannelError} deliveryChannelError={deliveryChannelError} + showDeliverySubjectError={showDeliverySubjectError} + deliverySubjectError={deliverySubjectError} + showDeliveryTextError={showDeliveryTextError} + deliveryTextError={deliveryTextError} /> diff --git a/dashboards-reports/public/components/report_definitions/delivery/__tests__/delivery.test.tsx b/dashboards-reports/public/components/report_definitions/delivery/__tests__/delivery.test.tsx index 0fa3effd..fcd4ab5f 100644 --- a/dashboards-reports/public/components/report_definitions/delivery/__tests__/delivery.test.tsx +++ b/dashboards-reports/public/components/report_definitions/delivery/__tests__/delivery.test.tsx @@ -61,6 +61,12 @@ const timeRange = { timeTo: new Date(1234567890), }; +global.fetch = jest.fn(() => ({ + then: jest.fn(() => ({ + then: jest.fn() + })) +})); + describe(' panel', () => { test('render create component', () => { const { container } = render( diff --git a/dashboards-reports/public/components/report_definitions/delivery/delivery.tsx b/dashboards-reports/public/components/report_definitions/delivery/delivery.tsx index c740634b..f8852ad5 100644 --- a/dashboards-reports/public/components/report_definitions/delivery/delivery.tsx +++ b/dashboards-reports/public/components/report_definitions/delivery/delivery.tsx @@ -39,11 +39,18 @@ import { EuiButton, } from '@elastic/eui'; import CSS from 'csstype'; -import { placeholderChannels, testMessageConfirmationMessage } from './delivery_constants'; +import { + getChannelsQueryObject, + noDeliveryChannelsSelectedMessage, + testMessageConfirmationMessage, + testMessageFailureMessage +} from './delivery_constants'; import 'react-mde/lib/styles/css/react-mde-all.css'; import { reportDefinitionParams } from '../create/create_report_definition'; import ReactMDE from 'react-mde'; import { converter } from '../utils'; +import { getAvailableNotificationsChannels } from '../../main/main_utils'; +import { REPORTING_NOTIFICATIONS_DASHBOARDS_API } from '../../../../common'; const styles: CSS.Properties = { maxWidth: '800px', @@ -59,6 +66,10 @@ export type ReportDeliveryProps = { httpClientProps: any; showDeliveryChannelError: boolean; deliveryChannelError: string; + showDeliverySubjectError: boolean; + deliverySubjectError: string; + showDeliveryTextError: boolean; + deliveryTextError: string; }; export function ReportDelivery(props: ReportDeliveryProps) { @@ -69,13 +80,18 @@ export function ReportDelivery(props: ReportDeliveryProps) { httpClientProps, showDeliveryChannelError, deliveryChannelError, + showDeliverySubjectError, + deliverySubjectError, + showDeliveryTextError, + deliveryTextError } = props; + const [isDeliveryHidden, setIsHidden] = useState(false); const [sendNotification, setSendNotification] = useState(false); const [channels, setChannels] = useState([]); const [selectedChannels, setSelectedChannels] = useState([]); - const [notificationSubject, setNotificationSubject] = useState(''); - const [notificationMessage, setNotificationMessage] = useState(''); + const [notificationSubject, setNotificationSubject] = useState('New report'); + const [notificationMessage, setNotificationMessage] = useState('New report available to view'); const [selectedTab, setSelectedTab] = React.useState<'write' | 'preview'>( 'write' ); @@ -84,6 +100,14 @@ export function ReportDelivery(props: ReportDeliveryProps) { const handleSendNotification = (e: { target: { checked: boolean }; }) => { setSendNotification(e.target.checked); includeDelivery = e.target.checked; + if (includeDelivery) { + reportDefinitionRequest.delivery.title = 'New report'; + reportDefinitionRequest.delivery.textDescription = 'New report available to view'; + } + else { + reportDefinitionRequest.delivery.title = `\u2014`; + reportDefinitionRequest.delivery.textDescription = `\u2014`; + } } const handleSelectedChannels = (e: Array<{ label: string, id: string}>) => { @@ -105,58 +129,154 @@ export function ReportDelivery(props: ReportDeliveryProps) { reportDefinitionRequest.delivery.htmlDescription = converter.makeHtml(e.toString()); } - const handleTestMessageConfirmation = (e: string) => { + const handleTestMessageConfirmation = (e: JSX.Element) => { setTestMessageConfirmation(e); } const defaultCreateDeliveryParams = () => { + includeDelivery = false; reportDefinitionRequest.delivery = { configIds: [], - title: '', - textDescription: '', + title: `\u2014`, // default values before any Notifications settings are configured + textDescription: `\u2014`, htmlDescription: '' }; }; - const sendTestNotificationsMessage = () => { - // implement send message - // on success, set test message confirmation message - handleTestMessageConfirmation(testMessageConfirmationMessage) + const isStatusCodeSuccess = (statusCode: string) => { + if (!statusCode) return true; + return /^2\d\d/.test(statusCode); + }; + + const eventToNotification = (event: any) => { + const success = event.event.status_list.every( + (status: any) => isStatusCodeSuccess(status.delivery_status.status_code) + ); + return { + event_source: event.event.event_source, + status_list: event.event.status_list, + event_id: event.event_id, + created_time_ms: event.created_time_ms, + last_updated_time_ms: event.last_updated_time_ms, + success, + }; + }; + + const getNotification = async (id: string) => { + const response = await httpClientProps.get( + `${REPORTING_NOTIFICATIONS_DASHBOARDS_API.GET_EVENT}/${id}` + ); + return eventToNotification(response.event_list[0]); + }; + + const sendTestNotificationsMessage = async () => { + if (selectedChannels.length === 0) { + handleTestMessageConfirmation(noDeliveryChannelsSelectedMessage); + } + let testMessageFailures = false; + let failedChannels: string[] = []; + // for each config ID in the current channels list + for (let i = 0; i < selectedChannels.length; ++i) { + try { + const eventId = await httpClientProps + .get(`${REPORTING_NOTIFICATIONS_DASHBOARDS_API.SEND_TEST_MESSAGE}/${selectedChannels[i].id}`, + { + query: { + feature: 'reports' + } + }) + .then((response) => response.event_id); + + await getNotification(eventId) + .then((response) => { + if (!response.success) { + const error = new Error('Failed to send the test message.'); + failedChannels.push(response.status_list[0].config_name); + error.stack = JSON.stringify(response.status_list, null, 2); + throw error; + } + }); + } catch (error) { + testMessageFailures = true; + } + } + if (testMessageFailures) { + handleTestMessageConfirmation(testMessageFailureMessage(failedChannels)); + } else { + handleTestMessageConfirmation(testMessageConfirmationMessage); + } + } + + const checkIfNotificationsPluginIsInstalled = () => { + fetch("http://localhost:5601/api/console/proxy?path=%2F_cat%2Fplugins%3Fv%3Dtrue%26s%3Dcomponent%26h%3Dcomponent&method=GET", { + "credentials": "include", + "headers": { + "Accept": "text/plain, */*; q=0.01", + "Accept-Language": "en-US,en;q=0.5", + "osd-xsrf": "true" + }, + "referrer": "http://localhost:5601/app/dev_tools", + "method": "POST", + "mode": "cors" + }) + .then((response) => { + return response.text(); + }) + .then(function(data) { + if (data.includes('opensearch-notifications')) { + setIsHidden(false); + return; + } + setIsHidden(true); + }) } useEffect(() => { - // to replace with actual channels from notifications plugin - setChannels(placeholderChannels); - if (edit) { - httpClientProps - .get(`../api/reporting/reportDefinitions/${editDefinitionId}`) - .then(async (response: any) => { - if (response.report_definition.delivery.configIds.length > 0) { - // add config IDs - handleSendNotification({target: {checked: true}}); - let delivery = response.report_definition.delivery; - let editChannelOptions = []; - for (let i = 0; i < delivery.configIds.length; ++i) { - for (let j = 0; j < placeholderChannels.length; ++j) { - if (delivery.configIds[i] === placeholderChannels[j].id) { - let editChannelOption = { - label: placeholderChannels[j].label, - id: placeholderChannels[j].id - }; - - editChannelOptions.push(editChannelOption); + checkIfNotificationsPluginIsInstalled(); + httpClientProps + .get(`${REPORTING_NOTIFICATIONS_DASHBOARDS_API.GET_CONFIGS}`, { + query: getChannelsQueryObject + }) + .then(async (response: any) => { + let availableChannels = getAvailableNotificationsChannels(response.config_list); + setChannels(availableChannels); + return availableChannels; + }) + .then((availableChannels: any) => { + if (edit) { + httpClientProps + .get(`../api/reporting/reportDefinitions/${editDefinitionId}`) + .then(async (response: any) => { + if (response.report_definition.delivery.configIds.length > 0) { + // add config IDs + handleSendNotification({target: {checked: true}}); + let delivery = response.report_definition.delivery; + let editChannelOptions = []; + for (let i = 0; i < delivery.configIds.length; ++i) { + for (let j = 0; j < availableChannels.length; ++j) { + if (delivery.configIds[i] === availableChannels[j].id) { + let editChannelOption = { + label: availableChannels[j].label, + id: availableChannels[j].id + }; + editChannelOptions.push(editChannelOption); + break; + } + } } + setSelectedChannels(editChannelOptions); + setNotificationSubject(delivery.title); + setNotificationMessage(delivery.textDescription); + reportDefinitionRequest.delivery = delivery; } - } - setSelectedChannels(editChannelOptions); - setNotificationSubject(delivery.title); - setNotificationMessage(delivery.textDescription); - reportDefinitionRequest.delivery = delivery; - } - }); - } else { - defaultCreateDeliveryParams(); - } + }); + } else { + defaultCreateDeliveryParams(); + } + }) + .catch((error: string) => { + console.log('error: cannot get available channels from Notifications plugin:', error); + }) }, []); const showNotificationsBody = sendNotification ? ( @@ -180,6 +300,8 @@ export function ReportDelivery(props: ReportDeliveryProps) { +