From 2c4e795e1807b0a6a681d20da49829b2512400cd Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 19 Nov 2021 11:11:22 -0800 Subject: [PATCH] [eslint] prevent using constructor property params in initializers (#119130) --- .../elastic-eslint-config-kibana/.eslintrc.js | 1 + packages/kbn-cli-dev-mode/src/log.ts | 20 ++-- packages/kbn-eslint-plugin-eslint/index.js | 1 + .../rules/no_async_promise_body.js | 4 - ...nstructor_args_in_property_initializers.js | 103 ++++++++++++++++++ ...ctor_args_in_property_initializers.test.js | 100 +++++++++++++++++ .../src/worker/bundle_refs_plugin.ts | 6 +- .../lib/lifecycle_event.ts | 24 ++-- .../lib/lifecycle_phase.ts | 18 +-- src/core/public/chrome/chrome_service.test.ts | 6 +- .../injected_metadata_service.ts | 10 +- src/core/server/plugins/plugins_service.ts | 6 +- src/core/server/preboot/preboot_service.ts | 7 +- src/dev/prs/github_api.ts | 22 ++-- src/dev/prs/pr.ts | 6 +- .../dashboard_container_factory.tsx | 12 +- src/plugins/home/server/plugin.ts | 7 +- .../control_group_container_factory.ts | 11 +- .../ui_actions/public/actions/action.ts | 6 +- .../public/actions/action_internal.ts | 26 +++-- src/plugins/ui_actions/public/index.ts | 2 +- test/functional/services/common/browser.ts | 8 +- .../web_element_wrapper.ts | 6 +- x-pack/plugins/security/public/plugin.tsx | 9 +- .../server/authentication/authenticator.ts | 8 +- .../server/authorization/actions/actions.ts | 36 +++--- x-pack/plugins/security/server/plugin.ts | 34 +++--- .../session_management/session_index.ts | 6 +- .../spaces/secure_spaces_client_wrapper.ts | 6 +- .../state/drilldown_manager_state.ts | 8 +- .../public/dynamic_actions/action_factory.ts | 50 ++++++--- 31 files changed, 430 insertions(+), 139 deletions(-) create mode 100644 packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.test.js diff --git a/packages/elastic-eslint-config-kibana/.eslintrc.js b/packages/elastic-eslint-config-kibana/.eslintrc.js index 99377540d38f7e..bc7b7aeed04999 100644 --- a/packages/elastic-eslint-config-kibana/.eslintrc.js +++ b/packages/elastic-eslint-config-kibana/.eslintrc.js @@ -104,5 +104,6 @@ module.exports = { '@kbn/eslint/no_async_promise_body': 'error', '@kbn/eslint/no_async_foreach': 'error', '@kbn/eslint/no_trailing_import_slash': 'error', + '@kbn/eslint/no_constructor_args_in_property_initializers': 'error', }, }; diff --git a/packages/kbn-cli-dev-mode/src/log.ts b/packages/kbn-cli-dev-mode/src/log.ts index 2cbd02b94a8442..dc38639f29e6e0 100644 --- a/packages/kbn-cli-dev-mode/src/log.ts +++ b/packages/kbn-cli-dev-mode/src/log.ts @@ -20,16 +20,18 @@ export interface Log { } export class CliLog implements Log { - public toolingLog = new ToolingLog({ - level: this.silent ? 'silent' : 'info', - writeTo: { - write: (msg) => { - this.write(msg); + public toolingLog: ToolingLog; + + constructor(private readonly silent: boolean) { + this.toolingLog = new ToolingLog({ + level: this.silent ? 'silent' : 'info', + writeTo: { + write: (msg) => { + this.write(msg); + }, }, - }, - }); - - constructor(private readonly silent: boolean) {} + }); + } good(label: string, ...args: any[]) { if (this.silent) { diff --git a/packages/kbn-eslint-plugin-eslint/index.js b/packages/kbn-eslint-plugin-eslint/index.js index 22d9c752d47459..68ffe967928215 100644 --- a/packages/kbn-eslint-plugin-eslint/index.js +++ b/packages/kbn-eslint-plugin-eslint/index.js @@ -16,5 +16,6 @@ module.exports = { no_async_promise_body: require('./rules/no_async_promise_body'), no_async_foreach: require('./rules/no_async_foreach'), no_trailing_import_slash: require('./rules/no_trailing_import_slash'), + no_constructor_args_in_property_initializers: require('./rules/no_constructor_args_in_property_initializers'), }, }; diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_async_promise_body.js b/packages/kbn-eslint-plugin-eslint/rules/no_async_promise_body.js index 317758fd3629ab..fef6cf76612a0a 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_async_promise_body.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_async_promise_body.js @@ -14,14 +14,10 @@ const esTypes = tsEstree.AST_NODE_TYPES; const babelTypes = require('@babel/types'); /** @typedef {import("eslint").Rule.RuleModule} Rule */ -/** @typedef {import("@typescript-eslint/parser").ParserServices} ParserServices */ /** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.Expression} Expression */ /** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.ArrowFunctionExpression} ArrowFunctionExpression */ /** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.FunctionExpression} FunctionExpression */ -/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.TryStatement} TryStatement */ /** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.NewExpression} NewExpression */ -/** @typedef {import("typescript").ExportDeclaration} ExportDeclaration */ -/** @typedef {import("eslint").Rule.RuleFixer} Fixer */ const ERROR_MSG = 'Passing an async function to the Promise constructor leads to a hidden promise being created and prevents handling rejections'; diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.js b/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.js new file mode 100644 index 00000000000000..e3666d1cd5fa83 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.js @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const tsEstree = require('@typescript-eslint/typescript-estree'); +const traverse = require('eslint-traverse'); +const esTypes = tsEstree.AST_NODE_TYPES; + +/** @typedef {import("eslint").Rule.RuleModule} Rule */ +/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.Node} Node */ +/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.ClassBody} ClassBody */ +/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.Parameter} Parameter */ +/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.TSParameterProperty} TSParameterProperty */ + +/** + * @param {Parameter} param + * @returns {param is TSParameterProperty} + */ +function isTsParameterProperty(param) { + return param.type === esTypes.TSParameterProperty; +} + +/** + * @param {string} arg + */ +const errorMsg = (arg) => + `The constructor argument "${arg}" can't be used in a class property intializer, define the property in the constructor instead`; + +/** @type {Rule} */ +module.exports = { + meta: { + schema: [], + }, + create: (context) => ({ + ClassBody(_) { + const node = /** @type {ClassBody} */ (_); + + const constructor = node.body.find( + (n) => n.type === esTypes.MethodDefinition && n.kind === 'constructor' + ); + + if (!constructor || constructor.type !== esTypes.MethodDefinition) { + return; + } + + const constructorArgProps = constructor.value.params + .filter(isTsParameterProperty) + .map((p) => { + if (p.parameter.type === esTypes.Identifier) { + return p.parameter.name; + } + + if ( + p.parameter.type === esTypes.AssignmentPattern && + p.parameter.left.type === esTypes.Identifier + ) { + return p.parameter.left.name; + } + }); + + if (!constructorArgProps.length) { + return; + } + + for (const prop of node.body) { + if (prop.type !== esTypes.PropertyDefinition) { + continue; + } + + const visitor = (path) => { + /** @type {Node} node */ + const node = path.node; + + if ( + node.type === esTypes.FunctionExpression || + node.type === esTypes.ArrowFunctionExpression + ) { + return traverse.STOP; + } + + if ( + node.type === esTypes.MemberExpression && + node.object.type === esTypes.ThisExpression && + node.property.type === esTypes.Identifier && + node.property.name && + constructorArgProps.includes(node.property.name) + ) { + context.report({ + message: errorMsg(node.property.name), + loc: node.property.loc, + }); + } + }; + + traverse(context, prop, visitor); + } + }, + }), +}; diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.test.js b/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.test.js new file mode 100644 index 00000000000000..af1d15982007d7 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/no_constructor_args_in_property_initializers.test.js @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const { RuleTester } = require('eslint'); +const rule = require('./no_constructor_args_in_property_initializers'); +const dedent = require('dedent'); + +const ruleTester = new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run('@kbn/eslint/no_constructor_args_in_property_initializers', rule, { + valid: [ + { + code: dedent` + class Foo { + bar = 'baz' + } + `, + }, + { + code: dedent` + class Foo { + bar = 'baz' + constructor(private readonly foo: Box) {} + } + `, + }, + { + code: dedent` + class Foo { + bar = 'baz' + constructor(private readonly foo: () => void) {} + + get = () => { + return this.foo() + } + } + `, + }, + ], + + invalid: [ + // no catch + { + code: dedent` + class Foo { + bar = this.foo.split().reverse() + constructor(private readonly foo: string) {} + } + `, + errors: [ + { + line: 2, + message: `The constructor argument "foo" can't be used in a class property intializer, define the property in the constructor instead`, + }, + ], + }, + { + code: dedent` + class Foo { + bar = this.foo() + constructor(private readonly foo: () => void) {} + } + `, + errors: [ + { + line: 2, + message: `The constructor argument "foo" can't be used in a class property intializer, define the property in the constructor instead`, + }, + ], + }, + { + code: dedent` + class Foo { + bar = this.foo() + constructor(private readonly foo: (() => void) = defaultValue) {} + } + `, + errors: [ + { + line: 2, + message: `The constructor argument "foo" can't be used in a class property intializer, define the property in the constructor instead`, + }, + ], + }, + ], +}); diff --git a/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts index 18b219336a92d8..6c768a0174b51a 100644 --- a/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts +++ b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts @@ -43,10 +43,12 @@ type ModuleFactory = (data: RequestData, callback: Callback) => export class BundleRefsPlugin { private readonly resolvedRefEntryCache = new Map>(); private readonly resolvedRequestCache = new Map>(); - private readonly ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep; + private readonly ignorePrefix: string; private allowedBundleIds = new Set(); - constructor(private readonly bundle: Bundle, private readonly bundleRefs: BundleRefs) {} + constructor(private readonly bundle: Bundle, private readonly bundleRefs: BundleRefs) { + this.ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep; + } /** * Called by webpack when the plugin is passed in the webpack config diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_event.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_event.ts index 6f4164d3e44f63..135111394648db 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_event.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_event.ts @@ -15,21 +15,27 @@ export type GetArgsType> = T extends LifecycleEven export class LifecycleEvent { private readonly handlers: Array<(...args: Args) => Promise | void> = []; - private readonly beforeSubj = this.options.singular - ? new Rx.BehaviorSubject(undefined) - : new Rx.Subject(); - public readonly before$ = this.beforeSubj.asObservable(); + private readonly beforeSubj: Rx.Subject; + public readonly before$: Rx.Observable; - private readonly afterSubj = this.options.singular - ? new Rx.BehaviorSubject(undefined) - : new Rx.Subject(); - public readonly after$ = this.afterSubj.asObservable(); + private readonly afterSubj: Rx.Subject; + public readonly after$: Rx.Observable; constructor( private readonly options: { singular?: boolean; } = {} - ) {} + ) { + this.beforeSubj = this.options.singular + ? new Rx.BehaviorSubject(undefined) + : new Rx.Subject(); + this.before$ = this.beforeSubj.asObservable(); + + this.afterSubj = this.options.singular + ? new Rx.BehaviorSubject(undefined) + : new Rx.Subject(); + this.after$ = this.afterSubj.asObservable(); + } public add(fn: (...args: Args) => Promise | void) { this.handlers.push(fn); diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts index a0367de8469683..09e7c6f3b8d159 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts @@ -19,19 +19,23 @@ export class LifecyclePhase { public triggered = false; - private readonly beforeSubj = new Rx.Subject(); - public readonly before$ = this.beforeSubj.asObservable(); + private readonly beforeSubj: Rx.Subject; + public readonly before$: Rx.Observable; - private readonly afterSubj = this.options.singular - ? new Rx.ReplaySubject(1) - : new Rx.Subject(); - public readonly after$ = this.afterSubj.asObservable(); + private readonly afterSubj: Rx.Subject; + public readonly after$: Rx.Observable; constructor( private readonly options: { singular?: boolean; } = {} - ) {} + ) { + this.beforeSubj = new Rx.Subject(); + this.before$ = this.beforeSubj.asObservable(); + + this.afterSubj = this.options.singular ? new Rx.ReplaySubject(1) : new Rx.Subject(); + this.after$ = this.afterSubj.asObservable(); + } public add(fn: (...args: Args) => Promise | void) { this.handlers.push(fn); diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index b3815c02674e1d..505f3fc7a23d28 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -21,10 +21,12 @@ import { ChromeService } from './chrome_service'; import { getAppInfo } from '../application/utils'; class FakeApp implements App { - public title = `${this.id} App`; + public title: string; public mount = () => () => {}; - constructor(public id: string, public chromeless?: boolean) {} + constructor(public id: string, public chromeless?: boolean) { + this.title = `${this.id} App`; + } } const store = new Map(); diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index bfda6d3f334ee9..7229cc903d317f 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -74,11 +74,13 @@ export interface InjectedMetadataParams { * @internal */ export class InjectedMetadataService { - private state = deepFreeze( - this.params.injectedMetadata - ) as InjectedMetadataParams['injectedMetadata']; + private state: InjectedMetadataParams['injectedMetadata']; - constructor(private readonly params: InjectedMetadataParams) {} + constructor(private readonly params: InjectedMetadataParams) { + this.state = deepFreeze( + this.params.injectedMetadata + ) as InjectedMetadataParams['injectedMetadata']; + } public start(): InjectedMetadataStart { return this.setup(); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 9def7554ccd09e..989cfed0778567 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -89,10 +89,10 @@ export interface PluginsServiceDiscoverDeps { /** @internal */ export class PluginsService implements CoreService { private readonly log: Logger; - private readonly prebootPluginsSystem = new PluginsSystem(this.coreContext, PluginType.preboot); + private readonly prebootPluginsSystem: PluginsSystem; private arePrebootPluginsStopped = false; private readonly prebootUiPluginInternalInfo = new Map(); - private readonly standardPluginsSystem = new PluginsSystem(this.coreContext, PluginType.standard); + private readonly standardPluginsSystem: PluginsSystem; private readonly standardUiPluginInternalInfo = new Map(); private readonly configService: IConfigService; private readonly config$: Observable; @@ -105,6 +105,8 @@ export class PluginsService implements CoreService('plugins') .pipe(map((rawConfig) => new PluginsConfig(rawConfig, coreContext.env))); + this.prebootPluginsSystem = new PluginsSystem(this.coreContext, PluginType.preboot); + this.standardPluginsSystem = new PluginsSystem(this.coreContext, PluginType.standard); } public async discover({ environment }: PluginsServiceDiscoverDeps): Promise { diff --git a/src/core/server/preboot/preboot_service.ts b/src/core/server/preboot/preboot_service.ts index 4313541ef91d3a..5795f4845493d2 100644 --- a/src/core/server/preboot/preboot_service.ts +++ b/src/core/server/preboot/preboot_service.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Logger } from '@kbn/logging'; import { CoreContext } from '../core_context'; import { InternalPrebootServicePreboot } from './types'; @@ -14,9 +15,11 @@ export class PrebootService { private readonly promiseList: Array> = []; private waitUntilCanSetupPromise?: Promise<{ shouldReloadConfig: boolean }>; private isSetupOnHold = false; - private readonly log = this.core.logger.get('preboot'); + private readonly log: Logger; - constructor(private readonly core: CoreContext) {} + constructor(private readonly core: CoreContext) { + this.log = this.core.logger.get('preboot'); + } public preboot(): InternalPrebootServicePreboot { return { diff --git a/src/dev/prs/github_api.ts b/src/dev/prs/github_api.ts index 2bf20439c7803f..8ffbe68efb5028 100644 --- a/src/dev/prs/github_api.ts +++ b/src/dev/prs/github_api.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import axios, { AxiosError, AxiosResponse } from 'axios'; +import axios, { AxiosError, AxiosResponse, AxiosInstance } from 'axios'; import { createFailError } from '@kbn/dev-utils'; @@ -23,16 +23,18 @@ const isRateLimitError = (error: any) => `${error.response.headers['X-RateLimit-Remaining']}` === '0'; export class GithubApi { - private api = axios.create({ - baseURL: 'https://api.github.com/', - headers: { - Accept: 'application/vnd.github.v3+json', - 'User-Agent': 'kibana/update_prs_cli', - ...(this.accessToken ? { Authorization: `token ${this.accessToken} ` } : {}), - }, - }); + private api: AxiosInstance; - constructor(private accessToken?: string) {} + constructor(private accessToken?: string) { + this.api = axios.create({ + baseURL: 'https://api.github.com/', + headers: { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'kibana/update_prs_cli', + ...(this.accessToken ? { Authorization: `token ${this.accessToken} ` } : {}), + }, + }); + } async getPrInfo(prNumber: number) { try { diff --git a/src/dev/prs/pr.ts b/src/dev/prs/pr.ts index 918ee6d3254c38..0a66e66371d743 100644 --- a/src/dev/prs/pr.ts +++ b/src/dev/prs/pr.ts @@ -21,12 +21,14 @@ export class Pr { return parseInt(input, 10); } - public readonly remoteRef = `pull/${this.number}/head`; + public readonly remoteRef: string; constructor( public readonly number: number, public readonly targetRef: string, public readonly owner: string, public readonly sourceBranch: string - ) {} + ) { + this.remoteRef = `pull/${this.number}/head`; + } } diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx index 1cb8d4ae269efc..f7cf329d0ae35d 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx @@ -43,10 +43,16 @@ export class DashboardContainerFactoryDefinition public readonly isContainerType = true; public readonly type = DASHBOARD_CONTAINER_TYPE; + public inject: EmbeddablePersistableStateService['inject']; + public extract: EmbeddablePersistableStateService['extract']; + constructor( private readonly getStartServices: () => Promise, private readonly persistableStateService: EmbeddablePersistableStateService - ) {} + ) { + this.inject = createInject(this.persistableStateService); + this.extract = createExtract(this.persistableStateService); + } public isEditable = async () => { // Currently unused for dashboards @@ -91,8 +97,4 @@ export class DashboardContainerFactoryDefinition return new DashboardContainerEmbeddable(initialInput, services, parent, controlGroup); }; - - public inject = createInject(this.persistableStateService); - - public extract = createExtract(this.persistableStateService); } diff --git a/src/plugins/home/server/plugin.ts b/src/plugins/home/server/plugin.ts index 98f7611655e55e..6f082dd561e933 100644 --- a/src/plugins/home/server/plugin.ts +++ b/src/plugins/home/server/plugin.ts @@ -27,11 +27,14 @@ export interface HomeServerPluginSetupDependencies { } export class HomeServerPlugin implements Plugin { - constructor(private readonly initContext: PluginInitializerContext) {} private readonly tutorialsRegistry = new TutorialsRegistry(); - private readonly sampleDataRegistry = new SampleDataRegistry(this.initContext); + private readonly sampleDataRegistry: SampleDataRegistry; private customIntegrations?: CustomIntegrationsPluginSetup; + constructor(private readonly initContext: PluginInitializerContext) { + this.sampleDataRegistry = new SampleDataRegistry(this.initContext); + } + public setup(core: CoreSetup, plugins: HomeServerPluginSetupDependencies): HomeServerPluginSetup { this.customIntegrations = plugins.customIntegrations; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts b/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts index c5b2972bf0d976..33d0d079d26f87 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts +++ b/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts @@ -27,7 +27,13 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition public readonly isContainerType = true; public readonly type = CONTROL_GROUP_TYPE; - constructor(private persistableStateService: EmbeddablePersistableStateService) {} + public inject: EmbeddablePersistableStateService['inject']; + public extract: EmbeddablePersistableStateService['extract']; + + constructor(private persistableStateService: EmbeddablePersistableStateService) { + this.inject = createControlGroupInject(this.persistableStateService); + this.extract = createControlGroupExtract(this.persistableStateService); + } public isEditable = async () => false; @@ -50,7 +56,4 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition const { ControlGroupContainer } = await import('./control_group_container'); return new ControlGroupContainer(initialInput, parent); }; - - public inject = createControlGroupInject(this.persistableStateService); - public extract = createControlGroupExtract(this.persistableStateService); } diff --git a/src/plugins/ui_actions/public/actions/action.ts b/src/plugins/ui_actions/public/actions/action.ts index 369e4888ddc731..2cca606ca9701b 100644 --- a/src/plugins/ui_actions/public/actions/action.ts +++ b/src/plugins/ui_actions/public/actions/action.ts @@ -35,6 +35,10 @@ export type ActionDefinitionContext = | Context | ActionExecutionContext; +export interface ActionMenuItemProps { + context: ActionExecutionContext; +} + export interface Action extends Partial>> { /** @@ -68,7 +72,7 @@ export interface Action * `UiComponent` to render when displaying this action as a context menu item. * If not provided, `getDisplayName` will be used instead. */ - MenuItem?: UiComponent<{ context: ActionExecutionContext }>; + MenuItem?: UiComponent>; /** * Returns a promise that resolves to true if this action is compatible given the context, diff --git a/src/plugins/ui_actions/public/actions/action_internal.ts b/src/plugins/ui_actions/public/actions/action_internal.ts index 9abddf39298332..9f5639b329cb4a 100644 --- a/src/plugins/ui_actions/public/actions/action_internal.ts +++ b/src/plugins/ui_actions/public/actions/action_internal.ts @@ -8,7 +8,8 @@ // @ts-ignore import React from 'react'; -import { Action, ActionContext as Context, ActionDefinition } from './action'; +import type { UiComponent } from 'src/plugins/kibana_utils/public'; +import { Action, ActionContext as Context, ActionDefinition, ActionMenuItemProps } from './action'; import { Presentable, PresentableGrouping } from '../util/presentable'; import { uiToReactComponent } from '../../../kibana_react/public'; @@ -18,14 +19,21 @@ import { uiToReactComponent } from '../../../kibana_react/public'; export class ActionInternal implements Action>, Presentable> { - constructor(public readonly definition: A) {} - - public readonly id: string = this.definition.id; - public readonly type: string = this.definition.type || ''; - public readonly order: number = this.definition.order || 0; - public readonly MenuItem? = this.definition.MenuItem; - public readonly ReactMenuItem? = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined; - public readonly grouping?: PresentableGrouping> = this.definition.grouping; + public readonly id: string; + public readonly type: string; + public readonly order: number; + public readonly MenuItem?: UiComponent>>; + public readonly ReactMenuItem?: React.FC>>; + public readonly grouping?: PresentableGrouping>; + + constructor(public readonly definition: A) { + this.id = this.definition.id; + this.type = this.definition.type || ''; + this.order = this.definition.order || 0; + this.MenuItem = this.definition.MenuItem; + this.ReactMenuItem = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined; + this.grouping = this.definition.grouping; + } public execute(context: Context) { return this.definition.execute(context); diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 8a6b7ed9314909..a93b5e3f958a96 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -38,4 +38,4 @@ export { ACTION_VISUALIZE_GEO_FIELD, ACTION_VISUALIZE_LENS_FIELD, } from './types'; -export type { ActionExecutionContext, ActionExecutionMeta } from './actions'; +export type { ActionExecutionContext, ActionExecutionMeta, ActionMenuItemProps } from './actions'; diff --git a/test/functional/services/common/browser.ts b/test/functional/services/common/browser.ts index 7581c17a58ebf1..9773091d675234 100644 --- a/test/functional/services/common/browser.ts +++ b/test/functional/services/common/browser.ts @@ -25,9 +25,8 @@ class BrowserService extends FtrService { * Keyboard events */ public readonly keys = Key; - public readonly isFirefox: boolean = this.browserType === Browsers.Firefox; - public readonly isChromium: boolean = - this.browserType === Browsers.Chrome || this.browserType === Browsers.ChromiumEdge; + public readonly isFirefox: boolean; + public readonly isChromium: boolean; private readonly log = this.ctx.getService('log'); @@ -37,6 +36,9 @@ class BrowserService extends FtrService { private readonly driver: WebDriver ) { super(ctx); + this.isFirefox = this.browserType === Browsers.Firefox; + this.isChromium = + this.browserType === Browsers.Chrome || this.browserType === Browsers.ChromiumEdge; } /** diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index d4fe5080bdfefb..332256307237dc 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -35,7 +35,7 @@ const RETRY_CLICK_RETRY_ON_ERRORS = [ export class WebElementWrapper { private By = By; private Keys = Key; - public isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes(this.browserType); + public isChromium: boolean; public static create( webElement: WebElement | WebElementWrapper, @@ -69,7 +69,9 @@ export class WebElementWrapper { private fixedHeaderHeight: number, private logger: ToolingLog, private browserType: Browsers - ) {} + ) { + this.isChromium = [Browsers.Chrome, Browsers.ChromiumEdge].includes(this.browserType); + } private async _findWithCustomTimeout( findFunction: () => Promise>, diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index 043cf0765ff31b..c2860ec059b8d5 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -55,17 +55,20 @@ export class SecurityPlugin PluginStartDependencies > { - private readonly config = this.initializerContext.config.get(); + private readonly config: ConfigType; private sessionTimeout!: SessionTimeout; private readonly authenticationService = new AuthenticationService(); private readonly navControlService = new SecurityNavControlService(); private readonly securityLicenseService = new SecurityLicenseService(); private readonly managementService = new ManagementService(); - private readonly securityCheckupService = new SecurityCheckupService(this.config, localStorage); + private readonly securityCheckupService: SecurityCheckupService; private readonly anonymousAccessService = new AnonymousAccessService(); private authc!: AuthenticationServiceSetup; - constructor(private readonly initializerContext: PluginInitializerContext) {} + constructor(private readonly initializerContext: PluginInitializerContext) { + this.config = this.initializerContext.config.get(); + this.securityCheckupService = new SecurityCheckupService(this.config, localStorage); + } public setup( core: CoreSetup, diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index ec804668c6078c..164cc1b027b37c 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { Logger } from '@kbn/logging'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { IBasePath, IClusterClient, LoggerFactory } from 'src/core/server'; @@ -196,18 +197,21 @@ export class Authenticator { /** * Session instance. */ - private readonly session = this.options.session; + private readonly session: AuthenticatorOptions['session']; /** * Internal authenticator logger. */ - private readonly logger = this.options.loggers.get('authenticator'); + private readonly logger: Logger; /** * Instantiates Authenticator and bootstrap configured providers. * @param options Authenticator options. */ constructor(private readonly options: Readonly) { + this.session = this.options.session; + this.logger = this.options.loggers.get('authenticator'); + const providerCommonOptions = { client: this.options.clusterClient, basePath: this.options.basePath, diff --git a/x-pack/plugins/security/server/authorization/actions/actions.ts b/x-pack/plugins/security/server/authorization/actions/actions.ts index 0234c3bc82042d..e523c866a58b84 100644 --- a/x-pack/plugins/security/server/authorization/actions/actions.ts +++ b/x-pack/plugins/security/server/authorization/actions/actions.ts @@ -18,27 +18,29 @@ import { UIActions } from './ui'; * by the various `checkPrivilegesWithRequest` derivatives. */ export class Actions { - public readonly api = new ApiActions(this.versionNumber); - - public readonly app = new AppActions(this.versionNumber); - - public readonly cases = new CasesActions(this.versionNumber); - - public readonly login = 'login:'; - - public readonly savedObject = new SavedObjectActions(this.versionNumber); - - public readonly alerting = new AlertingActions(this.versionNumber); - - public readonly space = new SpaceActions(this.versionNumber); - - public readonly ui = new UIActions(this.versionNumber); - - public readonly version = `version:${this.versionNumber}`; + public readonly api: ApiActions; + public readonly app: AppActions; + public readonly cases: CasesActions; + public readonly login: string; + public readonly savedObject: SavedObjectActions; + public readonly alerting: AlertingActions; + public readonly space: SpaceActions; + public readonly ui: UIActions; + public readonly version: string; constructor(private readonly versionNumber: string) { if (versionNumber === '') { throw new Error(`version can't be an empty string`); } + + this.api = new ApiActions(this.versionNumber); + this.app = new AppActions(this.versionNumber); + this.cases = new CasesActions(this.versionNumber); + this.login = 'login:'; + this.savedObject = new SavedObjectActions(this.versionNumber); + this.alerting = new AlertingActions(this.versionNumber); + this.space = new SpaceActions(this.versionNumber); + this.ui = new UIActions(this.versionNumber); + this.version = `version:${this.versionNumber}`; } } diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 80082e9b7dbce3..e8f7aa2aacfdd3 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -153,9 +153,7 @@ export class SecurityPlugin return this.kibanaIndexName; }; - private readonly authenticationService = new AuthenticationService( - this.initializerContext.logger.get('authentication') - ); + private readonly authenticationService: AuthenticationService; private authenticationStart?: InternalAuthenticationServiceStart; private readonly getAuthentication = () => { if (!this.authenticationStart) { @@ -173,19 +171,12 @@ export class SecurityPlugin return this.featureUsageServiceStart; }; - private readonly auditService = new AuditService(this.initializerContext.logger.get('audit')); + private readonly auditService: AuditService; private readonly securityLicenseService = new SecurityLicenseService(); private readonly authorizationService = new AuthorizationService(); - private readonly elasticsearchService = new ElasticsearchService( - this.initializerContext.logger.get('elasticsearch') - ); - private readonly sessionManagementService = new SessionManagementService( - this.initializerContext.logger.get('session') - ); - private readonly anonymousAccessService = new AnonymousAccessService( - this.initializerContext.logger.get('anonymous-access'), - this.getConfig - ); + private readonly elasticsearchService: ElasticsearchService; + private readonly sessionManagementService: SessionManagementService; + private readonly anonymousAccessService: AnonymousAccessService; private anonymousAccessStart?: AnonymousAccessServiceStart; private readonly getAnonymousAccess = () => { if (!this.anonymousAccessStart) { @@ -196,6 +187,21 @@ export class SecurityPlugin constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); + + this.authenticationService = new AuthenticationService( + this.initializerContext.logger.get('authentication') + ); + this.auditService = new AuditService(this.initializerContext.logger.get('audit')); + this.elasticsearchService = new ElasticsearchService( + this.initializerContext.logger.get('elasticsearch') + ); + this.sessionManagementService = new SessionManagementService( + this.initializerContext.logger.get('session') + ); + this.anonymousAccessService = new AnonymousAccessService( + this.initializerContext.logger.get('anonymous-access'), + this.getConfig + ); } public setup( diff --git a/x-pack/plugins/security/server/session_management/session_index.ts b/x-pack/plugins/security/server/session_management/session_index.ts index fd993f0fb843a9..58ee0d69565116 100644 --- a/x-pack/plugins/security/server/session_management/session_index.ts +++ b/x-pack/plugins/security/server/session_management/session_index.ts @@ -132,7 +132,7 @@ export class SessionIndex { /** * Name of the index to store session information in. */ - private readonly indexName = `${this.options.kibanaIndexName}_security_session_${SESSION_INDEX_TEMPLATE_VERSION}`; + private readonly indexName: string; /** * Promise that tracks session index initialization process. We'll need to get rid of this as soon @@ -142,7 +142,9 @@ export class SessionIndex { */ private indexInitialization?: Promise; - constructor(private readonly options: Readonly) {} + constructor(private readonly options: Readonly) { + this.indexName = `${this.options.kibanaIndexName}_security_session_${SESSION_INDEX_TEMPLATE_VERSION}`; + } /** * Retrieves session value with the specified ID from the index. If session value isn't found diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts index e433e9c366df51..9d20a6ea40b24c 100644 --- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts +++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts @@ -44,7 +44,7 @@ const PURPOSE_PRIVILEGE_MAP: Record< export const LEGACY_URL_ALIAS_TYPE = 'legacy-url-alias'; export class SecureSpacesClientWrapper implements ISpacesClient { - private readonly useRbac = this.authorization.mode.useRbacForRequest(this.request); + private readonly useRbac: boolean; constructor( private readonly spacesClient: ISpacesClient, @@ -52,7 +52,9 @@ export class SecureSpacesClientWrapper implements ISpacesClient { private readonly authorization: AuthorizationServiceSetup, private readonly auditLogger: AuditLogger, private readonly errors: SavedObjectsClientContract['errors'] - ) {} + ) { + this.useRbac = this.authorization.mode.useRbacForRequest(this.request); + } public async getAll({ purpose = 'any', diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts index e363d154dc141b..15997355a2ae24 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts @@ -107,9 +107,7 @@ export class DrilldownManagerState { triggerIncompatible: !this.deps.triggers.find((t) => t === firstTrigger), }; }; - public readonly events$ = new BehaviorSubject( - this.deps.dynamicActionManager.state.get().events.map(this.mapEventToDrilldownItem) - ); + public readonly events$: BehaviorSubject; /** * State for each drilldown type used for new drilldown creation, so when user @@ -136,6 +134,10 @@ export class DrilldownManagerState { (factory) => !factory.isCompatibleLicense ); + this.events$ = new BehaviorSubject( + this.deps.dynamicActionManager.state.get().events.map(this.mapEventToDrilldownItem) + ); + deps.dynamicActionManager.state.state$ .pipe(map((state) => state.events.map(this.mapEventToDrilldownItem))) .subscribe(this.events$); diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts index 888d28a17547fa..4115e3febe2bdf 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts @@ -5,8 +5,13 @@ * 2.0. */ +import type { UiComponent, CollectConfigProps } from 'src/plugins/kibana_utils/public'; +import type { MigrateFunctionsObject } from 'src/plugins/kibana_utils/common'; import { uiToReactComponent } from '../../../../../src/plugins/kibana_react/public'; -import type { UiActionsPresentable as Presentable } from '../../../../../src/plugins/ui_actions/public'; +import type { + UiActionsPresentable as Presentable, + ActionMenuItemProps, +} from '../../../../../src/plugins/ui_actions/public'; import type { ActionFactoryDefinition } from './action_factory_definition'; import type { Configurable } from '../../../../../src/plugins/kibana_utils/public'; import type { @@ -15,7 +20,7 @@ import type { SerializedAction, SerializedEvent, } from './types'; -import type { ILicense, LicensingPluginStart } from '../../../licensing/public'; +import type { ILicense, LicensingPluginStart, LicenseType } from '../../../licensing/public'; import type { UiActionsActionDefinition as ActionDefinition } from '../../../../../src/plugins/ui_actions/public'; import type { SavedObjectReference } from '../../../../../src/core/types'; import type { PersistableState } from '../../../../../src/plugins/kibana_utils/common'; @@ -34,6 +39,20 @@ export class ActionFactory< Configurable, PersistableState { + public readonly id: string; + public readonly isBeta: boolean; + public readonly minimalLicense?: LicenseType; + public readonly licenseFeatureName?: string; + public readonly order: number; + public readonly MenuItem?: UiComponent>; + public readonly ReactMenuItem?: React.FC>; + + public readonly CollectConfig: UiComponent>; + public readonly ReactCollectConfig: React.FC>; + public readonly createConfig: (context: FactoryContext) => Config; + public readonly isConfigValid: (config: Config, context: FactoryContext) => boolean; + public readonly migrations: MigrateFunctionsObject; + constructor( protected readonly def: ActionFactoryDefinition, protected readonly deps: ActionFactoryDeps @@ -43,21 +62,20 @@ export class ActionFactory< `ActionFactory [actionFactory.id = ${def.id}] "licenseFeatureName" is required, if "minimalLicense" is provided` ); } - } - public readonly id = this.def.id; - public readonly isBeta = this.def.isBeta ?? false; - public readonly minimalLicense = this.def.minimalLicense; - public readonly licenseFeatureName = this.def.licenseFeatureName; - public readonly order = this.def.order || 0; - public readonly MenuItem? = this.def.MenuItem; - public readonly ReactMenuItem? = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined; - - public readonly CollectConfig = this.def.CollectConfig; - public readonly ReactCollectConfig = uiToReactComponent(this.CollectConfig); - public readonly createConfig = this.def.createConfig; - public readonly isConfigValid = this.def.isConfigValid; - public readonly migrations = this.def.migrations || {}; + this.id = this.def.id; + this.isBeta = this.def.isBeta ?? false; + this.minimalLicense = this.def.minimalLicense; + this.licenseFeatureName = this.def.licenseFeatureName; + this.order = this.def.order || 0; + this.MenuItem = this.def.MenuItem; + this.ReactMenuItem = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined; + this.CollectConfig = this.def.CollectConfig; + this.ReactCollectConfig = uiToReactComponent(this.CollectConfig); + this.createConfig = this.def.createConfig; + this.isConfigValid = this.def.isConfigValid; + this.migrations = this.def.migrations || {}; + } public getIconType(context: FactoryContext): string | undefined { if (!this.def.getIconType) return undefined;