mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
Migrate Index Management and enrichers to the new ES JS client (#105863)
* Destructure index API request bodies consistently. * Remove unnecessary calls to encodeURIComponent on the server. * Migrate routes to handleEsError. Delete wrapEsError helpers. Remove unused isEsError and parseEsError dependencies. Remove isEsError from es_ui_shared. * Update tests and migrate API integration tests. * Clarify test details in CCR README. Update Index Management README with steps for testing Cloud-managed index templates and steps for testing indices and data streams that contain special characters.
This commit is contained in:
parent
5e8b24230a
commit
3491f05e95
55 changed files with 682 additions and 1135 deletions
|
@ -457,7 +457,7 @@ Index Management by running this series of requests in Console:
|
|||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/index_management/README.md[indexManagement]
|
||||
|Create a data stream using Console and you'll be able to view it in the UI:
|
||||
|Create an index with special characters and verify it renders correctly:
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra]
|
||||
|
|
|
@ -6,6 +6,5 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { isEsError } from './is_es_error';
|
||||
export { handleEsError } from './handle_es_error';
|
||||
export { parseEsError } from './es_error_parser';
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import * as legacyElasticsearch from 'elasticsearch';
|
||||
|
||||
const esErrorsParent = legacyElasticsearch.errors._Abstract;
|
||||
|
||||
interface RequestError extends Error {
|
||||
statusCode?: number;
|
||||
}
|
||||
|
||||
/*
|
||||
* @deprecated
|
||||
* Only works with legacy elasticsearch js client errors and will be removed after 7.x last
|
||||
*/
|
||||
export function isEsError(err: RequestError) {
|
||||
const isInstanceOfEsError = err instanceof esErrorsParent;
|
||||
const hasStatusCode = Boolean(err.statusCode);
|
||||
|
||||
return isInstanceOfEsError && hasStatusCode;
|
||||
}
|
|
@ -6,4 +6,4 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { isEsError, handleEsError, parseEsError } from '../../__packages_do_not_import__/errors';
|
||||
export { handleEsError, parseEsError } from '../../__packages_do_not_import__/errors';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { isEsError, handleEsError, parseEsError } from './errors';
|
||||
export { handleEsError, parseEsError } from './errors';
|
||||
|
||||
/** dummy plugin*/
|
||||
export function plugin() {
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
|
||||
You can run a local cluster and simulate a remote cluster within a single Kibana directory.
|
||||
|
||||
1. Ensure Kibana isn't running so it doesn't load up any data into your cluster. Run `yarn es snapshot --license=trial` to install a fresh snapshot. Wait for ES to finish setting up.
|
||||
1. Ensure Kibana isn't running so it doesn't load up any data into your cluster. Run `yarn es snapshot --license=trial` to install a fresh snapshot. Wait for ES to finish setting up and activate the license.
|
||||
2. Create a "remote" copy of your ES snapshot by running: `cp -R .es/8.0.0 .es/8.0.0-2`.
|
||||
4. Start your "remote" cluster by running `.es/8.0.0-2/bin/elasticsearch -E cluster.name=europe -E transport.port=9400`.
|
||||
4. Run `yarn start` to start Kibana.
|
||||
5. Index a document into your remote cluster by running `curl -X PUT http://elastic:changeme@localhost:9201/my-leader-index --data '{"settings":{"number_of_shards":1,"soft_deletes.enabled":true}}' --header "Content-Type: application/json"`. Note that these settings are required for testing auto-follow pattern conflicts errors (see below).
|
||||
4. Start your "local" cluster by running `.es/8.0.0/bin/elasticsearch`.
|
||||
5. Run `yarn start` to start Kibana so that it connects to the "local" cluster.
|
||||
6. Start your "remote" cluster by running `.es/8.0.0-2/bin/elasticsearch -E cluster.name=europe -E transport.port=9400`.
|
||||
7. Index a document into your "remote" cluster by running `curl -X PUT http://elastic:changeme@localhost:9201/my-leader-index --data '{"settings":{"number_of_shards":1,"soft_deletes.enabled":true}}' --header "Content-Type: application/json"`. Note that these settings are required for testing auto-follow pattern conflicts errors (see below).
|
||||
|
||||
Now you can create follower indices and auto-follow patterns to replicate the `my-leader-index`
|
||||
index on the remote cluster that's available at `127.0.0.1:9400`.
|
||||
index on the "remote" cluster that's available at `127.0.0.1:9400`.
|
||||
|
||||
### Auto-follow pattern conflict errors
|
||||
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin,
|
||||
Logger,
|
||||
PluginInitializerContext,
|
||||
LegacyAPICaller,
|
||||
} from 'src/core/server';
|
||||
import { CoreSetup, CoreStart, Plugin, Logger, PluginInitializerContext } from 'src/core/server';
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
|
||||
import { Index } from '../../index_management/server';
|
||||
import { PLUGIN } from '../common/constants';
|
||||
|
@ -23,20 +17,18 @@ import { registerApiRoutes } from './routes';
|
|||
import { CrossClusterReplicationConfig } from './config';
|
||||
import { License, handleEsError } from './shared_imports';
|
||||
|
||||
// TODO replace deprecated ES client after Index Management is updated
|
||||
const ccrDataEnricher = async (indicesList: Index[], callWithRequest: LegacyAPICaller) => {
|
||||
const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => {
|
||||
if (!indicesList?.length) {
|
||||
return indicesList;
|
||||
}
|
||||
const params = {
|
||||
path: '/_all/_ccr/info',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
try {
|
||||
const { follower_indices: followerIndices } = await callWithRequest(
|
||||
'transport.request',
|
||||
params
|
||||
);
|
||||
const {
|
||||
body: { follower_indices: followerIndices },
|
||||
} = await client.asCurrentUser.ccr.followInfo({
|
||||
index: '_all',
|
||||
});
|
||||
|
||||
return indicesList.map((index) => {
|
||||
const isFollowerIndex = !!followerIndices.find(
|
||||
(followerIndex: { follower_index: string }) => {
|
||||
|
|
|
@ -6,44 +6,36 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
CoreSetup,
|
||||
Plugin,
|
||||
Logger,
|
||||
PluginInitializerContext,
|
||||
LegacyAPICaller,
|
||||
} from 'src/core/server';
|
||||
import { handleEsError } from './shared_imports';
|
||||
import { CoreSetup, Plugin, Logger, PluginInitializerContext } from 'src/core/server';
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
|
||||
import { Index as IndexWithoutIlm } from '../../index_management/common/types';
|
||||
import { PLUGIN } from '../common/constants';
|
||||
import { Index, IndexLifecyclePolicy } from '../common/types';
|
||||
import { Index } from '../common/types';
|
||||
import { Dependencies } from './types';
|
||||
import { registerApiRoutes } from './routes';
|
||||
import { License } from './services';
|
||||
import { IndexLifecycleManagementConfig } from './config';
|
||||
import { handleEsError } from './shared_imports';
|
||||
|
||||
const indexLifecycleDataEnricher = async (
|
||||
indicesList: IndexWithoutIlm[],
|
||||
// TODO replace deprecated ES client after Index Management is updated
|
||||
callAsCurrentUser: LegacyAPICaller
|
||||
client: IScopedClusterClient
|
||||
): Promise<Index[]> => {
|
||||
if (!indicesList || !indicesList.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const params = {
|
||||
path: '/*/_ilm/explain',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
const { indices: ilmIndicesData } = await callAsCurrentUser<{
|
||||
indices: { [indexName: string]: IndexLifecyclePolicy };
|
||||
}>('transport.request', params);
|
||||
const {
|
||||
body: { indices: ilmIndicesData },
|
||||
} = await client.asCurrentUser.ilm.explainLifecycle({
|
||||
index: '*',
|
||||
});
|
||||
|
||||
return indicesList.map((index: IndexWithoutIlm) => {
|
||||
return {
|
||||
...index,
|
||||
// @ts-expect-error @elastic/elasticsearch Element implicitly has an 'any' type
|
||||
ilm: { ...(ilmIndicesData[index.name] || {}) },
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
# Index Management UI
|
||||
|
||||
## Indices tab
|
||||
|
||||
### Quick steps for testing
|
||||
|
||||
Create an index with special characters and verify it renders correctly:
|
||||
|
||||
```
|
||||
# Renders as %{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23
|
||||
PUT %25%7B%5B%40metadata%5D%5Bbeat%5D%7D-%25%7B%5B%40metadata%5D%5Bversion%5D%7D-2020.08.23
|
||||
```
|
||||
|
||||
## Data streams tab
|
||||
|
||||
### Quick steps for testing
|
||||
|
@ -20,3 +31,55 @@ POST ds/_doc
|
|||
"@timestamp": "2020-01-27"
|
||||
}
|
||||
```
|
||||
|
||||
Create a data stream with special characters and verify it renders correctly:
|
||||
|
||||
```
|
||||
# Configure template for creating a data stream
|
||||
PUT _index_template/special_ds
|
||||
{
|
||||
"index_patterns": ["%{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23"],
|
||||
"data_stream": {}
|
||||
}
|
||||
|
||||
# Add a document to the data stream, which will render as %{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23
|
||||
POST %25%7B%5B%40metadata%5D%5Bbeat%5D%7D-%25%7B%5B%40metadata%5D%5Bversion%5D%7D-2020.08.23/_doc
|
||||
{
|
||||
"@timestamp": "2020-01-27"
|
||||
}
|
||||
```
|
||||
|
||||
## Index templates tab
|
||||
|
||||
### Quick steps for testing
|
||||
|
||||
By default, **legacy index templates** are not shown in the UI. Make them appear by creating one in Console:
|
||||
|
||||
```
|
||||
PUT _template/template_1
|
||||
{
|
||||
"index_patterns": ["foo*"]
|
||||
}
|
||||
```
|
||||
|
||||
To test **Cloud-managed templates**:
|
||||
|
||||
1. Add `cluster.metadata.managed_index_templates` setting via Dev Tools:
|
||||
```
|
||||
PUT /_cluster/settings
|
||||
{
|
||||
"persistent": {
|
||||
"cluster.metadata.managed_index_templates": ".cloud-"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Create a template with the format: `.cloud-<template_name>` via Dev Tools.
|
||||
```
|
||||
PUT _template/.cloud-example
|
||||
{
|
||||
"index_patterns": [ "foobar*"]
|
||||
}
|
||||
```
|
||||
|
||||
The UI will now prevent you from editing or deleting this template.
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import uuid from 'uuid';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
|
||||
|
||||
|
@ -37,11 +36,6 @@ export const SimulateTemplate = React.memo(({ template, filters }: Props) => {
|
|||
}
|
||||
|
||||
const indexTemplate = serializeTemplate(stripEmptyFields(template) as TemplateDeserialized);
|
||||
|
||||
// Until ES fixes a bug on their side we will send a random index pattern to the simulate API.
|
||||
// Issue: https://github.com/elastic/elasticsearch/issues/59152
|
||||
indexTemplate.index_patterns = [uuid.v4()];
|
||||
|
||||
const { data, error } = await simulateIndexTemplate(indexTemplate);
|
||||
let filteredTemplate = data;
|
||||
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const elasticsearchJsPlugin = (Client: any, config: any, components: any) => {
|
||||
const ca = components.clientAction.factory;
|
||||
|
||||
Client.prototype.dataManagement = components.clientAction.namespaceFactory();
|
||||
const dataManagement = Client.prototype.dataManagement.prototype;
|
||||
|
||||
// Data streams
|
||||
|
||||
// We don't allow the user to create a data stream in the UI or API. We're just adding this here
|
||||
// to enable the API integration tests.
|
||||
dataManagement.createDataStream = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_data_stream/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'PUT',
|
||||
});
|
||||
|
||||
dataManagement.deleteDataStream = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_data_stream/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
// Component templates
|
||||
dataManagement.getComponentTemplates = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_component_template',
|
||||
},
|
||||
],
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
dataManagement.getComponentTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_component_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
dataManagement.saveComponentTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_component_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'PUT',
|
||||
});
|
||||
|
||||
dataManagement.deleteComponentTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_component_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
// Composable index templates
|
||||
dataManagement.getComposableIndexTemplates = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template',
|
||||
},
|
||||
],
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
dataManagement.getComposableIndexTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
dataManagement.saveComposableIndexTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
needBody: true,
|
||||
method: 'PUT',
|
||||
});
|
||||
|
||||
dataManagement.deleteComposableIndexTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
dataManagement.existsTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template/<%=name%>',
|
||||
req: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
method: 'HEAD',
|
||||
});
|
||||
|
||||
dataManagement.simulateTemplate = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_index_template/_simulate',
|
||||
},
|
||||
],
|
||||
needBody: true,
|
||||
method: 'POST',
|
||||
});
|
||||
};
|
|
@ -10,7 +10,7 @@ import { PluginInitializerContext } from 'src/core/server';
|
|||
import { IndexMgmtServerPlugin } from './plugin';
|
||||
import { configSchema } from './config';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => new IndexMgmtServerPlugin(ctx);
|
||||
export const plugin = (context: PluginInitializerContext) => new IndexMgmtServerPlugin(context);
|
||||
|
||||
export const config = {
|
||||
schema: configSchema,
|
||||
|
|
|
@ -5,99 +5,57 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CatIndicesParams } from 'elasticsearch';
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
import { IndexDataEnricher } from '../services';
|
||||
import { CallAsCurrentUser } from '../types';
|
||||
import { Index } from '../index';
|
||||
|
||||
interface Hit {
|
||||
health: string;
|
||||
status: string;
|
||||
index: string;
|
||||
uuid: string;
|
||||
pri: string;
|
||||
rep: string;
|
||||
'docs.count': any;
|
||||
'store.size': any;
|
||||
sth: 'true' | 'false';
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
interface IndexInfo {
|
||||
aliases: { [aliasName: string]: unknown };
|
||||
mappings: unknown;
|
||||
data_stream?: string;
|
||||
settings: {
|
||||
index: {
|
||||
hidden: 'true' | 'false';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIndicesResponse {
|
||||
[indexName: string]: IndexInfo;
|
||||
}
|
||||
|
||||
async function fetchIndicesCall(
|
||||
callAsCurrentUser: CallAsCurrentUser,
|
||||
client: IScopedClusterClient,
|
||||
indexNames?: string[]
|
||||
): Promise<Index[]> {
|
||||
const indexNamesString = indexNames && indexNames.length ? indexNames.join(',') : '*';
|
||||
|
||||
// This call retrieves alias and settings (incl. hidden status) information about indices
|
||||
const indices: GetIndicesResponse = await callAsCurrentUser('transport.request', {
|
||||
method: 'GET',
|
||||
// transport.request doesn't do any URI encoding, unlike other JS client APIs. This enables
|
||||
// working with Logstash indices with names like %{[@metadata][beat]}-%{[@metadata][version]}.
|
||||
path: `/${encodeURIComponent(indexNamesString)}`,
|
||||
query: {
|
||||
const { body: indices } = await client.asCurrentUser.indices.get({
|
||||
index: indexNamesString,
|
||||
expand_wildcards: 'hidden,all',
|
||||
},
|
||||
});
|
||||
|
||||
if (!Object.keys(indices).length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const catQuery: Pick<CatIndicesParams, 'format' | 'h'> & {
|
||||
expand_wildcards: string;
|
||||
index?: string;
|
||||
} = {
|
||||
const { body: catHits } = await client.asCurrentUser.cat.indices({
|
||||
format: 'json',
|
||||
h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size',
|
||||
expand_wildcards: 'hidden,all',
|
||||
index: indexNamesString,
|
||||
};
|
||||
|
||||
// This call retrieves health and other high-level information about indices.
|
||||
const catHits: Hit[] = await callAsCurrentUser('transport.request', {
|
||||
method: 'GET',
|
||||
path: '/_cat/indices',
|
||||
query: catQuery,
|
||||
});
|
||||
|
||||
// System indices may show up in _cat APIs, as these APIs are primarily used for troubleshooting
|
||||
// For now, we filter them out and only return index information for the indices we have
|
||||
// In the future, we should migrate away from using cat APIs (https://github.com/elastic/kibana/issues/57286)
|
||||
return catHits.reduce((decoratedIndices, hit) => {
|
||||
const index = indices[hit.index];
|
||||
const index = indices[hit.index!];
|
||||
|
||||
if (typeof index !== 'undefined') {
|
||||
const aliases = Object.keys(index.aliases);
|
||||
const aliases = Object.keys(index.aliases!);
|
||||
|
||||
decoratedIndices.push({
|
||||
health: hit.health,
|
||||
status: hit.status,
|
||||
name: hit.index,
|
||||
uuid: hit.uuid,
|
||||
primary: hit.pri,
|
||||
replica: hit.rep,
|
||||
health: hit.health!,
|
||||
status: hit.status!,
|
||||
name: hit.index!,
|
||||
uuid: hit.uuid!,
|
||||
primary: hit.pri!,
|
||||
replica: hit.rep!,
|
||||
documents: hit['docs.count'],
|
||||
size: hit['store.size'],
|
||||
isFrozen: hit.sth === 'true', // sth value coming back as a string from ES
|
||||
aliases: aliases.length ? aliases : 'none',
|
||||
// @ts-expect-error @elastic/elasticsearch Property 'index' does not exist on type 'IndicesIndexSettings | IndicesIndexStatePrefixedSettings'.
|
||||
hidden: index.settings.index.hidden === 'true',
|
||||
data_stream: index.data_stream,
|
||||
// @ts-expect-error @elastic/elasticsearch Property 'data_stream' does not exist on type 'IndicesIndexState'.
|
||||
data_stream: index.data_stream!,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -106,10 +64,10 @@ async function fetchIndicesCall(
|
|||
}
|
||||
|
||||
export const fetchIndices = async (
|
||||
callAsCurrentUser: CallAsCurrentUser,
|
||||
client: IScopedClusterClient,
|
||||
indexDataEnricher: IndexDataEnricher,
|
||||
indexNames?: string[]
|
||||
) => {
|
||||
const indices = await fetchIndicesCall(callAsCurrentUser, indexNames);
|
||||
return await indexDataEnricher.enrichIndices(indices, callAsCurrentUser);
|
||||
const indices = await fetchIndicesCall(client, indexNames);
|
||||
return await indexDataEnricher.enrichIndices(indices, client);
|
||||
};
|
||||
|
|
|
@ -5,16 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
|
||||
// Cloud has its own system for managing templates and we want to make
|
||||
// this clear in the UI when a template is used in a Cloud deployment.
|
||||
export const getCloudManagedTemplatePrefix = async (
|
||||
callAsCurrentUser: any
|
||||
client: IScopedClusterClient
|
||||
): Promise<string | undefined> => {
|
||||
try {
|
||||
const { persistent, transient, defaults } = await callAsCurrentUser('cluster.getSettings', {
|
||||
filterPath: '*.*managed_index_templates',
|
||||
flatSettings: true,
|
||||
includeDefaults: true,
|
||||
const {
|
||||
body: { persistent, transient, defaults },
|
||||
} = await client.asCurrentUser.cluster.getSettings({
|
||||
filter_path: '*.*managed_index_templates',
|
||||
flat_settings: true,
|
||||
include_defaults: true,
|
||||
});
|
||||
|
||||
const { 'cluster.metadata.managed_index_templates': managedTemplatesPrefix = undefined } = {
|
||||
|
|
|
@ -5,20 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
CoreSetup,
|
||||
Plugin,
|
||||
PluginInitializerContext,
|
||||
ILegacyCustomClusterClient,
|
||||
} from 'src/core/server';
|
||||
import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/server';
|
||||
|
||||
import { PLUGIN } from '../common/constants/plugin';
|
||||
import { Dependencies } from './types';
|
||||
import { ApiRoutes } from './routes';
|
||||
import { IndexDataEnricher } from './services';
|
||||
import { isEsError, handleEsError, parseEsError } from './shared_imports';
|
||||
import { elasticsearchJsPlugin } from './client/elasticsearch';
|
||||
import type { IndexManagementRequestHandlerContext } from './types';
|
||||
import { handleEsError } from './shared_imports';
|
||||
|
||||
export interface IndexManagementPluginSetup {
|
||||
indexDataEnricher: {
|
||||
|
@ -26,16 +19,9 @@ export interface IndexManagementPluginSetup {
|
|||
};
|
||||
}
|
||||
|
||||
async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) {
|
||||
const [core] = await getStartServices();
|
||||
const esClientConfig = { plugins: [elasticsearchJsPlugin] };
|
||||
return core.elasticsearch.legacy.createClient('dataManagement', esClientConfig);
|
||||
}
|
||||
|
||||
export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup, void, any, any> {
|
||||
private readonly apiRoutes: ApiRoutes;
|
||||
private readonly indexDataEnricher: IndexDataEnricher;
|
||||
private dataManagementESClient?: ILegacyCustomClusterClient;
|
||||
|
||||
constructor(initContext: PluginInitializerContext) {
|
||||
this.apiRoutes = new ApiRoutes();
|
||||
|
@ -46,8 +32,6 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
|||
{ http, getStartServices }: CoreSetup,
|
||||
{ features, security }: Dependencies
|
||||
): IndexManagementPluginSetup {
|
||||
const router = http.createRouter<IndexManagementRequestHandlerContext>();
|
||||
|
||||
features.registerElasticsearchFeature({
|
||||
id: PLUGIN.id,
|
||||
management: {
|
||||
|
@ -63,27 +47,13 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
|||
],
|
||||
});
|
||||
|
||||
http.registerRouteHandlerContext<IndexManagementRequestHandlerContext, 'dataManagement'>(
|
||||
'dataManagement',
|
||||
async (ctx, request) => {
|
||||
this.dataManagementESClient =
|
||||
this.dataManagementESClient ?? (await getCustomEsClient(getStartServices));
|
||||
|
||||
return {
|
||||
client: this.dataManagementESClient.asScoped(request),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
this.apiRoutes.setup({
|
||||
router,
|
||||
router: http.createRouter(),
|
||||
config: {
|
||||
isSecurityEnabled: () => security !== undefined && security.license.isEnabled(),
|
||||
},
|
||||
indexDataEnricher: this.indexDataEnricher,
|
||||
lib: {
|
||||
isEsError,
|
||||
parseEsError,
|
||||
handleEsError,
|
||||
},
|
||||
});
|
||||
|
@ -97,9 +67,5 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
|||
|
||||
start() {}
|
||||
|
||||
stop() {
|
||||
if (this.dataManagementESClient) {
|
||||
this.dataManagementESClient.close();
|
||||
}
|
||||
}
|
||||
stop() {}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,10 @@ import { RouteDependencies } from '../../../types';
|
|||
import { addBasePath } from '../index';
|
||||
import { componentTemplateSchema } from './schema_validation';
|
||||
|
||||
export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDependencies): void => {
|
||||
export const registerCreateRoute = ({
|
||||
router,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies): void => {
|
||||
router.post(
|
||||
{
|
||||
path: addBasePath('/component_templates'),
|
||||
|
@ -20,24 +23,23 @@ export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
|||
body: componentTemplateSchema,
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
|
||||
const serializedComponentTemplate = serializeComponentTemplate(req.body);
|
||||
const serializedComponentTemplate = serializeComponentTemplate(request.body);
|
||||
|
||||
const { name } = req.body;
|
||||
const { name } = request.body;
|
||||
|
||||
try {
|
||||
// Check that a component template with the same name doesn't already exist
|
||||
const componentTemplateResponse = await callAsCurrentUser(
|
||||
'dataManagement.getComponentTemplate',
|
||||
{ name }
|
||||
);
|
||||
|
||||
const { component_templates: componentTemplates } = componentTemplateResponse;
|
||||
const {
|
||||
body: { component_templates: componentTemplates },
|
||||
} = await client.asCurrentUser.cluster.getComponentTemplate({
|
||||
name,
|
||||
});
|
||||
|
||||
if (componentTemplates.length) {
|
||||
return res.conflict({
|
||||
return response.conflict({
|
||||
body: new Error(
|
||||
i18n.translate('xpack.idxMgmt.componentTemplates.createRoute.duplicateErrorMessage', {
|
||||
defaultMessage: "There is already a component template with name '{name}'.",
|
||||
|
@ -53,21 +55,15 @@ export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
|||
}
|
||||
|
||||
try {
|
||||
const response = await callAsCurrentUser('dataManagement.saveComponentTemplate', {
|
||||
const { body: responseBody } = await client.asCurrentUser.cluster.putComponentTemplate({
|
||||
name,
|
||||
// @ts-expect-error @elastic/elasticsearch Type 'ComponentTemplateSerialized' is not assignable
|
||||
body: serializedComponentTemplate,
|
||||
});
|
||||
|
||||
return res.ok({ body: response });
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
if (isEsError(error)) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,7 +14,10 @@ const paramsSchema = schema.object({
|
|||
names: schema.string(),
|
||||
});
|
||||
|
||||
export const registerDeleteRoute = ({ router }: RouteDependencies): void => {
|
||||
export const registerDeleteRoute = ({
|
||||
router,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies): void => {
|
||||
router.delete(
|
||||
{
|
||||
path: addBasePath('/component_templates/{names}'),
|
||||
|
@ -22,32 +25,34 @@ export const registerDeleteRoute = ({ router }: RouteDependencies): void => {
|
|||
params: paramsSchema,
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { names } = req.params;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { names } = request.params;
|
||||
const componentNames = names.split(',');
|
||||
|
||||
const response: { itemsDeleted: string[]; errors: any[] } = {
|
||||
const responseBody: { itemsDeleted: string[]; errors: any[] } = {
|
||||
itemsDeleted: [],
|
||||
errors: [],
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
componentNames.map((componentName) => {
|
||||
return callAsCurrentUser('dataManagement.deleteComponentTemplate', {
|
||||
componentNames.map(async (componentName) => {
|
||||
try {
|
||||
await client.asCurrentUser.cluster.deleteComponentTemplate({
|
||||
name: componentName,
|
||||
})
|
||||
.then(() => response.itemsDeleted.push(componentName))
|
||||
.catch((e) =>
|
||||
response.errors.push({
|
||||
});
|
||||
|
||||
return responseBody.itemsDeleted.push(componentName);
|
||||
} catch (error) {
|
||||
return responseBody.errors.push({
|
||||
name: componentName,
|
||||
error: e,
|
||||
})
|
||||
);
|
||||
error: handleEsError({ error, response }),
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return res.ok({ body: response });
|
||||
return response.ok({ body: responseBody });
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -19,25 +19,23 @@ const paramsSchema = schema.object({
|
|||
name: schema.string(),
|
||||
});
|
||||
|
||||
export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDependencies) {
|
||||
export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
// Get all component templates
|
||||
router.get(
|
||||
{ path: addBasePath('/component_templates'), validate: false },
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
|
||||
try {
|
||||
const {
|
||||
component_templates: componentTemplates,
|
||||
}: { component_templates: ComponentTemplateFromEs[] } = await callAsCurrentUser(
|
||||
'dataManagement.getComponentTemplates'
|
||||
);
|
||||
body: { component_templates: componentTemplates },
|
||||
} = await client.asCurrentUser.cluster.getComponentTemplate();
|
||||
|
||||
const { index_templates: indexTemplates } = await callAsCurrentUser(
|
||||
'dataManagement.getComposableIndexTemplates'
|
||||
);
|
||||
const {
|
||||
body: { index_templates: indexTemplates },
|
||||
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||
|
||||
const body = componentTemplates.map((componentTemplate) => {
|
||||
const body = componentTemplates.map((componentTemplate: ComponentTemplateFromEs) => {
|
||||
const deserializedComponentTemplateListItem = deserializeComponentTemplateList(
|
||||
componentTemplate,
|
||||
indexTemplates
|
||||
|
@ -45,16 +43,9 @@ export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDepende
|
|||
return deserializedComponentTemplateListItem;
|
||||
});
|
||||
|
||||
return res.ok({ body });
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
if (isEsError(error)) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -67,34 +58,26 @@ export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDepende
|
|||
params: paramsSchema,
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { name } = req.params;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { name } = request.params;
|
||||
|
||||
try {
|
||||
const { component_templates: componentTemplates } = await callAsCurrentUser(
|
||||
'dataManagement.getComponentTemplates',
|
||||
{
|
||||
const {
|
||||
body: { component_templates: componentTemplates },
|
||||
} = await client.asCurrentUser.cluster.getComponentTemplate({
|
||||
name,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const { index_templates: indexTemplates } = await callAsCurrentUser(
|
||||
'dataManagement.getComposableIndexTemplates'
|
||||
);
|
||||
const {
|
||||
body: { index_templates: indexTemplates },
|
||||
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||
|
||||
return res.ok({
|
||||
return response.ok({
|
||||
body: deserializeComponentTemplate(componentTemplates[0], indexTemplates),
|
||||
});
|
||||
} catch (error) {
|
||||
if (isEsError(error)) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -18,17 +18,15 @@ const httpService = httpServiceMock.createSetupContract();
|
|||
|
||||
const mockedIndexDataEnricher = new IndexDataEnricher();
|
||||
|
||||
const mockRouteContext = ({
|
||||
callAsCurrentUser,
|
||||
}: {
|
||||
callAsCurrentUser: any;
|
||||
}): RequestHandlerContext => {
|
||||
const mockRouteContext = ({ hasPrivileges }: { hasPrivileges: unknown }): RequestHandlerContext => {
|
||||
const routeContextMock = ({
|
||||
core: {
|
||||
elasticsearch: {
|
||||
legacy: {
|
||||
client: {
|
||||
callAsCurrentUser,
|
||||
asCurrentUser: {
|
||||
security: {
|
||||
hasPrivileges,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -51,8 +49,6 @@ describe('GET privileges', () => {
|
|||
},
|
||||
indexDataEnricher: mockedIndexDataEnricher,
|
||||
lib: {
|
||||
isEsError: jest.fn(),
|
||||
parseEsError: jest.fn(),
|
||||
handleEsError: jest.fn(),
|
||||
},
|
||||
});
|
||||
|
@ -62,15 +58,17 @@ describe('GET privileges', () => {
|
|||
|
||||
it('should return the correct response when a user has privileges', async () => {
|
||||
const privilegesResponseMock = {
|
||||
body: {
|
||||
username: 'elastic',
|
||||
has_all_requested: true,
|
||||
cluster: { manage_index_templates: true },
|
||||
index: {},
|
||||
application: {},
|
||||
},
|
||||
};
|
||||
|
||||
const routeContextMock = mockRouteContext({
|
||||
callAsCurrentUser: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||
hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
|
@ -86,15 +84,17 @@ describe('GET privileges', () => {
|
|||
|
||||
it('should return the correct response when a user does not have privileges', async () => {
|
||||
const privilegesResponseMock = {
|
||||
body: {
|
||||
username: 'elastic',
|
||||
has_all_requested: false,
|
||||
cluster: { manage_index_templates: false },
|
||||
index: {},
|
||||
application: {},
|
||||
},
|
||||
};
|
||||
|
||||
const routeContextMock = mockRouteContext({
|
||||
callAsCurrentUser: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||
hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
|
@ -119,8 +119,6 @@ describe('GET privileges', () => {
|
|||
},
|
||||
indexDataEnricher: mockedIndexDataEnricher,
|
||||
lib: {
|
||||
isEsError: jest.fn(),
|
||||
parseEsError: jest.fn(),
|
||||
handleEsError: jest.fn(),
|
||||
},
|
||||
});
|
||||
|
@ -130,7 +128,7 @@ describe('GET privileges', () => {
|
|||
|
||||
it('should return the default privileges response', async () => {
|
||||
const routeContextMock = mockRouteContext({
|
||||
callAsCurrentUser: jest.fn(),
|
||||
hasPrivileges: jest.fn(),
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
|
|
|
@ -17,13 +17,17 @@ const extractMissingPrivileges = (privilegesObject: { [key: string]: boolean } =
|
|||
return privileges;
|
||||
}, []);
|
||||
|
||||
export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) => {
|
||||
export const registerPrivilegesRoute = ({
|
||||
router,
|
||||
config,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies) => {
|
||||
router.get(
|
||||
{
|
||||
path: addBasePath('/component_templates/privileges'),
|
||||
validate: false,
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
async (context, request, response) => {
|
||||
const privilegesResult: Privileges = {
|
||||
hasAllPrivileges: true,
|
||||
missingPrivileges: {
|
||||
|
@ -33,38 +37,28 @@ export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) =
|
|||
|
||||
// Skip the privileges check if security is not enabled
|
||||
if (!config.isSecurityEnabled()) {
|
||||
return res.ok({ body: privilegesResult });
|
||||
return response.ok({ body: privilegesResult });
|
||||
}
|
||||
|
||||
const {
|
||||
core: {
|
||||
elasticsearch: {
|
||||
legacy: { client },
|
||||
},
|
||||
},
|
||||
} = ctx;
|
||||
const { client } = context.core.elasticsearch;
|
||||
|
||||
try {
|
||||
const { has_all_requested: hasAllPrivileges, cluster } = await client.callAsCurrentUser(
|
||||
'transport.request',
|
||||
{
|
||||
path: '/_security/user/_has_privileges',
|
||||
method: 'POST',
|
||||
const {
|
||||
body: { has_all_requested: hasAllPrivileges, cluster },
|
||||
} = await client.asCurrentUser.security.hasPrivileges({
|
||||
body: {
|
||||
cluster: ['manage_index_templates'],
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!hasAllPrivileges) {
|
||||
privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster);
|
||||
}
|
||||
|
||||
privilegesResult.hasAllPrivileges = hasAllPrivileges;
|
||||
|
||||
return res.ok({ body: privilegesResult });
|
||||
} catch (e) {
|
||||
throw e;
|
||||
return response.ok({ body: privilegesResult });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -15,7 +15,10 @@ const paramsSchema = schema.object({
|
|||
name: schema.string(),
|
||||
});
|
||||
|
||||
export const registerUpdateRoute = ({ router, lib: { isEsError } }: RouteDependencies): void => {
|
||||
export const registerUpdateRoute = ({
|
||||
router,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies): void => {
|
||||
router.put(
|
||||
{
|
||||
path: addBasePath('/component_templates/{name}'),
|
||||
|
@ -24,34 +27,28 @@ export const registerUpdateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
|||
params: paramsSchema,
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { name } = req.params;
|
||||
const { template, version, _meta } = req.body;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { name } = request.params;
|
||||
const { template, version, _meta } = request.body;
|
||||
|
||||
try {
|
||||
// Verify component exists; ES will throw 404 if not
|
||||
await callAsCurrentUser('dataManagement.getComponentTemplate', { name });
|
||||
await client.asCurrentUser.cluster.getComponentTemplate({ name });
|
||||
|
||||
const response = await callAsCurrentUser('dataManagement.saveComponentTemplate', {
|
||||
const { body: responseBody } = await client.asCurrentUser.cluster.putComponentTemplate({
|
||||
name,
|
||||
body: {
|
||||
// @ts-expect-error @elastic/elasticsearch Not assignable to type 'IndicesIndexState'
|
||||
template,
|
||||
version,
|
||||
_meta,
|
||||
},
|
||||
});
|
||||
|
||||
return res.ok({ body: response });
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
if (isEsError(error)) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -9,23 +9,22 @@ import { schema, TypeOf } from '@kbn/config-schema';
|
|||
|
||||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../index';
|
||||
import { wrapEsError } from '../../helpers';
|
||||
|
||||
const bodySchema = schema.object({
|
||||
dataStreams: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerDeleteRoute({ router }: RouteDependencies) {
|
||||
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: addBasePath('/delete_data_streams'),
|
||||
validate: { body: bodySchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { dataStreams } = req.body as TypeOf<typeof bodySchema>;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { dataStreams } = request.body as TypeOf<typeof bodySchema>;
|
||||
|
||||
const response: { dataStreamsDeleted: string[]; errors: any[] } = {
|
||||
const responseBody: { dataStreamsDeleted: string[]; errors: any[] } = {
|
||||
dataStreamsDeleted: [],
|
||||
errors: [],
|
||||
};
|
||||
|
@ -33,21 +32,21 @@ export function registerDeleteRoute({ router }: RouteDependencies) {
|
|||
await Promise.all(
|
||||
dataStreams.map(async (name: string) => {
|
||||
try {
|
||||
await callAsCurrentUser('dataManagement.deleteDataStream', {
|
||||
await client.asCurrentUser.indices.deleteDataStream({
|
||||
name,
|
||||
});
|
||||
|
||||
return response.dataStreamsDeleted.push(name);
|
||||
} catch (e) {
|
||||
return response.errors.push({
|
||||
return responseBody.dataStreamsDeleted.push(name);
|
||||
} catch (error) {
|
||||
return responseBody.errors.push({
|
||||
name,
|
||||
error: wrapEsError(e),
|
||||
error: handleEsError({ error, response }),
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return res.ok({ body: response });
|
||||
return response.ok({ body: responseBody });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import { ElasticsearchClient } from 'kibana/server';
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
import { deserializeDataStream, deserializeDataStreamList } from '../../../../common/lib';
|
||||
import { DataStreamFromEs } from '../../../../common/types';
|
||||
import { RouteDependencies } from '../../../types';
|
||||
|
@ -68,30 +68,23 @@ const enhanceDataStreams = ({
|
|||
});
|
||||
};
|
||||
|
||||
const getDataStreams = (client: ElasticsearchClient, name = '*') => {
|
||||
// TODO update when elasticsearch client has update requestParams for 'indices.getDataStream'
|
||||
return client.transport.request({
|
||||
path: `/_data_stream/${encodeURIComponent(name)}`,
|
||||
method: 'GET',
|
||||
querystring: {
|
||||
const getDataStreams = (client: IScopedClusterClient, name = '*') => {
|
||||
return client.asCurrentUser.indices.getDataStream({
|
||||
name,
|
||||
expand_wildcards: 'all',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getDataStreamsStats = (client: ElasticsearchClient, name = '*') => {
|
||||
return client.transport.request({
|
||||
path: `/_data_stream/${encodeURIComponent(name)}/_stats`,
|
||||
method: 'GET',
|
||||
querystring: {
|
||||
const getDataStreamsStats = (client: IScopedClusterClient, name = '*') => {
|
||||
return client.asCurrentUser.indices.dataStreamsStats({
|
||||
name,
|
||||
expand_wildcards: 'all',
|
||||
human: true,
|
||||
expand_wildcards: 'all',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getDataStreamsPrivileges = (client: ElasticsearchClient, names: string[]) => {
|
||||
return client.security.hasPrivileges({
|
||||
const getDataStreamsPrivileges = (client: IScopedClusterClient, names: string[]) => {
|
||||
return client.asCurrentUser.security.hasPrivileges({
|
||||
body: {
|
||||
index: [
|
||||
{
|
||||
|
@ -109,15 +102,15 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }:
|
|||
});
|
||||
router.get(
|
||||
{ path: addBasePath('/data_streams'), validate: { query: querySchema } },
|
||||
async (ctx, req, response) => {
|
||||
const { asCurrentUser } = ctx.core.elasticsearch.client;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
|
||||
const includeStats = (req.query as TypeOf<typeof querySchema>).includeStats === 'true';
|
||||
const includeStats = (request.query as TypeOf<typeof querySchema>).includeStats === 'true';
|
||||
|
||||
try {
|
||||
let {
|
||||
const {
|
||||
body: { data_streams: dataStreams },
|
||||
} = await getDataStreams(asCurrentUser);
|
||||
} = await getDataStreams(client);
|
||||
|
||||
let dataStreamsStats;
|
||||
let dataStreamsPrivileges;
|
||||
|
@ -125,24 +118,26 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }:
|
|||
if (includeStats) {
|
||||
({
|
||||
body: { data_streams: dataStreamsStats },
|
||||
} = await getDataStreamsStats(asCurrentUser));
|
||||
} = await getDataStreamsStats(client));
|
||||
}
|
||||
|
||||
if (config.isSecurityEnabled() && dataStreams.length > 0) {
|
||||
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(
|
||||
asCurrentUser,
|
||||
dataStreams.map((dataStream: DataStreamFromEs) => dataStream.name)
|
||||
client,
|
||||
dataStreams.map((dataStream) => dataStream.name)
|
||||
));
|
||||
}
|
||||
|
||||
dataStreams = enhanceDataStreams({
|
||||
const enhancedDataStreams = enhanceDataStreams({
|
||||
// @ts-expect-error @elastic/elasticsearch DataStreamFromEs incompatible with IndicesGetDataStreamIndicesGetDataStreamItem
|
||||
dataStreams,
|
||||
// @ts-expect-error @elastic/elasticsearch StatsFromEs incompatible with IndicesDataStreamsStatsDataStreamsStatsItem
|
||||
dataStreamsStats,
|
||||
// @ts-expect-error PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||
// @ts-expect-error @elastic/elasticsearch PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||
dataStreamsPrivileges,
|
||||
});
|
||||
|
||||
return response.ok({ body: deserializeDataStreamList(dataStreams) });
|
||||
return response.ok({ body: deserializeDataStreamList(enhancedDataStreams) });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
|
@ -159,9 +154,9 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }:
|
|||
path: addBasePath('/data_streams/{name}'),
|
||||
validate: { params: paramsSchema },
|
||||
},
|
||||
async (ctx, req, response) => {
|
||||
const { name } = req.params as TypeOf<typeof paramsSchema>;
|
||||
const { asCurrentUser } = ctx.core.elasticsearch.client;
|
||||
async (context, request, response) => {
|
||||
const { name } = request.params as TypeOf<typeof paramsSchema>;
|
||||
const { client } = context.core.elasticsearch;
|
||||
try {
|
||||
const [
|
||||
{
|
||||
|
@ -170,23 +165,22 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }:
|
|||
{
|
||||
body: { data_streams: dataStreamsStats },
|
||||
},
|
||||
] = await Promise.all([
|
||||
getDataStreams(asCurrentUser, name),
|
||||
getDataStreamsStats(asCurrentUser, name),
|
||||
]);
|
||||
] = await Promise.all([getDataStreams(client, name), getDataStreamsStats(client, name)]);
|
||||
|
||||
if (dataStreams[0]) {
|
||||
let dataStreamsPrivileges;
|
||||
if (config.isSecurityEnabled()) {
|
||||
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(asCurrentUser, [
|
||||
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(client, [
|
||||
dataStreams[0].name,
|
||||
]));
|
||||
}
|
||||
|
||||
const enhancedDataStreams = enhanceDataStreams({
|
||||
// @ts-expect-error @elastic/elasticsearch DataStreamFromEs incompatible with IndicesGetDataStreamIndicesGetDataStreamItem
|
||||
dataStreams,
|
||||
// @ts-expect-error @elastic/elasticsearch StatsFromEs incompatible with IndicesDataStreamsStatsDataStreamsStatsItem
|
||||
dataStreamsStats,
|
||||
// @ts-expect-error PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||
// @ts-expect-error @elastic/elasticsearch PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||
dataStreamsPrivileges,
|
||||
});
|
||||
const body = deserializeDataStream(enhancedDataStreams[0]);
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerClearCacheRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerClearCacheRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/clear_cache'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const payload = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = payload;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.clearCache', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.clearCache(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerCloseRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerCloseRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/close'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const payload = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = payload;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.close', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.close(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerDeleteRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/delete'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const body = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = body;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.delete', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.delete(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerFlushRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerFlushRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/flush'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const body = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = body;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.flush', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.flush(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ const bodySchema = schema.object({
|
|||
maxNumSegments: schema.maybe(schema.number()),
|
||||
});
|
||||
|
||||
export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerForcemergeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: addBasePath('/indices/forcemerge'),
|
||||
|
@ -23,10 +23,11 @@ export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
|||
body: bodySchema,
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { maxNumSegments, indices = [] } = req.body as typeof bodySchema.type;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { maxNumSegments, indices = [] } = request.body as typeof bodySchema.type;
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
|
@ -35,17 +36,10 @@ export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
|||
}
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.forcemerge', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.forcemerge(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,33 +14,20 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerFreezeRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerFreezeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/freeze'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const body = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = body;
|
||||
|
||||
const params = {
|
||||
path: `/${encodeURIComponent(indices.join(','))}/_freeze`,
|
||||
method: 'POST',
|
||||
};
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
try {
|
||||
await await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
||||
'transport.request',
|
||||
params
|
||||
);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
await client.asCurrentUser.indices.freeze({
|
||||
index: indices.join(','),
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -9,23 +9,21 @@ import { fetchIndices } from '../../../lib/fetch_indices';
|
|||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../index';
|
||||
|
||||
export function registerListRoute({ router, indexDataEnricher, lib }: RouteDependencies) {
|
||||
router.get({ path: addBasePath('/indices'), validate: false }, async (ctx, req, res) => {
|
||||
export function registerListRoute({
|
||||
router,
|
||||
indexDataEnricher,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/indices'), validate: false },
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
try {
|
||||
const indices = await fetchIndices(
|
||||
ctx.core.elasticsearch.legacy.client.callAsCurrentUser,
|
||||
indexDataEnricher
|
||||
const indices = await fetchIndices(client, indexDataEnricher);
|
||||
return response.ok({ body: indices });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
return res.ok({ body: indices });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerOpenRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerOpenRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/open'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const body = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = body;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.open', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.open(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerRefreshRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerRefreshRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/refresh'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const body = req.body as typeof bodySchema.type;
|
||||
const { indices = [] } = body;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
expand_wildcards: 'none',
|
||||
format: 'json',
|
||||
index: indices,
|
||||
};
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.refresh', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
await client.asCurrentUser.indices.refresh(params);
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -17,28 +17,22 @@ const bodySchema = schema.maybe(
|
|||
})
|
||||
);
|
||||
|
||||
export function registerReloadRoute({ router, indexDataEnricher, lib }: RouteDependencies) {
|
||||
export function registerReloadRoute({
|
||||
router,
|
||||
indexDataEnricher,
|
||||
lib: { handleEsError },
|
||||
}: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/reload'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { indexNames = [] } = (req.body as typeof bodySchema.type) ?? {};
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indexNames = [] } = (request.body as typeof bodySchema.type) ?? {};
|
||||
|
||||
try {
|
||||
const indices = await fetchIndices(
|
||||
ctx.core.elasticsearch.legacy.client.callAsCurrentUser,
|
||||
indexDataEnricher,
|
||||
indexNames
|
||||
);
|
||||
return res.ok({ body: indices });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
const indices = await fetchIndices(client, indexDataEnricher, indexNames);
|
||||
return response.ok({ body: indices });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,28 +14,20 @@ const bodySchema = schema.object({
|
|||
indices: schema.arrayOf(schema.string()),
|
||||
});
|
||||
|
||||
export function registerUnfreezeRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerUnfreezeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/indices/unfreeze'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { indices = [] } = req.body as typeof bodySchema.type;
|
||||
const params = {
|
||||
path: `/${encodeURIComponent(indices.join(','))}/_unfreeze`,
|
||||
method: 'POST',
|
||||
};
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||
|
||||
try {
|
||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('transport.request', params);
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
await client.asCurrentUser.indices.unfreeze({
|
||||
index: indices.join(','),
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
return response.ok();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -21,32 +21,23 @@ function formatHit(hit: { [key: string]: { mappings: any } }, indexName: string)
|
|||
};
|
||||
}
|
||||
|
||||
export function registerMappingRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerMappingRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/mapping/{indexName}'), validate: { params: paramsSchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { indexName } = req.params as typeof paramsSchema.type;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indexName } = request.params as typeof paramsSchema.type;
|
||||
const params = {
|
||||
expand_wildcards: 'none',
|
||||
index: indexName,
|
||||
};
|
||||
|
||||
try {
|
||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
||||
'indices.getMapping',
|
||||
params
|
||||
);
|
||||
const response = formatHit(hit, indexName);
|
||||
return res.ok({ body: response });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
const { body: hit } = await client.asCurrentUser.indices.getMapping(params);
|
||||
const responseBody = formatHit(hit, indexName);
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -21,34 +21,25 @@ function formatHit(hit: { [key: string]: {} }) {
|
|||
return hit[key];
|
||||
}
|
||||
|
||||
export function registerLoadRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerLoadRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/settings/{indexName}'), validate: { params: paramsSchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { indexName } = req.params as typeof paramsSchema.type;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indexName } = request.params as typeof paramsSchema.type;
|
||||
const params = {
|
||||
expandWildcards: 'none',
|
||||
flatSettings: false,
|
||||
expand_wildcards: 'none',
|
||||
flat_settings: false,
|
||||
local: false,
|
||||
includeDefaults: true,
|
||||
include_defaults: true,
|
||||
index: indexName,
|
||||
};
|
||||
|
||||
try {
|
||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
||||
'indices.getSettings',
|
||||
params
|
||||
);
|
||||
return res.ok({ body: formatHit(hit) });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
const { body: hit } = await client.asCurrentUser.indices.getSettings(params);
|
||||
return response.ok({ body: formatHit(hit) });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -16,37 +16,28 @@ const paramsSchema = schema.object({
|
|||
indexName: schema.string(),
|
||||
});
|
||||
|
||||
export function registerUpdateRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.put(
|
||||
{
|
||||
path: addBasePath('/settings/{indexName}'),
|
||||
validate: { body: bodySchema, params: paramsSchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { indexName } = req.params as typeof paramsSchema.type;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indexName } = request.params as typeof paramsSchema.type;
|
||||
const params = {
|
||||
ignoreUnavailable: true,
|
||||
allowNoIndices: false,
|
||||
expandWildcards: 'none',
|
||||
ignore_unavailable: true,
|
||||
allow_no_indices: false,
|
||||
expand_wildcards: 'none',
|
||||
index: indexName,
|
||||
body: req.body,
|
||||
body: request.body,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
||||
'indices.putSettings',
|
||||
params
|
||||
);
|
||||
return res.ok({ body: response });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
const { body: responseBody } = await client.asCurrentUser.indices.putSettings(params);
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../index';
|
||||
|
@ -14,40 +15,37 @@ const paramsSchema = schema.object({
|
|||
indexName: schema.string(),
|
||||
});
|
||||
|
||||
function formatHit(hit: { _shards: any; indices: { [key: string]: any } }, indexName: string) {
|
||||
interface Hit {
|
||||
_shards: unknown;
|
||||
indices?: Record<string, estypes.IndicesStatsIndicesStats>;
|
||||
}
|
||||
|
||||
function formatHit(hit: Hit, indexName: string) {
|
||||
const { _shards, indices } = hit;
|
||||
const stats = indices[indexName];
|
||||
const stats = indices![indexName];
|
||||
return {
|
||||
_shards,
|
||||
stats,
|
||||
};
|
||||
}
|
||||
|
||||
export function registerStatsRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerStatsRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/stats/{indexName}'), validate: { params: paramsSchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { indexName } = req.params as typeof paramsSchema.type;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { indexName } = request.params as typeof paramsSchema.type;
|
||||
const params = {
|
||||
expand_wildcards: 'none',
|
||||
index: indexName,
|
||||
};
|
||||
|
||||
try {
|
||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
||||
'indices.stats',
|
||||
params
|
||||
);
|
||||
return res.ok({ body: formatHit(hit, indexName) });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
const { body: hit } = await client.asCurrentUser.indices.stats(params);
|
||||
|
||||
return response.ok({ body: formatHit(hit, indexName) });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5,32 +5,33 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
import { serializeTemplate, serializeLegacyTemplate } from '../../../../common/lib';
|
||||
import { TemplateDeserialized, LegacyTemplateSerialized } from '../../../../common';
|
||||
import { CallAsCurrentUser } from '../../../types';
|
||||
|
||||
export const doesTemplateExist = async ({
|
||||
name,
|
||||
callAsCurrentUser,
|
||||
client,
|
||||
isLegacy,
|
||||
}: {
|
||||
name: string;
|
||||
callAsCurrentUser: CallAsCurrentUser;
|
||||
client: IScopedClusterClient;
|
||||
isLegacy?: boolean;
|
||||
}) => {
|
||||
if (isLegacy) {
|
||||
return await callAsCurrentUser('indices.existsTemplate', { name });
|
||||
return await client.asCurrentUser.indices.existsTemplate({ name });
|
||||
}
|
||||
return await callAsCurrentUser('dataManagement.existsTemplate', { name });
|
||||
|
||||
return await client.asCurrentUser.indices.existsIndexTemplate({ name });
|
||||
};
|
||||
|
||||
export const saveTemplate = async ({
|
||||
template,
|
||||
callAsCurrentUser,
|
||||
client,
|
||||
isLegacy,
|
||||
}: {
|
||||
template: TemplateDeserialized;
|
||||
callAsCurrentUser: CallAsCurrentUser;
|
||||
client: IScopedClusterClient;
|
||||
isLegacy?: boolean;
|
||||
}) => {
|
||||
const serializedTemplate = isLegacy
|
||||
|
@ -48,8 +49,9 @@ export const saveTemplate = async ({
|
|||
aliases,
|
||||
} = serializedTemplate as LegacyTemplateSerialized;
|
||||
|
||||
return await callAsCurrentUser('indices.putTemplate', {
|
||||
return await client.asCurrentUser.indices.putTemplate({
|
||||
name: template.name,
|
||||
// @ts-expect-error @elastic/elasticsearch not assignable to parameter of type 'IndicesPutTemplateRequest'
|
||||
order,
|
||||
body: {
|
||||
index_patterns,
|
||||
|
@ -61,8 +63,9 @@ export const saveTemplate = async ({
|
|||
});
|
||||
}
|
||||
|
||||
return await callAsCurrentUser('dataManagement.saveComposableIndexTemplate', {
|
||||
return await client.asCurrentUser.indices.putIndexTemplate({
|
||||
name: template.name,
|
||||
// @ts-expect-error @elastic/elasticsearch Type 'LegacyTemplateSerialized | TemplateSerialized' is not assignable
|
||||
body: serializedTemplate,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -15,25 +15,27 @@ import { saveTemplate, doesTemplateExist } from './lib';
|
|||
|
||||
const bodySchema = templateSchema;
|
||||
|
||||
export function registerCreateRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerCreateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/index_templates'), validate: { body: bodySchema } },
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const template = req.body as TemplateDeserialized;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const template = request.body as TemplateDeserialized;
|
||||
|
||||
try {
|
||||
const {
|
||||
_kbnMeta: { isLegacy },
|
||||
} = template;
|
||||
|
||||
// Check that template with the same name doesn't already exist
|
||||
const templateExists = await doesTemplateExist({
|
||||
const { body: templateExists } = await doesTemplateExist({
|
||||
name: template.name,
|
||||
callAsCurrentUser,
|
||||
client,
|
||||
isLegacy,
|
||||
});
|
||||
|
||||
if (templateExists) {
|
||||
return res.conflict({
|
||||
return response.conflict({
|
||||
body: new Error(
|
||||
i18n.translate('xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage', {
|
||||
defaultMessage: "There is already a template with name '{name}'.",
|
||||
|
@ -45,24 +47,12 @@ export function registerCreateRoute({ router, lib }: RouteDependencies) {
|
|||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Otherwise create new index template
|
||||
const response = await saveTemplate({ template, callAsCurrentUser, isLegacy });
|
||||
const { body: responseBody } = await saveTemplate({ template, client, isLegacy });
|
||||
|
||||
return res.ok({ body: response });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
const error = lib.parseEsError(e.response);
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: {
|
||||
message: error.message,
|
||||
attributes: error,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -9,7 +9,6 @@ import { schema, TypeOf } from '@kbn/config-schema';
|
|||
|
||||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../index';
|
||||
import { wrapEsError } from '../../helpers';
|
||||
|
||||
import { TemplateDeserialized } from '../../../../common';
|
||||
|
||||
|
@ -22,16 +21,19 @@ const bodySchema = schema.object({
|
|||
),
|
||||
});
|
||||
|
||||
export function registerDeleteRoute({ router }: RouteDependencies) {
|
||||
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: addBasePath('/delete_index_templates'),
|
||||
validate: { body: bodySchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { templates } = req.body as TypeOf<typeof bodySchema>;
|
||||
const response: { templatesDeleted: Array<TemplateDeserialized['name']>; errors: any[] } = {
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { templates } = request.body as TypeOf<typeof bodySchema>;
|
||||
const responseBody: {
|
||||
templatesDeleted: Array<TemplateDeserialized['name']>;
|
||||
errors: any[];
|
||||
} = {
|
||||
templatesDeleted: [],
|
||||
errors: [],
|
||||
};
|
||||
|
@ -40,26 +42,26 @@ export function registerDeleteRoute({ router }: RouteDependencies) {
|
|||
templates.map(async ({ name, isLegacy }) => {
|
||||
try {
|
||||
if (isLegacy) {
|
||||
await callAsCurrentUser('indices.deleteTemplate', {
|
||||
await client.asCurrentUser.indices.deleteTemplate({
|
||||
name,
|
||||
});
|
||||
} else {
|
||||
await callAsCurrentUser('dataManagement.deleteComposableIndexTemplate', {
|
||||
await client.asCurrentUser.indices.deleteIndexTemplate({
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
return response.templatesDeleted.push(name);
|
||||
} catch (e) {
|
||||
return response.errors.push({
|
||||
return responseBody.templatesDeleted.push(name);
|
||||
} catch (error) {
|
||||
return responseBody.errors.push({
|
||||
name,
|
||||
error: wrapEsError(e),
|
||||
error: handleEsError({ error, response }),
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return res.ok({ body: response });
|
||||
return response.ok({ body: responseBody });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,17 +17,19 @@ import { getCloudManagedTemplatePrefix } from '../../../lib/get_managed_template
|
|||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../index';
|
||||
|
||||
export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDependencies) {
|
||||
router.get({ path: addBasePath('/index_templates'), validate: false }, async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/index_templates'), validate: false },
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
|
||||
try {
|
||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(callAsCurrentUser);
|
||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(client);
|
||||
|
||||
const legacyTemplatesEs = await callAsCurrentUser('indices.getTemplate');
|
||||
const { index_templates: templatesEs } = await callAsCurrentUser(
|
||||
'dataManagement.getComposableIndexTemplates'
|
||||
);
|
||||
const { body: legacyTemplatesEs } = await client.asCurrentUser.indices.getTemplate();
|
||||
const {
|
||||
body: { index_templates: templatesEs },
|
||||
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||
|
||||
const legacyTemplates = deserializeLegacyTemplateList(
|
||||
legacyTemplatesEs,
|
||||
|
@ -40,18 +42,12 @@ export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDepende
|
|||
legacyTemplates,
|
||||
};
|
||||
|
||||
return res.ok({ body });
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
if (isEsError(error)) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error,
|
||||
});
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
// Case: default
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
const paramsSchema = schema.object({
|
||||
|
@ -63,26 +59,27 @@ const querySchema = schema.object({
|
|||
legacy: schema.maybe(schema.oneOf([schema.literal('true'), schema.literal('false')])),
|
||||
});
|
||||
|
||||
export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerGetOneRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.get(
|
||||
{
|
||||
path: addBasePath('/index_templates/{name}'),
|
||||
validate: { params: paramsSchema, query: querySchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { name } = req.params as TypeOf<typeof paramsSchema>;
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
|
||||
const isLegacy = (req.query as TypeOf<typeof querySchema>).legacy === 'true';
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { name } = request.params as TypeOf<typeof paramsSchema>;
|
||||
const isLegacy = (request.query as TypeOf<typeof querySchema>).legacy === 'true';
|
||||
|
||||
try {
|
||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(callAsCurrentUser);
|
||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(client);
|
||||
|
||||
if (isLegacy) {
|
||||
const indexTemplateByName = await callAsCurrentUser('indices.getTemplate', { name });
|
||||
const { body: indexTemplateByName } = await client.asCurrentUser.indices.getTemplate({
|
||||
name,
|
||||
});
|
||||
|
||||
if (indexTemplateByName[name]) {
|
||||
return res.ok({
|
||||
return response.ok({
|
||||
body: deserializeLegacyTemplate(
|
||||
{ ...indexTemplateByName[name], name },
|
||||
cloudManagedTemplatePrefix
|
||||
|
@ -91,11 +88,11 @@ export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
|||
}
|
||||
} else {
|
||||
const {
|
||||
index_templates: indexTemplates,
|
||||
} = await callAsCurrentUser('dataManagement.getComposableIndexTemplate', { name });
|
||||
body: { index_templates: indexTemplates },
|
||||
} = await client.asCurrentUser.indices.getIndexTemplate({ name });
|
||||
|
||||
if (indexTemplates.length > 0) {
|
||||
return res.ok({
|
||||
return response.ok({
|
||||
body: deserializeTemplate(
|
||||
{ ...indexTemplates[0].index_template, name },
|
||||
cloudManagedTemplatePrefix
|
||||
|
@ -104,16 +101,9 @@ export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
|||
}
|
||||
}
|
||||
|
||||
return res.notFound();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: e,
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
return response.notFound();
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -12,35 +12,30 @@ import { addBasePath } from '../index';
|
|||
|
||||
const bodySchema = schema.object({}, { unknowns: 'allow' });
|
||||
|
||||
export function registerSimulateRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerSimulateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: addBasePath('/index_templates/simulate'),
|
||||
validate: { body: bodySchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const template = req.body as TypeOf<typeof bodySchema>;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const template = request.body as TypeOf<typeof bodySchema>;
|
||||
|
||||
try {
|
||||
const templatePreview = await callAsCurrentUser('dataManagement.simulateTemplate', {
|
||||
body: template,
|
||||
});
|
||||
|
||||
return res.ok({ body: templatePreview });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
const error = lib.parseEsError(e.response);
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
const { body: templatePreview } = await client.asCurrentUser.indices.simulateTemplate({
|
||||
body: {
|
||||
message: error.message,
|
||||
attributes: error,
|
||||
...template,
|
||||
// Until ES fixes a bug on their side we need to send a fake index pattern
|
||||
// that won't match any indices.
|
||||
// Issue: https://github.com/elastic/elasticsearch/issues/59152
|
||||
index_patterns: ['a_fake_index_pattern_that_wont_match_any_indices'],
|
||||
},
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
|
||||
return response.ok({ body: templatePreview });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -18,45 +18,35 @@ const paramsSchema = schema.object({
|
|||
name: schema.string(),
|
||||
});
|
||||
|
||||
export function registerUpdateRoute({ router, lib }: RouteDependencies) {
|
||||
export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||
router.put(
|
||||
{
|
||||
path: addBasePath('/index_templates/{name}'),
|
||||
validate: { body: bodySchema, params: paramsSchema },
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
||||
const { name } = req.params as typeof paramsSchema.type;
|
||||
const template = req.body as TemplateDeserialized;
|
||||
async (context, request, response) => {
|
||||
const { client } = context.core.elasticsearch;
|
||||
const { name } = request.params as typeof paramsSchema.type;
|
||||
const template = request.body as TemplateDeserialized;
|
||||
|
||||
try {
|
||||
const {
|
||||
_kbnMeta: { isLegacy },
|
||||
} = template;
|
||||
|
||||
// Verify the template exists (ES will throw 404 if not)
|
||||
const doesExist = await doesTemplateExist({ name, callAsCurrentUser, isLegacy });
|
||||
const { body: templateExists } = await doesTemplateExist({ name, client, isLegacy });
|
||||
|
||||
if (!doesExist) {
|
||||
return res.notFound();
|
||||
if (!templateExists) {
|
||||
return response.notFound();
|
||||
}
|
||||
|
||||
try {
|
||||
// Next, update index template
|
||||
const response = await saveTemplate({ template, callAsCurrentUser, isLegacy });
|
||||
const { body: responseBody } = await saveTemplate({ template, client, isLegacy });
|
||||
|
||||
return res.ok({ body: response });
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
const error = lib.parseEsError(e.response);
|
||||
return res.customError({
|
||||
statusCode: e.statusCode,
|
||||
body: {
|
||||
message: error.message,
|
||||
attributes: error,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Case: default
|
||||
throw e;
|
||||
return response.ok({ body: responseBody });
|
||||
} catch (error) {
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
const extractCausedByChain = (causedBy: any = {}, accumulator: any[] = []): any => {
|
||||
const { reason, caused_by } = causedBy; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
|
||||
if (reason) {
|
||||
accumulator.push(reason);
|
||||
}
|
||||
|
||||
if (caused_by) {
|
||||
return extractCausedByChain(caused_by, accumulator);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps an error thrown by the ES JS client into a Boom error response and returns it
|
||||
*
|
||||
* @param err Object Error thrown by ES JS client
|
||||
* @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages
|
||||
* @return Object Boom error response
|
||||
*/
|
||||
export const wrapEsError = (err: any, statusCodeToMessageMap: any = {}) => {
|
||||
const { statusCode, response } = err;
|
||||
|
||||
const {
|
||||
error: {
|
||||
root_cause = [], // eslint-disable-line @typescript-eslint/naming-convention
|
||||
caused_by = {}, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
} = {},
|
||||
} = JSON.parse(response);
|
||||
|
||||
// If no custom message if specified for the error's status code, just
|
||||
// wrap the error as a Boom error response, include the additional information from ES, and return it
|
||||
if (!statusCodeToMessageMap[statusCode]) {
|
||||
// const boomError = Boom.boomify(err, { statusCode });
|
||||
const error: any = { statusCode };
|
||||
|
||||
// The caused_by chain has the most information so use that if it's available. If not then
|
||||
// settle for the root_cause.
|
||||
const causedByChain = extractCausedByChain(caused_by);
|
||||
const defaultCause = root_cause.length ? extractCausedByChain(root_cause[0]) : undefined;
|
||||
|
||||
error.cause = causedByChain.length ? causedByChain : defaultCause;
|
||||
return error;
|
||||
}
|
||||
|
||||
// Otherwise, use the custom message to create a Boom error response and
|
||||
// return it
|
||||
const message = statusCodeToMessageMap[statusCode];
|
||||
return { message, statusCode };
|
||||
};
|
|
@ -5,10 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CallAsCurrentUser } from '../types';
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
import { Index } from '../index';
|
||||
|
||||
export type Enricher = (indices: Index[], callAsCurrentUser: CallAsCurrentUser) => Promise<Index[]>;
|
||||
export type Enricher = (indices: Index[], client: IScopedClusterClient) => Promise<Index[]>;
|
||||
|
||||
export class IndexDataEnricher {
|
||||
private readonly _enrichers: Enricher[] = [];
|
||||
|
@ -19,14 +19,14 @@ export class IndexDataEnricher {
|
|||
|
||||
public enrichIndices = async (
|
||||
indices: Index[],
|
||||
callAsCurrentUser: CallAsCurrentUser
|
||||
client: IScopedClusterClient
|
||||
): Promise<Index[]> => {
|
||||
let enrichedIndices = indices;
|
||||
|
||||
for (let i = 0; i < this.enrichers.length; i++) {
|
||||
const dataEnricher = this.enrichers[i];
|
||||
try {
|
||||
const dataEnricherResponse = await dataEnricher(enrichedIndices, callAsCurrentUser);
|
||||
const dataEnricherResponse = await dataEnricher(enrichedIndices, client);
|
||||
enrichedIndices = dataEnricherResponse;
|
||||
} catch (e) {
|
||||
// silently swallow enricher response errors
|
||||
|
|
|
@ -5,8 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export {
|
||||
isEsError,
|
||||
parseEsError,
|
||||
handleEsError,
|
||||
} from '../../../../src/plugins/es_ui_shared/server';
|
||||
export { handleEsError } from '../../../../src/plugins/es_ui_shared/server';
|
||||
|
|
|
@ -5,17 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
LegacyScopedClusterClient,
|
||||
ILegacyScopedClusterClient,
|
||||
IRouter,
|
||||
RequestHandlerContext,
|
||||
} from 'src/core/server';
|
||||
import { IRouter } from 'src/core/server';
|
||||
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
|
||||
import { LicensingPluginSetup } from '../../licensing/server';
|
||||
import { SecurityPluginSetup } from '../../security/server';
|
||||
import { IndexDataEnricher } from './services';
|
||||
import { isEsError, parseEsError, handleEsError } from './shared_imports';
|
||||
import { handleEsError } from './shared_imports';
|
||||
|
||||
export interface Dependencies {
|
||||
security: SecurityPluginSetup;
|
||||
|
@ -24,39 +20,12 @@ export interface Dependencies {
|
|||
}
|
||||
|
||||
export interface RouteDependencies {
|
||||
router: IndexManagementRouter;
|
||||
router: IRouter;
|
||||
config: {
|
||||
isSecurityEnabled: () => boolean;
|
||||
};
|
||||
indexDataEnricher: IndexDataEnricher;
|
||||
lib: {
|
||||
isEsError: typeof isEsError;
|
||||
parseEsError: typeof parseEsError;
|
||||
handleEsError: typeof handleEsError;
|
||||
};
|
||||
}
|
||||
|
||||
export type CallAsCurrentUser = LegacyScopedClusterClient['callAsCurrentUser'];
|
||||
|
||||
export interface DataManagementContext {
|
||||
client: ILegacyScopedClusterClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IndexManagementApiRequestHandlerContext {
|
||||
client: ILegacyScopedClusterClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IndexManagementRequestHandlerContext extends RequestHandlerContext {
|
||||
dataManagement: IndexManagementApiRequestHandlerContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type IndexManagementRouter = IRouter<IndexManagementRequestHandlerContext>;
|
||||
|
|
|
@ -5,20 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IScopedClusterClient } from 'kibana/server';
|
||||
import { Index } from '../../../plugins/index_management/server';
|
||||
|
||||
export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => {
|
||||
export const rollupDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => {
|
||||
if (!indicesList || !indicesList.length) {
|
||||
return Promise.resolve(indicesList);
|
||||
}
|
||||
|
||||
const params = {
|
||||
path: '/_all/_rollup/data',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
try {
|
||||
const rollupJobData = await callWithRequest('transport.request', params);
|
||||
const { body: rollupJobData } = await client.asCurrentUser.rollup.getRollupIndexCaps({
|
||||
index: '_all',
|
||||
});
|
||||
|
||||
return indicesList.map((index) => {
|
||||
const isRollupIndex = !!rollupJobData[index.name];
|
||||
return {
|
||||
|
|
|
@ -281,8 +281,19 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body).to.eql({
|
||||
statusCode: 404,
|
||||
error: 'Not Found',
|
||||
message:
|
||||
'[resource_not_found_exception] component template matching [component_does_not_exist] not found',
|
||||
message: 'component template matching [component_does_not_exist] not found',
|
||||
attributes: {
|
||||
error: {
|
||||
reason: 'component template matching [component_does_not_exist] not found',
|
||||
root_cause: [
|
||||
{
|
||||
reason: 'component template matching [component_does_not_exist] not found',
|
||||
type: 'resource_not_found_exception',
|
||||
},
|
||||
],
|
||||
type: 'resource_not_found_exception',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -356,10 +367,19 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const uri = `${API_BASE_PATH}/component_templates/${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}`;
|
||||
|
||||
const { body } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200);
|
||||
|
||||
expect(body.itemsDeleted).to.eql([componentTemplateName]);
|
||||
expect(body.errors[0].name).to.eql(COMPONENT_DOES_NOT_EXIST);
|
||||
expect(body.errors[0].error.msg).to.contain('resource_not_found_exception');
|
||||
|
||||
expect(body.errors[0].error.payload.attributes.error).to.eql({
|
||||
root_cause: [
|
||||
{
|
||||
type: 'resource_not_found_exception',
|
||||
reason: 'component_does_not_exist',
|
||||
},
|
||||
],
|
||||
type: 'resource_not_found_exception',
|
||||
reason: 'component_does_not_exist',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@ import { DataStream } from '../../../../../plugins/index_management/common';
|
|||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('legacyEs');
|
||||
const es = getService('es');
|
||||
|
||||
const createDataStream = async (name: string) => {
|
||||
// A data stream requires an index template before it can be created.
|
||||
await es.dataManagement.saveComposableIndexTemplate({
|
||||
await es.indices.putIndexTemplate({
|
||||
name,
|
||||
body: {
|
||||
// We need to match the names of backing indices with this template.
|
||||
|
@ -36,15 +36,15 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
},
|
||||
});
|
||||
|
||||
await es.dataManagement.createDataStream({ name });
|
||||
await es.indices.createDataStream({ name });
|
||||
};
|
||||
|
||||
const deleteComposableIndexTemplate = async (name: string) => {
|
||||
await es.dataManagement.deleteComposableIndexTemplate({ name });
|
||||
await es.indices.deleteIndexTemplate({ name });
|
||||
};
|
||||
|
||||
const deleteDataStream = async (name: string) => {
|
||||
await es.dataManagement.deleteDataStream({ name });
|
||||
await es.indices.deleteDataStream({ name });
|
||||
await deleteComposableIndexTemplate(name);
|
||||
};
|
||||
|
||||
|
|
|
@ -56,13 +56,17 @@ export default function ({ getService }) {
|
|||
const index = await createIndex();
|
||||
|
||||
// Make sure the index is open
|
||||
const [cat1] = await catIndex(index);
|
||||
const {
|
||||
body: [cat1],
|
||||
} = await catIndex(index);
|
||||
expect(cat1.status).to.be('open');
|
||||
|
||||
await closeIndex(index).expect(200);
|
||||
|
||||
// Make sure the index has been closed
|
||||
const [cat2] = await catIndex(index);
|
||||
const {
|
||||
body: [cat2],
|
||||
} = await catIndex(index);
|
||||
expect(cat2.status).to.be('close');
|
||||
});
|
||||
});
|
||||
|
@ -78,13 +82,17 @@ export default function ({ getService }) {
|
|||
await closeIndex(index);
|
||||
|
||||
// Make sure the index is closed
|
||||
const [cat1] = await catIndex(index);
|
||||
const {
|
||||
body: [cat1],
|
||||
} = await catIndex(index);
|
||||
expect(cat1.status).to.be('close');
|
||||
|
||||
await openIndex(index).expect(200);
|
||||
|
||||
// Make sure the index is opened
|
||||
const [cat2] = await catIndex(index);
|
||||
const {
|
||||
body: [cat2],
|
||||
} = await catIndex(index);
|
||||
expect(cat2.status).to.be('open');
|
||||
});
|
||||
});
|
||||
|
@ -93,12 +101,12 @@ export default function ({ getService }) {
|
|||
it('should delete an index', async () => {
|
||||
const index = await createIndex();
|
||||
|
||||
const indices1 = await catIndex(undefined, 'i');
|
||||
const { body: indices1 } = await catIndex(undefined, 'i');
|
||||
expect(indices1.map((index) => index.i)).to.contain(index);
|
||||
|
||||
await deleteIndex([index]).expect(200);
|
||||
|
||||
const indices2 = await catIndex(undefined, 'i');
|
||||
const { body: indices2 } = await catIndex(undefined, 'i');
|
||||
expect(indices2.map((index) => index.i)).not.to.contain(index);
|
||||
});
|
||||
|
||||
|
@ -112,12 +120,16 @@ export default function ({ getService }) {
|
|||
it('should flush an index', async () => {
|
||||
const index = await createIndex();
|
||||
|
||||
const { indices: indices1 } = await indexStats(index, 'flush');
|
||||
const {
|
||||
body: { indices: indices1 },
|
||||
} = await indexStats(index, 'flush');
|
||||
expect(indices1[index].total.flush.total).to.be(0);
|
||||
|
||||
await flushIndex(index).expect(200);
|
||||
|
||||
const { indices: indices2 } = await indexStats(index, 'flush');
|
||||
const {
|
||||
body: { indices: indices2 },
|
||||
} = await indexStats(index, 'flush');
|
||||
expect(indices2[index].total.flush.total).to.be(1);
|
||||
});
|
||||
});
|
||||
|
@ -126,12 +138,16 @@ export default function ({ getService }) {
|
|||
it('should refresh an index', async () => {
|
||||
const index = await createIndex();
|
||||
|
||||
const { indices: indices1 } = await indexStats(index, 'refresh');
|
||||
const {
|
||||
body: { indices: indices1 },
|
||||
} = await indexStats(index, 'refresh');
|
||||
const previousRefreshes = indices1[index].total.refresh.total;
|
||||
|
||||
await refreshIndex(index).expect(200);
|
||||
|
||||
const { indices: indices2 } = await indexStats(index, 'refresh');
|
||||
const {
|
||||
body: { indices: indices2 },
|
||||
} = await indexStats(index, 'refresh');
|
||||
expect(indices2[index].total.refresh.total).to.be(previousRefreshes + 1);
|
||||
});
|
||||
});
|
||||
|
@ -153,12 +169,16 @@ export default function ({ getService }) {
|
|||
const index = await createIndex();
|
||||
// "sth" correspond to search throttling. Frozen indices are normal indices
|
||||
// with search throttling turned on.
|
||||
const [cat1] = await catIndex(index, 'sth');
|
||||
const {
|
||||
body: [cat1],
|
||||
} = await catIndex(index, 'sth');
|
||||
expect(cat1.sth).to.be('false');
|
||||
|
||||
await freeze(index).expect(200);
|
||||
|
||||
const [cat2] = await catIndex(index, 'sth');
|
||||
const {
|
||||
body: [cat2],
|
||||
} = await catIndex(index, 'sth');
|
||||
expect(cat2.sth).to.be('true');
|
||||
});
|
||||
});
|
||||
|
@ -168,11 +188,15 @@ export default function ({ getService }) {
|
|||
const index = await createIndex();
|
||||
|
||||
await freeze(index).expect(200);
|
||||
const [cat1] = await catIndex(index, 'sth');
|
||||
const {
|
||||
body: [cat1],
|
||||
} = await catIndex(index, 'sth');
|
||||
expect(cat1.sth).to.be('true');
|
||||
|
||||
await unfreeze(index).expect(200);
|
||||
const [cat2] = await catIndex(index, 'sth');
|
||||
const {
|
||||
body: [cat2],
|
||||
} = await catIndex(index, 'sth');
|
||||
expect(cat2.sth).to.be('false');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import { getRandomString } from './random';
|
|||
* @param {ElasticsearchClient} es The Elasticsearch client instance
|
||||
*/
|
||||
export const initElasticsearchHelpers = (getService) => {
|
||||
const es = getService('legacyEs');
|
||||
const es = getService('es');
|
||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||
|
||||
let indicesCreated = [];
|
||||
|
@ -42,11 +42,11 @@ export const initElasticsearchHelpers = (getService) => {
|
|||
componentTemplatesCreated.push(componentTemplate.name);
|
||||
}
|
||||
|
||||
return es.dataManagement.saveComponentTemplate(componentTemplate);
|
||||
return es.cluster.putComponentTemplate(componentTemplate);
|
||||
};
|
||||
|
||||
const deleteComponentTemplate = (componentTemplateName) => {
|
||||
return es.dataManagement.deleteComponentTemplate({ name: componentTemplateName });
|
||||
return es.cluster.deleteComponentTemplate({ name: componentTemplateName });
|
||||
};
|
||||
|
||||
const cleanUpComponentTemplates = () =>
|
||||
|
|
|
@ -193,8 +193,8 @@ export default function ({ getService }) {
|
|||
});
|
||||
|
||||
it('should parse the ES error and return the cause', async () => {
|
||||
const templateName = `template-${getRandomString()}`;
|
||||
const payload = getTemplatePayload(templateName, [getRandomString()]);
|
||||
const templateName = `template-create-parse-es-error}`;
|
||||
const payload = getTemplatePayload(templateName, ['create-parse-es-error']);
|
||||
const runtime = {
|
||||
myRuntimeField: {
|
||||
type: 'boolean',
|
||||
|
@ -207,9 +207,9 @@ export default function ({ getService }) {
|
|||
const { body } = await createTemplate(payload).expect(400);
|
||||
|
||||
expect(body.attributes).an('object');
|
||||
expect(body.attributes.message).contain('template after composition is invalid');
|
||||
expect(body.attributes.error.reason).contain('template after composition is invalid');
|
||||
// one of the item of the cause array should point to our script
|
||||
expect(body.attributes.cause.join(',')).contain('"hello with error');
|
||||
expect(body.attributes.causes.join(',')).contain('"hello with error');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -220,7 +220,7 @@ export default function ({ getService }) {
|
|||
|
||||
await createTemplate(indexTemplate).expect(200);
|
||||
|
||||
let catTemplateResponse = await catTemplate(templateName);
|
||||
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||
|
||||
const { name, version } = indexTemplate;
|
||||
|
||||
|
@ -234,7 +234,7 @@ export default function ({ getService }) {
|
|||
200
|
||||
);
|
||||
|
||||
catTemplateResponse = await catTemplate(templateName);
|
||||
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||
|
||||
expect(
|
||||
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
||||
|
@ -247,7 +247,7 @@ export default function ({ getService }) {
|
|||
|
||||
await createTemplate(legacyIndexTemplate).expect(200);
|
||||
|
||||
let catTemplateResponse = await catTemplate(templateName);
|
||||
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||
|
||||
const { name, version } = legacyIndexTemplate;
|
||||
|
||||
|
@ -262,7 +262,7 @@ export default function ({ getService }) {
|
|||
templateName
|
||||
).expect(200);
|
||||
|
||||
catTemplateResponse = await catTemplate(templateName);
|
||||
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||
|
||||
expect(
|
||||
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
||||
|
@ -270,8 +270,8 @@ export default function ({ getService }) {
|
|||
});
|
||||
|
||||
it('should parse the ES error and return the cause', async () => {
|
||||
const templateName = `template-${getRandomString()}`;
|
||||
const payload = getTemplatePayload(templateName, [getRandomString()]);
|
||||
const templateName = `template-update-parse-es-error}`;
|
||||
const payload = getTemplatePayload(templateName, ['update-parse-es-error']);
|
||||
const runtime = {
|
||||
myRuntimeField: {
|
||||
type: 'keyword',
|
||||
|
@ -292,7 +292,7 @@ export default function ({ getService }) {
|
|||
|
||||
expect(body.attributes).an('object');
|
||||
// one of the item of the cause array should point to our script
|
||||
expect(body.attributes.cause.join(',')).contain('"hello with error');
|
||||
expect(body.attributes.causes.join(',')).contain('"hello with error');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -306,7 +306,7 @@ export default function ({ getService }) {
|
|||
throw new Error(`Error creating template: ${createStatus} ${createBody.message}`);
|
||||
}
|
||||
|
||||
let catTemplateResponse = await catTemplate(templateName);
|
||||
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||
|
||||
expect(
|
||||
catTemplateResponse.find((template) => template.name === payload.name).name
|
||||
|
@ -322,7 +322,7 @@ export default function ({ getService }) {
|
|||
expect(deleteBody.errors).to.be.empty;
|
||||
expect(deleteBody.templatesDeleted[0]).to.equal(templateName);
|
||||
|
||||
catTemplateResponse = await catTemplate(templateName);
|
||||
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||
|
||||
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
||||
undefined
|
||||
|
@ -335,7 +335,7 @@ export default function ({ getService }) {
|
|||
|
||||
await createTemplate(payload).expect(200);
|
||||
|
||||
let catTemplateResponse = await catTemplate(templateName);
|
||||
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||
|
||||
expect(
|
||||
catTemplateResponse.find((template) => template.name === payload.name).name
|
||||
|
@ -348,7 +348,7 @@ export default function ({ getService }) {
|
|||
expect(body.errors).to.be.empty;
|
||||
expect(body.templatesDeleted[0]).to.equal(templateName);
|
||||
|
||||
catTemplateResponse = await catTemplate(templateName);
|
||||
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||
|
||||
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
||||
undefined
|
||||
|
|
|
@ -9,7 +9,6 @@ import { format as formatUrl } from 'url';
|
|||
|
||||
import * as legacyElasticsearch from 'elasticsearch';
|
||||
|
||||
import { elasticsearchJsPlugin as indexManagementEsClientPlugin } from '../../../plugins/index_management/server/client/elasticsearch';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config';
|
||||
|
||||
|
@ -20,6 +19,5 @@ export function LegacyEsProvider({ getService }) {
|
|||
apiVersion: DEFAULT_API_VERSION,
|
||||
host: formatUrl(config.get('servers.elasticsearch')),
|
||||
requestTimeout: config.get('timeouts.esRequestTimeout'),
|
||||
plugins: [indexManagementEsClientPlugin],
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue