Index pattern serialize and de-serialize (#68844)

* serialize and deserialize index patterns
This commit is contained in:
Matthew Kime 2020-06-25 09:21:41 -05:00 committed by GitHub
parent ec405931d2
commit b02e2d9de4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 868 additions and 96 deletions

View file

@ -28,6 +28,7 @@ export interface IFieldType
| [searchable](./kibana-plugin-plugins-data-public.ifieldtype.searchable.md) | <code>boolean</code> | |
| [sortable](./kibana-plugin-plugins-data-public.ifieldtype.sortable.md) | <code>boolean</code> | |
| [subType](./kibana-plugin-plugins-data-public.ifieldtype.subtype.md) | <code>IFieldSubType</code> | |
| [toSpec](./kibana-plugin-plugins-data-public.ifieldtype.tospec.md) | <code>() =&gt; FieldSpec</code> | |
| [type](./kibana-plugin-plugins-data-public.ifieldtype.type.md) | <code>string</code> | |
| [visualizable](./kibana-plugin-plugins-data-public.ifieldtype.visualizable.md) | <code>boolean</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) &gt; [toSpec](./kibana-plugin-plugins-data-public.ifieldtype.tospec.md)
## IFieldType.toSpec property
<b>Signature:</b>
```typescript
toSpec?: () => FieldSpec;
```

View file

@ -7,5 +7,7 @@
<b>Signature:</b>
```typescript
fields: IIndexPatternFieldList;
fields: IIndexPatternFieldList & {
toSpec: () => FieldSpec[];
};
```

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [initFromSpec](./kibana-plugin-plugins-data-public.indexpattern.initfromspec.md)
## IndexPattern.initFromSpec() method
<b>Signature:</b>
```typescript
initFromSpec(spec: IndexPatternSpec): this;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| spec | <code>IndexPatternSpec</code> | |
<b>Returns:</b>
`this`

View file

@ -21,7 +21,7 @@ export declare class IndexPattern implements IIndexPattern
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | <code>any</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | <code>IIndexPatternFieldList</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | <code>IIndexPatternFieldList &amp; {</code><br/><code> toSpec: () =&gt; FieldSpec[];</code><br/><code> }</code> | |
| [fieldsFetcher](./kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md) | | <code>any</code> | |
| [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | <code>any</code> | |
| [formatField](./kibana-plugin-plugins-data-public.indexpattern.formatfield.md) | | <code>any</code> | |
@ -30,7 +30,6 @@ export declare class IndexPattern implements IIndexPattern
| [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | <code>string[]</code> | |
| [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | <code>string &#124; undefined</code> | |
| [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | <code>string</code> | |
| [type](./kibana-plugin-plugins-data-public.indexpattern.type.md) | | <code>string</code> | |
| [typeMeta](./kibana-plugin-plugins-data-public.indexpattern.typemeta.md) | | <code>TypeMeta</code> | |
## Methods
@ -49,6 +48,7 @@ export declare class IndexPattern implements IIndexPattern
| [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | |
| [getTimeField()](./kibana-plugin-plugins-data-public.indexpattern.gettimefield.md) | | |
| [init(forceFieldRefresh)](./kibana-plugin-plugins-data-public.indexpattern.init.md) | | |
| [initFromSpec(spec)](./kibana-plugin-plugins-data-public.indexpattern.initfromspec.md) | | |
| [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | |
| [isTimeBasedWildcard()](./kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md) | | |
| [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | |
@ -59,5 +59,6 @@ export declare class IndexPattern implements IIndexPattern
| [removeScriptedField(field)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | |
| [save(saveAttempts)](./kibana-plugin-plugins-data-public.indexpattern.save.md) | | |
| [toJSON()](./kibana-plugin-plugins-data-public.indexpattern.tojson.md) | | |
| [toSpec()](./kibana-plugin-plugins-data-public.indexpattern.tospec.md) | | |
| [toString()](./kibana-plugin-plugins-data-public.indexpattern.tostring.md) | | |

View file

@ -1,11 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [type](./kibana-plugin-plugins-data-public.indexpattern.type.md)
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [toSpec](./kibana-plugin-plugins-data-public.indexpattern.tospec.md)
## IndexPattern.type property
## IndexPattern.toSpec() method
<b>Signature:</b>
```typescript
type?: string;
toSpec(): IndexPatternSpec;
```
<b>Returns:</b>
`IndexPatternSpec`

View file

@ -9,7 +9,7 @@ Constructs a new instance of the `Field` class
<b>Signature:</b>
```typescript
constructor(indexPattern: IIndexPattern, spec: FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
constructor(indexPattern: IIndexPattern, spec: FieldSpecExportFmt | FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
```
## Parameters
@ -17,7 +17,7 @@ constructor(indexPattern: IIndexPattern, spec: FieldSpec | Field, shortDotsEnabl
| Parameter | Type | Description |
| --- | --- | --- |
| indexPattern | <code>IIndexPattern</code> | |
| spec | <code>FieldSpec &#124; Field</code> | |
| spec | <code>FieldSpecExportFmt &#124; FieldSpec &#124; Field</code> | |
| shortDotsEnable | <code>boolean</code> | |
| { fieldFormats, onNotification } | <code>FieldDependencies</code> | |

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
conflictDescriptions?: Record<string, string[]>;
conflictDescriptions?: FieldSpecConflictDescriptions;
```

View file

@ -22,7 +22,7 @@ export declare class Field implements IFieldType
| --- | --- | --- | --- |
| [$$spec](./kibana-plugin-plugins-data-public.indexpatternfield.__spec.md) | | <code>FieldSpec</code> | |
| [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) | | <code>boolean</code> | |
| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | <code>Record&lt;string, string[]&gt;</code> | |
| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | <code>FieldSpecConflictDescriptions</code> | |
| [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | <code>number</code> | |
| [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | <code>string</code> | |
| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | <code>string[]</code> | |
@ -37,6 +37,7 @@ export declare class Field implements IFieldType
| [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | <code>boolean</code> | |
| [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) | | <code>boolean</code> | |
| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | <code>IFieldSubType</code> | |
| [toSpec](./kibana-plugin-plugins-data-public.indexpatternfield.tospec.md) | | <code>() =&gt; FieldSpecExportFmt</code> | |
| [type](./kibana-plugin-plugins-data-public.indexpatternfield.type.md) | | <code>string</code> | |
| [visualizable](./kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md) | | <code>boolean</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) &gt; [toSpec](./kibana-plugin-plugins-data-public.indexpatternfield.tospec.md)
## IndexPatternField.toSpec property
<b>Signature:</b>
```typescript
toSpec: () => FieldSpecExportFmt;
```

View file

@ -28,6 +28,7 @@ export interface IFieldType
| [searchable](./kibana-plugin-plugins-data-server.ifieldtype.searchable.md) | <code>boolean</code> | |
| [sortable](./kibana-plugin-plugins-data-server.ifieldtype.sortable.md) | <code>boolean</code> | |
| [subType](./kibana-plugin-plugins-data-server.ifieldtype.subtype.md) | <code>IFieldSubType</code> | |
| [toSpec](./kibana-plugin-plugins-data-server.ifieldtype.tospec.md) | <code>() =&gt; FieldSpec</code> | |
| [type](./kibana-plugin-plugins-data-server.ifieldtype.type.md) | <code>string</code> | |
| [visualizable](./kibana-plugin-plugins-data-server.ifieldtype.visualizable.md) | <code>boolean</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) &gt; [toSpec](./kibana-plugin-plugins-data-server.ifieldtype.tospec.md)
## IFieldType.toSpec property
<b>Signature:</b>
```typescript
toSpec?: () => FieldSpec;
```

View file

@ -27,6 +27,7 @@ export function stubbedSavedObjectIndexPattern(id) {
id,
type: 'index-pattern',
attributes: {
timeFieldName: 'timestamp',
customFormats: '{}',
fields: mockLogstashFields,
},

View file

@ -33,3 +33,43 @@ Object {
"type": "type",
}
`;
exports[`Field spec snapshot 1`] = `
Object {
"aggregatable": true,
"conflictDescriptions": Object {
"a": Array [
"b",
"c",
],
"d": Array [
"e",
],
},
"count": 1,
"esTypes": Array [
"type",
],
"format": Object {
"id": "number",
"params": Object {
"pattern": "$0,0.[00]",
},
},
"lang": "lang",
"name": "name",
"readFromDocValues": false,
"script": "script",
"scripted": true,
"searchable": true,
"subType": Object {
"multi": Object {
"parent": "parent",
},
"nested": Object {
"path": "path",
},
},
"type": "type",
}
`;

View file

@ -20,7 +20,7 @@
import { Field } from './field';
import { IndexPattern } from '../index_patterns';
import { FieldFormatsStartCommon } from '../..';
import { KBN_FIELD_TYPES } from '../../../common';
import { KBN_FIELD_TYPES, FieldSpec, FieldSpecExportFmt } from '../../../common';
describe('Field', function () {
function flatten(obj: Record<string, any>) {
@ -59,8 +59,9 @@ describe('Field', function () {
fieldFormatMap: { name: {}, _source: {}, _score: {}, _id: {} },
} as unknown) as IndexPattern,
format: { name: 'formatName' },
$$spec: {},
$$spec: ({} as unknown) as FieldSpec,
conflictDescriptions: { a: ['b', 'c'], d: ['e'] },
toSpec: () => (({} as unknown) as FieldSpecExportFmt),
} as Field;
it('the correct properties are writable', () => {
@ -145,7 +146,7 @@ describe('Field', function () {
}).toThrow();
expect(() => {
field.$$spec = { a: 'b' };
field.$$spec = ({ a: 'b' } as unknown) as FieldSpec;
}).toThrow();
});
@ -219,4 +220,21 @@ describe('Field', function () {
});
expect(flatten(field)).toMatchSnapshot();
});
it('spec snapshot', () => {
const field = new Field(
{
fieldFormatMap: {
name: { toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }) },
},
} as IndexPattern,
fieldValues,
false,
{
fieldFormats: {} as FieldFormatsStartCommon,
onNotification: () => {},
}
);
expect(field.toSpec()).toMatchSnapshot();
});
});

View file

@ -28,11 +28,14 @@ import {
FieldFormat,
shortenDottedString,
} from '../../../common';
import { OnNotification } from '../types';
import {
OnNotification,
FieldSpec,
FieldSpecConflictDescriptions,
FieldSpecExportFmt,
} from '../types';
import { FieldFormatsStartCommon } from '../../field_formats';
export type FieldSpec = Record<string, any>;
interface FieldDependencies {
fieldFormats: FieldFormatsStartCommon;
onNotification: OnNotification;
@ -59,11 +62,11 @@ export class Field implements IFieldType {
readFromDocValues?: boolean;
format: any;
$$spec: FieldSpec;
conflictDescriptions?: Record<string, string[]>;
conflictDescriptions?: FieldSpecConflictDescriptions;
constructor(
indexPattern: IIndexPattern,
spec: FieldSpec | Field,
spec: FieldSpecExportFmt | FieldSpec | Field,
shortDotsEnable: boolean,
{ fieldFormats, onNotification }: FieldDependencies
) {
@ -95,7 +98,7 @@ export class Field implements IFieldType {
if (!type) type = getKbnFieldType('unknown');
let format = spec.format;
let format: any = spec.format;
if (!FieldFormat.isInstanceOfFieldFormat(format)) {
format =
@ -148,6 +151,26 @@ export class Field implements IFieldType {
// multi info
obj.fact('subType');
return obj.create();
const newObj = obj.create();
newObj.toSpec = function () {
return {
count: this.count,
script: this.script,
lang: this.lang,
conflictDescriptions: this.conflictDescriptions,
name: this.name,
type: this.type,
esTypes: this.esTypes,
scripted: this.scripted,
searchable: this.searchable,
aggregatable: this.aggregatable,
readFromDocValues: this.readFromDocValues,
subType: this.subType,
format: this.indexPattern?.fieldFormatMap[this.name]?.toJSON() || undefined,
};
};
return newObj;
}
// only providing type info as constructor returns new object instead of `this`
toSpec = () => (({} as unknown) as FieldSpecExportFmt);
}

View file

@ -20,8 +20,8 @@
import { findIndex } from 'lodash';
import { IIndexPattern } from '../../types';
import { IFieldType } from '../../../common';
import { Field, FieldSpec } from './field';
import { OnNotification } from '../types';
import { Field } from './field';
import { OnNotification, FieldSpec } from '../types';
import { FieldFormatsStartCommon } from '../../field_formats';
type FieldMap = Map<Field['name'], Field>;
@ -102,6 +102,10 @@ export const getIndexPatternFieldListCreator = ({
this.removeByGroup(newField);
this.setByGroup(newField);
};
toSpec = () => {
return [...this.map((field) => field.toSpec())];
};
}
return new FieldList(...fieldListParams);

View file

@ -17,10 +17,7 @@
* under the License.
*/
export interface IFieldSubType {
multi?: { parent: string };
nested?: { path: string };
}
import { FieldSpec, IFieldSubType } from '../types';
export interface IFieldType {
name: string;
@ -41,4 +38,5 @@ export interface IFieldType {
subType?: IFieldSubType;
displayName?: string;
format?: any;
toSpec?: () => FieldSpec;
}

View file

@ -0,0 +1,503 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IndexPattern toSpec should match snapshot 1`] = `
Object {
"fields": Array [
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 10,
"esTypes": Array [
"long",
],
"format": Object {
"id": "number",
"params": Object {
"pattern": "$0,0.[00]",
},
},
"lang": undefined,
"name": "bytes",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "number",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 20,
"esTypes": Array [
"boolean",
],
"format": undefined,
"lang": undefined,
"name": "ssl",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "boolean",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 30,
"esTypes": Array [
"date",
],
"format": undefined,
"lang": undefined,
"name": "@timestamp",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "date",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 30,
"esTypes": Array [
"date",
],
"format": undefined,
"lang": undefined,
"name": "time",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "date",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"keyword",
],
"format": undefined,
"lang": undefined,
"name": "@tags",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"date",
],
"format": undefined,
"lang": undefined,
"name": "utc_time",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "date",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"integer",
],
"format": undefined,
"lang": undefined,
"name": "phpmemory",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "number",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"ip",
],
"format": undefined,
"lang": undefined,
"name": "ip",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "ip",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"attachment",
],
"format": undefined,
"lang": undefined,
"name": "request_body",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "attachment",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"geo_point",
],
"format": undefined,
"lang": undefined,
"name": "point",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "geo_point",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"geo_shape",
],
"format": undefined,
"lang": undefined,
"name": "area",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "geo_shape",
},
Object {
"aggregatable": false,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"murmur3",
],
"format": undefined,
"lang": undefined,
"name": "hashed",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "murmur3",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"geo_point",
],
"format": undefined,
"lang": undefined,
"name": "geo.coordinates",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "geo_point",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"text",
],
"format": undefined,
"lang": undefined,
"name": "extension",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"keyword",
],
"format": undefined,
"lang": undefined,
"name": "extension.keyword",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": Object {
"multi": Object {
"parent": "extension",
},
},
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"text",
],
"format": undefined,
"lang": undefined,
"name": "machine.os",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"keyword",
],
"format": undefined,
"lang": undefined,
"name": "machine.os.raw",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": Object {
"multi": Object {
"parent": "machine.os",
},
},
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"keyword",
],
"format": undefined,
"lang": undefined,
"name": "geo.src",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"_id",
],
"format": undefined,
"lang": undefined,
"name": "_id",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"_type",
],
"format": undefined,
"lang": undefined,
"name": "_type",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"_source",
],
"format": undefined,
"lang": undefined,
"name": "_source",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "_source",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"text",
],
"format": undefined,
"lang": undefined,
"name": "non-filterable",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": false,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": false,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"text",
],
"format": undefined,
"lang": undefined,
"name": "non-sortable",
"readFromDocValues": false,
"script": undefined,
"scripted": false,
"searchable": false,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"conflict",
],
"format": undefined,
"lang": undefined,
"name": "custom_user_field",
"readFromDocValues": true,
"script": undefined,
"scripted": false,
"searchable": true,
"subType": undefined,
"type": "conflict",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"text",
],
"format": undefined,
"lang": "expression",
"name": "script string",
"readFromDocValues": false,
"script": "'i am a string'",
"scripted": true,
"searchable": true,
"subType": undefined,
"type": "string",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"long",
],
"format": undefined,
"lang": "expression",
"name": "script number",
"readFromDocValues": false,
"script": "1234",
"scripted": true,
"searchable": true,
"subType": undefined,
"type": "number",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"date",
],
"format": undefined,
"lang": "painless",
"name": "script date",
"readFromDocValues": false,
"script": "1234",
"scripted": true,
"searchable": true,
"subType": undefined,
"type": "date",
},
Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"esTypes": Array [
"murmur3",
],
"format": undefined,
"lang": "expression",
"name": "script murmur3",
"readFromDocValues": false,
"script": "1234",
"scripted": true,
"searchable": true,
"subType": undefined,
"type": "murmur3",
},
],
"id": "test-pattern",
"sourceFilters": undefined,
"timeFieldName": "timestamp",
"title": "test-pattern",
"typeMeta": undefined,
"version": 2,
}
`;

View file

@ -18,7 +18,6 @@
*/
export * from './index_patterns_api_client';
export * from './types';
export * from './_pattern_cache';
export * from './flatten_hit';
export * from './format_hit';

View file

@ -30,6 +30,10 @@ import { Field } from '../fields';
import { fieldFormatsMock } from '../../field_formats/mocks';
class MockFieldFormatter {}
fieldFormatsMock.getType = jest.fn().mockImplementation(() => MockFieldFormatter);
jest.mock('../../field_mapping', () => {
const originalModule = jest.requireActual('../../field_mapping');
@ -303,6 +307,29 @@ describe('IndexPattern', () => {
});
});
describe('toSpec', () => {
test('should match snapshot', () => {
indexPattern.fieldFormatMap.bytes = {
toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }),
};
expect(indexPattern.toSpec()).toMatchSnapshot();
});
test('can restore from spec', async () => {
indexPattern.fieldFormatMap.bytes = {
toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }),
};
const spec = indexPattern.toSpec();
const restoredPattern = await create(spec.id as string);
restoredPattern.initFromSpec(spec);
expect(restoredPattern.id).toEqual(indexPattern.id);
expect(restoredPattern.title).toEqual(indexPattern.title);
expect(restoredPattern.timeFieldName).toEqual(indexPattern.timeFieldName);
expect(restoredPattern.fields.length).toEqual(indexPattern.fields.length);
expect(restoredPattern.fieldFormatMap.bytes instanceof MockFieldFormatter).toEqual(true);
});
});
describe('popularizeField', () => {
test('should increment the popularity count by default', () => {
// const saveSpy = sinon.stub(indexPattern, 'save');

View file

@ -20,6 +20,7 @@
import _, { each, reject } from 'lodash';
import { i18n } from '@kbn/i18n';
import { SavedObjectsClientContract } from 'src/core/public';
import { SavedObjectAttributes } from 'src/core/public';
import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common';
import {
@ -36,11 +37,12 @@ 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';
import { IndexPatternSpec, TypeMeta, FieldSpec, SourceFilter } from '../types';
import { SerializedFieldFormat } from '../../../../expressions/common';
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
const type = 'index-pattern';
@ -60,10 +62,9 @@ export class IndexPattern implements IIndexPattern {
public id?: string;
public title: string = '';
public type?: string;
public fieldFormatMap: any;
public typeMeta?: TypeMeta;
public fields: IIndexPatternFieldList;
public fields: IIndexPatternFieldList & { toSpec: () => FieldSpec[] };
public timeFieldName: string | undefined;
public formatHit: any;
public formatField: any;
@ -74,7 +75,7 @@ export class IndexPattern implements IIndexPattern {
private savedObjectsClient: SavedObjectsClientContract;
private patternCache: PatternCache;
private getConfig: any;
private sourceFilters?: [];
private sourceFilters?: SourceFilter[];
private originalBody: { [key: string]: any } = {};
public fieldsFetcher: any; // probably want to factor out any direct usage and change to private
private shortDotsEnable: boolean = false;
@ -196,6 +197,35 @@ export class IndexPattern implements IIndexPattern {
this.initFields();
}
public initFromSpec(spec: IndexPatternSpec) {
// create fieldFormatMap from field list
const fieldFormatMap: Record<string, SerializedFieldFormat> = {};
if (_.isArray(spec.fields)) {
spec.fields.forEach((field: FieldSpec) => {
if (field.format) {
fieldFormatMap[field.name as string] = { ...field.format };
}
});
}
this.version = spec.version;
this.title = spec.title || '';
this.timeFieldName = spec.timeFieldName;
this.sourceFilters = spec.sourceFilters;
// ignoring this because the same thing happens elsewhere but via _.assign
// @ts-ignore
this.fields = spec.fields || [];
this.typeMeta = spec.typeMeta;
this.fieldFormatMap = _.mapValues(fieldFormatMap, (mapping) => {
return this.deserializeFieldFormatMap(mapping);
});
this.initFields();
return this;
}
private updateFromElasticSearch(response: any, forceFieldRefresh: boolean = false) {
if (!response.found) {
throw new SavedObjectNotFound(type, this.id, 'management/kibana/indexPatterns');
@ -206,15 +236,16 @@ export class IndexPattern implements IIndexPattern {
return;
}
response._source[name] = fieldMapping._deserialize(response._source[name]);
response[name] = fieldMapping._deserialize(response[name]);
});
// give index pattern all of the values in _source
_.assign(this, response._source);
// give index pattern all of the values
_.assign(this, response);
if (!this.title && this.id) {
this.title = this.id;
}
this.version = response.version;
return this.indexFields(forceFieldRefresh);
}
@ -266,13 +297,11 @@ export class IndexPattern implements IIndexPattern {
}
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),
version: savedObject._version,
found: savedObject._version ? true : false,
...(_.cloneDeep(savedObject.attributes) as SavedObjectAttributes),
};
// Do this before we attempt to update from ES since that call can potentially perform a save
this.originalBody = this.prepBody();
@ -283,6 +312,19 @@ export class IndexPattern implements IIndexPattern {
return this;
}
public toSpec(): IndexPatternSpec {
return {
id: this.id,
version: this.version,
title: this.title,
timeFieldName: this.timeFieldName,
sourceFilters: this.sourceFilters,
fields: this.fields.toSpec(),
typeMeta: this.typeMeta,
};
}
// Get the source filtering configuration for that index.
getSourceFiltering() {
return {

View file

@ -32,12 +32,8 @@ import {
createEnsureDefaultIndexPattern,
EnsureDefaultIndexPattern,
} from './ensure_default_index_pattern';
import {
getIndexPatternFieldListCreator,
CreateIndexPatternFieldList,
Field,
FieldSpec,
} from '../fields';
import { getIndexPatternFieldListCreator, CreateIndexPatternFieldList, Field } from '../fields';
import { IndexPatternSpec, FieldSpec } from '../types';
import { OnNotification, OnError } from '../types';
import { FieldFormatsStartCommon } from '../../field_formats';
@ -195,6 +191,21 @@ export class IndexPatternsService {
return indexPatternCache.set(id, indexPattern);
};
specToIndexPattern(spec: IndexPatternSpec) {
const indexPattern = new IndexPattern(spec.id, {
getConfig: (cfg: any) => this.config.get(cfg),
savedObjectsClient: this.savedObjectsClient,
apiClient: this.apiClient,
patternCache: indexPatternCache,
fieldFormats: this.fieldFormats,
onNotification: this.onNotification,
onError: this.onError,
});
indexPattern.initFromSpec(spec);
return indexPattern;
}
make = (id?: string): Promise<IndexPattern> => {
const indexPattern = new IndexPattern(id, {
getConfig: (cfg: any) => this.config.get(cfg),

View file

@ -1,35 +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.
*/
export type AggregationRestrictions = Record<
string,
{
agg?: string;
interval?: number;
fixed_interval?: string;
calendar_interval?: string;
delay?: string;
time_zone?: string;
}
>;
export interface TypeMeta {
aggs?: Record<string, AggregationRestrictions>;
[key: string]: any;
}

View file

@ -19,6 +19,8 @@
import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notifications';
import { IFieldType } from './fields';
import { SerializedFieldFormat } from '../../../expressions/common';
import { KBN_FIELD_TYPES } from '..';
export interface IIndexPattern {
[key: string]: any;
@ -51,3 +53,65 @@ export interface IndexPatternAttributes {
export type OnNotification = (toastInputFields: ToastInputFields) => void;
export type OnError = (error: Error, toastInputFields: ErrorToastOptions) => void;
export type AggregationRestrictions = Record<
string,
{
agg?: string;
interval?: number;
fixed_interval?: string;
calendar_interval?: string;
delay?: string;
time_zone?: string;
}
>;
export interface IFieldSubType {
multi?: { parent: string };
nested?: { path: string };
}
export interface TypeMeta {
aggs?: Record<string, AggregationRestrictions>;
[key: string]: any;
}
export type FieldSpecConflictDescriptions = Record<string, string[]>;
// This should become FieldSpec once types are cleaned up
export interface FieldSpecExportFmt {
count?: number;
script?: string;
lang?: string;
conflictDescriptions?: FieldSpecConflictDescriptions;
name: string;
type: KBN_FIELD_TYPES;
esTypes?: string[];
scripted: boolean;
searchable: boolean;
aggregatable: boolean;
readFromDocValues?: boolean;
subType?: IFieldSubType;
format?: SerializedFieldFormat;
indexed?: boolean;
}
export interface FieldSpec {
[key: string]: any;
format?: SerializedFieldFormat;
}
export interface IndexPatternSpec {
id?: string;
version?: string;
title: string;
timeFieldName?: string;
sourceFilters?: SourceFilter[];
fields?: FieldSpec[];
typeMeta?: TypeMeta;
}
export interface SourceFilter {
value: string;
}

View file

@ -249,8 +249,6 @@ export {
IndexPattern,
IIndexPatternFieldList,
Field as IndexPatternField,
TypeMeta as IndexPatternTypeMeta,
AggregationRestrictions as IndexPatternAggRestrictions,
// TODO: exported only in stub_index_pattern test. Move into data plugin and remove export.
getIndexPatternFieldListCreator,
} from './index_patterns';
@ -263,6 +261,8 @@ export {
KBN_FIELD_TYPES,
IndexPatternAttributes,
UI_SETTINGS,
TypeMeta as IndexPatternTypeMeta,
AggregationRestrictions as IndexPatternAggRestrictions,
} from '../common';
/*

View file

@ -34,11 +34,4 @@ export {
IIndexPatternFieldList,
} from '../../common/index_patterns';
// TODO: figure out how to replace IndexPatterns in get_inner_angular.
export {
IndexPatternsService,
IndexPatternsContract,
IndexPattern,
TypeMeta,
AggregationRestrictions,
} from './index_patterns';
export { IndexPatternsService, IndexPatternsContract, IndexPattern } from './index_patterns';

View file

@ -902,6 +902,10 @@ export interface IFieldType {
sortable?: boolean;
// (undocumented)
subType?: IFieldSubType;
// Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts
//
// (undocumented)
toSpec?: () => FieldSpec;
// (undocumented)
type: string;
// (undocumented)
@ -937,8 +941,6 @@ export interface IIndexPattern {
//
// @public (undocumented)
export interface IIndexPatternFieldList extends Array<IndexPatternField> {
// Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts
//
// (undocumented)
add(field: FieldSpec): void;
// (undocumented)
@ -993,7 +995,9 @@ export class IndexPattern implements IIndexPattern {
// (undocumented)
fieldFormatMap: any;
// (undocumented)
fields: IIndexPatternFieldList;
fields: IIndexPatternFieldList & {
toSpec: () => FieldSpec[];
};
// (undocumented)
fieldsFetcher: any;
// (undocumented)
@ -1036,6 +1040,10 @@ export class IndexPattern implements IIndexPattern {
id?: string;
// (undocumented)
init(forceFieldRefresh?: boolean): Promise<this>;
// Warning: (ae-forgotten-export) The symbol "IndexPatternSpec" needs to be exported by the entry point index.d.ts
//
// (undocumented)
initFromSpec(spec: IndexPatternSpec): this;
// (undocumented)
isTimeBased(): boolean;
// (undocumented)
@ -1065,9 +1073,9 @@ export class IndexPattern implements IIndexPattern {
// (undocumented)
toJSON(): string | undefined;
// (undocumented)
toString(): string;
toSpec(): IndexPatternSpec;
// (undocumented)
type?: string;
toString(): string;
// (undocumented)
typeMeta?: IndexPatternTypeMeta;
}
@ -1106,12 +1114,15 @@ export interface IndexPatternAttributes {
export class IndexPatternField implements IFieldType {
// (undocumented)
$$spec: FieldSpec;
// Warning: (ae-forgotten-export) The symbol "FieldSpecExportFmt" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "FieldDependencies" needs to be exported by the entry point index.d.ts
constructor(indexPattern: IIndexPattern, spec: FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
constructor(indexPattern: IIndexPattern, spec: FieldSpecExportFmt | FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies);
// (undocumented)
aggregatable?: boolean;
// Warning: (ae-forgotten-export) The symbol "FieldSpecConflictDescriptions" needs to be exported by the entry point index.d.ts
//
// (undocumented)
conflictDescriptions?: Record<string, string[]>;
conflictDescriptions?: FieldSpecConflictDescriptions;
// (undocumented)
count?: number;
// (undocumented)
@ -1141,6 +1152,8 @@ export class IndexPatternField implements IFieldType {
// (undocumented)
subType?: IFieldSubType;
// (undocumented)
toSpec: () => FieldSpecExportFmt;
// (undocumented)
type: string;
// (undocumented)
visualizable?: boolean;

View file

@ -392,6 +392,10 @@ export interface IFieldType {
sortable?: boolean;
// (undocumented)
subType?: IFieldSubType;
// Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts
//
// (undocumented)
toSpec?: () => FieldSpec;
// (undocumented)
type: string;
// (undocumented)

View file

@ -28,6 +28,7 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DiscoverField } from './discover_field';
import { coreMock } from '../../../../../../core/public/mocks';
import { IndexPatternField } from '../../../../../data/public';
import { FieldSpecExportFmt } from '../../../../../data/common';
jest.mock('../../../kibana_services', () => ({
getServices: () => ({
@ -74,6 +75,7 @@ function getComponent(selected = false, showDetails = false, useShortDots = fals
format: null,
routes: {},
$$spec: {},
toSpec: () => (({} as unknown) as FieldSpecExportFmt),
} as IndexPatternField;
const props = {