mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[data views] Content management api implementation (#155803)
## Summary Data views implements the content management api and associated minor changes. The bulk of the changes are in `(common|public|server)/content_management)` Closes https://github.com/elastic/kibana/issues/157069 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Lukas Olson <olson.lukas@gmail.com>
This commit is contained in:
parent
282305fd65
commit
5fa2226b06
43 changed files with 592 additions and 171 deletions
|
@ -21,6 +21,7 @@ import type {
|
|||
SavedObjectsUpdateOptions,
|
||||
SavedObjectsFindResult,
|
||||
} from '@kbn/core-saved-objects-api-server';
|
||||
import { pick } from 'lodash';
|
||||
import type {
|
||||
CMCrudTypes,
|
||||
ServicesDefinitionSet,
|
||||
|
@ -44,16 +45,19 @@ type PartialSavedObject<T> = Omit<SavedObject<Partial<T>>, 'references'> & {
|
|||
|
||||
function savedObjectToItem<Attributes extends object, Item extends SOWithMetadata>(
|
||||
savedObject: SavedObject<Attributes>,
|
||||
allowedSavedObjectAttributes: string[],
|
||||
partial: false
|
||||
): Item;
|
||||
|
||||
function savedObjectToItem<Attributes extends object, PartialItem extends SOWithMetadata>(
|
||||
savedObject: PartialSavedObject<Attributes>,
|
||||
allowedSavedObjectAttributes: string[],
|
||||
partial: true
|
||||
): PartialItem;
|
||||
|
||||
function savedObjectToItem<Attributes extends object>(
|
||||
savedObject: SavedObject<Attributes> | PartialSavedObject<Attributes>
|
||||
savedObject: SavedObject<Attributes> | PartialSavedObject<Attributes>,
|
||||
allowedSavedObjectAttributes: string[]
|
||||
): SOWithMetadata | SOWithMetadataPartial {
|
||||
const {
|
||||
id,
|
||||
|
@ -64,6 +68,7 @@ function savedObjectToItem<Attributes extends object>(
|
|||
references,
|
||||
error,
|
||||
namespaces,
|
||||
version,
|
||||
} = savedObject;
|
||||
|
||||
return {
|
||||
|
@ -71,10 +76,11 @@ function savedObjectToItem<Attributes extends object>(
|
|||
type,
|
||||
updatedAt,
|
||||
createdAt,
|
||||
attributes,
|
||||
attributes: pick(attributes, allowedSavedObjectAttributes),
|
||||
references,
|
||||
error,
|
||||
namespaces,
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -123,6 +129,8 @@ export type UpdateArgsToSoUpdateOptions<Types extends CMCrudTypes> = (
|
|||
export interface SOContentStorageConstrutorParams<Types extends CMCrudTypes> {
|
||||
savedObjectType: string;
|
||||
cmServicesDefinition: ServicesDefinitionSet;
|
||||
// this is necessary since unexpected saved object attributes could cause schema validation to fail
|
||||
allowedSavedObjectAttributes: string[];
|
||||
createArgsToSoCreateOptions?: CreateArgsToSoCreateOptions<Types>;
|
||||
updateArgsToSoUpdateOptions?: UpdateArgsToSoUpdateOptions<Types>;
|
||||
searchArgsToSOFindOptions?: SearchArgsToSOFindOptions<Types>;
|
||||
|
@ -144,6 +152,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
updateArgsToSoUpdateOptions,
|
||||
searchArgsToSOFindOptions,
|
||||
enableMSearch,
|
||||
allowedSavedObjectAttributes,
|
||||
}: SOContentStorageConstrutorParams<Types>) {
|
||||
this.savedObjectType = savedObjectType;
|
||||
this.cmServicesDefinition = cmServicesDefinition;
|
||||
|
@ -152,6 +161,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
this.updateArgsToSoUpdateOptions =
|
||||
updateArgsToSoUpdateOptions || updateArgsToSoUpdateOptionsDefault;
|
||||
this.searchArgsToSOFindOptions = searchArgsToSOFindOptions || searchArgsToSOFindOptionsDefault;
|
||||
this.allowedSavedObjectAttributes = allowedSavedObjectAttributes;
|
||||
|
||||
if (enableMSearch) {
|
||||
this.mSearch = {
|
||||
|
@ -163,7 +173,13 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
const { value, error: resultError } = transforms.mSearch.out.result.down<
|
||||
Types['Item'],
|
||||
Types['Item']
|
||||
>(savedObjectToItem(savedObject as SavedObjectsFindResult<Types['Attributes']>, false));
|
||||
>(
|
||||
savedObjectToItem(
|
||||
savedObject as SavedObjectsFindResult<Types['Attributes']>,
|
||||
this.allowedSavedObjectAttributes,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
if (resultError) {
|
||||
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
|
||||
|
@ -180,6 +196,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
private createArgsToSoCreateOptions: CreateArgsToSoCreateOptions<Types>;
|
||||
private updateArgsToSoUpdateOptions: UpdateArgsToSoUpdateOptions<Types>;
|
||||
private searchArgsToSOFindOptions: SearchArgsToSOFindOptions<Types>;
|
||||
private allowedSavedObjectAttributes: string[];
|
||||
|
||||
mSearch?: {
|
||||
savedObjectType: string;
|
||||
|
@ -199,7 +216,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
} = await soClient.resolve<Types['Attributes']>(this.savedObjectType, id);
|
||||
|
||||
const response: Types['GetOut'] = {
|
||||
item: savedObjectToItem(savedObject, false),
|
||||
item: savedObjectToItem(savedObject, this.allowedSavedObjectAttributes, false),
|
||||
meta: {
|
||||
aliasPurpose,
|
||||
aliasTargetId,
|
||||
|
@ -264,7 +281,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
Types['CreateOut'],
|
||||
Types['CreateOut']
|
||||
>({
|
||||
item: savedObjectToItem(savedObject, false),
|
||||
item: savedObjectToItem(savedObject, this.allowedSavedObjectAttributes, false),
|
||||
});
|
||||
|
||||
if (resultError) {
|
||||
|
@ -315,7 +332,7 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
Types['UpdateOut'],
|
||||
Types['UpdateOut']
|
||||
>({
|
||||
item: savedObjectToItem(partialSavedObject, true),
|
||||
item: savedObjectToItem(partialSavedObject, this.allowedSavedObjectAttributes, true),
|
||||
});
|
||||
|
||||
if (resultError) {
|
||||
|
@ -325,9 +342,14 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
return value;
|
||||
}
|
||||
|
||||
async delete(ctx: StorageContext, id: string): Promise<Types['DeleteOut']> {
|
||||
async delete(
|
||||
ctx: StorageContext,
|
||||
id: string,
|
||||
// force is necessary to delete saved objects that exist in multiple namespaces
|
||||
options?: { force: boolean }
|
||||
): Promise<Types['DeleteOut']> {
|
||||
const soClient = await savedObjectClientFromRequest(ctx);
|
||||
await soClient.delete(this.savedObjectType, id);
|
||||
await soClient.delete(this.savedObjectType, id, { force: options?.force ?? false });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
@ -361,7 +383,9 @@ export abstract class SOContentStorage<Types extends CMCrudTypes>
|
|||
Types['SearchOut'],
|
||||
Types['SearchOut']
|
||||
>({
|
||||
hits: response.saved_objects.map((so) => savedObjectToItem(so, false)),
|
||||
hits: response.saved_objects.map((so) =>
|
||||
savedObjectToItem(so, this.allowedSavedObjectAttributes, false)
|
||||
),
|
||||
pagination: {
|
||||
total: response.total,
|
||||
},
|
||||
|
|
|
@ -176,7 +176,7 @@ export interface SavedObjectUpdateOptions<Attributes = unknown> {
|
|||
}
|
||||
|
||||
/** Return value for Saved Object get, T is item returned */
|
||||
export type GetResultSO<T extends object> = GetResult<
|
||||
export type GetResultSO<T extends object = object> = GetResult<
|
||||
T,
|
||||
{
|
||||
outcome: 'exactMatch' | 'aliasMatch' | 'conflict';
|
||||
|
|
|
@ -46,6 +46,7 @@ describe('kibanaContextFn', () => {
|
|||
delete: jest.fn(),
|
||||
find: jest.fn(),
|
||||
get: jest.fn(),
|
||||
getSavedSearch: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
@ -53,7 +54,7 @@ describe('kibanaContextFn', () => {
|
|||
|
||||
it('merges and deduplicates queries from different sources', async () => {
|
||||
const { fn } = kibanaContextFn;
|
||||
startServicesMock.savedObjectsClient.get.mockResolvedValue({
|
||||
startServicesMock.savedObjectsClient.getSavedSearch.mockResolvedValue({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: JSON.stringify({
|
||||
|
|
|
@ -129,7 +129,7 @@ export const getKibanaContextFn = (
|
|||
let filters = [...(input?.filters || [])];
|
||||
|
||||
if (args.savedSearchId) {
|
||||
const obj = await savedObjectsClient.get('search', args.savedSearchId);
|
||||
const obj = await savedObjectsClient.getSavedSearch(args.savedSearchId);
|
||||
const search = (obj.attributes as any).kibanaSavedObjectMeta.searchSourceJSON as string;
|
||||
const { query, filter } = getParsedValue(search, {});
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "Elastic",
|
||||
"info": Array [
|
||||
"Rollup aggregations:",
|
||||
"terms",
|
||||
|
@ -92,6 +93,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "timestamp",
|
||||
"info": Array [
|
||||
"Rollup aggregations:",
|
||||
"date_histogram (interval: 30s, delay: 30s, UTC)",
|
||||
|
@ -111,6 +113,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "conflictingField",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -126,6 +129,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "amount",
|
||||
"info": Array [
|
||||
"Rollup aggregations:",
|
||||
"histogram (interval: 5)",
|
||||
|
@ -149,6 +153,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"id": "runtime",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -191,6 +196,7 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "Elastic",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -228,6 +234,7 @@ exports[`IndexedFieldsTable should filter based on the schema filter 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"id": "runtime",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -270,6 +277,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "timestamp",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -306,6 +314,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "Elastic",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -322,6 +331,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "timestamp",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -338,6 +348,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "conflictingField",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -353,6 +364,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": false,
|
||||
"id": "amount",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
@ -368,6 +380,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"id": "runtime",
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
|
|
|
@ -76,6 +76,7 @@ export class IndexedFieldsTable extends Component<
|
|||
fields.map((field) => {
|
||||
return {
|
||||
...field.spec,
|
||||
id: field.name,
|
||||
type: field.esTypes?.join(', ') || '',
|
||||
kbnType: field.type,
|
||||
displayName: field.displayName,
|
||||
|
@ -114,6 +115,7 @@ export class IndexedFieldsTable extends Component<
|
|||
},
|
||||
},
|
||||
name,
|
||||
id: name,
|
||||
type: 'composite',
|
||||
kbnType: '',
|
||||
displayName: name,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ContentManagementServicesDefinition as ServicesDefinition,
|
||||
Version,
|
||||
} from '@kbn/object-versioning';
|
||||
|
||||
// We export the versionned service definition from this file and not the barrel to avoid adding
|
||||
// the schemas in the "public" js bundle
|
||||
|
||||
import { serviceDefinition as v1 } from './v1/cm_services';
|
||||
|
||||
export const cmServicesDefinition: { [version: Version]: ServicesDefinition } = {
|
||||
1: v1,
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './v1';
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { ContentManagementServicesDefinition as ServicesDefinition } from '@kbn/object-versioning';
|
||||
import {
|
||||
savedObjectSchema,
|
||||
objectTypeToGetResultSchema,
|
||||
createOptionsSchemas,
|
||||
updateOptionsSchema,
|
||||
createResultSchema,
|
||||
searchOptionsSchemas,
|
||||
} from '@kbn/content-management-utils';
|
||||
import { serializedFieldFormatSchema, fieldSpecSchema } from '../../schemas';
|
||||
|
||||
const dataViewAttributesSchema = schema.object(
|
||||
{
|
||||
title: schema.string(),
|
||||
type: schema.maybe(schema.literal('rollup')),
|
||||
timeFieldName: schema.maybe(schema.string()),
|
||||
sourceFilters: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
value: schema.string(),
|
||||
})
|
||||
)
|
||||
),
|
||||
fields: schema.maybe(schema.arrayOf(fieldSpecSchema)),
|
||||
typeMeta: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
fieldFormatMap: schema.maybe(schema.recordOf(schema.string(), serializedFieldFormatSchema)),
|
||||
fieldAttrs: schema.maybe(
|
||||
schema.recordOf(
|
||||
schema.string(),
|
||||
schema.object({
|
||||
customLabel: schema.maybe(schema.string()),
|
||||
count: schema.maybe(schema.number()),
|
||||
})
|
||||
)
|
||||
),
|
||||
allowNoIndex: schema.maybe(schema.boolean()),
|
||||
runtimeFieldMap: schema.maybe(schema.any()),
|
||||
name: schema.maybe(schema.string()),
|
||||
},
|
||||
{ unknowns: 'forbid' }
|
||||
);
|
||||
|
||||
const dataViewSavedObjectSchema = savedObjectSchema(dataViewAttributesSchema);
|
||||
|
||||
const dataViewCreateOptionsSchema = schema.object({
|
||||
id: createOptionsSchemas.id,
|
||||
initialNamespaces: createOptionsSchemas.initialNamespaces,
|
||||
});
|
||||
|
||||
const dataViewSearchOptionsSchema = schema.object({
|
||||
searchFields: searchOptionsSchemas.searchFields,
|
||||
fields: searchOptionsSchemas.fields,
|
||||
});
|
||||
|
||||
const dataViewUpdateOptionsSchema = schema.object({
|
||||
version: updateOptionsSchema.version,
|
||||
refresh: updateOptionsSchema.refresh,
|
||||
retryOnConflict: updateOptionsSchema.retryOnConflict,
|
||||
});
|
||||
|
||||
// Content management service definition.
|
||||
// We need it for BWC support between different versions of the content
|
||||
export const serviceDefinition: ServicesDefinition = {
|
||||
get: {
|
||||
out: {
|
||||
result: {
|
||||
schema: objectTypeToGetResultSchema(dataViewSavedObjectSchema),
|
||||
},
|
||||
},
|
||||
},
|
||||
create: {
|
||||
in: {
|
||||
options: {
|
||||
schema: dataViewCreateOptionsSchema,
|
||||
},
|
||||
data: {
|
||||
schema: dataViewAttributesSchema,
|
||||
},
|
||||
},
|
||||
out: {
|
||||
result: {
|
||||
schema: createResultSchema(dataViewSavedObjectSchema),
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
in: {
|
||||
options: {
|
||||
schema: dataViewUpdateOptionsSchema,
|
||||
},
|
||||
data: {
|
||||
schema: dataViewAttributesSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
in: {
|
||||
options: {
|
||||
schema: dataViewSearchOptionsSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
mSearch: {
|
||||
out: {
|
||||
result: {
|
||||
schema: dataViewSavedObjectSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE as DataViewSOType } from '../..';
|
||||
export { DataViewSOType };
|
||||
|
||||
/**
|
||||
* Data view saved object version.
|
||||
*/
|
||||
export const LATEST_VERSION = 1;
|
||||
|
||||
export type DataViewContentType = typeof DataViewSOType;
|
15
src/plugins/data_views/common/content_management/v1/index.ts
Normal file
15
src/plugins/data_views/common/content_management/v1/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { LATEST_VERSION } from './constants';
|
||||
|
||||
export type { DataViewCrudTypes } from './types';
|
||||
|
||||
export type { DataViewContentType } from './constants';
|
||||
|
||||
export { DataViewSOType } from './constants';
|
40
src/plugins/data_views/common/content_management/v1/types.ts
Normal file
40
src/plugins/data_views/common/content_management/v1/types.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ContentManagementCrudTypes,
|
||||
SavedObjectCreateOptions,
|
||||
SavedObjectSearchOptions,
|
||||
SavedObjectUpdateOptions,
|
||||
} from '@kbn/content-management-utils';
|
||||
import { DataViewAttributes } from '../../types';
|
||||
import { DataViewContentType } from './constants';
|
||||
|
||||
interface DataViewCreateOptions {
|
||||
id?: SavedObjectCreateOptions['id'];
|
||||
initialNamespaces?: SavedObjectCreateOptions['initialNamespaces'];
|
||||
}
|
||||
|
||||
interface DataViewUpdateOptions {
|
||||
version?: SavedObjectUpdateOptions['version'];
|
||||
refresh?: SavedObjectUpdateOptions['refresh'];
|
||||
retryOnConflict?: SavedObjectUpdateOptions['retryOnConflict'];
|
||||
}
|
||||
|
||||
interface DataViewSearchOptions {
|
||||
searchFields?: SavedObjectSearchOptions['searchFields'];
|
||||
fields?: SavedObjectSearchOptions['fields'];
|
||||
}
|
||||
|
||||
export type DataViewCrudTypes = ContentManagementCrudTypes<
|
||||
DataViewContentType,
|
||||
DataViewAttributes,
|
||||
DataViewCreateOptions,
|
||||
DataViewUpdateOptions,
|
||||
DataViewSearchOptions
|
||||
>;
|
|
@ -6,14 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObject } from '@kbn/core/types';
|
||||
import { stubFieldSpecMap, stubLogstashFieldSpecMap } from './field.stub';
|
||||
import { createStubDataView } from './data_views/data_view.stub';
|
||||
export {
|
||||
createStubDataView,
|
||||
createStubDataView as createStubIndexPattern,
|
||||
} from './data_views/data_view.stub';
|
||||
import { DataViewAttributes } from './types';
|
||||
|
||||
export const stubDataView = createStubDataView({
|
||||
spec: {
|
||||
|
@ -43,9 +41,7 @@ export const stubLogstashDataView = createStubDataView({
|
|||
},
|
||||
});
|
||||
|
||||
export function stubbedSavedObjectDataView(
|
||||
id: string | null = null
|
||||
): SavedObject<DataViewAttributes> {
|
||||
export function stubbedSavedObjectDataView(id: string | null = null) {
|
||||
return {
|
||||
id: id ?? '',
|
||||
type: 'index-pattern',
|
||||
|
|
|
@ -77,7 +77,7 @@ describe('IndexPatterns', () => {
|
|||
savedObjectsClient.find = jest.fn(
|
||||
() => Promise.resolve([indexPatternObj]) as Promise<Array<SavedObject<any>>>
|
||||
);
|
||||
savedObjectsClient.delete = jest.fn(() => Promise.resolve({}) as Promise<any>);
|
||||
savedObjectsClient.delete = jest.fn(() => Promise.resolve() as Promise<any>);
|
||||
savedObjectsClient.create = jest.fn();
|
||||
savedObjectsClient.get = jest.fn().mockImplementation(async (type, id) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, SOClientGetDelay));
|
||||
|
@ -87,23 +87,21 @@ describe('IndexPatterns', () => {
|
|||
attributes: object.attributes,
|
||||
};
|
||||
});
|
||||
savedObjectsClient.update = jest
|
||||
.fn()
|
||||
.mockImplementation(async (type, id, body, { version }) => {
|
||||
if (object.version !== version) {
|
||||
throw new Object({
|
||||
res: {
|
||||
status: 409,
|
||||
},
|
||||
});
|
||||
}
|
||||
object.attributes.title = body.title;
|
||||
object.version += 'a';
|
||||
return {
|
||||
id: object.id,
|
||||
version: object.version,
|
||||
};
|
||||
});
|
||||
savedObjectsClient.update = jest.fn().mockImplementation(async (id, body, { version }) => {
|
||||
if (object.version !== version) {
|
||||
throw new Object({
|
||||
res: {
|
||||
status: 409,
|
||||
},
|
||||
});
|
||||
}
|
||||
object.attributes.title = body.title;
|
||||
object.version += 'a';
|
||||
return {
|
||||
id: object.id,
|
||||
version: object.version,
|
||||
};
|
||||
});
|
||||
|
||||
apiClient = createFieldsFetcher();
|
||||
|
||||
|
@ -267,7 +265,6 @@ describe('IndexPatterns', () => {
|
|||
test('savedObjectCache pre-fetches title, type, typeMeta', async () => {
|
||||
expect(await indexPatterns.getIds()).toEqual(['id']);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
type: 'index-pattern',
|
||||
fields: ['title', 'type', 'typeMeta', 'name'],
|
||||
perPage: 10000,
|
||||
});
|
||||
|
@ -376,7 +373,6 @@ describe('IndexPatterns', () => {
|
|||
await indexPatterns.find('kibana*', size);
|
||||
|
||||
expect(savedObjectsClient.find).lastCalledWith({
|
||||
type: 'index-pattern',
|
||||
fields: ['title'],
|
||||
search,
|
||||
searchFields: ['title'],
|
||||
|
@ -449,13 +445,13 @@ describe('IndexPatterns', () => {
|
|||
dataView.setFieldFormat('field', { id: 'formatId' });
|
||||
await indexPatterns.updateSavedObject(dataView);
|
||||
let lastCall = (savedObjectsClient.update as jest.Mock).mock.calls.pop() ?? [];
|
||||
let [, , attrs] = lastCall;
|
||||
let [, attrs] = lastCall;
|
||||
expect(attrs).toHaveProperty('fieldFormatMap');
|
||||
expect(attrs.fieldFormatMap).toMatchInlineSnapshot(`"{\\"field\\":{\\"id\\":\\"formatId\\"}}"`);
|
||||
dataView.deleteFieldFormat('field');
|
||||
await indexPatterns.updateSavedObject(dataView);
|
||||
lastCall = (savedObjectsClient.update as jest.Mock).mock.calls.pop() ?? [];
|
||||
[, , attrs] = lastCall;
|
||||
[, attrs] = lastCall;
|
||||
|
||||
// https://github.com/elastic/kibana/issues/134873: must keep an empty object and not delete it
|
||||
expect(attrs).toHaveProperty('fieldFormatMap');
|
||||
|
@ -554,7 +550,7 @@ describe('IndexPatterns', () => {
|
|||
|
||||
savedObjectsClient.get = jest
|
||||
.fn()
|
||||
.mockImplementation((type: string, id: string) =>
|
||||
.mockImplementation((id: string) =>
|
||||
Promise.resolve({ id, version: 'a', attributes: { title: 'title' } })
|
||||
);
|
||||
|
||||
|
@ -586,7 +582,7 @@ describe('IndexPatterns', () => {
|
|||
|
||||
savedObjectsClient.get = jest
|
||||
.fn()
|
||||
.mockImplementation((type: string, id: string) =>
|
||||
.mockImplementation((id: string) =>
|
||||
Promise.resolve({ id, version: 'a', attributes: { title: '1' } })
|
||||
);
|
||||
|
||||
|
|
|
@ -10,9 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { castEsToKbnFieldTypeName } from '@kbn/field-types';
|
||||
import { FieldFormatsStartCommon, FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common';
|
||||
import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '..';
|
||||
import { SavedObjectsClientCommon } from '../types';
|
||||
|
||||
import { createDataViewCache } from '.';
|
||||
|
@ -31,6 +29,7 @@ import {
|
|||
DataViewFieldMap,
|
||||
TypeMeta,
|
||||
} from '../types';
|
||||
|
||||
import { META_FIELDS, SavedObject } from '..';
|
||||
import { DataViewMissingIndices } from '../lib';
|
||||
import { findByName } from '../utils';
|
||||
|
@ -175,7 +174,7 @@ export interface DataViewsServicePublicMethods {
|
|||
* Delete data view
|
||||
* @param indexPatternId - Id of the data view to delete.
|
||||
*/
|
||||
delete: (indexPatternId: string) => Promise<{}>;
|
||||
delete: (indexPatternId: string) => Promise<void>;
|
||||
/**
|
||||
* Takes field array and field attributes and returns field map by name.
|
||||
* @param fields - Array of fieldspecs
|
||||
|
@ -349,8 +348,7 @@ export class DataViewsService {
|
|||
* Refresh cache of index pattern ids and titles.
|
||||
*/
|
||||
private async refreshSavedObjectsCache() {
|
||||
const so = await this.savedObjectsClient.find<DataViewSavedObjectAttrs>({
|
||||
type: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
const so = await this.savedObjectsClient.find({
|
||||
fields: ['title', 'type', 'typeMeta', 'name'],
|
||||
perPage: 10000,
|
||||
});
|
||||
|
@ -392,8 +390,7 @@ export class DataViewsService {
|
|||
* @returns DataView[]
|
||||
*/
|
||||
find = async (search: string, size: number = 10): Promise<DataView[]> => {
|
||||
const savedObjects = await this.savedObjectsClient.find<DataViewSavedObjectAttrs>({
|
||||
type: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
const savedObjects = await this.savedObjectsClient.find({
|
||||
fields: ['title'],
|
||||
search,
|
||||
searchFields: ['title'],
|
||||
|
@ -726,14 +723,7 @@ export class DataViewsService {
|
|||
id: string,
|
||||
displayErrors: boolean = true
|
||||
): Promise<DataView> => {
|
||||
const savedObject = await this.savedObjectsClient.get<DataViewAttributes>(
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
id
|
||||
);
|
||||
|
||||
if (!savedObject.version) {
|
||||
throw new SavedObjectNotFound('data view', id, 'management/kibana/dataViews');
|
||||
}
|
||||
const savedObject = await this.savedObjectsClient.get(id);
|
||||
|
||||
return this.initFromSavedObject(savedObject, displayErrors);
|
||||
};
|
||||
|
@ -1006,14 +996,11 @@ export class DataViewsService {
|
|||
}
|
||||
|
||||
const body = dataView.getAsSavedObjectBody();
|
||||
const response: SavedObject<DataViewAttributes> = (await this.savedObjectsClient.create(
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
body,
|
||||
{
|
||||
id: dataView.id,
|
||||
initialNamespaces: dataView.namespaces.length > 0 ? dataView.namespaces : undefined,
|
||||
}
|
||||
)) as SavedObject<DataViewAttributes>;
|
||||
|
||||
const response: SavedObject<DataViewAttributes> = (await this.savedObjectsClient.create(body, {
|
||||
id: dataView.id,
|
||||
initialNamespaces: dataView.namespaces.length > 0 ? dataView.namespaces : undefined,
|
||||
})) as SavedObject<DataViewAttributes>;
|
||||
|
||||
const createdIndexPattern = await this.initFromSavedObject(response, displayErrors);
|
||||
if (this.savedObjectsCache) {
|
||||
|
@ -1055,7 +1042,7 @@ export class DataViewsService {
|
|||
});
|
||||
|
||||
return this.savedObjectsClient
|
||||
.update(DATA_VIEW_SAVED_OBJECT_TYPE, indexPattern.id, body, {
|
||||
.update(indexPattern.id, body, {
|
||||
version: indexPattern.version,
|
||||
})
|
||||
.then((response) => {
|
||||
|
@ -1136,7 +1123,7 @@ export class DataViewsService {
|
|||
throw new DataViewInsufficientAccessError(indexPatternId);
|
||||
}
|
||||
this.dataViewCache.clear(indexPatternId);
|
||||
return this.savedObjectsClient.delete(DATA_VIEW_SAVED_OBJECT_TYPE, indexPatternId);
|
||||
return this.savedObjectsClient.delete(indexPatternId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,8 @@ export {
|
|||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
} from './constants';
|
||||
|
||||
export { LATEST_VERSION } from './content_management/v1/constants';
|
||||
|
||||
export type { ToSpecConfig } from './fields';
|
||||
export type { IIndexPatternFieldList } from './fields';
|
||||
export {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema, Type } from '@kbn/config-schema';
|
||||
import { /* RUNTIME_FIELD_TYPES,*/ RuntimeType } from '../../../common';
|
||||
import { RuntimeType } from '.';
|
||||
|
||||
export const serializedFieldFormatSchema = schema.object({
|
||||
id: schema.maybe(schema.string()),
|
||||
|
@ -16,11 +16,11 @@ export const serializedFieldFormatSchema = schema.object({
|
|||
|
||||
export const fieldSpecSchemaFields = {
|
||||
name: schema.string({
|
||||
maxLength: 1_000,
|
||||
maxLength: 1000,
|
||||
}),
|
||||
type: schema.string({
|
||||
defaultValue: 'string',
|
||||
maxLength: 1_000,
|
||||
maxLength: 1000,
|
||||
}),
|
||||
count: schema.maybe(
|
||||
schema.number({
|
||||
|
@ -29,7 +29,7 @@ export const fieldSpecSchemaFields = {
|
|||
),
|
||||
script: schema.maybe(
|
||||
schema.string({
|
||||
maxLength: 1_000_000,
|
||||
maxLength: 1000000,
|
||||
})
|
||||
),
|
||||
format: schema.maybe(serializedFieldFormatSchema),
|
|
@ -7,11 +7,7 @@
|
|||
*/
|
||||
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type {
|
||||
SavedObject,
|
||||
SavedObjectsCreateOptions,
|
||||
SavedObjectsUpdateOptions,
|
||||
} from '@kbn/core/public';
|
||||
import type { SavedObject } from '@kbn/core/server';
|
||||
import type { ErrorToastOptions, ToastInputFields } from '@kbn/core-notifications-browser';
|
||||
import type { DataViewFieldBase } from '@kbn/es-query';
|
||||
import type { SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
|
@ -119,7 +115,7 @@ export interface DataViewAttributes {
|
|||
/**
|
||||
* Fields as a serialized array of field specs
|
||||
*/
|
||||
fields: string;
|
||||
fields?: string;
|
||||
/**
|
||||
* Data view title
|
||||
*/
|
||||
|
@ -236,10 +232,6 @@ export interface UiSettingsCommon {
|
|||
* @public
|
||||
*/
|
||||
export interface SavedObjectsClientCommonFindArgs {
|
||||
/**
|
||||
* Saved object type
|
||||
*/
|
||||
type: string | string[];
|
||||
/**
|
||||
* Saved object fields
|
||||
*/
|
||||
|
@ -267,13 +259,23 @@ export interface SavedObjectsClientCommon {
|
|||
* Search for saved objects
|
||||
* @param options - options for search
|
||||
*/
|
||||
find: <T = unknown>(options: SavedObjectsClientCommonFindArgs) => Promise<Array<SavedObject<T>>>;
|
||||
find: (
|
||||
options: SavedObjectsClientCommonFindArgs
|
||||
) => Promise<Array<SavedObject<DataViewAttributes>>>;
|
||||
/**
|
||||
* Get a single saved object by id
|
||||
* @param type - type of saved object
|
||||
* @param id - id of saved object
|
||||
*/
|
||||
get: <T = unknown>(type: string, id: string) => Promise<SavedObject<T>>;
|
||||
get: (id: string) => Promise<SavedObject<DataViewAttributes>>;
|
||||
/**
|
||||
* Update a saved object by id
|
||||
* @param type - type of saved object
|
||||
* @param id - id of saved object
|
||||
* @param attributes - attributes to update
|
||||
* @param options - client options
|
||||
*/
|
||||
getSavedSearch: (id: string) => Promise<SavedObject>;
|
||||
/**
|
||||
* Update a saved object by id
|
||||
* @param type - type of saved object
|
||||
|
@ -282,28 +284,26 @@ export interface SavedObjectsClientCommon {
|
|||
* @param options - client options
|
||||
*/
|
||||
update: (
|
||||
type: string,
|
||||
id: string,
|
||||
attributes: DataViewAttributes,
|
||||
options: SavedObjectsUpdateOptions
|
||||
options: { version?: string }
|
||||
) => Promise<SavedObject>;
|
||||
/**
|
||||
* Create a saved object
|
||||
* @param type - type of saved object
|
||||
* @param attributes - attributes to set
|
||||
* @param options - client options
|
||||
*/
|
||||
create: (
|
||||
type: string,
|
||||
attributes: DataViewAttributes,
|
||||
options: SavedObjectsCreateOptions & { initialNamespaces?: string[] }
|
||||
// SavedObjectsCreateOptions
|
||||
options: { id?: string; initialNamespaces?: string[] }
|
||||
) => Promise<SavedObject>;
|
||||
/**
|
||||
* Delete a saved object by id
|
||||
* @param type - type of saved object
|
||||
* @param id - id of saved object
|
||||
*/
|
||||
delete: (type: string, id: string) => Promise<{}>;
|
||||
delete: (id: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface GetFieldsOptions {
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DataViewSavedObjectAttrs } from './data_views';
|
||||
import type { SavedObjectsClientCommon } from './types';
|
||||
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from './constants';
|
||||
|
||||
/**
|
||||
* Returns an object matching a given name
|
||||
*
|
||||
|
@ -20,8 +17,7 @@ import { DATA_VIEW_SAVED_OBJECT_TYPE } from './constants';
|
|||
*/
|
||||
export async function findByName(client: SavedObjectsClientCommon, name: string) {
|
||||
if (name) {
|
||||
const savedObjects = await client.find<{ name: DataViewSavedObjectAttrs['name'] }>({
|
||||
type: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
const savedObjects = await client.find({
|
||||
perPage: 10,
|
||||
search: `"${name}"`,
|
||||
searchFields: ['name.keyword'],
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"browser": true,
|
||||
"requiredPlugins": [
|
||||
"fieldFormats",
|
||||
"expressions"
|
||||
"expressions",
|
||||
"contentManagement"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"usageCollection"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getIndexPatternLoad } from './expressions';
|
||||
import {
|
||||
DataViewsPublicPluginSetup,
|
||||
|
@ -25,6 +26,9 @@ import { getIndices, HasData } from './services';
|
|||
|
||||
import { debounceByKey } from './debounce_by_key';
|
||||
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../common/constants';
|
||||
import { LATEST_VERSION } from '../common/content_management/v1/constants';
|
||||
|
||||
export class DataViewsPublicPlugin
|
||||
implements
|
||||
Plugin<
|
||||
|
@ -38,18 +42,28 @@ export class DataViewsPublicPlugin
|
|||
|
||||
public setup(
|
||||
core: CoreSetup<DataViewsPublicStartDependencies, DataViewsPublicPluginStart>,
|
||||
{ expressions }: DataViewsPublicSetupDependencies
|
||||
{ expressions, contentManagement }: DataViewsPublicSetupDependencies
|
||||
): DataViewsPublicPluginSetup {
|
||||
expressions.registerFunction(getIndexPatternLoad({ getStartServices: core.getStartServices }));
|
||||
|
||||
contentManagement.registry.register({
|
||||
id: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
version: {
|
||||
latest: LATEST_VERSION,
|
||||
},
|
||||
name: i18n.translate('dataViews.contentManagementType', {
|
||||
defaultMessage: 'Data view',
|
||||
}),
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
{ fieldFormats }: DataViewsPublicStartDependencies
|
||||
{ fieldFormats, contentManagement }: DataViewsPublicStartDependencies
|
||||
): DataViewsPublicPluginStart {
|
||||
const { uiSettings, http, notifications, savedObjects, application } = core;
|
||||
const { uiSettings, http, notifications, application, savedObjects } = core;
|
||||
|
||||
const onNotifDebounced = debounceByKey(
|
||||
notifications.toasts.add.bind(notifications.toasts),
|
||||
|
@ -63,7 +77,10 @@ export class DataViewsPublicPlugin
|
|||
return new DataViewsServicePublic({
|
||||
hasData: this.hasData.start(core),
|
||||
uiSettings: new UiSettingsPublicToCommon(uiSettings),
|
||||
savedObjectsClient: new SavedObjectsClientPublicToCommon(savedObjects.client),
|
||||
savedObjectsClient: new SavedObjectsClientPublicToCommon(
|
||||
contentManagement.client,
|
||||
savedObjects.client
|
||||
),
|
||||
apiClient: new DataViewsApiClient(http),
|
||||
fieldFormats,
|
||||
onNotification: (toastInputFields, key) => {
|
||||
|
|
|
@ -7,22 +7,23 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper';
|
||||
import { ContentClient } from '@kbn/content-management-plugin/public';
|
||||
import { savedObjectsServiceMock } from '@kbn/core/public/mocks';
|
||||
|
||||
import { DataViewSavedObjectConflictError } from '../common';
|
||||
|
||||
describe('SavedObjectsClientPublicToCommon', () => {
|
||||
const soClient = savedObjectsServiceMock.createStartContract().client;
|
||||
const cmClient = {} as ContentClient;
|
||||
|
||||
test('get saved object - exactMatch', async () => {
|
||||
const mockedSavedObject = {
|
||||
version: 'abc',
|
||||
};
|
||||
soClient.resolve = jest
|
||||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(soClient);
|
||||
const result = await service.get('index-pattern', '1');
|
||||
.mockResolvedValue({ meta: { outcome: 'exactMatch' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
||||
|
@ -30,11 +31,11 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
const mockedSavedObject = {
|
||||
version: 'def',
|
||||
};
|
||||
soClient.resolve = jest
|
||||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(soClient);
|
||||
const result = await service.get('index-pattern', '1');
|
||||
.mockResolvedValue({ meta: { outcome: 'aliasMatch' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
||||
|
@ -43,13 +44,11 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
version: 'ghi',
|
||||
};
|
||||
|
||||
soClient.resolve = jest
|
||||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(soClient);
|
||||
.mockResolvedValue({ meta: { outcome: 'conflict' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
|
||||
await expect(service.get('index-pattern', '1')).rejects.toThrow(
|
||||
DataViewSavedObjectConflictError
|
||||
);
|
||||
await expect(service.get('1')).rejects.toThrow(DataViewSavedObjectConflictError);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,13 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsCreateOptions,
|
||||
SavedObjectsUpdateOptions,
|
||||
SimpleSavedObject,
|
||||
} from '@kbn/core/public';
|
||||
import { omit } from 'lodash';
|
||||
import type { ContentClient } from '@kbn/content-management-plugin/public';
|
||||
import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common';
|
||||
import type { SavedObjectsClientContract } from '@kbn/core/public';
|
||||
import { DataViewSavedObjectConflictError } from '../common/errors';
|
||||
import {
|
||||
DataViewAttributes,
|
||||
|
@ -21,53 +17,113 @@ import {
|
|||
SavedObjectsClientCommonFindArgs,
|
||||
} from '../common/types';
|
||||
|
||||
type SOClient = Pick<
|
||||
SavedObjectsClientContract,
|
||||
'find' | 'resolve' | 'update' | 'create' | 'delete'
|
||||
>;
|
||||
import type { DataViewCrudTypes } from '../common/content_management';
|
||||
|
||||
const simpleSavedObjectToSavedObject = <T>(simpleSavedObject: SimpleSavedObject): SavedObject<T> =>
|
||||
({
|
||||
version: simpleSavedObject._version,
|
||||
...omit(simpleSavedObject, '_version'),
|
||||
} as SavedObject<T>);
|
||||
import { DataViewSOType } from '../common/content_management';
|
||||
|
||||
type SOClient = Pick<SavedObjectsClientContract, 'resolve'>;
|
||||
|
||||
export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommon {
|
||||
private contentManagementClient: ContentClient;
|
||||
private savedObjectClient: SOClient;
|
||||
|
||||
constructor(savedObjectClient: SOClient) {
|
||||
constructor(contentManagementClient: ContentClient, savedObjectClient: SOClient) {
|
||||
this.contentManagementClient = contentManagementClient;
|
||||
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 find(options: SavedObjectsClientCommonFindArgs) {
|
||||
const results = await this.contentManagementClient.search<
|
||||
DataViewCrudTypes['SearchIn'],
|
||||
DataViewCrudTypes['SearchOut']
|
||||
>({
|
||||
contentTypeId: DataViewSOType,
|
||||
query: {
|
||||
text: options.search,
|
||||
limit: options.perPage,
|
||||
},
|
||||
options: {
|
||||
searchFields: options.searchFields,
|
||||
fields: options.fields,
|
||||
},
|
||||
});
|
||||
return results.hits;
|
||||
}
|
||||
|
||||
async get<T = unknown>(type: string, id: string) {
|
||||
const response = await this.savedObjectClient.resolve<T>(type, id);
|
||||
async get(id: string) {
|
||||
let response: DataViewCrudTypes['GetOut'];
|
||||
try {
|
||||
response = await this.contentManagementClient.get<
|
||||
DataViewCrudTypes['GetIn'],
|
||||
DataViewCrudTypes['GetOut']
|
||||
>({
|
||||
contentTypeId: DataViewSOType,
|
||||
id,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.body?.statusCode === 404) {
|
||||
throw new SavedObjectNotFound('data view', id, 'management/kibana/dataViews');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.meta.outcome === 'conflict') {
|
||||
throw new DataViewSavedObjectConflictError(id);
|
||||
}
|
||||
|
||||
return response.item;
|
||||
}
|
||||
|
||||
async getSavedSearch(id: string) {
|
||||
const response = await this.savedObjectClient.resolve('search', id);
|
||||
|
||||
if (response.outcome === 'conflict') {
|
||||
throw new DataViewSavedObjectConflictError(id);
|
||||
}
|
||||
return simpleSavedObjectToSavedObject<T>(response.saved_object);
|
||||
return response.saved_object;
|
||||
}
|
||||
|
||||
async update(
|
||||
type: string,
|
||||
id: string,
|
||||
attributes: DataViewAttributes,
|
||||
options: SavedObjectsUpdateOptions<unknown>
|
||||
options: DataViewCrudTypes['UpdateOptions']
|
||||
) {
|
||||
const response = await this.savedObjectClient.update(type, id, attributes, options);
|
||||
return simpleSavedObjectToSavedObject(response);
|
||||
const response = await this.contentManagementClient.update<
|
||||
DataViewCrudTypes['UpdateIn'],
|
||||
DataViewCrudTypes['UpdateOut']
|
||||
>({
|
||||
contentTypeId: DataViewSOType,
|
||||
id,
|
||||
data: attributes,
|
||||
options,
|
||||
});
|
||||
|
||||
// cast is necessary since its the full object and not just the changes
|
||||
return response.item as SavedObject<DataViewAttributes>;
|
||||
}
|
||||
|
||||
async create(type: string, attributes: DataViewAttributes, options?: SavedObjectsCreateOptions) {
|
||||
const response = await this.savedObjectClient.create(type, attributes, options);
|
||||
return simpleSavedObjectToSavedObject(response);
|
||||
async create(attributes: DataViewAttributes, options: DataViewCrudTypes['CreateOptions']) {
|
||||
const result = await this.contentManagementClient.create<
|
||||
DataViewCrudTypes['CreateIn'],
|
||||
DataViewCrudTypes['CreateOut']
|
||||
>({
|
||||
contentTypeId: DataViewSOType,
|
||||
data: attributes,
|
||||
options,
|
||||
});
|
||||
|
||||
return result.item;
|
||||
}
|
||||
|
||||
delete(type: string, id: string) {
|
||||
return this.savedObjectClient.delete(type, id, { force: true });
|
||||
async delete(id: string) {
|
||||
await this.contentManagementClient.delete<
|
||||
DataViewCrudTypes['DeleteIn'],
|
||||
DataViewCrudTypes['DeleteOut']
|
||||
>({
|
||||
contentTypeId: DataViewSOType,
|
||||
id,
|
||||
options: { force: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
import { ExpressionsSetup } from '@kbn/expressions-plugin/public';
|
||||
import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import type {
|
||||
ContentManagementPublicSetup,
|
||||
ContentManagementPublicStart,
|
||||
} from '@kbn/content-management-plugin/public';
|
||||
import { DataViewsServicePublicMethods } from './data_views';
|
||||
import { HasDataService } from '../common';
|
||||
|
||||
|
@ -82,6 +86,10 @@ export interface DataViewsPublicSetupDependencies {
|
|||
* Field formats
|
||||
*/
|
||||
fieldFormats: FieldFormatsSetup;
|
||||
/**
|
||||
* Content management
|
||||
*/
|
||||
contentManagement: ContentManagementPublicSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,6 +100,10 @@ export interface DataViewsPublicStartDependencies {
|
|||
* Field formats
|
||||
*/
|
||||
fieldFormats: FieldFormatsStart;
|
||||
/**
|
||||
* Content management
|
||||
*/
|
||||
contentManagement: ContentManagementPublicStart;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SOContentStorage } from '@kbn/content-management-utils';
|
||||
|
||||
import type { DataViewCrudTypes } from '../../common/content_management';
|
||||
import { DataViewSOType } from '../../common/content_management';
|
||||
import { cmServicesDefinition } from '../../common/content_management/cm_services';
|
||||
|
||||
export class DataViewsStorage extends SOContentStorage<DataViewCrudTypes> {
|
||||
constructor() {
|
||||
super({
|
||||
savedObjectType: DataViewSOType,
|
||||
cmServicesDefinition,
|
||||
enableMSearch: true,
|
||||
allowedSavedObjectAttributes: [
|
||||
'fields',
|
||||
'title',
|
||||
'type',
|
||||
'typeMeta',
|
||||
'timeFieldName',
|
||||
'sourceFilters',
|
||||
'fieldFormatMap',
|
||||
'fieldAttrs',
|
||||
'runtimeFieldMap',
|
||||
'allowNoIndex',
|
||||
'name',
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { DataViewsStorage } from './data_views_storage';
|
|
@ -14,12 +14,14 @@ import { capabilitiesProvider } from './capabilities_provider';
|
|||
import { getIndexPatternLoad } from './expressions';
|
||||
import { registerIndexPatternsUsageCollector } from './register_index_pattern_usage_collection';
|
||||
import { createScriptedFieldsDeprecationsConfig } from './deprecations';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE, LATEST_VERSION } from '../common';
|
||||
import {
|
||||
DataViewsServerPluginSetup,
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginSetupDependencies,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
} from './types';
|
||||
import { DataViewsStorage } from './content_management';
|
||||
|
||||
export class DataViewsServerPlugin
|
||||
implements
|
||||
|
@ -38,7 +40,7 @@ export class DataViewsServerPlugin
|
|||
|
||||
public setup(
|
||||
core: CoreSetup<DataViewsServerPluginStartDependencies, DataViewsServerPluginStart>,
|
||||
{ expressions, usageCollection }: DataViewsServerPluginSetupDependencies
|
||||
{ expressions, usageCollection, contentManagement }: DataViewsServerPluginSetupDependencies
|
||||
) {
|
||||
core.savedObjects.registerType(dataViewSavedObjectType);
|
||||
core.capabilities.registerProvider(capabilitiesProvider);
|
||||
|
@ -50,6 +52,14 @@ export class DataViewsServerPlugin
|
|||
registerIndexPatternsUsageCollector(core.getStartServices, usageCollection);
|
||||
core.deprecations.registerDeprecations(createScriptedFieldsDeprecationsConfig(core));
|
||||
|
||||
contentManagement.register({
|
||||
id: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
storage: new DataViewsStorage(),
|
||||
version: {
|
||||
latest: LATEST_VERSION,
|
||||
},
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,11 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { DataViewSpec } from '../../common/types';
|
||||
import { DataViewsService } from '../../common/data_views';
|
||||
import { handleErrors } from './util/handle_errors';
|
||||
import { fieldSpecSchema, runtimeFieldSchema, serializedFieldFormatSchema } from './util/schemas';
|
||||
import {
|
||||
fieldSpecSchema,
|
||||
runtimeFieldSchema,
|
||||
serializedFieldFormatSchema,
|
||||
} from '../../common/schemas';
|
||||
import type { DataViewsServerPluginStartDependencies, DataViewsServerPluginStart } from '../types';
|
||||
import {
|
||||
DATA_VIEW_PATH,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import { DataViewsService } from '../../../common';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { serializedFieldFormatSchema } from '../util/schemas';
|
||||
import { serializedFieldFormatSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStartDependencies,
|
||||
DataViewsServerPluginStart,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { DataViewsService } from '../../../common/data_views';
|
||||
import { RuntimeField } from '../../../common/types';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { runtimeFieldSchema } from '../util/schemas';
|
||||
import { runtimeFieldSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { DataViewsService } from '../../../common/data_views';
|
||||
import { RuntimeField } from '../../../common/types';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { runtimeFieldSchema } from '../util/schemas';
|
||||
import { runtimeFieldSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -13,7 +13,7 @@ import { DataViewsService } from '../../../common/data_views';
|
|||
import { RuntimeField } from '../../../common/types';
|
||||
import { ErrorIndexPatternFieldNotFound } from '../../error';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { runtimeFieldSchema } from '../util/schemas';
|
||||
import { runtimeFieldSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { fieldSpecSchema } from '../util/schemas';
|
||||
import { fieldSpecSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { fieldSpecSchema } from '../util/schemas';
|
||||
import { fieldSpecSchema } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { FieldSpec } from '../../../common';
|
||||
import { ErrorIndexPatternFieldNotFound } from '../../error';
|
||||
import { handleErrors } from '../util/handle_errors';
|
||||
import { fieldSpecSchemaFields } from '../util/schemas';
|
||||
import { fieldSpecSchemaFields } from '../../../common/schemas';
|
||||
import type {
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -12,7 +12,11 @@ import { IRouter, StartServicesAccessor } from '@kbn/core/server';
|
|||
import { DataViewsService, DataView } from '../../common/data_views';
|
||||
import { DataViewSpec } from '../../common/types';
|
||||
import { handleErrors } from './util/handle_errors';
|
||||
import { fieldSpecSchema, runtimeFieldSchema, serializedFieldFormatSchema } from './util/schemas';
|
||||
import {
|
||||
fieldSpecSchema,
|
||||
runtimeFieldSchema,
|
||||
serializedFieldFormatSchema,
|
||||
} from '../../common/schemas';
|
||||
import type { DataViewsServerPluginStartDependencies, DataViewsServerPluginStart } from '../types';
|
||||
import {
|
||||
SPECIFIC_DATA_VIEW_PATH,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Boom from '@hapi/boom';
|
||||
import type { RequestHandler, RouteMethod, RequestHandlerContext } from '@kbn/core/server';
|
||||
import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common';
|
||||
import { ErrorIndexPatternNotFound } from '../../error';
|
||||
|
||||
interface ErrorResponseBody {
|
||||
|
@ -49,7 +50,8 @@ export const handleErrors =
|
|||
|
||||
const is404 =
|
||||
(error as ErrorIndexPatternNotFound).is404 ||
|
||||
(error as Boom.Boom)?.output?.statusCode === 404;
|
||||
(error as Boom.Boom)?.output?.statusCode === 404 ||
|
||||
error instanceof SavedObjectNotFound;
|
||||
|
||||
if (is404) {
|
||||
return response.notFound({
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
.fn()
|
||||
.mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
const result = await service.get('index-pattern', '1');
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
||||
|
@ -34,7 +34,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
.fn()
|
||||
.mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
const result = await service.get('index-pattern', '1');
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
||||
|
@ -48,8 +48,6 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
.mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
|
||||
await expect(service.get('index-pattern', '1')).rejects.toThrow(
|
||||
DataViewSavedObjectConflictError
|
||||
);
|
||||
await expect(service.get('1')).rejects.toThrow(DataViewSavedObjectConflictError);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,30 +14,50 @@ import {
|
|||
} from '../common/types';
|
||||
import { DataViewSavedObjectConflictError } from '../common/errors';
|
||||
|
||||
import type { DataViewCrudTypes } from '../common/content_management';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../common';
|
||||
|
||||
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);
|
||||
const result = await this.savedObjectClient.find<T>({
|
||||
...options,
|
||||
type: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
});
|
||||
return result.saved_objects;
|
||||
}
|
||||
|
||||
async get<T = unknown>(type: string, id: string) {
|
||||
const response = await this.savedObjectClient.resolve<T>(type, id);
|
||||
async get<T = unknown>(id: string) {
|
||||
const response = await this.savedObjectClient.resolve<T>('index-pattern', id);
|
||||
if (response.outcome === 'conflict') {
|
||||
throw new DataViewSavedObjectConflictError(id);
|
||||
}
|
||||
return response.saved_object;
|
||||
}
|
||||
async update(type: string, id: string, attributes: DataViewAttributes, options: {}) {
|
||||
return (await this.savedObjectClient.update(type, id, attributes, options)) as SavedObject;
|
||||
|
||||
async getSavedSearch<T = unknown>(id: string) {
|
||||
const response = await this.savedObjectClient.resolve<T>('search', id);
|
||||
if (response.outcome === 'conflict') {
|
||||
throw new DataViewSavedObjectConflictError(id);
|
||||
}
|
||||
return response.saved_object;
|
||||
}
|
||||
async create(type: string, attributes: DataViewAttributes, options: {}) {
|
||||
return await this.savedObjectClient.create(type, attributes, options);
|
||||
|
||||
async update(id: string, attributes: DataViewAttributes, options: {}) {
|
||||
return (await this.savedObjectClient.update(
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
id,
|
||||
attributes,
|
||||
options
|
||||
)) as SavedObject;
|
||||
}
|
||||
delete(type: string, id: string) {
|
||||
return this.savedObjectClient.delete(type, id, { force: true });
|
||||
async create(attributes: DataViewAttributes, options: DataViewCrudTypes['CreateOptions']) {
|
||||
return await this.savedObjectClient.create(DATA_VIEW_SAVED_OBJECT_TYPE, attributes, options);
|
||||
}
|
||||
async delete(id: string) {
|
||||
await this.savedObjectClient.delete(DATA_VIEW_SAVED_OBJECT_TYPE, id, { force: true });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
||||
import type { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
|
||||
import { DataViewsService } from '../common';
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,10 @@ export interface DataViewsServerPluginSetupDependencies {
|
|||
* Usage collection
|
||||
*/
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
/**
|
||||
* Content management
|
||||
*/
|
||||
contentManagement: ContentManagementServerSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,7 +16,7 @@ export const getFieldByName = (
|
|||
fieldName: string,
|
||||
indexPattern: SavedObject<DataViewAttributes>
|
||||
): FieldSpec | undefined => {
|
||||
const fields: FieldSpec[] = indexPattern && JSON.parse(indexPattern.attributes.fields);
|
||||
const fields: FieldSpec[] = indexPattern && JSON.parse(indexPattern.attributes?.fields || '[]');
|
||||
const field = fields && fields.find((f) => f.name === fieldName);
|
||||
|
||||
return field;
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
"@kbn/utility-types-jest",
|
||||
"@kbn/safer-lodash-set",
|
||||
"@kbn/core-http-server",
|
||||
"@kbn/content-management-plugin",
|
||||
"@kbn/content-management-utils",
|
||||
"@kbn/object-versioning",
|
||||
"@kbn/core-saved-objects-server",
|
||||
],
|
||||
"exclude": [
|
||||
|
|
|
@ -33,6 +33,13 @@ export class MapsStorage extends SOContentStorage<MapCrudTypes> {
|
|||
cmServicesDefinition,
|
||||
searchArgsToSOFindOptions,
|
||||
enableMSearch: true,
|
||||
allowedSavedObjectAttributes: [
|
||||
'title',
|
||||
'description',
|
||||
'mapStateJSON',
|
||||
'layerListJSON',
|
||||
'uiStateJSON',
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue