Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Maps] Store map saved objects state in unmapped fields #173528

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { cloneDeep } from 'lodash';
import compare from 'semver/functions/compare';
import coerce from 'semver/functions/coerce';

import { migrateToLatest } from '@kbn/kibana-utils-plugin/common';
import { EmbeddableFactory, EmbeddableInput } from '../embeddables';
Expand All @@ -25,8 +26,10 @@ export const runEmbeddableFactoryMigrations = <ToType extends EmbeddableInput>(
}

// any embeddable with no version set is considered to require all clientside migrations so we default to 0.0.0
const inputVersion = initialInput.version ?? '0.0.0';
const migrationRun = compare(inputVersion, factory.latestVersion, true) !== 0;
const inputVersion = coerce(initialInput.version)?.format() ?? '0.0.0';
const latestVersion = coerce(factory.latestVersion)?.format() ?? '0.0.0';
// compare does not like integer strings, so we coerce to a SemVer string
const migrationRun = compare(inputVersion, latestVersion, true) !== 0;

// return early to avoid extra operations when there are no migrations to run.
if (!migrationRun) return { input: initialInput as unknown as ToType, migrationRun };
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/maps/common/content_management/cm_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import type {
// the schemas in the "public" js bundle

import { serviceDefinition as v1 } from './v1/cm_services';
import { serviceDefinition as v2 } from './v2/cm_services';

export const cmServicesDefinition: { [version: Version]: ServicesDefinition } = {
1: v1,
2: v2,
};
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/common/content_management/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
* 2.0.
*/

export const LATEST_VERSION = 1;
export const LATEST_VERSION = 2;

export const CONTENT_ID = 'map';
4 changes: 2 additions & 2 deletions x-pack/plugins/maps/common/content_management/latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
* 2.0.
*/

// Latest version is 1
export * from './v1';
// Latest version is 2
export * from './v2';
22 changes: 21 additions & 1 deletion x-pack/plugins/maps/common/content_management/v1/cm_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
createOptionsSchemas,
createResultSchema,
} from '@kbn/content-management-utils';
import { MapCrudTypes } from './types';
import { MapCrudTypes as MapCrudTypesV2 } from '../v2';

const mapAttributesSchema = schema.object(
export const mapAttributesSchema = schema.object(
{
title: schema.string(),
description: schema.maybe(schema.nullable(schema.string())),
Expand Down Expand Up @@ -56,6 +58,15 @@ export const serviceDefinition: ServicesDefinition = {
},
data: {
schema: mapAttributesSchema,
up: (data: MapCrudTypes['CreateIn']['data']): MapCrudTypesV2['CreateIn']['data'] => {
const { uiStateJSON, mapStateJSON, layerListJSON, ...rest } = data;
return {
...rest,
uiState: uiStateJSON ? JSON.parse(uiStateJSON) : undefined,
mapState: mapStateJSON ? JSON.parse(mapStateJSON) : undefined,
layerList: layerListJSON ? JSON.parse(layerListJSON) : undefined,
};
},
},
},
out: {
Expand All @@ -71,6 +82,15 @@ export const serviceDefinition: ServicesDefinition = {
},
data: {
schema: mapAttributesSchema,
up: (data: MapCrudTypes['CreateIn']['data']): MapCrudTypesV2['CreateIn']['data'] => {
const { uiStateJSON, mapStateJSON, layerListJSON, ...rest } = data;
return {
...rest,
uiState: uiStateJSON ? JSON.parse(uiStateJSON) : undefined,
mapState: mapStateJSON ? JSON.parse(mapStateJSON) : undefined,
layerList: layerListJSON ? JSON.parse(layerListJSON) : undefined,
};
},
},
},
},
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/common/content_management/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
import { MapCrudTypes } from './types';
export type { MapCrudTypes, MapAttributes } from './types';
export type MapItem = MapCrudTypes['Item'];
export { mapAttributesSchema, serviceDefinition } from './cm_services';
97 changes: 97 additions & 0 deletions x-pack/plugins/maps/common/content_management/v2/cm_services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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 {
objectTypeToGetResultSchema,
savedObjectSchema,
createResultSchema,
} from '@kbn/content-management-utils';
import { ServicesDefinition } from '@kbn/object-versioning/lib/content_management_types';
import {
serviceDefinition as serviceDefinitionV1,
mapAttributesSchema as mapAttributesSchemaV1,
MapCrudTypes as MapCrudTypesV1,
} from '../v1';
import { MapCrudTypes } from './types';

export const mapAttributesSchema = mapAttributesSchemaV1.extends(
{
mapState: schema.maybe(schema.object({}, { unknowns: 'allow' })),
uiState: schema.maybe(schema.object({}, { unknowns: 'allow' })),
layerList: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
// Setting the following to undefined removes them from the schema, I think?
// Based on docs in packages/kbn-config-schema/src/types/object_type.ts
// mapStateJSON: undefined,
// uiStateJSON: undefined,
// layerListJSON: undefined,
},
{
unknowns: 'forbid',
}
);

const mapSavedObjectSchema = savedObjectSchema(mapAttributesSchema);

export const serviceDefinition: ServicesDefinition = {
...serviceDefinitionV1,
get: {
out: {
result: {
schema: objectTypeToGetResultSchema(mapSavedObjectSchema),
down: (result: MapCrudTypes['GetOut']): MapCrudTypesV1['GetOut'] => {
const { uiState, mapState, layerList, ...rest } = result.item.attributes;
return {
...result,
item: {
...result.item,
attributes: {
...rest,
uiStateJSON: uiState ? JSON.stringify(uiState) : undefined,
mapStateJSON: mapState ? JSON.stringify(mapState) : undefined,
layerListJSON: layerList ? JSON.stringify(layerList) : undefined,
},
},
};
},
},
},
},
create: {
in: {
...serviceDefinitionV1.create?.in,
data: {
schema: mapAttributesSchema,
},
},
out: {
result: {
schema: createResultSchema(mapSavedObjectSchema),
},
},
},
update: {
in: {
...serviceDefinitionV1.update?.in,
data: {
schema: mapAttributesSchema,
},
},
out: {
result: {
schema: createResultSchema(mapSavedObjectSchema),
},
},
},
mSearch: {
out: {
result: {
schema: mapSavedObjectSchema,
},
},
},
};
11 changes: 11 additions & 0 deletions x-pack/plugins/maps/common/content_management/v2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 { MapCrudTypes } from './types';
export type { MapCrudTypes, MapAttributes } from './types';
export type MapItem = MapCrudTypes['Item'];
export { mapAttributesSchema } from './cm_services';
34 changes: 34 additions & 0 deletions x-pack/plugins/maps/common/content_management/v2/types.ts
Original file line number Diff line number Diff line change
@@ -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.
*/

import type {
ContentManagementCrudTypes,
SavedObjectCreateOptions,
SavedObjectUpdateOptions,
} from '@kbn/content-management-utils';
import { LayerDescriptor, SerializedMapState, SerializedUiState } from '../../descriptor_types';
import { MapContentType } from '../types';

export type MapCrudTypes = ContentManagementCrudTypes<
MapContentType,
MapAttributes,
Pick<SavedObjectCreateOptions, 'references'>,
Pick<SavedObjectUpdateOptions, 'references'>,
{
/** Flag to indicate to only search the text on the "title" field */
onlyTitle?: boolean;
}
>;

/* eslint-disable-next-line @typescript-eslint/consistent-type-definitions */
export type MapAttributes = {
title: string;
description?: string;
mapState?: SerializedMapState;
layerList?: LayerDescriptor[];
uiState?: SerializedUiState;
};
26 changes: 26 additions & 0 deletions x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

/* eslint-disable @typescript-eslint/consistent-type-definitions */

import type { DataViewSpec, Query } from '@kbn/data-plugin/common';
import { Filter } from '@kbn/es-query';
import type { TimeRange } from '@kbn/es-query';
import type { GeoShapeRelation } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { ReactNode } from 'react';
import { GeoJsonProperties } from 'geojson';
Expand Down Expand Up @@ -112,3 +115,26 @@ export type MapSettings = {
spatialFiltersFillColor: string;
spatialFiltersLineColor: string;
};

export interface RefreshConfig {
isPaused: boolean;
interval: number;
}

// parsed contents of mapStateJSON
export interface SerializedMapState {
adHocDataViews?: DataViewSpec[];
zoom: number;
center: MapCenter;
timeFilters?: TimeRange;
refreshConfig: RefreshConfig;
query?: Query;
filters: Filter[];
settings: MapSettings;
}

// parsed contents of uiStateJSON
export interface SerializedUiState {
isLayerTOCOpen: boolean;
openTOCDetails: string[];
}
3 changes: 1 addition & 2 deletions x-pack/plugins/maps/common/embeddable/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import { MapEmbeddablePersistableState } from './types';
import type { MapAttributes } from '../content_management';
import { extractReferences } from '../migrations/references';

export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
Expand All @@ -21,7 +20,7 @@ export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {

// by-value embeddable
const { attributes, references } = extractReferences({
attributes: typedState.attributes as MapAttributes,
attributes: typedState.attributes,
});

return {
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/maps/common/embeddable/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import type { MapEmbeddablePersistableState } from './types';
import type { MapAttributes } from '../content_management';
import { extractReferences, injectReferences } from '../migrations/references';

export const inject: EmbeddableRegistryDefinition['inject'] = (state, references) => {
Expand All @@ -23,7 +22,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 MapAttributes,
attributes: typedState.attributes,
});

const { attributes: attributesWithInjectedIds } = injectReferences({
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/maps/common/embeddable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* 2.0.
*/

import { SerializableRecord } from '@kbn/utility-types';
import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common';
import { MapAttributes } from '../content_management';

export type MapEmbeddablePersistableState = EmbeddableStateWithType & {
attributes: SerializableRecord;
attributes: MapAttributes;
};
8 changes: 6 additions & 2 deletions x-pack/plugins/maps/common/migrations/add_type_to_termjoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
* 2.0.
*/

import type { MapAttributes } from '../content_management';
import type { MapV1 } 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: MapAttributes }): MapAttributes {
export function addTypeToTermJoin({
attributes,
}: {
attributes: MapV1.MapAttributes;
}): MapV1.MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}
Expand Down
8 changes: 6 additions & 2 deletions x-pack/plugins/maps/common/migrations/join_agg_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
LayerDescriptor,
VectorLayerDescriptor,
} from '../descriptor_types';
import type { MapAttributes } from '../content_management';
import type { MapV1 } from '../content_management';

const GROUP_BY_DELIMITER = '_groupby_';

Expand Down Expand Up @@ -53,7 +53,11 @@ function parseLegacyAggKey(legacyAggKey: string): { aggType: AGG_TYPE; aggFieldN
};
}

export function migrateJoinAggKey({ attributes }: { attributes: MapAttributes }): MapAttributes {
export function migrateJoinAggKey({
attributes,
}: {
attributes: MapV1.MapAttributes;
}): MapV1.MapAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@

import { Filter } from '@kbn/es-query';
import { MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import type { MapAttributes } from '../content_management';
import type { MapV1 } from '../content_management';

export function migrateDataPersistedState(
{
attributes,
}: {
attributes: MapAttributes;
attributes: MapV1.MapAttributes;
},
filterMigration: MigrateFunction<Filter[], Filter[]>
): MapAttributes {
): MapV1.MapAttributes {
let mapState: { filters: Filter[] } = { filters: [] };
if (attributes.mapStateJSON) {
try {
Expand Down
Loading