[Cases] Migrate to new Connectors APIs (#102662)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Christos Nasikas 2021-07-29 14:20:51 +03:00 committed by GitHub
parent fe1c27208c
commit 82e0ce1b51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 126 additions and 79 deletions

View file

@ -4749,7 +4749,7 @@
"label": "ACTION_TYPES_URL",
"description": [],
"signature": [
"\"/api/actions/list_action_types\""
"\"/api/actions/connector_types\""
],
"source": {
"path": "x-pack/plugins/cases/common/constants.ts",

View file

@ -60,7 +60,9 @@ export const CASE_DETAILS_ALERTS_URL = `${CASE_DETAILS_URL}/alerts`;
*/
export const ACTION_URL = '/api/actions';
export const ACTION_TYPES_URL = '/api/actions/list_action_types';
export const ACTION_TYPES_URL = `${ACTION_URL}/connector_types`;
export const CONNECTORS_URL = `${ACTION_URL}/connectors`;
export const SUPPORTED_CONNECTORS = [
`${ConnectorTypes.serviceNowITSM}`,
`${ConnectorTypes.serviceNowSIR}`,

View file

@ -8,3 +8,4 @@
export * from './constants';
export * from './api';
export * from './ui/types';
export * from './utils/connectors_api';

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
/**
* Actions and connectors API endpoint helpers
*/
import { ACTION_URL, ACTION_TYPES_URL, CONNECTORS_URL } from '../../common';
/**
*
* @returns {string} Connector types endpoint
*/
export const getAllConnectorTypesUrl = (): string => ACTION_TYPES_URL;
/**
*
* @param connectorId
* @returns {string} Execute connector endpoint
*/
export const getExecuteConnectorUrl = (connectorId: string): string =>
`${ACTION_URL}/connector/${connectorId}/_execute`;
/**
*
* @returns {string} Create connector endpoint
*/
export const getCreateConnectorUrl = (): string => `${ACTION_URL}/connector`;
/**
*
* @returns {string} All connectors endpoint
*/
export const getAllConnectorsUrl = (): string => CONNECTORS_URL;

View file

@ -94,7 +94,7 @@ describe('Jira API', () => {
const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'test' });
expect(res).toEqual(issueTypesResponse);
expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', {
expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
body: '{"params":{"subAction":"issueTypes","subActionParams":{}}}',
signal: abortCtrl.signal,
});
@ -113,7 +113,7 @@ describe('Jira API', () => {
});
expect(res).toEqual(fieldsResponse);
expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', {
expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
body: '{"params":{"subAction":"fieldsByIssueType","subActionParams":{"id":"10006"}}}',
signal: abortCtrl.signal,
});
@ -132,7 +132,7 @@ describe('Jira API', () => {
});
expect(res).toEqual(issuesResponse);
expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', {
expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
body: '{"params":{"subAction":"issues","subActionParams":{"title":"test issue"}}}',
signal: abortCtrl.signal,
});
@ -151,7 +151,7 @@ describe('Jira API', () => {
});
expect(res).toEqual(issuesResponse);
expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', {
expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
body: '{"params":{"subAction":"issue","subActionParams":{"id":"RJ-107"}}}',
signal: abortCtrl.signal,
});

View file

@ -7,10 +7,9 @@
import { HttpSetup } from 'kibana/public';
import { ActionTypeExecutorResult } from '../../../../../actions/common';
import { getExecuteConnectorUrl } from '../../../../common/utils/connectors_api';
import { IssueTypes, Fields, Issues, Issue } from './types';
export const BASE_ACTION_API_PATH = '/api/actions';
export interface GetIssueTypesProps {
http: HttpSetup;
signal: AbortSignal;
@ -18,15 +17,12 @@ export interface GetIssueTypesProps {
}
export async function getIssueTypes({ http, signal, connectorId }: GetIssueTypesProps) {
return http.post<ActionTypeExecutorResult<IssueTypes>>(
`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`,
{
return http.post<ActionTypeExecutorResult<IssueTypes>>(getExecuteConnectorUrl(connectorId), {
body: JSON.stringify({
params: { subAction: 'issueTypes', subActionParams: {} },
}),
signal,
}
);
});
}
export interface GetFieldsByIssueTypeProps {
@ -42,7 +38,7 @@ export async function getFieldsByIssueType({
connectorId,
id,
}: GetFieldsByIssueTypeProps): Promise<ActionTypeExecutorResult<Fields>> {
return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, {
return http.post(getExecuteConnectorUrl(connectorId), {
body: JSON.stringify({
params: { subAction: 'fieldsByIssueType', subActionParams: { id } },
}),
@ -63,7 +59,7 @@ export async function getIssues({
connectorId,
title,
}: GetIssuesTypeProps): Promise<ActionTypeExecutorResult<Issues>> {
return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, {
return http.post(getExecuteConnectorUrl(connectorId), {
body: JSON.stringify({
params: { subAction: 'issues', subActionParams: { title } },
}),
@ -84,7 +80,7 @@ export async function getIssue({
connectorId,
id,
}: GetIssueTypeProps): Promise<ActionTypeExecutorResult<Issue>> {
return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, {
return http.post(getExecuteConnectorUrl(connectorId), {
body: JSON.stringify({
params: { subAction: 'issue', subActionParams: { id } },
}),

View file

@ -7,6 +7,7 @@
import { HttpSetup } from 'kibana/public';
import { ActionTypeExecutorResult } from '../../../../../actions/common';
import { getExecuteConnectorUrl } from '../../../../common/utils/connectors_api';
import { ResilientIncidentTypes, ResilientSeverity } from './types';
export const BASE_ACTION_API_PATH = '/api/actions';
@ -19,7 +20,7 @@ export interface Props {
export async function getIncidentTypes({ http, signal, connectorId }: Props) {
return http.post<ActionTypeExecutorResult<ResilientIncidentTypes>>(
`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`,
getExecuteConnectorUrl(connectorId),
{
body: JSON.stringify({
params: { subAction: 'incidentTypes', subActionParams: {} },
@ -31,7 +32,7 @@ export async function getIncidentTypes({ http, signal, connectorId }: Props) {
export async function getSeverity({ http, signal, connectorId }: Props) {
return http.post<ActionTypeExecutorResult<ResilientSeverity>>(
`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`,
getExecuteConnectorUrl(connectorId),
{
body: JSON.stringify({
params: { subAction: 'severity', subActionParams: {} },

View file

@ -31,7 +31,7 @@ describe('ServiceNow API', () => {
});
expect(res).toEqual(choicesResponse);
expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', {
expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
body: '{"params":{"subAction":"getChoices","subActionParams":{"fields":["priority"]}}}',
signal: abortCtrl.signal,
});

View file

@ -7,6 +7,7 @@
import { HttpSetup } from 'kibana/public';
import { ActionTypeExecutorResult } from '../../../../../actions/common';
import { getExecuteConnectorUrl } from '../../../../common/utils/connectors_api';
import { Choice } from './types';
export const BASE_ACTION_API_PATH = '/api/actions';
@ -19,13 +20,10 @@ export interface GetChoicesProps {
}
export async function getChoices({ http, signal, connectorId, fields }: GetChoicesProps) {
return http.post<ActionTypeExecutorResult<Choice[]>>(
`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`,
{
return http.post<ActionTypeExecutorResult<Choice[]>>(getExecuteConnectorUrl(connectorId), {
body: JSON.stringify({
params: { subAction: 'getChoices', subActionParams: { fields } },
}),
signal,
}
);
});
}

View file

@ -91,7 +91,7 @@ describe('Case Configuration API', () => {
test('check url, method, signal', async () => {
await getActionLicense(abortCtrl.signal);
expect(fetchMock).toHaveBeenCalledWith(`/api/actions/list_action_types`, {
expect(fetchMock).toHaveBeenCalledWith(`/api/actions/connector_types`, {
method: 'GET',
signal: abortCtrl.signal,
});

View file

@ -8,7 +8,6 @@
import { assign, omit } from 'lodash';
import {
ACTION_TYPES_URL,
CASE_REPORTERS_URL,
CASE_STATUS_URL,
CASE_TAGS_URL,
@ -38,6 +37,8 @@ import {
User,
} from '../../common';
import { getAllConnectorTypesUrl } from '../../common/utils/connectors_api';
import { KibanaServices } from '../common/lib/kibana';
import {
@ -351,9 +352,13 @@ export const pushCase = async (
};
export const getActionLicense = async (signal: AbortSignal): Promise<ActionLicense[]> => {
const response = await KibanaServices.get().http.fetch<ActionLicense[]>(ACTION_TYPES_URL, {
const response = await KibanaServices.get().http.fetch<ActionLicense[]>(
getAllConnectorTypesUrl(),
{
method: 'GET',
signal,
});
return response;
}
);
return convertArrayToCamelCase(response) as ActionLicense[];
};

View file

@ -151,7 +151,7 @@ describe('Case Configuration API', () => {
test('check url, method, signal', async () => {
await fetchActionTypes({ signal: abortCtrl.signal });
expect(fetchMock).toHaveBeenCalledWith('/api/actions/list_action_types', {
expect(fetchMock).toHaveBeenCalledWith('/api/actions/connector_types', {
method: 'GET',
signal: abortCtrl.signal,
});

View file

@ -6,8 +6,8 @@
*/
import { isEmpty } from 'lodash/fp';
import { getAllConnectorTypesUrl } from '../../../common/utils/connectors_api';
import {
ACTION_TYPES_URL,
ActionConnector,
ActionTypeConnector,
CASE_CONFIGURE_CONNECTORS_URL,
@ -22,6 +22,7 @@ import { KibanaServices } from '../../common/lib/kibana';
import { ApiProps } from '../types';
import {
convertArrayToCamelCase,
convertToCamelCase,
decodeCaseConfigurationsResponse,
decodeCaseConfigureResponse,
@ -60,15 +61,6 @@ export const getCaseConfigure = async ({
return null;
};
export const getConnectorMappings = async ({ signal }: ApiProps): Promise<ActionConnector[]> => {
const response = await KibanaServices.get().http.fetch(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`, {
method: 'GET',
signal,
});
return response;
};
export const postCaseConfigure = async (
caseConfiguration: CasesConfigureRequest,
signal: AbortSignal
@ -105,10 +97,10 @@ export const patchCaseConfigure = async (
};
export const fetchActionTypes = async ({ signal }: ApiProps): Promise<ActionTypeConnector[]> => {
const response = await KibanaServices.get().http.fetch(ACTION_TYPES_URL, {
const response = await KibanaServices.get().http.fetch(getAllConnectorTypesUrl(), {
method: 'GET',
signal,
});
return response;
return convertArrayToCamelCase(response) as ActionTypeConnector[];
};

View file

@ -18,6 +18,10 @@ import {
} from '../../../common';
import { ActionResult, ActionTypeExecutorResult } from '../../../../actions/common';
import { ContextTypeGeneratedAlertType, createAlertsString } from '../../connectors';
import {
getCreateConnectorUrl,
getExecuteConnectorUrl,
} from '../../../common/utils/connectors_api';
main();
@ -71,7 +75,7 @@ async function handleGenGroupAlerts(argv: any) {
try {
const createdAction = await client.request<ActionResult>({
path: '/api/actions/action',
path: getCreateConnectorUrl(),
method: 'POST',
body: {
name: 'A case connector',
@ -121,7 +125,7 @@ async function handleGenGroupAlerts(argv: any) {
};
const executeResp = await client.request<ActionTypeExecutorResult<CaseResponse>>({
path: `/api/actions/action/${createdAction.data.id}/_execute`,
path: getExecuteConnectorUrl(createdAction.data.id),
method: 'POST',
body: {
params: {

View file

@ -31,27 +31,31 @@ describe('Cases connector incident fields', () => {
beforeEach(() => {
cleanKibana();
cy.intercept('GET', '/api/cases/configure/connectors/_find', getMockConnectorsResponse());
cy.intercept('POST', `/api/actions/action/${getConnectorIds().sn}/_execute`, (req) => {
cy.intercept('POST', `/api/actions/connector/${getConnectorIds().sn}/_execute`, (req) => {
const response =
req.body.params.subAction === 'getChoices'
? getExecuteResponses().servicenow.choices
: { status: 'ok', data: [] };
req.reply(response);
});
cy.intercept('POST', `/api/actions/action/${getConnectorIds().jira}/_execute`, (req) => {
cy.intercept('POST', `/api/actions/connector/${getConnectorIds().jira}/_execute`, (req) => {
const response =
req.body.params.subAction === 'issueTypes'
? getExecuteResponses().jira.issueTypes
: getExecuteResponses().jira.fieldsByIssueType;
req.reply(response);
});
cy.intercept('POST', `/api/actions/action/${getConnectorIds().resilient}/_execute`, (req) => {
cy.intercept(
'POST',
`/api/actions/connector/${getConnectorIds().resilient}/_execute`,
(req) => {
const response =
req.body.params.subAction === 'incidentTypes'
? getExecuteResponses().resilient.incidentTypes
: getExecuteResponses().resilient.severity;
req.reply(response);
});
}
);
});
it('Correct incident fields show when connector is changed', () => {

View file

@ -62,6 +62,7 @@ export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] =>
return [...acc, value];
}
}, []);
export const convertToCamelCase = <T, U extends {}>(snakeCase: T): U =>
Object.entries(snakeCase).reduce((acc, [key, value]) => {
if (isArray(value)) {
@ -73,6 +74,7 @@ export const convertToCamelCase = <T, U extends {}>(snakeCase: T): U =>
}
return acc;
}, {} as U);
export const useCurrentUser = (): AuthenticatedElasticUser | null => {
const isMounted = useRef(false);
const [user, setUser] = useState<AuthenticatedElasticUser | null>(null);

View file

@ -21,7 +21,7 @@ import {
ActionVariables,
} from '../../../../../../triggers_actions_ui/public';
import { AlertAction } from '../../../../../../alerting/common';
import { useKibana } from '../../../../common/lib/kibana';
import { convertArrayToCamelCase, useKibana } from '../../../../common/lib/kibana';
import { FORM_ERRORS_TITLE } from './translations';
interface Props {
@ -137,7 +137,7 @@ export const RuleActionsField: React.FC<Props> = ({
useEffect(() => {
(async function () {
const actionTypes = await loadActionTypes({ http });
const actionTypes = convertArrayToCamelCase(await loadActionTypes({ http })) as ActionType[];
const supportedTypes = getSupportedActions(actionTypes, hasErrorOnCreationCaseAction);
setSupportedActionTypes(supportedTypes);
})();

View file

@ -6,11 +6,11 @@
*/
import { useEffect, useRef, useState } from 'react';
import { ACTION_URL } from '../../../../../../cases/common';
import { KibanaServices } from '../../../../common/lib/kibana';
import { getAllConnectorsUrl, getCreateConnectorUrl } from '../../../../../../cases/common';
import { convertArrayToCamelCase, KibanaServices } from '../../../../common/lib/kibana';
interface CaseAction {
actionTypeId: string;
connectorTypeId: string;
id: string;
isPreconfigured: boolean;
name: string;
@ -28,15 +28,18 @@ export const useManageCaseAction = () => {
const abortCtrl = new AbortController();
const fetchActions = async () => {
try {
const actions = await KibanaServices.get().http.fetch<CaseAction[]>(ACTION_URL, {
const actions = convertArrayToCamelCase(
await KibanaServices.get().http.fetch<CaseAction[]>(getAllConnectorsUrl(), {
method: 'GET',
signal: abortCtrl.signal,
});
if (!actions.some((a) => a.actionTypeId === '.case' && a.name === CASE_ACTION_NAME)) {
await KibanaServices.get().http.post<CaseAction[]>(`${ACTION_URL}/action`, {
})
) as CaseAction[];
if (!actions.some((a) => a.connectorTypeId === '.case' && a.name === CASE_ACTION_NAME)) {
await KibanaServices.get().http.post<CaseAction[]>(getCreateConnectorUrl(), {
method: 'POST',
body: JSON.stringify({
actionTypeId: '.case',
connector_type_id: '.case',
config: {},
name: CASE_ACTION_NAME,
secrets: {},

View file

@ -14,5 +14,5 @@ set -e
# https://github.com/elastic/kibana/blob/master/x-pack/plugins/actions/README.md
curl -s -k \
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-X GET ${KIBANA_URL}${SPACE_URL}/api/actions/list_action_types \
-X GET ${KIBANA_URL}${SPACE_URL}/api/actions/connector_types \
| jq .

View file

@ -14,7 +14,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context';
export default function listActionTypesTests({ getService }: FtrProviderContext) {
const supertestWithoutAuth = getService('supertestWithoutAuth');
describe('list_action_types', () => {
describe('connector_types', () => {
for (const scenario of UserAtSpaceScenarios) {
const { user, space } = scenario;
describe(scenario.id, () => {

View file

@ -24,6 +24,7 @@ import {
ExternalServiceSimulator,
getExternalServiceSimulatorPath,
} from '../../../../../../alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin';
import { getCreateConnectorUrl } from '../../../../../../../plugins/cases/common/utils/connectors_api';
// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext): void => {
@ -49,7 +50,7 @@ export default ({ getService }: FtrProviderContext): void => {
it(`on new push to service, user action: 'push-to-service' should be called with actionFields: ['pushed']`, async () => {
const { body: connector } = await supertest
.post('/api/actions/connector')
.post(getCreateConnectorUrl())
.set('kbn-xsrf', 'true')
.send({
...getServiceNowConnector(),