Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Basic Elasticsearch Service in the new platform #20272

Closed
wants to merge 18 commits into from
Closed
2 changes: 1 addition & 1 deletion src/core/lib/kbn_internal_native_observable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { observable as SymbolObservable } from 'rxjs/internal/symbol/observable'
// see https://github.com/tc39/proposal-observable.
//
// One change has been applied to work with current libraries: using the
// Symbol.observable ponyfill instead of relying on the implementation in the
// Symbol_observable ponyfill instead of relying on the implementation in the
// spec.

// === Abstract Operations ===
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`creates elasticsearch client config when shouldAuth is false (for admin clients) 1`] = `
Object {
"apiVersion": undefined,
"host": Object {
"headers": undefined,
"host": null,
"path": null,
"port": null,
"protocol": null,
"query": null,
},
"keepAlive": true,
"logQueries": undefined,
"pingTimeout": undefined,
"requestTimeout": undefined,
"username": undefined,
}
`;

exports[`creates elasticsearch client config when shouldAuth is true by default (for data clients) 1`] = `
Object {
"apiVersion": undefined,
"host": Object {
"auth": "foo:bar",
"headers": undefined,
"host": null,
"path": null,
"port": null,
"protocol": null,
"query": null,
},
"keepAlive": true,
"logQueries": undefined,
"pingTimeout": undefined,
"requestTimeout": undefined,
"username": "foo",
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should not create multiple clients while service is running 1`] = `
Array [
Array [
Object {
"options": Object {
"shouldAuth": false,
},
"type": "admin",
},
],
Array [
Object {
"options": undefined,
"type": "data",
},
],
]
`;
62 changes: 62 additions & 0 deletions src/core/server/elasticsearch/__tests__/admin_client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

const mockCallAPI = jest.fn();

jest.mock('../call_api', () => ({
callAPI: mockCallAPI,
}));

import { Client } from 'elasticsearch';
import { AdminClient } from '../admin_client';

let client: AdminClient;
let esClient: Client;

beforeEach(() => {
esClient = new Client({});
client = new AdminClient(esClient);
});

describe('call passes correct arguments to callAPI', () => {
test('when only endpoint is specified', () => {
client.call('foo');
expect(mockCallAPI).toHaveBeenCalledWith(esClient, 'foo', {}, { wrap401Errors: true });
});

test('when endpoint and clientParams are specified', () => {
client.call('foo', { bar: 'baz' });
expect(mockCallAPI).toHaveBeenCalledWith(
esClient,
'foo',
{ bar: 'baz' },
{ wrap401Errors: true }
);
});

test('when endpoint, clientParams, and options are specified', () => {
client.call('foo', {}, { wrap401Errors: true });
expect(mockCallAPI).toHaveBeenCalledWith(esClient, 'foo', {}, { wrap401Errors: true });
});

test('when endpoint contains periods', () => {
client.call('foo.bar.baz');
expect(mockCallAPI).toHaveBeenCalledWith(esClient, 'foo.bar.baz', {}, { wrap401Errors: true });
});
});
139 changes: 139 additions & 0 deletions src/core/server/elasticsearch/__tests__/call_api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { callAPI } from '../call_api';

describe('things', () => {
test('should call the api when it exists, with the right context and params', async () => {
let apiContext;
const baz = jest.fn(function(this: any) {
apiContext = this;
});
const clientParams = {};
const client: any = { foo: { bar: { baz } } };
await callAPI(client, 'foo.bar.baz', clientParams);

expect(baz).toHaveBeenCalledWith(clientParams);
expect(apiContext).toBe(client.foo.bar);
});

test('should fail when endpoint does not exist on client', async () => {
expect.assertions(1);

const client: any = {};

try {
await callAPI(client, 'foo.bar.baz', {});
} catch (error) {
expect(error.message).toEqual('called with an invalid endpoint: foo.bar.baz');
}
});

test('should handle top-level endpoint', async () => {
let apiContext;
const fooFn = jest.fn(function(this: any) {
apiContext = this;
});
const client: any = { foo: fooFn };
await callAPI(client, 'foo', {});

expect(apiContext).toBe(client);
});

test('should handle failing api call', async () => {
expect.assertions(2);

const fooFn = () => {
throw new Error('api call failed');
};

const client: any = { foo: fooFn };

try {
await callAPI(client, 'foo', {});
} catch (error) {
expect(error.message).toEqual('api call failed');
expect(error.wrap401Errors).toBeUndefined();
}
});
});

// TODO: change this test after implementing
// homegrown error lib or boom
// https://github.com/elastic/kibana/issues/12464
describe('should wrap 401 errors', () => {
test('when wrap401Errors is undefined', async () => {
expect.assertions(2);

const fooFn = () => {
const err: any = new Error('api call failed');
err.statusCode = 401;
throw err;
};

const client: any = { foo: fooFn };

try {
await callAPI(client, 'foo', {});
} catch (error) {
expect(error.message).toEqual('api call failed');
expect(error.wrap401Errors).toBe(true);
}
});
});

describe('should not wrap 401 errors', () => {
test('when wrap401Errors is false', async () => {
expect.assertions(2);

const fooFn = () => {
const err: any = new Error('api call failed');
err.statusCode = 401;
throw err;
};

const client: any = { foo: fooFn };

try {
await callAPI(client, 'foo', {}, { wrap401Errors: false });
} catch (error) {
expect(error.message).toEqual('api call failed');
expect(error.wrap401Errors).toBeUndefined();
}
});

test('when statusCode is not 401', async () => {
expect.assertions(2);

const fooFn = () => {
const err: any = new Error('api call failed');
err.statusCode = 400;
throw err;
};

const client: any = { foo: fooFn };

try {
await callAPI(client, 'foo', {}, { wrap401Errors: true });
} catch (error) {
expect(error.message).toEqual('api call failed');
expect(error.wrap401Errors).toBeUndefined();
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/* tslint:disable:no-empty */
import { ElasticsearchConfig } from '../elasticsearch_config';
import { ClusterSchema } from '../schema';

test('filters headers', () => {
const clusterSchema = {} as ClusterSchema;
const headers = { foo: 'bar', baz: 'qux' };
const config = new ElasticsearchConfig('data', clusterSchema);
config.requestHeadersWhitelist = ['foo'];

const expectedHeaders = { foo: 'bar' };
expect(config.filterHeaders(headers)).toEqual(expectedHeaders);
});

describe('creates elasticsearch client config', () => {
test('when shouldAuth is true by default (for data clients)', () => {
const clusterSchema = {
password: 'bar',
pingTimeout: { asMilliseconds: () => {} },
requestTimeout: { asMilliseconds: () => {} },
url: '',
username: 'foo',
} as ClusterSchema;
const config = new ElasticsearchConfig('data', clusterSchema);

expect(config.toElasticsearchClientConfig()).toMatchSnapshot();
});

test('when shouldAuth is false (for admin clients)', () => {
const clusterSchema = {
pingTimeout: { asMilliseconds: () => {} },
requestTimeout: { asMilliseconds: () => {} },
url: '',
} as ClusterSchema;
const config = new ElasticsearchConfig('data', clusterSchema);

expect(config.toElasticsearchClientConfig({ shouldAuth: false })).toMatchSnapshot();
});
});
Loading