[ResponseOps][Connectors] Common request axios utility function across frameworks (#139549)

* Adding common axios util

* Fixing tests

* Removing patch

* Moving config to be first param
This commit is contained in:
doakalexi 2022-08-30 09:30:46 -04:00 committed by GitHub
parent f9e4c64f03
commit eee492ab7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 90 additions and 230 deletions

View file

@ -8,15 +8,15 @@
import axios, { AxiosError, AxiosResponse } from 'axios';
import { createExternalService } from './service';
import { request, createAxiosResponse } from '../lib/axios_utils';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { CasesWebhookMethods, CasesWebhookPublicConfigurationType, ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -31,7 +31,7 @@ import {
} from './types';
import * as i18n from './translations';
import { request } from '../lib/axios_utils';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
export const createExternalService = (

View file

@ -7,7 +7,7 @@
import { AxiosResponse, AxiosError } from 'axios';
import { isEmpty, isObjectLike, get } from 'lodash';
import { getErrorMessage } from '../lib/axios_utils';
import { getErrorMessage } from '../../lib/axios_utils';
import * as i18n from './translations';
export const createServiceError = (error: AxiosError, message: string) => {

View file

@ -8,7 +8,7 @@
import axios from 'axios';
import { createExternalService } from './service';
import { request, createAxiosResponse } from '../lib/axios_utils';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
@ -20,8 +20,8 @@ interface ResponseError extends Error {
}
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -27,7 +27,7 @@ import {
} from './types';
import * as i18n from './translations';
import { request, getErrorMessage, throwIfResponseIsNotValid } from '../lib/axios_utils';
import { request, getErrorMessage, throwIfResponseIsNotValid } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
const VERSION = '2';

View file

@ -8,7 +8,7 @@
import axios, { AxiosResponse } from 'axios';
import { Logger } from '@kbn/core/server';
import { Services } from '../../types';
import { request } from './axios_utils';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
interface PostPagerdutyOptions {

View file

@ -7,7 +7,7 @@
import axios, { AxiosResponse } from 'axios';
import { Logger } from '@kbn/core/server';
import { request } from './axios_utils';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
interface PostXmattersOptions {

View file

@ -9,7 +9,7 @@ import qs from 'query-string';
import axios from 'axios';
import stringify from 'json-stable-stringify';
import { Logger } from '@kbn/core/server';
import { request } from './axios_utils';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { AsApiContract } from '../../../common';

View file

@ -9,7 +9,7 @@
import stringify from 'json-stringify-safe';
import axios, { AxiosInstance, AxiosResponse } from 'axios';
import { Logger } from '@kbn/core/server';
import { request } from './axios_utils';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { SendEmailOptions } from './send_email';

View file

@ -8,7 +8,7 @@
import axios from 'axios';
import { createExternalService, getValueTextContent, formatUpdateRequest } from './service';
import { request, createAxiosResponse } from '../lib/axios_utils';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
@ -18,8 +18,8 @@ import { actionsConfigMock } from '../../actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -24,7 +24,7 @@ import {
} from './types';
import * as i18n from './translations';
import { getErrorMessage, request, throwIfResponseIsNotValid } from '../lib/axios_utils';
import { getErrorMessage, request, throwIfResponseIsNotValid } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
const VIEW_INCIDENT_URL = `#incidents`;

View file

@ -8,7 +8,7 @@
import axios, { AxiosResponse } from 'axios';
import { createExternalService } from './service';
import * as utils from '../lib/axios_utils';
import * as utils from '../../lib/axios_utils';
import { ExternalService, ServiceNowITSMIncident } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
@ -18,8 +18,8 @@ import { snExternalServiceConfig } from './config';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -20,7 +20,7 @@ import {
import * as i18n from './translations';
import { ServiceNowPublicConfigurationType, ServiceNowSecretConfigurationType } from './types';
import { request } from '../lib/axios_utils';
import { request } from '../../lib/axios_utils';
import { createServiceError, getPushedDate, prepareIncident } from './utils';
export const SYS_DICTIONARY_ENDPOINT = `api/now/table/sys_dictionary`;

View file

@ -8,7 +8,7 @@
import axios from 'axios';
import { createExternalServiceITOM } from './service_itom';
import * as utils from '../lib/axios_utils';
import * as utils from '../../lib/axios_utils';
import { ExternalServiceITOM } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
@ -19,8 +19,8 @@ import { itomEventParams, serviceNowChoices } from './mocks';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -7,7 +7,7 @@
import { ServiceFactory, ExternalServiceITOM, ExecutorSubActionAddEventParams } from './types';
import { request } from '../lib/axios_utils';
import { request } from '../../lib/axios_utils';
import { createExternalService } from './service';
import { createServiceError } from './utils';

View file

@ -8,7 +8,7 @@
import axios from 'axios';
import { createExternalServiceSIR } from './service_sir';
import * as utils from '../lib/axios_utils';
import * as utils from '../../lib/axios_utils';
import { ExternalServiceSIR } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
@ -19,8 +19,8 @@ import { snExternalServiceConfig } from './config';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -7,7 +7,7 @@
import { Observable, ExternalServiceSIR, ObservableResponse, ServiceFactory } from './types';
import { request } from '../lib/axios_utils';
import { request } from '../../lib/axios_utils';
import { createExternalService } from './service';
import { createServiceError } from './utils';

View file

@ -17,7 +17,7 @@ import {
ServiceNowSecretConfigurationType,
} from './types';
import { FIELD_PREFIX } from './config';
import { addTimeZoneToDate, getErrorMessage } from '../lib/axios_utils';
import { addTimeZoneToDate, getErrorMessage } from '../../lib/axios_utils';
import * as i18n from './translations';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ConnectorTokenClientContract } from '../../types';

View file

@ -10,7 +10,7 @@ import axios from 'axios';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { Logger } from '@kbn/core/server';
import { actionsConfigMock } from '../../actions_config.mock';
import { request, createAxiosResponse } from '../lib/axios_utils';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { createExternalService } from './service';
import { mappings } from './mocks';
import { ExternalService } from './types';
@ -18,8 +18,8 @@ import { ExternalService } from './types';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -9,7 +9,7 @@ import { Logger } from '@kbn/logging';
import axios from 'axios';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { getErrorMessage, request, throwIfResponseIsNotValid } from '../lib/axios_utils';
import { getErrorMessage, request, throwIfResponseIsNotValid } from '../../lib/axios_utils';
import { getBodyForEventAction } from './helpers';
import {
CreateCommentParams,

View file

@ -13,12 +13,12 @@ import { ActionParamsType, ActionTypeSecretsType, getActionType, TeamsActionType
import { actionsConfigMock } from '../actions_config.mock';
import { actionsMock } from '../mocks';
import { createActionTypeRegistry } from './index.test';
import * as utils from './lib/axios_utils';
import * as utils from '../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../actions_config';
jest.mock('axios');
jest.mock('./lib/axios_utils', () => {
const originalUtils = jest.requireActual('./lib/axios_utils');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -15,7 +15,7 @@ import { map, getOrElse } from 'fp-ts/lib/Option';
import { Logger } from '@kbn/core/server';
import { getRetryAfterIntervalFromHeaders } from './lib/http_rersponse_retry_header';
import { isOk, promiseResult, Result } from './lib/result_type';
import { request } from './lib/axios_utils';
import { request } from '../lib/axios_utils';
import {
ActionType,
ActionTypeExecutorOptions,

View file

@ -21,12 +21,12 @@ import {
WebhookMethods,
} from './webhook';
import * as utils from './lib/axios_utils';
import * as utils from '../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../actions_config';
jest.mock('axios');
jest.mock('./lib/axios_utils', () => {
const originalUtils = jest.requireActual('./lib/axios_utils');
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -22,7 +22,7 @@ import {
ValidatorServices,
} from '../types';
import { ActionsConfigurationUtilities } from '../actions_config';
import { request } from './lib/axios_utils';
import { request } from '../lib/axios_utils';
import { renderMustacheString } from '../lib/mustache_renderer';
import {
AlertingConnectorFeatureId,
@ -190,7 +190,7 @@ export async function executor(
url,
logger,
...basicAuth,
headers,
headers: headers ? headers : {},
data,
configurationUtilities,
})

View file

@ -17,7 +17,7 @@ import { duration as momentDuration } from 'moment';
import { schema } from '@kbn/config-schema';
import getPort from 'get-port';
import { request } from '../builtin_action_types/lib/axios_utils';
import { request } from '../lib/axios_utils';
import { ByteSizeValue } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';

View file

@ -17,7 +17,7 @@ import { duration as momentDuration } from 'moment';
import { schema } from '@kbn/config-schema';
import getPort from 'get-port';
import { request } from '../builtin_action_types/lib/axios_utils';
import { request } from '../lib/axios_utils';
import { ByteSizeValue } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';

View file

@ -19,8 +19,8 @@ import {
createAxiosResponse,
} from './axios_utils';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { getCustomAgents } from './get_custom_agents';
import { actionsConfigMock } from '../actions_config.mock';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
const TestUrl = 'https://elastic.co/foo/bar/baz';

View file

@ -6,10 +6,10 @@
*/
import { isObjectLike, isEmpty } from 'lodash';
import { AxiosInstance, Method, AxiosResponse, AxiosBasicCredentials } from 'axios';
import { AxiosInstance, Method, AxiosResponse, AxiosRequestConfig } from 'axios';
import { Logger } from '@kbn/core/server';
import { getCustomAgents } from './get_custom_agents';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
import { ActionsConfigurationUtilities } from '../actions_config';
export const request = async <T = unknown>({
axios,
@ -19,24 +19,21 @@ export const request = async <T = unknown>({
data,
configurationUtilities,
headers,
...rest
...config
}: {
axios: AxiosInstance;
url: string;
logger: Logger;
method?: Method;
data?: T;
params?: unknown;
configurationUtilities: ActionsConfigurationUtilities;
headers?: Record<string, string> | null;
validateStatus?: (status: number) => boolean;
auth?: AxiosBasicCredentials;
}): Promise<AxiosResponse> => {
} & AxiosRequestConfig): Promise<AxiosResponse> => {
const { httpAgent, httpsAgent } = getCustomAgents(configurationUtilities, logger, url);
const { maxContentLength, timeout } = configurationUtilities.getResponseSettings();
return await axios(url, {
...rest,
...config,
method,
// Axios doesn't support `null` value for `headers` property.
headers: headers ?? undefined,

View file

@ -5,21 +5,27 @@
* 2.0.
*/
import { Agent as HttpsAgent } from 'https';
import HttpProxyAgent from 'http-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent';
import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { MockedLogger } from '@kbn/logging-mocks';
import { actionsConfigMock } from '../actions_config.mock';
import { actionsMock } from '../mocks';
import { TestSubActionConnector } from './mocks';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
import { ActionsConfigurationUtilities } from '../actions_config';
import * as utils from '../lib/axios_utils';
jest.mock('axios');
const axiosMock = axios as jest.Mocked<typeof axios>;
jest.mock('../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),
};
});
const axiosMock = axios as jest.Mocked<typeof axios>;
const requestMock = utils.request as jest.Mock;
const createAxiosError = (): AxiosError => {
const error = new Error() as AxiosError;
error.isAxiosError = true;
@ -42,7 +48,7 @@ describe('SubActionConnector', () => {
jest.resetAllMocks();
jest.clearAllMocks();
axiosInstanceMock.mockReturnValue({ data: { status: 'ok' } });
requestMock.mockReturnValue({ data: { status: 'ok' } });
axiosMock.create.mockImplementation(() => {
return axiosInstanceMock as unknown as AxiosInstance;
});
@ -80,12 +86,12 @@ describe('SubActionConnector', () => {
describe('URL validation', () => {
it('removes double slashes correctly', async () => {
await service.testUrl({ url: 'https://example.com//api///test-endpoint' });
expect(axiosInstanceMock.mock.calls[0][0]).toBe('https://example.com/api/test-endpoint');
expect(requestMock.mock.calls[0][0].url).toBe('https://example.com/api/test-endpoint');
});
it('removes the ending slash correctly', async () => {
await service.testUrl({ url: 'https://example.com/' });
expect(axiosInstanceMock.mock.calls[0][0]).toBe('https://example.com');
expect(requestMock.mock.calls[0][0].url).toBe('https://example.com');
});
it('throws an error if the url is invalid', async () => {
@ -126,24 +132,24 @@ describe('SubActionConnector', () => {
it('sets data to an empty object if the data are null', async () => {
await service.testUrl({ url: 'https://example.com', data: null });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { data } = axiosInstanceMock.mock.calls[0][1];
expect(requestMock).toHaveBeenCalledTimes(1);
const { data } = requestMock.mock.calls[0][0];
expect(data).toEqual({});
});
it('pass data to axios correctly if not null', async () => {
await service.testUrl({ url: 'https://example.com', data: { foo: 'foo' } });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { data } = axiosInstanceMock.mock.calls[0][1];
expect(requestMock).toHaveBeenCalledTimes(1);
const { data } = requestMock.mock.calls[0][0];
expect(data).toEqual({ foo: 'foo' });
});
it('removeNullOrUndefinedFields: removes null values and undefined values correctly', async () => {
await service.testData({ data: { foo: 'foo', bar: null, baz: undefined } });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { data } = axiosInstanceMock.mock.calls[0][1];
expect(requestMock).toHaveBeenCalledTimes(1);
const { data } = requestMock.mock.calls[0][0];
expect(data).toEqual({ foo: 'foo' });
});
@ -153,7 +159,7 @@ describe('SubActionConnector', () => {
// @ts-expect-error
await service.testData({ data: dataToTest });
const { data } = axiosInstanceMock.mock.calls[0][1];
const { data } = requestMock.mock.calls[0][0];
expect(data).toEqual({});
}
);
@ -163,19 +169,18 @@ describe('SubActionConnector', () => {
it('fetch correctly', async () => {
const res = await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
expect(axiosInstanceMock).toBeCalledWith('https://example.com', {
expect(requestMock).toHaveBeenCalledTimes(1);
expect(requestMock).toBeCalledWith({
axios: axiosInstanceMock,
configurationUtilities: mockedActionsConfig,
logger,
method: 'get',
data: {},
headers: {
'Content-Type': 'application/json',
'X-Test-Header': 'test',
},
httpAgent: undefined,
httpsAgent: expect.any(HttpsAgent),
proxy: false,
maxContentLength: 1000000,
timeout: 360000,
url: 'https://example.com',
});
expect(logger.debug).toBeCalledWith(
@ -186,14 +191,14 @@ describe('SubActionConnector', () => {
});
it('validates the response correctly', async () => {
axiosInstanceMock.mockReturnValue({ data: { invalidField: 'test' } });
requestMock.mockReturnValue({ data: { invalidField: 'test' } });
await expect(async () => service.testUrl({ url: 'https://example.com' })).rejects.toThrow(
'Response validation failed (Error: [status]: expected value of type [string] but got [undefined])'
);
});
it('formats the response error correctly', async () => {
axiosInstanceMock.mockImplementation(() => {
requestMock.mockImplementation(() => {
throw createAxiosError();
});
@ -206,138 +211,4 @@ describe('SubActionConnector', () => {
);
});
});
describe('Proxy', () => {
it('have been called with proper proxy agent for a valid url', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxySSLSettings: {
verificationMode: 'full',
},
proxyUrl: 'https://localhost:1212',
proxyBypassHosts: undefined,
proxyOnlyHosts: undefined,
});
const { httpAgent, httpsAgent } = getCustomAgents(
mockedActionsConfig,
logger,
'https://example.com'
);
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toBeCalledWith('https://example.com', {
method: 'get',
data: {},
headers: {
'X-Test-Header': 'test',
'Content-Type': 'application/json',
},
httpAgent,
httpsAgent,
proxy: false,
maxContentLength: 1000000,
timeout: 360000,
});
});
it('have been called with proper proxy agent for an invalid url', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxyUrl: ':nope:',
proxySSLSettings: {
verificationMode: 'none',
},
proxyBypassHosts: undefined,
proxyOnlyHosts: undefined,
});
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toBeCalledWith('https://example.com', {
method: 'get',
data: {},
headers: {
'X-Test-Header': 'test',
'Content-Type': 'application/json',
},
httpAgent: undefined,
httpsAgent: expect.any(HttpsAgent),
proxy: false,
maxContentLength: 1000000,
timeout: 360000,
});
});
it('bypasses with proxyBypassHosts when expected', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxySSLSettings: {
verificationMode: 'full',
},
proxyUrl: 'https://elastic.proxy.co',
proxyBypassHosts: new Set(['example.com']),
proxyOnlyHosts: undefined,
});
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { httpAgent, httpsAgent } = axiosInstanceMock.mock.calls[0][1];
expect(httpAgent instanceof HttpProxyAgent).toBe(false);
expect(httpsAgent instanceof HttpsProxyAgent).toBe(false);
});
it('does not bypass with proxyBypassHosts when expected', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxySSLSettings: {
verificationMode: 'full',
},
proxyUrl: 'https://elastic.proxy.co',
proxyBypassHosts: new Set(['not-example.com']),
proxyOnlyHosts: undefined,
});
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { httpAgent, httpsAgent } = axiosInstanceMock.mock.calls[0][1];
expect(httpAgent instanceof HttpProxyAgent).toBe(true);
expect(httpsAgent instanceof HttpsProxyAgent).toBe(true);
});
it('proxies with proxyOnlyHosts when expected', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxySSLSettings: {
verificationMode: 'full',
},
proxyUrl: 'https://elastic.proxy.co',
proxyBypassHosts: undefined,
proxyOnlyHosts: new Set(['example.com']),
});
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { httpAgent, httpsAgent } = axiosInstanceMock.mock.calls[0][1];
expect(httpAgent instanceof HttpProxyAgent).toBe(true);
expect(httpsAgent instanceof HttpsProxyAgent).toBe(true);
});
it('does not proxy with proxyOnlyHosts when expected', async () => {
mockedActionsConfig.getProxySettings.mockReturnValue({
proxySSLSettings: {
verificationMode: 'full',
},
proxyUrl: 'https://elastic.proxy.co',
proxyBypassHosts: undefined,
proxyOnlyHosts: new Set(['not-example.com']),
});
await service.testUrl({ url: 'https://example.com' });
expect(axiosInstanceMock).toHaveBeenCalledTimes(1);
const { httpAgent, httpsAgent } = axiosInstanceMock.mock.calls[0][1];
expect(httpAgent instanceof HttpProxyAgent).toBe(false);
expect(httpsAgent instanceof HttpsProxyAgent).toBe(false);
});
});
});

View file

@ -17,10 +17,10 @@ import axios, {
AxiosRequestHeaders,
} from 'axios';
import { ActionsConfigurationUtilities } from '../actions_config';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
import { SubAction } from './types';
import { ServiceParams } from './types';
import * as i18n from './translations';
import { request } from '../lib/axios_utils';
const isObject = (value: unknown): value is Record<string, unknown> => {
return isPlainObject(value);
@ -133,27 +133,19 @@ export abstract class SubActionConnector<Config, Secrets> {
this.ensureUriAllowed(url);
const normalizedURL = this.normalizeURL(url);
const { httpAgent, httpsAgent } = getCustomAgents(
this.configurationUtilities,
this.logger,
url
);
const { maxContentLength, timeout } = this.configurationUtilities.getResponseSettings();
this.logger.debug(
`Request to external service. Connector Id: ${this.connector.id}. Connector type: ${this.connector.type} Method: ${method}. URL: ${normalizedURL}`
);
const res = await this.axiosInstance(normalizedURL, {
const res = await request({
...config,
axios: this.axiosInstance,
url: normalizedURL,
logger: this.logger,
method,
headers: this.getHeaders(headers),
data: this.normalizeData(data),
// use httpAgent and httpsAgent and set axios proxy: false, to be able to handle fail on invalid certs
httpAgent,
httpsAgent,
proxy: false,
maxContentLength,
timeout,
configurationUtilities: this.configurationUtilities,
headers: this.getHeaders(headers),
});
this.validateResponse(responseSchema, res.data);