Index patterns - Server API (#69105)

* index patterns on the server
This commit is contained in:
Matthew Kime 2020-06-30 10:14:29 -05:00 committed by GitHub
parent 04b8d108d5
commit 2fe0051ec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 945 additions and 158 deletions

View file

@ -9,7 +9,7 @@ Constructs a new instance of the `IndexPattern` class
<b>Signature:</b>
```typescript
constructor(id: string | undefined, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, }: IndexPatternDeps);
constructor(id: string | undefined, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, uiSettingsValues, }: IndexPatternDeps);
```
## Parameters
@ -17,5 +17,5 @@ constructor(id: string | undefined, { getConfig, savedObjectsClient, apiClient,
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string &#124; undefined</code> | |
| { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, } | <code>IndexPatternDeps</code> | |
| { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, uiSettingsValues, } | <code>IndexPatternDeps</code> | |

View file

@ -14,7 +14,7 @@ export declare class IndexPattern implements IIndexPattern
| Constructor | Modifiers | Description |
| --- | --- | --- |
| [(constructor)(id, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, })](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the <code>IndexPattern</code> class |
| [(constructor)(id, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, uiSettingsValues, })](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the <code>IndexPattern</code> class |
## Properties

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; [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) &gt; [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md)
## IndexPatternAttributes.fieldFormatMap property
<b>Signature:</b>
```typescript
fieldFormatMap?: string;
```

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; [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) &gt; [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md)
## IndexPatternAttributes.intervalName property
<b>Signature:</b>
```typescript
intervalName?: string;
```

View file

@ -20,7 +20,10 @@ export interface IndexPatternAttributes
| Property | Type | Description |
| --- | --- | --- |
| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md) | <code>string</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpatternattributes.fields.md) | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md) | <code>string</code> | |
| [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternattributes.sourcefilters.md) | <code>string</code> | |
| [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternattributes.timefieldname.md) | <code>string</code> | |
| [title](./kibana-plugin-plugins-data-public.indexpatternattributes.title.md) | <code>string</code> | |
| [type](./kibana-plugin-plugins-data-public.indexpatternattributes.type.md) | <code>string</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; [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) &gt; [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternattributes.sourcefilters.md)
## IndexPatternAttributes.sourceFilters property
<b>Signature:</b>
```typescript
sourceFilters?: string;
```

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; [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) &gt; [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md)
## IndexPatternAttributes.fieldFormatMap property
<b>Signature:</b>
```typescript
fieldFormatMap?: string;
```

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; [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) &gt; [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md)
## IndexPatternAttributes.intervalName property
<b>Signature:</b>
```typescript
intervalName?: string;
```

View file

@ -20,7 +20,10 @@ export interface IndexPatternAttributes
| Property | Type | Description |
| --- | --- | --- |
| [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md) | <code>string</code> | |
| [fields](./kibana-plugin-plugins-data-server.indexpatternattributes.fields.md) | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md) | <code>string</code> | |
| [sourceFilters](./kibana-plugin-plugins-data-server.indexpatternattributes.sourcefilters.md) | <code>string</code> | |
| [timeFieldName](./kibana-plugin-plugins-data-server.indexpatternattributes.timefieldname.md) | <code>string</code> | |
| [title](./kibana-plugin-plugins-data-server.indexpatternattributes.title.md) | <code>string</code> | |
| [type](./kibana-plugin-plugins-data-server.indexpatternattributes.type.md) | <code>string</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; [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) &gt; [sourceFilters](./kibana-plugin-plugins-data-server.indexpatternattributes.sourcefilters.md)
## IndexPatternAttributes.sourceFilters property
<b>Signature:</b>
```typescript
sourceFilters?: string;
```

View file

@ -12,6 +12,9 @@ start(core: CoreStart): {
fieldFormats: {
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest<unknown, unknown, unknown, any>) => Promise<import("../common").IndexPatternsService>;
};
};
```
@ -28,5 +31,8 @@ start(core: CoreStart): {
fieldFormats: {
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest<unknown, unknown, unknown, any>) => Promise<import("../common").IndexPatternsService>;
};
}`

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; [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) &gt; [indexPatterns](./kibana-plugin-plugins-data-server.pluginstart.indexpatterns.md)
## PluginStart.indexPatterns property
<b>Signature:</b>
```typescript
indexPatterns: IndexPatternsServiceStart;
```

View file

@ -15,5 +15,6 @@ export interface DataPluginStart
| Property | Type | Description |
| --- | --- | --- |
| [fieldFormats](./kibana-plugin-plugins-data-server.pluginstart.fieldformats.md) | <code>FieldFormatsStart</code> | |
| [indexPatterns](./kibana-plugin-plugins-data-server.pluginstart.indexpatterns.md) | <code>IndexPatternsServiceStart</code> | |
| [search](./kibana-plugin-plugins-data-server.pluginstart.search.md) | <code>ISearchStart</code> | |

View file

@ -17,13 +17,13 @@
* under the License.
*/
// @ts-expect-error
import stubbedLogstashFields from './logstash_fields';
import { SimpleSavedObject } from '../core/public';
const mockLogstashFields = stubbedLogstashFields();
export function stubbedSavedObjectIndexPattern(id) {
return new SimpleSavedObject(undefined, {
export function stubbedSavedObjectIndexPattern(id: string | null = null) {
return {
id,
type: 'index-pattern',
attributes: {
@ -32,5 +32,5 @@ export function stubbedSavedObjectIndexPattern(id) {
fields: mockLogstashFields,
},
version: 2,
});
};
}

View file

@ -19,3 +19,4 @@
export * from './fields';
export * from './types';
export { IndexPatternsService } from './index_patterns';

View file

@ -17,7 +17,8 @@
* under the License.
*/
import { GetFieldsOptions, IIndexPatternsApiClient, IndexPattern } from '.';
import { IndexPattern } from '.';
import { GetFieldsOptions, IIndexPatternsApiClient } from '../types';
/** @internal */
export const createFieldsFetcher = (

View file

@ -18,13 +18,13 @@
*/
import { contains } from 'lodash';
import { CoreStart } from 'kibana/public';
import { IndexPatternsContract } from './index_patterns';
import { UiSettingsCommon } from '../types';
export type EnsureDefaultIndexPattern = () => Promise<unknown> | undefined;
export const createEnsureDefaultIndexPattern = (
uiSettings: CoreStart['uiSettings'],
uiSettings: UiSettingsCommon,
onRedirectNoIndexPattern: () => Promise<unknown> | void
) => {
/**
@ -33,12 +33,12 @@ export const createEnsureDefaultIndexPattern = (
*/
return async function ensureDefaultIndexPattern(this: IndexPatternsContract) {
const patterns = await this.getIds();
let defaultId = uiSettings.get('defaultIndex');
let defaultId = await uiSettings.get('defaultIndex');
let defined = !!defaultId;
const exists = contains(patterns, defaultId);
if (defined && !exists) {
uiSettings.remove('defaultIndex');
await uiSettings.remove('defaultIndex');
defaultId = defined = false;
}
@ -49,7 +49,7 @@ export const createEnsureDefaultIndexPattern = (
// If there is any index pattern created, set the first as default
if (patterns.length >= 1) {
defaultId = patterns[0];
uiSettings.set('defaultIndex', defaultId);
await uiSettings.set('defaultIndex', defaultId);
} else {
return onRedirectNoIndexPattern();
}

View file

@ -17,7 +17,6 @@
* under the License.
*/
export * from './index_patterns_api_client';
export * from './_pattern_cache';
export * from './flatten_hit';
export * from './format_hit';

View file

@ -66,7 +66,7 @@ const savedObjectsClient = {
create: jest.fn(),
get: jest.fn().mockImplementation(() => object),
update: jest.fn().mockImplementation(async (type, id, body, { version }) => {
if (object._version !== version) {
if (object.version !== version) {
throw new Object({
res: {
status: 409,
@ -74,10 +74,10 @@ const savedObjectsClient = {
});
}
object.attributes.title = body.title;
object._version += 'a';
object.version += 'a';
return {
id: object._id,
_version: object._version,
id: object.id,
version: object.version,
};
}),
};
@ -109,6 +109,7 @@ function create(id: string, payload?: any): Promise<IndexPattern> {
fieldFormats: fieldFormatsMock,
onNotification: () => {},
onError: () => {},
uiSettingsValues: { shortDotsEnable: false, metaFields: [] },
});
setDocsourcePayload(id, payload);
@ -382,8 +383,8 @@ describe('IndexPattern', () => {
test('should handle version conflicts', async () => {
setDocsourcePayload(null, {
_id: 'foo',
_version: 'foo',
id: 'foo',
version: 'foo',
attributes: {
title: 'something',
},
@ -397,6 +398,7 @@ describe('IndexPattern', () => {
fieldFormats: fieldFormatsMock,
onNotification: () => {},
onError: () => {},
uiSettingsValues: { shortDotsEnable: false, metaFields: [] },
});
await pattern.init();
@ -411,6 +413,7 @@ describe('IndexPattern', () => {
fieldFormats: fieldFormatsMock,
onNotification: () => {},
onError: () => {},
uiSettingsValues: { shortDotsEnable: false, metaFields: [] },
});
await samePattern.init();

View file

@ -19,25 +19,23 @@
import _, { each, reject } from 'lodash';
import { i18n } from '@kbn/i18n';
import { SavedObjectsClientContract } from 'src/core/public';
import { SavedObjectAttributes } from 'src/core/public';
import { SavedObjectsClientCommon } from '../..';
import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common';
import {
ES_FIELD_TYPES,
KBN_FIELD_TYPES,
IIndexPattern,
IFieldType,
UI_SETTINGS,
} from '../../../common';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } 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 { OnNotification, OnError } from '../types';
import {
OnNotification,
OnError,
UiSettingsCommon,
IIndexPatternsApiClient,
IndexPatternAttributes,
} from '../types';
import { FieldFormatsStartCommon } from '../../field_formats';
import { PatternCache } from './_pattern_cache';
import { expandShorthand, FieldMappingSpec, MappingObject } from '../../field_mapping';
@ -45,16 +43,22 @@ import { IndexPatternSpec, TypeMeta, FieldSpec, SourceFilter } from '../types';
import { SerializedFieldFormat } from '../../../../expressions/common';
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
const type = 'index-pattern';
const savedObjectType = 'index-pattern';
interface IUiSettingsValues {
[key: string]: any;
shortDotsEnable: any;
metaFields: any;
}
interface IndexPatternDeps {
getConfig: any;
savedObjectsClient: SavedObjectsClientContract;
getConfig: UiSettingsCommon['get'];
savedObjectsClient: SavedObjectsClientCommon;
apiClient: IIndexPatternsApiClient;
patternCache: PatternCache;
fieldFormats: FieldFormatsStartCommon;
onNotification: OnNotification;
onError: OnError;
uiSettingsValues: IUiSettingsValues;
}
export class IndexPattern implements IIndexPattern {
@ -72,9 +76,9 @@ export class IndexPattern implements IIndexPattern {
public metaFields: string[];
private version: string | undefined;
private savedObjectsClient: SavedObjectsClientContract;
private savedObjectsClient: SavedObjectsClientCommon;
private patternCache: PatternCache;
private getConfig: any;
private getConfig: UiSettingsCommon['get'];
private sourceFilters?: SourceFilter[];
private originalBody: { [key: string]: any } = {};
public fieldsFetcher: any; // probably want to factor out any direct usage and change to private
@ -83,6 +87,7 @@ export class IndexPattern implements IIndexPattern {
private onNotification: OnNotification;
private onError: OnError;
private apiClient: IIndexPatternsApiClient;
private uiSettingsValues: IUiSettingsValues;
private mapping: MappingObject = expandShorthand({
title: ES_FIELD_TYPES.TEXT,
@ -116,6 +121,7 @@ export class IndexPattern implements IIndexPattern {
fieldFormats,
onNotification,
onError,
uiSettingsValues,
}: IndexPatternDeps
) {
this.id = id;
@ -127,9 +133,10 @@ export class IndexPattern implements IIndexPattern {
this.fieldFormats = fieldFormats;
this.onNotification = onNotification;
this.onError = onError;
this.uiSettingsValues = uiSettingsValues;
this.shortDotsEnable = this.getConfig(UI_SETTINGS.SHORT_DOTS_ENABLE);
this.metaFields = this.getConfig(UI_SETTINGS.META_FIELDS);
this.shortDotsEnable = uiSettingsValues.shortDotsEnable;
this.metaFields = uiSettingsValues.metaFields;
this.createFieldList = getIndexPatternFieldListCreator({
fieldFormats,
@ -138,12 +145,8 @@ export class IndexPattern implements IIndexPattern {
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.fieldsFetcher = createFieldsFetcher(this, apiClient, uiSettingsValues.metaFields);
this.flattenHit = flattenHitWrapper(this, uiSettingsValues.metaFields);
this.formatHit = formatHitProvider(
this,
fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING)
@ -160,7 +163,13 @@ export class IndexPattern implements IIndexPattern {
private deserializeFieldFormatMap(mapping: any) {
const FieldFormat = this.fieldFormats.getType(mapping.id);
return FieldFormat && new FieldFormat(mapping.params, this.getConfig);
return (
FieldFormat &&
new FieldFormat(
mapping.params,
(key: string) => this.uiSettingsValues[key]?.userValue || this.uiSettingsValues[key]?.value
)
);
}
private initFields(input?: any) {
@ -228,7 +237,7 @@ export class IndexPattern implements IIndexPattern {
private updateFromElasticSearch(response: any, forceFieldRefresh: boolean = false) {
if (!response.found) {
throw new SavedObjectNotFound(type, this.id, 'management/kibana/indexPatterns');
throw new SavedObjectNotFound(savedObjectType, this.id, 'management/kibana/indexPatterns');
}
_.forOwn(this.mapping, (fieldMapping: FieldMappingSpec, name: string | undefined) => {
@ -296,12 +305,22 @@ export class IndexPattern implements IIndexPattern {
return this; // no id === no elasticsearch document
}
const savedObject = await this.savedObjectsClient.get(type, this.id);
const savedObject = await this.savedObjectsClient.get<IndexPatternAttributes>(
savedObjectType,
this.id
);
const response = {
version: savedObject._version,
found: savedObject._version ? true : false,
...(_.cloneDeep(savedObject.attributes) as SavedObjectAttributes),
version: savedObject.version,
found: savedObject.version ? true : false,
title: savedObject.attributes.title,
timeFieldName: savedObject.attributes.timeFieldName,
intervalName: savedObject.attributes.intervalName,
fields: savedObject.attributes.fields,
sourceFilters: savedObject.attributes.sourceFilters,
fieldFormatMap: savedObject.attributes.fieldFormatMap,
typeMeta: savedObject.attributes.typeMeta,
type: savedObject.attributes.type,
};
// Do this before we attempt to update from ES since that call can potentially perform a save
this.originalBody = this.prepBody();
@ -388,10 +407,10 @@ export class IndexPattern implements IIndexPattern {
field.count = count;
try {
const res = await this.savedObjectsClient.update(type, this.id, this.prepBody(), {
const res = await this.savedObjectsClient.update(savedObjectType, this.id, this.prepBody(), {
version: this.version,
});
this.version = res._version;
this.version = res.version;
} catch (e) {
// no need for an error message here
}
@ -462,13 +481,17 @@ export class IndexPattern implements IIndexPattern {
fieldFormats: this.fieldFormats,
onNotification: this.onNotification,
onError: this.onError,
uiSettingsValues: {
shortDotsEnable: this.shortDotsEnable,
metaFields: this.metaFields,
},
});
await duplicatePattern.destroy();
}
const body = this.prepBody();
const response = await this.savedObjectsClient.create(type, body, { id: this.id });
const response = await this.savedObjectsClient.create(savedObjectType, body, { id: this.id });
this.id = response.id;
return response.id;
@ -496,10 +519,10 @@ export class IndexPattern implements IIndexPattern {
(key) => body[key] !== this.originalBody[key]
);
return this.savedObjectsClient
.update(type, this.id, body, { version: this.version })
.then((resp: any) => {
.update(savedObjectType, this.id, body, { version: this.version })
.then((resp) => {
this.id = resp.id;
this.version = resp._version;
this.version = resp.version;
})
.catch((err) => {
if (
@ -514,7 +537,12 @@ export class IndexPattern implements IIndexPattern {
fieldFormats: this.fieldFormats,
onNotification: this.onNotification,
onError: this.onError,
uiSettingsValues: {
shortDotsEnable: this.shortDotsEnable,
metaFields: this.metaFields,
},
});
return samePattern.init().then(() => {
// What keys changed from now and what the server returned
const updatedBody = samePattern.prepBody();
@ -610,7 +638,7 @@ export class IndexPattern implements IIndexPattern {
destroy() {
if (this.id) {
this.patternCache.clear(this.id);
return this.savedObjectsClient.delete(type, this.id);
return this.savedObjectsClient.delete(savedObjectType, this.id);
}
}
}

View file

@ -19,12 +19,14 @@
// eslint-disable-next-line max-classes-per-file
import { IndexPatternsService } from './index_patterns';
import { SavedObjectsClientContract, SavedObjectsFindResponsePublic } from 'kibana/public';
import { coreMock, httpServiceMock } from '../../../../../core/public/mocks';
import { fieldFormatsMock } from '../../field_formats/mocks';
import {
UiSettingsCommon,
IIndexPatternsApiClient,
SavedObjectsClientCommon,
SavedObject,
} from '../types';
const core = coreMock.createStart();
const http = httpServiceMock.createStartContract();
const fieldFormats = fieldFormatsMock;
jest.mock('./index_pattern', () => {
@ -39,33 +41,26 @@ jest.mock('./index_pattern', () => {
};
});
jest.mock('./index_patterns_api_client', () => {
class IndexPatternsApiClient {
getFieldsForWildcard = async () => ({});
}
return {
IndexPatternsApiClient,
};
});
describe('IndexPatterns', () => {
let indexPatterns: IndexPatternsService;
let savedObjectsClient: SavedObjectsClientContract;
let savedObjectsClient: SavedObjectsClientCommon;
beforeEach(() => {
savedObjectsClient = {} as SavedObjectsClientContract;
savedObjectsClient = {} as SavedObjectsClientCommon;
savedObjectsClient.find = jest.fn(
() =>
Promise.resolve({
savedObjects: [{ id: 'id', attributes: { title: 'title' } }],
}) as Promise<SavedObjectsFindResponsePublic<any>>
Promise.resolve([{ id: 'id', attributes: { title: 'title' } }]) as Promise<
Array<SavedObject<any>>
>
);
indexPatterns = new IndexPatternsService({
uiSettings: core.uiSettings,
savedObjectsClient,
http,
uiSettings: ({
get: () => Promise.resolve(false),
getAll: () => {},
} as any) as UiSettingsCommon,
savedObjectsClient: (savedObjectsClient as unknown) as SavedObjectsClientCommon,
apiClient: {} as IIndexPatternsApiClient,
fieldFormats,
onNotification: () => {},
onError: () => {},

View file

@ -17,25 +17,26 @@
* under the License.
*/
import {
SavedObjectsClientContract,
SimpleSavedObject,
IUiSettingsClient,
HttpStart,
CoreStart,
} from 'src/core/public';
import { SavedObjectsClientCommon } from '../..';
import { createIndexPatternCache } from '.';
import { IndexPattern } from './index_pattern';
import { IndexPatternsApiClient, GetFieldsOptions } from '.';
import {
createEnsureDefaultIndexPattern,
EnsureDefaultIndexPattern,
} from './ensure_default_index_pattern';
import { getIndexPatternFieldListCreator, CreateIndexPatternFieldList, Field } from '../fields';
import { IndexPatternSpec, FieldSpec } from '../types';
import { OnNotification, OnError } from '../types';
import {
OnNotification,
OnError,
UiSettingsCommon,
IIndexPatternsApiClient,
GetFieldsOptions,
FieldSpec,
IndexPatternSpec,
} from '../types';
import { FieldFormatsStartCommon } from '../../field_formats';
import { UI_SETTINGS, SavedObject } from '../../../common';
const indexPatternCache = createIndexPatternCache();
@ -46,20 +47,20 @@ export interface IndexPatternSavedObjectAttrs {
}
interface IndexPatternsServiceDeps {
uiSettings: CoreStart['uiSettings'];
savedObjectsClient: SavedObjectsClientContract;
http: HttpStart;
uiSettings: UiSettingsCommon;
savedObjectsClient: SavedObjectsClientCommon;
apiClient: IIndexPatternsApiClient;
fieldFormats: FieldFormatsStartCommon;
onNotification: OnNotification;
onError: OnError;
onRedirectNoIndexPattern: () => void;
onRedirectNoIndexPattern?: () => void;
}
export class IndexPatternsService {
private config: IUiSettingsClient;
private savedObjectsClient: SavedObjectsClientContract;
private savedObjectsCache?: Array<SimpleSavedObject<IndexPatternSavedObjectAttrs>> | null;
private apiClient: IndexPatternsApiClient;
private config: UiSettingsCommon;
private savedObjectsClient: SavedObjectsClientCommon;
private savedObjectsCache?: Array<SavedObject<IndexPatternSavedObjectAttrs>> | null;
private apiClient: IIndexPatternsApiClient;
private fieldFormats: FieldFormatsStartCommon;
private onNotification: OnNotification;
private onError: OnError;
@ -74,13 +75,13 @@ export class IndexPatternsService {
constructor({
uiSettings,
savedObjectsClient,
http,
apiClient,
fieldFormats,
onNotification,
onError,
onRedirectNoIndexPattern,
onRedirectNoIndexPattern = () => {},
}: IndexPatternsServiceDeps) {
this.apiClient = new IndexPatternsApiClient(http);
this.apiClient = apiClient;
this.config = uiSettings;
this.savedObjectsClient = savedObjectsClient;
this.fieldFormats = fieldFormats;
@ -103,13 +104,11 @@ export class IndexPatternsService {
}
private async refreshSavedObjectsCache() {
this.savedObjectsCache = (
await this.savedObjectsClient.find<IndexPatternSavedObjectAttrs>({
type: 'index-pattern',
fields: ['title'],
perPage: 10000,
})
).savedObjects;
this.savedObjectsCache = await this.savedObjectsClient.find<IndexPatternSavedObjectAttrs>({
type: 'index-pattern',
fields: ['title'],
perPage: 10000,
});
}
getIds = async (refresh: boolean = false) => {
@ -172,7 +171,7 @@ export class IndexPatternsService {
};
getDefault = async () => {
const defaultIndexPatternId = this.config.get('defaultIndex');
const defaultIndexPatternId = await this.config.get('defaultIndex');
if (defaultIndexPatternId) {
return await this.get(defaultIndexPatternId);
}
@ -191,7 +190,11 @@ export class IndexPatternsService {
return indexPatternCache.set(id, indexPattern);
};
specToIndexPattern(spec: IndexPatternSpec) {
async specToIndexPattern(spec: IndexPatternSpec) {
const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE);
const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS);
const uiSettingsValues = await this.config.getAll();
const indexPattern = new IndexPattern(spec.id, {
getConfig: (cfg: any) => this.config.get(cfg),
savedObjectsClient: this.savedObjectsClient,
@ -200,13 +203,18 @@ export class IndexPatternsService {
fieldFormats: this.fieldFormats,
onNotification: this.onNotification,
onError: this.onError,
uiSettingsValues: { ...uiSettingsValues, shortDotsEnable, metaFields },
});
indexPattern.initFromSpec(spec);
return indexPattern;
}
make = (id?: string): Promise<IndexPattern> => {
async make(id?: string): Promise<IndexPattern> {
const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE);
const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS);
const uiSettingsValues = await this.config.getAll();
const indexPattern = new IndexPattern(id, {
getConfig: (cfg: any) => this.config.get(cfg),
savedObjectsClient: this.savedObjectsClient,
@ -215,10 +223,11 @@ export class IndexPatternsService {
fieldFormats: this.fieldFormats,
onNotification: this.onNotification,
onError: this.onError,
uiSettingsValues: { ...uiSettingsValues, shortDotsEnable, metaFields },
});
return indexPattern.init();
};
}
}
export type IndexPatternsContract = PublicMethodsOf<IndexPatternsService>;

View file

@ -19,7 +19,7 @@
/* eslint-disable */
import { KbnError } from '../../../../kibana_utils/public';
import { KbnError } from '../../../../kibana_utils/common/';
/**
* Tried to call a method that relies on SearchSource having an indexPattern assigned

View file

@ -18,6 +18,8 @@
*/
import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notifications';
// eslint-disable-next-line
import type { SavedObject } from 'src/core/server';
import { IFieldType } from './fields';
import { SerializedFieldFormat } from '../../../expressions/common';
import { KBN_FIELD_TYPES } from '..';
@ -49,11 +51,61 @@ export interface IndexPatternAttributes {
title: string;
typeMeta: string;
timeFieldName?: string;
intervalName?: string;
sourceFilters?: string;
fieldFormatMap?: string;
}
export type OnNotification = (toastInputFields: ToastInputFields) => void;
export type OnError = (error: Error, toastInputFields: ErrorToastOptions) => void;
export interface UiSettingsCommon {
get: (key: string) => Promise<any>;
getAll: () => Promise<Record<string, any>>;
set: (key: string, value: any) => Promise<void>;
remove: (key: string) => Promise<void>;
}
export interface SavedObjectsClientCommonFindArgs {
type: string | string[];
fields?: string[];
perPage?: number;
search?: string;
searchFields?: string[];
}
export interface SavedObjectsClientCommon {
find: <T = unknown>(options: SavedObjectsClientCommonFindArgs) => Promise<Array<SavedObject<T>>>;
get: <T = unknown>(type: string, id: string) => Promise<SavedObject<T>>;
update: (
type: string,
id: string,
attributes: Record<string, any>,
options: Record<string, any>
) => Promise<SavedObject>;
create: (
type: string,
attributes: Record<string, any>,
options: Record<string, any>
) => Promise<SavedObject>;
delete: (type: string, id: string) => Promise<{}>;
}
export interface GetFieldsOptions {
pattern?: string;
type?: string;
params?: any;
lookBack?: boolean;
metaFields?: string;
}
export interface IIndexPatternsApiClient {
getFieldsForTimePattern: (options: GetFieldsOptions) => Promise<any>;
getFieldsForWildcard: (options: GetFieldsOptions) => Promise<any>;
}
export type { SavedObject };
export type AggregationRestrictions = Record<
string,
{

View file

@ -18,24 +18,24 @@
*/
import { find } from 'lodash';
import { SavedObjectsClientContract, SimpleSavedObject } from 'src/core/public';
import { SavedObjectsClientCommon, SavedObject } from '..';
/**
* Returns an object matching a given title
*
* @param client {SavedObjectsClientContract}
* @param client {SavedObjectsClientCommon}
* @param title {string}
* @returns {Promise<SimpleSavedObject|undefined>}
* @returns {Promise<SavedObject|undefined>}
*/
export async function findByTitle(
client: SavedObjectsClientContract,
client: SavedObjectsClientCommon,
title: string
): Promise<SimpleSavedObject<any> | void> {
): Promise<SavedObject<any> | void> {
if (!title) {
return Promise.resolve();
}
const { savedObjects } = await client.find({
const savedObjects = await client.find({
type: 'index-pattern',
perPage: 10,
search: `"${title}"`,
@ -45,6 +45,6 @@ export async function findByTitle(
return find(
savedObjects,
(obj: SimpleSavedObject<any>) => obj.get('title').toLowerCase() === title.toLowerCase()
(obj: SavedObject<any>) => obj.attributes.title.toLowerCase() === title.toLowerCase()
);
}

View file

@ -34,4 +34,11 @@ export {
IIndexPatternFieldList,
} from '../../common/index_patterns';
export { IndexPatternsService, IndexPatternsContract, IndexPattern } from './index_patterns';
export {
IndexPatternsService,
IndexPatternsContract,
IndexPattern,
IndexPatternsApiClient,
} from './index_patterns';
export { UiSettingsPublicToCommon } from './ui_settings_wrapper';
export { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper';

View file

@ -19,3 +19,4 @@
export * from '../../../common/index_patterns/index_patterns';
export * from './redirect_no_index_pattern';
export * from './index_patterns_api_client';

View file

@ -18,21 +18,12 @@
*/
import { HttpSetup } from 'src/core/public';
import { IndexPatternMissingIndices } from '../lib';
import { IndexPatternMissingIndices } from '../../../common/index_patterns/lib';
import { GetFieldsOptions, IIndexPatternsApiClient } from '../../../common/index_patterns/types';
const API_BASE_URL: string = `/api/index_patterns/`;
export interface GetFieldsOptions {
pattern?: string;
type?: string;
params?: any;
lookBack?: boolean;
metaFields?: string;
}
export type IIndexPatternsApiClient = PublicMethodsOf<IndexPatternsApiClient>;
export class IndexPatternsApiClient {
export class IndexPatternsApiClient implements IIndexPatternsApiClient {
private http: HttpSetup;
constructor(http: HttpSetup) {
@ -53,7 +44,7 @@ export class IndexPatternsApiClient {
});
}
_getUrl(path: string[]) {
private _getUrl(path: string[]) {
return API_BASE_URL + path.filter(Boolean).map(encodeURIComponent).join('/');
}

View file

@ -0,0 +1,67 @@
/*
* 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 { omit } from 'lodash';
import { SavedObjectsClient, SimpleSavedObject } from 'src/core/public';
import {
SavedObjectsClientCommon,
SavedObjectsClientCommonFindArgs,
SavedObject,
} from '../../common/index_patterns';
type SOClient = Pick<SavedObjectsClient, 'find' | 'get' | 'update' | 'create' | 'delete'>;
const simpleSavedObjectToSavedObject = <T>(
simpleSavedObject: SimpleSavedObject
): SavedObject<T> => ({
version: simpleSavedObject._version,
...omit(simpleSavedObject, '_version'),
});
export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommon {
private savedObjectClient: SOClient;
constructor(savedObjectClient: SOClient) {
this.savedObjectClient = savedObjectClient;
}
async find<T = unknown>(options: SavedObjectsClientCommonFindArgs) {
const response = (await this.savedObjectClient.find<T>(options)).savedObjects;
return response.map<SavedObject<T>>(simpleSavedObjectToSavedObject);
}
async get<T = unknown>(type: string, id: string) {
const response = await this.savedObjectClient.get<T>(type, id);
return simpleSavedObjectToSavedObject<T>(response);
}
async update(
type: string,
id: string,
attributes: Record<string, any>,
options: Record<string, any>
) {
const response = await this.savedObjectClient.update(type, id, attributes, options);
return simpleSavedObjectToSavedObject(response);
}
async create(type: string, attributes: Record<string, any>, options: Record<string, any>) {
const response = await this.savedObjectClient.create(type, attributes, options);
return simpleSavedObjectToSavedObject(response);
}
delete(type: string, id: string) {
return this.savedObjectClient.delete(type, id);
}
}

View file

@ -0,0 +1,45 @@
/*
* 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 { IUiSettingsClient } from 'src/core/public';
import { UiSettingsCommon } from '../../common/index_patterns';
export class UiSettingsPublicToCommon implements UiSettingsCommon {
private uiSettings: IUiSettingsClient;
constructor(uiSettings: IUiSettingsClient) {
this.uiSettings = uiSettings;
}
get(key: string) {
return Promise.resolve(this.uiSettings.get(key));
}
getAll() {
return Promise.resolve(this.uiSettings.getAll());
}
set(key: string, value: any) {
this.uiSettings.set(key, value);
return Promise.resolve();
}
remove(key: string) {
this.uiSettings.remove(key);
return Promise.resolve();
}
}

View file

@ -40,7 +40,12 @@ import { SearchService } from './search/search_service';
import { FieldFormatsService } from './field_formats';
import { QueryService } from './query';
import { createIndexPatternSelect } from './ui/index_pattern_select';
import { IndexPatternsService, onRedirectNoIndexPattern } from './index_patterns';
import {
IndexPatternsService,
onRedirectNoIndexPattern,
IndexPatternsApiClient,
UiSettingsPublicToCommon,
} from './index_patterns';
import {
setFieldFormats,
setHttp,
@ -76,6 +81,7 @@ import {
ACTION_VALUE_CLICK,
ValueClickActionContext,
} from './actions/value_click_action';
import { SavedObjectsClientPublicToCommon } from './index_patterns';
declare module '../../ui_actions/public' {
export interface ActionContextMapping {
@ -164,9 +170,9 @@ export class DataPublicPlugin implements Plugin<DataPublicPluginSetup, DataPubli
setFieldFormats(fieldFormats);
const indexPatterns = new IndexPatternsService({
uiSettings,
savedObjectsClient: savedObjects.client,
http,
uiSettings: new UiSettingsPublicToCommon(uiSettings),
savedObjectsClient: new SavedObjectsClientPublicToCommon(savedObjects.client),
apiClient: new IndexPatternsApiClient(http),
fieldFormats,
onNotification: (toastInputFields) => {
notifications.toasts.add(toastInputFields);

View file

@ -66,8 +66,6 @@ import { GetSourceParams } from 'elasticsearch';
import { GetTemplateParams } from 'elasticsearch';
import { History } from 'history';
import { Href } from 'history';
import { HttpSetup } from 'src/core/public';
import { HttpStart } from 'src/core/public';
import { IconType } from '@elastic/eui';
import { IndexDocumentParams } from 'elasticsearch';
import { IndicesAnalyzeParams } from 'elasticsearch';
@ -148,15 +146,15 @@ import { ReindexRethrottleParams } from 'elasticsearch';
import { RenderSearchTemplateParams } from 'elasticsearch';
import { Required } from '@kbn/utility-types';
import * as Rx from 'rxjs';
import { SavedObject as SavedObject_2 } from 'src/core/public';
import { SavedObjectsClientContract } from 'src/core/public';
import { SavedObject } from 'src/core/server';
import { SavedObject as SavedObject_3 } from 'src/core/public';
import { SavedObjectsClientContract as SavedObjectsClientContract_3 } from 'src/core/public';
import { ScrollParams } from 'elasticsearch';
import { SearchParams } from 'elasticsearch';
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
import { SearchShardsParams } from 'elasticsearch';
import { SearchTemplateParams } from 'elasticsearch';
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/public';
import { SimpleSavedObject } from 'src/core/public';
import { SnapshotCreateParams } from 'elasticsearch';
import { SnapshotCreateRepositoryParams } from 'elasticsearch';
import { SnapshotDeleteParams } from 'elasticsearch';
@ -300,7 +298,7 @@ export const connectToQueryState: <S extends QueryState>({ timefilter: { timefil
// Warning: (ae-missing-release-tag) "createSavedQueryService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const createSavedQueryService: (savedObjectsClient: SavedObjectsClientContract) => SavedQueryService;
export const createSavedQueryService: (savedObjectsClient: SavedObjectsClientContract_3) => SavedQueryService;
// Warning: (ae-missing-release-tag) "CustomFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -982,7 +980,7 @@ export type IMetricAggType = MetricAggType;
// @public (undocumented)
export class IndexPattern implements IIndexPattern {
// Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts
constructor(id: string | undefined, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, }: IndexPatternDeps);
constructor(id: string | undefined, { getConfig, savedObjectsClient, apiClient, patternCache, fieldFormats, onNotification, onError, uiSettingsValues, }: IndexPatternDeps);
// (undocumented)
[key: string]: any;
// (undocumented)
@ -1097,9 +1095,15 @@ export type IndexPatternAggRestrictions = Record<string, {
//
// @public @deprecated
export interface IndexPatternAttributes {
// (undocumented)
fieldFormatMap?: string;
// (undocumented)
fields: string;
// (undocumented)
intervalName?: string;
// (undocumented)
sourceFilters?: string;
// (undocumented)
timeFieldName?: string;
// (undocumented)
title: string;

View file

@ -18,4 +18,4 @@
*/
export * from './utils';
export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues } from './fetcher';
export { IndexPatternsService } from './index_patterns_service';
export { IndexPatternsService, IndexPatternsServiceStart } from './index_patterns_service';

View file

@ -0,0 +1,29 @@
/*
* 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 { GetFieldsOptions, IIndexPatternsApiClient } from '../../common/index_patterns/types';
export class IndexPatternsApiServer implements IIndexPatternsApiClient {
async getFieldsForTimePattern(options: GetFieldsOptions = {}) {
throw new Error('IndexPatternsApiServer - getFieldsForTimePattern not defined');
}
async getFieldsForWildcard(options: GetFieldsOptions = {}) {
throw new Error('IndexPatternsApiServer - getFieldsForWildcard not defined');
}
}

View file

@ -17,12 +17,28 @@
* under the License.
*/
import { CoreSetup, Plugin } from 'kibana/server';
import { CoreSetup, CoreStart, Plugin, KibanaRequest, Logger } from 'kibana/server';
import { registerRoutes } from './routes';
import { indexPatternSavedObjectType } from '../saved_objects';
import { capabilitiesProvider } from './capabilities_provider';
import { IndexPatternsService as IndexPatternsCommonService } from '../../common/index_patterns';
import { FieldFormatsStart } from '../field_formats';
import { UiSettingsServerToCommon } from './ui_settings_wrapper';
import { IndexPatternsApiServer } from './index_patterns_api_client';
import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper';
export class IndexPatternsService implements Plugin<void> {
export interface IndexPatternsServiceStart {
indexPatternsServiceFactory: (
kibanaRequest: KibanaRequest
) => Promise<IndexPatternsCommonService>;
}
export interface IndexPatternsServiceStartDeps {
fieldFormats: FieldFormatsStart;
logger: Logger;
}
export class IndexPatternsService implements Plugin<void, IndexPatternsServiceStart> {
public setup(core: CoreSetup) {
core.savedObjects.registerType(indexPatternSavedObjectType);
core.capabilities.registerProvider(capabilitiesProvider);
@ -30,5 +46,28 @@ export class IndexPatternsService implements Plugin<void> {
registerRoutes(core.http);
}
public start() {}
public start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps) {
const { uiSettings, savedObjects } = core;
return {
indexPatternsServiceFactory: async (kibanaRequest: KibanaRequest) => {
const savedObjectsClient = savedObjects.getScopedClient(kibanaRequest);
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient);
return new IndexPatternsCommonService({
uiSettings: new UiSettingsServerToCommon(uiSettingsClient),
savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient),
apiClient: new IndexPatternsApiServer(),
fieldFormats: formats,
onError: (error) => {
logger.error(error);
},
onNotification: ({ title, text }) => {
logger.warn(`${title} : ${text}`);
},
});
},
};
}
}

View file

@ -0,0 +1,24 @@
/*
* 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 function createIndexPatternsStartMock() {
return {
indexPatternsServiceFactory: jest.fn(),
};
}

View file

@ -0,0 +1,53 @@
/*
* 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 { SavedObjectsClientContract, SavedObject } from 'src/core/server';
import {
SavedObjectsClientCommon,
SavedObjectsClientCommonFindArgs,
} from '../../common/index_patterns';
export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommon {
private savedObjectClient: SavedObjectsClientContract;
constructor(savedObjectClient: SavedObjectsClientContract) {
this.savedObjectClient = savedObjectClient;
}
async find<T = unknown>(options: SavedObjectsClientCommonFindArgs) {
const result = await this.savedObjectClient.find<T>(options);
return result.saved_objects;
}
async get<T = unknown>(type: string, id: string) {
return await this.savedObjectClient.get<T>(type, id);
}
async update(
type: string,
id: string,
attributes: Record<string, any>,
options: Record<string, any>
) {
return (await this.savedObjectClient.update(type, id, attributes, options)) as SavedObject;
}
async create(type: string, attributes: Record<string, any>, options: Record<string, any>) {
return await this.savedObjectClient.create(type, attributes, options);
}
delete(type: string, id: string) {
return this.savedObjectClient.delete(type, id);
}
}

View file

@ -0,0 +1,43 @@
/*
* 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 { IUiSettingsClient } from 'src/core/server';
import { UiSettingsCommon } from '../../common/index_patterns';
export class UiSettingsServerToCommon implements UiSettingsCommon {
private uiSettings: IUiSettingsClient;
constructor(uiSettings: IUiSettingsClient) {
this.uiSettings = uiSettings;
}
get(key: string) {
return this.uiSettings.get(key);
}
getAll() {
return this.uiSettings.getAll();
}
set(key: string, value: any) {
return this.uiSettings.set(key, value);
}
remove(key: string) {
return this.uiSettings.remove(key);
}
}

View file

@ -19,6 +19,7 @@
import { createSearchSetupMock, createSearchStartMock } from './search/mocks';
import { createFieldFormatsSetupMock, createFieldFormatsStartMock } from './field_formats/mocks';
import { createIndexPatternsStartMock } from './index_patterns/mocks';
function createSetupContract() {
return {
@ -31,6 +32,7 @@ function createStartContract() {
return {
search: createSearchStartMock(),
fieldFormats: createFieldFormatsStartMock(),
indexPatterns: createIndexPatternsStartMock(),
};
}

View file

@ -17,9 +17,15 @@
* under the License.
*/
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/server';
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
Logger,
} from '../../../core/server';
import { ConfigSchema } from '../config';
import { IndexPatternsService } from './index_patterns';
import { IndexPatternsService, IndexPatternsServiceStart } from './index_patterns';
import { ISearchSetup, ISearchStart } from './search';
import { SearchService } from './search/search_service';
import { QueryService } from './query/query_service';
@ -38,6 +44,7 @@ export interface DataPluginSetup {
export interface DataPluginStart {
search: ISearchStart;
fieldFormats: FieldFormatsStart;
indexPatterns: IndexPatternsServiceStart;
}
export interface DataPluginSetupDependencies {
@ -52,12 +59,14 @@ export class DataServerPlugin implements Plugin<DataPluginSetup, DataPluginStart
private readonly indexPatterns = new IndexPatternsService();
private readonly fieldFormats = new FieldFormatsService();
private readonly queryService = new QueryService();
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
this.searchService = new SearchService(initializerContext);
this.scriptsService = new ScriptsService();
this.kqlTelemetryService = new KqlTelemetryService(initializerContext);
this.autocompleteService = new AutocompleteService(initializerContext);
this.logger = initializerContext.logger.get('data');
}
public setup(
@ -79,9 +88,14 @@ export class DataServerPlugin implements Plugin<DataPluginSetup, DataPluginStart
}
public start(core: CoreStart) {
const fieldFormats = this.fieldFormats.start();
return {
search: this.searchService.start(),
fieldFormats: this.fieldFormats.start(),
fieldFormats,
indexPatterns: this.indexPatterns.start(core, {
fieldFormats,
logger: this.logger.get('indexPatterns'),
}),
};
}

View file

@ -38,6 +38,7 @@ import { DeleteScriptParams } from 'elasticsearch';
import { DeleteTemplateParams } from 'elasticsearch';
import { DetailedPeerCertificate } from 'tls';
import { Duration } from 'moment';
import { ErrorToastOptions } from 'src/core/public/notifications';
import { ExistsParams } from 'elasticsearch';
import { ExplainParams } from 'elasticsearch';
import { FieldStatsParams } from 'elasticsearch';
@ -91,6 +92,7 @@ import { IngestGetPipelineParams } from 'elasticsearch';
import { IngestPutPipelineParams } from 'elasticsearch';
import { IngestSimulateParams } from 'elasticsearch';
import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config';
import { KibanaRequest as KibanaRequest_2 } from 'kibana/server';
import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server';
import { Logger as Logger_2 } from 'kibana/server';
import { MGetParams } from 'elasticsearch';
@ -116,6 +118,7 @@ import { RenderSearchTemplateParams } from 'elasticsearch';
import { Request } from 'hapi';
import { ResponseObject } from 'hapi';
import { ResponseToolkit } from 'hapi';
import { SavedObject as SavedObject_2 } from 'src/core/server';
import { SchemaTypeError } from '@kbn/config-schema';
import { ScrollParams } from 'elasticsearch';
import { SearchParams } from 'elasticsearch';
@ -139,6 +142,7 @@ import { TasksCancelParams } from 'elasticsearch';
import { TasksGetParams } from 'elasticsearch';
import { TasksListParams } from 'elasticsearch';
import { TermvectorsParams } from 'elasticsearch';
import { ToastInputFields } from 'src/core/public/notifications';
import { Type } from '@kbn/config-schema';
import { TypeOf } from '@kbn/config-schema';
import { Unit } from '@elastic/datemath';
@ -439,9 +443,15 @@ export interface IIndexPattern {
//
// @public @deprecated
export interface IndexPatternAttributes {
// (undocumented)
fieldFormatMap?: string;
// (undocumented)
fields: string;
// (undocumented)
intervalName?: string;
// (undocumented)
sourceFilters?: string;
// (undocumented)
timeFieldName?: string;
// (undocumented)
title: string;
@ -653,6 +663,9 @@ export class Plugin implements Plugin_2<PluginSetup, PluginStart> {
fieldFormats: {
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
};
indexPatterns: {
indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest<unknown, unknown, unknown, any>) => Promise<import("../common").IndexPatternsService>;
};
};
// (undocumented)
stop(): void;
@ -681,6 +694,10 @@ export interface PluginStart {
//
// (undocumented)
fieldFormats: FieldFormatsStart;
// Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStart" needs to be exported by the entry point index.d.ts
//
// (undocumented)
indexPatterns: IndexPatternsServiceStart;
// (undocumented)
search: ISearchStart;
}

View file

@ -38,6 +38,7 @@ export default async function ({ readConfigFile }) {
require.resolve('./test_suites/management'),
require.resolve('./test_suites/doc_views'),
require.resolve('./test_suites/application_links'),
require.resolve('./test_suites/data_plugin'),
],
services: {
...functionalConfig.get('services'),

View file

@ -0,0 +1,9 @@
{
"id": "index_patterns_test_plugin",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["index_patterns_test_plugin"],
"server": true,
"ui": false,
"requiredPlugins": ["data"]
}

View file

@ -0,0 +1,17 @@
{
"name": "index_patterns_test_plugin",
"version": "1.0.0",
"main": "target/test/plugin_functional/plugins/index_patterns_test_plugin",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
},
"license": "Apache-2.0",
"scripts": {
"kbn": "node ../../../../scripts/kbn.js",
"build": "rm -rf './target' && tsc"
},
"devDependencies": {
"typescript": "3.9.5"
}
}

View file

@ -0,0 +1,30 @@
/*
* 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 { PluginInitializer } from 'kibana/server';
import {
IndexPatternsTestPlugin,
IndexPatternsTestPluginSetup,
IndexPatternsTestPluginStart,
} from './plugin';
export const plugin: PluginInitializer<
IndexPatternsTestPluginSetup,
IndexPatternsTestPluginStart
> = () => new IndexPatternsTestPlugin();

View file

@ -0,0 +1,111 @@
/*
* 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 { CoreSetup, Plugin } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server';
export interface IndexPatternsTestStartDeps {
data: DataPluginStart;
}
export class IndexPatternsTestPlugin
implements
Plugin<
IndexPatternsTestPluginSetup,
IndexPatternsTestPluginStart,
{},
IndexPatternsTestStartDeps
> {
public setup(core: CoreSetup<IndexPatternsTestStartDeps>) {
const router = core.http.createRouter();
router.get(
{ path: '/api/index-patterns-plugin/get-all', validate: false },
async (context, req, res) => {
const [, { data }] = await core.getStartServices();
const service = await data.indexPatterns.indexPatternsServiceFactory(req);
const ids = await service.getIds();
return res.ok({ body: ids });
}
);
router.get(
{
path: '/api/index-patterns-plugin/get/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
},
},
async (context, req, res) => {
const id = (req.params as Record<string, string>).id;
const [, { data }] = await core.getStartServices();
const service = await data.indexPatterns.indexPatternsServiceFactory(req);
const ip = await service.get(id);
return res.ok({ body: ip.toSpec() });
}
);
router.get(
{
path: '/api/index-patterns-plugin/update/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
},
},
async (context, req, res) => {
const [, { data }] = await core.getStartServices();
const id = (req.params as Record<string, string>).id;
const service = await data.indexPatterns.indexPatternsServiceFactory(req);
const ip = await service.get(id);
await ip.save();
return res.ok();
}
);
router.get(
{
path: '/api/index-patterns-plugin/delete/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
},
},
async (context, req, res) => {
const [, { data }] = await core.getStartServices();
const id = (req.params as Record<string, string>).id;
const service = await data.indexPatterns.indexPatternsServiceFactory(req);
const ip = await service.get(id);
await ip.destroy();
return res.ok();
}
);
}
public start() {}
public stop() {}
}
export type IndexPatternsTestPluginSetup = ReturnType<IndexPatternsTestPlugin['setup']>;
export type IndexPatternsTestPluginStart = ReturnType<IndexPatternsTestPlugin['start']>;

View file

@ -0,0 +1,14 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"server/**/*.ts",
"server/**/*.tsx",
"../../../../typings/**/*",
],
"exclude": []
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
// @ts-expect-error
export default function ({ loadTestFile }) {
describe('data plugin', () => {
loadTestFile(require.resolve('./index_patterns'));
});
}

View file

@ -0,0 +1,64 @@
/*
* 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 expect from '@kbn/expect';
import { PluginFunctionalProviderContext } from '../../services';
import '../../plugins/core_provider_plugin/types';
// eslint-disable-next-line import/no-default-export
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'settings']);
describe('index patterns', function () {
let indexPatternId = '';
before(async () => {
await esArchiver.loadIfNeeded(
'../functional/fixtures/es_archiver/getting_started/shakespeare'
);
await PageObjects.common.navigateToApp('settings');
await PageObjects.settings.createIndexPattern('shakespeare', '');
});
it('can get all ids', async () => {
const body = await (await supertest.get('/api/index-patterns-plugin/get-all').expect(200))
.body;
indexPatternId = body[0];
expect(body.length > 0).to.equal(true);
});
it('can get index pattern by id', async () => {
const body = await (
await supertest.get(`/api/index-patterns-plugin/get/${indexPatternId}`).expect(200)
).body;
expect(body.fields.length > 0).to.equal(true);
});
it('can update index pattern', async () => {
const body = await (
await supertest.get(`/api/index-patterns-plugin/update/${indexPatternId}`).expect(200)
).body;
expect(body).to.eql({});
});
it('can delete index pattern', async () => {
await supertest.get(`/api/index-patterns-plugin/delete/${indexPatternId}`).expect(200);
});
});
}

View file

@ -6,9 +6,6 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import minimatch from 'minimatch';
import { SimpleSavedObject } from 'src/core/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { IndexPatternSavedObjectAttrs } from 'src/plugins/data/common/index_patterns/index_patterns/index_patterns';
import { getIndexPatternService, getUiSettings } from '../../../../kibana_services';
export type IndexPatternMeta = {
@ -29,13 +26,13 @@ export async function getSecurityIndexPatterns(): Promise<IndexPatternMeta[]> {
const indexPatternCache = await getIndexPatternService().getCache();
return indexPatternCache!
.filter((savedObject: SimpleSavedObject<IndexPatternSavedObjectAttrs>) => {
.filter((savedObject) => {
return (securityIndexPatternTitles as string[]).some((indexPatternTitle) => {
// glob matching index pattern title
return minimatch(indexPatternTitle, savedObject?.attributes?.title);
});
})
.map((savedObject: SimpleSavedObject<IndexPatternSavedObjectAttrs>) => {
.map((savedObject) => {
return {
id: savedObject.id,
title: savedObject.attributes.title,

View file

@ -8,7 +8,6 @@ import { useReducer } from 'react';
import { i18n } from '@kbn/i18n';
import { SimpleSavedObject } from 'kibana/public';
import { getErrorMessage } from '../../../../../../../common/util/errors';
import { DeepReadonly } from '../../../../../../../common/types/common';
import { ml } from '../../../../../services/ml_api_service';
@ -235,7 +234,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
// Set the index pattern titles which the user can choose as the source.
const indexPatternsMap: SourceIndexMap = {};
const savedObjects = (await mlContext.indexPatterns.getCache()) || [];
savedObjects.forEach((obj: SimpleSavedObject<Record<string, any>>) => {
savedObjects.forEach((obj) => {
const title = obj?.attributes?.title;
if (title !== undefined) {
const id = obj?.id || '';