diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md
new file mode 100644
index 0000000000000..71835e6d28763
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md
@@ -0,0 +1,12 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [expandShorthand](./kibana-plugin-plugins-data-public.expandshorthand.md)
+
+## expandShorthand variable
+
+
+Signature:
+
+```typescript
+expandShorthand: (sh: Record) => Record
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._deserialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._deserialize.md
new file mode 100644
index 0000000000000..3e8b0abec529c
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._deserialize.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) > [\_deserialize](./kibana-plugin-plugins-data-public.fieldmappingspec._deserialize.md)
+
+## FieldMappingSpec.\_deserialize property
+
+Signature:
+
+```typescript
+_deserialize?: (mapping: string) => any | undefined;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._serialize.md
new file mode 100644
index 0000000000000..d0aaf7ddd0c17
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec._serialize.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) > [\_serialize](./kibana-plugin-plugins-data-public.fieldmappingspec._serialize.md)
+
+## FieldMappingSpec.\_serialize property
+
+Signature:
+
+```typescript
+_serialize?: (mapping: any) => string | undefined;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.md
new file mode 100644
index 0000000000000..38ebe60df99a1
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md)
+
+## FieldMappingSpec interface
+
+
+Signature:
+
+```typescript
+export interface FieldMappingSpec
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [\_deserialize](./kibana-plugin-plugins-data-public.fieldmappingspec._deserialize.md) | (mapping: string) => any | undefined
| |
+| [\_serialize](./kibana-plugin-plugins-data-public.fieldmappingspec._serialize.md) | (mapping: any) => string | undefined
| |
+| [type](./kibana-plugin-plugins-data-public.fieldmappingspec.type.md) | ES_FIELD_TYPES
| |
+
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.type.md
new file mode 100644
index 0000000000000..73cff623dc7f2
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldmappingspec.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) > [type](./kibana-plugin-plugins-data-public.fieldmappingspec.type.md)
+
+## FieldMappingSpec.type property
+
+Signature:
+
+```typescript
+type: ES_FIELD_TYPES;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md
index 60302286cbd72..880acdc8956d4 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-getIndexPatternFieldListCreator: ({ fieldFormats, toastNotifications, }: FieldListDependencies) => CreateIndexPatternFieldList
+getIndexPatternFieldListCreator: ({ fieldFormats, onNotification, }: FieldListDependencies) => CreateIndexPatternFieldList
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md
index 6256709e2ee36..c6359fc268882 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md
@@ -9,7 +9,7 @@ Constructs a new instance of the `IndexPattern` class
Signature:
```typescript
-constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache);
+constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache, fieldFormats: FieldFormatsStartCommon, onNotification: OnNotification, onError: OnError);
```
## Parameters
@@ -21,4 +21,7 @@ constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObj
| savedObjectsClient | SavedObjectsClientContract
| |
| apiClient | IIndexPatternsApiClient
| |
| patternCache | PatternCache
| |
+| fieldFormats | FieldFormatsStartCommon
| |
+| onNotification | OnNotification
| |
+| onError | OnError
| |
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 589069e63e7c9..6203aa22bd066 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
@@ -14,7 +14,7 @@ export declare class IndexPattern implements IIndexPattern
| Constructor | Modifiers | Description |
| --- | --- | --- |
-| [(constructor)(id, getConfig, savedObjectsClient, apiClient, patternCache)](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the IndexPattern
class |
+| [(constructor)(id, getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError)](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the IndexPattern
class |
## Properties
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md
index 8ee9acc684fb1..e1e0d58ce38c1 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md
@@ -9,15 +9,15 @@ Constructs a new instance of the `Field` class
Signature:
```typescript
-constructor(indexPattern: IndexPattern, spec: FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, toastNotifications }: FieldDependencies);
+constructor(indexPattern: IIndexPattern, spec: FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| indexPattern | IndexPattern
| |
+| indexPattern | IIndexPattern
| |
| spec | FieldSpec | Field
| |
| shortDotsEnable | boolean
| |
-| { fieldFormats, toastNotifications } | FieldDependencies
| |
+| { fieldFormats, onNotification } | FieldDependencies
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md
index d1a1ee0905c6e..4acaaa8c0dc2c 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-indexPattern?: IndexPattern;
+indexPattern?: IIndexPattern;
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
index 1d3cfa9305c18..8fa1ee0d72e54 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
@@ -14,7 +14,7 @@ export declare class Field implements IFieldType
| Constructor | Modifiers | Description |
| --- | --- | --- |
-| [(constructor)(indexPattern, spec, shortDotsEnable, { fieldFormats, toastNotifications })](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the Field
class |
+| [(constructor)(indexPattern, spec, shortDotsEnable, { fieldFormats, onNotification })](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the Field
class |
## Properties
@@ -28,7 +28,7 @@ export declare class Field implements IFieldType
| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[]
| |
| [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean
| |
| [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | any
| |
-| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IndexPattern
| |
+| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IIndexPattern
| |
| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string
| |
| [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string
| |
| [readFromDocValues](./kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md) | | boolean
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.mappingobject.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.mappingobject.md
new file mode 100644
index 0000000000000..b1f33c8e8546d
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.mappingobject.md
@@ -0,0 +1,12 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [MappingObject](./kibana-plugin-plugins-data-public.mappingobject.md)
+
+## MappingObject type
+
+
+Signature:
+
+```typescript
+export declare type MappingObject = Record;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index bc1eb9100e85c..f62479f02926e 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -54,6 +54,7 @@
| [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | |
| [FetchOptions](./kibana-plugin-plugins-data-public.fetchoptions.md) | |
| [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | |
+| [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) | |
| [Filter](./kibana-plugin-plugins-data-public.filter.md) | |
| [IDataPluginServices](./kibana-plugin-plugins-data-public.idatapluginservices.md) | |
| [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | |
@@ -101,6 +102,7 @@
| [esFilters](./kibana-plugin-plugins-data-public.esfilters.md) | |
| [esKuery](./kibana-plugin-plugins-data-public.eskuery.md) | |
| [esQuery](./kibana-plugin-plugins-data-public.esquery.md) | |
+| [expandShorthand](./kibana-plugin-plugins-data-public.expandshorthand.md) | |
| [extractSearchSourceReferences](./kibana-plugin-plugins-data-public.extractsearchsourcereferences.md) | |
| [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) | |
| [FilterBar](./kibana-plugin-plugins-data-public.filterbar.md) | |
@@ -141,6 +143,7 @@
| [ISearch](./kibana-plugin-plugins-data-public.isearch.md) | |
| [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | |
| [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | \* |
+| [MappingObject](./kibana-plugin-plugins-data-public.mappingobject.md) | |
| [MatchAllFilter](./kibana-plugin-plugins-data-public.matchallfilter.md) | |
| [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | |
| [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | |
diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts
index b64e115fd55ff..5c67073c07dd5 100644
--- a/src/plugins/data/common/field_formats/index.ts
+++ b/src/plugins/data/common/field_formats/index.ts
@@ -54,4 +54,5 @@ export {
// Used in data plugin only
FieldFormatInstanceType,
IFieldFormat,
+ FieldFormatsStartCommon,
} from './types';
diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts
index 5f11c7fe094bc..6f773378de08d 100644
--- a/src/plugins/data/common/field_formats/types.ts
+++ b/src/plugins/data/common/field_formats/types.ts
@@ -17,6 +17,7 @@
* under the License.
*/
import { FieldFormat } from './field_format';
+import { FieldFormatsRegistry } from './field_formats_registry';
/** @public **/
export type FieldFormatsContentType = 'html' | 'text';
@@ -99,3 +100,5 @@ export interface IFieldFormatMetaParams {
basePath?: string;
};
}
+
+export type FieldFormatsStartCommon = Omit;
diff --git a/src/plugins/kibana_utils/public/field_mapping/index.ts b/src/plugins/data/common/field_mapping/index.ts
similarity index 100%
rename from src/plugins/kibana_utils/public/field_mapping/index.ts
rename to src/plugins/data/common/field_mapping/index.ts
diff --git a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts b/src/plugins/data/common/field_mapping/mapping_setup.test.ts
similarity index 97%
rename from src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts
rename to src/plugins/data/common/field_mapping/mapping_setup.test.ts
index ca40685db0ebf..e57699e879a87 100644
--- a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts
+++ b/src/plugins/data/common/field_mapping/mapping_setup.test.ts
@@ -18,7 +18,7 @@
*/
import { expandShorthand } from './mapping_setup';
-import { ES_FIELD_TYPES } from '../../../data/public';
+import { ES_FIELD_TYPES } from '../../../data/common';
describe('mapping_setup', () => {
it('allows shortcuts for field types by just setting the value to the type name', () => {
diff --git a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts b/src/plugins/data/common/field_mapping/mapping_setup.ts
similarity index 100%
rename from src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts
rename to src/plugins/data/common/field_mapping/mapping_setup.ts
diff --git a/src/plugins/kibana_utils/public/field_mapping/types.ts b/src/plugins/data/common/field_mapping/types.ts
similarity index 95%
rename from src/plugins/kibana_utils/public/field_mapping/types.ts
rename to src/plugins/data/common/field_mapping/types.ts
index f3fb9b000e45a..973a58d3baec4 100644
--- a/src/plugins/kibana_utils/public/field_mapping/types.ts
+++ b/src/plugins/data/common/field_mapping/types.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ES_FIELD_TYPES } from '../../../data/public';
+import { ES_FIELD_TYPES } from '../../../data/common';
/** @public */
export interface FieldMappingSpec {
diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts
index e4a663a1599f1..adbd93d518fc7 100644
--- a/src/plugins/data/common/index.ts
+++ b/src/plugins/data/common/index.ts
@@ -27,3 +27,4 @@ export * from './search';
export * from './search/aggs';
export * from './types';
export * from './utils';
+export * from './field_mapping';
diff --git a/src/plugins/data/public/index_patterns/fields/__snapshots__/field.test.ts.snap b/src/plugins/data/common/index_patterns/fields/__snapshots__/field.test.ts.snap
similarity index 100%
rename from src/plugins/data/public/index_patterns/fields/__snapshots__/field.test.ts.snap
rename to src/plugins/data/common/index_patterns/fields/__snapshots__/field.test.ts.snap
diff --git a/src/plugins/data/public/index_patterns/fields/field.test.ts b/src/plugins/data/common/index_patterns/fields/field.test.ts
similarity index 94%
rename from src/plugins/data/public/index_patterns/fields/field.test.ts
rename to src/plugins/data/common/index_patterns/fields/field.test.ts
index 18252b159d98d..711c176fed9cc 100644
--- a/src/plugins/data/public/index_patterns/fields/field.test.ts
+++ b/src/plugins/data/common/index_patterns/fields/field.test.ts
@@ -18,9 +18,8 @@
*/
import { Field } from './field';
-import { IndexPattern } from '..';
-import { notificationServiceMock } from '../../../../../core/public/mocks';
-import { FieldFormatsStart } from '../../field_formats';
+import { IndexPattern } from '../index_patterns';
+import { FieldFormatsStartCommon } from '../..';
import { KBN_FIELD_TYPES } from '../../../common';
describe('Field', function () {
@@ -34,8 +33,8 @@ describe('Field', function () {
{ ...fieldValues, ...values },
false,
{
- fieldFormats: {} as FieldFormatsStart,
- toastNotifications: notificationServiceMock.createStartContract().toasts,
+ fieldFormats: {} as FieldFormatsStartCommon,
+ onNotification: () => {},
}
);
}
@@ -215,8 +214,8 @@ describe('Field', function () {
it('exports the property to JSON', () => {
const field = new Field({ fieldFormatMap: { name: {} } } as IndexPattern, fieldValues, false, {
- fieldFormats: {} as FieldFormatsStart,
- toastNotifications: notificationServiceMock.createStartContract().toasts,
+ fieldFormats: {} as FieldFormatsStartCommon,
+ onNotification: () => {},
});
expect(flatten(field)).toMatchSnapshot();
});
diff --git a/src/plugins/data/public/index_patterns/fields/field.ts b/src/plugins/data/common/index_patterns/fields/field.ts
similarity index 89%
rename from src/plugins/data/public/index_patterns/fields/field.ts
rename to src/plugins/data/common/index_patterns/fields/field.ts
index 625df17d62e0d..c53e3f2b1f621 100644
--- a/src/plugins/data/public/index_patterns/fields/field.ts
+++ b/src/plugins/data/common/index_patterns/fields/field.ts
@@ -18,10 +18,9 @@
*/
import { i18n } from '@kbn/i18n';
-import { ToastsStart } from 'kibana/public';
// @ts-ignore
import { ObjDefine } from './obj_define';
-import { IndexPattern } from '../index_patterns';
+import { IIndexPattern } from '../../types';
import {
IFieldType,
getKbnFieldType,
@@ -29,13 +28,14 @@ import {
FieldFormat,
shortenDottedString,
} from '../../../common';
-import { FieldFormatsStart } from '../../field_formats';
+import { OnNotification } from '../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
export type FieldSpec = Record;
interface FieldDependencies {
- fieldFormats: FieldFormatsStart;
- toastNotifications: ToastsStart;
+ fieldFormats: FieldFormatsStartCommon;
+ onNotification: OnNotification;
}
export class Field implements IFieldType {
@@ -55,17 +55,17 @@ export class Field implements IFieldType {
scripted?: boolean;
subType?: IFieldSubType;
displayName?: string;
- indexPattern?: IndexPattern;
+ indexPattern?: IIndexPattern;
readFromDocValues?: boolean;
format: any;
$$spec: FieldSpec;
conflictDescriptions?: Record;
constructor(
- indexPattern: IndexPattern,
+ indexPattern: IIndexPattern,
spec: FieldSpec | Field,
shortDotsEnable: boolean,
- { fieldFormats, toastNotifications }: FieldDependencies
+ { fieldFormats, onNotification }: FieldDependencies
) {
// unwrap old instances of Field
if (spec instanceof Field) spec = spec.$$spec;
@@ -90,11 +90,7 @@ export class Field implements IFieldType {
values: { name: spec.name, title: indexPattern.title },
defaultMessage: 'Field {name} in indexPattern {title} is using an unknown field type.',
});
-
- toastNotifications.addDanger({
- title,
- text,
- });
+ onNotification({ title, text, color: 'danger', iconType: 'alert' });
}
if (!type) type = getKbnFieldType('unknown');
@@ -103,7 +99,7 @@ export class Field implements IFieldType {
if (!FieldFormat.isInstanceOfFieldFormat(format)) {
format =
- indexPattern.fieldFormatMap[spec.name] ||
+ (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[spec.name]) ||
fieldFormats.getDefaultInstance(spec.type, spec.esTypes);
}
diff --git a/src/plugins/data/public/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts
similarity index 87%
rename from src/plugins/data/public/index_patterns/fields/field_list.ts
rename to src/plugins/data/common/index_patterns/fields/field_list.ts
index 1aef0b1ccadaa..173a629863a71 100644
--- a/src/plugins/data/public/index_patterns/fields/field_list.ts
+++ b/src/plugins/data/common/index_patterns/fields/field_list.ts
@@ -18,17 +18,17 @@
*/
import { findIndex } from 'lodash';
-import { ToastsStart } from 'kibana/public';
-import { IndexPattern } from '../index_patterns';
+import { IIndexPattern } from '../../types';
import { IFieldType } from '../../../common';
import { Field, FieldSpec } from './field';
-import { FieldFormatsStart } from '../../field_formats';
+import { OnNotification } from '../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
type FieldMap = Map;
interface FieldListDependencies {
- fieldFormats: FieldFormatsStart;
- toastNotifications: ToastsStart;
+ fieldFormats: FieldFormatsStartCommon;
+ onNotification: OnNotification;
}
export interface IIndexPatternFieldList extends Array {
@@ -40,19 +40,19 @@ export interface IIndexPatternFieldList extends Array {
}
export type CreateIndexPatternFieldList = (
- indexPattern: IndexPattern,
+ indexPattern: IIndexPattern,
specs?: FieldSpec[],
shortDotsEnable?: boolean
) => IIndexPatternFieldList;
export const getIndexPatternFieldListCreator = ({
fieldFormats,
- toastNotifications,
+ onNotification,
}: FieldListDependencies): CreateIndexPatternFieldList => (...fieldListParams) => {
class FieldList extends Array implements IIndexPatternFieldList {
private byName: FieldMap = new Map();
private groups: Map = new Map();
- private indexPattern: IndexPattern;
+ private indexPattern: IIndexPattern;
private shortDotsEnable: boolean;
private setByName = (field: Field) => this.byName.set(field.name, field);
private setByGroup = (field: Field) => {
@@ -63,7 +63,7 @@ export const getIndexPatternFieldListCreator = ({
};
private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name);
- constructor(indexPattern: IndexPattern, specs: FieldSpec[] = [], shortDotsEnable = false) {
+ constructor(indexPattern: IIndexPattern, specs: FieldSpec[] = [], shortDotsEnable = false) {
super();
this.indexPattern = indexPattern;
this.shortDotsEnable = shortDotsEnable;
@@ -76,7 +76,7 @@ export const getIndexPatternFieldListCreator = ({
add = (field: FieldSpec) => {
const newField = new Field(this.indexPattern, field, this.shortDotsEnable, {
fieldFormats,
- toastNotifications,
+ onNotification,
});
this.push(newField);
this.setByName(newField);
@@ -94,7 +94,7 @@ export const getIndexPatternFieldListCreator = ({
update = (field: FieldSpec) => {
const newField = new Field(this.indexPattern, field, this.shortDotsEnable, {
fieldFormats,
- toastNotifications,
+ onNotification,
});
const index = this.findIndex((f) => f.name === newField.name);
this.splice(index, 1, newField);
diff --git a/src/plugins/data/common/index_patterns/fields/index.ts b/src/plugins/data/common/index_patterns/fields/index.ts
index 5b6fef3e51fa9..1b7c87d556f59 100644
--- a/src/plugins/data/common/index_patterns/fields/index.ts
+++ b/src/plugins/data/common/index_patterns/fields/index.ts
@@ -19,3 +19,5 @@
export * from './types';
export { isFilterable, isNestedField } from './utils';
+export * from './field_list';
+export * from './field';
diff --git a/src/plugins/data/public/index_patterns/fields/obj_define.js b/src/plugins/data/common/index_patterns/fields/obj_define.js
similarity index 100%
rename from src/plugins/data/public/index_patterns/fields/obj_define.js
rename to src/plugins/data/common/index_patterns/fields/obj_define.js
diff --git a/src/plugins/data/public/index_patterns/fields/obj_define.test.js b/src/plugins/data/common/index_patterns/fields/obj_define.test.js
similarity index 100%
rename from src/plugins/data/public/index_patterns/fields/obj_define.test.js
rename to src/plugins/data/common/index_patterns/fields/obj_define.test.js
diff --git a/src/plugins/data/public/index_patterns/index_patterns/_fields_fetcher.ts b/src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts
similarity index 91%
rename from src/plugins/data/public/index_patterns/index_patterns/_fields_fetcher.ts
rename to src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts
index f47cd000b7ba0..775b559b9fec5 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/_fields_fetcher.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-import { IndexPattern } from './index_pattern';
-import { GetFieldsOptions, IIndexPatternsApiClient } from './index_patterns_api_client';
+import { GetFieldsOptions, IIndexPatternsApiClient, IndexPattern } from '.';
/** @internal */
export const createFieldsFetcher = (
diff --git a/src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts b/src/plugins/data/common/index_patterns/index_patterns/_pattern_cache.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts
rename to src/plugins/data/common/index_patterns/index_patterns/_pattern_cache.ts
diff --git a/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts
new file mode 100644
index 0000000000000..2737627bf1977
--- /dev/null
+++ b/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts
@@ -0,0 +1,57 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { contains } from 'lodash';
+import { CoreStart } from 'kibana/public';
+import { IndexPatternsContract } from './index_patterns';
+
+export type EnsureDefaultIndexPattern = () => Promise | undefined;
+
+export const createEnsureDefaultIndexPattern = (
+ uiSettings: CoreStart['uiSettings'],
+ onRedirectNoIndexPattern: () => Promise | void
+) => {
+ /**
+ * Checks whether a default index pattern is set and exists and defines
+ * one otherwise.
+ */
+ return async function ensureDefaultIndexPattern(this: IndexPatternsContract) {
+ const patterns = await this.getIds();
+ let defaultId = uiSettings.get('defaultIndex');
+ let defined = !!defaultId;
+ const exists = contains(patterns, defaultId);
+
+ if (defined && !exists) {
+ uiSettings.remove('defaultIndex');
+ defaultId = defined = false;
+ }
+
+ if (defined) {
+ return;
+ }
+
+ // If there is any index pattern created, set the first as default
+ if (patterns.length >= 1) {
+ defaultId = patterns[0];
+ uiSettings.set('defaultIndex', defaultId);
+ } else {
+ return onRedirectNoIndexPattern();
+ }
+ };
+};
diff --git a/src/plugins/data/public/index_patterns/index_patterns/flatten_hit.ts b/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/flatten_hit.ts
rename to src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts
diff --git a/src/plugins/data/public/index_patterns/index_patterns/format_hit.ts b/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/format_hit.ts
rename to src/plugins/data/common/index_patterns/index_patterns/format_hit.ts
diff --git a/src/plugins/data/public/index_patterns/fields/index.ts b/src/plugins/data/common/index_patterns/index_patterns/index.ts
similarity index 77%
rename from src/plugins/data/public/index_patterns/fields/index.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index.ts
index 1644e23a163a6..5fae08f3bb775 100644
--- a/src/plugins/data/public/index_patterns/fields/index.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index.ts
@@ -17,5 +17,10 @@
* under the License.
*/
-export * from './field_list';
-export * from './field';
+export * from './index_patterns_api_client';
+export * from './types';
+export * from './_pattern_cache';
+export * from './flatten_hit';
+export * from './format_hit';
+export * from './index_pattern';
+export * from './index_patterns';
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
similarity index 93%
rename from src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
index 84135bb5d1e2b..8ec3072bf916b 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
@@ -19,24 +19,19 @@
import { defaults, pluck, last, get } from 'lodash';
-jest.mock('../../../../kibana_utils/public/history');
import { IndexPattern } from './index_pattern';
-import { DuplicateField } from '../../../../kibana_utils/public';
+import { DuplicateField } from '../../../../kibana_utils/common';
// @ts-ignore
import mockLogStashFields from '../../../../../fixtures/logstash_fields';
// @ts-ignore
import { stubbedSavedObjectIndexPattern } from '../../../../../fixtures/stubbed_saved_object_index_pattern';
import { Field } from '../fields';
-import { setNotifications, setFieldFormats } from '../../services';
-// Temporary disable eslint, will be removed after moving to new platform folder
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { notificationServiceMock } from '../../../../../core/public/notifications/notifications_service.mock';
-import { FieldFormatsStart } from '../../field_formats';
+import { FieldFormatsStartCommon } from '../../field_formats';
-jest.mock('../../../../kibana_utils/public', () => {
- const originalModule = jest.requireActual('../../../../kibana_utils/public');
+jest.mock('../../field_mapping', () => {
+ const originalModule = jest.requireActual('../../field_mapping');
return {
...originalModule,
@@ -107,7 +102,10 @@ function create(id: string, payload?: any): Promise {
(cfg: any) => config.get(cfg),
savedObjectsClient as any,
apiClient,
- patternCache
+ patternCache,
+ ({ getDefaultInstance: () => {}, getType: () => {} } as unknown) as FieldFormatsStartCommon,
+ () => {},
+ () => {}
);
setDocsourcePayload(id, payload);
@@ -121,18 +119,11 @@ function setDocsourcePayload(id: string | null, providedPayload: any) {
describe('IndexPattern', () => {
const indexPatternId = 'test-pattern';
- const notifications = notificationServiceMock.createStartContract();
let indexPattern: IndexPattern;
// create an indexPattern instance for each test
beforeEach(() => {
- setNotifications(notifications);
- setFieldFormats(({
- getDefaultInstance: jest.fn(),
- deserialize: jest.fn() as any,
- } as unknown) as FieldFormatsStart);
-
return create(indexPatternId).then((pattern: IndexPattern) => {
indexPattern = pattern;
});
@@ -377,7 +368,10 @@ describe('IndexPattern', () => {
(cfg: any) => config.get(cfg),
savedObjectsClient as any,
apiClient,
- patternCache
+ patternCache,
+ ({ getDefaultInstance: () => {}, getType: () => {} } as unknown) as FieldFormatsStartCommon,
+ () => {},
+ () => {}
);
await pattern.init();
@@ -389,7 +383,10 @@ describe('IndexPattern', () => {
(cfg: any) => config.get(cfg),
savedObjectsClient as any,
apiClient,
- patternCache
+ patternCache,
+ ({ getDefaultInstance: () => {}, getType: () => {} } as unknown) as FieldFormatsStartCommon,
+ () => {},
+ () => {}
);
await samePattern.init();
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
new file mode 100644
index 0000000000000..c406c00131d15
--- /dev/null
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
@@ -0,0 +1,651 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import _, { each, reject } from 'lodash';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React from 'react';
+
+import { SavedObjectsClientContract } from 'src/core/public';
+import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common';
+
+import {
+ ES_FIELD_TYPES,
+ KBN_FIELD_TYPES,
+ IIndexPattern,
+ IFieldType,
+ UI_SETTINGS,
+} from '../../../common';
+import { findByTitle } from '../utils';
+import { IndexPatternMissingIndices } from '../lib';
+import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '../fields';
+import { createFieldsFetcher } from './_fields_fetcher';
+import { formatHitProvider } from './format_hit';
+import { flattenHitWrapper } from './flatten_hit';
+import { IIndexPatternsApiClient } from '.';
+import { TypeMeta } from '.';
+import { OnNotification, OnError } from '../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
+import { PatternCache } from './_pattern_cache';
+import { expandShorthand, FieldMappingSpec, MappingObject } from '../../field_mapping';
+
+const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
+const type = 'index-pattern';
+
+export class IndexPattern implements IIndexPattern {
+ [key: string]: any;
+
+ public id?: string;
+ public title: string = '';
+ public type?: string;
+ public fieldFormatMap: any;
+ public typeMeta?: TypeMeta;
+ public fields: IIndexPatternFieldList;
+ public timeFieldName: string | undefined;
+ public intervalName: string | undefined | null;
+ public formatHit: any;
+ public formatField: any;
+ public flattenHit: any;
+ public metaFields: string[];
+
+ private version: string | undefined;
+ private savedObjectsClient: SavedObjectsClientContract;
+ private patternCache: PatternCache;
+ private getConfig: any;
+ private sourceFilters?: [];
+ private originalBody: { [key: string]: any } = {};
+ public fieldsFetcher: any; // probably want to factor out any direct usage and change to private
+ private shortDotsEnable: boolean = false;
+ private fieldFormats: FieldFormatsStartCommon;
+ private onNotification: OnNotification;
+ private onError: OnError;
+ private apiClient: IIndexPatternsApiClient;
+
+ private mapping: MappingObject = expandShorthand({
+ title: ES_FIELD_TYPES.TEXT,
+ timeFieldName: ES_FIELD_TYPES.KEYWORD,
+ intervalName: ES_FIELD_TYPES.KEYWORD,
+ fields: 'json',
+ sourceFilters: 'json',
+ fieldFormatMap: {
+ type: ES_FIELD_TYPES.TEXT,
+ _serialize: (map = {}) => {
+ const serialized = _.transform(map, this.serializeFieldFormatMap);
+ return _.isEmpty(serialized) ? undefined : JSON.stringify(serialized);
+ },
+ _deserialize: (map = '{}') => {
+ return _.mapValues(JSON.parse(map), (mapping) => {
+ return this.deserializeFieldFormatMap(mapping);
+ });
+ },
+ },
+ type: ES_FIELD_TYPES.KEYWORD,
+ typeMeta: 'json',
+ });
+
+ constructor(
+ id: string | undefined,
+ getConfig: any,
+ savedObjectsClient: SavedObjectsClientContract,
+ apiClient: IIndexPatternsApiClient,
+ patternCache: PatternCache,
+ fieldFormats: FieldFormatsStartCommon,
+ onNotification: OnNotification,
+ onError: OnError
+ ) {
+ this.id = id;
+ this.savedObjectsClient = savedObjectsClient;
+ this.patternCache = patternCache;
+ // instead of storing config we rather store the getter only as np uiSettingsClient has circular references
+ // which cause problems when being consumed from angular
+ this.getConfig = getConfig;
+ this.fieldFormats = fieldFormats;
+ this.onNotification = onNotification;
+ this.onError = onError;
+
+ this.shortDotsEnable = this.getConfig(UI_SETTINGS.SHORT_DOTS_ENABLE);
+ this.metaFields = this.getConfig(UI_SETTINGS.META_FIELDS);
+
+ this.createFieldList = getIndexPatternFieldListCreator({
+ fieldFormats,
+ onNotification,
+ });
+
+ this.fields = this.createFieldList(this, [], this.shortDotsEnable);
+ this.apiClient = apiClient;
+ this.fieldsFetcher = createFieldsFetcher(
+ this,
+ apiClient,
+ this.getConfig(UI_SETTINGS.META_FIELDS)
+ );
+ this.flattenHit = flattenHitWrapper(this, this.getConfig(UI_SETTINGS.META_FIELDS));
+ this.formatHit = formatHitProvider(
+ this,
+ fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING)
+ );
+ this.formatField = this.formatHit.formatField;
+ }
+
+ private serializeFieldFormatMap(flat: any, format: string, field: string | undefined) {
+ if (format && field) {
+ flat[field] = format;
+ }
+ }
+
+ private deserializeFieldFormatMap(mapping: any) {
+ const FieldFormat = this.fieldFormats.getType(mapping.id);
+
+ return FieldFormat && new FieldFormat(mapping.params, this.getConfig);
+ }
+
+ private initFields(input?: any) {
+ const newValue = input || this.fields;
+
+ this.fields = this.createFieldList(this, newValue, this.shortDotsEnable);
+ }
+
+ private isFieldRefreshRequired(): boolean {
+ if (!this.fields) {
+ return true;
+ }
+
+ return this.fields.every((field) => {
+ // See https://github.com/elastic/kibana/pull/8421
+ const hasFieldCaps = 'aggregatable' in field && 'searchable' in field;
+
+ // See https://github.com/elastic/kibana/pull/11969
+ const hasDocValuesFlag = 'readFromDocValues' in field;
+
+ return !hasFieldCaps || !hasDocValuesFlag;
+ });
+ }
+
+ private async indexFields(forceFieldRefresh: boolean = false) {
+ if (!this.id) {
+ return;
+ }
+
+ if (forceFieldRefresh || this.isFieldRefreshRequired()) {
+ await this.refreshFields();
+ }
+
+ this.initFields();
+ }
+
+ private async updateFromElasticSearch(response: any, forceFieldRefresh: boolean = false) {
+ if (!response.found) {
+ throw new SavedObjectNotFound(type, this.id, 'kibana#/management/kibana/indexPatterns');
+ }
+
+ _.forOwn(this.mapping, (fieldMapping: FieldMappingSpec, name: string | undefined) => {
+ if (!fieldMapping._deserialize || !name) {
+ return;
+ }
+
+ response._source[name] = fieldMapping._deserialize(response._source[name]);
+ });
+
+ // give index pattern all of the values in _source
+ _.assign(this, response._source);
+
+ if (!this.title && this.id) {
+ this.title = this.id;
+ }
+
+ if (this.isUnsupportedTimePattern()) {
+ const warningTitle = i18n.translate('data.indexPatterns.warningTitle', {
+ defaultMessage: 'Support for time interval index patterns removed',
+ });
+
+ const warningText = i18n.translate('data.indexPatterns.warningText', {
+ defaultMessage:
+ 'Currently querying all indices matching {index}. {title} should be migrated to a wildcard-based index pattern.',
+ values: {
+ title: this.title,
+ index: this.getIndex(),
+ },
+ });
+
+ // kbnUrl was added to this service in #35262 before it was de-angularized, and merged in a PR
+ // directly against the 7.x branch. Index patterns were de-angularized in #39247, and in order
+ // to preserve the functionality from #35262 we need to get the injector here just for kbnUrl.
+ // This has all been removed as of 8.0.
+
+ // 2019-12-01 The usage of kbnUrl had to be removed due to the transition to NP.
+ // It's now temporarily replaced by a simple replace of the single argument used by all URLs.
+ // Once kbnUrl is migrated to NP, this can be updated.
+ const editUrl = `/app/kibana#/management/kibana/index_patterns/${this.id! || ''}`;
+
+ const { toasts } = getNotifications();
+
+ toasts.addWarning({
+ title: warningTitle,
+ text: toMountPoint(
+
+
{warningText}
+
+
+
+
+
+
+
+
+ ),
+ });
+ }
+
+ return this.indexFields(forceFieldRefresh);
+ }
+
+ getComputedFields() {
+ const scriptFields: any = {};
+ if (!this.fields) {
+ return {
+ storedFields: ['*'],
+ scriptFields,
+ docvalueFields: [],
+ };
+ }
+
+ // Date value returned in "_source" could be in any number of formats
+ // Use a docvalue for each date field to ensure standardized formats when working with date fields
+ // indexPattern.flattenHit will override "_source" values when the same field is also defined in "fields"
+ const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map(
+ (dateField: any) => {
+ return {
+ field: dateField.name,
+ format:
+ dateField.esTypes && dateField.esTypes.indexOf('date_nanos') !== -1
+ ? 'strict_date_time'
+ : 'date_time',
+ };
+ }
+ );
+
+ each(this.getScriptedFields(), function (field) {
+ scriptFields[field.name] = {
+ script: {
+ source: field.script,
+ lang: field.lang,
+ },
+ };
+ });
+
+ return {
+ storedFields: ['*'],
+ scriptFields,
+ docvalueFields,
+ };
+ }
+
+ async init(forceFieldRefresh = false) {
+ if (!this.id) {
+ return this; // no id === no elasticsearch document
+ }
+
+ const savedObject = await this.savedObjectsClient.get(type, this.id);
+ this.version = savedObject._version;
+
+ const response = {
+ _id: savedObject.id,
+ _type: savedObject.type,
+ _source: _.cloneDeep(savedObject.attributes),
+ found: savedObject._version ? true : false,
+ };
+ // Do this before we attempt to update from ES since that call can potentially perform a save
+ this.originalBody = this.prepBody();
+ await this.updateFromElasticSearch(response, forceFieldRefresh);
+ // Do it after to ensure we have the most up to date information
+ this.originalBody = this.prepBody();
+
+ return this;
+ }
+
+ migrate(newTitle: string) {
+ return this.savedObjectsClient
+ .update(type, this.id!, {
+ title: newTitle,
+ intervalName: null,
+ })
+ .then(({ attributes: { title, intervalName } }) => {
+ this.title = title;
+ this.intervalName = intervalName;
+ })
+ .then(() => this);
+ }
+
+ // Get the source filtering configuration for that index.
+ getSourceFiltering() {
+ return {
+ excludes: (this.sourceFilters && this.sourceFilters.map((filter: any) => filter.value)) || [],
+ };
+ }
+
+ async addScriptedField(name: string, script: string, fieldType: string = 'string', lang: string) {
+ const scriptedFields = this.getScriptedFields();
+ const names = _.pluck(scriptedFields, 'name');
+
+ if (_.contains(names, name)) {
+ throw new DuplicateField(name);
+ }
+
+ this.fields.add(
+ new Field(
+ this,
+ {
+ name,
+ script,
+ fieldType,
+ scripted: true,
+ lang,
+ aggregatable: true,
+ filterable: true,
+ searchable: true,
+ },
+ false,
+ {
+ fieldFormats: this.fieldFormats,
+ onNotification: this.onNotification,
+ }
+ )
+ );
+
+ await this.save();
+ }
+
+ removeScriptedField(field: IFieldType) {
+ this.fields.remove(field);
+ return this.save();
+ }
+
+ async popularizeField(fieldName: string, unit = 1) {
+ /**
+ * This function is just used by Discover and it's high likely to be removed in the near future
+ * It doesn't use the save function to skip the error message that's displayed when
+ * a user adds several columns in a higher frequency that the changes can be persisted to ES
+ * resulting in 409 errors
+ */
+ if (!this.id) return;
+ const field = this.fields.getByName(fieldName);
+ if (!field) {
+ return;
+ }
+ const count = Math.max((field.count || 0) + unit, 0);
+ if (field.count === count) {
+ return;
+ }
+ field.count = count;
+
+ try {
+ const res = await this.savedObjectsClient.update(type, this.id, this.prepBody(), {
+ version: this.version,
+ });
+ this.version = res._version;
+ } catch (e) {
+ // no need for an error message here
+ }
+ }
+
+ getNonScriptedFields() {
+ return _.where(this.fields, { scripted: false });
+ }
+
+ getScriptedFields() {
+ return _.where(this.fields, { scripted: true });
+ }
+
+ getIndex() {
+ if (!this.isUnsupportedTimePattern()) {
+ return this.title;
+ }
+
+ // Take a time-based interval index pattern title (like [foo-]YYYY.MM.DD[-bar]) and turn it
+ // into the actual index (like foo-*-bar) by replacing anything not inside square brackets
+ // with a *.
+ const regex = /\[[^\]]*]/g; // Matches text inside brackets
+ const splits = this.title.split(regex); // e.g. ['', 'YYYY.MM.DD', ''] from the above example
+ const matches = this.title.match(regex) || []; // e.g. ['[foo-]', '[-bar]'] from the above example
+ return splits
+ .map((split, i) => {
+ const match = i >= matches.length ? '' : matches[i].replace(/[\[\]]/g, '');
+ return `${split.length ? '*' : ''}${match}`;
+ })
+ .join('');
+ }
+
+ isUnsupportedTimePattern(): boolean {
+ return !!this.intervalName;
+ }
+
+ isTimeBased(): boolean {
+ return !!this.timeFieldName && (!this.fields || !!this.getTimeField());
+ }
+
+ isTimeNanosBased(): boolean {
+ const timeField: any = this.getTimeField();
+ return timeField && timeField.esTypes && timeField.esTypes.indexOf('date_nanos') !== -1;
+ }
+
+ isTimeBasedWildcard(): boolean {
+ return this.isTimeBased() && this.isWildcard();
+ }
+
+ getTimeField() {
+ if (!this.timeFieldName || !this.fields || !this.fields.getByName) return;
+ return this.fields.getByName(this.timeFieldName);
+ }
+
+ getFieldByName(name: string): Field | void {
+ if (!this.fields || !this.fields.getByName) return;
+ return this.fields.getByName(name);
+ }
+
+ getAggregationRestrictions() {
+ return this.typeMeta?.aggs;
+ }
+
+ isWildcard() {
+ return _.includes(this.title, '*');
+ }
+
+ prepBody() {
+ const body: { [key: string]: any } = {};
+
+ // serialize json fields
+ _.forOwn(this.mapping, (fieldMapping, fieldName) => {
+ if (!fieldName || this[fieldName] == null) return;
+
+ body[fieldName] = fieldMapping._serialize
+ ? fieldMapping._serialize(this[fieldName])
+ : this[fieldName];
+ });
+
+ return body;
+ }
+
+ async create(allowOverride: boolean = false) {
+ const _create = async (duplicateId?: string) => {
+ if (duplicateId) {
+ const duplicatePattern = new IndexPattern(
+ duplicateId,
+ this.getConfig,
+ this.savedObjectsClient,
+ this.apiClient,
+ this.patternCache,
+ this.fieldFormats,
+ this.onNotification,
+ this.onError
+ );
+
+ await duplicatePattern.destroy();
+ }
+
+ const body = this.prepBody();
+ const response = await this.savedObjectsClient.create(type, body, { id: this.id });
+
+ this.id = response.id;
+ return response.id;
+ };
+
+ const potentialDuplicateByTitle = await findByTitle(this.savedObjectsClient, this.title);
+ // If there is potentially duplicate title, just create it
+ if (!potentialDuplicateByTitle) {
+ return await _create();
+ }
+
+ // We found a duplicate but we aren't allowing override, show the warn modal
+ if (!allowOverride) {
+ return false;
+ }
+
+ return await _create(potentialDuplicateByTitle.id);
+ }
+
+ async save(saveAttempts: number = 0): Promise {
+ if (!this.id) return;
+ const body = this.prepBody();
+ // What keys changed since they last pulled the index pattern
+ const originalChangedKeys = Object.keys(body).filter(
+ (key) => body[key] !== this.originalBody[key]
+ );
+ return this.savedObjectsClient
+ .update(type, this.id, body, { version: this.version })
+ .then((resp: any) => {
+ this.id = resp.id;
+ this.version = resp._version;
+ })
+ .catch((err) => {
+ if (
+ _.get(err, 'res.status') === 409 &&
+ saveAttempts++ < MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS
+ ) {
+ const samePattern = new IndexPattern(
+ this.id,
+ this.getConfig,
+ this.savedObjectsClient,
+ this.apiClient,
+ this.patternCache,
+ this.fieldFormats,
+ this.onNotification,
+ this.onError
+ );
+ return samePattern.init().then(() => {
+ // What keys changed from now and what the server returned
+ const updatedBody = samePattern.prepBody();
+
+ // Build a list of changed keys from the server response
+ // and ensure we ignore the key if the server response
+ // is the same as the original response (since that is expected
+ // if we made a change in that key)
+ const serverChangedKeys = Object.keys(updatedBody).filter((key) => {
+ return updatedBody[key] !== body[key] && this.originalBody[key] !== updatedBody[key];
+ });
+
+ let unresolvedCollision = false;
+ for (const originalKey of originalChangedKeys) {
+ for (const serverKey of serverChangedKeys) {
+ if (originalKey === serverKey) {
+ unresolvedCollision = true;
+ break;
+ }
+ }
+ }
+
+ if (unresolvedCollision) {
+ const title = i18n.translate('data.indexPatterns.unableWriteLabel', {
+ defaultMessage:
+ 'Unable to write index pattern! Refresh the page to get the most up to date changes for this index pattern.',
+ });
+
+ this.onNotification({ title, color: 'danger' });
+ throw err;
+ }
+
+ // Set the updated response on this object
+ serverChangedKeys.forEach((key) => {
+ this[key] = samePattern[key];
+ });
+ this.version = samePattern.version;
+
+ // Clear cache
+ this.patternCache.clear(this.id!);
+
+ // Try the save again
+ return this.save(saveAttempts);
+ });
+ }
+ throw err;
+ });
+ }
+
+ async _fetchFields() {
+ const fields = await this.fieldsFetcher.fetch(this);
+ const scripted = this.getScriptedFields();
+ const all = fields.concat(scripted);
+ await this.initFields(all);
+ }
+
+ refreshFields() {
+ return this._fetchFields()
+ .then(() => this.save())
+ .catch((err) => {
+ // https://github.com/elastic/kibana/issues/9224
+ // This call will attempt to remap fields from the matching
+ // ES index which may not actually exist. In that scenario,
+ // we still want to notify the user that there is a problem
+ // but we do not want to potentially make any pages unusable
+ // so do not rethrow the error here
+
+ if (err instanceof IndexPatternMissingIndices) {
+ this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' });
+ return [];
+ }
+
+ this.onError(err, {
+ title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', {
+ defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})',
+ values: {
+ id: this.id,
+ title: this.title,
+ },
+ }),
+ });
+ });
+ }
+
+ toJSON() {
+ return this.id;
+ }
+
+ toString() {
+ return '' + this.toJSON();
+ }
+
+ destroy() {
+ if (this.id) {
+ this.patternCache.clear(this.id);
+ return this.savedObjectsClient.delete(type, this.id);
+ }
+ }
+}
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts
similarity index 91%
rename from src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts
index fc0be270e9c50..5ff19a3c54cb1 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts
@@ -21,11 +21,11 @@
import { IndexPatternsService } from './index_patterns';
import { SavedObjectsClientContract, SavedObjectsFindResponsePublic } from 'kibana/public';
import { coreMock, httpServiceMock } from '../../../../../core/public/mocks';
-import { fieldFormatsServiceMock } from '../../field_formats/mocks';
+import { fieldFormatsMock } from '../../field_formats/mocks';
const core = coreMock.createStart();
const http = httpServiceMock.createStartContract();
-const fieldFormats = fieldFormatsServiceMock.createStartContract();
+const fieldFormats = fieldFormatsMock;
jest.mock('./index_pattern', () => {
class IndexPattern {
@@ -62,7 +62,15 @@ describe('IndexPatterns', () => {
}) as Promise>
);
- indexPatterns = new IndexPatternsService(core, savedObjectsClient, http, fieldFormats);
+ indexPatterns = new IndexPatternsService(
+ core.uiSettings,
+ savedObjectsClient,
+ http,
+ fieldFormats,
+ () => {},
+ () => {},
+ () => {}
+ );
});
test('does cache gets for the same id', async () => {
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
similarity index 85%
rename from src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
index 32b31d4f2758d..db2a68956f106 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
@@ -25,9 +25,9 @@ import {
CoreStart,
} from 'src/core/public';
-import { createIndexPatternCache } from './_pattern_cache';
+import { createIndexPatternCache } from '.';
import { IndexPattern } from './index_pattern';
-import { IndexPatternsApiClient, GetFieldsOptions } from './index_patterns_api_client';
+import { IndexPatternsApiClient, GetFieldsOptions } from '.';
import {
createEnsureDefaultIndexPattern,
EnsureDefaultIndexPattern,
@@ -38,7 +38,8 @@ import {
Field,
FieldSpec,
} from '../fields';
-import { FieldFormatsStart } from '../../field_formats';
+import { OnNotification, OnError } from '../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
const indexPatternCache = createIndexPatternCache();
@@ -53,6 +54,9 @@ export class IndexPatternsService {
private savedObjectsClient: SavedObjectsClientContract;
private savedObjectsCache?: Array> | null;
private apiClient: IndexPatternsApiClient;
+ private fieldFormats: FieldFormatsStartCommon;
+ private onNotification: OnNotification;
+ private onError: OnError;
ensureDefaultIndexPattern: EnsureDefaultIndexPattern;
createFieldList: CreateIndexPatternFieldList;
createField: (
@@ -62,23 +66,32 @@ export class IndexPatternsService {
) => Field;
constructor(
- core: CoreStart,
+ uiSettings: CoreStart['uiSettings'],
savedObjectsClient: SavedObjectsClientContract,
http: HttpStart,
- fieldFormats: FieldFormatsStart
+ fieldFormats: FieldFormatsStartCommon,
+ onNotification: OnNotification,
+ onError: OnError,
+ onRedirectNoIndexPattern: () => void
) {
this.apiClient = new IndexPatternsApiClient(http);
- this.config = core.uiSettings;
+ this.config = uiSettings;
this.savedObjectsClient = savedObjectsClient;
- this.ensureDefaultIndexPattern = createEnsureDefaultIndexPattern(core);
+ this.fieldFormats = fieldFormats;
+ this.onNotification = onNotification;
+ this.onError = onError;
+ this.ensureDefaultIndexPattern = createEnsureDefaultIndexPattern(
+ uiSettings,
+ onRedirectNoIndexPattern
+ );
this.createFieldList = getIndexPatternFieldListCreator({
fieldFormats,
- toastNotifications: core.notifications.toasts,
+ onNotification,
});
this.createField = (indexPattern, spec, shortDotsEnable) => {
return new Field(indexPattern, spec, shortDotsEnable, {
fieldFormats,
- toastNotifications: core.notifications.toasts,
+ onNotification,
});
};
}
@@ -178,7 +191,10 @@ export class IndexPatternsService {
(cfg: any) => this.config.get(cfg),
this.savedObjectsClient,
this.apiClient,
- indexPatternCache
+ indexPatternCache,
+ this.fieldFormats,
+ this.onNotification,
+ this.onError
);
return indexPattern.init();
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.test.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.test.ts
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts
rename to src/plugins/data/common/index_patterns/index_patterns/index_patterns_api_client.ts
diff --git a/src/plugins/data/public/index_patterns/index_patterns/types.ts b/src/plugins/data/common/index_patterns/index_patterns/types.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/index_patterns/types.ts
rename to src/plugins/data/common/index_patterns/index_patterns/types.ts
diff --git a/src/plugins/data/public/index_patterns/lib/errors.ts b/src/plugins/data/common/index_patterns/lib/errors.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/errors.ts
rename to src/plugins/data/common/index_patterns/lib/errors.ts
diff --git a/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts b/src/plugins/data/common/index_patterns/lib/get_from_saved_object.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
rename to src/plugins/data/common/index_patterns/lib/get_from_saved_object.ts
diff --git a/src/plugins/data/public/index_patterns/lib/get_title.ts b/src/plugins/data/common/index_patterns/lib/get_title.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/get_title.ts
rename to src/plugins/data/common/index_patterns/lib/get_title.ts
diff --git a/src/plugins/data/public/index_patterns/lib/index.ts b/src/plugins/data/common/index_patterns/lib/index.ts
similarity index 99%
rename from src/plugins/data/public/index_patterns/lib/index.ts
rename to src/plugins/data/common/index_patterns/lib/index.ts
index 2893096c4af9d..d9eccb6685ded 100644
--- a/src/plugins/data/public/index_patterns/lib/index.ts
+++ b/src/plugins/data/common/index_patterns/lib/index.ts
@@ -17,9 +17,10 @@
* under the License.
*/
-export { getTitle } from './get_title';
-export * from './types';
-export { validateIndexPattern } from './validate_index_pattern';
export { IndexPatternMissingIndices } from './errors';
+export { getTitle } from './get_title';
export { getFromSavedObject } from './get_from_saved_object';
export { isDefault } from './is_default';
+
+export * from './types';
+export { validateIndexPattern } from './validate_index_pattern';
diff --git a/src/plugins/data/public/index_patterns/lib/is_default.ts b/src/plugins/data/common/index_patterns/lib/is_default.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/is_default.ts
rename to src/plugins/data/common/index_patterns/lib/is_default.ts
diff --git a/src/plugins/data/public/index_patterns/lib/types.ts b/src/plugins/data/common/index_patterns/lib/types.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/types.ts
rename to src/plugins/data/common/index_patterns/lib/types.ts
diff --git a/src/plugins/data/public/index_patterns/lib/validate_index_pattern.test.ts b/src/plugins/data/common/index_patterns/lib/validate_index_pattern.test.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/validate_index_pattern.test.ts
rename to src/plugins/data/common/index_patterns/lib/validate_index_pattern.test.ts
diff --git a/src/plugins/data/public/index_patterns/lib/validate_index_pattern.ts b/src/plugins/data/common/index_patterns/lib/validate_index_pattern.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/lib/validate_index_pattern.ts
rename to src/plugins/data/common/index_patterns/lib/validate_index_pattern.ts
diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts
index a9e8652a09a5e..cf88b0356766c 100644
--- a/src/plugins/data/common/index_patterns/types.ts
+++ b/src/plugins/data/common/index_patterns/types.ts
@@ -17,6 +17,7 @@
* under the License.
*/
+import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notifications';
import { IFieldType } from './fields';
export interface IIndexPattern {
@@ -48,3 +49,6 @@ export interface IndexPatternAttributes {
typeMeta: string;
timeFieldName?: string;
}
+
+export type OnNotification = (toastInputFields: ToastInputFields) => void;
+export type OnError = (error: Error, toastInputFields: ErrorToastOptions) => void;
diff --git a/src/plugins/data/public/index_patterns/utils.ts b/src/plugins/data/common/index_patterns/utils.ts
similarity index 100%
rename from src/plugins/data/public/index_patterns/utils.ts
rename to src/plugins/data/common/index_patterns/utils.ts
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index c80a6684ce56b..e57b02784ca9e 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -443,6 +443,8 @@ export {
getKbnTypeNames,
} from '../common';
+export * from '../common/field_mapping';
+
/*
* Plugin setup
*/
diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts
index 58c2cae1de0f3..0a8397467807c 100644
--- a/src/plugins/data/public/index_patterns/index.ts
+++ b/src/plugins/data/public/index_patterns/index.ts
@@ -25,10 +25,14 @@ export {
validateIndexPattern,
getFromSavedObject,
isDefault,
-} from './lib';
-export { flattenHitWrapper, formatHitProvider } from './index_patterns';
+} from '../../common/index_patterns/lib';
+export { flattenHitWrapper, formatHitProvider, onRedirectNoIndexPattern } from './index_patterns';
-export { getIndexPatternFieldListCreator, Field, IIndexPatternFieldList } from './fields';
+export {
+ getIndexPatternFieldListCreator,
+ Field,
+ IIndexPatternFieldList,
+} from '../../common/index_patterns';
// TODO: figure out how to replace IndexPatterns in get_inner_angular.
export {
diff --git a/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx b/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx
deleted file mode 100644
index 2088bd8c925df..0000000000000
--- a/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { contains } from 'lodash';
-import React from 'react';
-import { History } from 'history';
-import { i18n } from '@kbn/i18n';
-import { EuiCallOut } from '@elastic/eui';
-import { CoreStart } from 'kibana/public';
-import { toMountPoint } from '../../../../kibana_react/public';
-import { IndexPatternsContract } from './index_patterns';
-
-export type EnsureDefaultIndexPattern = (history: History) => Promise | undefined;
-
-export const createEnsureDefaultIndexPattern = (core: CoreStart) => {
- let bannerId: string;
- let timeoutId: NodeJS.Timeout | undefined;
-
- /**
- * Checks whether a default index pattern is set and exists and defines
- * one otherwise.
- *
- * If there are no index patterns, redirect to management page and show
- * banner. In this case the promise returned from this function will never
- * resolve to wait for the URL change to happen.
- */
- return async function ensureDefaultIndexPattern(this: IndexPatternsContract, history: History) {
- const patterns = await this.getIds();
- let defaultId = core.uiSettings.get('defaultIndex');
- let defined = !!defaultId;
- const exists = contains(patterns, defaultId);
-
- if (defined && !exists) {
- core.uiSettings.remove('defaultIndex');
- defaultId = defined = false;
- }
-
- if (defined) {
- return;
- }
-
- // If there is any index pattern created, set the first as default
- if (patterns.length >= 1) {
- defaultId = patterns[0];
- core.uiSettings.set('defaultIndex', defaultId);
- } else {
- const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns;
- const redirectTarget = canManageIndexPatterns ? '/management/kibana/indexPatterns' : '/home';
-
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
-
- const bannerMessage = i18n.translate(
- 'data.indexPatterns.ensureDefaultIndexPattern.bannerLabel',
- {
- defaultMessage:
- "In order to visualize and explore data in Kibana, you'll need to create an index pattern to retrieve data from Elasticsearch.",
- }
- );
-
- // Avoid being hostile to new users who don't have an index pattern setup yet
- // give them a friendly info message instead of a terse error message
- bannerId = core.overlays.banners.replace(
- bannerId,
- toMountPoint()
- );
-
- // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
- timeoutId = setTimeout(() => {
- core.overlays.banners.remove(bannerId);
- timeoutId = undefined;
- }, 15000);
-
- if (redirectTarget === '/home') {
- core.application.navigateToApp('home');
- } else {
- core.application.navigateToApp('management', {
- path: `/kibana/indexPatterns?bannerMessage=${bannerMessage}`,
- });
- }
-
- // return never-resolving promise to stop resolving and wait for the url change
- return new Promise(() => {});
- }
- };
-};
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index_patterns/index.ts
index fca82025cdc66..0db1c8c68b4ac 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index.ts
@@ -17,9 +17,5 @@
* under the License.
*/
-export * from './flatten_hit';
-export * from './format_hit';
-export * from './index_pattern';
-export * from './index_patterns';
-export * from './index_patterns_api_client';
-export * from './types';
+export * from '../../../common/index_patterns/index_patterns';
+export * from './redirect_no_index_pattern';
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx
index 2dc9330b1a461..c497c0e002105 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx
@@ -24,6 +24,7 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { SavedObjectsClientContract } from 'src/core/public';
+<<<<<<< HEAD:src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx
import {
DuplicateField,
SavedObjectNotFound,
@@ -32,6 +33,9 @@ import {
MappingObject,
} from '../../../../kibana_utils/public';
import { toMountPoint } from '../../../../kibana_react/public';
+=======
+import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common';
+>>>>>>> 40d2cfc4db6... Index pattern public api => common (#68289):src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
import {
ES_FIELD_TYPES,
@@ -46,10 +50,18 @@ import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '
import { createFieldsFetcher } from './_fields_fetcher';
import { formatHitProvider } from './format_hit';
import { flattenHitWrapper } from './flatten_hit';
+<<<<<<< HEAD:src/plugins/data/public/index_patterns/index_patterns/index_pattern.tsx
import { IIndexPatternsApiClient } from './index_patterns_api_client';
import { getNotifications, getFieldFormats, getHttp } from '../../services';
import { TypeMeta } from './types';
+=======
+import { IIndexPatternsApiClient } from '.';
+import { TypeMeta } from '.';
+import { OnNotification, OnError } from '../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
+>>>>>>> 40d2cfc4db6... Index pattern public api => common (#68289):src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
import { PatternCache } from './_pattern_cache';
+import { expandShorthand, FieldMappingSpec, MappingObject } from '../../field_mapping';
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
const type = 'index-pattern';
@@ -78,6 +90,9 @@ export class IndexPattern implements IIndexPattern {
private originalBody: { [key: string]: any } = {};
public fieldsFetcher: any; // probably want to factor out any direct usage and change to private
private shortDotsEnable: boolean = false;
+ private fieldFormats: FieldFormatsStartCommon;
+ private onNotification: OnNotification;
+ private onError: OnError;
private apiClient: IIndexPatternsApiClient;
private mapping: MappingObject = expandShorthand({
@@ -107,7 +122,10 @@ export class IndexPattern implements IIndexPattern {
getConfig: any,
savedObjectsClient: SavedObjectsClientContract,
apiClient: IIndexPatternsApiClient,
- patternCache: PatternCache
+ patternCache: PatternCache,
+ fieldFormats: FieldFormatsStartCommon,
+ onNotification: OnNotification,
+ onError: OnError
) {
this.id = id;
this.savedObjectsClient = savedObjectsClient;
@@ -115,13 +133,16 @@ export class IndexPattern implements IIndexPattern {
// instead of storing config we rather store the getter only as np uiSettingsClient has circular references
// which cause problems when being consumed from angular
this.getConfig = getConfig;
+ this.fieldFormats = fieldFormats;
+ this.onNotification = onNotification;
+ this.onError = onError;
this.shortDotsEnable = this.getConfig(UI_SETTINGS.SHORT_DOTS_ENABLE);
this.metaFields = this.getConfig(UI_SETTINGS.META_FIELDS);
this.createFieldList = getIndexPatternFieldListCreator({
- fieldFormats: getFieldFormats(),
- toastNotifications: getNotifications().toasts,
+ fieldFormats,
+ onNotification,
});
this.fields = this.createFieldList(this, [], this.shortDotsEnable);
@@ -134,7 +155,7 @@ export class IndexPattern implements IIndexPattern {
this.flattenHit = flattenHitWrapper(this, this.getConfig(UI_SETTINGS.META_FIELDS));
this.formatHit = formatHitProvider(
this,
- getFieldFormats().getDefaultInstance(KBN_FIELD_TYPES.STRING)
+ fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING)
);
this.formatField = this.formatHit.formatField;
}
@@ -146,7 +167,7 @@ export class IndexPattern implements IIndexPattern {
}
private deserializeFieldFormatMap(mapping: any) {
- const FieldFormat = getFieldFormats().getType(mapping.id);
+ const FieldFormat = this.fieldFormats.getType(mapping.id);
return FieldFormat && new FieldFormat(mapping.params, this.getConfig);
}
@@ -361,8 +382,8 @@ export class IndexPattern implements IIndexPattern {
},
false,
{
- fieldFormats: getFieldFormats(),
- toastNotifications: getNotifications().toasts,
+ fieldFormats: this.fieldFormats,
+ onNotification: this.onNotification,
}
)
);
@@ -488,8 +509,12 @@ export class IndexPattern implements IIndexPattern {
this.getConfig,
this.savedObjectsClient,
this.apiClient,
- this.patternCache
+ this.patternCache,
+ this.fieldFormats,
+ this.onNotification,
+ this.onError
);
+
await duplicatePattern.destroy();
}
@@ -537,7 +562,10 @@ export class IndexPattern implements IIndexPattern {
this.getConfig,
this.savedObjectsClient,
this.apiClient,
- this.patternCache
+ this.patternCache,
+ this.fieldFormats,
+ this.onNotification,
+ this.onError
);
return samePattern.init().then(() => {
// What keys changed from now and what the server returned
@@ -562,14 +590,12 @@ export class IndexPattern implements IIndexPattern {
}
if (unresolvedCollision) {
- const message = i18n.translate('data.indexPatterns.unableWriteLabel', {
+ const title = i18n.translate('data.indexPatterns.unableWriteLabel', {
defaultMessage:
'Unable to write index pattern! Refresh the page to get the most up to date changes for this index pattern.',
});
- const { toasts } = getNotifications();
-
- toasts.addDanger(message);
+ this.onNotification({ title, color: 'danger' });
throw err;
}
@@ -607,15 +633,13 @@ export class IndexPattern implements IIndexPattern {
// we still want to notify the user that there is a problem
// but we do not want to potentially make any pages unusable
// so do not rethrow the error here
- const { toasts } = getNotifications();
if (err instanceof IndexPatternMissingIndices) {
- toasts.addDanger((err as any).message);
-
+ this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' });
return [];
}
- toasts.addError(err, {
+ this.onError(err, {
title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', {
defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})',
values: {
diff --git a/src/plugins/data/public/index_patterns/index_patterns/redirect_no_index_pattern.tsx b/src/plugins/data/public/index_patterns/index_patterns/redirect_no_index_pattern.tsx
new file mode 100644
index 0000000000000..e32a8e023cf40
--- /dev/null
+++ b/src/plugins/data/public/index_patterns/index_patterns/redirect_no_index_pattern.tsx
@@ -0,0 +1,69 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { EuiCallOut } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { CoreStart } from 'kibana/public';
+import { toMountPoint } from '../../../../kibana_react/public';
+
+let bannerId: string;
+
+export const onRedirectNoIndexPattern = (
+ capabilities: CoreStart['application']['capabilities'],
+ navigateToApp: CoreStart['application']['navigateToApp'],
+ overlays: CoreStart['overlays']
+) => () => {
+ const canManageIndexPatterns = capabilities.management.kibana.index_patterns;
+ const redirectTarget = canManageIndexPatterns ? '/management/kibana/indexPatterns' : '/home';
+ let timeoutId: NodeJS.Timeout | undefined;
+
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+
+ const bannerMessage = i18n.translate('data.indexPatterns.ensureDefaultIndexPattern.bannerLabel', {
+ defaultMessage:
+ "In order to visualize and explore data in Kibana, you'll need to create an index pattern to retrieve data from Elasticsearch.",
+ });
+
+ // Avoid being hostile to new users who don't have an index pattern setup yet
+ // give them a friendly info message instead of a terse error message
+ bannerId = overlays.banners.replace(
+ bannerId,
+ toMountPoint()
+ );
+
+ // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
+ timeoutId = setTimeout(() => {
+ overlays.banners.remove(bannerId);
+ timeoutId = undefined;
+ }, 15000);
+
+ if (redirectTarget === '/home') {
+ navigateToApp('home');
+ } else {
+ navigateToApp('management', {
+ path: `/kibana/indexPatterns?bannerMessage=${bannerMessage}`,
+ });
+ }
+
+ // return never-resolving promise to stop resolving and wait for the url change
+ return new Promise(() => {});
+};
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 06b5cbdfdfdfb..baac2cf0d7a77 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -40,7 +40,7 @@ import { SearchService } from './search/search_service';
import { FieldFormatsService } from './field_formats';
import { QueryService } from './query';
import { createIndexPatternSelect } from './ui/index_pattern_select';
-import { IndexPatternsService } from './index_patterns';
+import { IndexPatternsService, onRedirectNoIndexPattern } from './index_patterns';
import {
setFieldFormats,
setHttp,
@@ -154,7 +154,7 @@ export class DataPublicPlugin implements Plugin {
+ notifications.toasts.add(toastInputFields);
+ },
+ notifications.toasts.addError,
+ onRedirectNoIndexPattern(application.capabilities, application.navigateToApp, overlays)
+ );
setIndexPatterns(indexPatterns);
const query = this.queryService.start(savedObjects);
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 1723828629913..dfa45a43c59de 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -15,6 +15,7 @@ import { CoreSetup } from 'src/core/public';
import { CoreStart } from 'kibana/public';
import { CoreStart as CoreStart_2 } from 'src/core/public';
import { Ensure } from '@kbn/utility-types';
+import { ErrorToastOptions } from 'src/core/public/notifications';
import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
@@ -53,6 +54,7 @@ import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
import { SimpleSavedObject } from 'src/core/public';
import { Subscription } from 'rxjs';
import { Toast } from 'kibana/public';
+import { ToastInputFields } from 'src/core/public/notifications';
import { ToastsStart } from 'kibana/public';
import { TypeOf } from '@kbn/config-schema';
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
@@ -421,6 +423,11 @@ export type ExistsFilter = Filter & {
exists?: FilterExistsProperty;
};
+// Warning: (ae-forgotten-export) The symbol "ShorthandFieldMapObject" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+export const expandShorthand: (sh: Record) => Record;
+
// Warning: (ae-forgotten-export) The symbol "SavedObjectReference" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "extractReferences" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -547,6 +554,16 @@ export type FieldFormatsContentType = 'html' | 'text';
// @public (undocumented)
export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+// @public (undocumented)
+export interface FieldMappingSpec {
+ // (undocumented)
+ _deserialize?: (mapping: string) => any | undefined;
+ // (undocumented)
+ _serialize?: (mapping: any) => string | undefined;
+ // (undocumented)
+ type: ES_FIELD_TYPES;
+}
+
// Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -624,7 +641,7 @@ export function getEsPreference(uiSettings: IUiSettingsClient_2, sessionId?: str
// Warning: (ae-missing-release-tag) "getIndexPatternFieldListCreator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const getIndexPatternFieldListCreator: ({ fieldFormats, toastNotifications, }: FieldListDependencies) => CreateIndexPatternFieldList;
+export const getIndexPatternFieldListCreator: ({ fieldFormats, onNotification, }: FieldListDependencies) => CreateIndexPatternFieldList;
// Warning: (ae-missing-release-tag) "getKbnTypeNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -863,7 +880,10 @@ export type IMetricAggType = MetricAggType;
export class IndexPattern implements IIndexPattern {
// Warning: (ae-forgotten-export) The symbol "IIndexPatternsApiClient" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "PatternCache" needs to be exported by the entry point index.d.ts
- constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache);
+ // Warning: (ae-forgotten-export) The symbol "FieldFormatsStartCommon" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "OnNotification" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "OnError" needs to be exported by the entry point index.d.ts
+ constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache, fieldFormats: FieldFormatsStartCommon, onNotification: OnNotification, onError: OnError);
// (undocumented)
[key: string]: any;
// (undocumented)
@@ -999,7 +1019,7 @@ export class IndexPatternField implements IFieldType {
// (undocumented)
$$spec: FieldSpec;
// Warning: (ae-forgotten-export) The symbol "FieldDependencies" needs to be exported by the entry point index.d.ts
- constructor(indexPattern: IndexPattern, spec: FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, toastNotifications }: FieldDependencies);
+ constructor(indexPattern: IIndexPattern, spec: FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
// (undocumented)
aggregatable?: boolean;
// (undocumented)
@@ -1015,7 +1035,7 @@ export class IndexPatternField implements IFieldType {
// (undocumented)
format: any;
// (undocumented)
- indexPattern?: IndexPattern;
+ indexPattern?: IIndexPattern;
// (undocumented)
lang?: string;
// (undocumented)
@@ -1224,6 +1244,9 @@ export interface KueryNode {
type: keyof NodeTypes;
}
+// @public (undocumented)
+export type MappingObject = Record;
+
// Warning: (ae-missing-release-tag) "MatchAllFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
index 6eb4f82a940b1..20e3fdae5ce5f 100644
--- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
+++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
@@ -24,7 +24,7 @@ import { Required } from '@kbn/utility-types';
import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public';
-import { getTitle } from '../../index_patterns/lib';
+import { getTitle } from '../../../common/index_patterns/lib';
export type IndexPatternSelectProps = Required<
// Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'append' | 'prepend' | 'sortMatchesBy'>,
diff --git a/src/plugins/kibana_utils/public/errors/errors.test.ts b/src/plugins/kibana_utils/common/errors/errors.test.ts
similarity index 100%
rename from src/plugins/kibana_utils/public/errors/errors.test.ts
rename to src/plugins/kibana_utils/common/errors/errors.test.ts
diff --git a/src/plugins/kibana_utils/public/errors/errors.ts b/src/plugins/kibana_utils/common/errors/errors.ts
similarity index 100%
rename from src/plugins/kibana_utils/public/errors/errors.ts
rename to src/plugins/kibana_utils/common/errors/errors.ts
diff --git a/src/plugins/kibana_utils/public/errors/index.ts b/src/plugins/kibana_utils/common/errors/index.ts
similarity index 100%
rename from src/plugins/kibana_utils/public/errors/index.ts
rename to src/plugins/kibana_utils/common/errors/index.ts
diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts
index 87b625ef9a64f..99daed98dbe64 100644
--- a/src/plugins/kibana_utils/common/index.ts
+++ b/src/plugins/kibana_utils/common/index.ts
@@ -22,6 +22,7 @@ export * from './of';
export * from './ui';
export * from './state_containers';
export * from './typed_json';
+export * from './errors';
export { createGetterSetter, Get, Set } from './create_getter_setter';
export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value';
export { url } from './url';
diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx
index cffb7ff4549a8..d8121b656deeb 100644
--- a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx
+++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx
@@ -24,7 +24,7 @@ import ReactDOM from 'react-dom';
import ReactMarkdown from 'react-markdown';
import { ApplicationStart, HttpStart, ToastsSetup } from 'kibana/public';
-import { SavedObjectNotFound } from '../errors';
+import { SavedObjectNotFound } from '..';
interface Mapping {
[key: string]: string | { app: string; path: string };
diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts
index 3d8a4414de70c..6f61e2c228970 100644
--- a/src/plugins/kibana_utils/public/index.ts
+++ b/src/plugins/kibana_utils/public/index.ts
@@ -34,8 +34,7 @@ export {
defaultFeedbackMessage,
} from '../common';
export * from './core';
-export * from './errors';
-export * from './field_mapping';
+export * from '../common/errors';
export * from './field_wildcard';
export * from './parse';
export * from './render_complete';
diff --git a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
index 9d0e25132271c..47390c7dc9104 100644
--- a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
@@ -18,11 +18,12 @@
*/
import _ from 'lodash';
import { EsResponse, SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../../types';
-import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public';
+import { SavedObjectNotFound } from '../../../../kibana_utils/public';
import {
IndexPattern,
injectSearchSourceReferences,
parseSearchSourceJSON,
+ expandShorthand,
} from '../../../../data/public';
/**
diff --git a/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
index acb371b8af9c2..24e467ad18ac4 100644
--- a/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
@@ -18,8 +18,7 @@
*/
import _ from 'lodash';
import { SavedObject, SavedObjectConfig } from '../../types';
-import { expandShorthand } from '../../../../kibana_utils/public';
-import { extractSearchSourceReferences } from '../../../../data/public';
+import { extractSearchSourceReferences, expandShorthand } from '../../../../data/public';
export function serializeSavedObject(savedObject: SavedObject, config: SavedObjectConfig) {
// mapping definition for the fields that this object will expose