Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.0] [Connectors][ServiceNow] Update store links (#117374) #117881

Merged
merged 1 commit into from
Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('config', () => {
table: 'incident',
useImportAPI: true,
commentFieldKey: 'work_notes',
appId: '7148dbc91bf1f450ced060a7234bcb88',
});
});

Expand All @@ -35,6 +36,7 @@ describe('config', () => {
table: 'sn_si_incident',
useImportAPI: true,
commentFieldKey: 'work_notes',
appId: '2f0746801baeb01019ae54e4604bcb0f',
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,25 @@ export const ServiceNowITSMActionTypeId = '.servicenow';
export const ServiceNowSIRActionTypeId = '.servicenow-sir';
export const ServiceNowITOMActionTypeId = '.servicenow-itom';

const SN_ITSM_APP_ID = '7148dbc91bf1f450ced060a7234bcb88';
const SN_SIR_APP_ID = '2f0746801baeb01019ae54e4604bcb0f';

export const snExternalServiceConfig: SNProductsConfig = {
'.servicenow': {
importSetTable: 'x_elas2_inc_int_elastic_incident',
appScope: 'x_elas2_inc_int',
table: 'incident',
useImportAPI: true,
commentFieldKey: 'work_notes',
appId: SN_ITSM_APP_ID,
},
'.servicenow-sir': {
importSetTable: 'x_elas2_sir_int_elastic_si_incident',
appScope: 'x_elas2_sir_int',
table: 'sn_si_incident',
useImportAPI: true,
commentFieldKey: 'work_notes',
appId: SN_SIR_APP_ID,
},
'.servicenow-itom': {
importSetTable: 'x_elas2_inc_int_elastic_incident',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export interface SNProductsConfigValue {
useImportAPI: boolean;
importSetTable: string;
commentFieldKey: string;
appId?: string;
}

export type SNProductsConfig = Record<string, SNProductsConfigValue>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,32 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import { ApplicationRequiredCallout } from './application_required_callout';

const appId = 'test';

describe('ApplicationRequiredCallout', () => {
test('it renders the callout', () => {
render(<ApplicationRequiredCallout />);
render(<ApplicationRequiredCallout appId={appId} />);
expect(screen.getByText('Elastic ServiceNow App not installed')).toBeInTheDocument();
expect(
screen.getByText('Please go to the ServiceNow app store and install the application')
).toBeInTheDocument();
});

test('it renders the ServiceNow store button', () => {
render(<ApplicationRequiredCallout />);
render(<ApplicationRequiredCallout appId={appId} />);
expect(screen.getByText('Visit ServiceNow app store')).toBeInTheDocument();
});

it('should render with correct href for the ServiceNow store button', () => {
render(<ApplicationRequiredCallout appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute(
'href',
'https://store.servicenow.com/sn_appstore_store.do#!/store/application/test'
);
});

test('it renders an error message if provided', () => {
render(<ApplicationRequiredCallout message="Denied" />);
render(<ApplicationRequiredCallout message="Denied" appId={appId} />);
expect(screen.getByText('Error message: Denied')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ const ERROR_MESSAGE = i18n.translate(
);

interface Props {
appId: string;
message?: string | null;
}

const ApplicationRequiredCalloutComponent: React.FC<Props> = ({ message }) => {
const ApplicationRequiredCalloutComponent: React.FC<Props> = ({ appId, message }) => {
return (
<>
<EuiSpacer size="s" />
Expand All @@ -50,7 +51,7 @@ const ApplicationRequiredCalloutComponent: React.FC<Props> = ({ message }) => {
{ERROR_MESSAGE}: {message}
</p>
)}
<SNStoreButton color="danger" />
<SNStoreButton color="danger" appId={appId} />
</EuiCallOut>
<EuiSpacer size="m" />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import { render, screen } from '@testing-library/react';

import { InstallationCallout } from './installation_callout';

const appId = 'test';

describe('DeprecatedCallout', () => {
test('it renders correctly', () => {
render(<InstallationCallout />);
render(<InstallationCallout appId={appId} />);
expect(
screen.getByText(
'To use this connector, first install the Elastic app from the ServiceNow app store.'
Expand All @@ -21,7 +23,15 @@ describe('DeprecatedCallout', () => {
});

test('it renders the button', () => {
render(<InstallationCallout />);
render(<InstallationCallout appId={appId} />);
expect(screen.getByRole('link')).toBeInTheDocument();
});

it('should render with correct href for the ServiceNow store button', () => {
render(<InstallationCallout appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute(
'href',
'https://store.servicenow.com/sn_appstore_store.do#!/store/application/test'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { EuiSpacer, EuiCallOut } from '@elastic/eui';
import * as i18n from './translations';
import { SNStoreButton } from './sn_store_button';

const InstallationCalloutComponent: React.FC = () => {
interface Props {
appId: string;
}

const InstallationCalloutComponent: React.FC<Props> = ({ appId }) => {
return (
<>
<EuiSpacer size="s" />
Expand All @@ -22,7 +26,7 @@ const InstallationCalloutComponent: React.FC = () => {
data-test-subj="snInstallationCallout"
title={i18n.INSTALLATION_CALLOUT_TITLE}
>
<SNStoreButton color="warning" />
<SNStoreButton color="warning" appId={appId} />
</EuiCallOut>
<EuiSpacer size="m" />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { InstallationCallout } from './installation_callout';
import { UpdateConnector } from './update_connector';
import { updateActionConnector } from '../../../lib/action_connector_api';
import { Credentials } from './credentials';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { snExternalServiceConfig } from '../../../../../../actions/server/builtin_action_types/servicenow/config';

const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowActionConnector>> =
({
Expand Down Expand Up @@ -151,7 +153,9 @@ const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowA
onCancel={onModalCancel}
/>
)}
{requiresNewApplication && <InstallationCallout />}
{requiresNewApplication && (
<InstallationCallout appId={snExternalServiceConfig[action.actionTypeId].appId ?? ''} />
)}
{!requiresNewApplication && <DeprecatedCallout onMigrate={onMigrateClick} />}
<Credentials
action={action}
Expand All @@ -162,7 +166,10 @@ const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowA
editActionConfig={editActionConfig}
/>
{showApplicationRequiredCallout && requiresNewApplication && (
<ApplicationRequiredCallout message={applicationInfoErrorMsg} />
<ApplicationRequiredCallout
message={applicationInfoErrorMsg}
appId={snExternalServiceConfig[action.actionTypeId].appId ?? ''}
/>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,49 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import { SNStoreButton, SNStoreLink } from './sn_store_button';

const appId = 'test';

describe('SNStoreButton', () => {
it('should render the button', () => {
render(<SNStoreButton color="warning" />);
render(<SNStoreButton color="warning" appId={appId} />);
expect(screen.getByText('Visit ServiceNow app store')).toBeInTheDocument();
});

it('should render a danger button', () => {
render(<SNStoreButton color="danger" />);
render(<SNStoreButton color="danger" appId={appId} />);
expect(screen.getByRole('link')).toHaveClass('euiButton--danger');
});

it('should render with correct href', () => {
render(<SNStoreButton color="warning" />);
expect(screen.getByRole('link')).toHaveAttribute('href', 'https://store.servicenow.com/');
render(<SNStoreButton color="warning" appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute(
'href',
'https://store.servicenow.com/sn_appstore_store.do#!/store/application/test'
);
});

it('should render with target blank', () => {
render(<SNStoreButton color="warning" />);
render(<SNStoreButton color="warning" appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute('target', '_blank');
});
});

describe('SNStoreLink', () => {
it('should render the link', () => {
render(<SNStoreLink />);
render(<SNStoreLink appId={appId} />);
expect(screen.getByText('Visit ServiceNow app store')).toBeInTheDocument();
});

it('should render with correct href', () => {
render(<SNStoreLink />);
expect(screen.getByRole('link')).toHaveAttribute('href', 'https://store.servicenow.com/');
render(<SNStoreLink appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute(
'href',
'https://store.servicenow.com/sn_appstore_store.do#!/store/application/test'
);
});

it('should render with target blank', () => {
render(<SNStoreLink />);
render(<SNStoreLink appId={appId} />);
expect(screen.getByRole('link')).toHaveAttribute('target', '_blank');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,32 @@ import { EuiButtonProps, EuiButton, EuiLink } from '@elastic/eui';

import * as i18n from './translations';

const STORE_URL = 'https://store.servicenow.com/';
const getStoreURL = (appId: string): string =>
`https://store.servicenow.com/sn_appstore_store.do#!/store/application/${appId}`;

interface Props {
appId: string;
color: EuiButtonProps['color'];
}

const SNStoreButtonComponent: React.FC<Props> = ({ color }) => {
const SNStoreButtonComponent: React.FC<Props> = ({ color, appId }) => {
return (
<EuiButton href={STORE_URL} color={color} iconSide="right" iconType="popout" target="_blank">
<EuiButton
href={getStoreURL(appId)}
color={color}
iconSide="right"
iconType="popout"
target="_blank"
>
{i18n.VISIT_SN_STORE}
</EuiButton>
);
};

export const SNStoreButton = memo(SNStoreButtonComponent);

const SNStoreLinkComponent: React.FC = () => (
<EuiLink href={STORE_URL} target="_blank">
const SNStoreLinkComponent: React.FC<Pick<Props, 'appId'>> = ({ appId }) => (
<EuiLink href={getStoreURL(appId)} target="_blank">
{i18n.VISIT_SN_STORE}
</EuiLink>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { isFieldInvalid } from './helpers';
import { ApplicationRequiredCallout } from './application_required_callout';
import { SNStoreLink } from './sn_store_button';
import { CredentialsAuth } from './credentials_auth';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { snExternalServiceConfig } from '../../../../../../actions/server/builtin_action_types/servicenow/config';

const title = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle',
Expand Down Expand Up @@ -140,7 +142,11 @@ const UpdateConnectorComponent: React.FC<Props> = ({
id="xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.serviceNowAppRunning"
defaultMessage="The Elastic App from the ServiceNow app store must be installed prior to running the update. {visitLink} to install the app"
values={{
visitLink: <SNStoreLink />,
visitLink: (
<SNStoreLink
appId={snExternalServiceConfig[action.actionTypeId].appId ?? ''}
/>
),
}}
/>
),
Expand Down Expand Up @@ -175,7 +181,10 @@ const UpdateConnectorComponent: React.FC<Props> = ({
<EuiFlexGroup>
<EuiFlexItem>
{applicationInfoErrorMsg && (
<ApplicationRequiredCallout message={applicationInfoErrorMsg} />
<ApplicationRequiredCallout
message={applicationInfoErrorMsg}
appId={snExternalServiceConfig[action.actionTypeId].appId ?? ''}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
Expand Down