[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 {
get: {
in: {
options: ObjectTransforms;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
bulkGet: {
in: {
options: ObjectTransforms;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
create: {
in: {
data: ObjectTransforms;
options: ObjectTransforms;
data: ObjectTransforms<any, any, any, any>;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
update: {
in: {
data: ObjectTransforms;
options: ObjectTransforms;
data: ObjectTransforms<any, any, any, any>;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
delete: {
in: {
options: ObjectTransforms;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
search: {
in: {
options: ObjectTransforms;
options: ObjectTransforms<any, any, any, any>;
};
out: {
result: ObjectTransforms;
result: ObjectTransforms<any, any, any, any>;
};
};
}

View file

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

View file

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

View file

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

View file

@ -34,11 +34,11 @@ export class RpcClient implements CrudClient {
constructor(private http: { post: HttpSetup['post'] }) {}
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) {
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) {

View file

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

View file

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

View file

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

View file

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

View file

@ -7,15 +7,16 @@
*/
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 { ContentRegistry } from '../../core/registry';
import { createMockedStorage } from '../../core/mocks';
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 { search } from './search';
const { fn, schemas } = search;
@ -34,7 +35,12 @@ const FOO_CONTENT_ID = 'foo';
describe('RPC -> search()', () => {
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 };
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
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' },
expectedError: '[unknown]: definition for this key is missing',

View file

@ -30,7 +30,7 @@ export interface VisualizationListItem {
export interface VisualizationsAppExtension {
docTypes: string[];
searchFields?: string[];
toListItem: (savedObject: SimpleSavedObject) => VisualizationListItem;
toListItem: (savedObject: SimpleSavedObject<any>) => VisualizationListItem;
}
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.
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
export const LATEST_VERSION = 1;
export type MapSavedObjectAttributes = {
title: string;
description?: string;
mapStateJSON?: string;
layerListJSON?: string;
uiStateJSON?: string;
};
export const CONTENT_ID = 'map';

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

View file

@ -7,7 +7,7 @@
import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
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';
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
// refName generation will produce consistent values allowing inject logic to then replace refNames with current ids.
const { attributes: attributesWithNoHardCodedIds } = extractReferences({
attributes: typedState.attributes as MapSavedObjectAttributes,
attributes: typedState.attributes as MapAttributes,
});
const { attributes: attributesWithInjectedIds } = injectReferences({

View file

@ -5,18 +5,14 @@
* 2.0.
*/
import { MapSavedObjectAttributes } from '../map_saved_object_type';
import type { MapAttributes } from '../content_management';
import { JoinDescriptor, LayerDescriptor, VectorLayerDescriptor } from '../descriptor_types';
import { SOURCE_TYPES } from '../constants';
// 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.
// This is just to be safe.
export function addTypeToTermJoin({
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
export function addTypeToTermJoin({ attributes }: { attributes: MapAttributes }): MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}

View file

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

View file

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

View file

@ -8,16 +8,16 @@
import type { Serializable } from '@kbn/utility-types';
import type { DataViewSpec } from '@kbn/data-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(
{
attributes,
}: {
attributes: MapSavedObjectAttributes;
attributes: MapAttributes;
},
migration: MigrateFunction<Serializable, Serializable>
): MapSavedObjectAttributes {
): MapAttributes {
let mapState: { adHocDataViews?: DataViewSpec[] } = { adHocDataViews: [] };
if (attributes.mapStateJSON) {
try {

View file

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

View file

@ -5,15 +5,11 @@
* 2.0.
*/
import { MapSavedObjectAttributes } from '../map_saved_object_type';
import type { MapAttributes } from '../content_management';
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.
export function moveAttribution({
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
export function moveAttribution({ attributes }: { attributes: MapAttributes }): MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}

View file

@ -9,7 +9,7 @@
import type { DataViewSpec } from '@kbn/data-plugin/common';
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';
interface IndexPatternReferenceDescriptor {
@ -21,7 +21,7 @@ export function extractReferences({
attributes,
references = [],
}: {
attributes: MapSavedObjectAttributes;
attributes: MapAttributes;
references?: SavedObjectReference[];
}) {
if (!attributes.layerListJSON) {
@ -119,7 +119,7 @@ export function injectReferences({
attributes,
references,
}: {
attributes: MapSavedObjectAttributes;
attributes: MapAttributes;
references: SavedObjectReference[];
}) {
if (!attributes.layerListJSON) {

View file

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

View file

@ -7,18 +7,14 @@
import { LAYER_TYPE } from '../constants';
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.
// TILED_VECTOR replaced with MVT_VECTOR
// VECTOR_TILE replaced with EMS_VECTOR_TILE
// VECTOR replaced with GEOJSON_VECTOR
// TILE replaced with RASTER_TILE
export function renameLayerTypes({
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
export function renameLayerTypes({ attributes }: { attributes: MapAttributes }): MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}

View file

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

View file

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

View file

@ -7,7 +7,7 @@
import { SOURCE_TYPES } from '../constants';
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
// 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({
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
attributes: MapAttributes;
}): MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}

View file

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

View file

@ -19,7 +19,7 @@ import {
LayerDescriptor,
VectorLayerDescriptor,
} 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';
export class LayerStatsCollector {
@ -34,7 +34,7 @@ export class LayerStatsCollector {
private _layerTypeCounts: { [key: string]: number } = {};
private _sourceIds: Set<string> = new Set();
constructor(attributes: MapSavedObjectAttributes) {
constructor(attributes: MapAttributes) {
if (!attributes || !attributes.layerListJSON) {
return;
}

View file

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

View file

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

View file

@ -29,7 +29,8 @@
"mapsEms",
"savedObjects",
"share",
"presentationUtil"
"presentationUtil",
"contentManagement"
],
"optionalPlugins": [
"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 type { DataViewFieldBase, DataViewBase } from '@kbn/es-query';
import { MapEmbeddableConfig, MapEmbeddableInput } from './types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type';
import type { MapAttributes } from '../../common/content_management';
jest.mock('../kibana_services', () => {
return {
@ -66,7 +66,7 @@ jest.mock('../routes/map_page', () => {
class MockSavedMap {
// eslint-disable-next-line @typescript-eslint/no-var-requires
private _store = require('../reducers/store').createMapStore();
private _attributes: MapSavedObjectAttributes = {
private _attributes: MapAttributes = {
title: 'myMap',
};

View file

@ -15,7 +15,7 @@ import {
} from '@kbn/embeddable-plugin/public';
import type { Filter, Query, TimeRange } from '@kbn/es-query';
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 {
editable: boolean;
@ -37,7 +37,7 @@ interface MapEmbeddableState {
isMovementSynchronized?: boolean;
}
export type MapByValueInput = {
attributes: MapSavedObjectAttributes;
attributes: MapAttributes;
} & EmbeddableInput &
MapEmbeddableState;
export type MapByReferenceInput = SavedObjectEmbeddableInput & MapEmbeddableState;

View file

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

View file

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

View file

@ -7,8 +7,7 @@
import { i18n } from '@kbn/i18n';
import type { VisualizationsSetup, VisualizationStage } from '@kbn/visualizations-plugin/public';
import type { SimpleSavedObject } from '@kbn/core/public';
import type { MapSavedObjectAttributes } from '../common/map_saved_object_type';
import type { MapItem } from '../common/content_management';
import {
APP_ID,
APP_ICON,
@ -37,9 +36,8 @@ export function getMapsVisTypeAlias(visualizations: VisualizationsSetup) {
visualizations: {
docTypes: [MAP_SAVED_OBJECT_TYPE],
searchFields: ['title^3'],
toListItem(savedObject: SimpleSavedObject) {
const { id, type, updatedAt, attributes } =
savedObject as SimpleSavedObject<MapSavedObjectAttributes>;
toListItem(mapItem: MapItem) {
const { id, type, updatedAt, attributes } = mapItem;
const { title, description } = attributes;
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 { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-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 { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-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 { LensPublicSetup } from '@kbn/lens-plugin/public';
import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public';
import type {
ContentManagementPublicSetup,
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
import {
createRegionMapFn,
GEOHASH_GRID,
@ -80,6 +84,7 @@ import { setIsCloudEnabled, setMapAppConfig, setStartServices } from './kibana_s
import { MapInspectorView, VectorTileInspectorView } from './inspector';
import { setupLensChoroplethChart } from './lens';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
export interface MapsPluginSetupDependencies {
cloud?: CloudSetup;
@ -94,6 +99,7 @@ export interface MapsPluginSetupDependencies {
licensing: LicensingPluginSetup;
usageCollection?: UsageCollectionSetup;
screenshotMode?: ScreenshotModePluginSetup;
contentManagement: ContentManagementPublicSetup;
}
export interface MapsPluginStartDependencies {
@ -109,13 +115,13 @@ export interface MapsPluginStartDependencies {
uiActions: UiActionsStart;
share: SharePluginStart;
visualizations: VisualizationsStart;
savedObjects: SavedObjectsStart;
dashboard: DashboardStart;
savedObjectsTagging?: SavedObjectTaggingPluginStart;
presentationUtil: PresentationUtilPluginStart;
security?: SecurityPluginStart;
spaces?: SpacesPluginStart;
mapsEms: MapsEmsPluginPublicStart;
contentManagement: ContentManagementPublicStart;
screenshotMode?: ScreenshotModePluginSetup;
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);
// 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 { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/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 { APP_ID, MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { APP_ID } from '../../../common/constants';
import { mapsClient } from '../../content_management';
interface Props {
history: ScopedHistory;
@ -38,13 +39,9 @@ export class LoadListAndRender extends Component<Props> {
async _loadMapsList() {
try {
const results = await getSavedObjectsClient().find({
type: MAP_SAVED_OBJECT_TYPE,
perPage: 1,
fields: ['title'],
});
const results = await mapsClient.search({ limit: 1 });
if (this._isMounted) {
this.setState({ mapsLoaded: true, hasSavedMaps: !!results.savedObjects.length });
this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length });
}
} catch (err) {
if (this._isMounted) {

View file

@ -5,37 +5,29 @@
* 2.0.
*/
import React from 'react';
import { SavedObjectReference } from '@kbn/core/types';
import React, { useCallback, memo } from 'react';
import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n';
import { TableListView } 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 {
getMapsCapabilities,
getCoreChrome,
getExecutionContextService,
getNavigateToApp,
getSavedObjectsClient,
getUiSettings,
getUsageCollection,
} from '../../kibana_services';
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_PER_PAGE_SETTING = 'savedObjects:perPage';
interface MapItem {
id: string;
title: string;
description?: string;
references?: SavedObjectReference[];
}
interface MapUserContent extends UserContentCommonSchema {
type: string;
attributes: {
@ -51,59 +43,26 @@ function navigateToNewMap() {
});
}
const toTableListViewSavedObject = (
savedObject: SimpleSavedObject<MapSavedObjectAttributes>
): MapUserContent => {
const toTableListViewSavedObject = (mapItem: MapItem): MapUserContent => {
return {
...savedObject,
updatedAt: savedObject.updatedAt!,
...mapItem,
updatedAt: mapItem.updatedAt!,
attributes: {
...savedObject.attributes,
title: savedObject.attributes.title ?? '',
...mapItem.attributes,
title: mapItem.attributes.title ?? '',
},
};
};
async function findMaps(
searchTerm: string,
{
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);
async function deleteMaps(items: Array<{ id: string }>) {
await Promise.all(items.map(({ id }) => mapsClient.delete(id)));
}
interface Props {
history: ScopedHistory;
}
export function MapsListView(props: Props) {
function MapsListViewComp({ history }: Props) {
getExecutionContextService().set({
type: 'application',
name: APP_ID,
@ -117,6 +76,42 @@ export function MapsListView(props: Props) {
getCoreChrome().docTitle.change(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 (
<TableListView<MapUserContent>
id="map"
@ -134,7 +129,9 @@ export function MapsListView(props: Props) {
defaultMessage: 'maps',
})}
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 { ScopedHistory } from '@kbn/core/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 { createMapStore, MapStore, MapStoreState } from '../../../reducers/store';
import { MapSettings } from '../../../../common/descriptor_types';
@ -71,7 +71,7 @@ function setMapSettingsFromEncodedState(settings: Partial<MapSettings>) {
}
export class SavedMap {
private _attributes: MapSavedObjectAttributes | null = null;
private _attributes: MapAttributes | null = null;
private _sharingSavedObjectProps: SharingSavedObjectProps | null = null;
private readonly _defaultLayers: LayerDescriptor[];
private readonly _embeddableId?: string;
@ -385,7 +385,7 @@ export class SavedMap {
return this._attributes.title !== undefined ? this._attributes.title : '';
}
public getAttributes(): MapSavedObjectAttributes {
public getAttributes(): MapAttributes {
if (!this._attributes) {
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 { Adapters } from '@kbn/inspector-plugin/public';
import {
checkForDuplicateTitle,
SavedObjectSaveModalOrigin,
OnSaveProps,
showSaveModal,
@ -24,14 +23,13 @@ import {
getMapsCapabilities,
getIsAllowByValueEmbeddables,
getInspector,
getSavedObjectsClient,
getCoreOverlays,
getSavedObjectsTagging,
getPresentationUtilContext,
} from '../../kibana_services';
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { SavedMap } from './saved_map';
import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters';
import { checkForDuplicateTitle } from '../../content_management';
const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard);
@ -180,13 +178,11 @@ export function getTopNavConfig({
title: props.newTitle,
copyOnSave: props.newCopyOnSave,
lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '',
getEsType: () => MAP_SAVED_OBJECT_TYPE,
isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed,
getDisplayName: getMapEmbeddableDisplayName,
onTitleDuplicate: props.onTitleDuplicate,
},
props.isTitleDuplicateConfirmed,
props.onTitleDuplicate,
{
savedObjectsClient: getSavedObjectsClient(),
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 { MapSavedObjectAttributes } from '../../common/map_saved_object_type';
import type { MapAttributes } from '../../common/content_management';
import { moveAttribution } from '../../common/migrations/move_attribution';
import { migrateOtherCategoryColor } from '../../common/migrations/migrate_other_category_color';
import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes';
@ -25,7 +25,7 @@ export const embeddableMigrations = {
try {
return {
...state,
attributes: moveAttribution(state as { attributes: MapSavedObjectAttributes }),
attributes: moveAttribution(state as { attributes: MapAttributes }),
} as SerializableRecord;
} catch (e) {
// Do not fail migration
@ -37,7 +37,7 @@ export const embeddableMigrations = {
try {
return {
...state,
attributes: setEmsTmsDefaultModes(state as { attributes: MapSavedObjectAttributes }),
attributes: setEmsTmsDefaultModes(state as { attributes: MapAttributes }),
} as SerializableRecord;
} catch (e) {
// Do not fail migration
@ -47,7 +47,7 @@ export const embeddableMigrations = {
},
'8.0.1': (state: SerializableRecord) => {
try {
const { attributes } = extractReferences(state as { attributes: MapSavedObjectAttributes });
const { attributes } = extractReferences(state as { attributes: MapAttributes });
return {
...state,
attributes,
@ -62,7 +62,7 @@ export const embeddableMigrations = {
try {
return {
...state,
attributes: renameLayerTypes(state as { attributes: MapSavedObjectAttributes }),
attributes: renameLayerTypes(state as { attributes: MapAttributes }),
} as SerializableRecord;
} catch (e) {
// Do not fail migration
@ -74,7 +74,7 @@ export const embeddableMigrations = {
try {
return {
...state,
attributes: migrateOtherCategoryColor(state as { attributes: MapSavedObjectAttributes }),
attributes: migrateOtherCategoryColor(state as { attributes: MapAttributes }),
} as SerializableRecord;
} catch (e) {
// Do not fail migration

View file

@ -8,16 +8,16 @@
import { asyncForEach } from '@kbn/std';
import type { ISavedObjectsRepository, SavedObject } from '@kbn/core/server';
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(
savedObjectsClient: Pick<ISavedObjectsRepository, 'find'>,
callback: (savedObject: SavedObject<MapSavedObjectAttributes>) => Promise<void>
callback: (savedObject: SavedObject<MapAttributes>) => Promise<void>
) {
let nextPage = 1;
let hasMorePages = false;
do {
const results = await savedObjectsClient.find<MapSavedObjectAttributes>({
const results = await savedObjectsClient.find<MapAttributes>({
type: MAP_SAVED_OBJECT_TYPE,
page: nextPage,
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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