[Alerting][Connectors] Add new executor subaction to get 3rd party case fields (#82519)

This commit is contained in:
Steph Milovic 2020-11-09 10:08:00 -07:00 committed by GitHub
parent 1885dda6e6
commit e1b7073a64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 2163 additions and 1455 deletions

View file

@ -69,18 +69,21 @@ Table of Contents
- [`secrets`](#secrets-6)
- [`params`](#params-6)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice)
- [`subActionParams (getFields)`](#subactionparams-getfields-1)
- [Jira](#jira)
- [`config`](#config-7)
- [`secrets`](#secrets-7)
- [`params`](#params-7)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1)
- [`subActionParams (issueTypes)`](#subactionparams-issuetypes)
- [`subActionParams (getFields)`](#subactionparams-getfields-2)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2)
- [IBM Resilient](#ibm-resilient)
- [`config`](#config-8)
- [`secrets`](#secrets-8)
- [`params`](#params-8)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3)
- [`subActionParams (getFields)`](#subactionparams-getfields-3)
- [Command Line Utility](#command-line-utility)
- [Developing New Action Types](#developing-new-action-types)
- [licensing](#licensing)
@ -563,7 +566,7 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------ | ------ |
| subAction | The sub action to perform. It can be `pushToService`, `handshake`, and `getIncident` | string |
| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string |
| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
@ -580,6 +583,10 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a
| urgency | The name of the urgency in ServiceNow. | string _(optional)_ |
| impact | The name of the impact in ServiceNow. | string _(optional)_ |
#### `subActionParams (getFields)`
No parameters for `getFields` sub-action. Provide an empty object `{}`.
---
## Jira
@ -606,7 +613,7 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla
| Property | Description | Type |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- | ------ |
| subAction | The sub action to perform. It can be `pushToService`, `handshake`, `getIncident`, `issueTypes`, and `fieldsByIssueType` | string |
| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, `getIncident`, `issueTypes`, and `fieldsByIssueType` | string |
| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
@ -627,6 +634,10 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla
No parameters for `issueTypes` sub-action. Provide an empty object `{}`.
#### `subActionParams (getFields)`
No parameters for `getFields` sub-action. Provide an empty object `{}`.
#### `subActionParams (pushToService)`
| Property | Description | Type |
@ -655,7 +666,7 @@ ID: `.resilient`
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------ | ------ |
| subAction | The sub action to perform. It can be `pushToService`, `handshake`, and `getIncident` | string |
| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string |
| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
@ -670,6 +681,10 @@ ID: `.resilient`
| incidentTypes | An array with the ids of IBM Resilient incident types. | number[] _(optional)_ |
| severityCode | IBM Resilient id of the severity code. | number _(optional)_ |
#### `subActionParams (getFields)`
No parameters for `getFields` sub-action. Provide an empty object `{}`.
# Command Line Utility
The [`kbn-action`](https://github.com/pmuellr/kbn-action) tool can be used to send HTTP requests to the Actions plugin. For instance, to create a Slack action from the `.slack` Action Type, use the following command:

View file

@ -17,6 +17,7 @@ import {
PushToServiceApiParams,
PushToServiceResponse,
GetIssueHandlerArgs,
GetCommonFieldsHandlerArgs,
} from './types';
// TODO: to remove, need to support Case
@ -39,6 +40,11 @@ const getIssueTypesHandler = async ({ externalService }: GetIssueTypesHandlerArg
return res;
};
const getFieldsHandler = async ({ externalService }: GetCommonFieldsHandlerArgs) => {
const res = await externalService.getFields();
return res;
};
const getFieldsByIssueTypeHandler = async ({
externalService,
params,
@ -157,6 +163,7 @@ const pushToServiceHandler = async ({
};
export const api: ExternalServiceApi = {
getFields: getFieldsHandler,
handshake: handshakeHandler,
pushToService: pushToServiceHandler,
getIncident: getIncidentHandler,

View file

@ -40,6 +40,7 @@ interface GetActionTypeParams {
}
const supportedSubActions: string[] = [
'getFields',
'pushToService',
'issueTypes',
'fieldsByIssueType',
@ -145,6 +146,13 @@ async function executor(
});
}
if (subAction === 'getFields') {
data = await api.getFields({
externalService,
params: subActionParams,
});
}
if (subAction === 'issues') {
const getIssuesParams = subActionParams as ExecutorSubActionGetIssuesParams;
data = await api.issues({

View file

@ -73,6 +73,20 @@ const createMock = (): jest.Mocked<ExternalService> => {
key: 'RJ-107',
title: 'Test title',
})),
getFields: jest.fn().mockImplementation(() => ({
description: {
allowedValues: [],
defaultValue: {},
required: true,
schema: { type: 'string' },
},
summary: {
allowedValues: [],
defaultValue: {},
required: true,
schema: { type: 'string' },
},
})),
};
service.createComment.mockImplementationOnce(() =>
@ -97,7 +111,6 @@ const createMock = (): jest.Mocked<ExternalService> => {
const externalServiceMock = {
create: createMock,
};
const mapping: Map<string, Partial<MapRecord>> = new Map();
mapping.set('title', {

View file

@ -55,6 +55,7 @@ export const ExecutorSubActionGetIncidentParamsSchema = schema.object({
});
// Reserved for future implementation
export const ExecutorSubActionCommonFieldsParamsSchema = schema.object({});
export const ExecutorSubActionHandshakeParamsSchema = schema.object({});
export const ExecutorSubActionGetCapabilitiesParamsSchema = schema.object({});
export const ExecutorSubActionGetIssueTypesParamsSchema = schema.object({});
@ -65,6 +66,10 @@ export const ExecutorSubActionGetIssuesParamsSchema = schema.object({ title: sch
export const ExecutorSubActionGetIssueParamsSchema = schema.object({ id: schema.string() });
export const ExecutorParamsSchema = schema.oneOf([
schema.object({
subAction: schema.literal('getFields'),
subActionParams: ExecutorSubActionCommonFieldsParamsSchema,
}),
schema.object({
subAction: schema.literal('getIncident'),
subActionParams: ExecutorSubActionGetIncidentParamsSchema,

View file

@ -57,8 +57,10 @@ const fieldsResponse = {
id: '10006',
name: 'Task',
fields: {
summary: { fieldId: 'summary' },
summary: { required: true, schema: { type: 'string' }, fieldId: 'summary' },
priority: {
required: false,
schema: { type: 'string' },
fieldId: 'priority',
allowedValues: [
{
@ -198,7 +200,7 @@ describe('Jira service', () => {
error.response = { data: { errors: { summary: 'Required field' } } };
throw error;
});
expect(service.getIncident('1')).rejects.toThrow(
await expect(service.getIncident('1')).rejects.toThrow(
'[Action][Jira]: Unable to get incident with id 1. Error: An error has occurred Reason: Required field'
);
});
@ -348,7 +350,7 @@ describe('Jira service', () => {
throw error;
});
expect(
await expect(
service.createIncident({
incident: {
summary: 'title',
@ -442,7 +444,7 @@ describe('Jira service', () => {
throw error;
});
expect(
await expect(
service.updateIncident({
incidentId: '1',
incident: {
@ -526,7 +528,7 @@ describe('Jira service', () => {
throw error;
});
expect(
await expect(
service.createComment({
incidentId: '1',
comment: {
@ -587,7 +589,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getCapabilities()).rejects.toThrow(
await expect(service.getCapabilities()).rejects.toThrow(
'[Action][Jira]: Unable to get capabilities. Error: An error has occurred. Reason: Could not get capabilities'
);
});
@ -657,7 +659,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getIssueTypes()).rejects.toThrow(
await expect(service.getIssueTypes()).rejects.toThrow(
'[Action][Jira]: Unable to get issue types. Error: An error has occurred. Reason: Could not get issue types'
);
});
@ -741,7 +743,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getIssueTypes()).rejects.toThrow(
await expect(service.getIssueTypes()).rejects.toThrow(
'[Action][Jira]: Unable to get issue types. Error: An error has occurred. Reason: Could not get issue types'
);
});
@ -765,6 +767,8 @@ describe('Jira service', () => {
expect(res).toEqual({
priority: {
required: false,
schema: { type: 'string' },
allowedValues: [
{ id: '1', name: 'Highest' },
{ id: '2', name: 'High' },
@ -774,7 +778,12 @@ describe('Jira service', () => {
],
defaultValue: { id: '3', name: 'Medium' },
},
summary: { allowedValues: [], defaultValue: {} },
summary: {
required: true,
schema: { type: 'string' },
allowedValues: [],
defaultValue: {},
},
});
});
@ -815,7 +824,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getFieldsByIssueType('10006')).rejects.toThrow(
await expect(service.getFieldsByIssueType('10006')).rejects.toThrow(
'[Action][Jira]: Unable to get fields. Error: An error has occurred. Reason: Could not get fields'
);
});
@ -837,8 +846,10 @@ describe('Jira service', () => {
requestMock.mockImplementationOnce(() => ({
data: {
values: [
{ fieldId: 'summary' },
{ required: true, schema: { type: 'string' }, fieldId: 'summary' },
{
required: false,
schema: { type: 'string' },
fieldId: 'priority',
allowedValues: [
{
@ -859,10 +870,17 @@ describe('Jira service', () => {
expect(res).toEqual({
priority: {
required: false,
schema: { type: 'string' },
allowedValues: [{ id: '3', name: 'Medium' }],
defaultValue: { id: '3', name: 'Medium' },
},
summary: { allowedValues: [], defaultValue: {} },
summary: {
required: true,
schema: { type: 'string' },
allowedValues: [],
defaultValue: {},
},
});
});
@ -881,8 +899,10 @@ describe('Jira service', () => {
requestMock.mockImplementationOnce(() => ({
data: {
values: [
{ fieldId: 'summary' },
{ required: true, schema: { type: 'string' }, fieldId: 'summary' },
{
required: true,
schema: { type: 'string' },
fieldId: 'priority',
allowedValues: [
{
@ -927,7 +947,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getFieldsByIssueType('10006')).rejects.toThrow(
await expect(service.getFieldsByIssueType('10006')).rejects.toThrowError(
'[Action][Jira]: Unable to get fields. Error: An error has occurred. Reason: Could not get issue types'
);
});
@ -976,7 +996,7 @@ describe('Jira service', () => {
throw error;
});
expect(service.getIssues('Test title')).rejects.toThrow(
await expect(service.getIssues('Test title')).rejects.toThrow(
'[Action][Jira]: Unable to get issues. Error: An error has occurred. Reason: Could not get issue types'
);
});
@ -1020,9 +1040,128 @@ describe('Jira service', () => {
throw error;
});
expect(service.getIssue('RJ-107')).rejects.toThrow(
await expect(service.getIssue('RJ-107')).rejects.toThrow(
'[Action][Jira]: Unable to get issue with id RJ-107. Error: An error has occurred. Reason: Could not get issue types'
);
});
});
describe('getFields', () => {
const callMocks = () => {
requestMock
.mockImplementationOnce(() => ({
data: {
capabilities: {
'list-project-issuetypes':
'https://siem-kibana.atlassian.net/rest/capabilities/list-project-issuetypes',
'list-issuetype-fields':
'https://siem-kibana.atlassian.net/rest/capabilities/list-issuetype-fields',
},
},
}))
.mockImplementationOnce(() => ({
data: {
values: issueTypesResponse.data.projects[0].issuetypes,
},
}))
.mockImplementationOnce(() => ({
data: {
capabilities: {
'list-project-issuetypes':
'https://siem-kibana.atlassian.net/rest/capabilities/list-project-issuetypes',
'list-issuetype-fields':
'https://siem-kibana.atlassian.net/rest/capabilities/list-issuetype-fields',
},
},
}))
.mockImplementationOnce(() => ({
data: {
capabilities: {
'list-project-issuetypes':
'https://siem-kibana.atlassian.net/rest/capabilities/list-project-issuetypes',
'list-issuetype-fields':
'https://siem-kibana.atlassian.net/rest/capabilities/list-issuetype-fields',
},
},
}))
.mockImplementationOnce(() => ({
data: {
values: [
{ required: true, schema: { type: 'string' }, fieldId: 'summary' },
{ required: true, schema: { type: 'string' }, fieldId: 'description' },
{
required: false,
schema: { type: 'string' },
fieldId: 'priority',
allowedValues: [
{
name: 'Medium',
id: '3',
},
],
defaultValue: {
name: 'Medium',
id: '3',
},
},
],
},
}))
.mockImplementationOnce(() => ({
data: {
values: [
{ required: true, schema: { type: 'string' }, fieldId: 'summary' },
{ required: true, schema: { type: 'string' }, fieldId: 'description' },
],
},
}));
};
beforeEach(() => {
jest.resetAllMocks();
});
test('it should call request with correct arguments', async () => {
callMocks();
await service.getFields();
const callUrls = [
'https://siem-kibana.atlassian.net/rest/capabilities',
'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta/CK/issuetypes',
'https://siem-kibana.atlassian.net/rest/capabilities',
'https://siem-kibana.atlassian.net/rest/capabilities',
'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta/CK/issuetypes/10006',
'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta/CK/issuetypes/10007',
];
requestMock.mock.calls.forEach((call, i) => {
expect(call[0].url).toEqual(callUrls[i]);
});
});
test('it returns common fields correctly', async () => {
callMocks();
const res = await service.getFields();
expect(res).toEqual({
description: {
allowedValues: [],
defaultValue: {},
required: true,
schema: { type: 'string' },
},
summary: {
allowedValues: [],
defaultValue: {},
required: true,
schema: { type: 'string' },
},
});
});
test('it should throw an error', async () => {
requestMock.mockImplementation(() => {
const error: ResponseError = new Error('An error has occurred');
error.response = { data: { errors: { summary: 'Required field' } } };
throw error;
});
await expect(service.getFields()).rejects.toThrow(
'[Action][Jira]: Unable to get capabilities. Error: An error has occurred. Reason: Required field'
);
});
});
});

View file

@ -8,18 +8,20 @@ import axios from 'axios';
import { Logger } from '../../../../../../src/core/server';
import {
ExternalServiceCredentials,
ExternalService,
CreateCommentParams,
CreateIncidentParams,
UpdateIncidentParams,
ExternalService,
ExternalServiceCommentResponse,
ExternalServiceCredentials,
ExternalServiceIncidentResponse,
Fields,
FieldSchema,
GetCommonFieldsResponse,
Incident,
JiraPublicConfigurationType,
JiraSecretConfigurationType,
Fields,
CreateCommentParams,
Incident,
ResponseError,
ExternalServiceCommentResponse,
ExternalServiceIncidentResponse,
UpdateIncidentParams,
} from './types';
import * as i18n from './translations';
@ -127,14 +129,21 @@ export const createExternalService = (
issueTypes.map((type) => ({ id: type.id, name: type.name }));
const normalizeFields = (fields: {
[key: string]: { allowedValues?: Array<{}>; defaultValue?: {} };
[key: string]: {
allowedValues?: Array<{}>;
defaultValue?: {};
required: boolean;
schema: FieldSchema;
};
}) =>
Object.keys(fields ?? {}).reduce((fieldsAcc, fieldKey) => {
return {
...fieldsAcc,
[fieldKey]: {
required: fields[fieldKey]?.required,
allowedValues: fields[fieldKey]?.allowedValues ?? [],
defaultValue: fields[fieldKey]?.defaultValue ?? {},
schema: fields[fieldKey]?.schema,
},
};
}, {});
@ -326,7 +335,6 @@ export const createExternalService = (
const getIssueTypes = async () => {
const capabilitiesResponse = await getCapabilities();
const supportsNewAPI = hasSupportForNewAPI(capabilitiesResponse);
try {
if (!supportsNewAPI) {
const res = await request({
@ -366,7 +374,6 @@ export const createExternalService = (
const getFieldsByIssueType = async (issueTypeId: string) => {
const capabilitiesResponse = await getCapabilities();
const supportsNewAPI = hasSupportForNewAPI(capabilitiesResponse);
try {
if (!supportsNewAPI) {
const res = await request({
@ -378,6 +385,7 @@ export const createExternalService = (
});
const fields = res.data.projects[0]?.issuetypes[0]?.fields || {};
return normalizeFields(fields);
} else {
const res = await request({
@ -409,6 +417,30 @@ export const createExternalService = (
}
};
const getFields = async () => {
try {
const issueTypes = await getIssueTypes();
const fieldsPerIssueType = await Promise.all(
issueTypes.map((issueType) => getFieldsByIssueType(issueType.id))
);
return fieldsPerIssueType.reduce((acc: GetCommonFieldsResponse, fieldTypesByIssue) => {
const currentListOfFields = Object.keys(acc);
return currentListOfFields.length === 0
? fieldTypesByIssue
: currentListOfFields.reduce(
(add: GetCommonFieldsResponse, field) =>
Object.keys(fieldTypesByIssue).includes(field)
? { ...add, [field]: acc[field] }
: add,
{}
);
}, {});
} catch (error) {
// errors that happen here would be thrown in the contained async calls
throw error;
}
};
const getIssues = async (title: string) => {
const query = `${searchUrl}?jql=${encodeURIComponent(
`project="${projectKey}" and summary ~"${title}"`
@ -461,6 +493,7 @@ export const createExternalService = (
};
return {
getFields,
getIncident,
createIncident,
updateIncident,

View file

@ -79,11 +79,34 @@ export interface CreateCommentParams {
comment: Comment;
}
export interface FieldsSchema {
type: string;
[key: string]: string;
}
export interface ExternalServiceFields {
clauseNames: string[];
custom: boolean;
id: string;
key: string;
name: string;
navigatable: boolean;
orderable: boolean;
schema: FieldsSchema;
searchable: boolean;
}
export type GetIssueTypesResponse = Array<{ id: string; name: string }>;
export interface FieldSchema {
type: string;
items?: string;
}
export type GetFieldsByIssueTypeResponse = Record<
string,
{ allowedValues: Array<{}>; defaultValue: {} }
{ allowedValues: Array<{}>; defaultValue: {}; required: boolean; schema: FieldSchema }
>;
export type GetCommonFieldsResponse = GetFieldsByIssueTypeResponse;
export type GetIssuesResponse = Array<{ id: string; key: string; title: string }>;
export interface GetIssueResponse {
@ -93,15 +116,16 @@ export interface GetIssueResponse {
}
export interface ExternalService {
getIncident: (id: string) => Promise<ExternalServiceParams | undefined>;
createIncident: (params: CreateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
updateIncident: (params: UpdateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
createComment: (params: CreateCommentParams) => Promise<ExternalServiceCommentResponse>;
createIncident: (params: CreateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
getFields: () => Promise<GetCommonFieldsResponse>;
getCapabilities: () => Promise<ExternalServiceParams>;
getIssueTypes: () => Promise<GetIssueTypesResponse>;
getFieldsByIssueType: (issueTypeId: string) => Promise<GetFieldsByIssueTypeResponse>;
getIssues: (title: string) => Promise<GetIssuesResponse>;
getIncident: (id: string) => Promise<ExternalServiceParams | undefined>;
getIssue: (id: string) => Promise<GetIssueResponse>;
getIssues: (title: string) => Promise<GetIssuesResponse>;
getIssueTypes: () => Promise<GetIssueTypesResponse>;
updateIncident: (params: UpdateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
}
export interface PushToServiceApiParams extends ExecutorSubActionPushParams {
@ -157,6 +181,11 @@ export interface GetIssueTypesHandlerArgs {
params: ExecutorSubActionGetIssueTypesParams;
}
export interface GetCommonFieldsHandlerArgs {
externalService: ExternalService;
params: ExecutorSubActionGetIssueTypesParams;
}
export interface GetFieldsByIssueTypeHandlerArgs {
externalService: ExternalService;
params: ExecutorSubActionGetFieldsByIssueTypeParams;
@ -177,15 +206,16 @@ export interface GetIssueHandlerArgs {
}
export interface ExternalServiceApi {
handshake: (args: HandshakeApiHandlerArgs) => Promise<void>;
pushToService: (args: PushToServiceApiHandlerArgs) => Promise<PushToServiceResponse>;
getFields: (args: GetCommonFieldsHandlerArgs) => Promise<GetCommonFieldsResponse>;
getIncident: (args: GetIncidentApiHandlerArgs) => Promise<void>;
handshake: (args: HandshakeApiHandlerArgs) => Promise<void>;
issueTypes: (args: GetIssueTypesHandlerArgs) => Promise<GetIssueTypesResponse>;
pushToService: (args: PushToServiceApiHandlerArgs) => Promise<PushToServiceResponse>;
fieldsByIssueType: (
args: GetFieldsByIssueTypeHandlerArgs
) => Promise<GetFieldsByIssueTypeResponse>;
issues: (args: GetIssuesHandlerArgs) => Promise<GetIssuesResponse>;
issue: (args: GetIssueHandlerArgs) => Promise<GetIssueResponse>;
issues: (args: GetIssuesHandlerArgs) => Promise<GetIssuesResponse>;
}
export type JiraExecutorResultData =

View file

@ -15,6 +15,7 @@ import {
GetSeverityHandlerArgs,
PushToServiceApiParams,
PushToServiceResponse,
GetCommonFieldsHandlerArgs,
} from './types';
// TODO: to remove, need to support Case
@ -32,6 +33,10 @@ const getIncidentHandler = async ({
params,
}: GetIncidentApiHandlerArgs) => {};
const getFieldsHandler = async ({ externalService }: GetCommonFieldsHandlerArgs) => {
const res = await externalService.getFields();
return res;
};
const getIncidentTypesHandler = async ({ externalService }: GetIncidentTypesHandlerArgs) => {
const res = await externalService.getIncidentTypes();
return res;
@ -136,9 +141,10 @@ const pushToServiceHandler = async ({
};
export const api: ExternalServiceApi = {
handshake: handshakeHandler,
pushToService: pushToServiceHandler,
getFields: getFieldsHandler,
getIncident: getIncidentHandler,
handshake: handshakeHandler,
incidentTypes: getIncidentTypesHandler,
pushToService: pushToServiceHandler,
severity: getSeverityHandler,
};

View file

@ -25,6 +25,7 @@ import {
ResilientExecutorResultData,
ExecutorSubActionGetIncidentTypesParams,
ExecutorSubActionGetSeverityParams,
ExecutorSubActionCommonFieldsParams,
} from './types';
import * as i18n from './translations';
import { Logger } from '../../../../../../src/core/server';
@ -37,7 +38,7 @@ interface GetActionTypeParams {
configurationUtilities: ActionsConfigurationUtilities;
}
const supportedSubActions: string[] = ['pushToService', 'incidentTypes', 'severity'];
const supportedSubActions: string[] = ['getFields', 'pushToService', 'incidentTypes', 'severity'];
// action type definition
export function getActionType(
@ -122,6 +123,14 @@ async function executor(
logger.debug(`response push to service for incident id: ${data.id}`);
}
if (subAction === 'getFields') {
const getFieldsParams = subActionParams as ExecutorSubActionCommonFieldsParams;
data = await api.getFields({
externalService,
params: getFieldsParams,
});
}
if (subAction === 'incidentTypes') {
const incidentTypesParams = subActionParams as ExecutorSubActionGetIncidentTypesParams;
data = await api.incidentTypes({

View file

@ -8,8 +8,275 @@ import { ExternalService, PushToServiceApiParams, ExecutorSubActionPushParams }
import { MapRecord } from '../case/types';
export const resilientFields = [
{
id: 17,
name: 'name',
text: 'Name',
prefix: null,
type_id: 0,
tooltip: 'A unique name to identify this particular incident.',
input_type: 'text',
required: 'always',
hide_notification: false,
chosen: false,
default_chosen_by_server: false,
blank_option: false,
internal: true,
uuid: 'ad6ed4f2-8d87-4ba2-81fa-03568a9326cc',
operations: [
'equals',
'not_equals',
'contains',
'not_contains',
'changed',
'changed_to',
'not_changed_to',
'has_a_value',
'not_has_a_value',
],
operation_perms: {
changed_to: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_changed_to: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
equals: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
changed: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
contains: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_contains: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_equals: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
},
values: [],
perms: {
delete: false,
modify_name: false,
modify_values: false,
modify_blank: false,
modify_required: false,
modify_operations: false,
modify_chosen: false,
modify_default: false,
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
show_in_scripts: true,
modify_type: ['text'],
sort: true,
},
read_only: false,
changeable: true,
rich_text: false,
templates: [],
deprecated: false,
tags: [],
calculated: false,
is_tracked: false,
allow_default_value: false,
},
{
id: 15,
name: 'description',
text: 'Description',
prefix: null,
type_id: 0,
tooltip: 'A free form text description of the incident.',
input_type: 'textarea',
hide_notification: false,
chosen: false,
default_chosen_by_server: false,
blank_option: false,
internal: true,
uuid: '420d70b1-98f9-4681-a20b-84f36a9e5e48',
operations: [
'equals',
'not_equals',
'contains',
'not_contains',
'changed',
'changed_to',
'not_changed_to',
'has_a_value',
'not_has_a_value',
],
operation_perms: {
changed_to: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_changed_to: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
equals: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
changed: {
show_in_manual_actions: false,
show_in_auto_actions: true,
show_in_notifications: true,
},
contains: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_contains: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_equals: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
},
values: [],
perms: {
delete: false,
modify_name: false,
modify_values: false,
modify_blank: false,
modify_required: false,
modify_operations: false,
modify_chosen: false,
modify_default: false,
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
show_in_scripts: true,
modify_type: ['textarea'],
sort: true,
},
read_only: false,
changeable: true,
rich_text: true,
templates: [],
deprecated: false,
tags: [],
calculated: false,
is_tracked: false,
allow_default_value: false,
},
{
id: 65,
name: 'create_date',
text: 'Date Created',
prefix: null,
type_id: 0,
tooltip: 'The date the incident was created. This field is read-only.',
input_type: 'datetimepicker',
hide_notification: false,
chosen: false,
default_chosen_by_server: false,
blank_option: false,
internal: true,
uuid: 'b4faf728-881a-4e8b-bf0b-d39b720392a1',
operations: ['due_within', 'overdue_by', 'has_a_value', 'not_has_a_value'],
operation_perms: {
has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
not_has_a_value: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
due_within: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
overdue_by: {
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
},
},
values: [],
perms: {
delete: false,
modify_name: false,
modify_values: false,
modify_blank: false,
modify_required: false,
modify_operations: false,
modify_chosen: false,
modify_default: false,
show_in_manual_actions: true,
show_in_auto_actions: true,
show_in_notifications: true,
show_in_scripts: true,
modify_type: ['datetimepicker'],
sort: true,
},
read_only: true,
changeable: false,
rich_text: false,
templates: [],
deprecated: false,
tags: [],
calculated: false,
is_tracked: false,
allow_default_value: false,
},
];
const createMock = (): jest.Mocked<ExternalService> => {
const service = {
getFields: jest.fn().mockImplementation(() => Promise.resolve(resilientFields)),
getIncident: jest.fn().mockImplementation(() =>
Promise.resolve({
id: '1',

View file

@ -53,11 +53,16 @@ export const ExecutorSubActionGetIncidentParamsSchema = schema.object({
});
// Reserved for future implementation
export const ExecutorSubActionCommonFieldsParamsSchema = schema.object({});
export const ExecutorSubActionHandshakeParamsSchema = schema.object({});
export const ExecutorSubActionGetIncidentTypesParamsSchema = schema.object({});
export const ExecutorSubActionGetSeverityParamsSchema = schema.object({});
export const ExecutorParamsSchema = schema.oneOf([
schema.object({
subAction: schema.literal('getFields'),
subActionParams: ExecutorSubActionCommonFieldsParamsSchema,
}),
schema.object({
subAction: schema.literal('getIncident'),
subActionParams: ExecutorSubActionGetIncidentParamsSchema,

View file

@ -11,7 +11,7 @@ import * as utils from '../lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '../../../../../../src/core/server';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { incidentTypes, severity } from './mocks';
import { incidentTypes, resilientFields, severity } from './mocks';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
@ -231,7 +231,7 @@ describe('IBM Resilient service', () => {
requestMock.mockImplementation(() => {
throw new Error('An error has occurred');
});
expect(service.getIncident('1')).rejects.toThrow(
await expect(service.getIncident('1')).rejects.toThrow(
'Unable to get incident with id 1. Error: An error has occurred'
);
});
@ -310,7 +310,7 @@ describe('IBM Resilient service', () => {
throw new Error('An error has occurred');
});
expect(
await expect(
service.createIncident({
incident: {
name: 'title',
@ -418,7 +418,7 @@ describe('IBM Resilient service', () => {
test('it should throw an error', async () => {
mockIncidentUpdate(true);
expect(
await expect(
service.updateIncident({
incidentId: '1',
incident: {
@ -502,7 +502,7 @@ describe('IBM Resilient service', () => {
throw new Error('An error has occurred');
});
expect(
await expect(
service.createComment({
incidentId: '1',
comment: {
@ -541,7 +541,7 @@ describe('IBM Resilient service', () => {
throw new Error('An error has occurred');
});
expect(service.getIncidentTypes()).rejects.toThrow(
await expect(service.getIncidentTypes()).rejects.toThrow(
'[Action][IBM Resilient]: Unable to get incident types. Error: An error has occurred.'
);
});
@ -578,9 +578,40 @@ describe('IBM Resilient service', () => {
throw new Error('An error has occurred');
});
expect(service.getIncidentTypes()).rejects.toThrow(
await expect(service.getIncidentTypes()).rejects.toThrow(
'[Action][IBM Resilient]: Unable to get incident types. Error: An error has occurred.'
);
});
});
describe('getFields', () => {
test('it should call request with correct arguments', async () => {
requestMock.mockImplementation(() => ({
data: resilientFields,
}));
await service.getFields();
expect(requestMock).toHaveBeenCalledWith({
axios,
logger,
url: 'https://resilient.elastic.co/rest/orgs/201/types/incident/fields',
});
});
test('it returns common fields correctly', async () => {
requestMock.mockImplementation(() => ({
data: resilientFields,
}));
const res = await service.getFields();
expect(res).toEqual(resilientFields);
});
test('it should throw an error', async () => {
requestMock.mockImplementation(() => {
throw new Error('An error has occurred');
});
await expect(service.getFields()).rejects.toThrow(
'Unable to get fields. Error: An error has occurred'
);
});
});
});

View file

@ -303,12 +303,27 @@ export const createExternalService = (
}
};
const getFields = async () => {
try {
const res = await request({
axios: axiosInstance,
url: incidentFieldsUrl,
logger,
proxySettings,
});
return res.data ?? [];
} catch (error) {
throw new Error(getErrorMessage(i18n.NAME, `Unable to get fields. Error: ${error.message}.`));
}
};
return {
getIncident,
createIncident,
updateIncident,
createComment,
createIncident,
getFields,
getIncident,
getIncidentTypes,
getSeverity,
updateIncident,
};
};

View file

@ -8,14 +8,15 @@
import { TypeOf } from '@kbn/config-schema';
import {
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
ExecutorParamsSchema,
ExecutorSubActionPushParamsSchema,
ExecutorSubActionCommonFieldsParamsSchema,
ExecutorSubActionGetIncidentParamsSchema,
ExecutorSubActionHandshakeParamsSchema,
ExecutorSubActionGetIncidentTypesParamsSchema,
ExecutorSubActionGetSeverityParamsSchema,
ExecutorSubActionHandshakeParamsSchema,
ExecutorSubActionPushParamsSchema,
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
} from './schema';
import { ActionsConfigurationUtilities } from '../../actions_config';
@ -31,6 +32,10 @@ export type ResilientSecretConfigurationType = TypeOf<
typeof ExternalIncidentServiceSecretConfigurationSchema
>;
export type ExecutorSubActionCommonFieldsParams = TypeOf<
typeof ExecutorSubActionCommonFieldsParamsSchema
>;
export type ExecutorParams = TypeOf<typeof ExecutorParamsSchema>;
export type ExecutorSubActionPushParams = TypeOf<typeof ExecutorSubActionPushParamsSchema>;
@ -60,6 +65,14 @@ export interface ExternalServiceCommentResponse {
}
export type ExternalServiceParams = Record<string, unknown>;
export interface ExternalServiceFields {
id: string;
input_type: string;
name: string;
read_only: boolean;
required?: string;
}
export type GetCommonFieldsResponse = ExternalServiceFields[];
export type Incident = Pick<
ExecutorSubActionPushParams,
@ -86,12 +99,13 @@ export type GetIncidentTypesResponse = Array<{ id: string; name: string }>;
export type GetSeverityResponse = Array<{ id: string; name: string }>;
export interface ExternalService {
getIncident: (id: string) => Promise<ExternalServiceParams | undefined>;
createIncident: (params: CreateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
updateIncident: (params: UpdateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
createComment: (params: CreateCommentParams) => Promise<ExternalServiceCommentResponse>;
createIncident: (params: CreateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
getFields: () => Promise<GetCommonFieldsResponse>;
getIncident: (id: string) => Promise<ExternalServiceParams | undefined>;
getIncidentTypes: () => Promise<GetIncidentTypesResponse>;
getSeverity: () => Promise<GetSeverityResponse>;
updateIncident: (params: UpdateIncidentParams) => Promise<ExternalServiceIncidentResponse>;
}
export interface PushToServiceApiParams extends ExecutorSubActionPushParams {
@ -132,6 +146,11 @@ export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs {
params: ExecutorSubActionHandshakeParams;
}
export interface GetCommonFieldsHandlerArgs {
externalService: ExternalService;
params: ExecutorSubActionCommonFieldsParams;
}
export interface GetIncidentTypesHandlerArgs {
externalService: ExternalService;
params: ExecutorSubActionGetIncidentTypesParams;
@ -147,6 +166,7 @@ export interface PushToServiceResponse extends ExternalServiceIncidentResponse {
}
export interface ExternalServiceApi {
getFields: (args: GetCommonFieldsHandlerArgs) => Promise<GetCommonFieldsResponse>;
handshake: (args: HandshakeApiHandlerArgs) => Promise<void>;
pushToService: (args: PushToServiceApiHandlerArgs) => Promise<PushToServiceResponse>;
getIncident: (args: GetIncidentApiHandlerArgs) => Promise<void>;
@ -156,6 +176,7 @@ export interface ExternalServiceApi {
export type ResilientExecutorResultData =
| PushToServiceResponse
| GetCommonFieldsResponse
| GetIncidentTypesResponse
| GetSeverityResponse;

View file

@ -12,6 +12,8 @@ import {
PushToServiceApiParams,
PushToServiceResponse,
Incident,
GetCommonFieldsHandlerArgs,
GetCommonFieldsResponse,
} from './types';
// TODO: to remove, need to support Case
@ -127,8 +129,16 @@ const pushToServiceHandler = async ({
return res;
};
const getFieldsHandler = async ({
externalService,
}: GetCommonFieldsHandlerArgs): Promise<GetCommonFieldsResponse> => {
const res = await externalService.getFields();
return res;
};
export const api: ExternalServiceApi = {
getFields: getFieldsHandler,
getIncident: getIncidentHandler,
handshake: handshakeHandler,
pushToService: pushToServiceHandler,
getIncident: getIncidentHandler,
};

View file

@ -25,6 +25,8 @@ import {
ServiceNowPublicConfigurationType,
ServiceNowSecretConfigurationType,
PushToServiceResponse,
ExecutorSubActionCommonFieldsParams,
ServiceNowExecutorResultData,
} from './types';
// TODO: to remove, need to support Case
@ -63,7 +65,7 @@ export function getActionType(
}
// action executor
const supportedSubActions: string[] = ['getFields', 'pushToService'];
async function executor(
{ logger }: { logger: Logger },
execOptions: ActionTypeExecutorOptions<
@ -71,10 +73,10 @@ async function executor(
ServiceNowSecretConfigurationType,
ExecutorParams
>
): Promise<ActionTypeExecutorResult<PushToServiceResponse | {}>> {
): Promise<ActionTypeExecutorResult<ServiceNowExecutorResultData | {}>> {
const { actionId, config, params, secrets } = execOptions;
const { subAction, subActionParams } = params;
let data: PushToServiceResponse | null = null;
let data: ServiceNowExecutorResultData | null = null;
const externalService = createExternalService(
{
@ -91,7 +93,7 @@ async function executor(
throw new Error(errorMessage);
}
if (subAction !== 'pushToService') {
if (!supportedSubActions.includes(subAction)) {
const errorMessage = `[Action][ExternalService] subAction ${subAction} not implemented.`;
logger.error(errorMessage);
throw new Error(errorMessage);
@ -117,5 +119,13 @@ async function executor(
logger.debug(`response push to service for incident id: ${data.id}`);
}
if (subAction === 'getFields') {
const getFieldsParams = subActionParams as ExecutorSubActionCommonFieldsParams;
data = await api.getFields({
externalService,
params: getFieldsParams,
});
}
return { status: 'ok', data: data ?? {}, actionId };
}

View file

@ -7,8 +7,36 @@
import { ExternalService, PushToServiceApiParams, ExecutorSubActionPushParams } from './types';
import { MapRecord } from '../case/types';
export const serviceNowCommonFields = [
{
column_label: 'Close notes',
max_length: '4000',
element: 'close_notes',
},
{
column_label: 'Description',
max_length: '4000',
element: 'description',
},
{
column_label: 'Short description',
max_length: '160',
element: 'short_description',
},
{
column_label: 'Created by',
max_length: '40',
element: 'sys_created_by',
},
{
column_label: 'Updated by',
max_length: '40',
element: 'sys_updated_by',
},
];
const createMock = (): jest.Mocked<ExternalService> => {
const service = {
getFields: jest.fn().mockImplementation(() => Promise.resolve(serviceNowCommonFields)),
getIncident: jest.fn().mockImplementation(() =>
Promise.resolve({
short_description: 'title from servicenow',

View file

@ -28,6 +28,7 @@ export const ExternalIncidentServiceSecretConfigurationSchema = schema.object(
);
export const ExecutorSubActionSchema = schema.oneOf([
schema.literal('getFields'),
schema.literal('getIncident'),
schema.literal('pushToService'),
schema.literal('handshake'),
@ -53,8 +54,13 @@ export const ExecutorSubActionGetIncidentParamsSchema = schema.object({
// Reserved for future implementation
export const ExecutorSubActionHandshakeParamsSchema = schema.object({});
export const ExecutorSubActionCommonFieldsParamsSchema = schema.object({});
export const ExecutorParamsSchema = schema.oneOf([
schema.object({
subAction: schema.literal('getFields'),
subActionParams: ExecutorSubActionCommonFieldsParamsSchema,
}),
schema.object({
subAction: schema.literal('getIncident'),
subActionParams: ExecutorSubActionGetIncidentParamsSchema,

View file

@ -11,6 +11,7 @@ import * as utils from '../lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '../../../../../../src/core/server';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { serviceNowCommonFields } from './mocks';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
@ -108,7 +109,7 @@ describe('ServiceNow service', () => {
requestMock.mockImplementation(() => {
throw new Error('An error has occurred');
});
expect(service.getIncident('1')).rejects.toThrow(
await expect(service.getIncident('1')).rejects.toThrow(
'Unable to get incident with id 1. Error: An error has occurred'
);
});
@ -155,7 +156,7 @@ describe('ServiceNow service', () => {
throw new Error('An error has occurred');
});
expect(
await expect(
service.createIncident({
incident: { short_description: 'title', description: 'desc' },
})
@ -207,7 +208,7 @@ describe('ServiceNow service', () => {
throw new Error('An error has occurred');
});
expect(
await expect(
service.updateIncident({
incidentId: '1',
incident: { short_description: 'title', description: 'desc' },
@ -234,4 +235,36 @@ describe('ServiceNow service', () => {
});
});
});
describe('getFields', () => {
test('it should call request with correct arguments', async () => {
requestMock.mockImplementation(() => ({
data: { result: serviceNowCommonFields },
}));
await service.getFields();
expect(requestMock).toHaveBeenCalledWith({
axios,
logger,
url:
'https://dev102283.service-now.com/api/now/v2/table/sys_dictionary?sysparm_query=name=task^internal_type=string&active=true&read_only=false&sysparm_fields=max_length,element,column_label',
});
});
test('it returns common fields correctly', async () => {
requestMock.mockImplementation(() => ({
data: { result: serviceNowCommonFields },
}));
const res = await service.getFields();
expect(res).toEqual(serviceNowCommonFields);
});
test('it should throw an error', async () => {
requestMock.mockImplementation(() => {
throw new Error('An error has occurred');
});
await expect(service.getFields()).rejects.toThrow(
'Unable to get common fields. Error: An error has occurred'
);
});
});
});

View file

@ -16,6 +16,7 @@ import { ProxySettings } from '../../types';
const API_VERSION = 'v2';
const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`;
const SYS_DICTIONARY = `api/now/${API_VERSION}/table/sys_dictionary`;
// Based on: https://docs.servicenow.com/bundle/orlando-platform-user-interface/page/use/navigation/reference/r_NavigatingByURLExamples.html
const VIEW_INCIDENT_URL = `nav_to.do?uri=incident.do?sys_id=`;
@ -33,6 +34,7 @@ export const createExternalService = (
}
const incidentUrl = `${url}/${INCIDENT_URL}`;
const fieldsUrl = `${url}/${SYS_DICTIONARY}?sysparm_query=name=task^internal_type=string&active=true&read_only=false&sysparm_fields=max_length,element,column_label`;
const axiosInstance = axios.create({
auth: { username, password },
});
@ -126,10 +128,28 @@ export const createExternalService = (
}
};
const getFields = async () => {
try {
const res = await request({
axios: axiosInstance,
url: fieldsUrl,
logger,
proxySettings,
});
return res.data.result.length > 0 ? res.data.result : [];
} catch (error) {
throw new Error(
getErrorMessage(i18n.NAME, `Unable to get common fields. Error: ${error.message}`)
);
}
};
return {
getIncident,
createIncident,
updateIncident,
findIncidents,
getFields,
getIncident,
updateIncident,
};
};

View file

@ -8,12 +8,13 @@
import { TypeOf } from '@kbn/config-schema';
import {
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
ExecutorParamsSchema,
ExecutorSubActionPushParamsSchema,
ExecutorSubActionCommonFieldsParamsSchema,
ExecutorSubActionGetIncidentParamsSchema,
ExecutorSubActionHandshakeParamsSchema,
ExecutorSubActionPushParamsSchema,
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
} from './schema';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ExternalServiceCommentResponse } from '../case/types';
@ -27,6 +28,12 @@ export type ServiceNowSecretConfigurationType = TypeOf<
typeof ExternalIncidentServiceSecretConfigurationSchema
>;
export type ExecutorSubActionCommonFieldsParams = TypeOf<
typeof ExecutorSubActionCommonFieldsParamsSchema
>;
export type ServiceNowExecutorResultData = PushToServiceResponse | GetCommonFieldsResponse;
export interface CreateCommentRequest {
[key: string]: string;
}
@ -59,6 +66,7 @@ export interface PushToServiceResponse extends ExternalServiceIncidentResponse {
export type ExternalServiceParams = Record<string, unknown>;
export interface ExternalService {
getFields: () => Promise<GetCommonFieldsResponse>;
getIncident: (id: string) => Promise<ExternalServiceParams | undefined>;
createIncident: (params: ExternalServiceParams) => Promise<ExternalServiceIncidentResponse>;
updateIncident: (params: ExternalServiceParams) => Promise<ExternalServiceIncidentResponse>;
@ -102,8 +110,24 @@ export interface GetIncidentApiHandlerArgs extends ExternalServiceApiHandlerArgs
export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs {
params: ExecutorSubActionHandshakeParams;
}
export interface ExternalServiceFields {
column_label: string;
name: string;
internal_type: {
link: string;
value: string;
};
max_length: string;
element: string;
}
export type GetCommonFieldsResponse = ExternalServiceFields[];
export interface GetCommonFieldsHandlerArgs {
externalService: ExternalService;
params: ExecutorSubActionCommonFieldsParams;
}
export interface ExternalServiceApi {
getFields: (args: GetCommonFieldsHandlerArgs) => Promise<GetCommonFieldsResponse>;
handshake: (args: HandshakeApiHandlerArgs) => Promise<void>;
pushToService: (args: PushToServiceApiHandlerArgs) => Promise<PushToServiceResponse>;
getIncident: (args: GetIncidentApiHandlerArgs) => Promise<void>;

View file

@ -333,7 +333,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [issueTypes]\n- [5.subAction]: expected value to equal [fieldsByIssueType]\n- [6.subAction]: expected value to equal [issues]\n- [7.subAction]: expected value to equal [issue]',
});
});
});
@ -351,7 +351,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [issueTypes]\n- [5.subAction]: expected value to equal [fieldsByIssueType]\n- [6.subAction]: expected value to equal [issues]\n- [7.subAction]: expected value to equal [issue]',
});
});
});
@ -374,7 +374,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [issueTypes]\n- [5.subAction]: expected value to equal [fieldsByIssueType]\n- [6.subAction]: expected value to equal [issues]\n- [7.subAction]: expected value to equal [issue]',
});
});
});
@ -402,7 +402,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [issueTypes]\n- [5.subAction]: expected value to equal [fieldsByIssueType]\n- [6.subAction]: expected value to equal [issues]\n- [7.subAction]: expected value to equal [issue]',
});
});
});
@ -430,7 +430,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [issueTypes]\n- [4.subAction]: expected value to equal [fieldsByIssueType]\n- [5.subAction]: expected value to equal [issues]\n- [6.subAction]: expected value to equal [issue]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [issueTypes]\n- [5.subAction]: expected value to equal [fieldsByIssueType]\n- [6.subAction]: expected value to equal [issues]\n- [7.subAction]: expected value to equal [issue]',
});
});
});

View file

@ -334,7 +334,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [incidentTypes]\n- [5.subAction]: expected value to equal [severity]',
});
});
});
@ -352,7 +352,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [incidentTypes]\n- [5.subAction]: expected value to equal [severity]',
});
});
});
@ -375,7 +375,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [incidentTypes]\n- [5.subAction]: expected value to equal [severity]',
});
});
});
@ -403,7 +403,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [incidentTypes]\n- [5.subAction]: expected value to equal [severity]',
});
});
});
@ -431,7 +431,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [incidentTypes]\n- [5.subAction]: expected value to equal [severity]',
});
});
});

View file

@ -328,7 +328,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]',
});
});
});
@ -346,7 +346,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]',
});
});
});
@ -369,7 +369,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.title]: expected value of type [string] but got [undefined]',
});
});
});
@ -397,7 +397,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments.0.commentId]: expected value of type [string] but got [undefined]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments.0.commentId]: expected value of type [string] but got [undefined]',
});
});
});
@ -425,7 +425,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
status: 'error',
retry: false,
message:
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments.0.comment]: expected value of type [string] but got [undefined]',
'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments.0.comment]: expected value of type [string] but got [undefined]',
});
});
});