Saved objects: improve typesafety (#140099)

* Remove SavedObjectAttributes from examples

* Remove SavedObjectAttributes from dev_docs

* Deprecate SavedObjectAttributes type

* Remove SavedObjectAttributes from kibana_usage_collection plugin

* Remove low hanging SavedObjectAttributes in security_solution

* Remove low hanging SavedObjectAttributes in upgrade_assistant

* Remove low hanging SavedObjectAttributes in lens

* Stricter types for SavedObjectsServiceSetup.registerType

* Review feedback

* Some more low hanging fruit

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Rudolf Meijering 2022-09-13 09:56:29 +02:00 committed by GitHub
parent c9628fc0be
commit 191bfbc97e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 47 additions and 89 deletions

View file

@ -688,11 +688,10 @@ reflected in the mock.
// src/plugins/myplugin/public/saved_query_service.ts
import {
SavedObjectsClientContract,
SavedObjectAttributes,
SimpleSavedObject,
} from 'src/core/public';
export type SavedQueryAttributes = SavedObjectAttributes & {
export type SavedQueryAttributes = {
title: string;
description: 'bar';
query: {

View file

@ -5,12 +5,9 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SavedObjectAttributes } from '@kbn/core/types';
export const BOOK_SAVED_OBJECT = 'book';
export interface BookSavedObjectAttributes extends SavedObjectAttributes {
export interface BookSavedObjectAttributes {
title: string;
author?: string;
readIt?: boolean;

View file

@ -6,9 +6,7 @@
* Side Public License, v 1.
*/
import type { SavedObjectAttributes } from '@kbn/core/types';
export interface TodoSavedObjectAttributes extends SavedObjectAttributes {
export interface TodoSavedObjectAttributes {
task: string;
icon?: string;
title?: string;

View file

@ -19,7 +19,7 @@ export interface SavedObjectsBulkUpdateObject<T = unknown>
id: string;
/** The type of this Saved Object. Each plugin can define it's own custom Saved Object types. */
type: string;
/** {@inheritdoc SavedObjectAttributes} */
/** The data for a Saved Object is stored as an object in the `attributes` property. **/
attributes: Partial<T>;
/**
* Optional namespace string to use when searching for this object. If this is defined, it will supersede the namespace ID that is in

View file

@ -31,6 +31,7 @@ export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttri
* property.
*
* @public
* @deprecated This type reduces the type safety of your code. Create an interface for your specific saved object type or use `unknown` instead.
*/
export interface SavedObjectAttributes {
[key: string]: SavedObjectAttribute;
@ -76,7 +77,7 @@ export interface SavedObject<T = unknown> {
/** Timestamp of the last time this document had been updated. */
updated_at?: string;
error?: SavedObjectError;
/** {@inheritdoc SavedObjectAttributes} */
/** The data for a Saved Object is stored as an object in the `attributes` property. **/
attributes: T;
/** {@inheritdoc SavedObjectReference} */
references: SavedObjectReference[];

View file

@ -7,7 +7,6 @@
*/
import type { KibanaRequest } from '@kbn/core-http-server';
import type { SavedObjectAttributes } from '@kbn/core-saved-objects-common';
import type {
SavedObjectsClientContract,
ISavedObjectsRepository,
@ -124,9 +123,7 @@ export interface SavedObjectsServiceSetup {
* }
* ```
*/
registerType: <Attributes extends SavedObjectAttributes = any>(
type: SavedObjectsType<Attributes>
) => void;
registerType: <Attributes = unknown>(type: SavedObjectsType<Attributes>) => void;
/**
* Returns the default index used for saved objects.

View file

@ -17,7 +17,7 @@ export const defaultEmbeddableFactoryProvider = <
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
T extends SavedObjectAttributes = SavedObjectAttributes
T = SavedObjectAttributes
>(
def: EmbeddableFactoryDefinition<I, O, E, T>
): EmbeddableFactory<I, O, E, T> => {

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { SavedObjectAttributes } from '@kbn/core/public';
import { SavedObjectMetaData } from '@kbn/saved-objects-plugin/public';
import { PersistableState } from '@kbn/kibana-utils-plugin/common';
import { UiActionsPresentableGrouping } from '@kbn/ui-actions-plugin/public';
@ -35,7 +34,7 @@ export interface EmbeddableFactory<
TEmbeddableInput,
TEmbeddableOutput
>,
TSavedObjectAttributes extends SavedObjectAttributes = SavedObjectAttributes
TSavedObjectAttributes = unknown
> extends PersistableState<EmbeddableStateWithType> {
// A unique identified for this factory, which will be used to map an embeddable spec to
// a factory that can generate an instance of it.

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import type { SavedObjectAttributes } from '@kbn/core/server';
import { IEmbeddable } from './i_embeddable';
import { EmbeddableFactory } from './embeddable_factory';
import { EmbeddableInput, EmbeddableOutput } from '..';
@ -15,7 +14,7 @@ export type EmbeddableFactoryDefinition<
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
T extends SavedObjectAttributes = SavedObjectAttributes
T = unknown
> =
// Required parameters
Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> &

View file

@ -34,7 +34,7 @@ export type EmbeddableFactoryProvider = <
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
T extends SavedObjectAttributes = SavedObjectAttributes
T = SavedObjectAttributes
>(
def: EmbeddableFactoryDefinition<I, O, E, T>
) => EmbeddableFactory<I, O, E, T>;

View file

@ -6,12 +6,12 @@
* Side Public License, v 1.
*/
import type { SavedObjectAttributes, SavedObjectsServiceSetup } from '@kbn/core/server';
import type { SavedObjectsServiceSetup } from '@kbn/core/server';
/**
* Used for accumulating the totals of all the stats older than 90d
*/
export interface ApplicationUsageTotal extends SavedObjectAttributes {
export interface ApplicationUsageTotal {
appId: string;
viewId: string;
minutesOnScreen: number;

View file

@ -6,16 +6,12 @@
* Side Public License, v 1.
*/
import type {
SavedObjectAttributes,
SavedObjectsServiceSetup,
ISavedObjectsRepository,
} from '@kbn/core/server';
import type { SavedObjectsServiceSetup, ISavedObjectsRepository } from '@kbn/core/server';
import moment from 'moment';
import type { IntervalHistogram } from '@kbn/core/server';
export const SAVED_OBJECTS_DAILY_TYPE = 'event_loop_delays_daily';
export interface EventLoopDelaysDaily extends SavedObjectAttributes, IntervalHistogram {
export interface EventLoopDelaysDaily extends IntervalHistogram {
processId: number;
instanceUuid: string;
}

View file

@ -6,15 +6,11 @@
* Side Public License, v 1.
*/
import {
ISavedObjectsRepository,
SavedObjectAttributes,
SavedObjectsServiceSetup,
} from '@kbn/core/server';
import { ISavedObjectsRepository, SavedObjectsServiceSetup } from '@kbn/core/server';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
import { uiMetricSchema } from './schema';
interface UIMetricsSavedObjects extends SavedObjectAttributes {
interface UIMetricsSavedObjects {
count: number;
}

View file

@ -9,7 +9,6 @@
import type {
SavedObject,
SavedObjectsRepository,
SavedObjectAttributes,
SavedObjectsServiceSetup,
} from '@kbn/core/server';
import moment from 'moment';
@ -18,7 +17,7 @@ import type { CounterMetric } from './usage_counter';
/**
* The attributes stored in the UsageCounters' SavedObjects
*/
export interface UsageCountersSavedObjectAttributes extends SavedObjectAttributes {
export interface UsageCountersSavedObjectAttributes {
/** The domain ID registered in the Usage Counter **/
domainId: string;
/** The counter name **/

View file

@ -16,7 +16,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
// example of a SO type that will mutates its properties
// during the export transform
savedObjects.registerType({
savedObjects.registerType<{ title: string; enabled: boolean }>({
name: 'test-export-transform',
hidden: false,
namespaceType: 'single',
@ -46,7 +46,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
// example of a SO type that will add additional objects
// to the export during the export transform
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test-export-add',
hidden: false,
namespaceType: 'single',
@ -74,7 +74,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
// dependency of `test_export_transform_2` that will be included
// when exporting them
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test-export-add-dep',
hidden: false,
namespaceType: 'single',
@ -91,7 +91,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
});
// example of a SO type that will throw an object-transform-error
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test-export-transform-error',
hidden: false,
namespaceType: 'single',
@ -111,7 +111,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
});
// example of a SO type that will throw an invalid-transform-error
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test-export-invalid-transform',
hidden: false,
namespaceType: 'single',
@ -134,7 +134,7 @@ export class SavedObjectExportTransformsPlugin implements Plugin {
});
// example of a SO type that is exportable while being hidden
savedObjects.registerType({
savedObjects.registerType<{ title: string; enabled: boolean }>({
name: 'test-actions-export-hidden',
hidden: true,
namespaceType: 'single',

View file

@ -10,7 +10,7 @@ import { Plugin, CoreSetup } from '@kbn/core/server';
export class SavedObjectImportWarningsPlugin implements Plugin {
public setup({ savedObjects }: CoreSetup, deps: {}) {
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test_import_warning_1',
hidden: false,
namespaceType: 'single',
@ -31,7 +31,7 @@ export class SavedObjectImportWarningsPlugin implements Plugin {
},
});
savedObjects.registerType({
savedObjects.registerType<{ title: string }>({
name: 'test_import_warning_2',
hidden: false,
namespaceType: 'single',

View file

@ -78,7 +78,7 @@ import {
// We'll set this max setting assuming it's never reached.
export const MAX_ACTIONS_RETURNED = 10000;
interface ActionUpdate extends SavedObjectAttributes {
interface ActionUpdate {
name: string;
config: SavedObjectAttributes;
secrets: SavedObjectAttributes;

View file

@ -6,7 +6,6 @@
*/
import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/public';
import { SavedObjectAttributes } from '@kbn/core/public';
import {
EmbeddableFactory,
EmbeddableFactoryDefinition,
@ -77,7 +76,7 @@ export class EmbeddableEnhancedPlugin
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
T extends SavedObjectAttributes = SavedObjectAttributes
T = unknown
>(
def: EmbeddableFactoryDefinition<I, O, E, T>
): EmbeddableFactory<I, O, E, T> => {

View file

@ -7,7 +7,6 @@
import { Filter, Query } from '@kbn/es-query';
import {
SavedObjectAttributes,
SavedObjectsClientContract,
SavedObjectReference,
ResolvedSimpleSavedObject,
@ -56,9 +55,7 @@ export class SavedObjectIndexStore implements SavedObjectStore {
save = async (vis: Document) => {
const { savedObjectId, type, references, ...rest } = vis;
// TODO: SavedObjectAttributes should support this kind of object,
// remove this workaround when SavedObjectAttributes is updated.
const attributes = rest as unknown as SavedObjectAttributes;
const attributes = rest;
const result = await this.client.create(
DOC_TYPE,

View file

@ -5,14 +5,10 @@
* 2.0.
*/
import type {
SavedObjectsClientContract,
SimpleSavedObject,
SavedObjectAttributes,
} from '@kbn/core/public';
import type { SavedObjectsClientContract, SimpleSavedObject } from '@kbn/core/public';
/** Returns an object matching a given title */
export async function findObjectByTitle<T extends SavedObjectAttributes>(
export async function findObjectByTitle<T>(
savedObjectsClient: SavedObjectsClientContract,
type: string,
title: string

View file

@ -19,7 +19,7 @@ export function setupSavedObjects(
core: CoreSetup,
getFilterMigrations: () => MigrateFunctionsObject
) {
core.savedObjects.registerType({
core.savedObjects.registerType<MapSavedObjectAttributes>({
name: 'map',
hidden: false,
namespaceType: 'multiple-isolated',

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import type { SavedObjectAttributes } from '@kbn/core/types';
import { BadRequestError } from '@kbn/securitysolution-es-utils';
import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils';
import { fold } from 'fp-ts/lib/Either';
@ -56,7 +55,7 @@ export const validateAllPrepackagedRules = (
* Validate the rules from Saved Objects created by Fleet.
*/
export const validateAllRuleSavedObjects = (
rules: Array<IRuleAssetSOAttributes & SavedObjectAttributes>
rules: IRuleAssetSOAttributes[]
): AddPrepackagedRulesSchema[] => {
return rules.map((rule) => {
const decoded = addPrepackagedRulesSchema.decode(rule);

View file

@ -7,7 +7,7 @@
import type { Readable } from 'stream';
import type { SavedObjectAttributes, SavedObjectsClientContract } from '@kbn/core/server';
import type { SavedObjectsClientContract } from '@kbn/core/server';
import type { SanitizedRule } from '@kbn/alerting-plugin/common';
import type { RulesClient, PartialRule } from '@kbn/alerting-plugin/server';
import { ruleTypeMappings } from '@kbn/securitysolution-rules';
@ -43,7 +43,7 @@ export interface IRuleAssetSOAttributes extends Record<string, any> {
export interface IRuleAssetSavedObject {
type: string;
id: string;
attributes: IRuleAssetSOAttributes & SavedObjectAttributes;
attributes: IRuleAssetSOAttributes;
}
export interface HapiReadableStream extends Readable {

View file

@ -258,19 +258,11 @@ export const convertSavedObjectToSavedNote = (
}, identity)
);
// we have to use any here because the SavedObjectAttributes interface is like below
// export interface SavedObjectAttributes {
// [key: string]: SavedObjectAttributes | string | number | boolean | null;
// }
// then this interface does not allow types without index signature
// this is limiting us with our type for now so the easy way was to use any
const pickSavedNote = (
noteId: string | null,
savedNote: SavedNote,
userInfo: AuthenticatedUser | null
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): any => {
) => {
if (noteId == null) {
savedNote.created = new Date().valueOf();
savedNote.createdBy = userInfo?.username ?? UNAUTHENTICATED_USER;

View file

@ -254,19 +254,11 @@ export const convertSavedObjectToSavedPinnedEvent = (
}, identity)
);
// we have to use any here because the SavedObjectAttributes interface is like below
// export interface SavedObjectAttributes {
// [key: string]: SavedObjectAttributes | string | number | boolean | null;
// }
// then this interface does not allow types without index signature
// this is limiting us with our type for now so the easy way was to use any
export const pickSavedPinnedEvent = (
pinnedEventId: string | null,
savedPinnedEvent: SavedPinnedEvent,
userInfo: AuthenticatedUser | null
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): any => {
) => {
const dateNow = new Date().valueOf();
if (pinnedEventId == null) {
savedPinnedEvent.created = dateNow;

View file

@ -100,7 +100,9 @@ export const hydrateSavedObjects = async ({
}
});
await server.authSavedObjectsClient?.bulkUpdate(updatedObjects);
await server.authSavedObjectsClient?.bulkUpdate<DecryptedSyntheticsMonitorSavedObject>(
updatedObjects
);
}
} catch (e) {
server.logger.error(e);

View file

@ -6,7 +6,7 @@
*/
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { SavedObject, SavedObjectAttributes } from '@kbn/core/types';
import { SavedObject } from '@kbn/core/types';
export type DeprecationSource = 'Kibana' | 'Elasticsearch';
@ -57,7 +57,7 @@ export interface ReindexStatusResponse {
export const REINDEX_OP_TYPE = 'upgrade-assistant-reindex-operation';
export interface QueueSettings extends SavedObjectAttributes {
export interface QueueSettings {
/**
* A Unix timestamp of when the reindex operation was enqueued.
*
@ -81,7 +81,7 @@ export interface QueueSettings extends SavedObjectAttributes {
startedAt?: number;
}
export interface ReindexOptions extends SavedObjectAttributes {
export interface ReindexOptions {
/**
* Whether to treat the index as if it were closed. This instructs the
* reindex strategy to first open the index, perform reindexing and
@ -96,7 +96,7 @@ export interface ReindexOptions extends SavedObjectAttributes {
queueSettings?: QueueSettings;
}
export interface ReindexOperation extends SavedObjectAttributes {
export interface ReindexOperation {
indexName: string;
newIndexName: string;
status: ReindexStatus;
@ -241,7 +241,7 @@ export interface ResolveIndexResponseFromES {
export const ML_UPGRADE_OP_TYPE = 'upgrade-assistant-ml-upgrade-operation';
export interface MlOperation extends SavedObjectAttributes {
export interface MlOperation {
nodeId: string;
snapshotId: string;
jobId: string;

View file

@ -12,7 +12,7 @@ export class Plugin {
public setup(core: CoreSetup) {
// called when plugin is setting up during Kibana's startup sequence
core.savedObjects.registerType({
core.savedObjects.registerType<{ title: string }>({
name: 'sharedtype',
hidden: false,
namespaceType: 'multiple',
@ -29,7 +29,7 @@ export class Plugin {
},
},
});
core.savedObjects.registerType({
core.savedObjects.registerType<{ title: string }>({
name: 'isolatedtype',
hidden: false,
namespaceType: 'single',