From 86bcf5ea775cb76cf00c4d3643fb60ab06ed54b7 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 14 Jul 2020 11:00:03 +0200 Subject: [PATCH 1/3] add migration guide for the new client --- src/core/MIGRATION_EXAMPLES.md | 180 ++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 6bb5a845ea2ab..e660bce8bfbb7 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -27,6 +27,10 @@ APIs to their New Platform equivalents. - [Changes in structure compared to legacy](#changes-in-structure-compared-to-legacy) - [Remarks](#remarks) - [UiSettings](#uisettings) + - [Elasticsearch client](#elasticsearch-client) + - [Client API Changes](#client-api-changes) + - [Accessing the client from a route handler](#accessing-the-client-from-a-route-handler) + - [Creating a custom client](#creating-a-custom-client) ## Configuration @@ -1003,4 +1007,178 @@ setup(core: CoreSetup){ }, }) } -``` \ No newline at end of file +``` + +## Elasticsearch client + +The new elasticsearch client is a thin wrapper around `@elastic/elasticsearch`'s `Client` class. Even if the API +is quite close to the legacy client Kibana was previously using, there are some subtle changes to take into account +during migration. + +[Official documentation](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html) + +### Client API Changes + +The most significant changes for the consumers are the following: + +- internal / current user client accessors has been renamed and are now properties instead of functions + - `callAsInternalUser('ping')` -> `asInternalUser.ping()` + - `callAsCurrentUser('ping')` -> `asCurrentUser.ping()` + +- the API now reflects the `Client`'s instead of leveraging the string-based endpoint names the `LegacyAPICaller` was using + +```ts +// before +const body = await client.callAsInternalUser('indices.get', { index: 'id' }); +// after +const { body } = await client.asInternalUser.indices.get({ index: 'id' }); +``` + +- calling any ES endpoint now returns the whole response object instead of only the body payload + +```ts +// before +const body = await legacyClient.callAsInternalUser('get', { id: 'id' }); +// after +const { body } = await client.asInternalUser.get({ id: 'id' }); +``` + +Note that more information from the ES response are available: + +```ts +const { + body, // response payload + statusCode, // http status code of the response + headers, // response headers + warnings, // warnings returned from ES + meta // meta information about the request, such as request parameters, number of attempts and so on +} = await client.asInternalUser.get({ id: 'id' }); +``` + +- all API methods are now generic to allow specifying the response body type + +```ts +// before +const body: GetResponse = await legacyClient.callAsInternalUser('get', { id: 'id' }); +``` + +```ts +// body is of type `GetResponse` +const { body } = await client.asInternalUser.get({ id: 'id' }); +// fallback to `Record` if unspecified +const { body } = await client.asInternalUser.get({ id: 'id' }); +``` + +- the returned error types changed + +There are no longer specific errors for every HTTP status code (such as `BadRequest` or `NotFound`). A generic +`ResponseError` with the specific `statusCode` is thrown instead. + +```ts +// before +import { errors } from 'elasticsearch'; +try { + await legacyClient.callAsInternalUser('ping'); +} catch(e) { + if(e instanceof errors.NotFound) { + // do something + } +} +``` + +```ts +// after +import { errors } from '@elastic/elasticsearch'; +try { + await client.asInternalUser.ping(); +} catch(e) { + if(e instanceof errors.ResponseError && e.statusCode === 404) { + // do something + } + // also possible, as all errors got a name property with the name of the class, + // so this slightly better in term of performances + if(e.name === 'ResponseError' && e.statusCode === 404) { + // do something + } +} +``` + +Please refer to the [Breaking changes list](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/breaking-changes.html) +for more information about the changes between the legacy and new client. + +### Accessing the client from a route handler + +Apart from the API format change, accessing the client from within a route handler +did not change. As it was done for the legacy client, a preconfigured scoped client +bound to the request is accessible using `core` context provider: + +```ts +// before +router.get( + { + path: '/my-route', + }, + async (context, req, res) => { + const { client } = context.core.elasticsearch.legacy; + // call as current user + const res = await client.callAsCurrentUser('ping'); + // call as internal user + const res2 = await client.callAsInternalUser('search', options); + return res.ok({ body: 'ok' }); + } +); +``` + +```ts +// after +router.get( + { + path: '/my-route', + }, + async (context, req, res) => { + const { client } = context.core.elasticsearch; + // call as current user + const res = await client.asCurrentUser.ping(); + // call as internal user + const res2 = await client.asInternalUser.search(options); + return res.ok({ body: 'ok' }); + } +); +``` + +### Creating a custom client + +The API to create custom clients did not change much: + +```ts +// before +const customClient = coreStart.elasticsearch.legacy.createClient('my-custom-client', customConfig); +// do something with the client, such as +await customClient.callAsInternalUser('ping'); +// custom client are closable +customClient.close(); +``` + +```ts +// after +const customClient = coreStart.elasticsearch.createClient('my-custom-client', customConfig); +// do something with the client, such as +await customClient.asInternalUser.ping(); +// custom client are closable +customClient.close(); +``` + +Note that the `plugins` option is now longer available on the new client. As the API is now exhaustive, adding custom +endpoints using plugins should no longer be necessary. If, for any reasons, one still needs to reach an +endpoint not listed on the client API, using `request.transport` is still possible: + +```ts +const { body } = await client.asCurrentUser.transport.request({ + method: 'get', + path: '/my-custom-endpoint', + body: { my: 'payload'}, + querystring: { param: 'foo' } +}) +``` + +Remark: the new client creation API is now only available from the `start` contract of the elasticsearch service. From a3f6fea3d53c108437f8addcaa071a8823ea8a0e Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 21 Jul 2020 13:33:29 +0200 Subject: [PATCH 2/3] add missing breaking changes --- src/core/MIGRATION_EXAMPLES.md | 97 ++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index e660bce8bfbb7..53c9fd3cf49c3 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -1027,23 +1027,33 @@ The most significant changes for the consumers are the following: - the API now reflects the `Client`'s instead of leveraging the string-based endpoint names the `LegacyAPICaller` was using +before: + ```ts -// before const body = await client.callAsInternalUser('indices.get', { index: 'id' }); -// after +``` + +after: + +```ts const { body } = await client.asInternalUser.indices.get({ index: 'id' }); ``` - calling any ES endpoint now returns the whole response object instead of only the body payload +before: + ```ts -// before const body = await legacyClient.callAsInternalUser('get', { id: 'id' }); -// after +``` + +after: + +```ts const { body } = await client.asInternalUser.get({ id: 'id' }); ``` -Note that more information from the ES response are available: +Note that more information from the ES response is available: ```ts const { @@ -1057,11 +1067,14 @@ const { - all API methods are now generic to allow specifying the response body type +before: + ```ts -// before const body: GetResponse = await legacyClient.callAsInternalUser('get', { id: 'id' }); ``` +after: + ```ts // body is of type `GetResponse` const { body } = await client.asInternalUser.get({ id: 'id' }); @@ -1074,8 +1087,9 @@ const { body } = await client.asInternalUser.get({ id: 'id' }); There are no longer specific errors for every HTTP status code (such as `BadRequest` or `NotFound`). A generic `ResponseError` with the specific `statusCode` is thrown instead. +before: + ```ts -// before import { errors } from 'elasticsearch'; try { await legacyClient.callAsInternalUser('ping'); @@ -1086,8 +1100,9 @@ try { } ``` +after: + ```ts -// after import { errors } from '@elastic/elasticsearch'; try { await client.asInternalUser.ping(); @@ -1103,6 +1118,52 @@ try { } ``` +- the parameter property names changed from camelCase to snake_case + +Even if technically, the javascript client accepts both formats, the typescript definitions are only defining the snake_case +properties. + +before: + +```ts +legacyClient.callAsCurrentUser('get', { + id: 'id', + storedFields: ['some', 'fields'], +}) +``` + +after: + +```ts +client.asCurrentUser.get({ + id: 'id', + stored_fields: ['some', 'fields'], +}) +``` + +- the request abortion API changed + +All promises returned from the client API calls now have an `abort` method that can be used to cancel the request. + +before: + +```ts +const controller = new AbortController(); +legacyClient.callAsCurrentUser('ping', {}, { + signal: controller.signal, +}) +// later +controller.abort(); +``` + +after: + +```ts +const request = client.asCurrentUser.ping(); +// later +request.abort(); +``` + Please refer to the [Breaking changes list](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/breaking-changes.html) for more information about the changes between the legacy and new client. @@ -1112,8 +1173,9 @@ Apart from the API format change, accessing the client from within a route handl did not change. As it was done for the legacy client, a preconfigured scoped client bound to the request is accessible using `core` context provider: +before: + ```ts -// before router.get( { path: '/my-route', @@ -1129,8 +1191,9 @@ router.get( ); ``` +after: + ```ts -// after router.get( { path: '/my-route', @@ -1148,10 +1211,14 @@ router.get( ### Creating a custom client +Note that the `plugins` option is now longer available on the new client. As the API is now exhaustive, adding custom +endpoints using plugins should no longer be necessary. + The API to create custom clients did not change much: +before: + ```ts -// before const customClient = coreStart.elasticsearch.legacy.createClient('my-custom-client', customConfig); // do something with the client, such as await customClient.callAsInternalUser('ping'); @@ -1159,8 +1226,9 @@ await customClient.callAsInternalUser('ping'); customClient.close(); ``` +after: + ```ts -// after const customClient = coreStart.elasticsearch.createClient('my-custom-client', customConfig); // do something with the client, such as await customClient.asInternalUser.ping(); @@ -1168,9 +1236,8 @@ await customClient.asInternalUser.ping(); customClient.close(); ``` -Note that the `plugins` option is now longer available on the new client. As the API is now exhaustive, adding custom -endpoints using plugins should no longer be necessary. If, for any reasons, one still needs to reach an -endpoint not listed on the client API, using `request.transport` is still possible: +If, for any reasons, one still needs to reach an endpoint not listed on the client API, using `request.transport` +is still possible: ```ts const { body } = await client.asCurrentUser.transport.request({ From 2769178c8936b1127b2d4d8459e44657ea6c65f5 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 22 Jul 2020 14:35:44 +0200 Subject: [PATCH 3/3] add paragraph on header override --- src/core/MIGRATION_EXAMPLES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 53c9fd3cf49c3..d630fec652a37 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -1164,6 +1164,20 @@ const request = client.asCurrentUser.ping(); request.abort(); ``` +- it is now possible to override headers when performing specific API calls. + +Note that doing so is strongly discouraged due to potential side effects with the ES service internal +behavior when scoping as the internal or as the current user. + +```ts +const request = client.asCurrentUser.ping({}, { + headers: { + authorization: 'foo', + custom: 'bar', + } +}); +``` + Please refer to the [Breaking changes list](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/breaking-changes.html) for more information about the changes between the legacy and new client.