ES client : use the new type definitions (#83808)

* Use client from branch

* Get type checking working in core

* Fix types in other plugins

* Update client types + remove type errors from core

* migrate Task Manager Elasticsearch typing from legacy library to client library

* use SortOrder instead o string in alerts

* Update client types + fix core type issues

* fix maps ts errors

* Update Lens types

* Convert Search Profiler body from a string to an object to conform to SearchRequest type.

* Fix SOT types

* Fix/mute Security/Spaces plugins type errors.

* Fix bootstrap types

* Fix painless_lab

* corrected es typing in Event Log

* Use new types from client for inferred search responses

* Latest type defs

* Integrate latest type defs for APM/UX

* fix core errors

* fix telemetry errors

* fix canvas errors

* fix data_enhanced errors

* fix event_log errors

* mute lens errors

* fix or mute maps errors

* fix reporting errors

* fix security errors

* mute errors in task_manager

* fix errors in telemetry_collection_xpack

* fix errors in data plugins

* fix errors in alerts

* mute errors in index_management

* fix task_manager errors

* mute or fix lens errors

* fix upgrade_assistant errors

* fix or mute errors in index_lifecycle_management

* fix discover errors

* fix core tests

* ML changes

* fix core type errors

* mute error in kbn-es-archiver

* fix error in data plugin

* fix error in telemetry plugin

* fix error in discover

* fix discover errors

* fix errors in task_manager

* fix security errors

* fix wrong conflict resolution

* address errors with upstream code

* update deps to the last commit

* remove outdated comments

* fix core errors

* fix errors after update

* adding more expect errors to ML

* pull the lastest changes

* fix core errors

* fix errors in infra plugin

* fix errors in uptime plugin

* fix errors in ml

* fix errors in xpack telemetry

* fix or mute errors in transform

* fix errors in upgrade assistant

* fix or mute fleet errors

* start fixing apm errors

* fix errors in osquery

* fix telemetry tests

* core cleanup

* fix asMutableArray imports

* cleanup

* data_enhanced cleanup

* cleanup events_log

* cleaup

* fix error in kbn-es-archiver

* fix errors in kbn-es-archiver

* fix errors in kbn-es-archiver

* fix ES typings for Hit

* fix SO

* fix actions plugin

* fix fleet

* fix maps

* fix stack_alerts

* fix eslint problems

* fix event_log unit tests

* fix failures in data_enhanced tests

* fix test failure in kbn-es-archiver

* fix test failures in index_pattern_management

* fixing ML test

* remove outdated comment in kbn-es-archiver

* fix error type in ml

* fix eslint errors in osquery plugin

* fix runtime error in infra plugin

* revert changes to event_log cluser exist check

* fix eslint error in osquery

* fixing ML endpoint argument types

* fx types

* Update api-extractor docs

* attempt fix for ese test

* Fix lint error

* Fix types for ts refs

* Fix data_enhanced unit test

* fix lens types

* generate docs

* Fix a number of type issues in monitoring and ml

* fix triggers_actions_ui

* Fix ILM functional test

* Put search.d.ts typings back

* fix data plugin

* Update typings in typings/elasticsearch

* Update snapshots

* mute errors in task_manager

* mute fleet errors

* lens. remove unnecessary ts-expect-errors

* fix errors in stack_alerts

* mute errors in osquery

* fix errors in security_solution

* fix errors in lists

* fix errors in cases

* mute errors in search_examples

* use KibanaClient to enforce promise-based API

* fix errors in test/ folder

* update comment

* fix errors in x-pack/test folder

* fix errors in ml plugin

* fix optional fields in ml api_integartoon tests

* fix another casting problem in ml tests

* fix another ml test failure

* fix fleet problem after conflict resolution

* rollback changes in security_solution. trying to fix test

* Update type for discover rows

* uncomment runtime_mappings as its outdated

* address comments from Wylie

* remove eslint error due to any

* mute error due to incompatibility

* Apply suggestions from code review

Co-authored-by: John Schulz <github.com@jfsiii.org>

* fix type error in lens tests

* Update x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts

Co-authored-by: Alison Goryachev <alisonmllr20@gmail.com>

* Update x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts

Co-authored-by: Alison Goryachev <alisonmllr20@gmail.com>

* update deps

* fix errors in core types

* fix errors for the new elastic/elasticsearch version

* remove unused type

* remove unnecessary manual type cast and put optional chaining back

* ML: mute Datafeed is missing indices_options

* Apply suggestions from code review

Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>

* use canary pacakge instead of git commit

Co-authored-by: Josh Dover <me@joshdover.com>
Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
Co-authored-by: Gidi Meir Morris <github@gidi.io>
Co-authored-by: Nathan Reese <reese.nathan@gmail.com>
Co-authored-by: Wylie Conlon <wylieconlon@gmail.com>
Co-authored-by: CJ Cenizal <cj@cenizal.com>
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
Co-authored-by: Dario Gieselaar <dario.gieselaar@elastic.co>
Co-authored-by: restrry <restrry@gmail.com>
Co-authored-by: James Gowdy <jgowdy@elastic.co>
Co-authored-by: John Schulz <github.com@jfsiii.org>
Co-authored-by: Alison Goryachev <alisonmllr20@gmail.com>
This commit is contained in:
Tomas Della Vedova 2021-03-25 09:47:16 +01:00 committed by GitHub
parent 9724051f92
commit 238791b942
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
541 changed files with 3666 additions and 3051 deletions

View file

@ -27,10 +27,10 @@ export interface SavedObjectsFindOptions
| [preference](./kibana-plugin-core-public.savedobjectsfindoptions.preference.md) | <code>string</code> | An optional ES preference value to be used for the query \* |
| [rootSearchFields](./kibana-plugin-core-public.savedobjectsfindoptions.rootsearchfields.md) | <code>string[]</code> | The fields to perform the parsed query against. Unlike the <code>searchFields</code> argument, these are expected to be root fields and will not be modified. If used in conjunction with <code>searchFields</code>, both are concatenated together. |
| [search](./kibana-plugin-core-public.savedobjectsfindoptions.search.md) | <code>string</code> | Search documents using the Elasticsearch Simple Query String syntax. See Elasticsearch Simple Query String <code>query</code> argument for more information |
| [searchAfter](./kibana-plugin-core-public.savedobjectsfindoptions.searchafter.md) | <code>unknown[]</code> | Use the sort values from the previous page to retrieve the next page of results. |
| [searchAfter](./kibana-plugin-core-public.savedobjectsfindoptions.searchafter.md) | <code>estypes.Id[]</code> | Use the sort values from the previous page to retrieve the next page of results. |
| [searchFields](./kibana-plugin-core-public.savedobjectsfindoptions.searchfields.md) | <code>string[]</code> | The fields to perform the parsed query against. See Elasticsearch Simple Query String <code>fields</code> argument for more information |
| [sortField](./kibana-plugin-core-public.savedobjectsfindoptions.sortfield.md) | <code>string</code> | |
| [sortOrder](./kibana-plugin-core-public.savedobjectsfindoptions.sortorder.md) | <code>string</code> | |
| [sortOrder](./kibana-plugin-core-public.savedobjectsfindoptions.sortorder.md) | <code>estypes.SortOrder</code> | |
| [type](./kibana-plugin-core-public.savedobjectsfindoptions.type.md) | <code>string &#124; string[]</code> | |
| [typeToNamespacesMap](./kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md) | <code>Map&lt;string, string[] &#124; undefined&gt;</code> | This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the <code>type</code> and <code>namespaces</code> fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. |

View file

@ -9,5 +9,5 @@ Use the sort values from the previous page to retrieve the next page of results.
<b>Signature:</b>
```typescript
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
```

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
sortOrder?: string;
sortOrder?: estypes.SortOrder;
```

View file

@ -27,10 +27,10 @@ export interface SavedObjectsFindOptions
| [preference](./kibana-plugin-core-server.savedobjectsfindoptions.preference.md) | <code>string</code> | An optional ES preference value to be used for the query \* |
| [rootSearchFields](./kibana-plugin-core-server.savedobjectsfindoptions.rootsearchfields.md) | <code>string[]</code> | The fields to perform the parsed query against. Unlike the <code>searchFields</code> argument, these are expected to be root fields and will not be modified. If used in conjunction with <code>searchFields</code>, both are concatenated together. |
| [search](./kibana-plugin-core-server.savedobjectsfindoptions.search.md) | <code>string</code> | Search documents using the Elasticsearch Simple Query String syntax. See Elasticsearch Simple Query String <code>query</code> argument for more information |
| [searchAfter](./kibana-plugin-core-server.savedobjectsfindoptions.searchafter.md) | <code>unknown[]</code> | Use the sort values from the previous page to retrieve the next page of results. |
| [searchAfter](./kibana-plugin-core-server.savedobjectsfindoptions.searchafter.md) | <code>estypes.Id[]</code> | Use the sort values from the previous page to retrieve the next page of results. |
| [searchFields](./kibana-plugin-core-server.savedobjectsfindoptions.searchfields.md) | <code>string[]</code> | The fields to perform the parsed query against. See Elasticsearch Simple Query String <code>fields</code> argument for more information |
| [sortField](./kibana-plugin-core-server.savedobjectsfindoptions.sortfield.md) | <code>string</code> | |
| [sortOrder](./kibana-plugin-core-server.savedobjectsfindoptions.sortorder.md) | <code>string</code> | |
| [sortOrder](./kibana-plugin-core-server.savedobjectsfindoptions.sortorder.md) | <code>estypes.SortOrder</code> | |
| [type](./kibana-plugin-core-server.savedobjectsfindoptions.type.md) | <code>string &#124; string[]</code> | |
| [typeToNamespacesMap](./kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md) | <code>Map&lt;string, string[] &#124; undefined&gt;</code> | This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the <code>type</code> and <code>namespaces</code> fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. |

View file

@ -9,5 +9,5 @@ Use the sort values from the previous page to retrieve the next page of results.
<b>Signature:</b>
```typescript
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
```

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
sortOrder?: string;
sortOrder?: estypes.SortOrder;
```

View file

@ -16,5 +16,5 @@ export interface SavedObjectsFindResult<T = unknown> extends SavedObject<T>
| Property | Type | Description |
| --- | --- | --- |
| [score](./kibana-plugin-core-server.savedobjectsfindresult.score.md) | <code>number</code> | The Elasticsearch <code>_score</code> of this result. |
| [sort](./kibana-plugin-core-server.savedobjectsfindresult.sort.md) | <code>unknown[]</code> | The Elasticsearch <code>sort</code> value of this result. |
| [sort](./kibana-plugin-core-server.savedobjectsfindresult.sort.md) | <code>string[]</code> | The Elasticsearch <code>sort</code> value of this result. |

View file

@ -9,7 +9,7 @@ The Elasticsearch `sort` value of this result.
<b>Signature:</b>
```typescript
sort?: unknown[];
sort?: string[];
```
## Remarks

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
export declare type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchResponse<Source>>;
export declare type IEsSearchResponse<Source = any> = IKibanaSearchResponse<estypes.SearchResponse<Source>>;
```

View file

@ -14,7 +14,7 @@ Fetch this source and reject the returned Promise on error
<b>Signature:</b>
```typescript
fetch(options?: ISearchOptions): Promise<import("elasticsearch").SearchResponse<any>>;
fetch(options?: ISearchOptions): Promise<import("@elastic/elasticsearch/api/types").SearchResponse<any>>;
```
## Parameters
@ -25,5 +25,5 @@ fetch(options?: ISearchOptions): Promise<import("elasticsearch").SearchResponse<
<b>Returns:</b>
`Promise<import("elasticsearch").SearchResponse<any>>`
`Promise<import("@elastic/elasticsearch/api/types").SearchResponse<any>>`

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
export declare type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchResponse<Source>>;
export declare type IEsSearchResponse<Source = any> = IKibanaSearchResponse<estypes.SearchResponse<Source>>;
```

View file

@ -145,7 +145,8 @@ export const SearchExamplesApp = ({
setResponse(res.rawResponse);
setTimeTook(res.rawResponse.took);
const avgResult: number | undefined = res.rawResponse.aggregations
? res.rawResponse.aggregations[1].value
? // @ts-expect-error @elastic/elasticsearch no way to declare a type for aggregation in the search response
res.rawResponse.aggregations[1].value
: undefined;
const message = (
<EuiText>

View file

@ -702,13 +702,15 @@ function doSearch(
const startTs = performance.now();
// Submit the search request using the `data.search` service.
// @ts-expect-error request.params is incompatible. Filter is not assignable to QueryContainer
return data.search
.search(req, { sessionId })
.pipe(
tap((res) => {
if (isCompleteResponse(res)) {
const avgResult: number | undefined = res.rawResponse.aggregations
? res.rawResponse.aggregations[1]?.value ?? res.rawResponse.aggregations[2]?.value
? // @ts-expect-error @elastic/elasticsearch no way to declare a type for aggregation in the search response
res.rawResponse.aggregations[1]?.value ?? res.rawResponse.aggregations[2]?.value
: undefined;
const message = (
<EuiText>

View file

@ -96,7 +96,7 @@
},
"dependencies": {
"@elastic/datemath": "link:packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.3",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.4",
"@elastic/ems-client": "7.12.0",
"@elastic/eui": "31.7.0",
"@elastic/filesaver": "1.1.2",

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient } from '@kbn/test';
@ -17,7 +17,7 @@ export async function emptyKibanaIndexAction({
log,
kbnClient,
}: {
client: Client;
client: KibanaClient;
log: ToolingLog;
kbnClient: KbnClient;
}) {

View file

@ -11,7 +11,7 @@ import { createReadStream } from 'fs';
import { Readable } from 'stream';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient } from '@kbn/test';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { createPromiseFromStreams, concatStreamProviders } from '@kbn/utils';
import { ES_CLIENT_HEADERS } from '../client_headers';
@ -48,7 +48,7 @@ export async function loadAction({
name: string;
skipExisting: boolean;
useCreate: boolean;
client: Client;
client: KibanaClient;
dataDir: string;
log: ToolingLog;
kbnClient: KbnClient;

View file

@ -9,7 +9,7 @@
import { resolve } from 'path';
import { createWriteStream, mkdirSync } from 'fs';
import { Readable, Writable } from 'stream';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { createListStream, createPromiseFromStreams } from '@kbn/utils';
@ -32,7 +32,7 @@ export async function saveAction({
}: {
name: string;
indices: string | string[];
client: Client;
client: KibanaClient;
dataDir: string;
log: ToolingLog;
raw: boolean;

View file

@ -9,7 +9,7 @@
import { resolve } from 'path';
import { createReadStream } from 'fs';
import { Readable, Writable } from 'stream';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient } from '@kbn/test';
import { createPromiseFromStreams } from '@kbn/utils';
@ -32,7 +32,7 @@ export async function unloadAction({
kbnClient,
}: {
name: string;
client: Client;
client: KibanaClient;
dataDir: string;
log: ToolingLog;
kbnClient: KbnClient;

View file

@ -8,4 +8,4 @@
export const ES_CLIENT_HEADERS = {
'x-elastic-product-origin': 'kibana',
};
} as const;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient } from '@kbn/test';
@ -20,14 +20,14 @@ import {
} from './actions';
interface Options {
client: Client;
client: KibanaClient;
dataDir: string;
log: ToolingLog;
kbnClient: KbnClient;
}
export class EsArchiver {
private readonly client: Client;
private readonly client: KibanaClient;
private readonly dataDir: string;
private readonly log: ToolingLog;
private readonly kbnClient: KbnClient;

View file

@ -7,7 +7,7 @@
*/
import { Transform } from 'stream';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { Stats } from '../stats';
import { Progress } from '../progress';
import { ES_CLIENT_HEADERS } from '../../client_headers';
@ -21,7 +21,7 @@ export function createGenerateDocRecordsStream({
progress,
query,
}: {
client: Client;
client: KibanaClient;
stats: Stats;
progress: Progress;
query?: Record<string, any>;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import AggregateError from 'aggregate-error';
import { Writable } from 'stream';
import { Stats } from '../stats';
@ -14,7 +14,7 @@ import { Progress } from '../progress';
import { ES_CLIENT_HEADERS } from '../../client_headers';
export function createIndexDocRecordsStream(
client: Client,
client: KibanaClient,
stats: Stats,
progress: Progress,
useCreate: boolean = false

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import sinon from 'sinon';
import { ToolingLog } from '@kbn/dev-utils';
import { Stats } from '../../stats';
@ -67,7 +67,7 @@ const createEsClientError = (errorType: string) => {
const indexAlias = (aliases: Record<string, any>, index: string) =>
Object.keys(aliases).find((k) => aliases[k] === index);
type StubClient = Client;
type StubClient = KibanaClient;
export const createStubClient = (
existingIndices: string[] = [],

View file

@ -125,7 +125,6 @@ describe('esArchiver: createCreateIndexStream()', () => {
]);
sinon.assert.calledWith(client.indices.create as sinon.SinonSpy, {
method: 'PUT',
index: 'index',
body: {
settings: undefined,

View file

@ -9,7 +9,8 @@
import { Transform, Readable } from 'stream';
import { inspect } from 'util';
import { Client } from '@elastic/elasticsearch';
import { estypes } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { Stats } from '../stats';
@ -18,12 +19,9 @@ import { deleteIndex } from './delete_index';
import { ES_CLIENT_HEADERS } from '../../client_headers';
interface DocRecord {
value: {
value: estypes.IndexState & {
index: string;
type: string;
settings: Record<string, any>;
mappings: Record<string, any>;
aliases: Record<string, any>;
};
}
@ -33,7 +31,7 @@ export function createCreateIndexStream({
skipExisting = false,
log,
}: {
client: Client;
client: KibanaClient;
stats: Stats;
skipExisting?: boolean;
log: ToolingLog;
@ -66,7 +64,6 @@ export function createCreateIndexStream({
await client.indices.create(
{
method: 'PUT',
index,
body: {
settings,

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { Stats } from '../stats';
import { ES_CLIENT_HEADERS } from '../../client_headers';
@ -15,7 +15,7 @@ import { ES_CLIENT_HEADERS } from '../../client_headers';
const PENDING_SNAPSHOT_STATUSES = ['INIT', 'STARTED', 'WAITING'];
export async function deleteIndex(options: {
client: Client;
client: KibanaClient;
stats: Stats;
index: string | string[];
log: ToolingLog;
@ -84,7 +84,7 @@ export function isDeleteWhileSnapshotInProgressError(error: any) {
* snapshotting this index to complete.
*/
export async function waitForSnapshotCompletion(
client: Client,
client: KibanaClient,
index: string | string[],
log: ToolingLog
) {

View file

@ -7,7 +7,7 @@
*/
import { Transform } from 'stream';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { Stats } from '../stats';
@ -15,7 +15,7 @@ import { deleteIndex } from './delete_index';
import { cleanKibanaIndices } from './kibana_index';
export function createDeleteIndexStream(
client: Client,
client: KibanaClient,
stats: Stats,
log: ToolingLog,
kibanaPluginIds: string[]

View file

@ -7,11 +7,11 @@
*/
import { Transform } from 'stream';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { Stats } from '../stats';
import { ES_CLIENT_HEADERS } from '../../client_headers';
export function createGenerateIndexRecordsStream(client: Client, stats: Stats) {
export function createGenerateIndexRecordsStream(client: KibanaClient, stats: Stats) {
return new Transform({
writableObjectMode: true,
readableObjectMode: true,

View file

@ -8,7 +8,7 @@
import { inspect } from 'util';
import { Client } from '@elastic/elasticsearch';
import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient } from '@kbn/test';
import { Stats } from '../stats';
@ -23,7 +23,7 @@ export async function deleteKibanaIndices({
stats,
log,
}: {
client: Client;
client: KibanaClient;
stats: Stats;
log: ToolingLog;
}) {
@ -67,22 +67,27 @@ export async function migrateKibanaIndex(kbnClient: KbnClient) {
* with .kibana, then filters out any that aren't actually Kibana's core
* index (e.g. we don't want to remove .kibana_task_manager or the like).
*/
async function fetchKibanaIndices(client: Client) {
const resp = await client.cat.indices<unknown>(
function isKibanaIndex(index?: string): index is string {
return Boolean(
index &&
(/^\.kibana(:?_\d*)?$/.test(index) ||
/^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index))
);
}
async function fetchKibanaIndices(client: KibanaClient) {
const resp = await client.cat.indices(
{ index: '.kibana*', format: 'json' },
{
headers: ES_CLIENT_HEADERS,
}
);
const isKibanaIndex = (index: string) =>
/^\.kibana(:?_\d*)?$/.test(index) ||
/^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index);
if (!Array.isArray(resp.body)) {
throw new Error(`expected response to be an array ${inspect(resp.body)}`);
}
return resp.body.map((x: { index: string }) => x.index).filter(isKibanaIndex);
return resp.body.map((x: { index?: string }) => x.index).filter(isKibanaIndex);
}
const delay = (delayInMs: number) => new Promise((resolve) => setTimeout(resolve, delayInMs));
@ -93,7 +98,7 @@ export async function cleanKibanaIndices({
log,
kibanaPluginIds,
}: {
client: Client;
client: KibanaClient;
stats: Stats;
log: ToolingLog;
kibanaPluginIds: string[];
@ -149,7 +154,13 @@ export async function cleanKibanaIndices({
stats.deletedIndex('.kibana');
}
export async function createDefaultSpace({ index, client }: { index: string; client: Client }) {
export async function createDefaultSpace({
index,
client,
}: {
index: string;
client: KibanaClient;
}) {
await client.create(
{
index,

View file

@ -11,6 +11,7 @@ import { ConfigDeprecationProvider } from '@kbn/config';
import { ConfigPath } from '@kbn/config';
import { DetailedPeerCertificate } from 'tls';
import { EnvironmentMode } from '@kbn/config';
import { estypes } from '@elastic/elasticsearch';
import { EuiBreadcrumb } from '@elastic/eui';
import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
@ -1225,12 +1226,12 @@ export interface SavedObjectsFindOptions {
preference?: string;
rootSearchFields?: string[];
search?: string;
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
searchFields?: string[];
// (undocumented)
sortField?: string;
// (undocumented)
sortOrder?: string;
sortOrder?: estypes.SortOrder;
// (undocumented)
type: string | string[];
typeToNamespacesMap?: Map<string, string[] | undefined>;

View file

@ -120,10 +120,10 @@ describe('CoreUsageDataService', () => {
body: [
{
name: '.kibana_task_manager_1',
'docs.count': 10,
'docs.deleted': 10,
'store.size': 1000,
'pri.store.size': 2000,
'docs.count': '10',
'docs.deleted': '10',
'store.size': '1000',
'pri.store.size': '2000',
},
],
} as any);
@ -131,10 +131,10 @@ describe('CoreUsageDataService', () => {
body: [
{
name: '.kibana_1',
'docs.count': 20,
'docs.deleted': 20,
'store.size': 2000,
'pri.store.size': 4000,
'docs.count': '20',
'docs.deleted': '20',
'store.size': '2000',
'pri.store.size': '4000',
},
],
} as any);

View file

@ -118,10 +118,14 @@ export class CoreUsageDataService implements CoreService<CoreUsageDataSetup, Cor
const stats = body[0];
return {
alias: kibanaOrTaskManagerIndex(index, this.kibanaConfig!.index),
docsCount: stats['docs.count'],
docsDeleted: stats['docs.deleted'],
storeSizeBytes: stats['store.size'],
primaryStoreSizeBytes: stats['pri.store.size'],
// @ts-expect-error @elastic/elasticsearch declares it 'docs.count' as optional
docsCount: parseInt(stats['docs.count'], 10),
// @ts-expect-error @elastic/elasticsearch declares it 'docs.deleted' as optional
docsDeleted: parseInt(stats['docs.deleted'], 10),
// @ts-expect-error @elastic/elasticsearch declares it 'store.size' as string | number
storeSizeBytes: parseInt(stats['store.size'], 10),
// @ts-expect-error @elastic/elasticsearch declares it 'pri.store.size' as string | number
primaryStoreSizeBytes: parseInt(stats['pri.store.size'], 10),
};
});
})

View file

@ -156,9 +156,11 @@ const createErrorTransportRequestPromise = (err: any): MockedTransportRequestPro
return promise as MockedTransportRequestPromise<never>;
};
function createApiResponse(opts: Partial<ApiResponse> = {}): ApiResponse {
function createApiResponse<TResponse = Record<string, any>>(
opts: Partial<ApiResponse<TResponse>> = {}
): ApiResponse<TResponse> {
return {
body: {},
body: {} as any,
statusCode: 200,
headers: {},
warnings: [],

View file

@ -11,7 +11,7 @@ import { elasticsearchClientMock } from './mocks';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { retryCallCluster, migrationRetryCallCluster } from './retry_call_cluster';
const dummyBody = { foo: 'bar' };
const dummyBody: any = { foo: 'bar' };
const createErrorReturn = (err: any) =>
elasticsearchClientMock.createErrorTransportRequestPromise(err);
@ -29,7 +29,7 @@ describe('retryCallCluster', () => {
client.asyncSearch.get.mockReturnValue(successReturn);
const result = await retryCallCluster(() => client.asyncSearch.get());
const result = await retryCallCluster(() => client.asyncSearch.get({} as any));
expect(result.body).toEqual(dummyBody);
});
@ -44,7 +44,7 @@ describe('retryCallCluster', () => {
)
.mockImplementationOnce(() => successReturn);
const result = await retryCallCluster(() => client.asyncSearch.get());
const result = await retryCallCluster(() => client.asyncSearch.get({} as any));
expect(result.body).toEqual(dummyBody);
});

View file

@ -117,6 +117,7 @@ describe('getSortedObjectsForExport()', () => {
"keepAlive": "2m",
},
"search": undefined,
"searchAfter": undefined,
"sortField": "updated_at",
"sortOrder": "desc",
"type": Array [
@ -145,7 +146,7 @@ describe('getSortedObjectsForExport()', () => {
type = 'index-pattern',
}: {
attributes?: Record<string, unknown>;
sort?: unknown[];
sort?: string[];
type?: string;
} = {}
) {
@ -461,6 +462,7 @@ describe('getSortedObjectsForExport()', () => {
"keepAlive": "2m",
},
"search": undefined,
"searchAfter": undefined,
"sortField": "updated_at",
"sortOrder": "desc",
"type": Array [
@ -617,6 +619,7 @@ describe('getSortedObjectsForExport()', () => {
"keepAlive": "2m",
},
"search": "foo",
"searchAfter": undefined,
"sortField": "updated_at",
"sortOrder": "desc",
"type": Array [
@ -710,6 +713,7 @@ describe('getSortedObjectsForExport()', () => {
"keepAlive": "2m",
},
"search": undefined,
"searchAfter": undefined,
"sortField": "updated_at",
"sortOrder": "desc",
"type": Array [
@ -808,6 +812,7 @@ describe('getSortedObjectsForExport()', () => {
"keepAlive": "2m",
},
"search": undefined,
"searchAfter": undefined,
"sortField": "updated_at",
"sortOrder": "desc",
"type": Array [

View file

@ -95,7 +95,7 @@ const checkOriginConflict = async (
perPage: 10,
fields: ['title'],
sortField: 'updated_at',
sortOrder: 'desc',
sortOrder: 'desc' as const,
...(namespace && { namespaces: [namespace] }),
};
const findResult = await savedObjectsClient.find<{ title?: string }>(findOptions);

View file

@ -102,7 +102,7 @@ export type SavedObjectsFieldMapping =
/** @internal */
export interface IndexMapping {
dynamic?: string;
dynamic?: boolean | 'strict';
properties: SavedObjectsMappingProperties;
_meta?: IndexMappingMeta;
}

View file

@ -164,6 +164,7 @@ describe('diffMappings', () => {
_meta: {
migrationMappingPropertyHashes: { foo: 'bar' },
},
// @ts-expect-error
dynamic: 'abcde',
properties: {},
};

View file

@ -12,11 +12,12 @@
* funcationality contained here.
*/
import type { estypes } from '@elastic/elasticsearch';
import { IndexMapping } from '../../mappings';
export interface CallCluster {
(path: 'bulk', opts: { body: object[] }): Promise<BulkResult>;
(path: 'count', opts: CountOpts): Promise<{ count: number; _shards: ShardsInfo }>;
(path: 'count', opts: CountOpts): Promise<{ count: number; _shards: estypes.ShardStatistics }>;
(path: 'clearScroll', opts: { scrollId: string }): Promise<any>;
(path: 'indices.create', opts: IndexCreationOpts): Promise<any>;
(path: 'indices.exists', opts: IndexOpts): Promise<boolean>;
@ -143,7 +144,7 @@ export interface IndexSettingsResult {
}
export interface RawDoc {
_id: string;
_id: estypes.Id;
_source: any;
_type?: string;
}
@ -153,14 +154,7 @@ export interface SearchResults {
hits: RawDoc[];
};
_scroll_id?: string;
_shards: ShardsInfo;
}
export interface ShardsInfo {
total: number;
successful: number;
skipped: number;
failed: number;
_shards: estypes.ShardStatistics;
}
export interface ErrorResponse {

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import _ from 'lodash';
import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks';
import * as Index from './elastic_index';
@ -33,41 +34,6 @@ describe('ElasticIndex', () => {
expect(client.indices.get).toHaveBeenCalledWith({ index: '.kibana-test' }, { ignore: [404] });
});
test('fails if the index doc type is unsupported', async () => {
client.indices.get.mockImplementation((params) => {
const index = params!.index as string;
return elasticsearchClientMock.createSuccessTransportRequestPromise({
[index]: {
aliases: { foo: index },
mappings: { spock: { dynamic: 'strict', properties: { a: 'b' } } },
},
});
});
await expect(Index.fetchInfo(client, '.baz')).rejects.toThrow(
/cannot be automatically migrated/
);
});
test('fails if there are multiple root types', async () => {
client.indices.get.mockImplementation((params) => {
const index = params!.index as string;
return elasticsearchClientMock.createSuccessTransportRequestPromise({
[index]: {
aliases: { foo: index },
mappings: {
doc: { dynamic: 'strict', properties: { a: 'b' } },
doctor: { dynamic: 'strict', properties: { a: 'b' } },
},
},
});
});
await expect(Index.fetchInfo(client, '.baz')).rejects.toThrow(
/cannot be automatically migrated/
);
});
test('decorates index info with exists and indexName', async () => {
client.indices.get.mockImplementation((params) => {
const index = params!.index as string;
@ -75,8 +41,9 @@ describe('ElasticIndex', () => {
[index]: {
aliases: { foo: index },
mappings: { dynamic: 'strict', properties: { a: 'b' } },
settings: {},
},
});
} as estypes.GetIndexResponse);
});
const info = await Index.fetchInfo(client, '.baz');
@ -85,6 +52,7 @@ describe('ElasticIndex', () => {
mappings: { dynamic: 'strict', properties: { a: 'b' } },
exists: true,
indexName: '.baz',
settings: {},
});
});
});
@ -134,7 +102,7 @@ describe('ElasticIndex', () => {
test('removes existing alias', async () => {
client.indices.getAlias.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
'.my-fanci-index': '.muchacha',
'.my-fanci-index': { aliases: { '.muchacha': {} } },
})
);
@ -157,7 +125,7 @@ describe('ElasticIndex', () => {
test('allows custom alias actions', async () => {
client.indices.getAlias.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
'.my-fanci-index': '.muchacha',
'.my-fanci-index': { aliases: { '.muchacha': {} } },
})
);
@ -185,14 +153,18 @@ describe('ElasticIndex', () => {
test('it creates the destination index, then reindexes to it', async () => {
client.indices.getAlias.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
'.my-fanci-index': '.muchacha',
'.my-fanci-index': { aliases: { '.muchacha': {} } },
})
);
client.reindex.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ task: 'abc' })
elasticsearchClientMock.createSuccessTransportRequestPromise({
task: 'abc',
} as estypes.ReindexResponse)
);
client.tasks.get.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ completed: true })
elasticsearchClientMock.createSuccessTransportRequestPromise({
completed: true,
} as estypes.GetTaskResponse)
);
const info = {
@ -200,7 +172,7 @@ describe('ElasticIndex', () => {
exists: true,
indexName: '.ze-index',
mappings: {
dynamic: 'strict',
dynamic: 'strict' as const,
properties: { foo: { type: 'keyword' } },
},
};
@ -259,13 +231,16 @@ describe('ElasticIndex', () => {
test('throws error if re-index task fails', async () => {
client.indices.getAlias.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
'.my-fanci-index': '.muchacha',
'.my-fanci-index': { aliases: { '.muchacha': {} } },
})
);
client.reindex.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ task: 'abc' })
elasticsearchClientMock.createSuccessTransportRequestPromise({
task: 'abc',
} as estypes.ReindexResponse)
);
client.tasks.get.mockResolvedValue(
// @ts-expect-error @elastic/elasticsearch GetTaskResponse requires a `task` property even on errors
elasticsearchClientMock.createSuccessTransportRequestPromise({
completed: true,
error: {
@ -273,7 +248,7 @@ describe('ElasticIndex', () => {
reason: 'all shards failed',
failed_shards: [],
},
})
} as estypes.GetTaskResponse)
);
const info = {
@ -286,6 +261,7 @@ describe('ElasticIndex', () => {
},
};
// @ts-expect-error
await expect(Index.convertToAlias(client, info, '.muchacha', 10)).rejects.toThrow(
/Re-index failed \[search_phase_execution_exception\] all shards failed/
);
@ -319,7 +295,9 @@ describe('ElasticIndex', () => {
describe('write', () => {
test('writes documents in bulk to the index', async () => {
client.bulk.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ items: [] })
elasticsearchClientMock.createSuccessTransportRequestPromise({
items: [] as any[],
} as estypes.BulkResponse)
);
const index = '.myalias';
@ -356,7 +334,7 @@ describe('ElasticIndex', () => {
client.bulk.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
items: [{ index: { error: { type: 'shazm', reason: 'dern' } } }],
})
} as estypes.BulkResponse)
);
const index = '.myalias';

View file

@ -12,11 +12,12 @@
*/
import _ from 'lodash';
import { estypes } from '@elastic/elasticsearch';
import { MigrationEsClient } from './migration_es_client';
import { CountResponse, SearchResponse } from '../../../elasticsearch';
import { IndexMapping } from '../../mappings';
import { SavedObjectsMigrationVersion } from '../../types';
import { AliasAction, RawDoc, ShardsInfo } from './call_cluster';
import { AliasAction, RawDoc } from './call_cluster';
import { SavedObjectsRawDocSource } from '../../serialization';
const settings = { number_of_shards: 1, auto_expand_replicas: '0-1' };
@ -46,6 +47,7 @@ export async function fetchInfo(client: MigrationEsClient, index: string): Promi
const [indexName, indexInfo] = Object.entries(body)[0];
// @ts-expect-error @elastic/elasticsearch IndexState.alias and IndexState.mappings should be required
return assertIsSupportedIndex({ ...indexInfo, exists: true, indexName });
}
@ -142,7 +144,7 @@ export async function write(client: MigrationEsClient, index: string, docs: RawD
return;
}
const exception: any = new Error(err.index.error!.reason);
const exception: any = new Error(err.index!.error!.reason);
exception.detail = err;
throw exception;
}
@ -322,7 +324,7 @@ function assertIsSupportedIndex(indexInfo: FullIndexInfo) {
* Object indices should only ever have a single shard. This is more to handle
* instances where customers manually expand the shards of an index.
*/
function assertResponseIncludeAllShards({ _shards }: { _shards: ShardsInfo }) {
function assertResponseIncludeAllShards({ _shards }: { _shards: estypes.ShardStatistics }) {
if (!_.has(_shards, 'total') || !_.has(_shards, 'successful')) {
return;
}
@ -375,11 +377,12 @@ async function reindex(
await new Promise((r) => setTimeout(r, pollInterval));
const { body } = await client.tasks.get({
task_id: task,
task_id: String(task),
});
if (body.error) {
const e = body.error;
// @ts-expect-error @elastic/elasticsearch GetTaskResponse doesn't contain `error` property
const e = body.error;
if (e) {
throw new Error(`Re-index failed [${e.type}] ${e.reason} :: ${JSON.stringify(e)}`);
}

View file

@ -7,6 +7,7 @@
*/
import _ from 'lodash';
import type { estypes } from '@elastic/elasticsearch';
import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks';
import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
@ -443,23 +444,28 @@ function withIndex(
elasticsearchClientMock.createSuccessTransportRequestPromise({
task: 'zeid',
_shards: { successful: 1, total: 1 },
})
} as estypes.ReindexResponse)
);
client.tasks.get.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ completed: true })
elasticsearchClientMock.createSuccessTransportRequestPromise({
completed: true,
} as estypes.GetTaskResponse)
);
client.search.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(searchResult(0))
elasticsearchClientMock.createSuccessTransportRequestPromise(searchResult(0) as any)
);
client.bulk.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ items: [] })
elasticsearchClientMock.createSuccessTransportRequestPromise({
items: [] as any[],
} as estypes.BulkResponse)
);
client.count.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
count: numOutOfDate,
_shards: { successful: 1, total: 1 },
})
} as estypes.CountResponse)
);
// @ts-expect-error
client.scroll.mockImplementation(() => {
if (scrollCallCounter <= docs.length) {
const result = searchResult(scrollCallCounter);

View file

@ -134,7 +134,7 @@ async function deleteIndexTemplates({ client, log, obsoleteIndexTemplatePattern
return;
}
const { body: templates } = await client.cat.templates<Array<{ name: string }>>({
const { body: templates } = await client.cat.templates({
format: 'json',
name: obsoleteIndexTemplatePattern,
});
@ -147,7 +147,7 @@ async function deleteIndexTemplates({ client, log, obsoleteIndexTemplatePattern
log.info(`Removing index templates: ${templateNames}`);
return Promise.all(templateNames.map((name) => client.indices.deleteTemplate({ name })));
return Promise.all(templateNames.map((name) => client.indices.deleteTemplate({ name: name! })));
}
/**
@ -185,7 +185,13 @@ async function migrateSourceToDest(context: Context) {
await Index.write(
client,
dest.indexName,
await migrateRawDocs(serializer, documentMigrator.migrateAndConvert, docs, log)
await migrateRawDocs(
serializer,
documentMigrator.migrateAndConvert,
// @ts-expect-error @elastic/elasticsearch `Hit._id` may be a string | number in ES, but we always expect strings in the SO index.
docs,
log
)
);
}
}

View file

@ -7,13 +7,13 @@
*/
import { take } from 'rxjs/operators';
import { estypes, errors as esErrors } from '@elastic/elasticsearch';
import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks';
import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { SavedObjectsType } from '../../types';
import { errors as esErrors } from '@elastic/elasticsearch';
import { DocumentMigrator } from '../core/document_migrator';
jest.mock('../core/document_migrator', () => {
return {
@ -105,10 +105,7 @@ describe('KibanaMigrator', () => {
const options = mockOptions();
options.client.cat.templates.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(
{ templates: [] },
{ statusCode: 404 }
)
elasticsearchClientMock.createSuccessTransportRequestPromise([], { statusCode: 404 })
);
options.client.indices.get.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
@ -129,7 +126,8 @@ describe('KibanaMigrator', () => {
options.client.cat.templates.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(
{ templates: [] },
// @ts-expect-error
{ templates: [] } as CatTemplatesResponse,
{ statusCode: 404 }
)
);
@ -155,7 +153,8 @@ describe('KibanaMigrator', () => {
options.client.cat.templates.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(
{ templates: [] },
// @ts-expect-error
{ templates: [] } as CatTemplatesResponse,
{ statusCode: 404 }
)
);
@ -193,7 +192,8 @@ describe('KibanaMigrator', () => {
options.client.cat.templates.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(
{ templates: [] },
// @ts-expect-error
{ templates: [] } as CatTemplatesResponse,
{ statusCode: 404 }
)
);
@ -323,7 +323,7 @@ describe('KibanaMigrator', () => {
completed: true,
error: { type: 'elatsicsearch_exception', reason: 'task failed with an error' },
failures: [],
task: { description: 'task description' },
task: { description: 'task description' } as any,
})
);
@ -365,15 +365,17 @@ const mockV2MigrationOptions = () => {
elasticsearchClientMock.createSuccessTransportRequestPromise({ acknowledged: true })
);
options.client.reindex.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ taskId: 'reindex_task_id' })
elasticsearchClientMock.createSuccessTransportRequestPromise({
taskId: 'reindex_task_id',
} as estypes.ReindexResponse)
);
options.client.tasks.get.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({
completed: true,
error: undefined,
failures: [],
task: { description: 'task description' },
})
task: { description: 'task description' } as any,
} as estypes.GetTaskResponse)
);
return options;

View file

@ -13,9 +13,10 @@ import { ElasticsearchClientError } from '@elastic/elasticsearch/lib/errors';
import { pipe } from 'fp-ts/lib/pipeable';
import { errors as EsErrors } from '@elastic/elasticsearch';
import { flow } from 'fp-ts/lib/function';
import type { estypes } from '@elastic/elasticsearch';
import { ElasticsearchClient } from '../../../elasticsearch';
import { IndexMapping } from '../../mappings';
import { SavedObjectsRawDoc } from '../../serialization';
import { SavedObjectsRawDoc, SavedObjectsRawDocSource } from '../../serialization';
import {
catchRetryableEsClientErrors,
RetryableEsClientError,
@ -56,20 +57,22 @@ export type FetchIndexResponse = Record<
export const fetchIndices = (
client: ElasticsearchClient,
indicesToFetch: string[]
): TaskEither.TaskEither<RetryableEsClientError, FetchIndexResponse> => () => {
return client.indices
.get(
{
index: indicesToFetch,
ignore_unavailable: true, // Don't return an error for missing indices. Note this *will* include closed indices, the docs are misleading https://github.com/elastic/elasticsearch/issues/63607
},
{ ignore: [404], maxRetries: 0 }
)
.then(({ body }) => {
return Either.right(body);
})
.catch(catchRetryableEsClientErrors);
};
): TaskEither.TaskEither<RetryableEsClientError, FetchIndexResponse> =>
// @ts-expect-error @elastic/elasticsearch IndexState.alias and IndexState.mappings should be required
() => {
return client.indices
.get(
{
index: indicesToFetch,
ignore_unavailable: true, // Don't return an error for missing indices. Note this *will* include closed indices, the docs are misleading https://github.com/elastic/elasticsearch/issues/63607
},
{ ignore: [404], maxRetries: 0 }
)
.then(({ body }) => {
return Either.right(body);
})
.catch(catchRetryableEsClientErrors);
};
/**
* Sets a write block in place for the given index. If the response includes
@ -98,7 +101,7 @@ export const setWriteBlock = (
},
{ maxRetries: 0 /** handle retry ourselves for now */ }
)
.then((res) => {
.then((res: any) => {
return res.body.acknowledged === true
? Either.right('set_write_block_succeeded' as const)
: Either.left({
@ -134,7 +137,11 @@ export const removeWriteBlock = (
// Don't change any existing settings
preserve_existing: true,
body: {
'index.blocks.write': false,
index: {
blocks: {
write: false,
},
},
},
},
{ maxRetries: 0 /** handle retry ourselves for now */ }
@ -285,7 +292,7 @@ interface WaitForTaskResponse {
error: Option.Option<{ type: string; reason: string; index: string }>;
completed: boolean;
failures: Option.Option<any[]>;
description: string;
description?: string;
}
/**
@ -299,12 +306,7 @@ const waitForTask = (
timeout: string
): TaskEither.TaskEither<RetryableEsClientError, WaitForTaskResponse> => () => {
return client.tasks
.get<{
completed: boolean;
response: { failures: any[] };
task: { description: string };
error: { type: string; reason: string; index: string };
}>({
.get({
task_id: taskId,
wait_for_completion: true,
timeout,
@ -314,6 +316,7 @@ const waitForTask = (
const failures = body.response?.failures ?? [];
return Either.right({
completed: body.completed,
// @ts-expect-error @elastic/elasticsearch GetTaskResponse doesn't declare `error` property
error: Option.fromNullable(body.error),
failures: failures.length > 0 ? Option.some(failures) : Option.none,
description: body.task.description,
@ -359,7 +362,7 @@ export const pickupUpdatedMappings = (
wait_for_completion: false,
})
.then(({ body: { task: taskId } }) => {
return Either.right({ taskId });
return Either.right({ taskId: String(taskId!) });
})
.catch(catchRetryableEsClientErrors);
};
@ -387,7 +390,6 @@ export const reindex = (
.reindex({
// Require targetIndex to be an alias. Prevents a new index from being
// created if targetIndex doesn't exist.
// @ts-expect-error This API isn't documented
require_alias: requireAlias,
body: {
// Ignore version conflicts from existing documents
@ -416,7 +418,7 @@ export const reindex = (
wait_for_completion: false,
})
.then(({ body: { task: taskId } }) => {
return Either.right({ taskId });
return Either.right({ taskId: String(taskId) });
})
.catch(catchRetryableEsClientErrors);
};
@ -624,7 +626,7 @@ export const createIndex = (
const aliasesObject = (aliases ?? []).reduce((acc, alias) => {
acc[alias] = {};
return acc;
}, {} as Record<string, {}>);
}, {} as Record<string, estypes.Alias>);
return client.indices
.create(
@ -727,7 +729,7 @@ export const updateAndPickupMappings = (
'update_mappings_succeeded'
> = () => {
return client.indices
.putMapping<Record<string, any>, IndexMapping>({
.putMapping({
index,
timeout: DEFAULT_TIMEOUT,
body: mappings,
@ -774,22 +776,16 @@ export const searchForOutdatedDocuments = (
query: Record<string, unknown>
): TaskEither.TaskEither<RetryableEsClientError, SearchResponse> => () => {
return client
.search<{
// when `filter_path` is specified, ES doesn't return empty arrays, so if
// there are no search results res.body.hits will be undefined.
hits?: {
hits?: SavedObjectsRawDoc[];
};
}>({
.search<SavedObjectsRawDocSource>({
index,
// Optimize search performance by sorting by the "natural" index order
sort: ['_doc'],
// Return the _seq_no and _primary_term so we can use optimistic
// concurrency control for updates
seq_no_primary_term: true,
size: BATCH_SIZE,
body: {
query,
// Optimize search performance by sorting by the "natural" index order
sort: ['_doc'],
},
// Return an error when targeting missing or closed indices
allow_no_indices: false,
@ -811,7 +807,9 @@ export const searchForOutdatedDocuments = (
'hits.hits._primary_term',
],
})
.then((res) => Either.right({ outdatedDocuments: res.body.hits?.hits ?? [] }))
.then((res) =>
Either.right({ outdatedDocuments: (res.body.hits?.hits as SavedObjectsRawDoc[]) ?? [] })
)
.catch(catchRetryableEsClientErrors);
};
@ -825,20 +823,7 @@ export const bulkOverwriteTransformedDocuments = (
transformedDocs: SavedObjectsRawDoc[]
): TaskEither.TaskEither<RetryableEsClientError, 'bulk_index_succeeded'> => () => {
return client
.bulk<{
took: number;
errors: boolean;
items: [
{
index: {
_id: string;
status: number;
// the filter_path ensures that only items with errors are returned
error: { type: string; reason: string };
};
}
];
}>({
.bulk({
// Because we only add aliases in the MARK_VERSION_INDEX_READY step we
// can't bulkIndex to an alias with require_alias=true. This means if
// users tamper during this operation (delete indices or restore a
@ -880,7 +865,7 @@ export const bulkOverwriteTransformedDocuments = (
// Filter out version_conflict_engine_exception since these just mean
// that another instance already updated these documents
const errors = (res.body.items ?? []).filter(
(item) => item.index.error.type !== 'version_conflict_engine_exception'
(item) => item.index?.error?.type !== 'version_conflict_engine_exception'
);
if (errors.length === 0) {
return Either.right('bulk_index_succeeded' as const);

View file

@ -258,7 +258,7 @@ describe('migration actions', () => {
index: 'clone_red_then_yellow_index',
body: {
// Enable all shard allocation so that the index status goes yellow
'index.routing.allocation.enable': 'all',
index: { routing: { allocation: { enable: 'all' } } },
},
});
indexYellow = true;
@ -500,7 +500,7 @@ describe('migration actions', () => {
// Create an index with incompatible mappings
await createIndex(client, 'reindex_target_6', {
dynamic: 'false',
dynamic: false,
properties: { title: { type: 'integer' } }, // integer is incompatible with string title
})();
@ -926,7 +926,7 @@ describe('migration actions', () => {
index: 'red_then_yellow_index',
body: {
// Disable all shard allocation so that the index status is red
'index.routing.allocation.enable': 'all',
index: { routing: { allocation: { enable: 'all' } } },
},
});
indexYellow = true;

View file

@ -162,7 +162,9 @@ describe('migration v2', () => {
const expectedVersions = getExpectedVersionPerType();
const res = await esClient.search({
index: migratedIndex,
sort: ['_doc'],
body: {
sort: ['_doc'],
},
size: 10000,
});
const allDocuments = res.body.hits.hits as SavedObjectsRawDoc[];
@ -217,7 +219,9 @@ describe('migration v2', () => {
const expectedVersions = getExpectedVersionPerType();
const res = await esClient.search({
index: migratedIndex,
sort: ['_doc'],
body: {
sort: ['_doc'],
},
size: 10000,
});
const allDocuments = res.body.hits.hits as SavedObjectsRawDoc[];

View file

@ -727,7 +727,6 @@ export const createInitialState = ({
};
const reindexTargetMappings: IndexMapping = {
// @ts-expect-error we don't allow plugins to set `dynamic`
dynamic: false,
properties: {
type: { type: 'keyword' },

View file

@ -12,7 +12,10 @@ function toArray(value: string | string[]): string[] {
/**
* Provides an array of paths for ES source filtering
*/
export function includedFields(type: string | string[] = '*', fields?: string[] | string) {
export function includedFields(
type: string | string[] = '*',
fields?: string[] | string
): string[] | undefined {
if (!fields || fields.length === 0) {
return;
}

View file

@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import type { Logger } from '../../../logging';
import type { SavedObjectsFindOptions, SavedObjectsClientContract } from '../../types';
import type { SavedObjectsFindResponse } from '../';
@ -96,12 +96,12 @@ export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
await this.open();
let lastResultsCount: number;
let lastHitSortValue: unknown[] | undefined;
let lastHitSortValue: estypes.Id[] | undefined;
do {
const results = await this.findNext({
findOptions: this.#findOptions,
id: this.#pitId,
...(lastHitSortValue ? { searchAfter: lastHitSortValue } : {}),
searchAfter: lastHitSortValue,
});
this.#pitId = results.pit_id;
lastResultsCount = results.saved_objects.length;
@ -159,7 +159,7 @@ export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
}: {
findOptions: SavedObjectsFindOptions;
id?: string;
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
}) {
try {
return await this.#client.find({
@ -168,8 +168,8 @@ export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
sortOrder: 'desc',
// Bump keep_alive by 2m on every new request to allow for the ES client
// to make multiple retries in the event of a network failure.
...(id ? { pit: { id, keepAlive: '2m' } } : {}),
...(searchAfter ? { searchAfter } : {}),
pit: id ? { id, keepAlive: '2m' } : undefined,
searchAfter,
...findOptions,
});
} catch (e) {
@ -181,7 +181,7 @@ export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
}
}
private getLastHitSortValue(res: SavedObjectsFindResponse): unknown[] | undefined {
private getLastHitSortValue(res: SavedObjectsFindResponse): estypes.Id[] | undefined {
if (res.saved_objects.length < 1) {
return undefined;
}

View file

@ -2782,18 +2782,20 @@ describe('SavedObjectsRepository', () => {
await findSuccess({ type, fields: ['title'] });
expect(client.search).toHaveBeenCalledWith(
expect.objectContaining({
_source: [
`${type}.title`,
'namespace',
'namespaces',
'type',
'references',
'migrationVersion',
'coreMigrationVersion',
'updated_at',
'originId',
'title',
],
body: expect.objectContaining({
_source: [
`${type}.title`,
'namespace',
'namespaces',
'type',
'references',
'migrationVersion',
'coreMigrationVersion',
'updated_at',
'originId',
'title',
],
}),
}),
expect.anything()
);
@ -3837,6 +3839,7 @@ describe('SavedObjectsRepository', () => {
id: '6.0.0-alpha1',
...mockTimestampFields,
version: mockVersion,
references: [],
attributes: {
buildNum: 8468,
apiCallsCount: 100,

View file

@ -7,13 +7,9 @@
*/
import { omit, isObject } from 'lodash';
import {
ElasticsearchClient,
DeleteDocumentResponse,
GetResponse,
SearchResponse,
} from '../../../elasticsearch/';
import { Logger } from '../../../logging';
import type { estypes } from '@elastic/elasticsearch';
import type { ElasticsearchClient } from '../../../elasticsearch/';
import type { Logger } from '../../../logging';
import { getRootPropertiesObjects, IndexMapping } from '../../mappings';
import {
ISavedObjectsPointInTimeFinder,
@ -397,7 +393,7 @@ export class SavedObjectsRepository {
_source: ['type', 'namespaces'],
}));
const bulkGetResponse = bulkGetDocs.length
? await this.client.mget(
? await this.client.mget<SavedObjectsRawDocSource>(
{
body: {
docs: bulkGetDocs,
@ -425,8 +421,9 @@ export class SavedObjectsRepository {
if (esRequestIndex !== undefined) {
const indexFound = bulkGetResponse?.statusCode !== 404;
const actualResult = indexFound ? bulkGetResponse?.body.docs[esRequestIndex] : undefined;
const docFound = indexFound && actualResult.found === true;
if (docFound && !this.rawDocExistsInNamespace(actualResult, namespace)) {
const docFound = indexFound && actualResult?.found === true;
// @ts-expect-error MultiGetHit._source is optional
if (docFound && !this.rawDocExistsInNamespace(actualResult!, namespace)) {
const { id, type } = object;
return {
tag: 'Left' as 'Left',
@ -441,7 +438,10 @@ export class SavedObjectsRepository {
};
}
savedObjectNamespaces =
initialNamespaces || getSavedObjectNamespaces(namespace, docFound && actualResult);
initialNamespaces ||
// @ts-expect-error MultiGetHit._source is optional
getSavedObjectNamespaces(namespace, docFound ? actualResult : undefined);
// @ts-expect-error MultiGetHit._source is optional
versionProperties = getExpectedVersionProperties(version, actualResult);
} else {
if (this._registry.isSingleNamespace(object.type)) {
@ -500,7 +500,7 @@ export class SavedObjectsRepository {
const { requestedId, rawMigratedDoc, esRequestIndex } = expectedResult.value;
const { error, ...rawResponse } = Object.values(
bulkResponse?.body.items[esRequestIndex]
bulkResponse?.body.items[esRequestIndex] ?? {}
)[0] as any;
if (error) {
@ -564,10 +564,10 @@ export class SavedObjectsRepository {
const bulkGetDocs = expectedBulkGetResults.filter(isRight).map(({ value: { type, id } }) => ({
_id: this._serializer.generateRawId(namespace, type, id),
_index: this.getIndexForType(type),
_source: ['type', 'namespaces'],
_source: { includes: ['type', 'namespaces'] },
}));
const bulkGetResponse = bulkGetDocs.length
? await this.client.mget(
? await this.client.mget<SavedObjectsRawDocSource>(
{
body: {
docs: bulkGetDocs,
@ -586,13 +586,14 @@ export class SavedObjectsRepository {
const { type, id, esRequestIndex } = expectedResult.value;
const doc = bulkGetResponse?.body.docs[esRequestIndex];
if (doc.found) {
if (doc?.found) {
errors.push({
id,
type,
error: {
...errorContent(SavedObjectsErrorHelpers.createConflictError(type, id)),
...(!this.rawDocExistsInNamespace(doc, namespace) && {
// @ts-expect-error MultiGetHit._source is optional
...(!this.rawDocExistsInNamespace(doc!, namespace) && {
metadata: { isNotOverwritable: true },
}),
},
@ -636,7 +637,7 @@ export class SavedObjectsRepository {
}
}
const { body, statusCode } = await this.client.delete<DeleteDocumentResponse>(
const { body, statusCode } = await this.client.delete(
{
id: rawId,
index: this.getIndexForType(type),
@ -652,6 +653,7 @@ export class SavedObjectsRepository {
}
const deleteDocNotFound = body.result === 'not_found';
// @ts-expect-error 'error' does not exist on type 'DeleteResponse'
const deleteIndexNotFound = body.error && body.error.type === 'index_not_found_exception';
if (deleteDocNotFound || deleteIndexNotFound) {
// see "404s from missing index" above
@ -813,15 +815,18 @@ export class SavedObjectsRepository {
const esOptions = {
// If `pit` is provided, we drop the `index`, otherwise ES returns 400.
...(pit ? {} : { index: this.getIndicesForTypes(allowedTypes) }),
index: pit ? undefined : this.getIndicesForTypes(allowedTypes),
// If `searchAfter` is provided, we drop `from` as it will not be used for pagination.
...(searchAfter ? {} : { from: perPage * (page - 1) }),
from: searchAfter ? undefined : perPage * (page - 1),
_source: includedFields(type, fields),
preference,
rest_total_hits_as_int: true,
size: perPage,
body: {
size: perPage,
seq_no_primary_term: true,
from: perPage * (page - 1),
_source: includedFields(type, fields),
...getSearchDsl(this._mappings, this._registry, {
search,
defaultSearchOperator,
@ -841,7 +846,7 @@ export class SavedObjectsRepository {
},
};
const { body, statusCode } = await this.client.search<SearchResponse<any>>(esOptions, {
const { body, statusCode } = await this.client.search<SavedObjectsRawDocSource>(esOptions, {
ignore: [404],
});
if (statusCode === 404) {
@ -860,13 +865,15 @@ export class SavedObjectsRepository {
per_page: perPage,
total: body.hits.total,
saved_objects: body.hits.hits.map(
(hit: SavedObjectsRawDoc): SavedObjectsFindResult => ({
(hit: estypes.Hit<SavedObjectsRawDocSource>): SavedObjectsFindResult => ({
// @ts-expect-error @elastic/elasticsearch declared Id as string | number
...this._rawToSavedObject(hit),
score: (hit as any)._score,
...((hit as any).sort && { sort: (hit as any).sort }),
score: hit._score!,
// @ts-expect-error @elastic/elasticsearch declared sort as string | number
sort: hit.sort,
})
),
...(body.pit_id && { pit_id: body.pit_id }),
pit_id: body.pit_id,
} as SavedObjectsFindResponse<T>;
}
@ -925,10 +932,10 @@ export class SavedObjectsRepository {
.map(({ value: { type, id, fields } }) => ({
_id: this._serializer.generateRawId(namespace, type, id),
_index: this.getIndexForType(type),
_source: includedFields(type, fields),
_source: { includes: includedFields(type, fields) },
}));
const bulkGetResponse = bulkGetDocs.length
? await this.client.mget(
? await this.client.mget<SavedObjectsRawDocSource>(
{
body: {
docs: bulkGetDocs,
@ -947,7 +954,8 @@ export class SavedObjectsRepository {
const { type, id, esRequestIndex } = expectedResult.value;
const doc = bulkGetResponse?.body.docs[esRequestIndex];
if (!doc.found || !this.rawDocExistsInNamespace(doc, namespace)) {
// @ts-expect-error MultiGetHit._source is optional
if (!doc?.found || !this.rawDocExistsInNamespace(doc, namespace)) {
return ({
id,
type,
@ -955,6 +963,7 @@ export class SavedObjectsRepository {
} as any) as SavedObject<T>;
}
// @ts-expect-error MultiGetHit._source is optional
return this.getSavedObjectFromSource(type, id, doc);
}),
};
@ -980,7 +989,7 @@ export class SavedObjectsRepository {
const namespace = normalizeNamespace(options.namespace);
const { body, statusCode } = await this.client.get<GetResponse<SavedObjectsRawDocSource>>(
const { body, statusCode } = await this.client.get<SavedObjectsRawDocSource>(
{
id: this._serializer.generateRawId(namespace, type, id),
index: this.getIndexForType(type),
@ -988,9 +997,13 @@ export class SavedObjectsRepository {
{ ignore: [404] }
);
const docNotFound = body.found === false;
const indexNotFound = statusCode === 404;
if (docNotFound || indexNotFound || !this.rawDocExistsInNamespace(body, namespace)) {
if (
!isFoundGetResponse(body) ||
indexNotFound ||
!this.rawDocExistsInNamespace(body, namespace)
) {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
@ -1026,7 +1039,7 @@ export class SavedObjectsRepository {
const time = this._getCurrentTime();
// retrieve the alias, and if it is not disabled, update it
const aliasResponse = await this.client.update(
const aliasResponse = await this.client.update<{ 'legacy-url-alias': LegacyUrlAlias }>(
{
id: rawAliasId,
index: this.getIndexForType(LEGACY_URL_ALIAS_TYPE),
@ -1059,15 +1072,16 @@ export class SavedObjectsRepository {
if (
aliasResponse.statusCode === 404 ||
aliasResponse.body.get.found === false ||
aliasResponse.body.get._source[LEGACY_URL_ALIAS_TYPE]?.disabled === true
aliasResponse.body.get?.found === false ||
aliasResponse.body.get?._source[LEGACY_URL_ALIAS_TYPE]?.disabled === true
) {
// no legacy URL alias exists, or one exists but it's disabled; just attempt to get the object
return this.resolveExactMatch(type, id, options);
}
const legacyUrlAlias: LegacyUrlAlias = aliasResponse.body.get._source[LEGACY_URL_ALIAS_TYPE];
const legacyUrlAlias: LegacyUrlAlias = aliasResponse.body.get!._source[LEGACY_URL_ALIAS_TYPE];
const objectIndex = this.getIndexForType(type);
const bulkGetResponse = await this.client.mget(
const bulkGetResponse = await this.client.mget<SavedObjectsRawDocSource>(
{
body: {
docs: [
@ -1090,23 +1104,28 @@ export class SavedObjectsRepository {
const exactMatchDoc = bulkGetResponse?.body.docs[0];
const aliasMatchDoc = bulkGetResponse?.body.docs[1];
const foundExactMatch =
// @ts-expect-error MultiGetHit._source is optional
exactMatchDoc.found && this.rawDocExistsInNamespace(exactMatchDoc, namespace);
const foundAliasMatch =
// @ts-expect-error MultiGetHit._source is optional
aliasMatchDoc.found && this.rawDocExistsInNamespace(aliasMatchDoc, namespace);
if (foundExactMatch && foundAliasMatch) {
return {
// @ts-expect-error MultiGetHit._source is optional
saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc),
outcome: 'conflict',
aliasTargetId: legacyUrlAlias.targetId,
};
} else if (foundExactMatch) {
return {
// @ts-expect-error MultiGetHit._source is optional
saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc),
outcome: 'exactMatch',
};
} else if (foundAliasMatch) {
return {
// @ts-expect-error MultiGetHit._source is optional
saved_object: this.getSavedObjectFromSource(type, legacyUrlAlias.targetId, aliasMatchDoc),
outcome: 'aliasMatch',
aliasTargetId: legacyUrlAlias.targetId,
@ -1153,7 +1172,7 @@ export class SavedObjectsRepository {
};
const { body } = await this.client
.update({
.update<SavedObjectsRawDocSource>({
id: this._serializer.generateRawId(namespace, type, id),
index: this.getIndexForType(type),
...getExpectedVersionProperties(version, preflightResult),
@ -1173,11 +1192,11 @@ export class SavedObjectsRepository {
throw err;
});
const { originId } = body.get._source;
let namespaces = [];
const { originId } = body.get?._source ?? {};
let namespaces: string[] = [];
if (!this._registry.isNamespaceAgnostic(type)) {
namespaces = body.get._source.namespaces ?? [
SavedObjectsUtils.namespaceIdToString(body.get._source.namespace),
namespaces = body.get?._source.namespaces ?? [
SavedObjectsUtils.namespaceIdToString(body.get?._source.namespace),
];
}
@ -1185,7 +1204,6 @@ export class SavedObjectsRepository {
id,
type,
updated_at: time,
// @ts-expect-error update doesn't have _seq_no, _primary_term as Record<string, any> / any in LP
version: encodeHitVersion(body),
namespaces,
...(originId && { originId }),
@ -1325,7 +1343,7 @@ export class SavedObjectsRepository {
return { namespaces: doc.namespaces };
} else {
// if there are no namespaces remaining, delete the saved object
const { body, statusCode } = await this.client.delete<DeleteDocumentResponse>(
const { body, statusCode } = await this.client.delete(
{
id: this._serializer.generateRawId(undefined, type, id),
refresh,
@ -1343,6 +1361,7 @@ export class SavedObjectsRepository {
}
const deleteDocNotFound = body.result === 'not_found';
// @ts-expect-error
const deleteIndexNotFound = body.error && body.error.type === 'index_not_found_exception';
if (deleteDocNotFound || deleteIndexNotFound) {
// see "404s from missing index" above
@ -1477,9 +1496,10 @@ export class SavedObjectsRepository {
if (esRequestIndex !== undefined) {
const indexFound = bulkGetResponse?.statusCode !== 404;
const actualResult = indexFound ? bulkGetResponse?.body.docs[esRequestIndex] : undefined;
const docFound = indexFound && actualResult.found === true;
const docFound = indexFound && actualResult?.found === true;
if (
!docFound ||
// @ts-expect-error MultiGetHit is incorrectly missing _id, _source
!this.rawDocExistsInNamespace(actualResult, getNamespaceId(objectNamespace))
) {
return {
@ -1491,10 +1511,13 @@ export class SavedObjectsRepository {
},
};
}
namespaces = actualResult._source.namespaces ?? [
SavedObjectsUtils.namespaceIdToString(actualResult._source.namespace),
// @ts-expect-error MultiGetHit is incorrectly missing _id, _source
namespaces = actualResult!._source.namespaces ?? [
// @ts-expect-error MultiGetHit is incorrectly missing _id, _source
SavedObjectsUtils.namespaceIdToString(actualResult!._source.namespace),
];
versionProperties = getExpectedVersionProperties(version, actualResult);
// @ts-expect-error MultiGetHit is incorrectly missing _id, _source
versionProperties = getExpectedVersionProperties(version, actualResult!);
} else {
if (this._registry.isSingleNamespace(type)) {
// if `objectNamespace` is undefined, fall back to `options.namespace`
@ -1543,7 +1566,7 @@ export class SavedObjectsRepository {
}
const { type, id, namespaces, documentToSave, esRequestIndex } = expectedResult.value;
const response = bulkUpdateResponse?.body.items[esRequestIndex];
const response = bulkUpdateResponse?.body.items[esRequestIndex] ?? {};
// When a bulk update operation is completed, any fields specified in `_sourceIncludes` will be found in the "get" value of the
// returned object. We need to retrieve the `originId` if it exists so we can return it to the consumer.
const { error, _seq_no: seqNo, _primary_term: primaryTerm, get } = Object.values(
@ -1636,7 +1659,7 @@ export class SavedObjectsRepository {
}
return {
updated: body.updated,
updated: body.updated!,
};
}
@ -1745,7 +1768,7 @@ export class SavedObjectsRepository {
const raw = this._serializer.savedObjectToRaw(migrated as SavedObjectSanitizedDoc);
const { body } = await this.client.update({
const { body } = await this.client.update<SavedObjectsRawDocSource>({
id: raw._id,
index: this.getIndexForType(type),
refresh,
@ -1783,17 +1806,16 @@ export class SavedObjectsRepository {
},
});
const { originId } = body.get._source;
const { originId } = body.get?._source ?? {};
return {
id,
type,
...(savedObjectNamespaces && { namespaces: savedObjectNamespaces }),
...(originId && { originId }),
updated_at: time,
references: body.get._source.references,
// @ts-expect-error
references: body.get?._source.references ?? [],
version: encodeHitVersion(body),
attributes: body.get._source[type],
attributes: body.get?._source[type],
};
}
@ -1852,9 +1874,13 @@ export class SavedObjectsRepository {
const {
body,
statusCode,
} = await this.client.openPointInTime<SavedObjectsOpenPointInTimeResponse>(esOptions, {
ignore: [404],
});
} = await this.client.openPointInTime<SavedObjectsOpenPointInTimeResponse>(
// @ts-expect-error @elastic/elasticsearch OpenPointInTimeRequest.index expected to accept string[]
esOptions,
{
ignore: [404],
}
);
if (statusCode === 404) {
throw SavedObjectsErrorHelpers.createGenericNotFoundError();
}
@ -1912,6 +1938,7 @@ export class SavedObjectsRepository {
const { body } = await this.client.closePointInTime<SavedObjectsClosePointInTimeResponse>({
body: { id },
});
return body;
}
@ -2054,7 +2081,7 @@ export class SavedObjectsRepository {
throw new Error(`Cannot make preflight get request for non-multi-namespace type '${type}'.`);
}
const { body, statusCode } = await this.client.get<GetResponse<SavedObjectsRawDocSource>>(
const { body, statusCode } = await this.client.get<SavedObjectsRawDocSource>(
{
id: this._serializer.generateRawId(undefined, type, id),
index: this.getIndexForType(type),
@ -2065,8 +2092,7 @@ export class SavedObjectsRepository {
);
const indexFound = statusCode !== 404;
const docFound = indexFound && body.found === true;
if (docFound) {
if (indexFound && isFoundGetResponse(body)) {
if (!this.rawDocExistsInNamespace(body, namespace)) {
throw SavedObjectsErrorHelpers.createConflictError(type, id);
}
@ -2091,7 +2117,7 @@ export class SavedObjectsRepository {
}
const rawId = this._serializer.generateRawId(undefined, type, id);
const { body, statusCode } = await this.client.get<GetResponse<SavedObjectsRawDocSource>>(
const { body, statusCode } = await this.client.get<SavedObjectsRawDocSource>(
{
id: rawId,
index: this.getIndexForType(type),
@ -2100,17 +2126,20 @@ export class SavedObjectsRepository {
);
const indexFound = statusCode !== 404;
const docFound = indexFound && body.found === true;
if (!docFound || !this.rawDocExistsInNamespace(body, namespace)) {
if (
!indexFound ||
!isFoundGetResponse(body) ||
!this.rawDocExistsInNamespace(body, namespace)
) {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
return body as SavedObjectsRawDoc;
return body;
}
private getSavedObjectFromSource<T>(
type: string,
id: string,
doc: { _seq_no: number; _primary_term: number; _source: SavedObjectsRawDocSource }
doc: { _seq_no?: number; _primary_term?: number; _source: SavedObjectsRawDocSource }
): SavedObject<T> {
const { originId, updated_at: updatedAt } = doc._source;
@ -2220,3 +2249,15 @@ const normalizeNamespace = (namespace?: string) => {
const errorContent = (error: DecoratedError) => error.output.payload;
const unique = (array: string[]) => [...new Set(array)];
/**
* Type and type guard function for converting a possibly not existant doc to an existant doc.
*/
type GetResponseFound<TDocument = unknown> = estypes.GetResponse<TDocument> &
Required<
Pick<estypes.GetResponse<TDocument>, '_primary_term' | '_seq_no' | '_version' | '_source'>
>;
const isFoundGetResponse = <TDocument = unknown>(
doc: estypes.GetResponse<TDocument>
): doc is GetResponseFound<TDocument> => doc.found;

View file

@ -86,7 +86,7 @@ describe('getSearchDsl', () => {
const opts = {
type: 'foo',
sortField: 'bar',
sortOrder: 'baz',
sortOrder: 'asc' as const,
pit: { id: 'abc123' },
};
@ -109,10 +109,10 @@ describe('getSearchDsl', () => {
it('returns searchAfter if provided', () => {
getQueryParams.mockReturnValue({ a: 'a' });
getSortingParams.mockReturnValue({ b: 'b' });
expect(getSearchDsl(mappings, registry, { type: 'foo', searchAfter: [1, 'bar'] })).toEqual({
expect(getSearchDsl(mappings, registry, { type: 'foo', searchAfter: ['1', 'bar'] })).toEqual({
a: 'a',
b: 'b',
search_after: [1, 'bar'],
search_after: ['1', 'bar'],
});
});
@ -123,14 +123,14 @@ describe('getSearchDsl', () => {
expect(
getSearchDsl(mappings, registry, {
type: 'foo',
searchAfter: [1, 'bar'],
searchAfter: ['1', 'bar'],
pit: { id: 'abc123' },
})
).toEqual({
a: 'a',
b: 'b',
pit: { id: 'abc123' },
search_after: [1, 'bar'],
search_after: ['1', 'bar'],
});
});
});

View file

@ -8,6 +8,7 @@
import Boom from '@hapi/boom';
import type { estypes } from '@elastic/elasticsearch';
import { IndexMapping } from '../../../mappings';
import { SavedObjectsPitParams } from '../../../types';
import { getQueryParams, HasReferenceQueryParams, SearchOperator } from './query_params';
@ -23,9 +24,9 @@ interface GetSearchDslOptions {
defaultSearchOperator?: SearchOperator;
searchFields?: string[];
rootSearchFields?: string[];
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
sortField?: string;
sortOrder?: string;
sortOrder?: estypes.SortOrder;
namespaces?: string[];
pit?: SavedObjectsPitParams;
typeToNamespacesMap?: Map<string, string[] | undefined>;
@ -80,6 +81,6 @@ export function getSearchDsl(
}),
...getSortingParams(mappings, type, sortField, sortOrder),
...(pit ? getPitParams(pit) : {}),
...(searchAfter ? { search_after: searchAfter } : {}),
search_after: searchAfter,
};
}

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import Boom from '@hapi/boom';
import { getProperty, IndexMapping } from '../../../mappings';
@ -15,8 +16,8 @@ export function getSortingParams(
mappings: IndexMapping,
type: string | string[],
sortField?: string,
sortOrder?: string
) {
sortOrder?: estypes.SortOrder
): { sort?: estypes.SortContainer[] } {
if (!sortField) {
return {};
}

View file

@ -162,7 +162,7 @@ export interface SavedObjectsFindResult<T = unknown> extends SavedObject<T> {
* await savedObjectsClient.closePointInTime(page2.pit_id);
* ```
*/
sort?: unknown[];
sort?: string[];
}
/**

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import { SavedObjectsClient } from './service/saved_objects_client';
import { SavedObjectsTypeMappingDefinition } from './mappings';
import { SavedObjectMigrationMap } from './migrations';
@ -79,7 +80,7 @@ export interface SavedObjectsFindOptions {
page?: number;
perPage?: number;
sortField?: string;
sortOrder?: string;
sortOrder?: estypes.SortOrder;
/**
* An array of fields to include in the results
* @example
@ -93,7 +94,7 @@ export interface SavedObjectsFindOptions {
/**
* Use the sort values from the previous page to retrieve the next page of results.
*/
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
/**
* The fields to perform the parsed query against. Unlike the `searchFields` argument, these are expected to be root fields and will not
* be modified. If used in conjunction with `searchFields`, both are concatenated together.

View file

@ -12,6 +12,6 @@ import { encodeVersion } from './encode_version';
* Helper for encoding a version from a "hit" (hits.hits[#] from _search) or
* "doc" (body from GET, update, etc) object
*/
export function encodeHitVersion(response: { _seq_no: number; _primary_term: number }) {
export function encodeHitVersion(response: { _seq_no?: number; _primary_term?: number }) {
return encodeVersion(response._seq_no, response._primary_term);
}

View file

@ -13,7 +13,7 @@ import { encodeBase64 } from './base64';
* that can be used in the saved object API in place of numeric
* version numbers
*/
export function encodeVersion(seqNo: number, primaryTerm: number) {
export function encodeVersion(seqNo?: number, primaryTerm?: number) {
if (!Number.isInteger(primaryTerm)) {
throw new TypeError('_primary_term from elasticsearch must be an integer');
}

View file

@ -50,6 +50,7 @@ import { DetailedPeerCertificate } from 'tls';
import { Duration } from 'moment';
import { Duration as Duration_2 } from 'moment-timezone';
import { EnvironmentMode } from '@kbn/config';
import { estypes } from '@elastic/elasticsearch';
import { ExistsParams } from 'elasticsearch';
import { ExplainParams } from 'elasticsearch';
import { FieldStatsParams } from 'elasticsearch';
@ -2507,12 +2508,12 @@ export interface SavedObjectsFindOptions {
preference?: string;
rootSearchFields?: string[];
search?: string;
searchAfter?: unknown[];
searchAfter?: estypes.Id[];
searchFields?: string[];
// (undocumented)
sortField?: string;
// (undocumented)
sortOrder?: string;
sortOrder?: estypes.SortOrder;
// (undocumented)
type: string | string[];
typeToNamespacesMap?: Map<string, string[] | undefined>;
@ -2543,7 +2544,7 @@ export interface SavedObjectsFindResponse<T = unknown> {
// @public (undocumented)
export interface SavedObjectsFindResult<T = unknown> extends SavedObject<T> {
score: number;
sort?: unknown[];
sort?: string[];
}
// @public

View file

@ -5,19 +5,18 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import { SearchResponse } from 'elasticsearch';
import { Search } from '@elastic/elasticsearch/api/requestParams';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types';
export const ES_SEARCH_STRATEGY = 'es';
export type ISearchRequestParams<T = Record<string, any>> = {
export type ISearchRequestParams = {
trackTotalHits?: boolean;
} & Search<T>;
} & estypes.SearchRequest;
export interface IEsSearchRequest extends IKibanaSearchRequest<ISearchRequestParams> {
indexType?: string;
}
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchResponse<Source>>;
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<estypes.SearchResponse<Source>>;

View file

@ -14,7 +14,7 @@
*/
import { i18n } from '@kbn/i18n';
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { ISearchSource } from 'src/plugins/data/public';
import { RequestStatistics } from 'src/plugins/inspector/common';
@ -50,7 +50,7 @@ export function getRequestInspectorStats(searchSource: ISearchSource) {
/** @public */
export function getResponseInspectorStats(
resp: SearchResponse<unknown>,
resp: estypes.SearchResponse<unknown>,
searchSource?: ISearchSource
) {
const lastRequest =

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { KbnError } from '../../../../../kibana_utils/common';
import { SearchError } from './types';
@ -16,8 +16,8 @@ import { SearchError } from './types';
* @param {Object} resp - optional HTTP response
*/
export class RequestFailure extends KbnError {
public resp?: SearchResponse<any>;
constructor(err: SearchError | null = null, resp?: SearchResponse<any>) {
public resp?: estypes.SearchResponse<any>;
constructor(err: SearchError | null = null, resp?: estypes.SearchResponse<any>) {
super(`Request to Elasticsearch failed: ${JSON.stringify(resp || err?.message)}`);
this.resp = resp;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { LegacyFetchHandlers } from '../legacy/types';
import { GetConfigFn } from '../../../types';
@ -25,7 +25,10 @@ export interface FetchHandlers {
* Callback which can be used to hook into responses, modify them, or perform
* side effects like displaying UI errors on the client.
*/
onResponse: (request: SearchRequest, response: SearchResponse<any>) => SearchResponse<any>;
onResponse: (
request: SearchRequest,
response: estypes.SearchResponse<any>
) => estypes.SearchResponse<any>;
/**
* These handlers are only used by the legacy defaultSearchStrategy and can be removed
* once that strategy has been deprecated.

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { FetchHandlers, SearchRequest } from '../fetch';
import { defaultSearchStrategy } from './default_search_strategy';
import { ISearchOptions } from '../../index';
@ -21,7 +21,7 @@ export function callClient(
[SearchRequest, ISearchOptions]
> = searchRequests.map((request, i) => [request, requestsOptions[i]]);
const requestOptionsMap = new Map<SearchRequest, ISearchOptions>(requestOptionEntries);
const requestResponseMap = new Map<SearchRequest, Promise<SearchResponse<any>>>();
const requestResponseMap = new Map<SearchRequest, Promise<estypes.SearchResponse<any>>>();
const { searching, abort } = defaultSearchStrategy.search({
searchRequests,

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { UI_SETTINGS } from '../../../constants';
import { FetchHandlers, SearchRequest } from '../fetch';
import { ISearchOptions } from '../../index';
@ -57,9 +57,11 @@ async function delayedFetch(
options: ISearchOptions,
fetchHandlers: FetchHandlers,
ms: number
): Promise<SearchResponse<any>> {
): Promise<estypes.SearchResponse<any>> {
if (ms === 0) {
return callClient([request], [options], fetchHandlers)[0];
return callClient([request], [options], fetchHandlers)[0] as Promise<
estypes.SearchResponse<any>
>;
}
const i = requestsToFetch.length;

View file

@ -7,8 +7,7 @@
*/
import { BehaviorSubject } from 'rxjs';
import { ApiResponse } from '@elastic/elasticsearch';
import { SearchResponse } from 'elasticsearch';
import type { estypes, ApiResponse } from '@elastic/elasticsearch';
import { FetchHandlers, SearchRequest } from '../fetch';
interface MsearchHeaders {
@ -28,7 +27,7 @@ export interface MsearchRequestBody {
// @internal
export interface MsearchResponse {
body: ApiResponse<{ responses: Array<SearchResponse<any>> }>;
body: ApiResponse<{ responses: Array<estypes.SearchResponse<any>> }>;
}
// @internal
@ -51,6 +50,6 @@ export interface SearchStrategyProvider {
}
export interface SearchStrategyResponse<T = any> {
searching: Promise<Array<SearchResponse<T>>>;
searching: Promise<Array<estypes.SearchResponse<T>>>;
abort: () => void;
}

View file

@ -28,6 +28,7 @@ import { DetailedPeerCertificate } from 'tls';
import { Ensure } from '@kbn/utility-types';
import { EnvironmentMode } from '@kbn/config';
import { ErrorToastOptions } from 'src/core/public/notifications';
import { estypes } from '@elastic/elasticsearch';
import { EuiBreadcrumb } from '@elastic/eui';
import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiComboBoxProps } from '@elastic/eui';
@ -92,8 +93,6 @@ import { SavedObjectsFindOptions } from 'kibana/public';
import { SavedObjectsFindResponse } from 'kibana/server';
import { SavedObjectsUpdateResponse } from 'kibana/server';
import { SchemaTypeError } from '@kbn/config-schema';
import { Search } from '@elastic/elasticsearch/api/requestParams';
import { SearchResponse } from 'elasticsearch';
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
import { StartServicesAccessor } from 'kibana/public';
import { ToastInputFields } from 'src/core/public/notifications';
@ -1128,7 +1127,7 @@ export interface IEsSearchRequest extends IKibanaSearchRequest<ISearchRequestPar
// Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchResponse<Source>>;
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<estypes.SearchResponse<Source>>;
// Warning: (ae-missing-release-tag) "IFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -2415,9 +2414,9 @@ export class SearchSource {
createChild(options?: {}): SearchSource;
createCopy(): SearchSource;
destroy(): void;
fetch$(options?: ISearchOptions): import("rxjs").Observable<import("elasticsearch").SearchResponse<any>>;
fetch$(options?: ISearchOptions): import("rxjs").Observable<import("@elastic/elasticsearch/api/types").SearchResponse<any>>;
// @deprecated
fetch(options?: ISearchOptions): Promise<import("elasticsearch").SearchResponse<any>>;
fetch(options?: ISearchOptions): Promise<import("@elastic/elasticsearch/api/types").SearchResponse<any>>;
getField<K extends keyof SearchSourceFields>(field: K, recurse?: boolean): SearchSourceFields[K];
getFields(): SearchSourceFields;
getId(): string;

View file

@ -6,14 +6,14 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { ExpressionTypeDefinition } from '../../../../expressions/common';
const name = 'es_raw_response';
export interface EsRawResponse<T = unknown> {
type: typeof name;
body: SearchResponse<T>;
body: estypes.SearchResponse<T>;
}
// flattens elasticsearch object into table rows
@ -46,11 +46,11 @@ function flatten(obj: any, keyPrefix = '') {
}
}
const parseRawDocs = (hits: SearchResponse<unknown>['hits']) => {
const parseRawDocs = (hits: estypes.SearchResponse<unknown>['hits']) => {
return hits.hits.map((hit) => hit.fields || hit._source).filter((hit) => hit);
};
const convertResult = (body: SearchResponse<unknown>) => {
const convertResult = (body: estypes.SearchResponse<unknown>) => {
return !body.aggregations ? parseRawDocs(body.hits) : flatten(body.aggregations);
};

View file

@ -9,13 +9,13 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { ShardFailureOpenModalButton } from '../../ui/shard_failure_modal';
import { toMountPoint } from '../../../../kibana_react/public';
import { getNotifications } from '../../services';
import { SearchRequest } from '..';
export function handleResponse(request: SearchRequest, response: SearchResponse<any>) {
export function handleResponse(request: SearchRequest, response: estypes.SearchResponse<any>) {
if (response.timed_out) {
getNotifications().toasts.addWarning({
title: i18n.translate('data.search.searchSource.fetch.requestTimedOutNotificationMessage', {

View file

@ -21,14 +21,14 @@ import {
EuiButtonEmpty,
EuiCallOut,
} from '@elastic/eui';
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { ShardFailureTable } from './shard_failure_table';
import { ShardFailureRequest } from './shard_failure_types';
export interface Props {
onClose: () => void;
request: ShardFailureRequest;
response: SearchResponse<any>;
response: estypes.SearchResponse<any>;
title: string;
}

View file

@ -9,8 +9,8 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton, EuiTextAlign } from '@elastic/eui';
import type { estypes } from '@elastic/elasticsearch';
import { SearchResponse } from 'elasticsearch';
import { getOverlays } from '../../services';
import { toMountPoint } from '../../../../kibana_react/public';
import { ShardFailureModal } from './shard_failure_modal';
@ -19,7 +19,7 @@ import { ShardFailureRequest } from './shard_failure_types';
// @internal
export interface ShardFailureOpenModalButtonProps {
request: ShardFailureRequest;
response: SearchResponse<any>;
response: estypes.SearchResponse<any>;
title: string;
}

View file

@ -12,7 +12,8 @@ import { IRouter, SharedGlobalConfig } from 'kibana/server';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { IFieldType, Filter } from '../index';
import type { estypes } from '@elastic/elasticsearch';
import type { IFieldType } from '../index';
import { findIndexPatternById, getFieldByName } from '../index_patterns';
import { getRequestAbortedSignal } from '../lib';
@ -73,7 +74,7 @@ async function getBody(
{ timeout, terminate_after }: Record<string, any>,
field: IFieldType | string,
query: string,
filters: Filter[] = []
filters: estypes.QueryContainer[] = []
) {
const isFieldObject = (f: any): f is IFieldType => Boolean(f && f.name);
@ -82,7 +83,7 @@ async function getBody(
q.replace(/[.?+*|{}[\]()"\\#@&<>~]/g, (match) => `\\${match}`);
// Helps ensure that the regex is not evaluated eagerly against the terms dictionary
const executionHint = 'map';
const executionHint = 'map' as const;
// We don't care about the accuracy of the counts, just the content of the terms, so this reduces
// the amount of information that needs to be transmitted to the coordinating node

View file

@ -8,17 +8,6 @@
import { ElasticsearchClient } from 'kibana/server';
import { convertEsError } from './errors';
import { FieldCapsResponse } from './field_capabilities';
export interface IndicesAliasResponse {
[index: string]: IndexAliasResponse;
}
export interface IndexAliasResponse {
aliases: {
[aliasName: string]: Record<string, any>;
};
}
/**
* Call the index.getAlias API for a list of indices.
@ -67,7 +56,7 @@ export async function callFieldCapsApi(
fieldCapsOptions: { allow_no_indices: boolean } = { allow_no_indices: false }
) {
try {
return await callCluster.fieldCaps<FieldCapsResponse>({
return await callCluster.fieldCaps({
index: indices,
fields: '*',
ignore_unavailable: true,

View file

@ -7,23 +7,11 @@
*/
import { uniq } from 'lodash';
import type { estypes } from '@elastic/elasticsearch';
import { castEsToKbnFieldTypeName } from '../../../../../common';
import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values';
import { FieldDescriptor } from '../../../fetcher';
interface FieldCapObject {
type: string;
searchable: boolean;
aggregatable: boolean;
indices?: string[];
non_searchable_indices?: string[];
non_aggregatable_indices?: string[];
}
export interface FieldCapsResponse {
fields: Record<string, Record<string, FieldCapObject>>;
}
/**
* Read the response from the _field_caps API to determine the type and
* "aggregatable"/"searchable" status of each field.
@ -80,7 +68,9 @@ export interface FieldCapsResponse {
* @param {FieldCapsResponse} fieldCapsResponse
* @return {Array<FieldDescriptor>}
*/
export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): FieldDescriptor[] {
export function readFieldCapsResponse(
fieldCapsResponse: estypes.FieldCapabilitiesResponse
): FieldDescriptor[] {
const capsByNameThenType = fieldCapsResponse.fields;
const kibanaFormattedCaps = Object.keys(capsByNameThenType).reduce<{

View file

@ -7,5 +7,4 @@
*/
export { getFieldCapabilities } from './field_capabilities';
export { FieldCapsResponse } from './field_caps_response';
export { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values';

View file

@ -12,7 +12,7 @@ import moment from 'moment';
import { ElasticsearchClient } from 'kibana/server';
import { timePatternToWildcard } from './time_pattern_to_wildcard';
import { callIndexAliasApi, IndicesAliasResponse } from './es_api';
import { callIndexAliasApi } from './es_api';
/**
* Convert a time pattern into a list of indexes it could
@ -28,7 +28,7 @@ import { callIndexAliasApi, IndicesAliasResponse } from './es_api';
export async function resolveTimePattern(callCluster: ElasticsearchClient, timePattern: string) {
const aliases = await callIndexAliasApi(callCluster, timePatternToWildcard(timePattern));
const allIndexDetails = chain<IndicesAliasResponse>(aliases.body)
const allIndexDetails = chain(aliases.body)
.reduce(
(acc: string[], index: any, indexName: string) =>
acc.concat(indexName, Object.keys(index.aliases || {})),

View file

@ -9,18 +9,16 @@
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { SharedGlobalConfig } from 'kibana/server';
import { SearchResponse } from 'elasticsearch';
import { CollectorFetchContext } from 'src/plugins/usage_collection/server';
import { CollectedUsage, ReportedUsage } from './register';
interface SearchTelemetry {
'search-telemetry': CollectedUsage;
}
type ESResponse = SearchResponse<SearchTelemetry>;
export function fetchProvider(config$: Observable<SharedGlobalConfig>) {
return async ({ esClient }: CollectorFetchContext): Promise<ReportedUsage> => {
const config = await config$.pipe(first()).toPromise();
const { body: esResponse } = await esClient.search<ESResponse>(
const { body: esResponse } = await esClient.search<SearchTelemetry>(
{
index: config.kibana.index,
body: {
@ -37,7 +35,7 @@ export function fetchProvider(config$: Observable<SharedGlobalConfig>) {
averageDuration: null,
};
}
const { successCount, errorCount, totalDuration } = esResponse.hits.hits[0]._source[
const { successCount, errorCount, totalDuration } = esResponse.hits.hits[0]._source![
'search-telemetry'
];
const averageDuration = totalDuration / successCount;

View file

@ -8,7 +8,6 @@
import { from, Observable } from 'rxjs';
import { first, tap } from 'rxjs/operators';
import type { SearchResponse } from 'elasticsearch';
import type { Logger, SharedGlobalConfig } from 'kibana/server';
import type { ISearchStrategy } from '../types';
import type { SearchUsage } from '../collectors';
@ -44,7 +43,7 @@ export const esSearchStrategyProvider = (
...getShardTimeout(config),
...request.params,
};
const promise = esClient.asCurrentUser.search<SearchResponse<unknown>>(params);
const promise = esClient.asCurrentUser.search(params);
const { body } = await shimAbortSignal(promise, abortSignal);
const response = shimHitsTotal(body, options);
return toKibanaSearchResponse(response);

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { ISearchOptions } from '../../../common';
/**
@ -14,7 +14,7 @@ import { ISearchOptions } from '../../../common';
* not included as it is already included in `successful`.
* @internal
*/
export function getTotalLoaded(response: SearchResponse<unknown>) {
export function getTotalLoaded(response: estypes.SearchResponse<unknown>) {
const { total, failed, successful } = response._shards;
const loaded = failed + successful;
return { total, loaded };
@ -24,7 +24,7 @@ export function getTotalLoaded(response: SearchResponse<unknown>) {
* Get the Kibana representation of this response (see `IKibanaSearchResponse`).
* @internal
*/
export function toKibanaSearchResponse(rawResponse: SearchResponse<unknown>) {
export function toKibanaSearchResponse(rawResponse: estypes.SearchResponse<unknown>) {
return {
rawResponse,
isPartial: false,
@ -41,7 +41,7 @@ export function toKibanaSearchResponse(rawResponse: SearchResponse<unknown>) {
* @internal
*/
export function shimHitsTotal(
response: SearchResponse<unknown>,
response: estypes.SearchResponse<unknown>,
{ legacyHitsTotal = true }: ISearchOptions = {}
) {
if (!legacyHitsTotal) return response;

View file

@ -8,7 +8,6 @@
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { SearchResponse } from 'elasticsearch';
import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server';
import type { MsearchRequestBody, MsearchResponse } from '../../../common/search/search_source';
@ -66,6 +65,7 @@ export function getCallMsearch(dependencies: CallMsearchDependencies) {
try {
const promise = esClient.asCurrentUser.msearch(
{
// @ts-expect-error @elastic/elasticsearch client types don't support plain string bodies
body: convertRequestBody(params.body, timeout),
},
{
@ -78,9 +78,7 @@ export function getCallMsearch(dependencies: CallMsearchDependencies) {
body: {
...response,
body: {
responses: response.body.responses?.map((r: SearchResponse<unknown>) =>
shimHitsTotal(r)
),
responses: response.body.responses?.map((r) => shimHitsTotal(r)),
},
},
};

View file

@ -25,6 +25,7 @@ import { ElasticsearchClient as ElasticsearchClient_2 } from 'kibana/server';
import { Ensure } from '@kbn/utility-types';
import { EnvironmentMode } from '@kbn/config';
import { ErrorToastOptions } from 'src/core/public/notifications';
import { estypes } from '@elastic/elasticsearch';
import { ExecutionContext } from 'src/plugins/expressions/common';
import { ExpressionAstExpression } from 'src/plugins/expressions/common';
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
@ -64,7 +65,6 @@ import { SavedObjectsFindOptions } from 'kibana/server';
import { SavedObjectsFindResponse } from 'kibana/server';
import { SavedObjectsUpdateResponse } from 'kibana/server';
import { Search } from '@elastic/elasticsearch/api/requestParams';
import { SearchResponse } from 'elasticsearch';
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
import { SharedGlobalConfig as SharedGlobalConfig_2 } from 'kibana/server';
import { ToastInputFields } from 'src/core/public/notifications';
@ -596,7 +596,7 @@ export function getTime(indexPattern: IIndexPattern | undefined, timeRange: Time
}): import("../..").RangeFilter | undefined;
// @internal
export function getTotalLoaded(response: SearchResponse<unknown>): {
export function getTotalLoaded(response: estypes.SearchResponse<unknown>): {
total: number;
loaded: number;
};
@ -631,7 +631,7 @@ export interface IEsSearchRequest extends IKibanaSearchRequest<ISearchRequestPar
// Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchResponse<Source>>;
export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<estypes.SearchResponse<Source>>;
// Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -1384,30 +1384,26 @@ export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage, { isR
export const shimAbortSignal: <T>(promise: TransportRequestPromise<T>, signal?: AbortSignal | undefined) => TransportRequestPromise<T>;
// @internal
export function shimHitsTotal(response: SearchResponse<unknown>, { legacyHitsTotal }?: ISearchOptions): {
export function shimHitsTotal(response: estypes.SearchResponse<unknown>, { legacyHitsTotal }?: ISearchOptions): {
hits: {
total: any;
max_score: number;
hits: {
_index: string;
_type: string;
_id: string;
_score: number;
_source: unknown;
_version?: number | undefined;
_explanation?: import("elasticsearch").Explanation | undefined;
fields?: any;
highlight?: any;
inner_hits?: any;
matched_queries?: string[] | undefined;
sort?: string[] | undefined;
}[];
hits: estypes.Hit<unknown>[];
max_score?: number | undefined;
};
took: number;
timed_out: boolean;
_shards: estypes.ShardStatistics;
aggregations?: Record<string, estypes.Aggregate> | undefined;
_clusters?: estypes.ClusterStatistics | undefined;
documents?: unknown[] | undefined;
fields?: Record<string, any> | undefined;
max_score?: number | undefined;
num_reduce_phases?: number | undefined;
profile?: estypes.Profile | undefined;
pit_id?: string | undefined;
_scroll_id?: string | undefined;
_shards: import("elasticsearch").ShardsResponse;
aggregations?: any;
suggest?: Record<string, estypes.Suggest<unknown>[]> | undefined;
terminated_early?: boolean | undefined;
};
// Warning: (ae-missing-release-tag) "shouldReadFieldFromDocValues" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@ -1425,10 +1421,10 @@ export type TimeRange = {
};
// @internal
export function toKibanaSearchResponse(rawResponse: SearchResponse<unknown>): {
export function toKibanaSearchResponse(rawResponse: estypes.SearchResponse<unknown>): {
total: number;
loaded: number;
rawResponse: SearchResponse<unknown>;
rawResponse: estypes.SearchResponse<unknown>;
isPartial: boolean;
isRunning: boolean;
};

View file

@ -9,6 +9,7 @@
import angular, { auto, ICompileService, IScope } from 'angular';
import { render } from 'react-dom';
import React, { useRef, useEffect, useState, useCallback } from 'react';
import type { estypes } from '@elastic/elasticsearch';
import { EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { getServices, IIndexPattern } from '../../../kibana_services';
@ -20,7 +21,7 @@ export interface DocTableLegacyProps {
searchDescription?: string;
searchTitle?: string;
onFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void;
rows: Array<Record<string, unknown>>;
rows: estypes.Hit[];
indexPattern: IIndexPattern;
minimumVisibleRows: number;
onAddColumn?: (column: string) => void;

View file

@ -87,6 +87,7 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
minimumVisibleRows,
useNewFieldsApi,
} = renderProps;
// @ts-expect-error doesn't implement full DocTableLegacyProps interface
return {
columns,
indexPattern,

View file

@ -140,7 +140,7 @@ export function DiscoverGridFlyout({
iconType="documents"
flush="left"
href={getContextUrl(
hit._id,
String(hit._id),
indexPattern.id,
columns,
services.filterManager,

View file

@ -10,6 +10,7 @@ import React from 'react';
import { ReactWrapper, shallow } from 'enzyme';
import { getRenderCellValueFn } from './get_render_cell_value';
import { indexPatternMock } from '../../../__mocks__/index_pattern';
import { ElasticSearchHit } from '../../doc_views/doc_views_types';
jest.mock('../../../../../kibana_react/public', () => ({
useUiSetting: () => true,
@ -26,7 +27,7 @@ jest.mock('../../../kibana_services', () => ({
}),
}));
const rowsSource = [
const rowsSource: ElasticSearchHit[] = [
{
_id: '1',
_index: 'test',
@ -34,12 +35,12 @@ const rowsSource = [
_score: 1,
_source: { bytes: 100, extension: '.gz' },
highlight: {
extension: '@kibana-highlighted-field.gz@/kibana-highlighted-field',
extension: ['@kibana-highlighted-field.gz@/kibana-highlighted-field'],
},
},
];
const rowsFields = [
const rowsFields: ElasticSearchHit[] = [
{
_id: '1',
_index: 'test',
@ -48,12 +49,12 @@ const rowsFields = [
_source: undefined,
fields: { bytes: [100], extension: ['.gz'] },
highlight: {
extension: '@kibana-highlighted-field.gz@/kibana-highlighted-field',
extension: ['@kibana-highlighted-field.gz@/kibana-highlighted-field'],
},
},
];
const rowsFieldsWithTopLevelObject = [
const rowsFieldsWithTopLevelObject: ElasticSearchHit[] = [
{
_id: '1',
_index: 'test',
@ -62,7 +63,7 @@ const rowsFieldsWithTopLevelObject = [
_source: undefined,
fields: { 'object.value': [100], extension: ['.gz'] },
highlight: {
extension: '@kibana-highlighted-field.gz@/kibana-highlighted-field',
extension: ['@kibana-highlighted-field.gz@/kibana-highlighted-field'],
},
},
];
@ -167,7 +168,9 @@ describe('Discover grid cell rendering', function () {
},
"_type": "test",
"highlight": Object {
"extension": "@kibana-highlighted-field.gz@/kibana-highlighted-field",
"extension": Array [
"@kibana-highlighted-field.gz@/kibana-highlighted-field",
],
},
}
}
@ -264,7 +267,9 @@ describe('Discover grid cell rendering', function () {
],
},
"highlight": Object {
"extension": "@kibana-highlighted-field.gz@/kibana-highlighted-field",
"extension": Array [
"@kibana-highlighted-field.gz@/kibana-highlighted-field",
],
},
}
}

View file

@ -87,7 +87,7 @@ describe('DocViewTable at Discover', () => {
scripted: 123,
_underscore: 123,
},
};
} as any;
const props = {
hit,
@ -185,7 +185,7 @@ describe('DocViewTable at Discover Context', () => {
Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \
Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut',
},
};
} as any;
const props = {
hit,
columns: ['extension'],

View file

@ -8,7 +8,7 @@
import { ComponentType } from 'react';
import { IScope } from 'angular';
import { SearchResponse } from 'elasticsearch';
import type { estypes } from '@elastic/elasticsearch';
import { IndexPattern } from '../../../../data/public';
export interface AngularDirective {
@ -18,7 +18,7 @@ export interface AngularDirective {
export type AngularScope = IScope;
export type ElasticSearchHit<T = unknown> = SearchResponse<T>['hits']['hits'][number];
export type ElasticSearchHit<T = unknown> = estypes.SearchResponse<T>['hits']['hits'][number];
export interface FieldMapping {
filterable?: boolean;

View file

@ -357,7 +357,7 @@ export class SearchEmbeddable
// Apply the changes to the angular scope
this.searchScope.$apply(() => {
this.searchScope!.hits = resp.hits.hits;
this.searchScope!.totalHitCount = resp.hits.total;
this.searchScope!.totalHitCount = resp.hits.total as number;
this.searchScope!.isLoading = false;
});
} catch (error) {

View file

@ -15,6 +15,7 @@ import * as CSS from 'csstype';
import { DetailedPeerCertificate } from 'tls';
import { EmbeddableStart as EmbeddableStart_2 } from 'src/plugins/embeddable/public/plugin';
import { EnvironmentMode } from '@kbn/config';
import { estypes } from '@elastic/elasticsearch';
import { EuiBreadcrumb } from '@elastic/eui';
import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';

View file

@ -46,8 +46,8 @@ describe('preview_scripted_field route', () => {
expect(mockClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
"_source": undefined,
"body": Object {
"_source": undefined,
"query": Object {
"match_all": Object {},
},
@ -59,10 +59,10 @@ describe('preview_scripted_field route', () => {
},
},
},
"size": 10,
"timeout": "30s",
},
"index": "kibana_sample_data_logs",
"size": 10,
"timeout": "30s",
}
`);
@ -102,12 +102,12 @@ describe('preview_scripted_field route', () => {
expect(mockClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
"_source": Array [
"a",
"b",
"c",
],
"body": Object {
"_source": Array [
"a",
"b",
"c",
],
"query": Object {
"bool": Object {
"some": "query",
@ -121,10 +121,10 @@ describe('preview_scripted_field route', () => {
},
},
},
"size": 10,
"timeout": "30s",
},
"index": "kibana_sample_data_logs",
"size": 10,
"timeout": "30s",
}
`);
});

View file

@ -30,10 +30,10 @@ export function registerPreviewScriptedFieldRoute(router: IRouter): void {
try {
const response = await client.search({
index,
_source: additionalFields && additionalFields.length > 0 ? additionalFields : undefined,
size: 10,
timeout: '30s',
body: {
_source: additionalFields && additionalFields.length > 0 ? additionalFields : undefined,
size: 10,
timeout: '30s',
query: query ?? { match_all: {} },
script_fields: {
[name]: {

View file

@ -58,6 +58,7 @@ export async function getSavedObjectsCounts(
};
const { body } = await esClient.search(savedObjectCountSearchParams);
const buckets: Array<{ key: string; doc_count: number }> =
// @ts-expect-error @elastic/elasticsearch Aggregate does not include `buckets`
body.aggregations?.types?.buckets || [];
// Initialise the object with all zeros for all the types

View file

@ -27,6 +27,7 @@ describe('checkClusterForUserData', () => {
it('returns false if data only exists in system indices', async () => {
const esClient = elasticsearchServiceMock.createElasticsearchClient();
esClient.cat.indices.mockResolvedValue(
// @ts-expect-error @elastic/elasticsearch ES types don't support array response format
elasticsearchServiceMock.createApiResponse({
body: [
{
@ -55,6 +56,7 @@ describe('checkClusterForUserData', () => {
it('returns true if data exists in non-system indices', async () => {
const esClient = elasticsearchServiceMock.createElasticsearchClient();
esClient.cat.indices.mockResolvedValue(
// @ts-expect-error @elastic/elasticsearch ES types don't support array response format
elasticsearchServiceMock.createApiResponse({
body: [
{
@ -85,6 +87,7 @@ describe('checkClusterForUserData', () => {
)
.mockRejectedValueOnce(new Error('something terrible happened'))
.mockResolvedValueOnce(
// @ts-expect-error @elastic/elasticsearch ES types don't support array response format
elasticsearchServiceMock.createApiResponse({
body: [
{
@ -95,6 +98,7 @@ describe('checkClusterForUserData', () => {
})
)
.mockResolvedValueOnce(
// @ts-expect-error @elastic/elasticsearch ES types don't support array response format
elasticsearchServiceMock.createApiResponse({
body: [
{

View file

@ -14,17 +14,15 @@ export const createClusterDataCheck = () => {
return async function doesClusterHaveUserData(esClient: ElasticsearchClient, log: Logger) {
if (!clusterHasUserData) {
try {
const indices = await esClient.cat.indices<
Array<{ index: string; ['docs.count']: string }>
>({
const indices = await esClient.cat.indices({
format: 'json',
h: ['index', 'docs.count'],
});
clusterHasUserData = indices.body.some((indexCount) => {
const isInternalIndex =
indexCount.index.startsWith('.') || indexCount.index.startsWith('kibana_sample_');
indexCount.index?.startsWith('.') || indexCount.index?.startsWith('kibana_sample_');
return !isInternalIndex && parseInt(indexCount['docs.count'], 10) > 0;
return !isInternalIndex && parseInt(indexCount['docs.count']!, 10) > 0;
});
} catch (e) {
log.warn(`Error encountered while checking cluster for user data: ${e}`);

View file

@ -8,22 +8,6 @@
import { ElasticsearchClient } from 'src/core/server';
// This can be removed when the ES client improves the types
export interface ESClusterInfo {
cluster_uuid: string;
cluster_name: string;
version: {
number: string;
build_flavor?: string;
build_type?: string;
build_hash?: string;
build_date?: string;
build_snapshot?: boolean;
lucene_version?: string;
minimum_wire_compatibility_version?: string;
minimum_index_compatibility_version?: string;
};
}
/**
* Get the cluster info from the connected cluster.
*
@ -32,6 +16,6 @@ export interface ESClusterInfo {
* @param {function} esClient The asInternalUser handler (exposed for testing)
*/
export async function getClusterInfo(esClient: ElasticsearchClient) {
const { body } = await esClient.info<ESClusterInfo>();
const { body } = await esClient.info();
return body;
}

View file

@ -261,14 +261,16 @@ export async function getDataTelemetry(esClient: ElasticsearchClient) {
const indices = indexNames.map((name) => {
const baseIndexInfo = {
name,
isECS: !!indexMappings[name]?.mappings?.properties.ecs?.properties.version?.type,
isECS: !!indexMappings[name]?.mappings?.properties?.ecs?.properties?.version?.type,
shipper: indexMappings[name]?.mappings?._meta?.beat,
packageName: indexMappings[name]?.mappings?._meta?.package?.name,
managedBy: indexMappings[name]?.mappings?._meta?.managed_by,
dataStreamDataset:
indexMappings[name]?.mappings?.properties.data_stream?.properties.dataset?.value,
// @ts-expect-error @elastic/elasticsearch PropertyBase doesn't decalre value
indexMappings[name]?.mappings?.properties?.data_stream?.properties?.dataset?.value,
dataStreamType:
indexMappings[name]?.mappings?.properties.data_stream?.properties.type?.value,
// @ts-expect-error @elastic/elasticsearch PropertyBase doesn't decalre value
indexMappings[name]?.mappings?.properties?.data_stream?.properties?.type?.value,
};
const stats = (indexStats?.indices || {})[name];

View file

@ -7,6 +7,7 @@
*/
import { merge, omit } from 'lodash';
import type { estypes } from '@elastic/elasticsearch';
import { getLocalStats, handleLocalStats } from './get_local_stats';
import {
@ -34,35 +35,33 @@ function mockGetLocalStats(clusterInfo: any, clusterStats: any) {
esClient.cluster.stats
// @ts-expect-error we only care about the response body
.mockResolvedValue({ body: { ...clusterStats } });
esClient.nodes.usage.mockResolvedValue(
esClient.nodes.usage.mockResolvedValue({
// @ts-expect-error we only care about the response body
{
body: {
cluster_name: 'testCluster',
nodes: {
some_node_id: {
timestamp: 1588617023177,
since: 1588616945163,
rest_actions: {
nodes_usage_action: 1,
create_index_action: 1,
document_get_action: 1,
search_action: 19,
nodes_info_action: 36,
body: {
cluster_name: 'testCluster',
nodes: {
some_node_id: {
timestamp: 1588617023177,
since: 1588616945163,
rest_actions: {
nodes_usage_action: 1,
create_index_action: 1,
document_get_action: 1,
search_action: 19,
nodes_info_action: 36,
},
aggregations: {
scripted_metric: {
other: 7,
},
aggregations: {
terms: {
bytes: 2,
},
scripted_metric: {
other: 7,
},
terms: {
bytes: 2,
},
},
},
},
}
);
},
});
// @ts-expect-error we only care about the response body
esClient.indices.getMapping.mockResolvedValue({ body: { mappings: {} } });
// @ts-expect-error we only care about the response body
@ -188,7 +187,7 @@ describe('get_local_stats', () => {
describe('handleLocalStats', () => {
it('returns expected object without xpack or kibana data', () => {
const result = handleLocalStats(
clusterInfo,
clusterInfo as estypes.RootNodeInfoResponse,
clusterStatsWithNodesUsage,
void 0,
void 0,
@ -205,7 +204,7 @@ describe('get_local_stats', () => {
it('returns expected object with xpack', () => {
const result = handleLocalStats(
clusterInfo,
clusterInfo as estypes.RootNodeInfoResponse,
clusterStatsWithNodesUsage,
void 0,
void 0,

View file

@ -6,11 +6,12 @@
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import {
StatsGetter,
StatsCollectionContext,
} from 'src/plugins/telemetry_collection_manager/server';
import { getClusterInfo, ESClusterInfo } from './get_cluster_info';
import { getClusterInfo } from './get_cluster_info';
import { getClusterStats } from './get_cluster_stats';
import { getKibana, handleKibanaStats, KibanaUsageStats } from './get_kibana';
import { getNodesUsage } from './get_nodes_usage';
@ -27,7 +28,7 @@ import { getDataTelemetry, DATA_TELEMETRY_ID, DataTelemetryPayload } from './get
*/
export function handleLocalStats(
// eslint-disable-next-line @typescript-eslint/naming-convention
{ cluster_name, cluster_uuid, version }: ESClusterInfo,
{ cluster_name, cluster_uuid, version }: estypes.RootNodeInfoResponse,
{ _nodes, cluster_name: clusterName, ...clusterStats }: any,
kibana: KibanaUsageStats | undefined,
dataTelemetry: DataTelemetryPayload | undefined,

View file

@ -16,7 +16,7 @@ export interface NodeAggregation {
// we set aggregations as an optional type because it was only added in v7.8.0
export interface NodeObj {
node_id?: string;
timestamp: number;
timestamp: number | string;
since: number;
rest_actions: {
[key: string]: number;
@ -46,9 +46,10 @@ export type NodesUsageGetter = (
export async function fetchNodesUsage(
esClient: ElasticsearchClient
): Promise<NodesFeatureUsageResponse> {
const { body } = await esClient.nodes.usage<NodesFeatureUsageResponse>({
const { body } = await esClient.nodes.usage({
timeout: TIMEOUT,
});
// @ts-expect-error TODO: Does the client parse `timestamp` to a Date object? Expected a number
return body;
}

Some files were not shown because too many files have changed in this diff Show more