Skip to content

Commit

Permalink
Introduce the SO versionModel API (#150149)
Browse files Browse the repository at this point in the history
## Summary

Fix #150297

First step toward managed upgrades

This PR adds the types that will be used for the new SO model version
API, and adds the new properties to the `SavedObjectsType` type.

The implementation is outside of scope of the PR and will be implemented
in a future PR.

The PR also adapt the `check_registered_types` test to trigger a review
when the attributes of `SavedObjectsType` introduced in this PR are
changed.
  • Loading branch information
pgayvallet committed Feb 7, 2023
1 parent 82c0dfe commit 7136039
Show file tree
Hide file tree
Showing 10 changed files with 779 additions and 98 deletions.
12 changes: 12 additions & 0 deletions packages/core/saved-objects/core-saved-objects-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ export {
SECURITY_EXTENSION_ID,
SPACES_EXTENSION_ID,
} from './src/extensions/extensions';
export type {
SavedObjectsModelVersion,
SavedObjectsModelVersionMap,
SavedObjectsModelVersionMapProvider,
SavedObjectsModelChange,
SavedObjectsModelExpansionChange,
SavedObjectModelTransformationDoc,
SavedObjectModelTransformationContext,
SavedObjectModelTransformationFn,
SavedObjectModelBidirectionalTransformation,
SavedObjectModelTransformationResult,
} from './src/model_version';

export type {
SavedObject,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export type {
SavedObjectModelTransformationDoc,
SavedObjectModelTransformationContext,
SavedObjectModelTransformationFn,
SavedObjectModelBidirectionalTransformation,
SavedObjectModelTransformationResult,
} from './transformations';

export type {
SavedObjectsModelChange,
SavedObjectsModelExpansionChange,
SavedObjectsModelVersion,
SavedObjectsModelVersionMap,
SavedObjectsModelVersionMapProvider,
} from './model_version';
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { SavedObjectsMappingProperties } from '../mapping_definition';
import type { SavedObjectModelBidirectionalTransformation } from './transformations';

/**
* Represents a model version of a given savedObjects type.
*
* Model versions supersede the {@link SavedObjectsType.migrations | migrations} (and {@link SavedObjectsType.schemas | schemas}) APIs
* by exposing an unified way of describing the changes of shape or data of a type.
*
* @public
*/
export interface SavedObjectsModelVersion {
/**
* The {@link SavedObjectsModelChange | changes} associated with this version.
*/
modelChange: SavedObjectsModelChange;
}

/**
* {@link SavedObjectsModelChange | model change} representing an expansion.
*
* A model expansion can do either, or both, or those:
* - add new mappings
* - migrate data in a backward-compatible way
*
* @remark when adding mappings, {@link SavedObjectsType.mappings | the type mappings} must also be updated accordingly.
* Overall, the type's mapping represents the latests version of the mappings, where the model changes
* represent the changes of mappings between two versions.
*
* @public
*/
export interface SavedObjectsModelExpansionChange<
PreviousAttributes = unknown,
NewAttributes = unknown
> {
/**
* The type of {@link SavedObjectsModelChange | change}, used to identify them internally.
*/
type: 'expansion';
/**
* (optional) A bidirectional transformation to migrate the document from and/or to the previous model version.
*/
transformation?: SavedObjectModelBidirectionalTransformation<PreviousAttributes, NewAttributes>;
/**
* (optional) The new mappings introduced in this version.
*/
addedMappings?: SavedObjectsMappingProperties;
/**
* (optional) A list of paths to mappings to flag as deprecated. Deprecated mappings should no longer be used and will
* eventually be deleted later.
*/
deprecatedMappings?: string[];
}

/**
* Identify the model change associated with a given {@link SavedObjectsModelVersion}.
*
* At the moment, Only one type of change is supported: {@link SavedObjectsModelExpansionChange | expansions}.
*
* @public
*/
export type SavedObjectsModelChange = SavedObjectsModelExpansionChange;

/**
* A record of {@link SavedObjectsModelVersion | model versions} for a given savedObjects type.
* The record's keys must be integers, starting with 1 for the first entry, and there shouldn't be gaps.
*
* @example
* ```typescript
* const modelVersionMap: SavedObjectsModelVersionMap = {
* '1': modelVersion1,
* '2': modelVersion2,
* '3': modelVersion3,
* }
* ```
*
* @public
*/
export interface SavedObjectsModelVersionMap {
[modelVersion: string]: SavedObjectsModelVersion;
}

/**
* A function returning a {@link SavedObjectsModelVersionMap | model version map}
*
* Ensured to be called after all plugins executed their `setup` phase.
* Similar to what was done with migrations, can be used to defer resolving the model versions
* associated to a type to after all plugins have been set up.
*
* @public
*/
export type SavedObjectsModelVersionMapProvider = () => SavedObjectsModelVersionMap;
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { SavedObjectSanitizedDoc } from '../serialization';
import type { SavedObjectsMigrationLogger } from '../migration';

/**
* Document type used during model migration.
*
* @public
*/
export type SavedObjectModelTransformationDoc<T = unknown> = SavedObjectSanitizedDoc<T>;

/**
* Context passed down to {@link SavedObjectModelTransformationFn | transformation functions}.
*
* @public
*/
export interface SavedObjectModelTransformationContext {
/**
* logger instance to be used by the migration handler
*/
readonly log: SavedObjectsMigrationLogger;
/**
* The model version this migration is registered for
*/
readonly modelVersion: number;
}

/**
* Return type for the {@link SavedObjectModelTransformationFn | transformation functions}
*
* @public
*/
export interface SavedObjectModelTransformationResult<DocAttrs = unknown> {
document: SavedObjectModelTransformationDoc<DocAttrs>;
}

/**
* Transformation function for the model version API.
*
* Similar to the old migration system, model version transformations take the document to migrate
* and a context object as input and must return the transformed document in its return value.
*
* @public
*/
export type SavedObjectModelTransformationFn<
InputAttributes = unknown,
OutputAttributes = unknown
> = (
document: SavedObjectModelTransformationDoc<InputAttributes>,
context: SavedObjectModelTransformationContext
) => SavedObjectModelTransformationResult<OutputAttributes>;

/**
* A bidirectional transformation.
*
* Bidirectional transformations define migration functions that can be used to
* transform a document from the lower version to the higher one (`up`), and
* the other way around, from the higher version to the lower one (`down`)
*
* @public
*/
export interface SavedObjectModelBidirectionalTransformation<
PreviousAttributes = unknown,
NewAttributes = unknown
> {
/**
* The upward (previous=>next) transformation.
*/
up: SavedObjectModelTransformationFn<PreviousAttributes, NewAttributes>;
/**
* The downward (next=>previous) transformation.
*/
down: SavedObjectModelTransformationFn<NewAttributes, PreviousAttributes>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import type { SavedObjectsTypeManagementDefinition } from './saved_objects_manag
import type { SavedObjectsValidationMap } from './validation';
import type { SavedObjectMigrationMap } from './migration';
import type { SavedObjectsTypeMappingDefinition } from './mapping_definition';
import type {
SavedObjectsModelVersionMap,
SavedObjectsModelVersionMapProvider,
} from './model_version';

/**
* @public
Expand Down Expand Up @@ -128,6 +132,102 @@ export interface SavedObjectsType<Attributes = any> {
* An optional {@link SavedObjectsTypeManagementDefinition | saved objects management section} definition for the type.
*/
management?: SavedObjectsTypeManagementDefinition<Attributes>;

/**
* A map of model versions associated with this type.
*
* Model versions supersede the {@link SavedObjectsType.migrations | migrations} (and {@link SavedObjectsType.schemas | schemas}) APIs
* by exposing an unified way of describing the changes of shape or data of the type.
*
* Model versioning is decoupled from Kibana versioning, and isolated between types.
* Model versions are identified by a single numeric value, starting at `1` and without gaps.
*
* Please refer to {@link SavedObjectsModelVersion} for details on the API's usages.
*
* A **valid** versioning would be:
*
* ```ts
* {
* name: 'foo',
* // other mandatory attributes...
* modelVersions: {
* '1': modelVersion1,
* '2': modelVersion2,
* '3': modelVersion3,
* }
* }
* ```
*
* A **invalid** versioning would be:
*
* ```ts
* {
* name: 'foo',
* // other mandatory attributes...
* modelVersions: {
* '1': modelVersion1,
* '3': modelVersion3, // ERROR, no model version 2
* '3.1': modelVersion31, // ERROR, model version is a single numeric value
* }
* }
* ```
*
* @alpha experimental and subject to change.
*/
modelVersions?: SavedObjectsModelVersionMap | SavedObjectsModelVersionMapProvider;

/**
* Allows to opt-in to the new model version API.
*
* Must be a valid semver version (with the patch version being necessarily 0)
*
* When specified, the type will switch from using the {@link SavedObjectsType.migrations | legacy migration API}
* to use the {@link SavedObjectsType.modelVersions | modelVersion API} after the specified version.
*
* When opted in, it will no longer be possible to use the legacy migration API after the specified version.
*
* A **valid** usage example would be:
*
* ```ts
* {
* name: 'foo',
* // other mandatory attributes...
* switchToModelVersionAt: '8.8.0',
* migrations: {
* '8.1.0': migrateTo810,
* '8.7.0': migrateTo870,
* },
* modelVersions: {
* '1': modelVersion1
* }
* }
* ```
*
* An **invalid** usage example would be:
*
* ```ts
* {
* name: 'foo',
* // other mandatory attributes...
* switchToModelVersionAt: '8.9.0',
* migrations: {
* '8.1.0': migrateTo8_1,
* '8.9.0': migrateTo8_9, // error: migration registered for the switch version
* '8.10.0': migrateTo8_10, // error: migration registered for after the switch version
* },
* modelVersions: {
* '1': modelVersion1
* }
* }
* ```
*
* Please refer to the {@link SavedObjectsType.modelVersions | modelVersion API} for more documentation on
* the new API.
*
* @remarks All types will be forced to switch to use the new API in a later version. This switch is
* allowing types owners to switch their types before the milestone.
*/
switchToModelVersionAt?: string;
}

/**
Expand Down
Loading

0 comments on commit 7136039

Please sign in to comment.