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

add migration section for the new ES client #71604

Merged
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 179 additions & 1 deletion src/core/MIGRATION_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1003,4 +1007,178 @@ setup(core: CoreSetup){
},
})
}
```
```

## 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:
pgayvallet marked this conversation as resolved.
Show resolved Hide resolved

```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<GetResponse>({ id: 'id' });
mshustov marked this conversation as resolved.
Show resolved Hide resolved
// fallback to `Record<string, any>` 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)
mshustov marked this conversation as resolved.
Show resolved Hide resolved
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
pgayvallet marked this conversation as resolved.
Show resolved Hide resolved
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.