diff --git a/docs/api/index-patterns.asciidoc b/docs/api/index-patterns.asciidoc index 79d2f164fb8c3b..b343d80b4fc15c 100644 --- a/docs/api/index-patterns.asciidoc +++ b/docs/api/index-patterns.asciidoc @@ -20,6 +20,12 @@ The following index patterns APIs are available: ** <> to set a default index pattern * Fields ** <> to change field metadata, such as `count`, `customLabel` and `format` +* Runtime fields + ** <> to retrieve a runtime field + ** <> to create a runtime field + ** <> to create or update a runtime field + ** <> to partially update an existing runtime field + ** <> 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[] + diff --git a/docs/api/index-patterns/create.asciidoc b/docs/api/index-patterns/create.asciidoc index 771292d6f934d5..521e25931ad49f 100644 --- a/docs/api/index-patterns/create.asciidoc +++ b/docs/api/index-patterns/create.asciidoc @@ -84,6 +84,7 @@ $ curl -X POST api/index_patterns/index_pattern "typeMeta": {}, "fieldFormats": {}, "fieldAttrs": {}, + "runtimeFieldMap": {} "allowNoIndex": "..." } } diff --git a/docs/api/index-patterns/get.asciidoc b/docs/api/index-patterns/get.asciidoc index 3f53bf0726bf14..64588e63f62ae3 100644 --- a/docs/api/index-patterns/get.asciidoc +++ b/docs/api/index-patterns/get.asciidoc @@ -58,6 +58,7 @@ The API returns an index pattern object: "typeMeta": {}, "fieldFormats": {}, "fieldAttrs": {}, + "runtimeFieldMap" {}, "allowNoIndex: "..." } } diff --git a/docs/api/index-patterns/runtime-fields/create.asciidoc b/docs/api/index-patterns/runtime-fields/create.asciidoc new file mode 100644 index 00000000000000..b0773c29e5309f --- /dev/null +++ b/docs/api/index-patterns/runtime-fields/create.asciidoc @@ -0,0 +1,61 @@ +[[index-patterns-runtime-field-api-create]] +=== Create runtime field API +++++ +Create runtime field +++++ + +experimental[] Create a runtime field + +[[index-patterns-runtime-field-create-request]] +==== Request + +`POST :/api/index_patterns/index_pattern//runtime_field` + +`POST :/s//api/index_patterns/index_pattern//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//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": {...} +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/runtime-fields/delete.asciidoc b/docs/api/index-patterns/runtime-fields/delete.asciidoc new file mode 100644 index 00000000000000..840789fe1ec23f --- /dev/null +++ b/docs/api/index-patterns/runtime-fields/delete.asciidoc @@ -0,0 +1,37 @@ +[[index-patterns-runtime-field-api-delete]] +=== Delete runtime field API +++++ +Delete runtime field +++++ + +experimental[] Delete a runtime field from an index pattern. + +[[index-patterns-runtime-field-api-delete-request]] +==== Request + +`DELETE :/api/index_patterns/index_pattern//runtime_field/` + +`DELETE :/s//api/index_patterns/index_pattern//runtime_field/` + +[[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//runtime_field/ +-------------------------------------------------- +// KIBANA diff --git a/docs/api/index-patterns/runtime-fields/get.asciidoc b/docs/api/index-patterns/runtime-fields/get.asciidoc new file mode 100644 index 00000000000000..42bd209c708bc3 --- /dev/null +++ b/docs/api/index-patterns/runtime-fields/get.asciidoc @@ -0,0 +1,52 @@ +[[index-patterns-runtime-field-api-get]] +=== Get runtime field API +++++ +Get runtime field +++++ + +experimental[] Get a runtime field + +[[index-patterns-runtime-field-get-request]] +==== Request + +`GET :/api/index_patterns/index_pattern//runtime_field/` + +`GET :/s//api/index_patterns/index_pattern//runtime_field/` + +[[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": { + ... + } +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/runtime-fields/update.asciidoc b/docs/api/index-patterns/runtime-fields/update.asciidoc new file mode 100644 index 00000000000000..f34460896f7bc9 --- /dev/null +++ b/docs/api/index-patterns/runtime-fields/update.asciidoc @@ -0,0 +1,66 @@ +[[index-patterns-runtime-field-api-update]] +=== Update runtime field API +++++ +Update runtime field +++++ + +experimental[] Update an existing runtime field + +[[index-patterns-runtime-field-update-request]] +==== Request + +`POST :/api/index_patterns/index_pattern//runtime_field/` + +`POST :/s//api/index_patterns/index_pattern//runtime_field/` + +[[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//runtime_field/ +{ + "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": {...} +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/runtime-fields/upsert.asciidoc b/docs/api/index-patterns/runtime-fields/upsert.asciidoc new file mode 100644 index 00000000000000..1b436db19c62e4 --- /dev/null +++ b/docs/api/index-patterns/runtime-fields/upsert.asciidoc @@ -0,0 +1,61 @@ +[[index-patterns-runtime-field-api-upsert]] +=== Upsert runtime field API +++++ +Upsert runtime field +++++ + +experimental[] Create or update an existing runtime field + +[[index-patterns-runtime-field-upsert-request]] +==== Request + +`PUT :/api/index_patterns/index_pattern//runtime_field` + +`PUT :/s//api/index_patterns/index_pattern//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//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": {...} +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/update.asciidoc b/docs/api/index-patterns/update.asciidoc index 8ed0ff89fb928c..2d5fe882d448df 100644 --- a/docs/api/index-patterns/update.asciidoc +++ b/docs/api/index-patterns/update.asciidoc @@ -93,7 +93,8 @@ $ curl -X POST api/saved_objects/index-pattern/my-pattern "fieldFormats": {}, "type": "...", "typeMeta": {}, - "fields": {} + "fields": {}, + "runtimeFieldMap": {} } } -------------------------------------------------- diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md new file mode 100644 index 00000000000000..c0aca53255b8fd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md) + +## IndexPattern.getRuntimeField() method + +Returns runtime field if exists + +Signature: + +```typescript +getRuntimeField(name: string): RuntimeField | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeField | null` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md new file mode 100644 index 00000000000000..96dbe13a7f1978 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [hasRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md) + +## IndexPattern.hasRuntimeField() method + +Checks if runtime field exists + +Signature: + +```typescript +hasRuntimeField(name: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index 53d173d39f50d0..51ca42fdce70a7 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -54,13 +54,16 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [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) | | | -| [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) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md index 7a5228fece782e..f2774924fc73c1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md @@ -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. Signature: @@ -16,7 +16,7 @@ removeRuntimeField(name: string): void; | Parameter | Type | Description | | --- | --- | --- | -| name | string | | +| name | string | Field name to remove | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md new file mode 100644 index 00000000000000..076b2b38cf474a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [replaceAllRuntimeFields](./kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md) + +## IndexPattern.replaceAllRuntimeFields() method + +Replaces all existing runtime fields with new fields + +Signature: + +```typescript +replaceAllRuntimeFields(newFields: Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| newFields | Record<string, RuntimeField> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md new file mode 100644 index 00000000000000..d5dc8f966316b0 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md) + +## IndexPattern.getRuntimeField() method + +Returns runtime field if exists + +Signature: + +```typescript +getRuntimeField(name: string): RuntimeField | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeField | null` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md new file mode 100644 index 00000000000000..5000d5e645cbb9 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [hasRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md) + +## IndexPattern.hasRuntimeField() method + +Checks if runtime field exists + +Signature: + +```typescript +hasRuntimeField(name: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md index 97d1cd91152623..27b8a31a2582ba 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -54,13 +54,16 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [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) | | | -| [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) | | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md index da8e7e40a7fac2..ef32b80ba8502e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md @@ -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. Signature: @@ -16,7 +16,7 @@ removeRuntimeField(name: string): void; | Parameter | Type | Description | | --- | --- | --- | -| name | string | | +| name | string | Field name to remove | Returns: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md new file mode 100644 index 00000000000000..35df871763f8a1 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [replaceAllRuntimeFields](./kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md) + +## IndexPattern.replaceAllRuntimeFields() method + +Replaces all existing runtime fields with new fields + +Signature: + +```typescript +replaceAllRuntimeFields(newFields: Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| newFields | Record<string, RuntimeField> | | + +Returns: + +`void` + diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index c897cdbce2309a..0c3a9901f8c8ca 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -364,7 +364,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) { @@ -384,11 +383,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) { + 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) { @@ -396,9 +425,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); } } diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 5ef499840fa6de..67534577d99fcf 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1413,6 +1413,7 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; getSourceFiltering(): { @@ -1420,6 +1421,7 @@ export class IndexPattern implements IIndexPattern { }; // (undocumented) getTimeField(): IndexPatternField | undefined; + hasRuntimeField(name: string): boolean; // (undocumented) id?: string; // @deprecated (undocumented) @@ -1433,6 +1435,7 @@ export class IndexPattern implements IIndexPattern { removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; + replaceAllRuntimeFields(newFields: Record): void; resetOriginalSavedObjectBody: () => void; // (undocumented) protected setFieldAttrs(fieldName: string, attrName: K, value: FieldAttrSet[K]): void; diff --git a/src/plugins/data/server/index_patterns/routes.ts b/src/plugins/data/server/index_patterns/routes.ts index 9bff590b54f1c1..d2d8cb82cf646e 100644 --- a/src/plugins/data/server/index_patterns/routes.ts +++ b/src/plugins/data/server/index_patterns/routes.ts @@ -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', diff --git a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts index d0767334626229..7049903f84e8c2 100644 --- a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts @@ -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, diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts new file mode 100644 index 00000000000000..faf6d87b6d10bf --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts @@ -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 +) => { + 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(), + }, + }); + }) + ); +}; diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts new file mode 100644 index 00000000000000..58b8529d7cf5af --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts @@ -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 +) => { + 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(); + }) + ); +}; diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts new file mode 100644 index 00000000000000..6bc2bf396c0b44 --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts @@ -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 +) => { + 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), + }, + }); + }) + ); +}; diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts new file mode 100644 index 00000000000000..a5e92fa5a36ecb --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts @@ -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 +) => { + 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(), + }, + }); + }) + ); +}; diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts new file mode 100644 index 00000000000000..3f3aae46c43882 --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts @@ -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 +) => { + 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; + + 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(), + }, + }); + }) + ); +}; diff --git a/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts index c1509b9b848bef..1c88550c154c56 100644 --- a/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts @@ -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.'); } diff --git a/src/plugins/data/server/index_patterns/routes/util/schemas.ts b/src/plugins/data/server/index_patterns/routes/util/schemas.ts index d916423c4fc69c..79ee1ffa1ab970 100644 --- a/src/plugins/data/server/index_patterns/routes/util/schemas.ts +++ b/src/plugins/data/server/index_patterns/routes/util/schemas.ts @@ -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 + ] +); +export const runtimeFieldSpec = { + type: runtimeFieldSpecTypeSchema, + script: schema.maybe( + schema.object({ + source: schema.string(), + }) + ), +}; +export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index ff265ccf533015..783bd8d2fcd0e1 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -816,6 +816,7 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; getSourceFiltering(): { @@ -823,6 +824,7 @@ export class IndexPattern implements IIndexPattern { }; // (undocumented) getTimeField(): IndexPatternField | undefined; + hasRuntimeField(name: string): boolean; // (undocumented) id?: string; // @deprecated (undocumented) @@ -836,6 +838,7 @@ export class IndexPattern implements IIndexPattern { removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; + replaceAllRuntimeFields(newFields: Record): void; resetOriginalSavedObjectBody: () => void; // (undocumented) protected setFieldAttrs(fieldName: string, attrName: K, value: FieldAttrSet[K]): void; diff --git a/test/api_integration/apis/index_patterns/fields_api/update_fields/main.ts b/test/api_integration/apis/index_patterns/fields_api/update_fields/main.ts index 3bec22de48ee66..16861f3c28051e 100644 --- a/test/api_integration/apis/index_patterns/fields_api/update_fields/main.ts +++ b/test/api_integration/apis/index_patterns/fields_api/update_fields/main.ts @@ -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, }, diff --git a/test/api_integration/apis/index_patterns/index.js b/test/api_integration/apis/index_patterns/index.js index 9c1e1bba0ab9a5..656b4e506fa23d 100644 --- a/test/api_integration/apis/index_patterns/index.js +++ b/test/api_integration/apis/index_patterns/index.js @@ -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')); }); } diff --git a/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/main.ts b/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/main.ts index 31c3f13a6e05fb..500a642f60850e 100644 --- a/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/main.ts +++ b/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/main.ts @@ -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 () => { diff --git a/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/validation.ts b/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/validation.ts index 2f62ea231b7229..598001644eedbe 100644 --- a/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/validation.ts +++ b/test/api_integration/apis/index_patterns/index_pattern_crud/create_index_pattern/validation.ts @@ -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); + }); }); } diff --git a/test/api_integration/apis/index_patterns/index_pattern_crud/update_index_pattern/main.ts b/test/api_integration/apis/index_patterns/index_pattern_crud/update_index_pattern/main.ts index cd34724e6cda35..7532278d7eb128 100644 --- a/test/api_integration/apis/index_patterns/index_pattern_crud/update_index_pattern/main.ts +++ b/test/api_integration/apis/index_patterns/index_pattern_crud/update_index_pattern/main.ts @@ -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); + }); }); } diff --git a/test/api_integration/apis/index_patterns/integration/index.ts b/test/api_integration/apis/index_patterns/integration/index.ts new file mode 100644 index 00000000000000..6fd5f644ae8949 --- /dev/null +++ b/test/api_integration/apis/index_patterns/integration/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/integration/integration.ts b/test/api_integration/apis/index_patterns/integration/integration.ts new file mode 100644 index 00000000000000..22f07553733235 --- /dev/null +++ b/test/api_integration/apis/index_patterns/integration/integration.ts @@ -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') + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/errors.ts new file mode 100644 index 00000000000000..8ce9e3b36b5c8d --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/errors.ts @@ -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]' + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/index.ts new file mode 100644 index 00000000000000..2cb90ca087f497 --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/main.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/main.ts new file mode 100644 index 00000000000000..e262b9d838e97a --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/create_runtime_field/main.ts @@ -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 + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts new file mode 100644 index 00000000000000..b41a630889ff8a --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts @@ -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].' + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/index.ts new file mode 100644 index 00000000000000..a14201e750ddaf --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/main.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/main.ts new file mode 100644 index 00000000000000..3c74aa336e440c --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/main.ts @@ -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 + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts new file mode 100644 index 00000000000000..3608089e4641a2 --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts @@ -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.'); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/index.ts new file mode 100644 index 00000000000000..2e48ba64841eeb --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/main.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/main.ts new file mode 100644 index 00000000000000..fa0283d69d8e3b --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/main.ts @@ -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 + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/index.ts new file mode 100644 index 00000000000000..7a727a3e867550 --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts new file mode 100644 index 00000000000000..9faca08238033e --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts @@ -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'); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/index.ts new file mode 100644 index 00000000000000..724f18e57f3efd --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/main.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/main.ts new file mode 100644 index 00000000000000..92d8c6fd6d3c25 --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/main.ts @@ -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'); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/errors.ts new file mode 100644 index 00000000000000..3980821c0fd096 --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/errors.ts @@ -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" + ); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/index.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/index.ts new file mode 100644 index 00000000000000..f5d556ca9994ab --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/index.ts @@ -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')); + }); +} diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/main.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/main.ts new file mode 100644 index 00000000000000..6b924570a0e45b --- /dev/null +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/update_runtime_field/main.ts @@ -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"); + }); + }); +} diff --git a/test/api_integration/apis/index_patterns/scripted_fields_crud/create_scripted_field/main.ts b/test/api_integration/apis/index_patterns/scripted_fields_crud/create_scripted_field/main.ts index 1210e7247f72dd..663deae1d34769 100644 --- a/test/api_integration/apis/index_patterns/scripted_fields_crud/create_scripted_field/main.ts +++ b/test/api_integration/apis/index_patterns/scripted_fields_crud/create_scripted_field/main.ts @@ -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, }, diff --git a/test/api_integration/apis/index_patterns/scripted_fields_crud/delete_scripted_field/main.ts b/test/api_integration/apis/index_patterns/scripted_fields_crud/delete_scripted_field/main.ts index 466af26f6e1272..b9ce3e84d53904 100644 --- a/test/api_integration/apis/index_patterns/scripted_fields_crud/delete_scripted_field/main.ts +++ b/test/api_integration/apis/index_patterns/scripted_fields_crud/delete_scripted_field/main.ts @@ -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: { diff --git a/test/api_integration/apis/index_patterns/scripted_fields_crud/get_scripted_field/main.ts b/test/api_integration/apis/index_patterns/scripted_fields_crud/get_scripted_field/main.ts index 64d909480260e5..c38c3a01708a74 100644 --- a/test/api_integration/apis/index_patterns/scripted_fields_crud/get_scripted_field/main.ts +++ b/test/api_integration/apis/index_patterns/scripted_fields_crud/get_scripted_field/main.ts @@ -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: { diff --git a/test/api_integration/apis/index_patterns/scripted_fields_crud/put_scripted_field/main.ts b/test/api_integration/apis/index_patterns/scripted_fields_crud/put_scripted_field/main.ts index 96f5924e7c132f..16b19583068187 100644 --- a/test/api_integration/apis/index_patterns/scripted_fields_crud/put_scripted_field/main.ts +++ b/test/api_integration/apis/index_patterns/scripted_fields_crud/put_scripted_field/main.ts @@ -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: { diff --git a/test/api_integration/apis/index_patterns/scripted_fields_crud/update_scripted_field/main.ts b/test/api_integration/apis/index_patterns/scripted_fields_crud/update_scripted_field/main.ts index 055b3fe9abe04d..4ffc98e0660bca 100644 --- a/test/api_integration/apis/index_patterns/scripted_fields_crud/update_scripted_field/main.ts +++ b/test/api_integration/apis/index_patterns/scripted_fields_crud/update_scripted_field/main.ts @@ -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: {