Skip to content

Commit

Permalink
Merge branch 'master' into siem_np_alerting
Browse files Browse the repository at this point in the history
  • Loading branch information
rylnd committed Mar 2, 2020
2 parents 3254c49 + 74d0e92 commit 49ef2f2
Show file tree
Hide file tree
Showing 71 changed files with 1,359 additions and 5,055 deletions.
13 changes: 13 additions & 0 deletions src/core/server/elasticsearch/retry_call_cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ describe('migrationsRetryCallCluster', () => {
});
});

it('retries ES API calls that rejects with snapshot_in_progress_exception', () => {
expect.assertions(1);
const callEsApi = jest.fn();
let i = 0;
callEsApi.mockImplementation(() => {
return i++ <= 2
? Promise.reject({ body: { error: { type: 'snapshot_in_progress_exception' } } })
: Promise.resolve('success');
});
const retried = migrationsRetryCallCluster(callEsApi, mockLogger.get('mock log'), 1);
return expect(retried('endpoint')).resolves.toMatchInlineSnapshot(`"success"`);
});

it('rejects when ES API calls reject with other errors', async () => {
expect.assertions(3);
const callEsApi = jest.fn();
Expand Down
13 changes: 3 additions & 10 deletions src/core/server/elasticsearch/retry_call_cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export function migrationsRetryCallCluster(
error instanceof esErrors.AuthenticationException ||
error instanceof esErrors.AuthorizationException ||
// @ts-ignore
error instanceof esErrors.Gone
error instanceof esErrors.Gone ||
error?.body?.error?.type === 'snapshot_in_progress_exception'
);
},
timer(delay),
Expand All @@ -85,15 +86,7 @@ export function migrationsRetryCallCluster(
*
* @param apiCaller
*/

// TODO: Replace with APICaller from './scoped_cluster_client' once #46668 is merged
export function retryCallCluster(
apiCaller: (
endpoint: string,
clientParams: Record<string, any>,
options?: CallAPIOptions
) => Promise<any>
) {
export function retryCallCluster(apiCaller: APICaller) {
return (endpoint: string, clientParams: Record<string, any> = {}, options?: CallAPIOptions) => {
return defer(() => apiCaller(endpoint, clientParams, options))
.pipe(
Expand Down
24 changes: 24 additions & 0 deletions src/core/utils/merge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

// eslint-disable-next-line max-classes-per-file
import { merge } from './merge';

describe('merge', () => {
Expand Down Expand Up @@ -62,6 +63,29 @@ describe('merge', () => {
expect(merge({ a: 0 }, { a: 1 }, {})).toEqual({ a: 1 });
});

test('does not merge class instances', () => {
class Folder {
constructor(public readonly path: string) {}
getPath() {
return this.path;
}
}
class File {
constructor(public readonly content: string) {}
getContent() {
return this.content;
}
}
const folder = new Folder('/etc');
const file = new File('yolo');

const result = merge({}, { content: folder }, { content: file });
expect(result).toStrictEqual({
content: file,
});
expect(result.content.getContent()).toBe('yolo');
});

test(`doesn't pollute prototypes`, () => {
merge({}, JSON.parse('{ "__proto__": { "foo": "bar" } }'));
merge({}, JSON.parse('{ "constructor": { "prototype": { "foo": "bar" } } }'));
Expand Down
4 changes: 2 additions & 2 deletions src/core/utils/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/

import { isPlainObject } from 'lodash';
/**
* Deeply merges two objects, omitting undefined values, and not deeply merging Arrays.
*
Expand Down Expand Up @@ -60,7 +60,7 @@ export function merge<TReturn extends Record<string, any>>(
) as TReturn;
}

const isMergable = (obj: any) => typeof obj === 'object' && obj !== null && !Array.isArray(obj);
const isMergable = (obj: any) => isPlainObject(obj);

const mergeObjects = <T extends Record<string, any>, U extends Record<string, any>>(
baseObj: T,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,73 @@
* under the License.
*/

import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import {
buildOtherBucketAgg,
mergeOtherBucketAggResponse,
updateMissingBucket,
} from '../../buckets/_terms_other_bucket_helper';
import { start as visualizationsStart } from '../../../../../../../core_plugins/visualizations/public/np_ready/public/legacy';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
} from './_terms_other_bucket_helper';
import { AggConfigs, CreateAggConfigParams } from '../agg_configs';
import { BUCKET_TYPES } from './bucket_agg_types';
import { IBucketAggConfig } from './_bucket_agg_type';
import { mockDataServices, mockAggTypesRegistry } from '../test_helpers';

const visConfigSingleTerm = {
type: 'pie',
const indexPattern = {
id: '1234',
title: 'logstash-*',
fields: [
{
name: 'field',
},
],
} as any;

const singleTerm = {
aggs: [
{
type: 'terms',
schema: 'segment',
params: { field: 'machine.os.raw', otherBucket: true, missingBucket: true },
id: '1',
type: BUCKET_TYPES.TERMS,
params: {
field: {
name: 'machine.os.raw',
indexPattern,
filterable: true,
},
otherBucket: true,
missingBucket: true,
},
},
],
};

const visConfigNestedTerm = {
type: 'pie',
const nestedTerm = {
aggs: [
{
type: 'terms',
schema: 'segment',
params: { field: 'geo.src', size: 2, otherBucket: false, missingBucket: false },
id: '1',
type: BUCKET_TYPES.TERMS,
params: {
field: {
name: 'geo.src',
indexPattern,
filterable: true,
},
size: 2,
otherBucket: false,
missingBucket: false,
},
},
{
type: 'terms',
schema: 'segment',
params: { field: 'machine.os.raw', size: 2, otherBucket: true, missingBucket: true },
id: '2',
type: BUCKET_TYPES.TERMS,
params: {
field: {
name: 'machine.os.raw',
indexPattern,
filterable: true,
},
size: 2,
otherBucket: true,
missingBucket: true,
},
},
],
};
Expand Down Expand Up @@ -183,28 +217,36 @@ const nestedOtherResponse = {
status: 200,
};

describe('Terms Agg Other bucket helper', () => {
let vis;
jest.mock('ui/new_platform');

function init(aggConfig) {
ngMock.module('kibana');
ngMock.inject(Private => {
const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
describe('Terms Agg Other bucket helper', () => {
const typesRegistry = mockAggTypesRegistry();
const getAggConfigs = (aggs: CreateAggConfigParams[] = []) => {
return new AggConfigs(indexPattern, [...aggs], { typesRegistry });
};

vis = new visualizationsStart.Vis(indexPattern, aggConfig);
});
}
beforeEach(() => {
mockDataServices();
});

describe('buildOtherBucketAgg', () => {
it('returns a function', () => {
init(visConfigSingleTerm);
const agg = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[0], singleTermResponse);
expect(agg).to.be.a('function');
test('returns a function', () => {
const aggConfigs = getAggConfigs(singleTerm.aggs);
const agg = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[0] as IBucketAggConfig,
singleTermResponse
);
expect(typeof agg).toBe('function');
});

it('correctly builds query with single terms agg', () => {
init(visConfigSingleTerm);
const agg = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[0], singleTermResponse)();
test('correctly builds query with single terms agg', () => {
const aggConfigs = getAggConfigs(singleTerm.aggs);
const agg = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[0] as IBucketAggConfig,
singleTermResponse
);
const expectedResponse = {
aggs: undefined,
filters: {
Expand All @@ -223,13 +265,19 @@ describe('Terms Agg Other bucket helper', () => {
},
},
};

expect(agg['other-filter']).to.eql(expectedResponse);
expect(agg).toBeDefined();
if (agg) {
expect(agg()['other-filter']).toEqual(expectedResponse);
}
});

it('correctly builds query for nested terms agg', () => {
init(visConfigNestedTerm);
const agg = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[1], nestedTermResponse)();
test('correctly builds query for nested terms agg', () => {
const aggConfigs = getAggConfigs(nestedTerm.aggs);
const agg = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[1] as IBucketAggConfig,
nestedTermResponse
);
const expectedResponse = {
'other-filter': {
aggs: undefined,
Expand Down Expand Up @@ -267,54 +315,84 @@ describe('Terms Agg Other bucket helper', () => {
},
},
};

expect(agg).to.eql(expectedResponse);
expect(agg).toBeDefined();
if (agg) {
expect(agg()).toEqual(expectedResponse);
}
});

it('returns false when nested terms agg has no buckets', () => {
init(visConfigNestedTerm);
const agg = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[1], nestedTermResponseNoResults);
expect(agg).to.eql(false);
test('returns false when nested terms agg has no buckets', () => {
const aggConfigs = getAggConfigs(nestedTerm.aggs);
const agg = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[1] as IBucketAggConfig,
nestedTermResponseNoResults
);

expect(agg).toEqual(false);
});
});

describe('mergeOtherBucketAggResponse', () => {
it('correctly merges other bucket with single terms agg', () => {
init(visConfigSingleTerm);
const otherAggConfig = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[0], singleTermResponse)();
const mergedResponse = mergeOtherBucketAggResponse(
vis.aggs,
singleTermResponse,
singleOtherResponse,
vis.aggs.aggs[0],
otherAggConfig
test('correctly merges other bucket with single terms agg', () => {
const aggConfigs = getAggConfigs(singleTerm.aggs);
const otherAggConfig = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[0] as IBucketAggConfig,
singleTermResponse
);

expect(mergedResponse.aggregations['1'].buckets[3].key).to.equal('__other__');
expect(otherAggConfig).toBeDefined();
if (otherAggConfig) {
const mergedResponse = mergeOtherBucketAggResponse(
aggConfigs,
singleTermResponse,
singleOtherResponse,
aggConfigs.aggs[0] as IBucketAggConfig,
otherAggConfig()
);
expect(mergedResponse.aggregations['1'].buckets[3].key).toEqual('__other__');
}
});

it('correctly merges other bucket with nested terms agg', () => {
init(visConfigNestedTerm);
const otherAggConfig = buildOtherBucketAgg(vis.aggs, vis.aggs.aggs[1], nestedTermResponse)();
const mergedResponse = mergeOtherBucketAggResponse(
vis.aggs,
nestedTermResponse,
nestedOtherResponse,
vis.aggs.aggs[1],
otherAggConfig
test('correctly merges other bucket with nested terms agg', () => {
const aggConfigs = getAggConfigs(nestedTerm.aggs);
const otherAggConfig = buildOtherBucketAgg(
aggConfigs,
aggConfigs.aggs[1] as IBucketAggConfig,
nestedTermResponse
);

expect(mergedResponse.aggregations['1'].buckets[1]['2'].buckets[3].key).to.equal('__other__');
expect(otherAggConfig).toBeDefined();
if (otherAggConfig) {
const mergedResponse = mergeOtherBucketAggResponse(
aggConfigs,
nestedTermResponse,
nestedOtherResponse,
aggConfigs.aggs[1] as IBucketAggConfig,
otherAggConfig()
);

expect(mergedResponse.aggregations['1'].buckets[1]['2'].buckets[3].key).toEqual(
'__other__'
);
}
});
});

describe('updateMissingBucket', () => {
it('correctly updates missing bucket key', () => {
init(visConfigNestedTerm);
const updatedResponse = updateMissingBucket(singleTermResponse, vis.aggs, vis.aggs.aggs[0]);
test('correctly updates missing bucket key', () => {
const aggConfigs = getAggConfigs(nestedTerm.aggs);
const updatedResponse = updateMissingBucket(
singleTermResponse,
aggConfigs,
aggConfigs.aggs[0] as IBucketAggConfig
);
expect(
updatedResponse.aggregations['1'].buckets.find(bucket => bucket.key === '__missing__')
).to.not.be('undefined');
updatedResponse.aggregations['1'].buckets.find(
(bucket: Record<string, any>) => bucket.key === '__missing__'
)
).toBeDefined();
});
});
});
Loading

0 comments on commit 49ef2f2

Please sign in to comment.