[Content Management] Maps onboard (#153304)

This commit is contained in:
Sébastien Loix 2023-04-13 10:32:39 +01:00 committed by GitHub
parent 045b58fe5b
commit 42a893db40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 1094 additions and 300 deletions

View file

@ -64,52 +64,52 @@ export interface ServicesDefinition {
export interface ServiceTransforms { export interface ServiceTransforms {
get: { get: {
in: { in: {
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
bulkGet: { bulkGet: {
in: { in: {
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
create: { create: {
in: { in: {
data: ObjectTransforms; data: ObjectTransforms<any, any, any, any>;
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
update: { update: {
in: { in: {
data: ObjectTransforms; data: ObjectTransforms<any, any, any, any>;
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
delete: { delete: {
in: { in: {
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
search: { search: {
in: { in: {
options: ObjectTransforms; options: ObjectTransforms<any, any, any, any>;
}; };
out: { out: {
result: ObjectTransforms; result: ObjectTransforms<any, any, any, any>;
}; };
}; };
} }

View file

@ -9,12 +9,7 @@
import { schema } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema';
import { initTransform } from './object_transform'; import { initTransform } from './object_transform';
import type { import type { ObjectMigrationDefinition, Version, VersionableObject } from './types';
ObjectMigrationDefinition,
ObjectTransforms,
Version,
VersionableObject,
} from './types';
interface FooV1 { interface FooV1 {
fullName: string; fullName: string;
@ -58,7 +53,7 @@ const fooMigrationDef: ObjectMigrationDefinition = {
const setup = <UpIn = unknown, UpOut = unknown, DownIn = unknown, DownOut = unknown>( const setup = <UpIn = unknown, UpOut = unknown, DownIn = unknown, DownOut = unknown>(
browserVersion: Version browserVersion: Version
): ObjectTransforms<UpIn, UpOut, DownIn, DownOut> => { ) => {
const transformsFactory = initTransform<UpIn, UpOut, DownIn, DownOut>(browserVersion); const transformsFactory = initTransform<UpIn, UpOut, DownIn, DownOut>(browserVersion);
return transformsFactory(fooMigrationDef); return transformsFactory(fooMigrationDef);
}; };

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { ObjectMigrationDefinition, ObjectTransform, ObjectTransforms, Version } from './types'; import { ObjectMigrationDefinition, ObjectTransform, Version } from './types';
import { validateObj, validateVersion } from './utils'; import { validateObj, validateVersion } from './utils';
/** /**
@ -97,11 +97,8 @@ const getTransformFns = <I = unknown, O = unknown>(
*/ */
export const initTransform = export const initTransform =
<UpIn = unknown, UpOut = unknown, DownIn = unknown, DownOut = unknown>(requestVersion: Version) => <UpIn = unknown, UpOut = unknown, DownIn = unknown, DownOut = unknown>(requestVersion: Version) =>
( (migrationDefinition: ObjectMigrationDefinition) => {
migrationDefinition: ObjectMigrationDefinition
): ObjectTransforms<UpIn, UpOut, DownIn, DownOut> => {
const { latestVersion } = getVersionsMeta(migrationDefinition); const { latestVersion } = getVersionsMeta(migrationDefinition);
const getVersion = (v: Version | 'latest'): Version => (v === 'latest' ? latestVersion : v); const getVersion = (v: Version | 'latest'): Version => (v === 'latest' ? latestVersion : v);
const validateFn = (value: unknown, version: number = requestVersion) => { const validateFn = (value: unknown, version: number = requestVersion) => {
@ -120,7 +117,11 @@ export const initTransform =
}; };
return { return {
up: (obj, to = 'latest', { validate = true }: { validate?: boolean } = {}) => { up: <I = UpIn, O = UpOut>(
obj: I,
to: number | 'latest' = 'latest',
{ validate = true }: { validate?: boolean } = {}
) => {
try { try {
if (!migrationDefinition[requestVersion]) { if (!migrationDefinition[requestVersion]) {
return { return {
@ -145,16 +146,12 @@ export const initTransform =
}; };
} }
const fns = getTransformFns<UpIn, UpOut>( const fns = getTransformFns<I, O>(requestVersion, targetVersion, migrationDefinition);
requestVersion,
targetVersion,
migrationDefinition
);
const value = fns.reduce((acc, fn) => { const value = fns.reduce((acc, fn) => {
const res = fn(acc as unknown as UpIn); const res = fn(acc as unknown as I);
return res; return res;
}, obj as unknown as UpOut); }, obj as unknown as O);
return { value, error: null }; return { value, error: null };
} catch (e) { } catch (e) {
@ -164,7 +161,11 @@ export const initTransform =
}; };
} }
}, },
down: (obj, from = 'latest', { validate = true }: { validate?: boolean } = {}) => { down: <I = DownIn, O = DownOut>(
obj: I,
from: number | 'latest' = 'latest',
{ validate = true }: { validate?: boolean } = {}
) => {
try { try {
if (!migrationDefinition[requestVersion]) { if (!migrationDefinition[requestVersion]) {
return { return {
@ -189,16 +190,12 @@ export const initTransform =
} }
} }
const fns = getTransformFns<DownIn, DownOut>( const fns = getTransformFns<I, O>(fromVersion, requestVersion, migrationDefinition);
fromVersion,
requestVersion,
migrationDefinition
);
const value = fns.reduce((acc, fn) => { const value = fns.reduce((acc, fn) => {
const res = fn(acc as unknown as DownIn); const res = fn(acc as unknown as I);
return res; return res;
}, obj as unknown as DownOut); }, obj as unknown as O);
return { value: value as any, error: null }; return { value: value as any, error: null };
} catch (e) { } catch (e) {

View file

@ -42,21 +42,21 @@ export interface ObjectTransforms<
DownIn = unknown, DownIn = unknown,
DownOut = unknown DownOut = unknown
> { > {
up: ( up: <I = UpIn, O = UpOut>(
obj: UpIn, obj: I,
version?: Version | 'latest', version?: Version | 'latest',
options?: { options?: {
/** Validate the object _before_ up transform */ /** Validate the object _before_ up transform */
validate?: boolean; validate?: boolean;
} }
) => TransformReturn<UpOut>; ) => TransformReturn<O>;
down: ( down: <I = DownIn, O = DownOut>(
obj: DownIn, obj: DownIn,
version?: Version | 'latest', version?: Version | 'latest',
options?: { options?: {
/** Validate the object _before_ down transform */ /** Validate the object _before_ down transform */
validate?: boolean; validate?: boolean;
} }
) => TransformReturn<DownOut>; ) => TransformReturn<O>;
validate: (obj: any, version?: Version) => ValidationError | null; validate: (obj: any, version?: Version) => ValidationError | null;
} }

View file

@ -34,11 +34,11 @@ export class RpcClient implements CrudClient {
constructor(private http: { post: HttpSetup['post'] }) {} constructor(private http: { post: HttpSetup['post'] }) {}
public get<I extends GetIn = GetIn, O = unknown, M = unknown>(input: I) { public get<I extends GetIn = GetIn, O = unknown, M = unknown>(input: I) {
return this.sendMessage<GetResponse<O, M>>('get', input).then((r) => r.item); return this.sendMessage<GetResponse<O, M>>('get', input).then((r) => r.result);
} }
public bulkGet<I extends BulkGetIn = BulkGetIn, O = unknown, M = unknown>(input: I) { public bulkGet<I extends BulkGetIn = BulkGetIn, O = unknown, M = unknown>(input: I) {
return this.sendMessage<BulkGetResponse<O, M>>('bulkGet', input).then((r) => r.items); return this.sendMessage<BulkGetResponse<O, M>>('bulkGet', input).then((r) => r.result);
} }
public create<I extends CreateIn = CreateIn, O = unknown, M = unknown>(input: I) { public create<I extends CreateIn = CreateIn, O = unknown, M = unknown>(input: I) {

View file

@ -165,7 +165,7 @@ describe('Content Core', () => {
const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true }); const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true });
const res = await fooContentCrud!.get(ctx, '1'); const res = await fooContentCrud!.get(ctx, '1');
expect(res.item.item).toBeUndefined(); expect(res.result.item).toBeUndefined();
cleanUp(); cleanUp();
}); });
@ -176,7 +176,7 @@ describe('Content Core', () => {
const res = await fooContentCrud!.get(ctx, '1', { forwardInResponse: { foo: 'bar' } }); const res = await fooContentCrud!.get(ctx, '1', { forwardInResponse: { foo: 'bar' } });
expect(res).toEqual({ expect(res).toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: { item: {
// Options forwared in response // Options forwared in response
options: { foo: 'bar' }, options: { foo: 'bar' },
@ -191,7 +191,7 @@ describe('Content Core', () => {
const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true }); const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true });
const res = await fooContentCrud!.bulkGet(ctx, ['1', '2']); const res = await fooContentCrud!.bulkGet(ctx, ['1', '2']);
expect(res.items).toEqual({ expect(res.result).toEqual({
hits: [{ item: undefined }, { item: undefined }], hits: [{ item: undefined }, { item: undefined }],
}); });
@ -207,7 +207,7 @@ describe('Content Core', () => {
expect(res).toEqual({ expect(res).toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
items: { result: {
hits: [ hits: [
{ {
item: { item: {
@ -230,7 +230,7 @@ describe('Content Core', () => {
const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true }); const { fooContentCrud, ctx, cleanUp } = setup({ registerFooType: true });
const res = await fooContentCrud!.get(ctx, '1234'); const res = await fooContentCrud!.get(ctx, '1234');
expect(res.item.item).toBeUndefined(); expect(res.result.item).toBeUndefined();
await fooContentCrud!.create( await fooContentCrud!.create(
ctx, ctx,
{ title: 'Hello' }, { title: 'Hello' },
@ -238,7 +238,7 @@ describe('Content Core', () => {
); );
expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({ expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: { item: {
id: '1234', id: '1234',
title: 'Hello', title: 'Hello',
@ -256,7 +256,7 @@ describe('Content Core', () => {
await fooContentCrud!.update(ctx, '1234', { title: 'changed' }); await fooContentCrud!.update(ctx, '1234', { title: 'changed' });
expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({ expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: { item: {
id: '1234', id: '1234',
title: 'changed', title: 'changed',
@ -292,7 +292,7 @@ describe('Content Core', () => {
expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({ expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: { item: {
id: '1234', id: '1234',
title: 'changed', title: 'changed',
@ -309,14 +309,14 @@ describe('Content Core', () => {
await fooContentCrud!.create(ctx, { title: 'Hello' }, { id: '1234' }); await fooContentCrud!.create(ctx, { title: 'Hello' }, { id: '1234' });
expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({ expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: expect.any(Object), item: expect.any(Object),
}, },
}); });
await fooContentCrud!.delete(ctx, '1234'); await fooContentCrud!.delete(ctx, '1234');
expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({ expect(fooContentCrud!.get(ctx, '1234')).resolves.toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: { result: {
item: undefined, item: undefined,
}, },
}); });

View file

@ -19,12 +19,12 @@ import type { ContentStorage, StorageContext } from './types';
export interface GetResponse<T = unknown, M = void> { export interface GetResponse<T = unknown, M = void> {
contentTypeId: string; contentTypeId: string;
item: GetResult<T, M>; result: GetResult<T, M>;
} }
export interface BulkGetResponse<T = unknown, M = void> { export interface BulkGetResponse<T = unknown, M = void> {
contentTypeId: string; contentTypeId: string;
items: BulkGetResult<T, M>; result: BulkGetResult<T, M>;
} }
export interface CreateItemResponse<T = unknown, M = void> { export interface CreateItemResponse<T = unknown, M = void> {
@ -89,7 +89,7 @@ export class ContentCrud<T = unknown> {
options, options,
}); });
return { contentTypeId: this.contentTypeId, item }; return { contentTypeId: this.contentTypeId, result: item };
} catch (e) { } catch (e) {
this.eventBus.emit({ this.eventBus.emit({
type: 'getItemError', type: 'getItemError',
@ -128,7 +128,7 @@ export class ContentCrud<T = unknown> {
return { return {
contentTypeId: this.contentTypeId, contentTypeId: this.contentTypeId,
items, result: items,
}; };
} catch (e) { } catch (e) {
this.eventBus.emit({ this.eventBus.emit({

View file

@ -206,7 +206,7 @@ describe('RPC -> bulkGet()', () => {
expect(result).toEqual({ expect(result).toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
items: expected, result: expected,
}); });
expect(storage.bulkGet).toHaveBeenCalledWith( expect(storage.bulkGet).toHaveBeenCalledWith(

View file

@ -161,7 +161,7 @@ describe('RPC -> get()', () => {
expect(result).toEqual({ expect(result).toEqual({
contentTypeId: FOO_CONTENT_ID, contentTypeId: FOO_CONTENT_ID,
item: expected, result: expected,
}); });
expect(storage.get).toHaveBeenCalledWith( expect(storage.get).toHaveBeenCalledWith(

View file

@ -7,15 +7,16 @@
*/ */
import { omit } from 'lodash'; import { omit } from 'lodash';
import { schema } from '@kbn/config-schema';
import { ContentManagementServiceDefinitionVersioned } from '@kbn/object-versioning';
import type { SearchQuery } from '../../../common';
import { validate } from '../../utils'; import { validate } from '../../utils';
import { ContentRegistry } from '../../core/registry'; import { ContentRegistry } from '../../core/registry';
import { createMockedStorage } from '../../core/mocks'; import { createMockedStorage } from '../../core/mocks';
import { EventBus } from '../../core/event_bus'; import { EventBus } from '../../core/event_bus';
import { search } from './search';
import { ContentManagementServiceDefinitionVersioned } from '@kbn/object-versioning';
import { schema } from '@kbn/config-schema';
import { getServiceObjectTransformFactory } from '../services_transforms_factory'; import { getServiceObjectTransformFactory } from '../services_transforms_factory';
import { search } from './search';
const { fn, schemas } = search; const { fn, schemas } = search;
@ -34,7 +35,12 @@ const FOO_CONTENT_ID = 'foo';
describe('RPC -> search()', () => { describe('RPC -> search()', () => {
describe('Input/Output validation', () => { describe('Input/Output validation', () => {
const query = { text: 'hello' }; const query: SearchQuery = {
text: 'hello',
tags: { included: ['abc'], excluded: ['def'] },
cursor: '1',
limit: 50,
};
const validInput = { contentTypeId: 'foo', version: 1, query }; const validInput = { contentTypeId: 'foo', version: 1, query };
test('should validate that a contentTypeId and "query" object is passed', () => { test('should validate that a contentTypeId and "query" object is passed', () => {
@ -63,6 +69,10 @@ describe('RPC -> search()', () => {
input: { ...validInput, query: 123 }, // query is not an object input: { ...validInput, query: 123 }, // query is not an object
expectedError: '[query]: expected a plain object value, but found [number] instead.', expectedError: '[query]: expected a plain object value, but found [number] instead.',
}, },
{
input: { ...validInput, query: { tags: { included: 123 } } }, // invalid query
expectedError: '[query.tags.included]: expected value of type [array] but got [number]',
},
{ {
input: { ...validInput, unknown: 'foo' }, input: { ...validInput, unknown: 'foo' },
expectedError: '[unknown]: definition for this key is missing', expectedError: '[unknown]: definition for this key is missing',

View file

@ -30,7 +30,7 @@ export interface VisualizationListItem {
export interface VisualizationsAppExtension { export interface VisualizationsAppExtension {
docTypes: string[]; docTypes: string[];
searchFields?: string[]; searchFields?: string[];
toListItem: (savedObject: SimpleSavedObject) => VisualizationListItem; toListItem: (savedObject: SimpleSavedObject<any>) => VisualizationListItem;
} }
export interface VisTypeAlias { export interface VisTypeAlias {

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
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,
};

View file

@ -5,12 +5,6 @@
* 2.0. * 2.0.
*/ */
/* eslint-disable @typescript-eslint/consistent-type-definitions */ export const LATEST_VERSION = 1;
export type MapSavedObjectAttributes = { export const CONTENT_ID = 'map';
title: string;
description?: string;
mapStateJSON?: string;
layerListJSON?: string;
uiStateJSON?: string;
};

View file

@ -0,0 +1,34 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { LATEST_VERSION, CONTENT_ID } from './constants';
export type { MapContentType } from './types';
export type {
MapAttributes,
MapItem,
PartialMapItem,
MapGetIn,
MapGetOut,
MapCreateIn,
MapCreateOut,
CreateOptions,
MapUpdateIn,
MapUpdateOut,
UpdateOptions,
MapDeleteIn,
MapDeleteOut,
MapSearchIn,
MapSearchOptions,
MapSearchOut,
} from './latest';
// Today "v1" === "latest" so the export under MapV1 namespace is not really useful
// We leave it as a reference for future version when it will be needed to export/support older types
// in the UIs.
export * as MapV1 from './v1';

View file

@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// Latest version is 1
export * from './v1';

View file

@ -0,0 +1,8 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export type MapContentType = 'map';

View file

@ -0,0 +1,137 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import type { ContentManagementServicesDefinition as ServicesDefinition } from '@kbn/object-versioning';
const apiError = schema.object({
error: schema.string(),
message: schema.string(),
statusCode: schema.number(),
metadata: schema.object({}, { unknowns: 'allow' }),
});
const referenceSchema = schema.object(
{
name: schema.maybe(schema.string()),
type: schema.string(),
id: schema.string(),
},
{ unknowns: 'forbid' }
);
const referencesSchema = schema.arrayOf(referenceSchema);
const mapAttributesSchema = schema.object(
{
title: schema.string(),
description: schema.maybe(schema.string()),
mapStateJSON: schema.maybe(schema.string()),
layerListJSON: schema.maybe(schema.string()),
uiStateJSON: schema.maybe(schema.string()),
},
{ unknowns: 'forbid' }
);
const mapSavedObjectSchema = schema.object(
{
id: schema.string(),
type: schema.string(),
version: schema.maybe(schema.string()),
createdAt: schema.maybe(schema.string()),
updatedAt: schema.maybe(schema.string()),
error: schema.maybe(apiError),
attributes: mapAttributesSchema,
references: referencesSchema,
namespaces: schema.maybe(schema.arrayOf(schema.string())),
originId: schema.maybe(schema.string()),
},
{ unknowns: 'allow' }
);
const getResultSchema = schema.object(
{
item: mapSavedObjectSchema,
meta: schema.object(
{
outcome: schema.oneOf([
schema.literal('exactMatch'),
schema.literal('aliasMatch'),
schema.literal('conflict'),
]),
aliasTargetId: schema.maybe(schema.string()),
aliasPurpose: schema.maybe(
schema.oneOf([
schema.literal('savedObjectConversion'),
schema.literal('savedObjectImport'),
])
),
},
{ unknowns: 'forbid' }
),
},
{ unknowns: 'forbid' }
);
const createOptionsSchema = schema.object({
references: schema.maybe(referencesSchema),
});
// Content management service definition.
// We need it for BWC support between different versions of the content
export const serviceDefinition: ServicesDefinition = {
get: {
out: {
result: {
schema: getResultSchema,
},
},
},
create: {
in: {
options: {
schema: createOptionsSchema,
},
data: {
schema: mapAttributesSchema,
},
},
out: {
result: {
schema: schema.object(
{
item: mapSavedObjectSchema,
},
{ unknowns: 'forbid' }
),
},
},
},
update: {
in: {
options: {
schema: createOptionsSchema, // same schema as "create"
},
data: {
schema: mapAttributesSchema,
},
},
},
search: {
in: {
options: {
schema: schema.maybe(
schema.object(
{
onlyTitle: schema.maybe(schema.boolean()),
},
{ unknowns: 'forbid' }
)
),
},
},
},
};

View file

@ -0,0 +1,25 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export type {
MapAttributes,
MapItem,
PartialMapItem,
MapGetIn,
MapGetOut,
MapCreateIn,
MapCreateOut,
CreateOptions,
MapUpdateIn,
MapUpdateOut,
UpdateOptions,
MapDeleteIn,
MapDeleteOut,
MapSearchIn,
MapSearchOptions,
MapSearchOut,
} from './types';

View file

@ -0,0 +1,110 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type {
GetIn,
GetResult,
CreateIn,
CreateResult,
SearchIn,
SearchResult,
UpdateIn,
UpdateResult,
DeleteIn,
DeleteResult,
} from '@kbn/content-management-plugin/common';
import { MapContentType } from '../types';
interface Reference {
type: string;
id: string;
name: string;
}
/* eslint-disable-next-line @typescript-eslint/consistent-type-definitions */
export type MapAttributes = {
title: string;
description?: string;
mapStateJSON?: string;
layerListJSON?: string;
uiStateJSON?: string;
};
export interface MapItem {
id: string;
type: string;
version?: string;
createdAt?: string;
updatedAt?: string;
error?: {
error: string;
message: string;
statusCode: number;
metadata?: Record<string, unknown>;
};
attributes: MapAttributes;
references: Reference[];
namespaces?: string[];
originId?: string;
}
export type PartialMapItem = Omit<MapItem, 'attributes' | 'references'> & {
attributes: Partial<MapAttributes>;
references: Reference[] | undefined;
};
// ----------- GET --------------
export type MapGetIn = GetIn<MapContentType>;
export type MapGetOut = GetResult<
MapItem,
{
outcome: 'exactMatch' | 'aliasMatch' | 'conflict';
aliasTargetId?: string;
aliasPurpose?: 'savedObjectConversion' | 'savedObjectImport';
}
>;
// ----------- CREATE --------------
export interface CreateOptions {
/** Array of referenced saved objects. */
references?: Reference[];
}
export type MapCreateIn = CreateIn<MapContentType, MapAttributes, CreateOptions>;
export type MapCreateOut = CreateResult<MapItem>;
// ----------- UPDATE --------------
export interface UpdateOptions {
/** Array of referenced saved objects. */
references?: Reference[];
}
export type MapUpdateIn = UpdateIn<MapContentType, MapAttributes, UpdateOptions>;
export type MapUpdateOut = UpdateResult<PartialMapItem>;
// ----------- DELETE --------------
export type MapDeleteIn = DeleteIn<MapContentType>;
export type MapDeleteOut = DeleteResult;
// ----------- SEARCH --------------
export interface MapSearchOptions {
/** Flag to indicate to only search the text on the "title" field */
onlyTitle?: boolean;
}
export type MapSearchIn = SearchIn<MapContentType, MapSearchOptions>;
export type MapSearchOut = SearchResult<MapItem>;

View file

@ -7,7 +7,7 @@
import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common'; import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import { MapEmbeddablePersistableState } from './types'; import { MapEmbeddablePersistableState } from './types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { extractReferences } from '../migrations/references'; import { extractReferences } from '../migrations/references';
export const extract: EmbeddableRegistryDefinition['extract'] = (state) => { export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
@ -21,7 +21,7 @@ export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
// by-value embeddable // by-value embeddable
const { attributes, references } = extractReferences({ const { attributes, references } = extractReferences({
attributes: typedState.attributes as MapSavedObjectAttributes, attributes: typedState.attributes as MapAttributes,
}); });
return { return {

View file

@ -7,7 +7,7 @@
import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common'; import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import type { MapEmbeddablePersistableState } from './types'; import type { MapEmbeddablePersistableState } from './types';
import type { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { extractReferences, injectReferences } from '../migrations/references'; import { extractReferences, injectReferences } from '../migrations/references';
export const inject: EmbeddableRegistryDefinition['inject'] = (state, references) => { export const inject: EmbeddableRegistryDefinition['inject'] = (state, references) => {
@ -23,7 +23,7 @@ export const inject: EmbeddableRegistryDefinition['inject'] = (state, references
// run embeddable state through extract logic to ensure any state with hard coded ids is replace with refNames // run embeddable state through extract logic to ensure any state with hard coded ids is replace with refNames
// refName generation will produce consistent values allowing inject logic to then replace refNames with current ids. // refName generation will produce consistent values allowing inject logic to then replace refNames with current ids.
const { attributes: attributesWithNoHardCodedIds } = extractReferences({ const { attributes: attributesWithNoHardCodedIds } = extractReferences({
attributes: typedState.attributes as MapSavedObjectAttributes, attributes: typedState.attributes as MapAttributes,
}); });
const { attributes: attributesWithInjectedIds } = injectReferences({ const { attributes: attributesWithInjectedIds } = injectReferences({

View file

@ -5,18 +5,14 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { JoinDescriptor, LayerDescriptor, VectorLayerDescriptor } from '../descriptor_types'; import { JoinDescriptor, LayerDescriptor, VectorLayerDescriptor } from '../descriptor_types';
import { SOURCE_TYPES } from '../constants'; import { SOURCE_TYPES } from '../constants';
// enforce type property on joins. It's possible older saved-objects do not have this correctly filled in // enforce type property on joins. It's possible older saved-objects do not have this correctly filled in
// e.g. sample-data was missing the right.type field. // e.g. sample-data was missing the right.type field.
// This is just to be safe. // This is just to be safe.
export function addTypeToTermJoin({ export function addTypeToTermJoin({ attributes }: { attributes: MapAttributes }): MapAttributes {
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -21,7 +21,7 @@ import {
LayerDescriptor, LayerDescriptor,
VectorLayerDescriptor, VectorLayerDescriptor,
} from '../descriptor_types'; } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
const GROUP_BY_DELIMITER = '_groupby_'; const GROUP_BY_DELIMITER = '_groupby_';
@ -53,11 +53,7 @@ function parseLegacyAggKey(legacyAggKey: string): { aggType: AGG_TYPE; aggFieldN
}; };
} }
export function migrateJoinAggKey({ export function migrateJoinAggKey({ attributes }: { attributes: MapAttributes }): MapAttributes {
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -7,16 +7,16 @@
import { Filter } from '@kbn/es-query'; import { Filter } from '@kbn/es-query';
import { MigrateFunction } from '@kbn/kibana-utils-plugin/common'; import { MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
export function migrateDataPersistedState( export function migrateDataPersistedState(
{ {
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}, },
filterMigration: MigrateFunction<Filter[], Filter[]> filterMigration: MigrateFunction<Filter[], Filter[]>
): MapSavedObjectAttributes { ): MapAttributes {
let mapState: { filters: Filter[] } = { filters: [] }; let mapState: { filters: Filter[] } = { filters: [] };
if (attributes.mapStateJSON) { if (attributes.mapStateJSON) {
try { try {

View file

@ -8,16 +8,16 @@
import type { Serializable } from '@kbn/utility-types'; import type { Serializable } from '@kbn/utility-types';
import type { DataViewSpec } from '@kbn/data-plugin/common'; import type { DataViewSpec } from '@kbn/data-plugin/common';
import { MigrateFunction } from '@kbn/kibana-utils-plugin/common'; import { MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
export function migrateDataViewsPersistedState( export function migrateDataViewsPersistedState(
{ {
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}, },
migration: MigrateFunction<Serializable, Serializable> migration: MigrateFunction<Serializable, Serializable>
): MapSavedObjectAttributes { ): MapAttributes {
let mapState: { adHocDataViews?: DataViewSpec[] } = { adHocDataViews: [] }; let mapState: { adHocDataViews?: DataViewSpec[] } = { adHocDataViews: [] };
if (attributes.mapStateJSON) { if (attributes.mapStateJSON) {
try { try {

View file

@ -12,7 +12,7 @@ import {
LayerDescriptor, LayerDescriptor,
VectorStyleDescriptor, VectorStyleDescriptor,
} from '../descriptor_types'; } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
const COLOR_STYLES = [ const COLOR_STYLES = [
VECTOR_STYLES.FILL_COLOR, VECTOR_STYLES.FILL_COLOR,
@ -53,8 +53,8 @@ function migrateColorProperty(
export function migrateOtherCategoryColor({ export function migrateOtherCategoryColor({
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}): MapSavedObjectAttributes { }): MapAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -5,15 +5,11 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { LayerDescriptor } from '../descriptor_types'; import { LayerDescriptor } from '../descriptor_types';
// In 7.14, attribution added to the layer_descriptor. Prior to 7.14, 2 sources, WMS and TMS, had attribution on source descriptor. // In 7.14, attribution added to the layer_descriptor. Prior to 7.14, 2 sources, WMS and TMS, had attribution on source descriptor.
export function moveAttribution({ export function moveAttribution({ attributes }: { attributes: MapAttributes }): MapAttributes {
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -9,7 +9,7 @@
import type { DataViewSpec } from '@kbn/data-plugin/common'; import type { DataViewSpec } from '@kbn/data-plugin/common';
import { SavedObjectReference } from '@kbn/core/types'; import { SavedObjectReference } from '@kbn/core/types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { LayerDescriptor, VectorLayerDescriptor } from '../descriptor_types'; import { LayerDescriptor, VectorLayerDescriptor } from '../descriptor_types';
interface IndexPatternReferenceDescriptor { interface IndexPatternReferenceDescriptor {
@ -21,7 +21,7 @@ export function extractReferences({
attributes, attributes,
references = [], references = [],
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
references?: SavedObjectReference[]; references?: SavedObjectReference[];
}) { }) {
if (!attributes.layerListJSON) { if (!attributes.layerListJSON) {
@ -119,7 +119,7 @@ export function injectReferences({
attributes, attributes,
references, references,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
references: SavedObjectReference[]; references: SavedObjectReference[];
}) { }) {
if (!attributes.layerListJSON) { if (!attributes.layerListJSON) {

View file

@ -5,13 +5,13 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
export function removeBoundsFromSavedObject({ export function removeBoundsFromSavedObject({
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}): MapSavedObjectAttributes { }): MapAttributes {
const newAttributes = { ...attributes }; const newAttributes = { ...attributes };
// @ts-expect-error // @ts-expect-error
// This removes an unused parameter from pre 7.8=< saved objects // This removes an unused parameter from pre 7.8=< saved objects

View file

@ -7,18 +7,14 @@
import { LAYER_TYPE } from '../constants'; import { LAYER_TYPE } from '../constants';
import { LayerDescriptor } from '../descriptor_types'; import { LayerDescriptor } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
// LAYER_TYPE constants renamed in 8.1 to provide more distinguishable names that better refect layer. // LAYER_TYPE constants renamed in 8.1 to provide more distinguishable names that better refect layer.
// TILED_VECTOR replaced with MVT_VECTOR // TILED_VECTOR replaced with MVT_VECTOR
// VECTOR_TILE replaced with EMS_VECTOR_TILE // VECTOR_TILE replaced with EMS_VECTOR_TILE
// VECTOR replaced with GEOJSON_VECTOR // VECTOR replaced with GEOJSON_VECTOR
// TILE replaced with RASTER_TILE // TILE replaced with RASTER_TILE
export function renameLayerTypes({ export function renameLayerTypes({ attributes }: { attributes: MapAttributes }): MapAttributes {
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -8,7 +8,7 @@
import _ from 'lodash'; import _ from 'lodash';
import { SOURCE_TYPES, SCALING_TYPES } from '../constants'; import { SOURCE_TYPES, SCALING_TYPES } from '../constants';
import { LayerDescriptor, ESSearchSourceDescriptor } from '../descriptor_types'; import { LayerDescriptor, ESSearchSourceDescriptor } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
function isEsDocumentSource(layerDescriptor: LayerDescriptor) { function isEsDocumentSource(layerDescriptor: LayerDescriptor) {
const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type'); const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type');
@ -18,8 +18,8 @@ function isEsDocumentSource(layerDescriptor: LayerDescriptor) {
export function migrateUseTopHitsToScalingType({ export function migrateUseTopHitsToScalingType({
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}): MapSavedObjectAttributes { }): MapAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -5,13 +5,13 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
export function setDefaultAutoFitToBounds({ export function setDefaultAutoFitToBounds({
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}): MapSavedObjectAttributes { }): MapAttributes {
if (!attributes || !attributes.mapStateJSON) { if (!attributes || !attributes.mapStateJSON) {
return attributes; return attributes;
} }

View file

@ -7,7 +7,7 @@
import { SOURCE_TYPES } from '../constants'; import { SOURCE_TYPES } from '../constants';
import { LayerDescriptor, EMSTMSSourceDescriptor } from '../descriptor_types'; import { LayerDescriptor, EMSTMSSourceDescriptor } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
// LightModeDefault added to EMSTMSSourceDescriptor in 8.0.0 // LightModeDefault added to EMSTMSSourceDescriptor in 8.0.0
// to avoid changing auto selected light mode tiles for maps created < 8.0.0 // to avoid changing auto selected light mode tiles for maps created < 8.0.0
@ -16,8 +16,8 @@ import { MapSavedObjectAttributes } from '../map_saved_object_type';
export function setEmsTmsDefaultModes({ export function setEmsTmsDefaultModes({
attributes, attributes,
}: { }: {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
}): MapSavedObjectAttributes { }): MapAttributes {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return attributes; return attributes;
} }

View file

@ -8,7 +8,7 @@
// @ts-ignore // @ts-ignore
import mapSavedObjects from './test_resources/sample_map_saved_objects.json'; import mapSavedObjects from './test_resources/sample_map_saved_objects.json';
import { LayerStatsCollector } from './layer_stats_collector'; import { LayerStatsCollector } from './layer_stats_collector';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
const expecteds = [ const expecteds = [
{ {
@ -69,7 +69,7 @@ const expecteds = [
]; ];
const testsToRun = mapSavedObjects.map( const testsToRun = mapSavedObjects.map(
(savedObject: { attributes: MapSavedObjectAttributes }, index: number) => { (savedObject: { attributes: MapAttributes }, index: number) => {
const { attributes } = savedObject; const { attributes } = savedObject;
return [attributes, expecteds[index]] as const; return [attributes, expecteds[index]] as const;
} }

View file

@ -19,7 +19,7 @@ import {
LayerDescriptor, LayerDescriptor,
VectorLayerDescriptor, VectorLayerDescriptor,
} from '../descriptor_types'; } from '../descriptor_types';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { EMS_BASEMAP_KEYS, JOIN_KEYS, LAYER_KEYS, RESOLUTION_KEYS, SCALING_KEYS } from './types'; import { EMS_BASEMAP_KEYS, JOIN_KEYS, LAYER_KEYS, RESOLUTION_KEYS, SCALING_KEYS } from './types';
export class LayerStatsCollector { export class LayerStatsCollector {
@ -34,7 +34,7 @@ export class LayerStatsCollector {
private _layerTypeCounts: { [key: string]: number } = {}; private _layerTypeCounts: { [key: string]: number } = {};
private _sourceIds: Set<string> = new Set(); private _sourceIds: Set<string> = new Set();
constructor(attributes: MapSavedObjectAttributes) { constructor(attributes: MapAttributes) {
if (!attributes || !attributes.layerListJSON) { if (!attributes || !attributes.layerListJSON) {
return; return;
} }

View file

@ -8,7 +8,7 @@
// @ts-ignore // @ts-ignore
import mapSavedObjects from './test_resources/sample_map_saved_objects.json'; import mapSavedObjects from './test_resources/sample_map_saved_objects.json';
import { MapSettingsCollector } from './map_settings_collector'; import { MapSettingsCollector } from './map_settings_collector';
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
const expecteds = [ const expecteds = [
{ {
@ -30,7 +30,7 @@ const expecteds = [
]; ];
const testsToRun = mapSavedObjects.map( const testsToRun = mapSavedObjects.map(
(savedObject: { attributes: MapSavedObjectAttributes }, index: number) => { (savedObject: { attributes: MapAttributes }, index: number) => {
const { attributes } = savedObject; const { attributes } = savedObject;
return [attributes, expecteds[index]] as const; return [attributes, expecteds[index]] as const;
} }

View file

@ -5,13 +5,13 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../map_saved_object_type'; import type { MapAttributes } from '../content_management';
import { MapSettings } from '../descriptor_types'; import { MapSettings } from '../descriptor_types';
export class MapSettingsCollector { export class MapSettingsCollector {
private _customIconsCount: number = 0; private _customIconsCount: number = 0;
constructor(attributes: MapSavedObjectAttributes) { constructor(attributes: MapAttributes) {
if (!attributes || !attributes.mapStateJSON) { if (!attributes || !attributes.mapStateJSON) {
return; return;
} }

View file

@ -29,7 +29,8 @@
"mapsEms", "mapsEms",
"savedObjects", "savedObjects",
"share", "share",
"presentationUtil" "presentationUtil",
"contentManagement"
], ],
"optionalPlugins": [ "optionalPlugins": [
"cloud", "cloud",

View file

@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { OverlayStart } from '@kbn/core/public';
import { mapsClient } from './maps_client';
const rejectErrorMessage = i18n.translate('xpack.maps.saveDuplicateRejectedDescription', {
defaultMessage: 'Save with duplicate title confirmation was rejected',
});
interface Props {
title: string;
id?: string;
getDisplayName: () => string;
onTitleDuplicate: () => void;
lastSavedTitle: string;
copyOnSave: boolean;
isTitleDuplicateConfirmed: boolean;
}
interface Context {
overlays: OverlayStart;
}
export const checkForDuplicateTitle = async (
{
id,
title,
lastSavedTitle,
copyOnSave,
isTitleDuplicateConfirmed,
getDisplayName,
onTitleDuplicate,
}: Props,
{ overlays }: Context
) => {
if (isTitleDuplicateConfirmed) {
return true;
}
if (title === lastSavedTitle && !copyOnSave) {
return true;
}
const { hits } = await mapsClient.search(
{
text: `"${title}"`,
limit: 10,
},
{ onlyTitle: true }
);
const existing = hits.find((obj) => obj.attributes.title.toLowerCase() === title.toLowerCase());
if (!existing || existing.id === id) {
return true;
}
onTitleDuplicate();
return Promise.reject(new Error(rejectErrorMessage));
};

View file

@ -0,0 +1,10 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { mapsClient } from './maps_client';
export { checkForDuplicateTitle } from './duplicate_title_check';

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { SearchQuery } from '@kbn/content-management-plugin/common';
import type {
MapGetIn,
MapGetOut,
MapCreateIn,
MapCreateOut,
MapUpdateIn,
MapUpdateOut,
MapDeleteIn,
MapDeleteOut,
MapSearchIn,
MapSearchOut,
MapSearchOptions,
} from '../../common/content_management';
import { getContentManagement } from '../kibana_services';
const get = async (id: string) => {
return getContentManagement().client.get<MapGetIn, MapGetOut>({
contentTypeId: 'map',
id,
});
};
const create = async ({ data, options }: Omit<MapCreateIn, 'contentTypeId'>) => {
const res = await getContentManagement().client.create<MapCreateIn, MapCreateOut>({
contentTypeId: 'map',
data,
options,
});
return res;
};
const update = async ({ id, data, options }: Omit<MapUpdateIn, 'contentTypeId'>) => {
const res = await getContentManagement().client.update<MapUpdateIn, MapUpdateOut>({
contentTypeId: 'map',
id,
data,
options,
});
return res;
};
const deleteMap = async (id: string) => {
await getContentManagement().client.delete<MapDeleteIn, MapDeleteOut>({
contentTypeId: 'map',
id,
});
};
const search = async (query: SearchQuery = {}, options?: MapSearchOptions) => {
return getContentManagement().client.search<MapSearchIn, MapSearchOut>({
contentTypeId: 'map',
query,
options,
});
};
export const mapsClient = {
get,
create,
update,
delete: deleteMap,
search,
};

View file

@ -11,7 +11,7 @@ import { getControlledBy, MapEmbeddable } from './map_embeddable';
import { buildExistsFilter, disableFilter, pinFilter, toggleFilterNegated } from '@kbn/es-query'; import { buildExistsFilter, disableFilter, pinFilter, toggleFilterNegated } from '@kbn/es-query';
import type { DataViewFieldBase, DataViewBase } from '@kbn/es-query'; import type { DataViewFieldBase, DataViewBase } from '@kbn/es-query';
import { MapEmbeddableConfig, MapEmbeddableInput } from './types'; import { MapEmbeddableConfig, MapEmbeddableInput } from './types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
jest.mock('../kibana_services', () => { jest.mock('../kibana_services', () => {
return { return {
@ -66,7 +66,7 @@ jest.mock('../routes/map_page', () => {
class MockSavedMap { class MockSavedMap {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
private _store = require('../reducers/store').createMapStore(); private _store = require('../reducers/store').createMapStore();
private _attributes: MapSavedObjectAttributes = { private _attributes: MapAttributes = {
title: 'myMap', title: 'myMap',
}; };

View file

@ -15,7 +15,7 @@ import {
} from '@kbn/embeddable-plugin/public'; } from '@kbn/embeddable-plugin/public';
import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { Filter, Query, TimeRange } from '@kbn/es-query';
import { MapCenterAndZoom, MapExtent, MapSettings } from '../../common/descriptor_types'; import { MapCenterAndZoom, MapExtent, MapSettings } from '../../common/descriptor_types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
export interface MapEmbeddableConfig { export interface MapEmbeddableConfig {
editable: boolean; editable: boolean;
@ -37,7 +37,7 @@ interface MapEmbeddableState {
isMovementSynchronized?: boolean; isMovementSynchronized?: boolean;
} }
export type MapByValueInput = { export type MapByValueInput = {
attributes: MapSavedObjectAttributes; attributes: MapAttributes;
} & EmbeddableInput & } & EmbeddableInput &
MapEmbeddableState; MapEmbeddableState;
export type MapByReferenceInput = SavedObjectEmbeddableInput & MapEmbeddableState; export type MapByReferenceInput = SavedObjectEmbeddableInput & MapEmbeddableState;

View file

@ -44,7 +44,6 @@ export const getHttp = () => coreStart.http;
export const getExecutionContextService = () => coreStart.executionContext; export const getExecutionContextService = () => coreStart.executionContext;
export const getTimeFilter = () => pluginsStart.data.query.timefilter.timefilter; export const getTimeFilter = () => pluginsStart.data.query.timefilter.timefilter;
export const getToasts = () => coreStart.notifications.toasts; export const getToasts = () => coreStart.notifications.toasts;
export const getSavedObjectsClient = () => coreStart.savedObjects.client;
export const getCoreChrome = () => coreStart.chrome; export const getCoreChrome = () => coreStart.chrome;
export const getDevToolsCapabilities = () => coreStart.application.capabilities.dev_tools; export const getDevToolsCapabilities = () => coreStart.application.capabilities.dev_tools;
export const getMapsCapabilities = () => coreStart.application.capabilities.maps; export const getMapsCapabilities = () => coreStart.application.capabilities.maps;
@ -68,6 +67,7 @@ export const getSpacesApi = () => pluginsStart.spaces;
export const getTheme = () => coreStart.theme; export const getTheme = () => coreStart.theme;
export const getApplication = () => coreStart.application; export const getApplication = () => coreStart.application;
export const getUsageCollection = () => pluginsStart.usageCollection; export const getUsageCollection = () => pluginsStart.usageCollection;
export const getContentManagement = () => pluginsStart.contentManagement;
export const isScreenshotMode = () => { export const isScreenshotMode = () => {
return pluginsStart.screenshotMode ? pluginsStart.screenshotMode.isScreenshotMode() : false; return pluginsStart.screenshotMode ? pluginsStart.screenshotMode.isScreenshotMode() : false;
}; };

View file

@ -8,12 +8,13 @@
import { SavedObjectReference } from '@kbn/core/types'; import { SavedObjectReference } from '@kbn/core/types';
import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; import type { ResolvedSimpleSavedObject } from '@kbn/core/public';
import { AttributeService } from '@kbn/embeddable-plugin/public'; import { AttributeService } from '@kbn/embeddable-plugin/public';
import { checkForDuplicateTitle, OnSaveProps } from '@kbn/saved-objects-plugin/public'; import type { OnSaveProps } from '@kbn/saved-objects-plugin/public';
import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; import type { MapAttributes } from '../common/content_management';
import { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { getMapEmbeddableDisplayName } from '../common/i18n_getters'; import { getMapEmbeddableDisplayName } from '../common/i18n_getters';
import { getCoreOverlays, getEmbeddableService, getSavedObjectsClient } from './kibana_services'; import { getCoreOverlays, getEmbeddableService } from './kibana_services';
import { extractReferences, injectReferences } from '../common/migrations/references'; import { extractReferences, injectReferences } from '../common/migrations/references';
import { mapsClient, checkForDuplicateTitle } from './content_management';
import { MapByValueInput, MapByReferenceInput } from './embeddable/types'; import { MapByValueInput, MapByReferenceInput } from './embeddable/types';
export interface SharingSavedObjectProps { export interface SharingSavedObjectProps {
@ -23,7 +24,7 @@ export interface SharingSavedObjectProps {
sourceId?: string; sourceId?: string;
} }
type MapDoc = MapSavedObjectAttributes & { type MapDoc = MapAttributes & {
references?: SavedObjectReference[]; references?: SavedObjectReference[];
}; };
export interface MapUnwrapMetaInfo { export interface MapUnwrapMetaInfo {
@ -62,19 +63,12 @@ export function getMapAttributeService(): MapAttributeService {
references: savedObjectClientReferences, references: savedObjectClientReferences,
}); });
const savedObject = await (savedObjectId const {
? getSavedObjectsClient().update<MapSavedObjectAttributes>( item: { id },
MAP_SAVED_OBJECT_TYPE, } = await (savedObjectId
savedObjectId, ? mapsClient.update({ id: savedObjectId, data: updatedAttributes, options: { references } })
updatedAttributes, : mapsClient.create({ data: updatedAttributes, options: { references } }));
{ references } return { id };
)
: getSavedObjectsClient().create<MapSavedObjectAttributes>(
MAP_SAVED_OBJECT_TYPE,
updatedAttributes,
{ references }
));
return { id: savedObject.id };
}, },
unwrapMethod: async ( unwrapMethod: async (
savedObjectId: string savedObjectId: string
@ -83,14 +77,9 @@ export function getMapAttributeService(): MapAttributeService {
metaInfo: MapUnwrapMetaInfo; metaInfo: MapUnwrapMetaInfo;
}> => { }> => {
const { const {
saved_object: savedObject, item: savedObject,
outcome, meta: { outcome, aliasPurpose, aliasTargetId },
alias_target_id: aliasTargetId, } = await mapsClient.get(savedObjectId);
alias_purpose: aliasPurpose,
} = await getSavedObjectsClient().resolve<MapSavedObjectAttributes>(
MAP_SAVED_OBJECT_TYPE,
savedObjectId
);
if (savedObject.error) { if (savedObject.error) {
throw savedObject.error; throw savedObject.error;
@ -118,13 +107,11 @@ export function getMapAttributeService(): MapAttributeService {
title: props.newTitle, title: props.newTitle,
copyOnSave: false, copyOnSave: false,
lastSavedTitle: '', lastSavedTitle: '',
getEsType: () => MAP_SAVED_OBJECT_TYPE, isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed,
getDisplayName: getMapEmbeddableDisplayName, getDisplayName: getMapEmbeddableDisplayName,
onTitleDuplicate: props.onTitleDuplicate,
}, },
props.isTitleDuplicateConfirmed,
props.onTitleDuplicate,
{ {
savedObjectsClient: getSavedObjectsClient(),
overlays: getCoreOverlays(), overlays: getCoreOverlays(),
} }
); );

View file

@ -7,8 +7,7 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import type { VisualizationsSetup, VisualizationStage } from '@kbn/visualizations-plugin/public'; import type { VisualizationsSetup, VisualizationStage } from '@kbn/visualizations-plugin/public';
import type { SimpleSavedObject } from '@kbn/core/public'; import type { MapItem } from '../common/content_management';
import type { MapSavedObjectAttributes } from '../common/map_saved_object_type';
import { import {
APP_ID, APP_ID,
APP_ICON, APP_ICON,
@ -37,9 +36,8 @@ export function getMapsVisTypeAlias(visualizations: VisualizationsSetup) {
visualizations: { visualizations: {
docTypes: [MAP_SAVED_OBJECT_TYPE], docTypes: [MAP_SAVED_OBJECT_TYPE],
searchFields: ['title^3'], searchFields: ['title^3'],
toListItem(savedObject: SimpleSavedObject) { toListItem(mapItem: MapItem) {
const { id, type, updatedAt, attributes } = const { id, type, updatedAt, attributes } = mapItem;
savedObject as SimpleSavedObject<MapSavedObjectAttributes>;
const { title, description } = attributes; const { title, description } = attributes;
return { return {

View file

@ -33,7 +33,6 @@ import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plu
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/public';
import type { FileUploadPluginStart } from '@kbn/file-upload-plugin/public'; import type { FileUploadPluginStart } from '@kbn/file-upload-plugin/public';
import type { SavedObjectsStart } from '@kbn/saved-objects-plugin/public';
import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public';
import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
@ -42,6 +41,11 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { CloudSetup } from '@kbn/cloud-plugin/public';
import type { LensPublicSetup } from '@kbn/lens-plugin/public'; import type { LensPublicSetup } from '@kbn/lens-plugin/public';
import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public'; import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public';
import type {
ContentManagementPublicSetup,
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
import { import {
createRegionMapFn, createRegionMapFn,
GEOHASH_GRID, GEOHASH_GRID,
@ -80,6 +84,7 @@ import { setIsCloudEnabled, setMapAppConfig, setStartServices } from './kibana_s
import { MapInspectorView, VectorTileInspectorView } from './inspector'; import { MapInspectorView, VectorTileInspectorView } from './inspector';
import { setupLensChoroplethChart } from './lens'; import { setupLensChoroplethChart } from './lens';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
export interface MapsPluginSetupDependencies { export interface MapsPluginSetupDependencies {
cloud?: CloudSetup; cloud?: CloudSetup;
@ -94,6 +99,7 @@ export interface MapsPluginSetupDependencies {
licensing: LicensingPluginSetup; licensing: LicensingPluginSetup;
usageCollection?: UsageCollectionSetup; usageCollection?: UsageCollectionSetup;
screenshotMode?: ScreenshotModePluginSetup; screenshotMode?: ScreenshotModePluginSetup;
contentManagement: ContentManagementPublicSetup;
} }
export interface MapsPluginStartDependencies { export interface MapsPluginStartDependencies {
@ -109,13 +115,13 @@ export interface MapsPluginStartDependencies {
uiActions: UiActionsStart; uiActions: UiActionsStart;
share: SharePluginStart; share: SharePluginStart;
visualizations: VisualizationsStart; visualizations: VisualizationsStart;
savedObjects: SavedObjectsStart;
dashboard: DashboardStart; dashboard: DashboardStart;
savedObjectsTagging?: SavedObjectTaggingPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart;
presentationUtil: PresentationUtilPluginStart; presentationUtil: PresentationUtilPluginStart;
security?: SecurityPluginStart; security?: SecurityPluginStart;
spaces?: SpacesPluginStart; spaces?: SpacesPluginStart;
mapsEms: MapsEmsPluginPublicStart; mapsEms: MapsEmsPluginPublicStart;
contentManagement: ContentManagementPublicStart;
screenshotMode?: ScreenshotModePluginSetup; screenshotMode?: ScreenshotModePluginSetup;
usageCollection?: UsageCollectionSetup; usageCollection?: UsageCollectionSetup;
} }
@ -201,6 +207,14 @@ export class MapsPlugin
}, },
}); });
plugins.contentManagement.registry.register({
id: CONTENT_ID,
version: {
latest: LATEST_VERSION,
},
name: getAppTitle(),
});
setupLensChoroplethChart(core, plugins.expressions, plugins.lens); setupLensChoroplethChart(core, plugins.expressions, plugins.lens);
// register wrapper around legacy tile_map and region_map visualizations // register wrapper around legacy tile_map and region_map visualizations

View file

@ -10,9 +10,10 @@ import { i18n } from '@kbn/i18n';
import { Redirect } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public';
import { ScopedHistory } from '@kbn/core/public'; import { ScopedHistory } from '@kbn/core/public';
import { getSavedObjectsClient, getToasts } from '../../kibana_services'; import { getToasts } from '../../kibana_services';
import { MapsListView } from './maps_list_view'; import { MapsListView } from './maps_list_view';
import { APP_ID, MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; import { APP_ID } from '../../../common/constants';
import { mapsClient } from '../../content_management';
interface Props { interface Props {
history: ScopedHistory; history: ScopedHistory;
@ -38,13 +39,9 @@ export class LoadListAndRender extends Component<Props> {
async _loadMapsList() { async _loadMapsList() {
try { try {
const results = await getSavedObjectsClient().find({ const results = await mapsClient.search({ limit: 1 });
type: MAP_SAVED_OBJECT_TYPE,
perPage: 1,
fields: ['title'],
});
if (this._isMounted) { if (this._isMounted) {
this.setState({ mapsLoaded: true, hasSavedMaps: !!results.savedObjects.length }); this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length });
} }
} catch (err) { } catch (err) {
if (this._isMounted) { if (this._isMounted) {

View file

@ -5,37 +5,29 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React, { useCallback, memo } from 'react';
import { SavedObjectReference } from '@kbn/core/types';
import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public'; import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public';
import { METRIC_TYPE } from '@kbn/analytics'; import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { TableListView } from '@kbn/content-management-table-list'; import { TableListView } from '@kbn/content-management-table-list';
import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list';
import { SimpleSavedObject } from '@kbn/core-saved-objects-api-browser';
import { APP_ID, getEditPath, MAP_PATH, MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; import type { MapItem } from '../../../common/content_management';
import { APP_ID, getEditPath, MAP_PATH } from '../../../common/constants';
import { import {
getMapsCapabilities, getMapsCapabilities,
getCoreChrome, getCoreChrome,
getExecutionContextService, getExecutionContextService,
getNavigateToApp, getNavigateToApp,
getSavedObjectsClient,
getUiSettings, getUiSettings,
getUsageCollection, getUsageCollection,
} from '../../kibana_services'; } from '../../kibana_services';
import { getAppTitle } from '../../../common/i18n_getters'; import { getAppTitle } from '../../../common/i18n_getters';
import { MapSavedObjectAttributes } from '../../../common/map_saved_object_type'; import { mapsClient } from '../../content_management';
const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit';
const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage';
interface MapItem {
id: string;
title: string;
description?: string;
references?: SavedObjectReference[];
}
interface MapUserContent extends UserContentCommonSchema { interface MapUserContent extends UserContentCommonSchema {
type: string; type: string;
attributes: { attributes: {
@ -51,59 +43,26 @@ function navigateToNewMap() {
}); });
} }
const toTableListViewSavedObject = ( const toTableListViewSavedObject = (mapItem: MapItem): MapUserContent => {
savedObject: SimpleSavedObject<MapSavedObjectAttributes>
): MapUserContent => {
return { return {
...savedObject, ...mapItem,
updatedAt: savedObject.updatedAt!, updatedAt: mapItem.updatedAt!,
attributes: { attributes: {
...savedObject.attributes, ...mapItem.attributes,
title: savedObject.attributes.title ?? '', title: mapItem.attributes.title ?? '',
}, },
}; };
}; };
async function findMaps( async function deleteMaps(items: Array<{ id: string }>) {
searchTerm: string, await Promise.all(items.map(({ id }) => mapsClient.delete(id)));
{
references,
referencesToExclude,
}: {
references?: SavedObjectsFindOptionsReference[];
referencesToExclude?: SavedObjectsFindOptionsReference[];
} = {}
) {
const resp = await getSavedObjectsClient().find<MapSavedObjectAttributes>({
type: MAP_SAVED_OBJECT_TYPE,
search: searchTerm ? `${searchTerm}*` : undefined,
perPage: getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING),
page: 1,
searchFields: ['title^3', 'description'],
defaultSearchOperator: 'AND',
fields: ['description', 'title'],
hasReference: references,
hasNoReference: referencesToExclude,
});
return {
total: resp.total,
hits: resp.savedObjects.map(toTableListViewSavedObject),
};
}
async function deleteMaps(items: object[]) {
const deletions = items.map((item) => {
return getSavedObjectsClient().delete(MAP_SAVED_OBJECT_TYPE, (item as MapItem).id);
});
await Promise.all(deletions);
} }
interface Props { interface Props {
history: ScopedHistory; history: ScopedHistory;
} }
export function MapsListView(props: Props) { function MapsListViewComp({ history }: Props) {
getExecutionContextService().set({ getExecutionContextService().set({
type: 'application', type: 'application',
name: APP_ID, name: APP_ID,
@ -117,6 +76,42 @@ export function MapsListView(props: Props) {
getCoreChrome().docTitle.change(getAppTitle()); getCoreChrome().docTitle.change(getAppTitle());
getCoreChrome().setBreadcrumbs([{ text: getAppTitle() }]); getCoreChrome().setBreadcrumbs([{ text: getAppTitle() }]);
const findMaps = useCallback(
async (
searchTerm: string,
{
references = [],
referencesToExclude = [],
}: {
references?: SavedObjectsFindOptionsReference[];
referencesToExclude?: SavedObjectsFindOptionsReference[];
} = {}
) => {
return mapsClient
.search({
text: searchTerm ? `${searchTerm}*` : undefined,
limit: getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING),
tags: {
included: references.map(({ id }) => id),
excluded: referencesToExclude.map(({ id }) => id),
},
})
.then(({ hits, pagination: { total } }) => {
return {
total,
hits: hits.map(toTableListViewSavedObject),
};
})
.catch((e) => {
return {
total: 0,
hits: [],
};
});
},
[]
);
return ( return (
<TableListView<MapUserContent> <TableListView<MapUserContent>
id="map" id="map"
@ -134,7 +129,9 @@ export function MapsListView(props: Props) {
defaultMessage: 'maps', defaultMessage: 'maps',
})} })}
tableListTitle={getAppTitle()} tableListTitle={getAppTitle()}
onClickTitle={({ id }) => props.history.push(getEditPath(id))} onClickTitle={({ id }) => history.push(getEditPath(id))}
/> />
); );
} }
export const MapsListView = memo(MapsListViewComp);

View file

@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public';
import { ScopedHistory } from '@kbn/core/public'; import { ScopedHistory } from '@kbn/core/public';
import { OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { OnSaveProps } from '@kbn/saved-objects-plugin/public';
import { MapSavedObjectAttributes } from '../../../../common/map_saved_object_type'; import type { MapAttributes } from '../../../../common/content_management';
import { APP_ID, MAP_PATH, MAP_SAVED_OBJECT_TYPE } from '../../../../common/constants'; import { APP_ID, MAP_PATH, MAP_SAVED_OBJECT_TYPE } from '../../../../common/constants';
import { createMapStore, MapStore, MapStoreState } from '../../../reducers/store'; import { createMapStore, MapStore, MapStoreState } from '../../../reducers/store';
import { MapSettings } from '../../../../common/descriptor_types'; import { MapSettings } from '../../../../common/descriptor_types';
@ -71,7 +71,7 @@ function setMapSettingsFromEncodedState(settings: Partial<MapSettings>) {
} }
export class SavedMap { export class SavedMap {
private _attributes: MapSavedObjectAttributes | null = null; private _attributes: MapAttributes | null = null;
private _sharingSavedObjectProps: SharingSavedObjectProps | null = null; private _sharingSavedObjectProps: SharingSavedObjectProps | null = null;
private readonly _defaultLayers: LayerDescriptor[]; private readonly _defaultLayers: LayerDescriptor[];
private readonly _embeddableId?: string; private readonly _embeddableId?: string;
@ -385,7 +385,7 @@ export class SavedMap {
return this._attributes.title !== undefined ? this._attributes.title : ''; return this._attributes.title !== undefined ? this._attributes.title : '';
} }
public getAttributes(): MapSavedObjectAttributes { public getAttributes(): MapAttributes {
if (!this._attributes) { if (!this._attributes) {
throw new Error('Invalid usage, must await whenReady before calling getAttributes'); throw new Error('Invalid usage, must await whenReady before calling getAttributes');
} }

View file

@ -9,7 +9,6 @@ import React from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { Adapters } from '@kbn/inspector-plugin/public'; import { Adapters } from '@kbn/inspector-plugin/public';
import { import {
checkForDuplicateTitle,
SavedObjectSaveModalOrigin, SavedObjectSaveModalOrigin,
OnSaveProps, OnSaveProps,
showSaveModal, showSaveModal,
@ -24,14 +23,13 @@ import {
getMapsCapabilities, getMapsCapabilities,
getIsAllowByValueEmbeddables, getIsAllowByValueEmbeddables,
getInspector, getInspector,
getSavedObjectsClient,
getCoreOverlays, getCoreOverlays,
getSavedObjectsTagging, getSavedObjectsTagging,
getPresentationUtilContext, getPresentationUtilContext,
} from '../../kibana_services'; } from '../../kibana_services';
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { SavedMap } from './saved_map'; import { SavedMap } from './saved_map';
import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters'; import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters';
import { checkForDuplicateTitle } from '../../content_management';
const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard); const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard);
@ -180,13 +178,11 @@ export function getTopNavConfig({
title: props.newTitle, title: props.newTitle,
copyOnSave: props.newCopyOnSave, copyOnSave: props.newCopyOnSave,
lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '', lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '',
getEsType: () => MAP_SAVED_OBJECT_TYPE, isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed,
getDisplayName: getMapEmbeddableDisplayName, getDisplayName: getMapEmbeddableDisplayName,
onTitleDuplicate: props.onTitleDuplicate,
}, },
props.isTitleDuplicateConfirmed,
props.onTitleDuplicate,
{ {
savedObjectsClient: getSavedObjectsClient(),
overlays: getCoreOverlays(), overlays: getCoreOverlays(),
} }
); );

View file

@ -0,0 +1,8 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { MapsStorage } from './maps_storage';

View file

@ -0,0 +1,313 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import Boom from '@hapi/boom';
import type { SearchQuery } from '@kbn/content-management-plugin/common';
import type { ContentStorage, StorageContext } from '@kbn/content-management-plugin/server';
import type {
SavedObject,
SavedObjectReference,
SavedObjectsFindOptions,
} from '@kbn/core-saved-objects-api-server';
import { CONTENT_ID } from '../../common/content_management';
import { cmServicesDefinition } from '../../common/content_management/cm_services';
import type {
MapItem,
PartialMapItem,
MapContentType,
MapAttributes,
MapGetOut,
MapCreateIn,
MapCreateOut,
CreateOptions,
MapUpdateIn,
MapUpdateOut,
UpdateOptions,
MapDeleteOut,
MapSearchOptions,
MapSearchOut,
} from '../../common/content_management';
const savedObjectClientFromRequest = async (ctx: StorageContext) => {
if (!ctx.requestHandlerContext) {
throw new Error('Storage context.requestHandlerContext missing.');
}
const { savedObjects } = await ctx.requestHandlerContext.core;
return savedObjects.client;
};
type PartialSavedObject<T> = Omit<SavedObject<Partial<T>>, 'references'> & {
references: SavedObjectReference[] | undefined;
};
function savedObjectToMapItem(savedObject: SavedObject<MapAttributes>, partial: false): MapItem;
function savedObjectToMapItem(
savedObject: PartialSavedObject<MapAttributes>,
partial: true
): PartialMapItem;
function savedObjectToMapItem(
savedObject: SavedObject<MapAttributes> | PartialSavedObject<MapAttributes>
): MapItem | PartialMapItem {
const {
id,
type,
updated_at: updatedAt,
created_at: createdAt,
attributes: { title, description, layerListJSON, mapStateJSON, uiStateJSON },
references,
error,
namespaces,
} = savedObject;
return {
id,
type,
updatedAt,
createdAt,
attributes: {
title,
description,
layerListJSON,
mapStateJSON,
uiStateJSON,
},
references,
error,
namespaces,
};
}
const SO_TYPE: MapContentType = 'map';
export class MapsStorage implements ContentStorage<MapItem, PartialMapItem> {
constructor() {}
async get(ctx: StorageContext, id: string): Promise<MapGetOut> {
const {
utils: { getTransforms },
version: { request: requestVersion },
} = ctx;
const transforms = getTransforms(cmServicesDefinition, requestVersion);
const soClient = await savedObjectClientFromRequest(ctx);
// Save data in DB
const {
saved_object: savedObject,
alias_purpose: aliasPurpose,
alias_target_id: aliasTargetId,
outcome,
} = await soClient.resolve<MapAttributes>(SO_TYPE, id);
const response: MapGetOut = {
item: savedObjectToMapItem(savedObject, false),
meta: {
aliasPurpose,
aliasTargetId,
outcome,
},
};
// Validate DB response and DOWN transform to the request version
const { value, error: resultError } = transforms.get.out.result.down<MapGetOut, MapGetOut>(
response
);
if (resultError) {
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
}
return value;
}
async bulkGet(): Promise<never> {
// Not implemented. Maps does not use bulkGet
throw new Error(`[bulkGet] has not been implemented. See MapsStorage class.`);
}
async create(
ctx: StorageContext,
data: MapCreateIn['data'],
options: CreateOptions
): Promise<MapCreateOut> {
const {
utils: { getTransforms },
version: { request: requestVersion },
} = ctx;
const transforms = getTransforms(cmServicesDefinition, requestVersion);
// Validate input (data & options) & UP transform them to the latest version
const { value: dataToLatest, error: dataError } = transforms.create.in.data.up<
MapAttributes,
MapAttributes
>(data);
if (dataError) {
throw Boom.badRequest(`Invalid data. ${dataError.message}`);
}
const { value: optionsToLatest, error: optionsError } = transforms.create.in.options.up<
CreateOptions,
CreateOptions
>(options);
if (optionsError) {
throw Boom.badRequest(`Invalid options. ${optionsError.message}`);
}
// Save data in DB
const soClient = await savedObjectClientFromRequest(ctx);
const savedObject = await soClient.create<MapAttributes>(
SO_TYPE,
dataToLatest,
optionsToLatest
);
// Validate DB response and DOWN transform to the request version
const { value, error: resultError } = transforms.create.out.result.down<
MapCreateOut,
MapCreateOut
>({
item: savedObjectToMapItem(savedObject, false),
});
if (resultError) {
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
}
return value;
}
async update(
ctx: StorageContext,
id: string,
data: MapUpdateIn['data'],
options: UpdateOptions
): Promise<MapUpdateOut> {
const {
utils: { getTransforms },
version: { request: requestVersion },
} = ctx;
const transforms = getTransforms(cmServicesDefinition, requestVersion);
// Validate input (data & options) & UP transform them to the latest version
const { value: dataToLatest, error: dataError } = transforms.update.in.data.up<
MapAttributes,
MapAttributes
>(data);
if (dataError) {
throw Boom.badRequest(`Invalid data. ${dataError.message}`);
}
const { value: optionsToLatest, error: optionsError } = transforms.update.in.options.up<
CreateOptions,
CreateOptions
>(options);
if (optionsError) {
throw Boom.badRequest(`Invalid options. ${optionsError.message}`);
}
// Save data in DB
const soClient = await savedObjectClientFromRequest(ctx);
const partialSavedObject = await soClient.update<MapAttributes>(
SO_TYPE,
id,
dataToLatest,
optionsToLatest
);
// Validate DB response and DOWN transform to the request version
const { value, error: resultError } = transforms.update.out.result.down<
MapUpdateOut,
MapUpdateOut
>({
item: savedObjectToMapItem(partialSavedObject, true),
});
if (resultError) {
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
}
return value;
}
async delete(ctx: StorageContext, id: string): Promise<MapDeleteOut> {
const soClient = await savedObjectClientFromRequest(ctx);
await soClient.delete(SO_TYPE, id);
return { success: true };
}
async search(
ctx: StorageContext,
query: SearchQuery,
options: MapSearchOptions = {}
): Promise<MapSearchOut> {
const {
utils: { getTransforms },
version: { request: requestVersion },
} = ctx;
const transforms = getTransforms(cmServicesDefinition, requestVersion);
const soClient = await savedObjectClientFromRequest(ctx);
// Validate and UP transform the options
const { value: optionsToLatest, error: optionsError } = transforms.search.in.options.up<
MapSearchOptions,
MapSearchOptions
>(options);
if (optionsError) {
throw Boom.badRequest(`Invalid payload. ${optionsError.message}`);
}
const { onlyTitle = false } = optionsToLatest;
const { included, excluded } = query.tags ?? {};
const hasReference: SavedObjectsFindOptions['hasReference'] = included
? included.map((id) => ({
id,
type: 'tag',
}))
: undefined;
const hasNoReference: SavedObjectsFindOptions['hasNoReference'] = excluded
? excluded.map((id) => ({
id,
type: 'tag',
}))
: undefined;
const soQuery: SavedObjectsFindOptions = {
type: CONTENT_ID,
search: query.text,
perPage: query.limit,
page: query.cursor ? +query.cursor : undefined,
defaultSearchOperator: 'AND',
searchFields: onlyTitle ? ['title'] : ['title^3', 'description'],
fields: ['description', 'title'],
hasReference,
hasNoReference,
};
// Execute the query in the DB
const response = await soClient.find<MapAttributes>(soQuery);
// Validate the response and DOWN transform to the request version
const { value, error: resultError } = transforms.search.out.result.down<
MapSearchOut,
MapSearchOut
>({
hits: response.saved_objects.map((so) => savedObjectToMapItem(so, false)),
pagination: {
total: response.total,
},
});
if (resultError) {
throw Boom.badRequest(`Invalid response. ${resultError.message}`);
}
return value;
}
}

View file

@ -6,7 +6,7 @@
*/ */
import type { SerializableRecord } from '@kbn/utility-types'; import type { SerializableRecord } from '@kbn/utility-types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
import { moveAttribution } from '../../common/migrations/move_attribution'; import { moveAttribution } from '../../common/migrations/move_attribution';
import { migrateOtherCategoryColor } from '../../common/migrations/migrate_other_category_color'; import { migrateOtherCategoryColor } from '../../common/migrations/migrate_other_category_color';
import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes';
@ -25,7 +25,7 @@ export const embeddableMigrations = {
try { try {
return { return {
...state, ...state,
attributes: moveAttribution(state as { attributes: MapSavedObjectAttributes }), attributes: moveAttribution(state as { attributes: MapAttributes }),
} as SerializableRecord; } as SerializableRecord;
} catch (e) { } catch (e) {
// Do not fail migration // Do not fail migration
@ -37,7 +37,7 @@ export const embeddableMigrations = {
try { try {
return { return {
...state, ...state,
attributes: setEmsTmsDefaultModes(state as { attributes: MapSavedObjectAttributes }), attributes: setEmsTmsDefaultModes(state as { attributes: MapAttributes }),
} as SerializableRecord; } as SerializableRecord;
} catch (e) { } catch (e) {
// Do not fail migration // Do not fail migration
@ -47,7 +47,7 @@ export const embeddableMigrations = {
}, },
'8.0.1': (state: SerializableRecord) => { '8.0.1': (state: SerializableRecord) => {
try { try {
const { attributes } = extractReferences(state as { attributes: MapSavedObjectAttributes }); const { attributes } = extractReferences(state as { attributes: MapAttributes });
return { return {
...state, ...state,
attributes, attributes,
@ -62,7 +62,7 @@ export const embeddableMigrations = {
try { try {
return { return {
...state, ...state,
attributes: renameLayerTypes(state as { attributes: MapSavedObjectAttributes }), attributes: renameLayerTypes(state as { attributes: MapAttributes }),
} as SerializableRecord; } as SerializableRecord;
} catch (e) { } catch (e) {
// Do not fail migration // Do not fail migration
@ -74,7 +74,7 @@ export const embeddableMigrations = {
try { try {
return { return {
...state, ...state,
attributes: migrateOtherCategoryColor(state as { attributes: MapSavedObjectAttributes }), attributes: migrateOtherCategoryColor(state as { attributes: MapAttributes }),
} as SerializableRecord; } as SerializableRecord;
} catch (e) { } catch (e) {
// Do not fail migration // Do not fail migration

View file

@ -8,16 +8,16 @@
import { asyncForEach } from '@kbn/std'; import { asyncForEach } from '@kbn/std';
import type { ISavedObjectsRepository, SavedObject } from '@kbn/core/server'; import type { ISavedObjectsRepository, SavedObject } from '@kbn/core/server';
import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants';
import type { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
export async function findMaps( export async function findMaps(
savedObjectsClient: Pick<ISavedObjectsRepository, 'find'>, savedObjectsClient: Pick<ISavedObjectsRepository, 'find'>,
callback: (savedObject: SavedObject<MapSavedObjectAttributes>) => Promise<void> callback: (savedObject: SavedObject<MapAttributes>) => Promise<void>
) { ) {
let nextPage = 1; let nextPage = 1;
let hasMorePages = false; let hasMorePages = false;
do { do {
const results = await savedObjectsClient.find<MapSavedObjectAttributes>({ const results = await savedObjectsClient.find<MapAttributes>({
type: MAP_SAVED_OBJECT_TYPE, type: MAP_SAVED_OBJECT_TYPE,
page: nextPage, page: nextPage,
}); });

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import { MapSavedObjectAttributes } from '../../../common/map_saved_object_type'; import type { MapAttributes } from '../../../common/content_management';
import { import {
EMS_BASEMAP_KEYS, EMS_BASEMAP_KEYS,
JOIN_KEYS, JOIN_KEYS,
@ -38,7 +38,7 @@ export class MapStatsCollector {
private _customIconsCountStats: ClusterCountStats | undefined; private _customIconsCountStats: ClusterCountStats | undefined;
private _sourceCountStats: ClusterCountStats | undefined; private _sourceCountStats: ClusterCountStats | undefined;
push(attributes: MapSavedObjectAttributes) { push(attributes: MapAttributes) {
if (!attributes || !attributes.mapStateJSON || !attributes.layerListJSON) { if (!attributes || !attributes.mapStateJSON || !attributes.layerListJSON) {
return; return;
} }

View file

@ -17,6 +17,8 @@ import {
import { HomeServerPluginSetup } from '@kbn/home-plugin/server'; import { HomeServerPluginSetup } from '@kbn/home-plugin/server';
import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common';
import type { EMSSettings } from '@kbn/maps-ems-plugin/server'; import type { EMSSettings } from '@kbn/maps-ems-plugin/server';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects'; import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects';
import { getFlightsSavedObjects } from './sample_data/flights_saved_objects'; import { getFlightsSavedObjects } from './sample_data/flights_saved_objects';
import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects'; import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects';
@ -30,6 +32,7 @@ import { setupEmbeddable } from './embeddable';
import { setupSavedObjects } from './saved_objects'; import { setupSavedObjects } from './saved_objects';
import { registerIntegrations } from './register_integrations'; import { registerIntegrations } from './register_integrations';
import { StartDeps, SetupDeps } from './types'; import { StartDeps, SetupDeps } from './types';
import { MapsStorage } from './content_management';
export class MapsPlugin implements Plugin { export class MapsPlugin implements Plugin {
readonly _initializerContext: PluginInitializerContext<MapsXPackConfig>; readonly _initializerContext: PluginInitializerContext<MapsXPackConfig>;
@ -150,7 +153,7 @@ export class MapsPlugin implements Plugin {
DataViewPersistableStateService DataViewPersistableStateService
); );
const { usageCollection, home, features, customIntegrations } = plugins; const { usageCollection, home, features, customIntegrations, contentManagement } = plugins;
const config$ = this._initializerContext.config.create(); const config$ = this._initializerContext.config.create();
const emsSettings = plugins.mapsEms.createEMSSettings(); const emsSettings = plugins.mapsEms.createEMSSettings();
@ -199,6 +202,14 @@ export class MapsPlugin implements Plugin {
setupSavedObjects(core, getFilterMigrations, getDataViewMigrations); setupSavedObjects(core, getFilterMigrations, getDataViewMigrations);
registerMapsUsageCollector(usageCollection); registerMapsUsageCollector(usageCollection);
contentManagement.register({
id: CONTENT_ID,
storage: new MapsStorage(),
version: {
latest: LATEST_VERSION,
},
});
setupEmbeddable(plugins.embeddable, getFilterMigrations, getDataViewMigrations); setupEmbeddable(plugins.embeddable, getFilterMigrations, getDataViewMigrations);
return { return {

View file

@ -21,12 +21,12 @@ import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin'
import { moveAttribution } from '../../common/migrations/move_attribution'; import { moveAttribution } from '../../common/migrations/move_attribution';
import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes';
import { renameLayerTypes } from '../../common/migrations/rename_layer_types'; import { renameLayerTypes } from '../../common/migrations/rename_layer_types';
import type { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
function logMigrationWarning( function logMigrationWarning(
context: SavedObjectMigrationContext, context: SavedObjectMigrationContext,
errorMsg: string, errorMsg: string,
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes> doc: SavedObjectUnsanitizedDoc<MapAttributes>
) { ) {
context.log.warn( context.log.warn(
`map migration failed (${context.migrationVersion}). ${errorMsg}. attributes: ${JSON.stringify( `map migration failed (${context.migrationVersion}). ${errorMsg}. attributes: ${JSON.stringify(
@ -44,7 +44,7 @@ function logMigrationWarning(
*/ */
export const savedObjectMigrations = { export const savedObjectMigrations = {
'7.2.0': ( '7.2.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -61,7 +61,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.4.0': ( '7.4.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -77,7 +77,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.5.0': ( '7.5.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -93,7 +93,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.6.0': ( '7.6.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -110,7 +110,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.7.0': ( '7.7.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -127,7 +127,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.8.0': ( '7.8.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -143,7 +143,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.9.0': ( '7.9.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -159,7 +159,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.10.0': ( '7.10.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -175,7 +175,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.12.0': ( '7.12.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -191,7 +191,7 @@ export const savedObjectMigrations = {
} }
}, },
'7.14.0': ( '7.14.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -207,7 +207,7 @@ export const savedObjectMigrations = {
} }
}, },
'8.0.0': ( '8.0.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -223,7 +223,7 @@ export const savedObjectMigrations = {
} }
}, },
'8.1.0': ( '8.1.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {
@ -239,7 +239,7 @@ export const savedObjectMigrations = {
} }
}, },
'8.4.0': ( '8.4.0': (
doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>, doc: SavedObjectUnsanitizedDoc<MapAttributes>,
context: SavedObjectMigrationContext context: SavedObjectMigrationContext
) => { ) => {
try { try {

View file

@ -11,9 +11,10 @@ import type { SavedObjectMigrationMap } from '@kbn/core/server';
import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common';
import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { mergeSavedObjectMigrationMaps } from '@kbn/core/server';
import { APP_ICON, getFullPath } from '../../common/constants'; import { APP_ICON, getFullPath } from '../../common/constants';
import { CONTENT_ID } from '../../common/content_management';
import { migrateDataPersistedState } from '../../common/migrations/migrate_data_persisted_state'; import { migrateDataPersistedState } from '../../common/migrations/migrate_data_persisted_state';
import { migrateDataViewsPersistedState } from '../../common/migrations/migrate_data_view_persisted_state'; import { migrateDataViewsPersistedState } from '../../common/migrations/migrate_data_view_persisted_state';
import type { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import type { MapAttributes } from '../../common/content_management';
import { savedObjectMigrations } from './saved_object_migrations'; import { savedObjectMigrations } from './saved_object_migrations';
export function setupSavedObjects( export function setupSavedObjects(
@ -21,8 +22,8 @@ export function setupSavedObjects(
getFilterMigrations: () => MigrateFunctionsObject, getFilterMigrations: () => MigrateFunctionsObject,
getDataViewMigrations: () => MigrateFunctionsObject getDataViewMigrations: () => MigrateFunctionsObject
) { ) {
core.savedObjects.registerType<MapSavedObjectAttributes>({ core.savedObjects.registerType<MapAttributes>({
name: 'map', name: CONTENT_ID,
hidden: false, hidden: false,
namespaceType: 'multiple-isolated', namespaceType: 'multiple-isolated',
convertToMultiNamespaceTypeVersion: '8.0.0', convertToMultiNamespaceTypeVersion: '8.0.0',
@ -71,7 +72,7 @@ export const getMapsFilterMigrations = (
): MigrateFunctionsObject => ): MigrateFunctionsObject =>
mapValues( mapValues(
filterMigrations, filterMigrations,
(filterMigration) => (doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>) => { (filterMigration) => (doc: SavedObjectUnsanitizedDoc<MapAttributes>) => {
try { try {
const attributes = migrateDataPersistedState(doc, filterMigration); const attributes = migrateDataPersistedState(doc, filterMigration);
@ -93,9 +94,7 @@ export const getMapsFilterMigrations = (
export const getMapsDataViewMigrations = ( export const getMapsDataViewMigrations = (
migrations: MigrateFunctionsObject migrations: MigrateFunctionsObject
): MigrateFunctionsObject => ): MigrateFunctionsObject =>
mapValues( mapValues(migrations, (migration) => (doc: SavedObjectUnsanitizedDoc<MapAttributes>) => {
migrations,
(migration) => (doc: SavedObjectUnsanitizedDoc<MapSavedObjectAttributes>) => {
try { try {
const attributes = migrateDataViewsPersistedState(doc, migration); const attributes = migrateDataViewsPersistedState(doc, migration);
@ -108,5 +107,4 @@ export const getMapsDataViewMigrations = (
// Maps application can display error when saved object is viewed // Maps application can display error when saved object is viewed
return doc; return doc;
} }
} });
);

View file

@ -16,6 +16,7 @@ import {
PluginStart as DataPluginStart, PluginStart as DataPluginStart,
} from '@kbn/data-plugin/server'; } from '@kbn/data-plugin/server';
import { CustomIntegrationsPluginSetup } from '@kbn/custom-integrations-plugin/server'; import { CustomIntegrationsPluginSetup } from '@kbn/custom-integrations-plugin/server';
import type { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
export interface SetupDeps { export interface SetupDeps {
data: DataPluginSetup; data: DataPluginSetup;
@ -26,6 +27,7 @@ export interface SetupDeps {
mapsEms: MapsEmsPluginServerSetup; mapsEms: MapsEmsPluginServerSetup;
embeddable: EmbeddableSetup; embeddable: EmbeddableSetup;
customIntegrations: CustomIntegrationsPluginSetup; customIntegrations: CustomIntegrationsPluginSetup;
contentManagement: ContentManagementServerSetup;
} }
export interface StartDeps { export interface StartDeps {

View file

@ -56,7 +56,6 @@
"@kbn/mapbox-gl", "@kbn/mapbox-gl",
"@kbn/core-execution-context-common", "@kbn/core-execution-context-common",
"@kbn/chart-icons", "@kbn/chart-icons",
"@kbn/core-saved-objects-api-browser",
"@kbn/ui-theme", "@kbn/ui-theme",
"@kbn/monaco", "@kbn/monaco",
"@kbn/safer-lodash-set", "@kbn/safer-lodash-set",
@ -64,6 +63,9 @@
"@kbn/config-schema", "@kbn/config-schema",
"@kbn/controls-plugin", "@kbn/controls-plugin",
"@kbn/shared-ux-router", "@kbn/shared-ux-router",
"@kbn/content-management-plugin",
"@kbn/core-saved-objects-api-server",
"@kbn/object-versioning",
"@kbn/field-types", "@kbn/field-types",
], ],
"exclude": [ "exclude": [