[eslint] prevent using constructor property params in initializers (#119130) (#119223)

Co-authored-by: Spencer <email@spalger.com>
This commit is contained in:
Kibana Machine 2021-11-19 15:43:48 -05:00 committed by GitHub
parent 9918816b0d
commit da882b900c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 429 additions and 138 deletions

View file

@ -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',
},
};

View file

@ -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) {}
constructor(private readonly silent: boolean) {
this.toolingLog = new ToolingLog({
level: this.silent ? 'silent' : 'info',
writeTo: {
write: (msg) => {
this.write(msg);
},
},
});
}
good(label: string, ...args: any[]) {
if (this.silent) {

View file

@ -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'),
},
};

View file

@ -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';

View file

@ -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);
}
},
}),
};

View file

@ -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`,
},
],
},
],
});

View file

@ -43,10 +43,12 @@ type ModuleFactory = (data: RequestData, callback: Callback<BundleRefModule>) =>
export class BundleRefsPlugin {
private readonly resolvedRefEntryCache = new Map<BundleRef, Promise<string>>();
private readonly resolvedRequestCache = new Map<string, Promise<string | undefined>>();
private readonly ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep;
private readonly ignorePrefix: string;
private allowedBundleIds = new Set<string>();
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

View file

@ -15,21 +15,27 @@ export type GetArgsType<T extends LifecycleEvent<any>> = T extends LifecycleEven
export class LifecycleEvent<Args extends readonly any[]> {
private readonly handlers: Array<(...args: Args) => Promise<void> | void> = [];
private readonly beforeSubj = this.options.singular
? new Rx.BehaviorSubject(undefined)
: new Rx.Subject<void>();
public readonly before$ = this.beforeSubj.asObservable();
private readonly beforeSubj: Rx.Subject<void>;
public readonly before$: Rx.Observable<void>;
private readonly afterSubj = this.options.singular
? new Rx.BehaviorSubject(undefined)
: new Rx.Subject<void>();
public readonly after$ = this.afterSubj.asObservable();
private readonly afterSubj: Rx.Subject<void>;
public readonly after$: Rx.Observable<void>;
constructor(
private readonly options: {
singular?: boolean;
} = {}
) {}
) {
this.beforeSubj = this.options.singular
? new Rx.BehaviorSubject<void>(undefined)
: new Rx.Subject<void>();
this.before$ = this.beforeSubj.asObservable();
this.afterSubj = this.options.singular
? new Rx.BehaviorSubject<void>(undefined)
: new Rx.Subject<void>();
this.after$ = this.afterSubj.asObservable();
}
public add(fn: (...args: Args) => Promise<void> | void) {
this.handlers.push(fn);

View file

@ -19,19 +19,23 @@ export class LifecyclePhase<Args extends readonly any[]> {
public triggered = false;
private readonly beforeSubj = new Rx.Subject<void>();
public readonly before$ = this.beforeSubj.asObservable();
private readonly beforeSubj: Rx.Subject<void>;
public readonly before$: Rx.Observable<void>;
private readonly afterSubj = this.options.singular
? new Rx.ReplaySubject<void>(1)
: new Rx.Subject<void>();
public readonly after$ = this.afterSubj.asObservable();
private readonly afterSubj: Rx.Subject<void>;
public readonly after$: Rx.Observable<void>;
constructor(
private readonly options: {
singular?: boolean;
} = {}
) {}
) {
this.beforeSubj = new Rx.Subject<void>();
this.before$ = this.beforeSubj.asObservable();
this.afterSubj = this.options.singular ? new Rx.ReplaySubject<void>(1) : new Rx.Subject<void>();
this.after$ = this.afterSubj.asObservable();
}
public add(fn: (...args: Args) => Promise<void> | void) {
this.handlers.push(fn);

View file

@ -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();

View file

@ -69,11 +69,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();

View file

@ -89,10 +89,10 @@ export interface PluginsServiceDiscoverDeps {
/** @internal */
export class PluginsService implements CoreService<PluginsServiceSetup, PluginsServiceStart> {
private readonly log: Logger;
private readonly prebootPluginsSystem = new PluginsSystem(this.coreContext, PluginType.preboot);
private readonly prebootPluginsSystem: PluginsSystem<PluginType.preboot>;
private arePrebootPluginsStopped = false;
private readonly prebootUiPluginInternalInfo = new Map<PluginName, InternalPluginInfo>();
private readonly standardPluginsSystem = new PluginsSystem(this.coreContext, PluginType.standard);
private readonly standardPluginsSystem: PluginsSystem<PluginType.standard>;
private readonly standardUiPluginInternalInfo = new Map<PluginName, InternalPluginInfo>();
private readonly configService: IConfigService;
private readonly config$: Observable<PluginsConfig>;
@ -105,6 +105,8 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
this.config$ = coreContext.configService
.atPath<PluginsConfigType>('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<DiscoveredPlugins> {

View file

@ -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<Promise<{ shouldReloadConfig: boolean } | undefined>> = [];
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 {

View file

@ -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 {

View file

@ -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`;
}
}

View file

@ -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<DashboardContainerServices>,
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);
}

View file

@ -27,11 +27,14 @@ export interface HomeServerPluginSetupDependencies {
}
export class HomeServerPlugin implements Plugin<HomeServerPluginSetup, HomeServerPluginStart> {
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;

View file

@ -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);
}

View file

@ -35,6 +35,10 @@ export type ActionDefinitionContext<Context extends object = object> =
| Context
| ActionExecutionContext<Context>;
export interface ActionMenuItemProps<Context extends object> {
context: ActionExecutionContext<Context>;
}
export interface Action<Context extends object = object>
extends Partial<Presentable<ActionExecutionContext<Context>>> {
/**
@ -68,7 +72,7 @@ export interface Action<Context extends object = object>
* `UiComponent` to render when displaying this action as a context menu item.
* If not provided, `getDisplayName` will be used instead.
*/
MenuItem?: UiComponent<{ context: ActionExecutionContext<Context> }>;
MenuItem?: UiComponent<ActionMenuItemProps<Context>>;
/**
* Returns a promise that resolves to true if this action is compatible given the context,

View file

@ -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<A extends ActionDefinition = ActionDefinition>
implements Action<Context<A>>, Presentable<Context<A>>
{
constructor(public readonly definition: A) {}
public readonly id: string;
public readonly type: string;
public readonly order: number;
public readonly MenuItem?: UiComponent<ActionMenuItemProps<Context<A>>>;
public readonly ReactMenuItem?: React.FC<ActionMenuItemProps<Context<A>>>;
public readonly grouping?: PresentableGrouping<Context<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<Context<A>> = this.definition.grouping;
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<A>) {
return this.definition.execute(context);

View file

@ -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';

View file

@ -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;
}
/**

View file

@ -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<Array<WebElement | WebElementWrapper>>,

View file

@ -55,17 +55,20 @@ export class SecurityPlugin
PluginStartDependencies
>
{
private readonly config = this.initializerContext.config.get<ConfigType>();
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<ConfigType>();
this.securityCheckupService = new SecurityCheckupService(this.config, localStorage);
}
public setup(
core: CoreSetup<PluginStartDependencies>,

View file

@ -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<AuthenticatorOptions>) {
this.session = this.options.session;
this.logger = this.options.loggers.get('authenticator');
const providerCommonOptions = {
client: this.options.clusterClient,
basePath: this.options.basePath,

View file

@ -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}`;
}
}

View file

@ -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(

View file

@ -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<void>;
constructor(private readonly options: Readonly<SessionIndexOptions>) {}
constructor(private readonly options: Readonly<SessionIndexOptions>) {
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

View file

@ -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',

View file

@ -107,9 +107,7 @@ export class DrilldownManagerState {
triggerIncompatible: !this.deps.triggers.find((t) => t === firstTrigger),
};
};
public readonly events$ = new BehaviorSubject<DrilldownTableItem[]>(
this.deps.dynamicActionManager.state.get().events.map(this.mapEventToDrilldownItem)
);
public readonly events$: BehaviorSubject<DrilldownTableItem[]>;
/**
* 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<DrilldownTableItem[]>(
this.deps.dynamicActionManager.state.get().events.map(this.mapEventToDrilldownItem)
);
deps.dynamicActionManager.state.state$
.pipe(map((state) => state.events.map(this.mapEventToDrilldownItem)))
.subscribe(this.events$);

View file

@ -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<Config, FactoryContext>,
PersistableState<SerializedEvent>
{
public readonly id: string;
public readonly isBeta: boolean;
public readonly minimalLicense?: LicenseType;
public readonly licenseFeatureName?: string;
public readonly order: number;
public readonly MenuItem?: UiComponent<ActionMenuItemProps<FactoryContext>>;
public readonly ReactMenuItem?: React.FC<ActionMenuItemProps<FactoryContext>>;
public readonly CollectConfig: UiComponent<CollectConfigProps<Config, FactoryContext>>;
public readonly ReactCollectConfig: React.FC<CollectConfigProps<Config, FactoryContext>>;
public readonly createConfig: (context: FactoryContext) => Config;
public readonly isConfigValid: (config: Config, context: FactoryContext) => boolean;
public readonly migrations: MigrateFunctionsObject;
constructor(
protected readonly def: ActionFactoryDefinition<Config, ExecutionContext, FactoryContext>,
protected readonly deps: ActionFactoryDeps
@ -43,22 +62,21 @@ export class ActionFactory<
`ActionFactory [actionFactory.id = ${def.id}] "licenseFeatureName" is required, if "minimalLicense" is provided`
);
}
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 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 || {};
public getIconType(context: FactoryContext): string | undefined {
if (!this.def.getIconType) return undefined;
return this.def.getIconType(context);