[7.x] [Index Patterns] Runtime fields CRUD REST API (#101164) (#101770)

* [Index Patterns] Runtime fields CRUD REST API  (#101164)

Part of index pattern REST API
# Conflicts:
#	docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md
#	docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md

* fix bad docs merge
This commit is contained in:
Anton Dosov 2021-06-09 18:37:53 +02:00 committed by GitHub
parent e4a251d043
commit af45343bee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 1993 additions and 19 deletions

View file

@ -20,6 +20,12 @@ The following index patterns APIs are available:
** <<index-patterns-api-default-set, Set default index pattern API>> to set a default index pattern
* Fields
** <<index-patterns-fields-api-update, Update index pattern field>> to change field metadata, such as `count`, `customLabel` and `format`
* Runtime fields
** <<index-patterns-runtime-field-api-get, Get runtime field API>> to retrieve a runtime field
** <<index-patterns-runtime-field-api-create, Create runtime field API>> to create a runtime field
** <<index-patterns-runtime-field-api-upsert, Upsert runtime field API>> to create or update a runtime field
** <<index-patterns-runtime-field-api-update, Update runtime field API>> to partially update an existing runtime field
** <<index-patterns-runtime-field-api-delete, Delete runtime field API>> to delete a runtime field
include::index-patterns/get.asciidoc[]
include::index-patterns/create.asciidoc[]
@ -28,3 +34,9 @@ include::index-patterns/delete.asciidoc[]
include::index-patterns/default-get.asciidoc[]
include::index-patterns/default-set.asciidoc[]
include::index-patterns/update-fields.asciidoc[]
include::index-patterns/runtime-fields/get.asciidoc[]
include::index-patterns/runtime-fields/create.asciidoc[]
include::index-patterns/runtime-fields/upsert.asciidoc[]
include::index-patterns/runtime-fields/update.asciidoc[]
include::index-patterns/runtime-fields/delete.asciidoc[]

View file

@ -84,6 +84,7 @@ $ curl -X POST api/index_patterns/index_pattern
"typeMeta": {},
"fieldFormats": {},
"fieldAttrs": {},
"runtimeFieldMap": {}
"allowNoIndex": "..."
}
}

View file

@ -58,6 +58,7 @@ The API returns an index pattern object:
"typeMeta": {},
"fieldFormats": {},
"fieldAttrs": {},
"runtimeFieldMap" {},
"allowNoIndex: "..."
}
}

View file

@ -0,0 +1,61 @@
[[index-patterns-runtime-field-api-create]]
=== Create runtime field API
++++
<titleabbrev>Create runtime field</titleabbrev>
++++
experimental[] Create a runtime field
[[index-patterns-runtime-field-create-request]]
==== Request
`POST <kibana host>:<port>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field`
`POST <kibana host>:<port>/s/<space_id>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field`
[[index-patterns-runtime-field-create-params]]
==== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
`index_pattern_id`::
(Required, string) The ID of the index pattern.
[[index-patterns-runtime-field-create-body]]
==== Request body
`name`:: (Required, string) The name for a runtime field.
`runtimeField`:: (Required, object) The runtime field definition object.
[[index-patterns-runtime-field-create-example]]
==== Examples
Create a runtime field on an index pattern:
[source,sh]
--------------------------------------------------
$ curl -X POST api/index_patterns/index_pattern/<index_pattern_id>/runtime_field
{
"name": "runtimeFoo",
"runtimeField": {
"type": "long",
"script": {
"source": "emit(doc["foo"].value)"
}
}
}
--------------------------------------------------
// KIBANA
The API returns created runtime field object and update index pattern object:
[source,sh]
--------------------------------------------------
{
"index_pattern": {...},
"field": {...}
}
--------------------------------------------------

View file

@ -0,0 +1,37 @@
[[index-patterns-runtime-field-api-delete]]
=== Delete runtime field API
++++
<titleabbrev>Delete runtime field</titleabbrev>
++++
experimental[] Delete a runtime field from an index pattern.
[[index-patterns-runtime-field-api-delete-request]]
==== Request
`DELETE <kibana host>:<port>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
`DELETE <kibana host>:<port>/s/<space_id>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
[[index-patterns-runtime-field-api-delete-path-params]]
==== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
`index_pattern_id`::
(Required, string) The ID of the index pattern your want to delete a runtime field from.
`name`::
(Required, string) The name of the runtime field you want to delete.
==== Example
Delete a runtime field from an index pattern:
[source,sh]
--------------------------------------------------
$ curl -X DELETE api/index_patterns/index_pattern/<my-pattern>/runtime_field/<runtime-field-name>
--------------------------------------------------
// KIBANA

View file

@ -0,0 +1,52 @@
[[index-patterns-runtime-field-api-get]]
=== Get runtime field API
++++
<titleabbrev>Get runtime field</titleabbrev>
++++
experimental[] Get a runtime field
[[index-patterns-runtime-field-get-request]]
==== Request
`GET <kibana host>:<port>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
`GET <kibana host>:<port>/s/<space_id>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
[[index-patterns-runtime-field-get-params]]
==== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
`index_pattern_id`::
(Required, string) The ID of the index pattern.
`name`::
(Required, string) The name of the runtime field you want to retrieve.
[[index-patterns-runtime-field-get-example]]
==== Example
Retrieve a runtime field named `foo` of index pattern with the `my-pattern` ID:
[source,sh]
--------------------------------------------------
$ curl -X GET api/index_patterns/index_pattern/my-pattern/runtime_field/foo
--------------------------------------------------
// KIBANA
The API returns a runtime `field` object, and a `runtimeField` definition object:
[source,sh]
--------------------------------------------------
{
"field": {
...
},
"runtimeField": {
...
}
}
--------------------------------------------------

View file

@ -0,0 +1,66 @@
[[index-patterns-runtime-field-api-update]]
=== Update runtime field API
++++
<titleabbrev>Update runtime field</titleabbrev>
++++
experimental[] Update an existing runtime field
[[index-patterns-runtime-field-update-request]]
==== Request
`POST <kibana host>:<port>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
`POST <kibana host>:<port>/s/<space_id>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<name>`
[[index-patterns-runtime-field-update-params]]
==== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
`index_pattern_id`::
(Required, string) The ID of the index pattern.
`name`::
(Required, string) The name of the runtime field you want to update.
[[index-patterns-runtime-field-update-body]]
==== Request body
`runtimeField`:: (Required, object) The runtime field definition object.
You can update following fields:
* `type`
* `script`
[[index-patterns-runtime-field-update-example]]
==== Examples
Update an existing runtime field on an index pattern:
[source,sh]
--------------------------------------------------
$ curl -X POST api/index_patterns/index_pattern/<index_pattern_id>/runtime_field/<runtime_field_name>
{
"runtimeField": {
"script": {
"source": "emit(doc["bar"].value)"
}
}
}
--------------------------------------------------
// KIBANA
The API returns updated runtime field object and updated index pattern object:
[source,sh]
--------------------------------------------------
{
"index_pattern": {...},
"field": {...}
}
--------------------------------------------------

View file

@ -0,0 +1,61 @@
[[index-patterns-runtime-field-api-upsert]]
=== Upsert runtime field API
++++
<titleabbrev>Upsert runtime field</titleabbrev>
++++
experimental[] Create or update an existing runtime field
[[index-patterns-runtime-field-upsert-request]]
==== Request
`PUT <kibana host>:<port>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field`
`PUT <kibana host>:<port>/s/<space_id>/api/index_patterns/index_pattern/<index_pattern_id>/runtime_field`
[[index-patterns-runtime-field-upsert-params]]
==== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
`index_pattern_id`::
(Required, string) The ID of the index pattern.
[[index-patterns-runtime-field-upsert-body]]
==== Request body
`name`:: (Required, string) The name for a new runtime field or a name of an existing runtime field.
`runtimeField`:: (Required, object) The runtime field definition object.
[[index-patterns-runtime-field-upsert-example]]
==== Examples
Create or update an existing runtime field on an index pattern:
[source,sh]
--------------------------------------------------
$ curl -X PUT api/index_patterns/index_pattern/<index_pattern_id>/runtime_field
{
"name": "runtimeFoo",
"runtimeField": {
"type": "long",
"script": {
"source": "emit(doc["foo"].value)"
}
}
}
--------------------------------------------------
// KIBANA
The API returns created or updated runtime field object and updated index pattern object:
[source,sh]
--------------------------------------------------
{
"index_pattern": {...},
"field": {...}
}
--------------------------------------------------

View file

@ -93,7 +93,8 @@ $ curl -X POST api/saved_objects/index-pattern/my-pattern
"fieldFormats": {},
"type": "...",
"typeMeta": {},
"fields": {}
"fields": {},
"runtimeFieldMap": {}
}
}
--------------------------------------------------

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [getRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md)
## IndexPattern.getRuntimeField() method
Returns runtime field if exists
<b>Signature:</b>
```typescript
getRuntimeField(name: string): RuntimeField | null;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
<b>Returns:</b>
`RuntimeField | null`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [hasRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md)
## IndexPattern.hasRuntimeField() method
Checks if runtime field exists
<b>Signature:</b>
```typescript
hasRuntimeField(name: string): boolean;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
<b>Returns:</b>
`boolean`

View file

@ -55,14 +55,17 @@ export declare class IndexPattern implements IIndexPattern
| [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists |
| [getIndex()](./kibana-plugin-plugins-data-public.indexpattern.getindex.md) | | |
| [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | |
| [getRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md) | | Returns runtime field if exists |
| [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | |
| [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. |
| [getTimeField()](./kibana-plugin-plugins-data-public.indexpattern.gettimefield.md) | | |
| [hasRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md) | | Checks if runtime field exists |
| [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | |
| [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | |
| [isUnsupportedTimePattern()](./kibana-plugin-plugins-data-public.indexpattern.isunsupportedtimepattern.md) | | |
| [removeRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate |
| [removeRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. |
| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | Remove scripted field from field list |
| [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields |
| [setFieldAttrs(fieldName, attrName, value)](./kibana-plugin-plugins-data-public.indexpattern.setfieldattrs.md) | | |
| [setFieldCount(fieldName, count)](./kibana-plugin-plugins-data-public.indexpattern.setfieldcount.md) | | |
| [setFieldCustomLabel(fieldName, customLabel)](./kibana-plugin-plugins-data-public.indexpattern.setfieldcustomlabel.md) | | |

View file

@ -4,7 +4,7 @@
## IndexPattern.removeRuntimeField() method
Remove a runtime field - removed from mapped field or removed unmapped field as appropriate
Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes.
<b>Signature:</b>
@ -16,7 +16,7 @@ removeRuntimeField(name: string): void;
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
| name | <code>string</code> | Field name to remove |
<b>Returns:</b>

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [replaceAllRuntimeFields](./kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md)
## IndexPattern.replaceAllRuntimeFields() method
Replaces all existing runtime fields with new fields
<b>Signature:</b>
```typescript
replaceAllRuntimeFields(newFields: Record<string, RuntimeField>): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| newFields | <code>Record&lt;string, RuntimeField&gt;</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) &gt; [getRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md)
## IndexPattern.getRuntimeField() method
Returns runtime field if exists
<b>Signature:</b>
```typescript
getRuntimeField(name: string): RuntimeField | null;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
<b>Returns:</b>
`RuntimeField | null`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) &gt; [hasRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md)
## IndexPattern.hasRuntimeField() method
Checks if runtime field exists
<b>Signature:</b>
```typescript
hasRuntimeField(name: string): boolean;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
<b>Returns:</b>
`boolean`

View file

@ -55,14 +55,17 @@ export declare class IndexPattern implements IIndexPattern
| [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists |
| [getIndex()](./kibana-plugin-plugins-data-server.indexpattern.getindex.md) | | |
| [getNonScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) | | |
| [getRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md) | | Returns runtime field if exists |
| [getScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) | | |
| [getSourceFiltering()](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. |
| [getTimeField()](./kibana-plugin-plugins-data-server.indexpattern.gettimefield.md) | | |
| [hasRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md) | | Checks if runtime field exists |
| [isTimeBased()](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) | | |
| [isTimeNanosBased()](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) | | |
| [isUnsupportedTimePattern()](./kibana-plugin-plugins-data-server.indexpattern.isunsupportedtimepattern.md) | | |
| [removeRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate |
| [removeRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. |
| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) | | Remove scripted field from field list |
| [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields |
| [setFieldAttrs(fieldName, attrName, value)](./kibana-plugin-plugins-data-server.indexpattern.setfieldattrs.md) | | |
| [setFieldCount(fieldName, count)](./kibana-plugin-plugins-data-server.indexpattern.setfieldcount.md) | | |
| [setFieldCustomLabel(fieldName, customLabel)](./kibana-plugin-plugins-data-server.indexpattern.setfieldcustomlabel.md) | | |

View file

@ -4,7 +4,7 @@
## IndexPattern.removeRuntimeField() method
Remove a runtime field - removed from mapped field or removed unmapped field as appropriate
Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes.
<b>Signature:</b>
@ -16,7 +16,7 @@ removeRuntimeField(name: string): void;
| Parameter | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | |
| name | <code>string</code> | Field name to remove |
<b>Returns:</b>

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) &gt; [replaceAllRuntimeFields](./kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md)
## IndexPattern.replaceAllRuntimeFields() method
Replaces all existing runtime fields with new fields
<b>Signature:</b>
```typescript
replaceAllRuntimeFields(newFields: Record<string, RuntimeField>): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| newFields | <code>Record&lt;string, RuntimeField&gt;</code> | |
<b>Returns:</b>
`void`

View file

@ -387,7 +387,6 @@ export class IndexPattern implements IIndexPattern {
* @param name Field name
* @param runtimeField Runtime field definition
*/
addRuntimeField(name: string, runtimeField: RuntimeField) {
const existingField = this.getFieldByName(name);
if (existingField) {
@ -407,11 +406,41 @@ export class IndexPattern implements IIndexPattern {
}
/**
* Remove a runtime field - removed from mapped field or removed unmapped
* field as appropriate
* @param name Field name
* Checks if runtime field exists
* @param name
*/
hasRuntimeField(name: string): boolean {
return !!this.runtimeFieldMap[name];
}
/**
* Returns runtime field if exists
* @param name
*/
getRuntimeField(name: string): RuntimeField | null {
return this.runtimeFieldMap[name] ?? null;
}
/**
* Replaces all existing runtime fields with new fields
* @param newFields
*/
replaceAllRuntimeFields(newFields: Record<string, RuntimeField>) {
const oldRuntimeFieldNames = Object.keys(this.runtimeFieldMap);
oldRuntimeFieldNames.forEach((name) => {
this.removeRuntimeField(name);
});
Object.entries(newFields).forEach(([name, field]) => {
this.addRuntimeField(name, field);
});
}
/**
* Remove a runtime field - removed from mapped field or removed unmapped
* field as appropriate. Doesn't clear associated field attributes.
* @param name - Field name to remove
*/
removeRuntimeField(name: string) {
const existingField = this.getFieldByName(name);
if (existingField) {
@ -419,9 +448,6 @@ export class IndexPattern implements IIndexPattern {
// mapped field, remove runtimeField def
existingField.runtimeField = undefined;
} else {
// runtimeField only
this.setFieldCustomLabel(name, null);
this.deleteFieldFormat(name);
this.fields.remove(existingField);
}
}

View file

@ -1417,6 +1417,7 @@ export class IndexPattern implements IIndexPattern {
typeMeta?: string | undefined;
type?: string | undefined;
};
getRuntimeField(name: string): RuntimeField | null;
// @deprecated (undocumented)
getScriptedFields(): IndexPatternField[];
getSourceFiltering(): {
@ -1424,6 +1425,7 @@ export class IndexPattern implements IIndexPattern {
};
// (undocumented)
getTimeField(): IndexPatternField | undefined;
hasRuntimeField(name: string): boolean;
// (undocumented)
id?: string;
// @deprecated (undocumented)
@ -1439,6 +1441,7 @@ export class IndexPattern implements IIndexPattern {
removeRuntimeField(name: string): void;
// @deprecated
removeScriptedField(fieldName: string): void;
replaceAllRuntimeFields(newFields: Record<string, RuntimeField>): void;
resetOriginalSavedObjectBody: () => void;
// (undocumented)
protected setFieldAttrs<K extends keyof FieldAttrSet>(fieldName: string, attrName: K, value: FieldAttrSet[K]): void;

View file

@ -21,6 +21,11 @@ import { registerDeleteScriptedFieldRoute } from './routes/scripted_fields/delet
import { registerUpdateScriptedFieldRoute } from './routes/scripted_fields/update_scripted_field';
import type { DataPluginStart, DataPluginStartDependencies } from '../plugin';
import { registerManageDefaultIndexPatternRoutes } from './routes/default_index_pattern';
import { registerCreateRuntimeFieldRoute } from './routes/runtime_fields/create_runtime_field';
import { registerGetRuntimeFieldRoute } from './routes/runtime_fields/get_runtime_field';
import { registerDeleteRuntimeFieldRoute } from './routes/runtime_fields/delete_runtime_field';
import { registerPutRuntimeFieldRoute } from './routes/runtime_fields/put_runtime_field';
import { registerUpdateRuntimeFieldRoute } from './routes/runtime_fields/update_runtime_field';
export function registerRoutes(
http: HttpServiceSetup,
@ -55,6 +60,13 @@ export function registerRoutes(
registerDeleteScriptedFieldRoute(router, getStartServices);
registerUpdateScriptedFieldRoute(router, getStartServices);
// Runtime Fields API
registerCreateRuntimeFieldRoute(router, getStartServices);
registerGetRuntimeFieldRoute(router, getStartServices);
registerDeleteRuntimeFieldRoute(router, getStartServices);
registerPutRuntimeFieldRoute(router, getStartServices);
registerUpdateRuntimeFieldRoute(router, getStartServices);
router.get(
{
path: '/api/index_patterns/_fields_for_wildcard',

View file

@ -9,7 +9,11 @@
import { schema } from '@kbn/config-schema';
import { IndexPatternSpec } from 'src/plugins/data/common';
import { handleErrors } from './util/handle_errors';
import { fieldSpecSchema, serializedFieldFormatSchema } from './util/schemas';
import {
fieldSpecSchema,
runtimeFieldSpecSchema,
serializedFieldFormatSchema,
} from './util/schemas';
import { IRouter, StartServicesAccessor } from '../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../plugin';
@ -39,6 +43,7 @@ const indexPatternSpecSchema = schema.object({
)
),
allowNoIndex: schema.maybe(schema.boolean()),
runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSpecSchema)),
});
export const registerCreateIndexPatternRoute = (
@ -66,6 +71,7 @@ export const registerCreateIndexPatternRoute = (
elasticsearchClient
);
const body = req.body;
const indexPattern = await indexPatternsService.createAndSave(
body.index_pattern as IndexPatternSpec,
body.override,

View file

@ -0,0 +1,74 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { handleErrors } from '../util/handle_errors';
import { runtimeFieldSpecSchema } from '../util/schemas';
import { IRouter, StartServicesAccessor } from '../../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../../plugin';
export const registerCreateRuntimeFieldRoute = (
router: IRouter,
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>
) => {
router.post(
{
path: '/api/index_patterns/index_pattern/{id}/runtime_field',
validate: {
params: schema.object({
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
}),
body: schema.object({
name: schema.string({
minLength: 1,
maxLength: 1_000,
}),
runtimeField: runtimeFieldSpecSchema,
}),
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const [, , { indexPatterns }] = await getStartServices();
const indexPatternsService = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const id = req.params.id;
const { name, runtimeField } = req.body;
const indexPattern = await indexPatternsService.get(id);
if (indexPattern.fields.getByName(name)) {
throw new Error(`Field [name = ${name}] already exists.`);
}
indexPattern.addRuntimeField(name, runtimeField);
const addedField = indexPattern.fields.getByName(name);
if (!addedField) throw new Error(`Could not create a field [name = ${name}].`);
await indexPatternsService.updateSavedObject(indexPattern);
const savedField = indexPattern.fields.getByName(name);
if (!savedField) throw new Error(`Could not create a field [name = ${name}].`);
return res.ok({
body: {
field: savedField.toSpec(),
index_pattern: indexPattern.toSpec(),
},
});
})
);
};

View file

@ -0,0 +1,64 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { ErrorIndexPatternFieldNotFound } from '../../error';
import { handleErrors } from '../util/handle_errors';
import { IRouter, StartServicesAccessor } from '../../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../../plugin';
export const registerDeleteRuntimeFieldRoute = (
router: IRouter,
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>
) => {
router.delete(
{
path: '/api/index_patterns/index_pattern/{id}/runtime_field/{name}',
validate: {
params: schema.object({
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
name: schema.string({
minLength: 1,
maxLength: 1_000,
}),
}),
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const [, , { indexPatterns }] = await getStartServices();
const indexPatternsService = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const id = req.params.id;
const name = req.params.name;
const indexPattern = await indexPatternsService.get(id);
const field = indexPattern.fields.getByName(name);
if (!field) {
throw new ErrorIndexPatternFieldNotFound(id, name);
}
if (!field.runtimeField) {
throw new Error('Only runtime fields can be deleted.');
}
indexPattern.removeRuntimeField(name);
await indexPatternsService.updateSavedObject(indexPattern);
return res.ok();
})
);
};

View file

@ -0,0 +1,67 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { ErrorIndexPatternFieldNotFound } from '../../error';
import { handleErrors } from '../util/handle_errors';
import { IRouter, StartServicesAccessor } from '../../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../../plugin';
export const registerGetRuntimeFieldRoute = (
router: IRouter,
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>
) => {
router.get(
{
path: '/api/index_patterns/index_pattern/{id}/runtime_field/{name}',
validate: {
params: schema.object({
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
name: schema.string({
minLength: 1,
maxLength: 1_000,
}),
}),
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const [, , { indexPatterns }] = await getStartServices();
const indexPatternsService = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const id = req.params.id;
const name = req.params.name;
const indexPattern = await indexPatternsService.get(id);
const field = indexPattern.fields.getByName(name);
if (!field) {
throw new ErrorIndexPatternFieldNotFound(id, name);
}
if (!field.runtimeField) {
throw new Error('Only runtime fields can be retrieved.');
}
return res.ok({
body: {
field: field.toSpec(),
runtimeField: indexPattern.getRuntimeField(name),
},
});
})
);
};

View file

@ -0,0 +1,76 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { handleErrors } from '../util/handle_errors';
import { runtimeFieldSpecSchema } from '../util/schemas';
import { IRouter, StartServicesAccessor } from '../../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../../plugin';
export const registerPutRuntimeFieldRoute = (
router: IRouter,
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>
) => {
router.put(
{
path: '/api/index_patterns/index_pattern/{id}/runtime_field',
validate: {
params: schema.object({
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
}),
body: schema.object({
name: schema.string({
minLength: 1,
maxLength: 1_000,
}),
runtimeField: runtimeFieldSpecSchema,
}),
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const [, , { indexPatterns }] = await getStartServices();
const indexPatternsService = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const id = req.params.id;
const { name, runtimeField } = req.body;
const indexPattern = await indexPatternsService.get(id);
const oldFieldObject = indexPattern.fields.getByName(name);
if (oldFieldObject && !oldFieldObject.runtimeField) {
throw new Error('Only runtime fields can be updated');
}
if (oldFieldObject) {
indexPattern.removeRuntimeField(name);
}
indexPattern.addRuntimeField(name, runtimeField);
await indexPatternsService.updateSavedObject(indexPattern);
const fieldObject = indexPattern.fields.getByName(name);
if (!fieldObject) throw new Error(`Could not create a field [name = ${name}].`);
return res.ok({
body: {
field: fieldObject.toSpec(),
index_pattern: indexPattern.toSpec(),
},
});
})
);
};

View file

@ -0,0 +1,84 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { RuntimeField } from 'src/plugins/data/common';
import { ErrorIndexPatternFieldNotFound } from '../../error';
import { handleErrors } from '../util/handle_errors';
import { runtimeFieldSpec, runtimeFieldSpecTypeSchema } from '../util/schemas';
import { IRouter, StartServicesAccessor } from '../../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../../plugin';
export const registerUpdateRuntimeFieldRoute = (
router: IRouter,
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>
) => {
router.post(
{
path: '/api/index_patterns/index_pattern/{id}/runtime_field/{name}',
validate: {
params: schema.object({
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
name: schema.string({
minLength: 1,
maxLength: 1_000,
}),
}),
body: schema.object({
name: schema.never(),
runtimeField: schema.object({
...runtimeFieldSpec,
// We need to overwrite the below fields on top of `runtimeFieldSpec`,
// because some fields would be optional
type: schema.maybe(runtimeFieldSpecTypeSchema),
}),
}),
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const [, , { indexPatterns }] = await getStartServices();
const indexPatternsService = await indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const id = req.params.id;
const name = req.params.name;
const runtimeField = req.body.runtimeField as Partial<RuntimeField>;
const indexPattern = await indexPatternsService.get(id);
const existingRuntimeField = indexPattern.getRuntimeField(name);
if (!existingRuntimeField) {
throw new ErrorIndexPatternFieldNotFound(id, name);
}
indexPattern.removeRuntimeField(name);
indexPattern.addRuntimeField(name, {
...existingRuntimeField,
...runtimeField,
});
await indexPatternsService.updateSavedObject(indexPattern);
const fieldObject = indexPattern.fields.getByName(name);
if (!fieldObject) throw new Error(`Could not create a field [name = ${name}].`);
return res.ok({
body: {
field: fieldObject.toSpec(),
index_pattern: indexPattern.toSpec(),
},
});
})
);
};

View file

@ -8,7 +8,11 @@
import { schema } from '@kbn/config-schema';
import { handleErrors } from './util/handle_errors';
import { fieldSpecSchema, serializedFieldFormatSchema } from './util/schemas';
import {
fieldSpecSchema,
runtimeFieldSpecSchema,
serializedFieldFormatSchema,
} from './util/schemas';
import { IRouter, StartServicesAccessor } from '../../../../../core/server';
import type { DataPluginStart, DataPluginStartDependencies } from '../../plugin';
@ -28,6 +32,7 @@ const indexPatternUpdateSchema = schema.object({
fieldFormats: schema.maybe(schema.recordOf(schema.string(), serializedFieldFormatSchema)),
fields: schema.maybe(schema.recordOf(schema.string(), fieldSpecSchema)),
allowNoIndex: schema.maybe(schema.boolean()),
runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSpecSchema)),
});
export const registerUpdateIndexPatternRoute = (
@ -78,6 +83,7 @@ export const registerUpdateIndexPatternRoute = (
type,
typeMeta,
fields,
runtimeFieldMap,
},
} = req.body;
@ -131,6 +137,11 @@ export const registerUpdateIndexPatternRoute = (
);
}
if (runtimeFieldMap !== undefined) {
changeCount++;
indexPattern.replaceAllRuntimeFields(runtimeFieldMap);
}
if (changeCount < 1) {
throw new Error('Index pattern change set is empty.');
}

View file

@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
import { schema } from '@kbn/config-schema';
import { schema, Type } from '@kbn/config-schema';
import { RUNTIME_FIELD_TYPES, RuntimeType } from '../../../../common';
export const serializedFieldFormatSchema = schema.object({
id: schema.maybe(schema.string()),
@ -52,4 +53,24 @@ export const fieldSpecSchemaFields = {
shortDotsEnable: schema.maybe(schema.boolean()),
};
export const fieldSpecSchema = schema.object(fieldSpecSchemaFields);
export const fieldSpecSchema = schema.object(fieldSpecSchemaFields, {
// Allow and ignore unknowns to make fields transient.
// Because `fields` have a bunch of calculated fields
// this allows to retrieve an index pattern and then to re-create by using the retrieved payload
unknowns: 'ignore',
});
export const runtimeFieldSpecTypeSchema = schema.oneOf(
RUNTIME_FIELD_TYPES.map((runtimeFieldType) => schema.literal(runtimeFieldType)) as [
Type<RuntimeType>
]
);
export const runtimeFieldSpec = {
type: runtimeFieldSpecTypeSchema,
script: schema.maybe(
schema.object({
source: schema.string(),
})
),
};
export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec);

View file

@ -818,6 +818,7 @@ export class IndexPattern implements IIndexPattern {
typeMeta?: string | undefined;
type?: string | undefined;
};
getRuntimeField(name: string): RuntimeField | null;
// @deprecated (undocumented)
getScriptedFields(): IndexPatternField[];
getSourceFiltering(): {
@ -825,6 +826,7 @@ export class IndexPattern implements IIndexPattern {
};
// (undocumented)
getTimeField(): IndexPatternField | undefined;
hasRuntimeField(name: string): boolean;
// (undocumented)
id?: string;
// @deprecated (undocumented)
@ -840,6 +842,7 @@ export class IndexPattern implements IIndexPattern {
removeRuntimeField(name: string): void;
// @deprecated
removeScriptedField(fieldName: string): void;
replaceAllRuntimeFields(newFields: Record<string, RuntimeField>): void;
resetOriginalSavedObjectBody: () => void;
// (undocumented)
protected setFieldAttrs<K extends keyof FieldAttrSet>(fieldName: string, attrName: K, value: FieldAttrSet[K]): void;

View file

@ -22,6 +22,7 @@ export default function ({ getService }: FtrProviderContext) {
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title: basicIndex,
},

View file

@ -15,5 +15,7 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./scripted_fields_crud'));
loadTestFile(require.resolve('./fields_api'));
loadTestFile(require.resolve('./default_index_pattern'));
loadTestFile(require.resolve('./runtime_fields_crud'));
loadTestFile(require.resolve('./integration'));
});
}

View file

@ -117,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(response.body.index_pattern.fields.bar.type).to.be('boolean');
});
it('Can add scripted fields, other fields created from es index', async () => {
it('can add scripted fields, other fields created from es index', async () => {
const title = `basic_index*`;
const response = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
@ -159,6 +159,32 @@ export default function ({ getService }: FtrProviderContext) {
expect(response.body.index_pattern.fields.bar.esTypes[0]).to.be('test-type');
expect(response.body.index_pattern.fields.bar.scripted).to.be(true);
});
it('can add runtime fields', async () => {
const title = `basic_index*`;
const response = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response.status).to.be(200);
expect(response.body.index_pattern.title).to.be(title);
expect(response.body.index_pattern.runtimeFieldMap.runtimeFoo.type).to.be('keyword');
expect(response.body.index_pattern.runtimeFieldMap.runtimeFoo.script.source).to.be(
'emit(doc["foo"].value)'
);
});
});
it('can specify optional typeMeta attribute when creating an index pattern', async () => {

View file

@ -64,5 +64,25 @@ export default function ({ getService }: FtrProviderContext) {
'[request body.refresh_fields]: expected value of type [boolean] but got [number]'
);
});
it('returns an error when unknown runtime field type', async () => {
const title = `basic_index*`;
const response = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'wrong-type',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response.status).to.be(400);
});
});
}

View file

@ -284,5 +284,53 @@ export default function ({ getService }: FtrProviderContext) {
expect(response3.body.index_pattern.intervalName).to.be('intervalName2');
expect(response3.body.index_pattern.typeMeta.baz).to.be('qux');
});
it('can update runtime fields', async () => {
const title = `basic_index*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body.index_pattern.title).to.be(title);
expect(response1.body.index_pattern.runtimeFieldMap.runtimeFoo.type).to.be('keyword');
expect(response1.body.index_pattern.runtimeFieldMap.runtimeFoo.script.source).to.be(
'emit(doc["foo"].value)'
);
const id = response1.body.index_pattern.id;
const response2 = await supertest.post('/api/index_patterns/index_pattern/' + id).send({
index_pattern: {
runtimeFieldMap: {
runtimeBar: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response2.body.index_pattern.runtimeFieldMap.runtimeBar.type).to.be('keyword');
expect(response2.body.index_pattern.runtimeFieldMap.runtimeFoo).to.be(undefined);
const response3 = await supertest.get('/api/index_patterns/index_pattern/' + id);
expect(response3.body.index_pattern.runtimeFieldMap.runtimeBar.type).to.be('keyword');
expect(response3.body.index_pattern.runtimeFieldMap.runtimeFoo).to.be(undefined);
});
});
}

View file

@ -0,0 +1,18 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
/**
* Test usage of different index patterns APIs in combination
*/
export default function ({ loadTestFile }: FtrProviderContext) {
describe('integration', () => {
loadTestFile(require.resolve('./integration'));
});
}

View file

@ -0,0 +1,119 @@
/*
* 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 expect from '@kbn/expect';
import _ from 'lodash';
import { FtrProviderContext } from '../../../ftr_provider_context';
/**
* Test usage of different index patterns APIs in combination
*/
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('integration', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('create an index pattern, add a runtime field, add a field formatter, then re-create the same index pattern', async () => {
const title = `basic_index*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
},
});
const id = response1.body.index_pattern.id;
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field`)
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
.send({
fields: {
runtimeBar: {
count: 123,
customLabel: 'test',
},
},
});
expect(response3.status).to.be(200);
const response4 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
.send({
fields: {
runtimeBar: {
format: {
id: 'duration',
params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' },
},
},
},
});
expect(response4.status).to.be(200);
const response5 = await supertest.get(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
expect(response5.status).to.be(200);
const resultIndexPattern = response5.body.index_pattern;
const runtimeField = resultIndexPattern.fields.runtimeBar;
expect(runtimeField.name).to.be('runtimeBar');
expect(runtimeField.runtimeField.type).to.be('long');
expect(runtimeField.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
expect(runtimeField.scripted).to.be(false);
expect(resultIndexPattern.fieldFormats.runtimeBar.id).to.be('duration');
expect(resultIndexPattern.fieldFormats.runtimeBar.params.inputFormat).to.be('milliseconds');
expect(resultIndexPattern.fieldFormats.runtimeBar.params.outputFormat).to.be(
'humanizePrecise'
);
expect(resultIndexPattern.fieldAttrs.runtimeBar.count).to.be(123);
expect(resultIndexPattern.fieldAttrs.runtimeBar.customLabel).to.be('test');
// check that retrieved object is transient and a clone can be created
const response6 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: resultIndexPattern,
});
expect(response6.status).to.be(200);
const recreatedIndexPattern = response6.body.index_pattern;
expect(_.omit(recreatedIndexPattern, 'version')).to.eql(
_.omit(resultIndexPattern, 'version')
);
});
});
}

View file

@ -0,0 +1,35 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
describe('errors', () => {
it('returns an error field object is not provided', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
},
});
const id = response1.body.index_pattern.id;
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field`)
.send({});
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be(
'[request body.name]: expected value of type [string] but got [undefined]'
);
});
});
}

View file

@ -0,0 +1,16 @@
/*
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('create_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,95 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can create a new runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
},
});
const id = response1.body.index_pattern.id;
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field`)
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(200);
expect(response2.body.field.name).to.be('runtimeBar');
expect(response2.body.field.runtimeField.type).to.be('long');
expect(response2.body.field.runtimeField.script.source).to.be(
"emit(doc['field_name'].value)"
);
expect(response2.body.field.scripted).to.be(false);
});
it('newly created runtime field is available in the index_pattern object', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
},
});
await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field`)
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
const response2 = await supertest.get(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
expect(response2.status).to.be(200);
const field = response2.body.index_pattern.fields.runtimeBar;
expect(field.name).to.be('runtimeBar');
expect(field.runtimeField.type).to.be('long');
expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
expect(field.scripted).to.be(false);
await supertest.delete(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
});
});
}

View file

@ -0,0 +1,81 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
describe('errors', () => {
const basicIndex = 'b*sic_index';
let indexPattern: any;
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title: basicIndex,
},
})
).body.index_pattern;
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
if (indexPattern) {
await supertest.delete('/api/index_patterns/index_pattern/' + indexPattern.id);
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest.delete(
`/api/index_patterns/index_pattern/${id}/runtime_field/foo`
);
expect(response.status).to.be(404);
});
it('returns 404 error on non-existing runtime field', async () => {
const response1 = await supertest.delete(
`/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/test`
);
expect(response1.status).to.be(404);
});
it('returns error when attempting to delete a field which is not a runtime field', async () => {
const response2 = await supertest.delete(
`/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/foo`
);
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be('Only runtime fields can be deleted.');
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest.delete(
`/api/index_patterns/index_pattern/${id}/runtime_field/foo`
);
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
}

View file

@ -0,0 +1,16 @@
/*
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('delete_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,66 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can delete a runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeBar: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
},
},
});
const response2 = await supertest.get(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
expect(typeof response2.body.index_pattern.fields.runtimeBar).to.be('object');
const response3 = await supertest.delete(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field/runtimeBar`
);
expect(response3.status).to.be(200);
const response4 = await supertest.get(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
expect(typeof response4.body.index_pattern.fields.runtimeBar).to.be('undefined');
await supertest.delete(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
});
});
}

View file

@ -0,0 +1,81 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
describe('errors', () => {
const basicIndex = '*asic_index';
let indexPattern: any;
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title: basicIndex,
},
})
).body.index_pattern;
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
if (indexPattern) {
await supertest.delete('/api/index_patterns/index_pattern/' + indexPattern.id);
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest.get(
`/api/index_patterns/index_pattern/${id}/runtime_field/foo`
);
expect(response.status).to.be(404);
});
it('returns 404 error on non-existing runtime field', async () => {
const response1 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/sf`
);
expect(response1.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest.get(
`/api/index_patterns/index_pattern/${id}/runtime_field/foo`
);
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
it('returns error when attempting to fetch a field which is not a runtime field', async () => {
const response2 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/foo`
);
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be('Only runtime fields can be retrieved.');
});
});
}

View file

@ -0,0 +1,16 @@
/*
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('get_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,71 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can fetch a runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "emit(doc['field_name'].value)",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "emit(doc['field_name'].value)",
},
},
},
},
});
expect(response1.status).to.be(200);
const response2 = await supertest.get(
'/api/index_patterns/index_pattern/' +
response1.body.index_pattern.id +
'/runtime_field/runtimeFoo'
);
expect(response2.status).to.be(200);
expect(typeof response2.body.field).to.be('object');
expect(response2.body.field.name).to.be('runtimeFoo');
expect(response2.body.field.type).to.be('string');
expect(response2.body.field.scripted).to.be(false);
expect(response2.body.field.runtimeField.script.source).to.be(
"emit(doc['field_name'].value)"
);
await supertest.delete(
'/api/index_patterns/index_pattern/' + response1.body.index_pattern.id
);
});
});
}

View file

@ -0,0 +1,19 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('runtime_fields_crud', () => {
loadTestFile(require.resolve('./create_runtime_field'));
loadTestFile(require.resolve('./get_runtime_field'));
loadTestFile(require.resolve('./delete_runtime_field'));
loadTestFile(require.resolve('./put_runtime_field'));
loadTestFile(require.resolve('./update_runtime_field'));
});
}

View file

@ -0,0 +1,69 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('errors', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.put(`/api/index_patterns/index_pattern/${id}/runtime_field`)
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response.status).to.be(404);
});
it('returns error on non-runtime field update attempt', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
},
});
const response2 = await supertest
.put(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field`)
.send({
name: 'bar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(400);
expect(response2.body.message).to.be('Only runtime fields can be updated');
});
});
}

View file

@ -0,0 +1,16 @@
/*
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('put_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,122 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can overwrite an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
const response2 = await supertest
.put(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field`)
.send({
name: 'runtimeFoo',
runtimeField: {
type: 'long',
script: {
source: "doc['field_name'].value",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest.get(
'/api/index_patterns/index_pattern/' +
response1.body.index_pattern.id +
'/runtime_field/runtimeFoo'
);
expect(response3.status).to.be(200);
expect(response3.body.field.type).to.be('number');
const response4 = await supertest.get(
'/api/index_patterns/index_pattern/' +
response1.body.index_pattern.id +
'/runtime_field/runtimeBar'
);
expect(response4.status).to.be(200);
expect(response4.body.field.type).to.be('string');
});
it('can add a new runtime field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
await supertest
.put(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field`)
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "doc['field_name'].value",
},
},
});
const response2 = await supertest.get(
'/api/index_patterns/index_pattern/' +
response1.body.index_pattern.id +
'/runtime_field/runtimeBar'
);
expect(response2.status).to.be(200);
expect(typeof response2.body.field.runtimeField).to.be('object');
});
});
}

View file

@ -0,0 +1,51 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
describe('errors', () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field/foo`)
.send({
runtimeField: {
script: {
source: "doc['something_new'].value",
},
},
});
expect(response.status).to.be(404);
});
it('returns error when field name is specified', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field/foo`)
.send({
name: 'foo',
runtimeField: {
script: {
source: "doc['something_new'].value",
},
},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
"[request body.name]: a value wasn't expected to be present"
);
});
});
}

View file

@ -0,0 +1,16 @@
/*
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('update_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,76 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can update an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
const response2 = await supertest
.post(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field/runtimeFoo`
)
.send({
runtimeField: {
script: {
source: "doc['something_new'].value",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest.get(
'/api/index_patterns/index_pattern/' +
response1.body.index_pattern.id +
'/runtime_field/runtimeFoo'
);
expect(response3.status).to.be(200);
expect(response3.body.field.type).to.be('string');
expect(response3.body.field.runtimeField.type).to.be('keyword');
expect(response3.body.field.runtimeField.script.source).to.be("doc['something_new'].value");
});
});
}

View file

@ -54,6 +54,7 @@ export default function ({ getService }: FtrProviderContext) {
it('newly created scripted field is materialized in the index_pattern object', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
},

View file

@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) {
it('can remove a scripted field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
fields: {

View file

@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) {
it('can fetch a scripted field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
fields: {

View file

@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) {
it('can overwrite an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
fields: {

View file

@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) {
it('can update an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
override: true,
index_pattern: {
title,
fields: {