mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Remove legacy SavedObjects (#76852)
* remove legacy SO integration * cleanup integration in the legacy platorm * remove so schema * update docs * remove leftovers, update docs * update docs after merge master
This commit is contained in:
parent
bb2aa42e39
commit
bcaffba12b
44 changed files with 47 additions and 2026 deletions
|
@ -16,8 +16,6 @@ export interface SavedObjectsServiceSetup
|
|||
|
||||
When plugins access the Saved Objects client, a new client is created using the factory provided to `setClientFactory` and wrapped by all wrappers registered through `addClientWrapper`<!-- -->.
|
||||
|
||||
All the setup APIs will throw if called after the service has started, and therefor cannot be used from legacy plugin code. Legacy plugins should use the legacy savedObject service until migrated.
|
||||
|
||||
## Example 1
|
||||
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@ See the [mappings format](./kibana-plugin-core-server.savedobjectstypemappingdef
|
|||
registerType: (type: SavedObjectsType) => void;
|
||||
```
|
||||
|
||||
## Remarks
|
||||
|
||||
The type definition is an aggregation of the legacy savedObjects `schema`<!-- -->, `mappings` and `migration` concepts. This API is the single entry point to register saved object types in the new platform.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
|
|
2
kibana.d.ts
vendored
2
kibana.d.ts
vendored
|
@ -39,8 +39,6 @@ export namespace Legacy {
|
|||
export type KibanaConfig = LegacyKibanaServer.KibanaConfig;
|
||||
export type Request = LegacyKibanaServer.Request;
|
||||
export type ResponseToolkit = LegacyKibanaServer.ResponseToolkit;
|
||||
export type SavedObjectsClient = LegacyKibanaServer.SavedObjectsClient;
|
||||
export type SavedObjectsService = LegacyKibanaServer.SavedObjectsLegacyService;
|
||||
export type Server = LegacyKibanaServer.Server;
|
||||
|
||||
export type InitPluginFunction = LegacyKibanaPluginSpec.InitPluginFunction;
|
||||
|
|
|
@ -266,9 +266,7 @@ export {
|
|||
SavedObjectUnsanitizedDoc,
|
||||
SavedObjectsRepositoryFactory,
|
||||
SavedObjectsResolveImportErrorsOptions,
|
||||
SavedObjectsSchema,
|
||||
SavedObjectsSerializer,
|
||||
SavedObjectsLegacyService,
|
||||
SavedObjectsUpdateOptions,
|
||||
SavedObjectsUpdateResponse,
|
||||
SavedObjectsAddToNamespacesOptions,
|
||||
|
|
|
@ -24,13 +24,7 @@ type LegacyServiceMock = jest.Mocked<PublicMethodsOf<LegacyService> & { legacyId
|
|||
|
||||
const createDiscoverPluginsMock = (): LegacyServiceDiscoverPlugins => ({
|
||||
pluginSpecs: [],
|
||||
uiExports: {
|
||||
savedObjectSchemas: {},
|
||||
savedObjectMappings: [],
|
||||
savedObjectMigrations: {},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
},
|
||||
uiExports: {},
|
||||
navLinks: [],
|
||||
pluginExtendedConfig: {
|
||||
get: jest.fn(),
|
||||
|
|
|
@ -341,11 +341,9 @@ export class LegacyService implements CoreService {
|
|||
registerStaticDir: setupDeps.core.http.registerStaticDir,
|
||||
},
|
||||
hapiServer: setupDeps.core.http.server,
|
||||
kibanaMigrator: startDeps.core.savedObjects.migrator,
|
||||
uiPlugins: setupDeps.uiPlugins,
|
||||
elasticsearch: setupDeps.core.elasticsearch,
|
||||
rendering: setupDeps.core.rendering,
|
||||
savedObjectsClientProvider: startDeps.core.savedObjects.clientProvider,
|
||||
legacy: this.legacyInternals,
|
||||
},
|
||||
logger: this.coreContext.logger,
|
||||
|
|
|
@ -24,7 +24,6 @@ import { KibanaRequest, LegacyRequest } from '../http';
|
|||
import { InternalCoreSetup, InternalCoreStart } from '../internal_types';
|
||||
import { PluginsServiceSetup, PluginsServiceStart, UiPlugins } from '../plugins';
|
||||
import { InternalRenderingServiceSetup } from '../rendering';
|
||||
import { SavedObjectsLegacyUiExports } from '../types';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -128,13 +127,13 @@ export type LegacyNavLink = Omit<ChromeNavLink, 'baseUrl' | 'legacy' | 'order' |
|
|||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export type LegacyUiExports = SavedObjectsLegacyUiExports & {
|
||||
export interface LegacyUiExports {
|
||||
defaultInjectedVarProviders?: VarsProvider[];
|
||||
injectedVarsReplacers?: VarsReplacer[];
|
||||
navLinkSpecs?: LegacyNavLinkSpec[] | null;
|
||||
uiAppSpecs?: Array<LegacyAppSpec | undefined>;
|
||||
unknown?: [{ pluginSpec: LegacyPluginSpec; type: unknown }];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`convertLegacyTypes converts the legacy mappings using default values if no schemas are specified 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldA": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeA",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldB": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeB",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldC": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeC",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`convertLegacyTypes merges everything when all are present 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": true,
|
||||
"indexPattern": "myIndex",
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldA": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {
|
||||
"1.0.0": [Function],
|
||||
"2.0.4": [Function],
|
||||
},
|
||||
"name": "typeA",
|
||||
"namespaceType": "agnostic",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": "some alias script",
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"anotherFieldB": Object {
|
||||
"type": "boolean",
|
||||
},
|
||||
"fieldB": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeB",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldC": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {
|
||||
"1.5.3": [Function],
|
||||
},
|
||||
"name": "typeC",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`convertLegacyTypes merges the mappings and the schema to create the type when schema exists for the type 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": true,
|
||||
"indexPattern": "fooBar",
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldA": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeA",
|
||||
"namespaceType": "agnostic",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": "barBaz",
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldB": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeB",
|
||||
"namespaceType": "multiple",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": undefined,
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldC": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeC",
|
||||
"namespaceType": "single",
|
||||
},
|
||||
Object {
|
||||
"convertToAliasScript": undefined,
|
||||
"hidden": false,
|
||||
"indexPattern": "bazQux",
|
||||
"management": undefined,
|
||||
"mappings": Object {
|
||||
"properties": Object {
|
||||
"fieldD": Object {
|
||||
"type": "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
"migrations": Object {},
|
||||
"name": "typeD",
|
||||
"namespaceType": "agnostic",
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
export * from './service';
|
||||
|
||||
export { SavedObjectsSchema } from './schema';
|
||||
|
||||
export * from './import';
|
||||
|
||||
export {
|
||||
|
|
|
@ -48,7 +48,6 @@ describe('DocumentMigrator', () => {
|
|||
return {
|
||||
kibanaVersion: '25.2.3',
|
||||
typeRegistry: createRegistry(),
|
||||
validateDoc: _.noop,
|
||||
log: mockLogger,
|
||||
};
|
||||
}
|
||||
|
@ -60,7 +59,6 @@ describe('DocumentMigrator', () => {
|
|||
name: 'foo',
|
||||
migrations: _.noop as any,
|
||||
}),
|
||||
validateDoc: _.noop,
|
||||
log: mockLogger,
|
||||
};
|
||||
expect(() => new DocumentMigrator(invalidDefinition)).toThrow(
|
||||
|
@ -77,7 +75,6 @@ describe('DocumentMigrator', () => {
|
|||
bar: (doc) => doc,
|
||||
},
|
||||
}),
|
||||
validateDoc: _.noop,
|
||||
log: mockLogger,
|
||||
};
|
||||
expect(() => new DocumentMigrator(invalidDefinition)).toThrow(
|
||||
|
@ -94,7 +91,6 @@ describe('DocumentMigrator', () => {
|
|||
'1.2.3': 23 as any,
|
||||
},
|
||||
}),
|
||||
validateDoc: _.noop,
|
||||
log: mockLogger,
|
||||
};
|
||||
expect(() => new DocumentMigrator(invalidDefinition)).toThrow(
|
||||
|
@ -633,27 +629,6 @@ describe('DocumentMigrator', () => {
|
|||
bbb: '3.2.3',
|
||||
});
|
||||
});
|
||||
|
||||
test('fails if the validate doc throws', () => {
|
||||
const migrator = new DocumentMigrator({
|
||||
...testOpts(),
|
||||
typeRegistry: createRegistry({
|
||||
name: 'aaa',
|
||||
migrations: {
|
||||
'2.3.4': (d) => set(d, 'attributes.counter', 42),
|
||||
},
|
||||
}),
|
||||
validateDoc: (d) => {
|
||||
if ((d.attributes as any).counter === 42) {
|
||||
throw new Error('Meaningful!');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const doc = { id: '1', type: 'foo', attributes: {}, migrationVersion: {}, aaa: {} };
|
||||
|
||||
expect(() => migrator.migrate(doc)).toThrow(/Meaningful/);
|
||||
});
|
||||
});
|
||||
|
||||
function renameAttr(path: string, newPath: string) {
|
||||
|
|
|
@ -73,12 +73,9 @@ import { SavedObjectMigrationFn } from '../types';
|
|||
|
||||
export type TransformFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc;
|
||||
|
||||
type ValidateDoc = (doc: SavedObjectUnsanitizedDoc) => void;
|
||||
|
||||
interface DocumentMigratorOptions {
|
||||
kibanaVersion: string;
|
||||
typeRegistry: ISavedObjectTypeRegistry;
|
||||
validateDoc: ValidateDoc;
|
||||
log: Logger;
|
||||
}
|
||||
|
||||
|
@ -113,19 +110,16 @@ export class DocumentMigrator implements VersionedTransformer {
|
|||
* @param {DocumentMigratorOptions} opts
|
||||
* @prop {string} kibanaVersion - The current version of Kibana
|
||||
* @prop {SavedObjectTypeRegistry} typeRegistry - The type registry to get type migrations from
|
||||
* @prop {ValidateDoc} validateDoc - A function which, given a document throws an error if it is
|
||||
* not up to date. This is used to ensure we don't let unmigrated documents slip through.
|
||||
* @prop {Logger} log - The migration logger
|
||||
* @memberof DocumentMigrator
|
||||
*/
|
||||
constructor({ typeRegistry, kibanaVersion, log, validateDoc }: DocumentMigratorOptions) {
|
||||
constructor({ typeRegistry, kibanaVersion, log }: DocumentMigratorOptions) {
|
||||
validateMigrationDefinition(typeRegistry);
|
||||
|
||||
this.migrations = buildActiveMigrations(typeRegistry, log);
|
||||
this.transformDoc = buildDocumentTransform({
|
||||
kibanaVersion,
|
||||
migrations: this.migrations,
|
||||
validateDoc,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -231,21 +225,16 @@ function buildActiveMigrations(
|
|||
* Creates a function which migrates and validates any document that is passed to it.
|
||||
*/
|
||||
function buildDocumentTransform({
|
||||
kibanaVersion,
|
||||
migrations,
|
||||
validateDoc,
|
||||
}: {
|
||||
kibanaVersion: string;
|
||||
migrations: ActiveMigrations;
|
||||
validateDoc: ValidateDoc;
|
||||
}): TransformFn {
|
||||
return function transformAndValidate(doc: SavedObjectUnsanitizedDoc) {
|
||||
const result = doc.migrationVersion
|
||||
? applyMigrations(doc, migrations)
|
||||
: markAsUpToDate(doc, migrations);
|
||||
|
||||
validateDoc(result);
|
||||
|
||||
// In order to keep tests a bit more stable, we won't
|
||||
// tack on an empy migrationVersion to docs that have
|
||||
// no migrations defined.
|
||||
|
|
|
@ -134,7 +134,6 @@ const mockOptions = () => {
|
|||
const options: MockedOptions = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
kibanaVersion: '8.2.3',
|
||||
savedObjectValidations: {},
|
||||
typeRegistry: createRegistry([
|
||||
{
|
||||
name: 'testtype',
|
||||
|
|
|
@ -28,7 +28,6 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import { Logger } from '../../../logging';
|
||||
import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings';
|
||||
import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization';
|
||||
import { docValidator, PropertyValidators } from '../../validation';
|
||||
import { buildActiveMappings, IndexMigrator, MigrationResult, MigrationStatus } from '../core';
|
||||
import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator';
|
||||
import { MigrationEsClient } from '../core/';
|
||||
|
@ -44,7 +43,6 @@ export interface KibanaMigratorOptions {
|
|||
kibanaConfig: KibanaConfigType;
|
||||
kibanaVersion: string;
|
||||
logger: Logger;
|
||||
savedObjectValidations: PropertyValidators;
|
||||
}
|
||||
|
||||
export type IKibanaMigrator = Pick<KibanaMigrator, keyof KibanaMigrator>;
|
||||
|
@ -80,7 +78,6 @@ export class KibanaMigrator {
|
|||
typeRegistry,
|
||||
kibanaConfig,
|
||||
savedObjectsConfig,
|
||||
savedObjectValidations,
|
||||
kibanaVersion,
|
||||
logger,
|
||||
}: KibanaMigratorOptions) {
|
||||
|
@ -94,7 +91,6 @@ export class KibanaMigrator {
|
|||
this.documentMigrator = new DocumentMigrator({
|
||||
kibanaVersion,
|
||||
typeRegistry,
|
||||
validateDoc: docValidator(savedObjectValidations || {}),
|
||||
log: this.log,
|
||||
});
|
||||
// Building the active mappings (and associated md5sums) is an expensive
|
||||
|
|
|
@ -26,8 +26,7 @@ import {
|
|||
SavedObjectsServiceSetup,
|
||||
SavedObjectsServiceStart,
|
||||
} from './saved_objects_service';
|
||||
import { mockKibanaMigrator } from './migrations/kibana/kibana_migrator.mock';
|
||||
import { savedObjectsClientProviderMock } from './service/lib/scoped_client_provider.mock';
|
||||
|
||||
import { savedObjectsRepositoryMock } from './service/lib/repository.mock';
|
||||
import { savedObjectsClientMock } from './service/saved_objects_client.mock';
|
||||
import { typeRegistryMock } from './saved_objects_type_registry.mock';
|
||||
|
@ -54,11 +53,7 @@ const createStartContractMock = () => {
|
|||
};
|
||||
|
||||
const createInternalStartContractMock = () => {
|
||||
const internalStartContract: jest.Mocked<InternalSavedObjectsServiceStart> = {
|
||||
...createStartContractMock(),
|
||||
clientProvider: savedObjectsClientProviderMock.create(),
|
||||
migrator: mockKibanaMigrator.create(),
|
||||
};
|
||||
const internalStartContract: jest.Mocked<InternalSavedObjectsServiceStart> = createStartContractMock();
|
||||
|
||||
return internalStartContract;
|
||||
};
|
||||
|
|
|
@ -33,7 +33,6 @@ import { Env } from '../config';
|
|||
import { configServiceMock } from '../mocks';
|
||||
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
|
||||
import { elasticsearchClientMock } from '../elasticsearch/client/mocks';
|
||||
import { legacyServiceMock } from '../legacy/legacy_service.mock';
|
||||
import { httpServiceMock } from '../http/http_service.mock';
|
||||
import { httpServerMock } from '../http/http_server.mocks';
|
||||
import { SavedObjectsClientFactoryProvider } from './service/lib';
|
||||
|
@ -65,7 +64,6 @@ describe('SavedObjectsService', () => {
|
|||
return {
|
||||
http: httpServiceMock.createInternalSetupContract(),
|
||||
elasticsearch: elasticsearchMock,
|
||||
legacyPlugins: legacyServiceMock.createDiscoverPlugins(),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -239,8 +237,7 @@ describe('SavedObjectsService', () => {
|
|||
await soService.setup(createSetupDeps());
|
||||
expect(migratorInstanceMock.runMigrations).toHaveBeenCalledTimes(0);
|
||||
|
||||
const startContract = await soService.start(createStartDeps());
|
||||
expect(startContract.migrator).toBe(migratorInstanceMock);
|
||||
await soService.start(createStartDeps());
|
||||
expect(migratorInstanceMock.runMigrations).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
|
|
@ -23,12 +23,10 @@ import { CoreService } from '../../types';
|
|||
import {
|
||||
SavedObjectsClient,
|
||||
SavedObjectsClientProvider,
|
||||
ISavedObjectsClientProvider,
|
||||
SavedObjectsClientProviderOptions,
|
||||
} from './';
|
||||
import { KibanaMigrator, IKibanaMigrator } from './migrations';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { LegacyServiceDiscoverPlugins } from '../legacy';
|
||||
import {
|
||||
ElasticsearchClient,
|
||||
IClusterClient,
|
||||
|
@ -49,9 +47,7 @@ import {
|
|||
SavedObjectsClientWrapperFactory,
|
||||
} from './service/lib/scoped_client_provider';
|
||||
import { Logger } from '../logging';
|
||||
import { convertLegacyTypes } from './utils';
|
||||
import { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';
|
||||
import { PropertyValidators } from './validation';
|
||||
import { SavedObjectsSerializer } from './serialization';
|
||||
import { registerRoutes } from './routes';
|
||||
import { ServiceStatus } from '../status';
|
||||
|
@ -67,9 +63,6 @@ import { createMigrationEsClient } from './migrations/core/';
|
|||
* the factory provided to `setClientFactory` and wrapped by all wrappers
|
||||
* registered through `addClientWrapper`.
|
||||
*
|
||||
* All the setup APIs will throw if called after the service has started, and therefor cannot be used
|
||||
* from legacy plugin code. Legacy plugins should use the legacy savedObject service until migrated.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { SavedObjectsClient, CoreSetup } from 'src/core/server';
|
||||
|
@ -155,9 +148,6 @@ export interface SavedObjectsServiceSetup {
|
|||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @remarks The type definition is an aggregation of the legacy savedObjects `schema`, `mappings` and `migration` concepts.
|
||||
* This API is the single entry point to register saved object types in the new platform.
|
||||
*/
|
||||
registerType: (type: SavedObjectsType) => void;
|
||||
|
||||
|
@ -230,16 +220,7 @@ export interface SavedObjectsServiceStart {
|
|||
getTypeRegistry: () => ISavedObjectTypeRegistry;
|
||||
}
|
||||
|
||||
export interface InternalSavedObjectsServiceStart extends SavedObjectsServiceStart {
|
||||
/**
|
||||
* @deprecated Exposed only for injecting into Legacy
|
||||
*/
|
||||
migrator: IKibanaMigrator;
|
||||
/**
|
||||
* @deprecated Exposed only for injecting into Legacy
|
||||
*/
|
||||
clientProvider: ISavedObjectsClientProvider;
|
||||
}
|
||||
export type InternalSavedObjectsServiceStart = SavedObjectsServiceStart;
|
||||
|
||||
/**
|
||||
* Factory provided when invoking a {@link SavedObjectsClientFactoryProvider | client factory provider}
|
||||
|
@ -271,7 +252,6 @@ export interface SavedObjectsRepositoryFactory {
|
|||
/** @internal */
|
||||
export interface SavedObjectsSetupDeps {
|
||||
http: InternalHttpServiceSetup;
|
||||
legacyPlugins: LegacyServiceDiscoverPlugins;
|
||||
elasticsearch: InternalElasticsearchServiceSetup;
|
||||
}
|
||||
|
||||
|
@ -296,9 +276,8 @@ export class SavedObjectsService
|
|||
private clientFactoryProvider?: SavedObjectsClientFactoryProvider;
|
||||
private clientFactoryWrappers: WrappedClientFactoryWrapper[] = [];
|
||||
|
||||
private migrator$ = new Subject<KibanaMigrator>();
|
||||
private migrator$ = new Subject<IKibanaMigrator>();
|
||||
private typeRegistry = new SavedObjectTypeRegistry();
|
||||
private validations: PropertyValidators = {};
|
||||
private started = false;
|
||||
|
||||
constructor(private readonly coreContext: CoreContext) {
|
||||
|
@ -310,13 +289,6 @@ export class SavedObjectsService
|
|||
|
||||
this.setupDeps = setupDeps;
|
||||
|
||||
const legacyTypes = convertLegacyTypes(
|
||||
setupDeps.legacyPlugins.uiExports,
|
||||
setupDeps.legacyPlugins.pluginExtendedConfig
|
||||
);
|
||||
legacyTypes.forEach((type) => this.typeRegistry.registerType(type));
|
||||
this.validations = setupDeps.legacyPlugins.uiExports.savedObjectValidations || {};
|
||||
|
||||
const savedObjectsConfig = await this.coreContext.configService
|
||||
.atPath<SavedObjectsConfigType>('savedObjects')
|
||||
.pipe(first())
|
||||
|
@ -471,8 +443,6 @@ export class SavedObjectsService
|
|||
this.started = true;
|
||||
|
||||
return {
|
||||
migrator,
|
||||
clientProvider,
|
||||
getScopedClient: clientProvider.getClient.bind(clientProvider),
|
||||
createScopedRepository: repositoryFactory.createScopedRepository,
|
||||
createInternalRepository: repositoryFactory.createInternalRepository,
|
||||
|
@ -488,13 +458,12 @@ export class SavedObjectsService
|
|||
savedObjectsConfig: SavedObjectsMigrationConfigType,
|
||||
client: IClusterClient,
|
||||
migrationsRetryDelay?: number
|
||||
): KibanaMigrator {
|
||||
): IKibanaMigrator {
|
||||
return new KibanaMigrator({
|
||||
typeRegistry: this.typeRegistry,
|
||||
logger: this.logger,
|
||||
kibanaVersion: this.coreContext.env.packageInfo.version,
|
||||
savedObjectsConfig,
|
||||
savedObjectValidations: this.validations,
|
||||
kibanaConfig,
|
||||
client: createMigrationEsClient(client.asInternalUser, this.logger, migrationsRetryDelay),
|
||||
});
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { SavedObjectsSchema, SavedObjectsSchemaDefinition } from './schema';
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsSchema, SavedObjectsSchemaDefinition } from './schema';
|
||||
|
||||
describe('#isNamespaceAgnostic', () => {
|
||||
const expectResult = (expected: boolean, schemaDefinition?: SavedObjectsSchemaDefinition) => {
|
||||
const schema = new SavedObjectsSchema(schemaDefinition);
|
||||
const result = schema.isNamespaceAgnostic('foo');
|
||||
expect(result).toBe(expected);
|
||||
};
|
||||
|
||||
it(`returns false when no schema is defined`, () => {
|
||||
expectResult(false);
|
||||
});
|
||||
|
||||
it(`returns false for unknown types`, () => {
|
||||
expectResult(false, { bar: {} });
|
||||
});
|
||||
|
||||
it(`returns false for non-namespace-agnostic type`, () => {
|
||||
expectResult(false, { foo: { isNamespaceAgnostic: false } });
|
||||
expectResult(false, { foo: { isNamespaceAgnostic: undefined } });
|
||||
});
|
||||
|
||||
it(`returns true for explicitly namespace-agnostic type`, () => {
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: true } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isSingleNamespace', () => {
|
||||
const expectResult = (expected: boolean, schemaDefinition?: SavedObjectsSchemaDefinition) => {
|
||||
const schema = new SavedObjectsSchema(schemaDefinition);
|
||||
const result = schema.isSingleNamespace('foo');
|
||||
expect(result).toBe(expected);
|
||||
};
|
||||
|
||||
it(`returns true when no schema is defined`, () => {
|
||||
expectResult(true);
|
||||
});
|
||||
|
||||
it(`returns true for unknown types`, () => {
|
||||
expectResult(true, { bar: {} });
|
||||
});
|
||||
|
||||
it(`returns false for explicitly namespace-agnostic type`, () => {
|
||||
expectResult(false, { foo: { isNamespaceAgnostic: true } });
|
||||
});
|
||||
|
||||
it(`returns false for explicitly multi-namespace type`, () => {
|
||||
expectResult(false, { foo: { multiNamespace: true } });
|
||||
});
|
||||
|
||||
it(`returns true for non-namespace-agnostic and non-multi-namespace type`, () => {
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: false, multiNamespace: false } });
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: false, multiNamespace: undefined } });
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: undefined, multiNamespace: false } });
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: undefined, multiNamespace: undefined } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isMultiNamespace', () => {
|
||||
const expectResult = (expected: boolean, schemaDefinition?: SavedObjectsSchemaDefinition) => {
|
||||
const schema = new SavedObjectsSchema(schemaDefinition);
|
||||
const result = schema.isMultiNamespace('foo');
|
||||
expect(result).toBe(expected);
|
||||
};
|
||||
|
||||
it(`returns false when no schema is defined`, () => {
|
||||
expectResult(false);
|
||||
});
|
||||
|
||||
it(`returns false for unknown types`, () => {
|
||||
expectResult(false, { bar: {} });
|
||||
});
|
||||
|
||||
it(`returns false for explicitly namespace-agnostic type`, () => {
|
||||
expectResult(false, { foo: { isNamespaceAgnostic: true } });
|
||||
});
|
||||
|
||||
it(`returns false for non-multi-namespace type`, () => {
|
||||
expectResult(false, { foo: { multiNamespace: false } });
|
||||
expectResult(false, { foo: { multiNamespace: undefined } });
|
||||
});
|
||||
|
||||
it(`returns true for non-namespace-agnostic and explicitly multi-namespace type`, () => {
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: false, multiNamespace: true } });
|
||||
expectResult(true, { foo: { isNamespaceAgnostic: undefined, multiNamespace: true } });
|
||||
});
|
||||
});
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { LegacyConfig } from '../../legacy';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal
|
||||
**/
|
||||
interface SavedObjectsSchemaTypeDefinition {
|
||||
isNamespaceAgnostic?: boolean;
|
||||
multiNamespace?: boolean;
|
||||
hidden?: boolean;
|
||||
indexPattern?: ((config: LegacyConfig) => string) | string;
|
||||
convertToAliasScript?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal
|
||||
**/
|
||||
export interface SavedObjectsSchemaDefinition {
|
||||
[type: string]: SavedObjectsSchemaTypeDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This is only used by the {@link SavedObjectsLegacyService | legacy savedObjects service}
|
||||
* @internal
|
||||
**/
|
||||
export class SavedObjectsSchema {
|
||||
private readonly definition?: SavedObjectsSchemaDefinition;
|
||||
constructor(schemaDefinition?: SavedObjectsSchemaDefinition) {
|
||||
this.definition = schemaDefinition;
|
||||
}
|
||||
|
||||
public isHiddenType(type: string) {
|
||||
if (this.definition && this.definition.hasOwnProperty(type)) {
|
||||
return Boolean(this.definition[type].hidden);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getIndexForType(config: LegacyConfig, type: string): string | undefined {
|
||||
if (this.definition != null && this.definition.hasOwnProperty(type)) {
|
||||
const { indexPattern } = this.definition[type];
|
||||
return typeof indexPattern === 'function' ? indexPattern(config) : indexPattern;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public getConvertToAliasScript(type: string): string | undefined {
|
||||
if (this.definition != null && this.definition.hasOwnProperty(type)) {
|
||||
return this.definition[type].convertToAliasScript;
|
||||
}
|
||||
}
|
||||
|
||||
public isNamespaceAgnostic(type: string) {
|
||||
// if no plugins have registered a Saved Objects Schema,
|
||||
// this.schema will be undefined, and no types are namespace agnostic
|
||||
if (!this.definition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const typeSchema = this.definition[type];
|
||||
if (!typeSchema) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(typeSchema.isNamespaceAgnostic);
|
||||
}
|
||||
|
||||
public isSingleNamespace(type: string) {
|
||||
// if no plugins have registered a Saved Objects Schema,
|
||||
// this.schema will be undefined, and all types are namespace isolated
|
||||
if (!this.definition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const typeSchema = this.definition[type];
|
||||
if (!typeSchema) {
|
||||
return true;
|
||||
}
|
||||
return !Boolean(typeSchema.isNamespaceAgnostic) && !Boolean(typeSchema.multiNamespace);
|
||||
}
|
||||
|
||||
public isMultiNamespace(type: string) {
|
||||
// if no plugins have registered a Saved Objects Schema,
|
||||
// this.schema will be undefined, and no types are multi-namespace
|
||||
if (!this.definition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const typeSchema = this.definition[type];
|
||||
if (!typeSchema) {
|
||||
return false;
|
||||
}
|
||||
return !Boolean(typeSchema.isNamespaceAgnostic) && Boolean(typeSchema.multiNamespace);
|
||||
}
|
||||
}
|
|
@ -17,37 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Readable } from 'stream';
|
||||
import { SavedObjectsClientProvider } from './lib';
|
||||
import { SavedObjectsClient } from './saved_objects_client';
|
||||
import { SavedObjectsExportOptions } from '../export';
|
||||
import { SavedObjectsImportOptions, SavedObjectsImportResponse } from '../import';
|
||||
import { SavedObjectsSchema } from '../schema';
|
||||
import { SavedObjectsResolveImportErrorsOptions } from '../import/types';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacyService {
|
||||
// ATTENTION: these types are incomplete
|
||||
addScopedSavedObjectsClientWrapperFactory: SavedObjectsClientProvider['addClientWrapperFactory'];
|
||||
setScopedSavedObjectsClientFactory: SavedObjectsClientProvider['setClientFactory'];
|
||||
getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient'];
|
||||
SavedObjectsClient: typeof SavedObjectsClient;
|
||||
types: string[];
|
||||
schema: SavedObjectsSchema;
|
||||
getSavedObjectsRepository(...rest: any[]): any;
|
||||
importExport: {
|
||||
objectLimit: number;
|
||||
importSavedObjects(options: SavedObjectsImportOptions): Promise<SavedObjectsImportResponse>;
|
||||
resolveImportErrors(
|
||||
options: SavedObjectsResolveImportErrorsOptions
|
||||
): Promise<SavedObjectsImportResponse>;
|
||||
getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise<Readable>;
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
SavedObjectsRepository,
|
||||
SavedObjectsClientProvider,
|
||||
|
|
|
@ -153,7 +153,6 @@ describe('SavedObjectsRepository', () => {
|
|||
typeRegistry: registry,
|
||||
kibanaVersion: '2.0.0',
|
||||
log: {},
|
||||
validateDoc: jest.fn(),
|
||||
});
|
||||
|
||||
const getMockGetResponse = ({ type, id, references, namespace, originId }) => ({
|
||||
|
|
|
@ -31,7 +31,7 @@ import { getSearchDsl } from './search_dsl';
|
|||
import { includedFields } from './included_fields';
|
||||
import { SavedObjectsErrorHelpers, DecoratedError } from './errors';
|
||||
import { decodeRequestVersion, encodeVersion, encodeHitVersion } from '../../version';
|
||||
import { KibanaMigrator } from '../../migrations';
|
||||
import { IKibanaMigrator } from '../../migrations';
|
||||
import {
|
||||
SavedObjectsSerializer,
|
||||
SavedObjectSanitizedDoc,
|
||||
|
@ -85,7 +85,7 @@ export interface SavedObjectsRepositoryOptions {
|
|||
client: ElasticsearchClient;
|
||||
typeRegistry: SavedObjectTypeRegistry;
|
||||
serializer: SavedObjectsSerializer;
|
||||
migrator: KibanaMigrator;
|
||||
migrator: IKibanaMigrator;
|
||||
allowedTypes: string[];
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ export type ISavedObjectsRepository = Pick<SavedObjectsRepository, keyof SavedOb
|
|||
* @public
|
||||
*/
|
||||
export class SavedObjectsRepository {
|
||||
private _migrator: KibanaMigrator;
|
||||
private _migrator: IKibanaMigrator;
|
||||
private _index: string;
|
||||
private _mappings: IndexMapping;
|
||||
private _registry: SavedObjectTypeRegistry;
|
||||
|
@ -137,7 +137,7 @@ export class SavedObjectsRepository {
|
|||
* @internal
|
||||
*/
|
||||
public static createRepository(
|
||||
migrator: KibanaMigrator,
|
||||
migrator: IKibanaMigrator,
|
||||
typeRegistry: SavedObjectTypeRegistry,
|
||||
indexName: string,
|
||||
client: ElasticsearchClient,
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectsClient } from './service/saved_objects_client';
|
||||
import { SavedObjectsTypeMappingDefinition, SavedObjectsTypeMappingDefinitions } from './mappings';
|
||||
import { SavedObjectsTypeMappingDefinition } from './mappings';
|
||||
import { SavedObjectMigrationMap } from './migrations';
|
||||
import { PropertyValidators } from './validation';
|
||||
|
||||
export {
|
||||
SavedObjectsImportResponse,
|
||||
|
@ -34,9 +33,6 @@ export {
|
|||
SavedObjectsImportRetry,
|
||||
} from './import/types';
|
||||
|
||||
import { LegacyConfig } from '../legacy';
|
||||
import { SavedObjectUnsanitizedDoc } from './serialization';
|
||||
import { SavedObjectsMigrationLogger } from './migrations/core/migration_logger';
|
||||
import { SavedObject } from '../../types';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
|
@ -269,92 +265,3 @@ export interface SavedObjectsTypeManagementDefinition {
|
|||
*/
|
||||
getInAppUrl?: (savedObject: SavedObject<any>) => { path: string; uiCapabilitiesPath: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacyUiExports {
|
||||
savedObjectMappings: SavedObjectsLegacyMapping[];
|
||||
savedObjectMigrations: SavedObjectsLegacyMigrationDefinitions;
|
||||
savedObjectSchemas: SavedObjectsLegacySchemaDefinitions;
|
||||
savedObjectValidations: PropertyValidators;
|
||||
savedObjectsManagement: SavedObjectsLegacyManagementDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacyMapping {
|
||||
pluginId: string;
|
||||
properties: SavedObjectsTypeMappingDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated Use {@link SavedObjectsTypeManagementDefinition | management definition} when registering
|
||||
* from new platform plugins
|
||||
*/
|
||||
export interface SavedObjectsLegacyManagementDefinition {
|
||||
[key: string]: SavedObjectsLegacyManagementTypeDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacyManagementTypeDefinition {
|
||||
isImportableAndExportable?: boolean;
|
||||
defaultSearchField?: string;
|
||||
icon?: string;
|
||||
getTitle?: (savedObject: SavedObject<any>) => string;
|
||||
getEditUrl?: (savedObject: SavedObject<any>) => string;
|
||||
getInAppUrl?: (savedObject: SavedObject<any>) => { path: string; uiCapabilitiesPath: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacyMigrationDefinitions {
|
||||
[type: string]: SavedObjectLegacyMigrationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectLegacyMigrationMap {
|
||||
[version: string]: SavedObjectLegacyMigrationFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export type SavedObjectLegacyMigrationFn = (
|
||||
doc: SavedObjectUnsanitizedDoc,
|
||||
log: SavedObjectsMigrationLogger
|
||||
) => SavedObjectUnsanitizedDoc;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
interface SavedObjectsLegacyTypeSchema {
|
||||
isNamespaceAgnostic?: boolean;
|
||||
/** Cannot be used in conjunction with `isNamespaceAgnostic` */
|
||||
multiNamespace?: boolean;
|
||||
hidden?: boolean;
|
||||
indexPattern?: ((config: LegacyConfig) => string) | string;
|
||||
convertToAliasScript?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface SavedObjectsLegacySchemaDefinitions {
|
||||
[type: string]: SavedObjectsLegacyTypeSchema;
|
||||
}
|
||||
|
|
|
@ -1,445 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { legacyServiceMock } from '../legacy/legacy_service.mock';
|
||||
import { convertLegacyTypes, convertTypesToLegacySchema } from './utils';
|
||||
import { SavedObjectsLegacyUiExports, SavedObjectsType } from './types';
|
||||
import { LegacyConfig, SavedObjectMigrationContext } from 'kibana/server';
|
||||
import { SavedObjectUnsanitizedDoc } from './serialization';
|
||||
|
||||
describe('convertLegacyTypes', () => {
|
||||
let legacyConfig: ReturnType<typeof legacyServiceMock.createLegacyConfig>;
|
||||
|
||||
beforeEach(() => {
|
||||
legacyConfig = legacyServiceMock.createLegacyConfig();
|
||||
});
|
||||
|
||||
it('converts the legacy mappings using default values if no schemas are specified', () => {
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
typeB: {
|
||||
properties: {
|
||||
fieldB: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginB',
|
||||
properties: {
|
||||
typeC: {
|
||||
properties: {
|
||||
fieldC: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {},
|
||||
savedObjectSchemas: {},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(converted).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('merges the mappings and the schema to create the type when schema exists for the type', () => {
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginB',
|
||||
properties: {
|
||||
typeB: {
|
||||
properties: {
|
||||
fieldB: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginC',
|
||||
properties: {
|
||||
typeC: {
|
||||
properties: {
|
||||
fieldC: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginD',
|
||||
properties: {
|
||||
typeD: {
|
||||
properties: {
|
||||
fieldD: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {},
|
||||
savedObjectSchemas: {
|
||||
typeA: {
|
||||
indexPattern: 'fooBar',
|
||||
hidden: true,
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
typeB: {
|
||||
indexPattern: 'barBaz',
|
||||
hidden: false,
|
||||
multiNamespace: true,
|
||||
},
|
||||
typeD: {
|
||||
indexPattern: 'bazQux',
|
||||
hidden: false,
|
||||
// if both isNamespaceAgnostic and multiNamespace are true, the resulting namespaceType is 'agnostic'
|
||||
isNamespaceAgnostic: true,
|
||||
multiNamespace: true,
|
||||
},
|
||||
},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(converted).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('invokes indexPattern to retrieve the index when it is a function', () => {
|
||||
const indexPatternAccessor: (config: LegacyConfig) => string = jest.fn((config) => {
|
||||
config.get('foo.bar');
|
||||
return 'myIndex';
|
||||
});
|
||||
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {},
|
||||
savedObjectSchemas: {
|
||||
typeA: {
|
||||
indexPattern: indexPatternAccessor,
|
||||
hidden: true,
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
|
||||
expect(indexPatternAccessor).toHaveBeenCalledWith(legacyConfig);
|
||||
expect(legacyConfig.get).toHaveBeenCalledWith('foo.bar');
|
||||
expect(converted.length).toEqual(1);
|
||||
expect(converted[0].indexPattern).toEqual('myIndex');
|
||||
});
|
||||
|
||||
it('import migrations from the uiExports', () => {
|
||||
const migrationsA = {
|
||||
'1.0.0': jest.fn(),
|
||||
'2.0.4': jest.fn(),
|
||||
};
|
||||
const migrationsB = {
|
||||
'1.5.3': jest.fn(),
|
||||
};
|
||||
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginB',
|
||||
properties: {
|
||||
typeB: {
|
||||
properties: {
|
||||
fieldC: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {
|
||||
typeA: migrationsA,
|
||||
typeB: migrationsB,
|
||||
},
|
||||
savedObjectSchemas: {},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(converted.length).toEqual(2);
|
||||
expect(Object.keys(converted[0]!.migrations!)).toEqual(Object.keys(migrationsA));
|
||||
expect(Object.keys(converted[1]!.migrations!)).toEqual(Object.keys(migrationsB));
|
||||
});
|
||||
|
||||
it('converts the migration to the new format', () => {
|
||||
const legacyMigration = jest.fn();
|
||||
const migrationsA = {
|
||||
'1.0.0': legacyMigration,
|
||||
};
|
||||
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {
|
||||
typeA: migrationsA,
|
||||
},
|
||||
savedObjectSchemas: {},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(Object.keys(converted[0]!.migrations!)).toEqual(['1.0.0']);
|
||||
|
||||
const migration = converted[0]!.migrations!['1.0.0']!;
|
||||
|
||||
const doc = {} as SavedObjectUnsanitizedDoc;
|
||||
const context = { log: {} } as SavedObjectMigrationContext;
|
||||
migration(doc, context);
|
||||
|
||||
expect(legacyMigration).toHaveBeenCalledTimes(1);
|
||||
expect(legacyMigration).toHaveBeenCalledWith(doc, context.log);
|
||||
});
|
||||
|
||||
it('imports type management information', () => {
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginB',
|
||||
properties: {
|
||||
typeB: {
|
||||
properties: {
|
||||
fieldB: { type: 'text' },
|
||||
},
|
||||
},
|
||||
typeC: {
|
||||
properties: {
|
||||
fieldC: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectsManagement: {
|
||||
typeA: {
|
||||
isImportableAndExportable: true,
|
||||
icon: 'iconA',
|
||||
defaultSearchField: 'searchFieldA',
|
||||
getTitle: (savedObject) => savedObject.id,
|
||||
},
|
||||
typeB: {
|
||||
isImportableAndExportable: false,
|
||||
icon: 'iconB',
|
||||
getEditUrl: (savedObject) => `/some-url/${savedObject.id}`,
|
||||
getInAppUrl: (savedObject) => ({ path: 'path', uiCapabilitiesPath: 'ui-path' }),
|
||||
},
|
||||
},
|
||||
savedObjectMigrations: {},
|
||||
savedObjectSchemas: {},
|
||||
savedObjectValidations: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(converted.length).toEqual(3);
|
||||
const [typeA, typeB, typeC] = converted;
|
||||
|
||||
expect(typeA.management).toEqual({
|
||||
importableAndExportable: true,
|
||||
icon: 'iconA',
|
||||
defaultSearchField: 'searchFieldA',
|
||||
getTitle: uiExports.savedObjectsManagement.typeA.getTitle,
|
||||
});
|
||||
|
||||
expect(typeB.management).toEqual({
|
||||
importableAndExportable: false,
|
||||
icon: 'iconB',
|
||||
getEditUrl: uiExports.savedObjectsManagement.typeB.getEditUrl,
|
||||
getInAppUrl: uiExports.savedObjectsManagement.typeB.getInAppUrl,
|
||||
});
|
||||
|
||||
expect(typeC.management).toBeUndefined();
|
||||
});
|
||||
|
||||
it('merges everything when all are present', () => {
|
||||
const uiExports: SavedObjectsLegacyUiExports = {
|
||||
savedObjectMappings: [
|
||||
{
|
||||
pluginId: 'pluginA',
|
||||
properties: {
|
||||
typeA: {
|
||||
properties: {
|
||||
fieldA: { type: 'text' },
|
||||
},
|
||||
},
|
||||
typeB: {
|
||||
properties: {
|
||||
fieldB: { type: 'text' },
|
||||
anotherFieldB: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'pluginB',
|
||||
properties: {
|
||||
typeC: {
|
||||
properties: {
|
||||
fieldC: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
savedObjectMigrations: {
|
||||
typeA: {
|
||||
'1.0.0': jest.fn(),
|
||||
'2.0.4': jest.fn(),
|
||||
},
|
||||
typeC: {
|
||||
'1.5.3': jest.fn(),
|
||||
},
|
||||
},
|
||||
savedObjectSchemas: {
|
||||
typeA: {
|
||||
indexPattern: jest.fn((config) => {
|
||||
config.get('foo.bar');
|
||||
return 'myIndex';
|
||||
}),
|
||||
hidden: true,
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
typeB: {
|
||||
convertToAliasScript: 'some alias script',
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
savedObjectValidations: {},
|
||||
savedObjectsManagement: {},
|
||||
};
|
||||
|
||||
const converted = convertLegacyTypes(uiExports, legacyConfig);
|
||||
expect(converted).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertTypesToLegacySchema', () => {
|
||||
it('converts types to the legacy schema format', () => {
|
||||
const types: SavedObjectsType[] = [
|
||||
{
|
||||
name: 'typeA',
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
mappings: { properties: {} },
|
||||
convertToAliasScript: 'some script',
|
||||
},
|
||||
{
|
||||
name: 'typeB',
|
||||
hidden: true,
|
||||
namespaceType: 'single',
|
||||
indexPattern: 'myIndex',
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'typeC',
|
||||
hidden: false,
|
||||
namespaceType: 'multiple',
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
];
|
||||
expect(convertTypesToLegacySchema(types)).toEqual({
|
||||
typeA: {
|
||||
hidden: false,
|
||||
isNamespaceAgnostic: true,
|
||||
multiNamespace: false,
|
||||
convertToAliasScript: 'some script',
|
||||
},
|
||||
typeB: {
|
||||
hidden: true,
|
||||
isNamespaceAgnostic: false,
|
||||
multiNamespace: false,
|
||||
indexPattern: 'myIndex',
|
||||
},
|
||||
typeC: {
|
||||
hidden: false,
|
||||
isNamespaceAgnostic: false,
|
||||
multiNamespace: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { LegacyConfig } from '../legacy';
|
||||
import { SavedObjectMigrationMap } from './migrations';
|
||||
import {
|
||||
SavedObjectsNamespaceType,
|
||||
SavedObjectsType,
|
||||
SavedObjectsLegacyUiExports,
|
||||
SavedObjectLegacyMigrationMap,
|
||||
SavedObjectsLegacyManagementTypeDefinition,
|
||||
SavedObjectsTypeManagementDefinition,
|
||||
} from './types';
|
||||
import { SavedObjectsSchemaDefinition } from './schema';
|
||||
|
||||
/**
|
||||
* Converts the legacy savedObjects mappings, schema, and migrations
|
||||
* to actual {@link SavedObjectsType | saved object types}
|
||||
*/
|
||||
export const convertLegacyTypes = (
|
||||
{
|
||||
savedObjectMappings = [],
|
||||
savedObjectMigrations = {},
|
||||
savedObjectSchemas = {},
|
||||
savedObjectsManagement = {},
|
||||
}: SavedObjectsLegacyUiExports,
|
||||
legacyConfig: LegacyConfig
|
||||
): SavedObjectsType[] => {
|
||||
return savedObjectMappings.reduce((types, { properties }) => {
|
||||
return [
|
||||
...types,
|
||||
...Object.entries(properties).map(([type, mappings]) => {
|
||||
const schema = savedObjectSchemas[type];
|
||||
const migrations = savedObjectMigrations[type];
|
||||
const management = savedObjectsManagement[type];
|
||||
const namespaceType = (schema?.isNamespaceAgnostic
|
||||
? 'agnostic'
|
||||
: schema?.multiNamespace
|
||||
? 'multiple'
|
||||
: 'single') as SavedObjectsNamespaceType;
|
||||
return {
|
||||
name: type,
|
||||
hidden: schema?.hidden ?? false,
|
||||
namespaceType,
|
||||
mappings,
|
||||
indexPattern:
|
||||
typeof schema?.indexPattern === 'function'
|
||||
? schema.indexPattern(legacyConfig)
|
||||
: schema?.indexPattern,
|
||||
convertToAliasScript: schema?.convertToAliasScript,
|
||||
migrations: convertLegacyMigrations(migrations ?? {}),
|
||||
management: management ? convertLegacyTypeManagement(management) : undefined,
|
||||
};
|
||||
}),
|
||||
];
|
||||
}, [] as SavedObjectsType[]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert {@link SavedObjectsType | saved object types} to the legacy {@link SavedObjectsSchemaDefinition | schema} format
|
||||
*/
|
||||
export const convertTypesToLegacySchema = (
|
||||
types: SavedObjectsType[]
|
||||
): SavedObjectsSchemaDefinition => {
|
||||
return types.reduce((schema, type) => {
|
||||
return {
|
||||
...schema,
|
||||
[type.name]: {
|
||||
isNamespaceAgnostic: type.namespaceType === 'agnostic',
|
||||
multiNamespace: type.namespaceType === 'multiple',
|
||||
hidden: type.hidden,
|
||||
indexPattern: type.indexPattern,
|
||||
convertToAliasScript: type.convertToAliasScript,
|
||||
},
|
||||
};
|
||||
}, {} as SavedObjectsSchemaDefinition);
|
||||
};
|
||||
|
||||
const convertLegacyMigrations = (
|
||||
legacyMigrations: SavedObjectLegacyMigrationMap
|
||||
): SavedObjectMigrationMap => {
|
||||
return Object.entries(legacyMigrations).reduce((migrated, [version, migrationFn]) => {
|
||||
return {
|
||||
...migrated,
|
||||
[version]: (doc, context) => migrationFn(doc, context.log),
|
||||
};
|
||||
}, {} as SavedObjectMigrationMap);
|
||||
};
|
||||
|
||||
const convertLegacyTypeManagement = (
|
||||
legacyTypeManagement: SavedObjectsLegacyManagementTypeDefinition
|
||||
): SavedObjectsTypeManagementDefinition => {
|
||||
return {
|
||||
importableAndExportable: legacyTypeManagement.isImportableAndExportable,
|
||||
defaultSearchField: legacyTypeManagement.defaultSearchField,
|
||||
icon: legacyTypeManagement.icon,
|
||||
getTitle: legacyTypeManagement.getTitle,
|
||||
getEditUrl: legacyTypeManagement.getEditUrl,
|
||||
getInAppUrl: legacyTypeManagement.getInAppUrl,
|
||||
};
|
||||
};
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the core logic for validating saved object properties. The saved object client
|
||||
* and migrations consume this in order to validate saved object documents prior to
|
||||
* persisting them.
|
||||
*/
|
||||
|
||||
interface SavedObjectDoc {
|
||||
type: string;
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dictionary of property name -> validation function. The property name
|
||||
* is generally the document's type (e.g. "dashboard"), but will also
|
||||
* match other properties.
|
||||
*
|
||||
* For example, the "acl" and "dashboard" validators both apply to the
|
||||
* following saved object: { type: "dashboard", attributes: {}, acl: "sdlaj3w" }
|
||||
*
|
||||
* @export
|
||||
* @interface Validators
|
||||
*/
|
||||
export interface PropertyValidators {
|
||||
[prop: string]: ValidateDoc;
|
||||
}
|
||||
|
||||
export type ValidateDoc = (doc: SavedObjectDoc) => void;
|
||||
|
||||
/**
|
||||
* Creates a function which uses a dictionary of property validators to validate
|
||||
* individual saved object documents.
|
||||
*
|
||||
* @export
|
||||
* @param {Validators} validators
|
||||
* @param {SavedObjectDoc} doc
|
||||
*/
|
||||
export function docValidator(validators: PropertyValidators = {}): ValidateDoc {
|
||||
return function validateDoc(doc: SavedObjectDoc) {
|
||||
Object.keys(doc)
|
||||
.concat(doc.type)
|
||||
.forEach((prop) => {
|
||||
const validator = validators[prop];
|
||||
if (validator) {
|
||||
validator(doc);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
# Saved Object Validations
|
||||
|
||||
The saved object client supports validation of documents during create / bulkCreate operations.
|
||||
|
||||
This allows us tighter control over what documents get written to the saved object index, and helps us keep the index in a healthy state.
|
||||
|
||||
## Creating validations
|
||||
|
||||
Plugin authors can write their own validations by adding a `validations` property to their uiExports. A validation is nothing more than a dictionary of `{[prop: string]: validationFunction}` where:
|
||||
|
||||
* `prop` - a root-property on a saved object document
|
||||
* `validationFunction` - a function that takes a document and throws an error if it does not meet expectations.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
// In myFanciPlugin...
|
||||
uiExports: {
|
||||
validations: {
|
||||
myProperty(doc) {
|
||||
if (doc.attributes.someField === undefined) {
|
||||
throw new Error(`Document ${doc.id} did not define "someField"`);
|
||||
}
|
||||
},
|
||||
|
||||
someOtherProp(doc) {
|
||||
if (doc.attributes.counter < 0) {
|
||||
throw new Error(`Document ${doc.id} cannot have a negative counter.`);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
In this example, `myFanciPlugin` defines validations for two properties: `myProperty` and `someOtherProp`.
|
||||
|
||||
This means that no other plugin can define validations for myProperty or someOtherProp.
|
||||
|
||||
The `myProperty` validation would run for any doc that has a `type="myProperty"` or for any doc that has a root-level property of `myProperty`. e.g. it would apply to all documents in the following array:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
type: 'foo',
|
||||
attributes: { stuff: 'here' },
|
||||
myProperty: 'shazm!',
|
||||
},
|
||||
{
|
||||
type: 'myProperty',
|
||||
attributes: { shazm: true },
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
Validating properties other than just 'type' allows us to support potential future saved object scenarios in which plugins might want to annotate other plugin documents, such as a security plugin adding an acl to another document:
|
||||
|
||||
```js
|
||||
{
|
||||
type: 'dashboard',
|
||||
attributes: { stuff: 'here' },
|
||||
acl: '342343',
|
||||
}
|
||||
```
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { docValidator } from './index';
|
||||
|
||||
describe('docValidator', () => {
|
||||
test('does not run validators that have no application to the doc', () => {
|
||||
const validators = {
|
||||
foo: () => {
|
||||
throw new Error('Boom!');
|
||||
},
|
||||
};
|
||||
expect(() => docValidator(validators)({ type: 'shoo', bar: 'hi' })).not.toThrow();
|
||||
});
|
||||
|
||||
test('validates the doc type', () => {
|
||||
const validators = {
|
||||
foo: () => {
|
||||
throw new Error('Boom!');
|
||||
},
|
||||
};
|
||||
expect(() => docValidator(validators)({ type: 'foo' })).toThrow(/Boom!/);
|
||||
});
|
||||
|
||||
test('validates various props', () => {
|
||||
const validators = {
|
||||
a: jest.fn(),
|
||||
b: jest.fn(),
|
||||
c: jest.fn(),
|
||||
};
|
||||
docValidator(validators)({ type: 'a', b: 'foo' });
|
||||
|
||||
expect(validators.c).not.toHaveBeenCalled();
|
||||
|
||||
expect(validators.a.mock.calls).toEqual([[{ type: 'a', b: 'foo' }]]);
|
||||
expect(validators.b.mock.calls).toEqual([[{ type: 'a', b: 'foo' }]]);
|
||||
});
|
||||
});
|
|
@ -1411,19 +1411,30 @@ export interface LegacyServiceStartDeps {
|
|||
plugins: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "SavedObjectsLegacyUiExports" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @internal @deprecated (undocumented)
|
||||
export type LegacyUiExports = SavedObjectsLegacyUiExports & {
|
||||
export interface LegacyUiExports {
|
||||
// Warning: (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
defaultInjectedVarProviders?: VarsProvider[];
|
||||
// Warning: (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
injectedVarsReplacers?: VarsReplacer[];
|
||||
// Warning: (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
navLinkSpecs?: LegacyNavLinkSpec[] | null;
|
||||
// Warning: (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
uiAppSpecs?: Array<LegacyAppSpec | undefined>;
|
||||
// (undocumented)
|
||||
unknown?: [{
|
||||
pluginSpec: LegacyPluginSpec;
|
||||
type: unknown;
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
|
@ -2437,33 +2448,6 @@ export interface SavedObjectsIncrementCounterOptions extends SavedObjectsBaseOpt
|
|||
refresh?: MutatingOperationRefreshSetting;
|
||||
}
|
||||
|
||||
// @internal @deprecated (undocumented)
|
||||
export interface SavedObjectsLegacyService {
|
||||
// Warning: (ae-forgotten-export) The symbol "SavedObjectsClientProvider" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
addScopedSavedObjectsClientWrapperFactory: SavedObjectsClientProvider['addClientWrapperFactory'];
|
||||
// (undocumented)
|
||||
getSavedObjectsRepository(...rest: any[]): any;
|
||||
// (undocumented)
|
||||
getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient'];
|
||||
// (undocumented)
|
||||
importExport: {
|
||||
objectLimit: number;
|
||||
importSavedObjects(options: SavedObjectsImportOptions): Promise<SavedObjectsImportResponse>;
|
||||
resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise<SavedObjectsImportResponse>;
|
||||
getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise<Readable>;
|
||||
};
|
||||
// (undocumented)
|
||||
SavedObjectsClient: typeof SavedObjectsClient;
|
||||
// (undocumented)
|
||||
schema: SavedObjectsSchema;
|
||||
// (undocumented)
|
||||
setScopedSavedObjectsClientFactory: SavedObjectsClientProvider['setClientFactory'];
|
||||
// (undocumented)
|
||||
types: string[];
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface SavedObjectsMappingProperties {
|
||||
// (undocumented)
|
||||
|
@ -2517,10 +2501,10 @@ export class SavedObjectsRepository {
|
|||
bulkUpdate<T = unknown>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
|
||||
checkConflicts(objects?: SavedObjectsCheckConflictsObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsCheckConflictsResponse>;
|
||||
create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
|
||||
// Warning: (ae-forgotten-export) The symbol "KibanaMigrator" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-forgotten-export) The symbol "IKibanaMigrator" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @internal
|
||||
static createRepository(migrator: KibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
|
||||
static createRepository(migrator: IKibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
|
||||
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
|
||||
deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise<any>;
|
||||
deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<SavedObjectsDeleteFromNamespacesResponse>;
|
||||
|
@ -2548,24 +2532,6 @@ export interface SavedObjectsResolveImportErrorsOptions {
|
|||
typeRegistry: ISavedObjectTypeRegistry;
|
||||
}
|
||||
|
||||
// @internal @deprecated (undocumented)
|
||||
export class SavedObjectsSchema {
|
||||
// Warning: (ae-forgotten-export) The symbol "SavedObjectsSchemaDefinition" needs to be exported by the entry point index.d.ts
|
||||
constructor(schemaDefinition?: SavedObjectsSchemaDefinition);
|
||||
// (undocumented)
|
||||
getConvertToAliasScript(type: string): string | undefined;
|
||||
// (undocumented)
|
||||
getIndexForType(config: LegacyConfig, type: string): string | undefined;
|
||||
// (undocumented)
|
||||
isHiddenType(type: string): boolean;
|
||||
// (undocumented)
|
||||
isMultiNamespace(type: string): boolean;
|
||||
// (undocumented)
|
||||
isNamespaceAgnostic(type: string): boolean;
|
||||
// (undocumented)
|
||||
isSingleNamespace(type: string): boolean;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class SavedObjectsSerializer {
|
||||
// @internal
|
||||
|
@ -2888,11 +2854,7 @@ export const validBodyOutput: readonly ["data", "stream"];
|
|||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:132:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:133:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:134:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:135:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:136:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/legacy/types.ts:135:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/plugins/types.ts:266:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/plugins/types.ts:266:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
|
||||
// src/core/server/plugins/types.ts:268:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -142,7 +142,6 @@ export class Server {
|
|||
const savedObjectsSetup = await this.savedObjects.setup({
|
||||
http: httpSetup,
|
||||
elasticsearch: elasticsearchServiceSetup,
|
||||
legacyPlugins,
|
||||
});
|
||||
|
||||
const uiSettingsSetup = await this.uiSettings.setup({
|
||||
|
|
|
@ -36,8 +36,6 @@ describe('createOrUpgradeSavedConfig()', () => {
|
|||
let esServer: TestElasticsearchUtils;
|
||||
let kbn: TestKibanaUtils;
|
||||
|
||||
let kbnServer: TestKibanaUtils['kbnServer'];
|
||||
|
||||
beforeAll(async function () {
|
||||
servers = createTestServers({
|
||||
adjustTimeout: (t) => {
|
||||
|
@ -46,10 +44,8 @@ describe('createOrUpgradeSavedConfig()', () => {
|
|||
});
|
||||
esServer = await servers.startES();
|
||||
kbn = await servers.startKibana();
|
||||
kbnServer = kbn.kbnServer;
|
||||
|
||||
const savedObjects = kbnServer.server.savedObjects;
|
||||
savedObjectsClient = savedObjects.getScopedSavedObjectsClient(
|
||||
savedObjectsClient = kbn.coreStart.savedObjects.getScopedClient(
|
||||
httpServerMock.createKibanaRequest()
|
||||
);
|
||||
|
||||
|
|
|
@ -68,8 +68,7 @@ export function getServices() {
|
|||
|
||||
const callCluster = esServer.es.getCallCluster();
|
||||
|
||||
const savedObjects = kbnServer.server.savedObjects;
|
||||
const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(
|
||||
const savedObjectsClient = kbn.coreStart.savedObjects.getScopedClient(
|
||||
httpServerMock.createKibanaRequest()
|
||||
);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import { resolve } from 'path';
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { CoreStart } from 'src/core/server';
|
||||
import { LegacyAPICaller } from '../server/elasticsearch';
|
||||
import { CliArgs, Env } from '../server/config';
|
||||
import { Root } from '../server/root';
|
||||
|
@ -170,6 +171,7 @@ export interface TestElasticsearchUtils {
|
|||
|
||||
export interface TestKibanaUtils {
|
||||
root: Root;
|
||||
coreStart: CoreStart;
|
||||
kbnServer: KbnServer;
|
||||
stop: () => Promise<void>;
|
||||
}
|
||||
|
@ -289,13 +291,14 @@ export function createTestServers({
|
|||
const root = createRootWithCorePlugins(kbnSettings);
|
||||
|
||||
await root.setup();
|
||||
await root.start();
|
||||
const coreStart = await root.start();
|
||||
|
||||
const kbnServer = getKbnServer(root);
|
||||
|
||||
return {
|
||||
root,
|
||||
kbnServer,
|
||||
coreStart,
|
||||
stop: async () => await root.shutdown(),
|
||||
};
|
||||
},
|
||||
|
|
|
@ -18,14 +18,10 @@
|
|||
*/
|
||||
import { Server } from '../../server/kbn_server';
|
||||
import { Capabilities } from '../../../core/server';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { SavedObjectsLegacyManagementDefinition } from '../../../core/server/saved_objects/types';
|
||||
|
||||
export type InitPluginFunction = (server: Server) => void;
|
||||
export interface UiExports {
|
||||
injectDefaultVars?: (server: Server) => { [key: string]: any };
|
||||
savedObjectsManagement?: SavedObjectsLegacyManagementDefinition;
|
||||
mappings?: unknown;
|
||||
}
|
||||
|
||||
export interface PluginSpecOptions {
|
||||
|
|
|
@ -19,11 +19,6 @@
|
|||
|
||||
import { Server } from '../server/kbn_server';
|
||||
import { Capabilities } from '../../core/server';
|
||||
// Disable lint errors for imports from src/core/* until SavedObjects migration is complete
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { SavedObjectsSchemaDefinition } from '../../core/server/saved_objects/schema';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { SavedObjectsLegacyManagementDefinition } from '../../core/server/saved_objects/types';
|
||||
import { AppCategory } from '../../core/types';
|
||||
|
||||
/**
|
||||
|
@ -70,8 +65,6 @@ export interface LegacyPluginOptions {
|
|||
home: string[];
|
||||
mappings: any;
|
||||
migrations: any;
|
||||
savedObjectSchemas: SavedObjectsSchemaDefinition;
|
||||
savedObjectsManagement: SavedObjectsLegacyManagementDefinition;
|
||||
visTypes: string[];
|
||||
embeddableActions?: string[];
|
||||
embeddableFactories?: string[];
|
||||
|
|
27
src/legacy/server/kbn_server.d.ts
vendored
27
src/legacy/server/kbn_server.d.ts
vendored
|
@ -17,33 +17,24 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ResponseObject, Server } from 'hapi';
|
||||
import { UnwrapPromise } from '@kbn/utility-types';
|
||||
import { Server } from 'hapi';
|
||||
|
||||
import { TelemetryCollectionManagerPluginSetup } from 'src/plugins/telemetry_collection_manager/server';
|
||||
import {
|
||||
ConfigService,
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
ElasticsearchServiceSetup,
|
||||
EnvironmentMode,
|
||||
LoggerFactory,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsLegacyService,
|
||||
SavedObjectsClientProviderOptions,
|
||||
IUiSettingsClient,
|
||||
PackageInfo,
|
||||
LegacyRequest,
|
||||
LegacyServiceSetupDeps,
|
||||
LegacyServiceStartDeps,
|
||||
LegacyServiceDiscoverPlugins,
|
||||
} from '../../core/server';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { LegacyConfig, ILegacyService, ILegacyInternals } from '../../core/server/legacy';
|
||||
import { LegacyConfig, ILegacyInternals } from '../../core/server/legacy';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { UiPlugins } from '../../core/server/plugins';
|
||||
import { CallClusterWithRequest, ElasticsearchPlugin } from '../core_plugins/elasticsearch';
|
||||
import { ElasticsearchPlugin } from '../core_plugins/elasticsearch';
|
||||
import { UsageCollectionSetup } from '../../plugins/usage_collection/server';
|
||||
import { HomeServerPluginSetup } from '../../plugins/home/server';
|
||||
|
||||
|
@ -61,16 +52,9 @@ declare module 'hapi' {
|
|||
|
||||
interface Server {
|
||||
config: () => KibanaConfig;
|
||||
savedObjects: SavedObjectsLegacyService;
|
||||
logWithMetadata: (tags: string[], message: string, meta: Record<string, any>) => void;
|
||||
newPlatform: KbnServer['newPlatform'];
|
||||
}
|
||||
|
||||
interface Request {
|
||||
getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract;
|
||||
getBasePath(): string;
|
||||
getUiSettingsService(): IUiSettingsClient;
|
||||
}
|
||||
}
|
||||
|
||||
type KbnMixinFunc = (kbnServer: KbnServer, server: Server, config: any) => Promise<any> | void;
|
||||
|
@ -86,11 +70,9 @@ export interface KibanaCore {
|
|||
__internals: {
|
||||
elasticsearch: LegacyServiceSetupDeps['core']['elasticsearch'];
|
||||
hapiServer: LegacyServiceSetupDeps['core']['http']['server'];
|
||||
kibanaMigrator: LegacyServiceStartDeps['core']['savedObjects']['migrator'];
|
||||
legacy: ILegacyInternals;
|
||||
rendering: LegacyServiceSetupDeps['core']['rendering'];
|
||||
uiPlugins: UiPlugins;
|
||||
savedObjectsClientProvider: LegacyServiceStartDeps['core']['savedObjects']['clientProvider'];
|
||||
};
|
||||
env: {
|
||||
mode: Readonly<EnvironmentMode>;
|
||||
|
@ -149,6 +131,3 @@ export default class KbnServer {
|
|||
|
||||
// Re-export commonly used hapi types.
|
||||
export { Server, Request, ResponseToolkit } from 'hapi';
|
||||
|
||||
// Re-export commonly accessed api types.
|
||||
export { SavedObjectsLegacyService, SavedObjectsClient } from 'src/core/server';
|
||||
|
|
|
@ -33,7 +33,6 @@ import pidMixin from './pid';
|
|||
import configCompleteMixin from './config/complete';
|
||||
import { optimizeMixin } from '../../optimize';
|
||||
import * as Plugins from './plugins';
|
||||
import { savedObjectsMixin } from './saved_objects/saved_objects_mixin';
|
||||
import { uiMixin } from '../ui';
|
||||
import { i18nMixin } from './i18n';
|
||||
|
||||
|
@ -108,9 +107,6 @@ export default class KbnServer {
|
|||
|
||||
uiMixin,
|
||||
|
||||
// setup saved object routes
|
||||
savedObjectsMixin,
|
||||
|
||||
// setup routes that serve the @kbn/optimizer output
|
||||
optimizeMixin,
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
|
||||
/* eslint-disable @kbn/eslint/no-restricted-paths */
|
||||
import { SavedObjectsSchema } from '../../../core/server/saved_objects/schema';
|
||||
import {
|
||||
SavedObjectsClient,
|
||||
SavedObjectsRepository,
|
||||
exportSavedObjectsToStream,
|
||||
importSavedObjectsFromStream,
|
||||
resolveSavedObjectsImportErrors,
|
||||
} from '../../../core/server/saved_objects';
|
||||
import { convertTypesToLegacySchema } from '../../../core/server/saved_objects/utils';
|
||||
|
||||
export function savedObjectsMixin(kbnServer, server) {
|
||||
const migrator = kbnServer.newPlatform.__internals.kibanaMigrator;
|
||||
const typeRegistry = kbnServer.newPlatform.start.core.savedObjects.getTypeRegistry();
|
||||
const mappings = migrator.getActiveMappings();
|
||||
const allTypes = typeRegistry.getAllTypes().map((t) => t.name);
|
||||
const visibleTypes = typeRegistry.getVisibleTypes().map((t) => t.name);
|
||||
const schema = new SavedObjectsSchema(convertTypesToLegacySchema(typeRegistry.getAllTypes()));
|
||||
|
||||
server.decorate('server', 'kibanaMigrator', migrator);
|
||||
|
||||
const serializer = kbnServer.newPlatform.start.core.savedObjects.createSerializer();
|
||||
|
||||
const createRepository = (callCluster, includedHiddenTypes = []) => {
|
||||
if (typeof callCluster !== 'function') {
|
||||
throw new TypeError('Repository requires a "callCluster" function to be provided.');
|
||||
}
|
||||
// throw an exception if an extraType is not defined.
|
||||
includedHiddenTypes.forEach((type) => {
|
||||
if (!allTypes.includes(type)) {
|
||||
throw new Error(`Missing mappings for saved objects type '${type}'`);
|
||||
}
|
||||
});
|
||||
const combinedTypes = visibleTypes.concat(includedHiddenTypes);
|
||||
const allowedTypes = [...new Set(combinedTypes)];
|
||||
|
||||
const config = server.config();
|
||||
|
||||
return new SavedObjectsRepository({
|
||||
index: config.get('kibana.index'),
|
||||
migrator,
|
||||
mappings,
|
||||
typeRegistry,
|
||||
serializer,
|
||||
allowedTypes,
|
||||
callCluster,
|
||||
});
|
||||
};
|
||||
|
||||
const provider = kbnServer.newPlatform.__internals.savedObjectsClientProvider;
|
||||
|
||||
const service = {
|
||||
types: visibleTypes,
|
||||
SavedObjectsClient,
|
||||
SavedObjectsRepository,
|
||||
getSavedObjectsRepository: createRepository,
|
||||
getScopedSavedObjectsClient: (...args) => provider.getClient(...args),
|
||||
setScopedSavedObjectsClientFactory: (...args) => provider.setClientFactory(...args),
|
||||
addScopedSavedObjectsClientWrapperFactory: (...args) =>
|
||||
provider.addClientWrapperFactory(...args),
|
||||
importExport: {
|
||||
objectLimit: server.config().get('savedObjects.maxImportExportSize'),
|
||||
importSavedObjects: importSavedObjectsFromStream,
|
||||
resolveImportErrors: resolveSavedObjectsImportErrors,
|
||||
getSortedObjectsForExport: exportSavedObjectsToStream,
|
||||
},
|
||||
schema,
|
||||
};
|
||||
server.decorate('server', 'savedObjects', service);
|
||||
|
||||
const savedObjectsClientCache = new WeakMap();
|
||||
server.decorate('request', 'getSavedObjectsClient', function (options) {
|
||||
const request = this;
|
||||
|
||||
if (savedObjectsClientCache.has(request)) {
|
||||
return savedObjectsClientCache.get(request);
|
||||
}
|
||||
|
||||
const savedObjectsClient = server.savedObjects.getScopedSavedObjectsClient(request, options);
|
||||
|
||||
savedObjectsClientCache.set(request, savedObjectsClient);
|
||||
return savedObjectsClient;
|
||||
});
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { savedObjectsMixin } from './saved_objects_mixin';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { mockKibanaMigrator } from '../../../core/server/saved_objects/migrations/kibana/kibana_migrator.mock';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { savedObjectsClientProviderMock } from '../../../core/server/saved_objects/service/lib/scoped_client_provider.mock';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { convertLegacyTypes } from '../../../core/server/saved_objects/utils';
|
||||
import { SavedObjectTypeRegistry } from '../../../core/server';
|
||||
import { coreMock } from '../../../core/server/mocks';
|
||||
|
||||
const mockConfig = {
|
||||
get: jest.fn().mockReturnValue('anything'),
|
||||
};
|
||||
|
||||
const savedObjectMappings = [
|
||||
{
|
||||
pluginId: 'testtype',
|
||||
properties: {
|
||||
testtype: {
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'testtype2',
|
||||
properties: {
|
||||
doc1: {
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
doc2: {
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: 'secretPlugin',
|
||||
properties: {
|
||||
hiddentype: {
|
||||
properties: {
|
||||
secret: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const savedObjectSchemas = {
|
||||
hiddentype: {
|
||||
hidden: true,
|
||||
},
|
||||
doc1: {
|
||||
indexPattern: 'other-index',
|
||||
},
|
||||
};
|
||||
|
||||
const savedObjectTypes = convertLegacyTypes(
|
||||
{
|
||||
savedObjectMappings,
|
||||
savedObjectSchemas,
|
||||
savedObjectMigrations: {},
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
|
||||
const typeRegistry = new SavedObjectTypeRegistry();
|
||||
savedObjectTypes.forEach((type) => typeRegistry.registerType(type));
|
||||
|
||||
const migrator = mockKibanaMigrator.create({
|
||||
types: savedObjectTypes,
|
||||
});
|
||||
|
||||
describe('Saved Objects Mixin', () => {
|
||||
let mockKbnServer;
|
||||
let mockServer;
|
||||
const mockCallCluster = jest.fn();
|
||||
const stubCallCluster = jest.fn();
|
||||
const config = {
|
||||
'kibana.index': 'kibana.index',
|
||||
'savedObjects.maxImportExportSize': 10000,
|
||||
};
|
||||
const stubConfig = jest.fn((key) => {
|
||||
return config[key];
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const clientProvider = savedObjectsClientProviderMock.create();
|
||||
mockServer = {
|
||||
log: jest.fn(),
|
||||
route: jest.fn(),
|
||||
decorate: jest.fn(),
|
||||
config: () => {
|
||||
return {
|
||||
get: stubConfig,
|
||||
};
|
||||
},
|
||||
plugins: {
|
||||
elasticsearch: {
|
||||
getCluster: () => {
|
||||
return {
|
||||
callWithRequest: mockCallCluster,
|
||||
callWithInternalUser: stubCallCluster,
|
||||
};
|
||||
},
|
||||
waitUntilReady: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry);
|
||||
|
||||
mockKbnServer = {
|
||||
newPlatform: {
|
||||
__internals: {
|
||||
kibanaMigrator: migrator,
|
||||
savedObjectsClientProvider: clientProvider,
|
||||
},
|
||||
setup: {
|
||||
core: coreMock.createSetup(),
|
||||
},
|
||||
start: {
|
||||
core: coreStart,
|
||||
},
|
||||
},
|
||||
server: mockServer,
|
||||
ready: () => {},
|
||||
pluginSpecs: {
|
||||
some: () => {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
uiExports: {
|
||||
savedObjectMappings,
|
||||
savedObjectSchemas,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('Saved object service', () => {
|
||||
let service;
|
||||
|
||||
beforeEach(async () => {
|
||||
await savedObjectsMixin(mockKbnServer, mockServer);
|
||||
const call = mockServer.decorate.mock.calls.filter(
|
||||
([objName, methodName]) => objName === 'server' && methodName === 'savedObjects'
|
||||
);
|
||||
service = call[0][2];
|
||||
});
|
||||
|
||||
it('should return all but hidden types', async () => {
|
||||
expect(service).toBeDefined();
|
||||
expect(service.types).toEqual(['testtype', 'doc1', 'doc2']);
|
||||
});
|
||||
|
||||
const mockCallEs = jest.fn();
|
||||
describe('repository creation', () => {
|
||||
it('should not allow a repository with an undefined type', () => {
|
||||
expect(() => {
|
||||
service.getSavedObjectsRepository(mockCallEs, ['extraType']);
|
||||
}).toThrow(new Error("Missing mappings for saved objects type 'extraType'"));
|
||||
});
|
||||
|
||||
it('should create a repository without hidden types', () => {
|
||||
const repository = service.getSavedObjectsRepository(mockCallEs);
|
||||
expect(repository).toBeDefined();
|
||||
expect(repository._allowedTypes).toEqual(['testtype', 'doc1', 'doc2']);
|
||||
});
|
||||
|
||||
it('should create a repository with a unique list of allowed types', () => {
|
||||
const repository = service.getSavedObjectsRepository(mockCallEs, ['doc1', 'doc1', 'doc1']);
|
||||
expect(repository._allowedTypes).toEqual(['testtype', 'doc1', 'doc2']);
|
||||
});
|
||||
|
||||
it('should create a repository with extraTypes minus duplicate', () => {
|
||||
const repository = service.getSavedObjectsRepository(mockCallEs, [
|
||||
'hiddentype',
|
||||
'hiddentype',
|
||||
]);
|
||||
expect(repository._allowedTypes).toEqual(['testtype', 'doc1', 'doc2', 'hiddentype']);
|
||||
});
|
||||
|
||||
it('should not allow a repository without a callCluster function', () => {
|
||||
expect(() => {
|
||||
service.getSavedObjectsRepository({});
|
||||
}).toThrow(new Error('Repository requires a "callCluster" function to be provided.'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('get client', () => {
|
||||
it('should have a method to get the client', () => {
|
||||
expect(service).toHaveProperty('getScopedSavedObjectsClient');
|
||||
});
|
||||
|
||||
it('should have a method to set the client factory', () => {
|
||||
expect(service).toHaveProperty('setScopedSavedObjectsClientFactory');
|
||||
});
|
||||
|
||||
it('should have a method to add a client wrapper factory', () => {
|
||||
expect(service).toHaveProperty('addScopedSavedObjectsClientWrapperFactory');
|
||||
});
|
||||
|
||||
it('should allow you to set a scoped saved objects client factory', () => {
|
||||
expect(() => {
|
||||
service.setScopedSavedObjectsClientFactory({});
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
it('should allow you to add a scoped saved objects client wrapper factory', () => {
|
||||
expect(() => {
|
||||
service.addScopedSavedObjectsClientWrapperFactory({});
|
||||
}).not.toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getSavedObjectsClient', () => {
|
||||
let getSavedObjectsClient;
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsMixin(mockKbnServer, mockServer);
|
||||
const call = mockServer.decorate.mock.calls.filter(
|
||||
([objName, methodName]) => objName === 'request' && methodName === 'getSavedObjectsClient'
|
||||
);
|
||||
getSavedObjectsClient = call[0][2];
|
||||
});
|
||||
|
||||
it('should be callable', () => {
|
||||
mockServer.savedObjects = service;
|
||||
getSavedObjectsClient = getSavedObjectsClient.bind({});
|
||||
expect(() => {
|
||||
getSavedObjectsClient();
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
it('should use cached request object', () => {
|
||||
mockServer.savedObjects = service;
|
||||
getSavedObjectsClient = getSavedObjectsClient.bind({ _test: 'me' });
|
||||
const savedObjectsClient = getSavedObjectsClient();
|
||||
expect(getSavedObjectsClient()).toEqual(savedObjectsClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { PluginPack } from '../../../plugin_discovery';
|
||||
|
||||
import { collectUiExports } from '../collect_ui_exports';
|
||||
|
||||
const specs = new PluginPack({
|
||||
path: '/dev/null',
|
||||
pkg: {
|
||||
name: 'test',
|
||||
version: 'kibana',
|
||||
},
|
||||
provider({ Plugin }) {
|
||||
return [
|
||||
new Plugin({
|
||||
id: 'test',
|
||||
uiExports: {
|
||||
savedObjectSchemas: {
|
||||
foo: {
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
new Plugin({
|
||||
id: 'test2',
|
||||
uiExports: {
|
||||
savedObjectSchemas: {
|
||||
bar: {
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
}).getPluginSpecs();
|
||||
|
||||
describe('plugin discovery', () => {
|
||||
describe('collectUiExports()', () => {
|
||||
it('merges uiExports from all provided plugin specs', () => {
|
||||
const uiExports = collectUiExports(specs);
|
||||
|
||||
expect(uiExports.savedObjectSchemas).to.eql({
|
||||
foo: {
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
bar: {
|
||||
isNamespaceAgnostic: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`throws an error when migrations and mappings aren't defined in the same plugin`, () => {
|
||||
const invalidSpecs = new PluginPack({
|
||||
path: '/dev/null',
|
||||
pkg: {
|
||||
name: 'test',
|
||||
version: 'kibana',
|
||||
},
|
||||
provider({ Plugin }) {
|
||||
return [
|
||||
new Plugin({
|
||||
id: 'test',
|
||||
uiExports: {
|
||||
mappings: {
|
||||
'test-type': {
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
new Plugin({
|
||||
id: 'test2',
|
||||
uiExports: {
|
||||
migrations: {
|
||||
'test-type': {
|
||||
'1.2.3': (doc) => {
|
||||
return doc;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
}).getPluginSpecs();
|
||||
expect(() => collectUiExports(invalidSpecs)).to.throwError((err) => {
|
||||
expect(err).to.be.a(Error);
|
||||
expect(err).to.have.property(
|
||||
'message',
|
||||
'Migrations and mappings must be defined together in the uiExports of a single plugin. ' +
|
||||
'test2 defines migrations for types test-type but does not define their mappings.'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -193,13 +193,11 @@ export function uiRenderMixin(kbnServer, server, config) {
|
|||
async function renderApp(h) {
|
||||
const app = { getId: () => 'core' };
|
||||
const { http } = kbnServer.newPlatform.setup.core;
|
||||
const {
|
||||
rendering,
|
||||
legacy,
|
||||
savedObjectsClientProvider: savedObjects,
|
||||
} = kbnServer.newPlatform.__internals;
|
||||
const { savedObjects } = kbnServer.newPlatform.start.core;
|
||||
const { rendering, legacy } = kbnServer.newPlatform.__internals;
|
||||
const req = KibanaRequest.from(h.request);
|
||||
const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient(
|
||||
savedObjects.getClient(h.request)
|
||||
savedObjects.getScopedClient(req)
|
||||
);
|
||||
const vars = await legacy.getVars(app.getId(), h.request, {
|
||||
apmConfig: getApmConfig(h.request.path),
|
||||
|
|
|
@ -379,14 +379,12 @@ async function migrateIndex({
|
|||
index,
|
||||
migrations,
|
||||
mappingProperties,
|
||||
validateDoc,
|
||||
obsoleteIndexTemplatePattern,
|
||||
}: {
|
||||
esClient: ElasticsearchClient;
|
||||
index: string;
|
||||
migrations: Record<string, SavedObjectMigrationMap>;
|
||||
mappingProperties: SavedObjectsTypeMappingDefinitions;
|
||||
validateDoc?: (doc: any) => void;
|
||||
obsoleteIndexTemplatePattern?: string;
|
||||
}) {
|
||||
const typeRegistry = new SavedObjectTypeRegistry();
|
||||
|
@ -396,7 +394,6 @@ async function migrateIndex({
|
|||
const documentMigrator = new DocumentMigrator({
|
||||
kibanaVersion: '99.9.9',
|
||||
typeRegistry,
|
||||
validateDoc: validateDoc || _.noop,
|
||||
log: getLogMock(),
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { merge } from 'lodash';
|
||||
import { Server } from 'hapi';
|
||||
|
||||
import { SavedObjectsClient } from 'src/core/server';
|
||||
import { PromiseReturnType } from '../../../../../observability/typings/common';
|
||||
import {
|
||||
|
@ -32,10 +32,6 @@ export interface ApmIndicesConfig {
|
|||
|
||||
export type ApmIndicesName = keyof ApmIndicesConfig;
|
||||
|
||||
export type ScopedSavedObjectsClient = ReturnType<
|
||||
Server['savedObjects']['getScopedSavedObjectsClient']
|
||||
>;
|
||||
|
||||
async function getApmIndicesSavedObject(
|
||||
savedObjectsClient: ISavedObjectsClient
|
||||
) {
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
} from 'src/core/server';
|
||||
import { PickByValue, Optional } from 'utility-types';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Server } from 'hapi';
|
||||
import { ObservabilityPluginSetup } from '../../../observability/server';
|
||||
import { SecurityPluginSetup } from '../../../security/server';
|
||||
import { MlPluginSetup } from '../../../ml/server';
|
||||
|
@ -57,12 +56,6 @@ export interface Route<
|
|||
}) => Promise<TReturn>;
|
||||
}
|
||||
|
||||
export type APMLegacyServer = Pick<Server, 'savedObjects' | 'log'> & {
|
||||
plugins: {
|
||||
elasticsearch: Server['plugins']['elasticsearch'];
|
||||
};
|
||||
};
|
||||
|
||||
export type APMRequestHandlerContext<
|
||||
TDecodedParams extends { [key in keyof Params]: any } = {}
|
||||
> = RequestHandlerContext & {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue