-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[Saved objects] Prepare SO management for versionable types #149495
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
Changes from all commits
b427c20
3d1901d
849971c
b2374bd
adeb24e
cf77557
9962f8d
935ad21
225ec1d
04fe02d
0782ba8
7ffd75a
c35b104
969507f
8806c50
bff8a0f
f9b9839
ba2df2e
d8bf1f9
05c586e
697f6ca
c7e35d3
f9b5bcb
8f706f1
70967c9
2b347fc
f1074aa
f05ac13
681d372
7db7f77
d950e25
84ba11f
88d03c1
fba8964
4aa0459
542b448
e23f91e
bf02a74
026665e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| ## Versioned interfaces | ||
|
|
||
| This folder contains types that are shared between the server and client: | ||
|
|
||
| ```ts | ||
| // v1.ts | ||
| export interface SavedObjectWithMetadata { name: string } | ||
|
|
||
| // index.ts | ||
| import * as v1 from './v1'; | ||
| export type { v1 }; | ||
|
|
||
| // Used elsewhere | ||
| import type { v1 } from '../common'; | ||
| const myObject: v1.SavedObjectWithMetadata = { name: 'my object' }; | ||
| ``` | ||
|
|
||
| **Do not alter a versioned type**. Types may be in use by clients (if the code is released). | ||
| Alterations must be made on a new version of the TS interface. | ||
|
|
||
| ## Create a new version | ||
|
|
||
| Versions in this plugin are determined using monotonically increasing numbers: 1, 2, 3, etc. | ||
|
|
||
| 1. Find the latest version, e.g: `v2`. | ||
| 2. Create a new file, e.g., `v3.ts` if it does not exist. | ||
| 3. Copy the type(s) to change from previous version. E.g. `v2.ts`'s `SavedObjectWithMetadata`. | ||
| 4. Alter the interface as needed | ||
| 5. Re-export `v2` types to "inherit" the entire previous version's types: `export * from './v2';` | ||
| 6. Export your new version from latest: `export * from './v3';`. This may result in TS errors | ||
| to be fixed. | ||
| 7. Export your new file from index.ts as `v3`. | ||
|
|
||
| Your `v3.ts` file should look something like: | ||
|
|
||
| ```ts | ||
| export * from './v3'; | ||
| export interface SavedObjectWithMetadata { name: string; a_new_field: string; } | ||
| ``` | ||
|
|
||
| In this way the entire API is accessible from `v3` including types that may | ||
| not have changed. | ||
|
|
||
| Any alterations post-release must be in a new version (start at step 1). | ||
|
|
||
|
|
||
| ## The `latest.ts` file | ||
|
|
||
| The `latest.ts` file is a container for all "latest" versions of types. This is useful | ||
| for app code that always needs the latest version of your interfaces. E.g.: | ||
|
|
||
| ```ts | ||
| import type { SavedObjectWithMetadata } from '../common'; | ||
| ``` | ||
|
|
||
| Notice that there is no version number mentioned. Either in the interface name | ||
| or import path. To update the "latest" type you must re-export the new version | ||
| from the appropriate versioned path. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 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 { | ||
| SavedObjectInvalidRelation, | ||
| SavedObjectManagementTypeInfo, | ||
| SavedObjectMetadata, | ||
| SavedObjectRelation, | ||
| SavedObjectRelationKind, | ||
| SavedObjectWithMetadata, | ||
| } from './latest'; | ||
|
|
||
| import type * as v1 from './v1'; | ||
| export type { v1 }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| /* | ||
| * 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 { SavedObjectError } from '@kbn/core/types'; | ||
| import type { SavedObjectsNamespaceType } from '@kbn/core/public'; | ||
|
|
||
| /** Domain interfaces */ | ||
|
|
||
| /** | ||
| * Saved Object Management metadata associated with a saved object. See | ||
| * {@link SavedObjectWithMetadata}. | ||
| */ | ||
| export interface SavedObjectMetadata { | ||
| icon?: string; | ||
| title?: string; | ||
| editUrl?: string; | ||
| inAppUrl?: { path: string; uiCapabilitiesPath: string }; | ||
| namespaceType?: SavedObjectsNamespaceType; | ||
| hiddenType?: boolean; | ||
| } | ||
|
|
||
| /** | ||
| * One saved object's reference to another saved object. | ||
| */ | ||
| export interface SavedObjectReference { | ||
| name: string; | ||
| type: string; | ||
| id: string; | ||
| } | ||
|
|
||
| /** | ||
| * A saved object. | ||
| * | ||
| * @note This is intended as a domain-specific representation of a SavedObject | ||
| * which is intended for server-side only use. | ||
| */ | ||
| export interface SavedObjectWithMetadata<T = unknown> { | ||
| id: string; | ||
| type: string; | ||
| meta: SavedObjectMetadata; | ||
| error?: SavedObjectError; | ||
| created_at?: string; | ||
| updated_at?: string; | ||
| attributes: T; | ||
| namespaces?: string[]; | ||
| references: SavedObjectReference[]; | ||
| } | ||
|
|
||
| export type SavedObjectRelationKind = 'child' | 'parent'; | ||
|
|
||
| /** | ||
| * Represents a relation between two {@link SavedObjectWithMetadata | saved objects}. | ||
| */ | ||
| export interface SavedObjectRelation { | ||
| id: string; | ||
| type: string; | ||
| relationship: SavedObjectRelationKind; | ||
| meta: SavedObjectMetadata; | ||
| } | ||
|
|
||
| /** | ||
| * Represents a relation between two {@link SavedObjectWithMetadata | saved objects}. | ||
| */ | ||
| export interface SavedObjectInvalidRelation { | ||
| id: string; | ||
| type: string; | ||
| relationship: SavedObjectRelationKind; | ||
| error: string; | ||
| } | ||
|
|
||
| export interface SavedObjectManagementTypeInfo { | ||
| name: string; | ||
| // TODO: Fix. We should not directly expose these values to public code. | ||
| namespaceType: SavedObjectsNamespaceType; | ||
| hidden: boolean; | ||
| displayName: string; | ||
| } | ||
|
|
||
| /** HTTP API interfaces */ | ||
|
|
||
| export type BulkGetBodyHTTP = Array<{ | ||
| id: string; | ||
| type: string; | ||
| }>; | ||
|
|
||
| export type BulkGetResponseHTTP = SavedObjectWithMetadata[]; | ||
|
|
||
| export type BulkDeleteBodyHTTP = Array<{ | ||
| type: string; | ||
| id: string; | ||
| }>; | ||
|
|
||
| export type BulkDeleteResponseHTTP = Array<{ | ||
| /** The ID of the saved object */ | ||
| id: string; | ||
| /** The type of the saved object */ | ||
| type: string; | ||
| /** The status of deleting the object: true for deleted, false for error */ | ||
| success: boolean; | ||
| /** Reason the object could not be deleted (success is false) */ | ||
| error?: SavedObjectError; | ||
| }>; | ||
|
|
||
| export type FindSearchOperatorHTTP = 'AND' | 'OR'; | ||
| export type FindSortOrderHTTP = 'asc' | 'desc'; | ||
|
|
||
| export interface ReferenceHTTP { | ||
| type: string; | ||
| id: string; | ||
| } | ||
|
|
||
| export interface FindQueryHTTP { | ||
| perPage?: number; | ||
| page?: number; | ||
| type: string | string[]; | ||
| // TODO: Fix. this API allows writing an arbitrary query that is passed straight to our persistence layer, thus leaking SO attributes to the public... | ||
| search?: string; | ||
| defaultSearchOperator?: FindSearchOperatorHTTP; | ||
| // TODO: Fix. this API allows sorting by any field, thus leaking SO attributes to the public... | ||
| sortField?: string; | ||
| sortOrder?: FindSortOrderHTTP; | ||
| hasReference?: ReferenceHTTP | ReferenceHTTP[]; | ||
| hasReferenceOperator?: FindSearchOperatorHTTP; | ||
| // TODO: Fix. This exposes attribute schemas to clients. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comments like this represent tech debt that needs to be addressed to be fully compatible with versioned HTTP APIs. Need to dig a bit deeper into current use cases of each of these so deferring to follow up PRs.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of those comments are relatively old. I agree though, we should resolve as much tech debt as possible now. |
||
| fields?: string | string[]; | ||
| } | ||
|
|
||
| export interface FindResponseHTTP { | ||
| saved_objects: SavedObjectWithMetadata[]; | ||
| total: number; | ||
| page: number; | ||
| per_page: number; | ||
| } | ||
|
|
||
| export interface GetAllowedTypesResponseHTTP { | ||
| types: SavedObjectManagementTypeInfo[]; | ||
| } | ||
|
|
||
| export interface RelationshipsParamsHTTP { | ||
| type: string; | ||
| id: string; | ||
| } | ||
|
|
||
| export interface RelationshipsQueryHTTP { | ||
| size: number; | ||
| savedObjectTypes: string | string[]; | ||
| } | ||
|
|
||
| export interface RelationshipsResponseHTTP { | ||
| relations: SavedObjectRelation[]; | ||
| invalidRelations: SavedObjectInvalidRelation[]; | ||
| } | ||
|
|
||
| export interface ScrollCountBodyHTTP { | ||
| typesToInclude: string[]; | ||
| // TODO: Fix. this API allows writing an arbitrary query that is passed straight to our persistence layer, thus leaking SO attributes to the public... | ||
| searchString?: string; | ||
| references?: Array<{ type: string; id: string }>; | ||
| } | ||
|
|
||
| export interface DeleteObjectBodyHTTP { | ||
| id: string; | ||
| type: string; | ||
| } | ||
|
|
||
| export interface DeleteObjectResponseHTTP { | ||
| id: string; | ||
| } | ||
|
|
||
| /** | ||
| * In this case "string" is a direct mapping from "typesToInlcude" in {@link ScrollCountBodyHTTP['typesToInclude']']} | ||
| */ | ||
| export type ScrollCountResponseHTTP = Record<string, number>; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,24 +6,14 @@ | |
| * Side Public License, v 1. | ||
| */ | ||
|
|
||
| import { HttpStart } from '@kbn/core/public'; | ||
| import { SavedObjectError, SavedObjectTypeIdTuple } from '@kbn/core-saved-objects-common'; | ||
|
|
||
| interface SavedObjectDeleteStatus { | ||
| id: string; | ||
| success: boolean; | ||
| type: string; | ||
| error?: SavedObjectError; | ||
| } | ||
| import type { HttpStart } from '@kbn/core/public'; | ||
| import type { v1 } from '../../common'; | ||
|
|
||
| export function bulkDeleteObjects( | ||
| http: HttpStart, | ||
| objects: SavedObjectTypeIdTuple[] | ||
| ): Promise<SavedObjectDeleteStatus[]> { | ||
| return http.post<SavedObjectDeleteStatus[]>( | ||
| '/internal/kibana/management/saved_objects/_bulk_delete', | ||
| { | ||
| body: JSON.stringify(objects), | ||
| } | ||
| ); | ||
| objects: v1.BulkDeleteBodyHTTP | ||
| ): Promise<v1.BulkDeleteResponseHTTP> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we indicate these are for internal purposes only? |
||
| return http.post('/internal/kibana/management/saved_objects/_bulk_delete', { | ||
| body: JSON.stringify(objects), | ||
| }); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the types that don't have the
V1suffix not expected to change at all? I'd expect to see all the types and interfaces declared in this file to only be the ones that change.Shared types/interfaces that we can guarantee will never change could live elsewhere, for example, in the main
typesfile.Keeping the versioned-types file as small as possible helps us monitor changes over time.