diff --git a/packages/kbn-config/src/config_service.mock.ts b/packages/kbn-config/src/config_service.mock.ts index 83fbf20b5c0b3..68adbba7c0ed7 100644 --- a/packages/kbn-config/src/config_service.mock.ts +++ b/packages/kbn-config/src/config_service.mock.ts @@ -26,11 +26,13 @@ const createConfigServiceMock = ({ addDeprecationProvider: jest.fn(), validate: jest.fn(), getHandledDeprecatedConfigs: jest.fn(), + getDeprecatedConfigPath$: jest.fn(), }; mocked.atPath.mockReturnValue(new BehaviorSubject(atPath)); mocked.atPathSync.mockReturnValue(atPath); mocked.getConfig$.mockReturnValue(new BehaviorSubject(new ObjectToConfigAdapter(getConfig$))); + mocked.getDeprecatedConfigPath$.mockReturnValue(new BehaviorSubject({ set: [], unset: [] })); mocked.getUsedPaths.mockResolvedValue([]); mocked.getUnusedPaths.mockResolvedValue([]); mocked.isEnabledAtPath.mockResolvedValue(true); diff --git a/packages/kbn-config/src/config_service.test.mocks.ts b/packages/kbn-config/src/config_service.test.mocks.ts index d8da2852b9251..39aa551ae85f9 100644 --- a/packages/kbn-config/src/config_service.test.mocks.ts +++ b/packages/kbn-config/src/config_service.test.mocks.ts @@ -11,10 +11,17 @@ import type { applyDeprecations } from './deprecation/apply_deprecations'; jest.mock('../../../package.json', () => mockPackage); +const changedPaths = { + set: ['foo'], + unset: ['bar.baz'], +}; + +export { changedPaths as mockedChangedPaths }; + export const mockApplyDeprecations = jest.fn< - Record, + ReturnType, Parameters ->((config, deprecations, createAddDeprecation) => config); +>((config, deprecations, createAddDeprecation) => ({ config, changedPaths })); jest.mock('./deprecation/apply_deprecations', () => ({ applyDeprecations: mockApplyDeprecations, diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index 64404341bc64d..c2d4f15b6d915 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -9,7 +9,7 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { first, take } from 'rxjs/operators'; -import { mockApplyDeprecations } from './config_service.test.mocks'; +import { mockApplyDeprecations, mockedChangedPaths } from './config_service.test.mocks'; import { rawConfigServiceMock } from './raw/raw_config_service.mock'; import { schema } from '@kbn/config-schema'; @@ -420,7 +420,7 @@ test('logs deprecation warning during validation', async () => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'some deprecation message' }); addDeprecation({ message: 'another deprecation message' }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); loggerMock.clear(logger); @@ -446,12 +446,12 @@ test('does not log warnings for silent deprecations during validation', async () const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'some deprecation message', silent: true }); addDeprecation({ message: 'another deprecation message' }); - return config; + return { config, changedPaths: mockedChangedPaths }; }) .mockImplementationOnce((config, deprecations, createAddDeprecation) => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'I am silent', silent: true }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); loggerMock.clear(logger); @@ -521,7 +521,7 @@ describe('getHandledDeprecatedConfigs', () => { const addDeprecation = createAddDeprecation!(deprecation.path); addDeprecation({ message: `some deprecation message`, documentationUrl: 'some-url' }); }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); await configService.validate(); @@ -541,3 +541,18 @@ describe('getHandledDeprecatedConfigs', () => { `); }); }); + +describe('getDeprecatedConfigPath$', () => { + it('returns all config paths changes during deprecation', async () => { + const rawConfig$ = new BehaviorSubject>({ key: 'value' }); + const rawConfigProvider = rawConfigServiceMock.create({ rawConfig$ }); + + const configService = new ConfigService(rawConfigProvider, defaultEnv, logger); + await configService.setSchema('key', schema.string()); + await configService.validate(); + + const deprecatedConfigPath$ = configService.getDeprecatedConfigPath$(); + const deprecatedConfigPath = await deprecatedConfigPath$.pipe(first()).toPromise(); + expect(deprecatedConfigPath).toEqual(mockedChangedPaths); + }); +}); diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index 91927b4c7b5c9..a80680bd46dfc 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -22,6 +22,7 @@ import { ConfigDeprecationProvider, configDeprecationFactory, DeprecatedConfigDetails, + ChangedDeprecatedPaths, } from './deprecation'; import { LegacyObjectToConfigAdapter } from './legacy'; @@ -36,6 +37,10 @@ export class ConfigService { private validated = false; private readonly config$: Observable; private lastConfig?: Config; + private readonly deprecatedConfigPaths = new BehaviorSubject({ + set: [], + unset: [], + }); /** * Whenever a config if read at a path, we mark that path as 'handled'. We can @@ -57,7 +62,8 @@ export class ConfigService { this.config$ = combineLatest([this.rawConfigProvider.getConfig$(), this.deprecations]).pipe( map(([rawConfig, deprecations]) => { const migrated = applyDeprecations(rawConfig, deprecations); - return new LegacyObjectToConfigAdapter(migrated); + this.deprecatedConfigPaths.next(migrated.changedPaths); + return new LegacyObjectToConfigAdapter(migrated.config); }), tap((config) => { this.lastConfig = config; @@ -191,6 +197,10 @@ export class ConfigService { return config.getFlattenedPaths().filter((path) => isPathHandled(path, handledPaths)); } + public getDeprecatedConfigPath$() { + return this.deprecatedConfigPaths.asObservable(); + } + private async logDeprecation() { const rawConfig = await this.rawConfigProvider.getConfig$().pipe(take(1)).toPromise(); const deprecations = await this.deprecations.pipe(take(1)).toPromise(); diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts index 47746967bbe5f..8ad1491c19c9b 100644 --- a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts +++ b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts @@ -82,7 +82,7 @@ describe('applyDeprecations', () => { it('returns the migrated config', () => { const initialConfig = { foo: 'bar', deprecated: 'deprecated', renamed: 'renamed' }; - const migrated = applyDeprecations(initialConfig, [ + const { config: migrated } = applyDeprecations(initialConfig, [ wrapHandler(deprecations.unused('deprecated')), wrapHandler(deprecations.rename('renamed', 'newname')), ]); @@ -93,7 +93,7 @@ describe('applyDeprecations', () => { it('does not alter the initial config', () => { const initialConfig = { foo: 'bar', deprecated: 'deprecated' }; - const migrated = applyDeprecations(initialConfig, [ + const { config: migrated } = applyDeprecations(initialConfig, [ wrapHandler(deprecations.unused('deprecated')), ]); @@ -110,7 +110,7 @@ describe('applyDeprecations', () => { return { unset: [{ path: 'unknown' }] }; }); - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( initialConfig, [wrapHandler(handler, 'pathA')], createAddDeprecation @@ -128,7 +128,7 @@ describe('applyDeprecations', () => { return { rewrite: [{ path: 'foo' }] }; }); - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( initialConfig, [wrapHandler(handler, 'pathA')], createAddDeprecation @@ -136,4 +136,25 @@ describe('applyDeprecations', () => { expect(migrated).toEqual(initialConfig); }); + + it('returns a list of changes config paths', () => { + const addDeprecation = jest.fn(); + const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation); + const initialConfig = { foo: 'bar', deprecated: 'deprecated' }; + + const handler = jest.fn().mockImplementation((config) => { + return { set: [{ path: 'foo', value: 'bar' }], unset: [{ path: 'baz' }] }; + }); + + const { changedPaths } = applyDeprecations( + initialConfig, + [wrapHandler(handler, 'pathA')], + createAddDeprecation + ); + + expect(changedPaths).toEqual({ + set: ['foo'], + unset: ['baz'], + }); + }); }); diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts index 092a5ced28371..d38ae98835831 100644 --- a/packages/kbn-config/src/deprecation/apply_deprecations.ts +++ b/packages/kbn-config/src/deprecation/apply_deprecations.ts @@ -8,7 +8,11 @@ import { cloneDeep, unset } from 'lodash'; import { set } from '@elastic/safer-lodash-set'; -import { ConfigDeprecationWithContext, AddConfigDeprecation } from './types'; +import type { + AddConfigDeprecation, + ChangedDeprecatedPaths, + ConfigDeprecationWithContext, +} from './types'; const noopAddDeprecationFactory: () => AddConfigDeprecation = () => () => undefined; /** @@ -22,22 +26,31 @@ export const applyDeprecations = ( config: Record, deprecations: ConfigDeprecationWithContext[], createAddDeprecation: (pluginId: string) => AddConfigDeprecation = noopAddDeprecationFactory -) => { +): { config: Record; changedPaths: ChangedDeprecatedPaths } => { const result = cloneDeep(config); + const changedPaths: ChangedDeprecatedPaths = { + set: [], + unset: [], + }; deprecations.forEach(({ deprecation, path }) => { const commands = deprecation(result, path, createAddDeprecation(path)); if (commands) { if (commands.set) { + changedPaths.set.push(...commands.set.map((c) => c.path)); commands.set.forEach(function ({ path: commandPath, value }) { set(result, commandPath, value); }); } if (commands.unset) { + changedPaths.unset.push(...commands.unset.map((c) => c.path)); commands.unset.forEach(function ({ path: commandPath }) { unset(result, commandPath); }); } } }); - return result; + return { + config: result, + changedPaths, + }; }; diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts index 48576e6d830be..ce10bafd9c575 100644 --- a/packages/kbn-config/src/deprecation/index.ts +++ b/packages/kbn-config/src/deprecation/index.ts @@ -14,6 +14,7 @@ export type { AddConfigDeprecation, ConfigDeprecationProvider, DeprecatedConfigDetails, + ChangedDeprecatedPaths, } from './types'; export { configDeprecationFactory } from './deprecation_factory'; export { applyDeprecations } from './apply_deprecations'; diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts index 6944f45c1e1d2..0522365ad76c1 100644 --- a/packages/kbn-config/src/deprecation/types.ts +++ b/packages/kbn-config/src/deprecation/types.ts @@ -55,6 +55,16 @@ export type ConfigDeprecation = ( addDeprecation: AddConfigDeprecation ) => void | ConfigDeprecationCommand; +/** + * List of config paths changed during deprecation. + * + * @public + */ +export interface ChangedDeprecatedPaths { + set: string[]; + unset: string[]; +} + /** * Outcome of deprecation operation. Allows mutating config values in a declarative way. * diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index cf875d3daa4a2..294caba4e7048 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -13,6 +13,7 @@ export type { ConfigDeprecationWithContext, ConfigDeprecation, ConfigDeprecationCommand, + ChangedDeprecatedPaths, } from './deprecation'; export { applyDeprecations, configDeprecationFactory } from './deprecation'; diff --git a/src/core/server/config/test_utils.ts b/src/core/server/config/test_utils.ts index 8e20e87e6f7d8..ab06ff50012b7 100644 --- a/src/core/server/config/test_utils.ts +++ b/src/core/server/config/test_utils.ts @@ -16,7 +16,7 @@ function collectDeprecations( ) { const deprecations = provider(configDeprecationFactory); const deprecationMessages: string[] = []; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( settings, deprecations.map((deprecation) => ({ deprecation, diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts index e09f595747c30..5fa67fecb2a8a 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts @@ -116,6 +116,10 @@ const createStartContractMock = () => { maxImportExportSize: 10000, maxImportPayloadBytes: 26214400, }, + deprecatedKeys: { + set: ['path.to.a.prop'], + unset: [], + }, }, environment: { memory: { diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index 5c6e1c3ac575f..b04700d6261e1 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -91,7 +91,8 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); const savedObjects = await savedObjectsStartPromise; expect(savedObjects.createInternalRepository).toHaveBeenCalledTimes(1); @@ -105,7 +106,13 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + const coreUsageData = service.setup({ + http, + metrics, + savedObjectsStartPromise, + changedDeprecatedConfigPath$, + }); const typeRegistry = typeRegistryMock.create(); coreUsageData.registerType(typeRegistry); @@ -126,7 +133,13 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + const coreUsageData = service.setup({ + http, + metrics, + savedObjectsStartPromise, + changedDeprecatedConfigPath$, + }); const usageStatsClient = coreUsageData.getClient(); expect(usageStatsClient).toBeInstanceOf(CoreUsageStatsClient); @@ -142,7 +155,11 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = new BehaviorSubject({ + set: ['new.path'], + unset: ['deprecated.path'], + }); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); const elasticsearch = elasticsearchServiceMock.createStart(); elasticsearch.client.asInternalUser.cat.indices.mockResolvedValueOnce({ body: [ @@ -180,6 +197,14 @@ describe('CoreUsageDataService', () => { expect(getCoreUsageData()).resolves.toMatchInlineSnapshot(` Object { "config": Object { + "deprecatedKeys": Object { + "set": Array [ + "new.path", + ], + "unset": Array [ + "deprecated.path", + ], + }, "elasticsearch": Object { "apiVersion": "7.x", "customHeadersConfigured": false, @@ -381,12 +406,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.enabled": "[redacted]", - "pluginAB.enabled": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.enabled': '[redacted]', + 'pluginAB.enabled': '[redacted]', + }); }); it('returns an object of plugin config usage', async () => { @@ -418,23 +441,21 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "elasticsearch.password": "[redacted]", - "elasticsearch.username": "[redacted]", - "logging.json": false, - "pluginA.arrayOfNumbers": "[redacted]", - "pluginA.enabled": true, - "pluginA.objectConfig.debug": true, - "pluginA.objectConfig.username": "[redacted]", - "pluginAB.enabled": false, - "pluginB.arrayOfObjects": "[redacted]", - "plugins.paths": "[redacted]", - "server.basePath": "/zvt", - "server.port": 5603, - "server.rewriteBasePath": true, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'elasticsearch.password': '[redacted]', + 'elasticsearch.username': '[redacted]', + 'logging.json': false, + 'pluginA.arrayOfNumbers': '[redacted]', + 'pluginA.enabled': true, + 'pluginA.objectConfig.debug': true, + 'pluginA.objectConfig.username': '[redacted]', + 'pluginAB.enabled': false, + 'pluginB.arrayOfObjects': '[redacted]', + 'plugins.paths': '[redacted]', + 'server.basePath': '/zvt', + 'server.port': 5603, + 'server.rewriteBasePath': true, + }); }); describe('config explicitly exposed to usage', () => { @@ -457,12 +478,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "server.basePath": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'server.basePath': '[redacted]', + }); }); it('returns config value on safe complete match', async () => { @@ -478,11 +497,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "server.basePath": "/zvt", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'server.basePath': '/zvt', + }); }); it('returns [redacted] on unsafe parent match', async () => { @@ -501,12 +518,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns config value on safe parent match', async () => { @@ -525,12 +540,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": true, - "pluginA.objectConfig.username": "some_user", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': true, + 'pluginA.objectConfig.username': 'some_user', + }); }); it('returns [redacted] on explicitly marked as safe array of objects', async () => { @@ -546,11 +559,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginB.arrayOfObjects": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginB.arrayOfObjects': '[redacted]', + }); }); it('returns values on explicitly marked as safe array of numbers', async () => { @@ -566,15 +577,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.arrayOfNumbers": Array [ - 1, - 2, - 3, - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.arrayOfNumbers': [1, 2, 3], + }); }); it('returns values on explicitly marked as safe array of strings', async () => { @@ -590,15 +595,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "plugins.paths": Array [ - "pluginA", - "pluginAB", - "pluginB", - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'plugins.paths': ['pluginA', 'pluginAB', 'pluginB'], + }); }); }); @@ -619,12 +618,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns config value on safe parent match', async () => { @@ -640,13 +637,11 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "elasticsearch.password": "[redacted]", - "elasticsearch.username": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'elasticsearch.password': '[redacted]', + 'elasticsearch.username': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns [redacted] on implicit array of objects', async () => { @@ -658,11 +653,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginB.arrayOfObjects": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginB.arrayOfObjects': '[redacted]', + }); }); it('returns values on implicit array of numbers', async () => { @@ -674,16 +667,11 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.arrayOfNumbers": Array [ - 1, - 2, - 3, - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.arrayOfNumbers': [1, 2, 3], + }); }); + it('returns [redacted] on implicit array of strings', async () => { configService.getUsedPaths.mockResolvedValue(['plugins.paths']); @@ -693,11 +681,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "plugins.paths": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'plugins.paths': '[redacted]', + }); }); it('returns config value for numbers', async () => { @@ -709,11 +695,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "server.port": 5603, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'server.port': 5603, + }); }); it('returns config value for booleans', async () => { @@ -728,12 +712,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "logging.json": false, - "pluginA.objectConfig.debug": true, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'logging.json': false, + 'pluginA.objectConfig.debug': true, + }); }); it('ignores exposed to usage configs but not used', async () => { @@ -749,11 +731,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "logging.json": false, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'logging.json': false, + }); }); }); }); @@ -779,7 +759,8 @@ describe('CoreUsageDataService', () => { savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); // Use the stopTimer$ to delay calling stop() until the third frame const stopTimer$ = cold('---a|'); diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts index 85abdca9ea5dc..dc24f889cd8dd 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { Subject } from 'rxjs'; +import { Subject, Observable } from 'rxjs'; import { takeUntil, first } from 'rxjs/operators'; import { get } from 'lodash'; -import { hasConfigPathIntersection } from '@kbn/config'; +import { hasConfigPathIntersection, ChangedDeprecatedPaths } from '@kbn/config'; import { CoreService } from 'src/core/types'; import { Logger, SavedObjectsServiceStart, SavedObjectTypeRegistry } from 'src/core/server'; @@ -39,6 +39,7 @@ export interface SetupDeps { http: InternalHttpServiceSetup; metrics: MetricsServiceSetup; savedObjectsStartPromise: Promise; + changedDeprecatedConfigPath$: Observable; } export interface StartDeps { @@ -89,6 +90,7 @@ export class CoreUsageDataService implements CoreService); } - setup({ http, metrics, savedObjectsStartPromise }: SetupDeps) { + setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }: SetupDeps) { metrics .getOpsMetrics$() .pipe(takeUntil(this.stop$)) @@ -417,6 +421,10 @@ export class CoreUsageDataService implements CoreService (this.deprecatedConfigPaths = deprecatedConfigPaths)); + const internalRepositoryPromise = savedObjectsStartPromise.then((savedObjects) => savedObjects.createInternalRepository([CORE_USAGE_STATS_TYPE]) ); diff --git a/src/core/server/core_usage_data/types.ts b/src/core/server/core_usage_data/types.ts index 1d5ef6d893f53..affd3d5c66ab7 100644 --- a/src/core/server/core_usage_data/types.ts +++ b/src/core/server/core_usage_data/types.ts @@ -254,6 +254,11 @@ export interface CoreConfigUsageData { // uiSettings: { // overridesCount: number; // }; + + deprecatedKeys: { + set: string[]; + unset: string[]; + }; } /** @internal */ diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index d9ad24a4a2c0c..7f108dbeb0086 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -399,6 +399,11 @@ export interface ContextSetup { // @internal export interface CoreConfigUsageData { + // (undocumented) + deprecatedKeys: { + set: string[]; + unset: string[]; + }; // (undocumented) elasticsearch: { sniffOnStart: boolean; diff --git a/src/core/server/server.ts b/src/core/server/server.ts index fcfca3a5e0e2f..4d99368f9bf70 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -153,6 +153,7 @@ export class Server { http: httpSetup, metrics: metricsSetup, savedObjectsStartPromise: this.savedObjectsStartPromise, + changedDeprecatedConfigPath$: this.configService.getDeprecatedConfigPath$(), }); const savedObjectsSetup = await this.savedObjects.setup({ @@ -265,6 +266,7 @@ export class Server { await this.http.start(); startTransaction?.end(); + return this.coreStart; } diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts index 3f39b5563ebc0..bf51e21bb9bf4 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts @@ -308,6 +308,23 @@ export function getCoreUsageCollector( }, }, }, + + deprecatedKeys: { + set: { + type: 'array', + items: { + type: 'keyword', + _meta: { description: 'Config path added during config deprecation.' }, + }, + }, + unset: { + type: 'array', + items: { + type: 'keyword', + _meta: { description: 'Config path removed during config deprecation.' }, + }, + }, + }, }, environment: { memory: { diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 24401b393278a..28752adb8c504 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -6950,6 +6950,28 @@ } } } + }, + "deprecatedKeys": { + "properties": { + "set": { + "type": "array", + "items": { + "type": "keyword", + "_meta": { + "description": "Config path added during config deprecation." + } + } + }, + "unset": { + "type": "array", + "items": { + "type": "keyword", + "_meta": { + "description": "Config path removed during config deprecation." + } + } + } + } } } }, diff --git a/x-pack/plugins/reporting/server/config/index.test.ts b/x-pack/plugins/reporting/server/config/index.test.ts index cba64500575aa..8f13fe8b53810 100644 --- a/x-pack/plugins/reporting/server/config/index.test.ts +++ b/x-pack/plugins/reporting/server/config/index.test.ts @@ -15,7 +15,7 @@ const applyReportingDeprecations = (settings: Record = {}) => { const deprecationMessages: string[] = []; const _config: any = {}; _config[CONFIG_PATH] = settings; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( _config, deprecations.map((deprecation) => ({ deprecation, diff --git a/x-pack/plugins/security/server/config_deprecations.test.ts b/x-pack/plugins/security/server/config_deprecations.test.ts index 80173dd42a49e..a233d760359e5 100644 --- a/x-pack/plugins/security/server/config_deprecations.test.ts +++ b/x-pack/plugins/security/server/config_deprecations.test.ts @@ -14,7 +14,7 @@ import { securityConfigDeprecationProvider } from './config_deprecations'; const applyConfigDeprecations = (settings: Record = {}) => { const deprecations = securityConfigDeprecationProvider(configDeprecationFactory); const deprecationMessages: string[] = []; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( settings, deprecations.map((deprecation) => ({ deprecation, diff --git a/x-pack/plugins/spaces/server/config.test.ts b/x-pack/plugins/spaces/server/config.test.ts index 1e60c1b635320..a6f8c37b293ef 100644 --- a/x-pack/plugins/spaces/server/config.test.ts +++ b/x-pack/plugins/spaces/server/config.test.ts @@ -13,7 +13,7 @@ import { spacesConfigDeprecationProvider } from './config'; const applyConfigDeprecations = (settings: Record = {}) => { const deprecations = spacesConfigDeprecationProvider(configDeprecationFactory); const deprecationMessages: string[] = []; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( settings, deprecations.map((deprecation) => ({ deprecation, diff --git a/x-pack/plugins/task_manager/server/index.test.ts b/x-pack/plugins/task_manager/server/index.test.ts index 3fce5f7bdfdf4..8eb98c39a2ccd 100644 --- a/x-pack/plugins/task_manager/server/index.test.ts +++ b/x-pack/plugins/task_manager/server/index.test.ts @@ -16,7 +16,7 @@ const applyTaskManagerDeprecations = (settings: Record = {}) => const _config = { [CONFIG_PATH]: settings, }; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( _config, deprecations.map((deprecation) => ({ deprecation,