diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchclientconfig.md b/docs/development/core/server/kibana-plugin-server.elasticsearchclientconfig.md
index cd42a84f621a9..39e6ac428292b 100644
--- a/docs/development/core/server/kibana-plugin-server.elasticsearchclientconfig.md
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchclientconfig.md
@@ -4,6 +4,7 @@
## ElasticsearchClientConfig type
+
Signature:
```typescript
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md
index 8b1ce883c4a14..c29d3fbbf69ab 100644
--- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md
@@ -4,8 +4,20 @@
## ElasticsearchServiceSetup.createClient property
+Create application specific Elasticsearch cluster API client with customized config.
+
Signature:
```typescript
-readonly createClient: (type: string, config: ElasticsearchClientConfig) => ClusterClient;
+readonly createClient: (type: string, clientConfig?: Partial) => ClusterClient;
```
+
+## Example
+
+
+```js
+const client = elasticsearch.createCluster('my-app-name', config);
+const data = await client.callAsInternalUser();
+
+```
+
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md
index 3197869d6d663..a295022971a14 100644
--- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md
@@ -16,7 +16,7 @@ export interface ElasticsearchServiceSetup
| Property | Type | Description |
| --- | --- | --- |
| [adminClient$](./kibana-plugin-server.elasticsearchservicesetup.adminclient$.md) | Observable<ClusterClient>
| |
-| [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md) | (type: string, config: ElasticsearchClientConfig) => ClusterClient
| |
+| [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md) | (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ClusterClient
| Create application specific Elasticsearch cluster API client with customized config. |
| [dataClient$](./kibana-plugin-server.elasticsearchservicesetup.dataclient$.md) | Observable<ClusterClient>
| |
| [legacy](./kibana-plugin-server.elasticsearchservicesetup.legacy.md) | {
readonly config$: Observable<ElasticsearchConfig>;
}
| |
diff --git a/src/core/server/elasticsearch/elasticsearch_client_config.ts b/src/core/server/elasticsearch/elasticsearch_client_config.ts
index 9fe56bf8c7e58..dcc09f711abbe 100644
--- a/src/core/server/elasticsearch/elasticsearch_client_config.ts
+++ b/src/core/server/elasticsearch/elasticsearch_client_config.ts
@@ -28,7 +28,7 @@ import { Logger } from '../logging';
import { ElasticsearchConfig } from './elasticsearch_config';
/**
- * @internalremarks Config that consumers can pass to the Elasticsearch JS client is complex and includes
+ * @privateRemarks Config that consumers can pass to the Elasticsearch JS client is complex and includes
* not only entries from standard `elasticsearch.*` yaml config, but also some Elasticsearch JS
* client specific options like `keepAlive` or `plugins` (that eventually will be deprecated).
*
diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts
index a0f7180129382..e84ddb2baa30c 100644
--- a/src/core/server/elasticsearch/elasticsearch_service.test.ts
+++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts
@@ -39,8 +39,12 @@ const deps = {
configService.atPath.mockReturnValue(
new BehaviorSubject({
hosts: ['http://1.2.3.4'],
- healthCheck: {},
- ssl: {},
+ healthCheck: {
+ delay: 2000,
+ },
+ ssl: {
+ verificationMode: 'none',
+ },
} as any)
);
@@ -57,7 +61,7 @@ beforeEach(() => {
afterEach(() => jest.clearAllMocks());
describe('#setup', () => {
- test('returns legacy Elasticsearch config as a part of the contract', async () => {
+ it('returns legacy Elasticsearch config as a part of the contract', async () => {
const setupContract = await elasticsearchService.setup(deps);
await expect(setupContract.legacy.config$.pipe(first()).toPromise()).resolves.toBeInstanceOf(
@@ -65,7 +69,7 @@ describe('#setup', () => {
);
});
- test('returns data and admin client observables as a part of the contract', async () => {
+ it('returns data and admin client observables as a part of the contract', async () => {
const mockAdminClusterClientInstance = { close: jest.fn() };
const mockDataClusterClientInstance = { close: jest.fn() };
MockClusterClient.mockImplementationOnce(
@@ -103,27 +107,83 @@ describe('#setup', () => {
expect(mockDataClusterClientInstance.close).not.toHaveBeenCalled();
});
- test('returns `createClient` as a part of the contract', async () => {
- const setupContract = await elasticsearchService.setup(deps);
-
- const mockClusterClientInstance = { close: jest.fn() };
- MockClusterClient.mockImplementation(() => mockClusterClientInstance);
-
- const mockConfig = { logQueries: true };
- const clusterClient = setupContract.createClient('some-custom-type', mockConfig as any);
-
- expect(clusterClient).toBe(mockClusterClientInstance);
-
- expect(MockClusterClient).toHaveBeenCalledWith(
- mockConfig,
- expect.objectContaining({ context: ['elasticsearch', 'some-custom-type'] }),
- expect.any(Function)
- );
+ describe('#createClient', () => {
+ it('allows to specify config properties', async () => {
+ const setupContract = await elasticsearchService.setup(deps);
+
+ const mockClusterClientInstance = { close: jest.fn() };
+ MockClusterClient.mockImplementation(() => mockClusterClientInstance);
+
+ const customConfig = { logQueries: true };
+ const clusterClient = setupContract.createClient('some-custom-type', customConfig);
+
+ expect(clusterClient).toBe(mockClusterClientInstance);
+
+ expect(MockClusterClient).toHaveBeenCalledWith(
+ expect.objectContaining(customConfig),
+ expect.objectContaining({ context: ['elasticsearch', 'some-custom-type'] }),
+ expect.any(Function)
+ );
+ });
+
+ it('falls back to elasticsearch default config values if property not specified', async () => {
+ const setupContract = await elasticsearchService.setup(deps);
+ // reset all mocks called during setup phase
+ MockClusterClient.mockClear();
+
+ const customConfig = {
+ hosts: ['http://8.8.8.8'],
+ logQueries: true,
+ ssl: { certificate: 'certificate-value' },
+ };
+ setupContract.createClient('some-custom-type', customConfig);
+
+ const config = MockClusterClient.mock.calls[0][0];
+ expect(config).toMatchInlineSnapshot(`
+Object {
+ "healthCheckDelay": 2000,
+ "hosts": Array [
+ "http://8.8.8.8",
+ ],
+ "logQueries": true,
+ "requestHeadersWhitelist": Array [
+ undefined,
+ ],
+ "ssl": Object {
+ "certificate": "certificate-value",
+ "verificationMode": "none",
+ },
+}
+`);
+ });
+ it('falls back to elasticsearch config if custom config not passed', async () => {
+ const setupContract = await elasticsearchService.setup(deps);
+ // reset all mocks called during setup phase
+ MockClusterClient.mockClear();
+
+ setupContract.createClient('another-type');
+
+ const config = MockClusterClient.mock.calls[0][0];
+ expect(config).toMatchInlineSnapshot(`
+Object {
+ "healthCheckDelay": 2000,
+ "hosts": Array [
+ "http://1.2.3.4",
+ ],
+ "requestHeadersWhitelist": Array [
+ undefined,
+ ],
+ "ssl": Object {
+ "verificationMode": "none",
+ },
+}
+`);
+ });
});
});
describe('#stop', () => {
- test('stops both admin and data clients', async () => {
+ it('stops both admin and data clients', async () => {
const mockAdminClusterClientInstance = { close: jest.fn() };
const mockDataClusterClientInstance = { close: jest.fn() };
MockClusterClient.mockImplementationOnce(
diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts
index 4e90ff2e2baec..38a0d19b1ae3f 100644
--- a/src/core/server/elasticsearch/elasticsearch_service.ts
+++ b/src/core/server/elasticsearch/elasticsearch_service.ts
@@ -18,7 +18,8 @@
*/
import { ConnectableObservable, Observable, Subscription } from 'rxjs';
-import { filter, map, publishReplay, switchMap } from 'rxjs/operators';
+import { filter, first, map, publishReplay, switchMap } from 'rxjs/operators';
+import { merge } from 'lodash';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';
@@ -44,8 +45,27 @@ export interface ElasticsearchServiceSetup {
readonly legacy: {
readonly config$: Observable;
};
-
- readonly createClient: (type: string, config: ElasticsearchClientConfig) => ClusterClient;
+ /**
+ * Create application specific Elasticsearch cluster API client with customized config.
+ *
+ * @param type Unique identifier of the client
+ * @param clientConfig A config consists of Elasticsearch JS client options and
+ * valid sub-set of Elasticsearch service config.
+ * We fill all the missing properties in the `clientConfig` using the default
+ * Elasticsearch config so that we don't depend on default values set and
+ * controlled by underlying Elasticsearch JS client.
+ * We don't run validation against passed config expect it to be valid.
+ *
+ * @example
+ * ```js
+ * const client = elasticsearch.createCluster('my-app-name', config);
+ * const data = await client.callAsInternalUser();
+ * ```
+ */
+ readonly createClient: (
+ type: string,
+ clientConfig?: Partial
+ ) => ClusterClient;
readonly adminClient$: Observable;
readonly dataClient$: Observable;
}
@@ -101,14 +121,17 @@ export class ElasticsearchService implements CoreService clients.config)) },
adminClient$: clients$.pipe(map(clients => clients.adminClient)),
dataClient$: clients$.pipe(map(clients => clients.dataClient)),
- createClient: (type: string, clientConfig: ElasticsearchClientConfig) => {
- return this.createClusterClient(type, clientConfig, deps.http.auth.getAuthHeaders);
+ createClient: (type: string, clientConfig: Partial = {}) => {
+ const finalConfig = merge({}, config, clientConfig);
+ return this.createClusterClient(type, finalConfig, deps.http.auth.getAuthHeaders);
},
};
}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index b1fdc1289151c..79d784224eccd 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -142,8 +142,7 @@ export class ElasticsearchErrorHelpers {
export interface ElasticsearchServiceSetup {
// (undocumented)
readonly adminClient$: Observable;
- // (undocumented)
- readonly createClient: (type: string, config: ElasticsearchClientConfig) => ClusterClient;
+ readonly createClient: (type: string, clientConfig?: Partial) => ClusterClient;
// (undocumented)
readonly dataClient$: Observable;
// (undocumented)
diff --git a/src/legacy/core_plugins/elasticsearch/index.js b/src/legacy/core_plugins/elasticsearch/index.js
index 8dc9c77e79d99..51dac9e3a4682 100644
--- a/src/legacy/core_plugins/elasticsearch/index.js
+++ b/src/legacy/core_plugins/elasticsearch/index.js
@@ -77,13 +77,7 @@ export default function (kibana) {
throw new Error(`cluster '${name}' already exists`);
}
- // We fill all the missing properties in the `clientConfig` using the default
- // Elasticsearch config so that we don't depend on default values set and
- // controlled by underlying Elasticsearch JS client.
- const cluster = new Cluster(server.newPlatform.setup.core.elasticsearch.createClient(name, {
- ...esConfig,
- ...clientConfig,
- }));
+ const cluster = new Cluster(server.newPlatform.setup.core.elasticsearch.createClient(name, clientConfig));
clusters.set(name, cluster);