mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[RAM] Feature flagging for triggers actions UI plugin (#126957)
* Feature flagging for triggers actions UI Co-authored-by: Zacqary Adam Xeper <zacqary.xeper@elastic.co> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2577d36a08
commit
6084c972ee
9 changed files with 221 additions and 4 deletions
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export type ExperimentalFeatures = typeof allowedExperimentalValues;
|
||||
|
||||
/**
|
||||
* A list of allowed values that can be used in `xpack.triggersActionsUi.enableExperimental`.
|
||||
* This object is then used to validate and parse the value entered.
|
||||
*/
|
||||
export const allowedExperimentalValues = Object.freeze({
|
||||
rulesListDatagrid: true,
|
||||
rulesDetailLogs: false,
|
||||
});
|
||||
|
||||
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
|
||||
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
|
||||
const TriggersActionsUIInvalidExperimentalValue = class extends Error {};
|
||||
const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly<ExperimentalConfigKeys>;
|
||||
|
||||
/**
|
||||
* Parses the string value used in `xpack.triggersActionsUi.enableExperimental` kibana configuration,
|
||||
* which should be a string of values delimited by a comma (`,`)
|
||||
*
|
||||
* @param configValue
|
||||
* @throws TriggersActionsUIInvalidExperimentalValue
|
||||
*/
|
||||
export const parseExperimentalConfigValue = (configValue: string[]): ExperimentalFeatures => {
|
||||
const enabledFeatures: Mutable<Partial<ExperimentalFeatures>> = {};
|
||||
|
||||
for (const value of configValue) {
|
||||
if (!isValidExperimentalValue(value)) {
|
||||
throw new TriggersActionsUIInvalidExperimentalValue(`[${value}] is not valid.`);
|
||||
}
|
||||
|
||||
enabledFeatures[value as keyof ExperimentalFeatures] = true;
|
||||
}
|
||||
|
||||
return {
|
||||
...allowedExperimentalValues,
|
||||
...enabledFeatures,
|
||||
};
|
||||
};
|
||||
|
||||
export const isValidExperimentalValue = (value: string): boolean => {
|
||||
return allowedKeys.includes(value as keyof ExperimentalFeatures);
|
||||
};
|
||||
|
||||
export const getExperimentalAllowedValues = (): string[] => [...allowedKeys];
|
10
x-pack/plugins/triggers_actions_ui/common/types.ts
Normal file
10
x-pack/plugins/triggers_actions_ui/common/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export interface TriggersActionsUiConfigType {
|
||||
enableExperimental: string[];
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
|
||||
export class ExperimentalFeaturesService {
|
||||
private static experimentalFeatures?: ExperimentalFeatures;
|
||||
|
||||
public static init({ experimentalFeatures }: { experimentalFeatures: ExperimentalFeatures }) {
|
||||
this.experimentalFeatures = experimentalFeatures;
|
||||
}
|
||||
|
||||
public static get(): ExperimentalFeatures {
|
||||
if (!this.experimentalFeatures) {
|
||||
this.throwUninitializedError();
|
||||
}
|
||||
|
||||
return this.experimentalFeatures;
|
||||
}
|
||||
|
||||
private static throwUninitializedError(): never {
|
||||
throw new Error(
|
||||
'Experimental features services not initialized - are you trying to import this module from outside of the triggers actions UI app?'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ExperimentalFeaturesService } from './experimental_features_service';
|
||||
import { getIsExperimentalFeatureEnabled } from './get_experimental_features';
|
||||
|
||||
describe('getIsExperimentalFeatureEnabled', () => {
|
||||
it('getIsExperimentalFeatureEnabled returns the flag enablement', async () => {
|
||||
ExperimentalFeaturesService.init({
|
||||
experimentalFeatures: {
|
||||
rulesListDatagrid: true,
|
||||
rulesDetailLogs: false,
|
||||
},
|
||||
});
|
||||
|
||||
let result = getIsExperimentalFeatureEnabled('rulesListDatagrid');
|
||||
|
||||
expect(result).toEqual(true);
|
||||
|
||||
result = getIsExperimentalFeatureEnabled('rulesDetailLogs');
|
||||
|
||||
expect(result).toEqual(false);
|
||||
|
||||
expect(() => getIsExperimentalFeatureEnabled('doesNotExist' as any)).toThrowError(
|
||||
'Invalid enable value doesNotExist. Allowed values are: rulesListDatagrid, rulesDetailLogs'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
allowedExperimentalValues,
|
||||
isValidExperimentalValue,
|
||||
getExperimentalAllowedValues,
|
||||
} from '../../common/experimental_features';
|
||||
|
||||
const allowedExperimentalValueKeys = getExperimentalAllowedValues();
|
||||
|
||||
export const getIsExperimentalFeatureEnabled = (feature: keyof ExperimentalFeatures): boolean => {
|
||||
if (!isValidExperimentalValue(feature)) {
|
||||
throw new Error(
|
||||
`Invalid enable value ${feature}. Allowed values are: ${allowedExperimentalValueKeys.join(
|
||||
', '
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return allowedExperimentalValues[feature];
|
||||
};
|
|
@ -8,6 +8,7 @@
|
|||
// TODO: https://github.com/elastic/kibana/issues/110895
|
||||
/* eslint-disable @kbn/eslint/no_export_all */
|
||||
|
||||
import { PluginInitializerContext } from 'kibana/server';
|
||||
import { Plugin } from './plugin';
|
||||
|
||||
export type {
|
||||
|
@ -42,8 +43,8 @@ export { AlertConditions, AlertConditionsGroup } from './application/sections';
|
|||
|
||||
export * from './common';
|
||||
|
||||
export function plugin() {
|
||||
return new Plugin();
|
||||
export function plugin(context: PluginInitializerContext) {
|
||||
return new Plugin(context);
|
||||
}
|
||||
|
||||
export { Plugin };
|
||||
|
|
|
@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin as CorePlugin } from 'src/core/public';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ReactElement } from 'react';
|
||||
import { PluginInitializerContext } from 'kibana/public';
|
||||
import { FeaturesPluginStart } from '../../features/public';
|
||||
import { KibanaFeature } from '../../features/common';
|
||||
import { registerBuiltInActionTypes } from './application/components/builtin_action_types';
|
||||
|
@ -31,6 +32,11 @@ import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout';
|
|||
import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout';
|
||||
import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout';
|
||||
import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout';
|
||||
import { ExperimentalFeaturesService } from './common/experimental_features_service';
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
parseExperimentalConfigValue,
|
||||
} from '../common/experimental_features';
|
||||
|
||||
import type {
|
||||
ActionTypeModel,
|
||||
|
@ -40,6 +46,7 @@ import type {
|
|||
ConnectorAddFlyoutProps,
|
||||
ConnectorEditFlyoutProps,
|
||||
} from './types';
|
||||
import { TriggersActionsUiConfigType } from '../common/types';
|
||||
|
||||
export interface TriggersAndActionsUIPublicPluginSetup {
|
||||
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
|
||||
|
@ -89,16 +96,22 @@ export class Plugin
|
|||
{
|
||||
private actionTypeRegistry: TypeRegistry<ActionTypeModel>;
|
||||
private ruleTypeRegistry: TypeRegistry<RuleTypeModel>;
|
||||
private config: TriggersActionsUiConfigType;
|
||||
readonly experimentalFeatures: ExperimentalFeatures;
|
||||
|
||||
constructor() {
|
||||
constructor(ctx: PluginInitializerContext) {
|
||||
this.actionTypeRegistry = new TypeRegistry<ActionTypeModel>();
|
||||
this.ruleTypeRegistry = new TypeRegistry<RuleTypeModel>();
|
||||
this.config = ctx.config.get();
|
||||
this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental || []);
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup, plugins: PluginsSetup): TriggersAndActionsUIPublicPluginSetup {
|
||||
const actionTypeRegistry = this.actionTypeRegistry;
|
||||
const ruleTypeRegistry = this.ruleTypeRegistry;
|
||||
|
||||
ExperimentalFeaturesService.init({ experimentalFeatures: this.experimentalFeatures });
|
||||
|
||||
const featureTitle = i18n.translate('xpack.triggersActionsUI.managementSection.displayName', {
|
||||
defaultMessage: 'Rules and Connectors',
|
||||
});
|
||||
|
|
50
x-pack/plugins/triggers_actions_ui/server/config.ts
Normal file
50
x-pack/plugins/triggers_actions_ui/server/config.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { PluginInitializerContext } from '../../../../src/core/server';
|
||||
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
getExperimentalAllowedValues,
|
||||
isValidExperimentalValue,
|
||||
parseExperimentalConfigValue,
|
||||
} from '../common/experimental_features';
|
||||
|
||||
const allowedExperimentalValues = getExperimentalAllowedValues();
|
||||
|
||||
export const configSchema = schema.object({
|
||||
enableGeoTrackingThresholdAlert: schema.maybe(schema.boolean({ defaultValue: false })),
|
||||
enableExperimental: schema.arrayOf(schema.string(), {
|
||||
defaultValue: () => [],
|
||||
validate(list) {
|
||||
for (const key of list) {
|
||||
if (!isValidExperimentalValue(key)) {
|
||||
return `[${key}] is not allowed. Allowed values are: ${allowedExperimentalValues.join(
|
||||
', '
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export type ConfigSchema = TypeOf<typeof configSchema>;
|
||||
|
||||
export type ConfigType = ConfigSchema & {
|
||||
experimentalFeatures: ExperimentalFeatures;
|
||||
};
|
||||
|
||||
export const createConfig = (context: PluginInitializerContext): ConfigType => {
|
||||
const pluginConfig = context.config.get<TypeOf<typeof configSchema>>();
|
||||
const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental);
|
||||
|
||||
return {
|
||||
...pluginConfig,
|
||||
experimentalFeatures,
|
||||
};
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server';
|
||||
import { configSchema, ConfigSchema } from '../config';
|
||||
import { configSchema, ConfigSchema } from './config';
|
||||
import { TriggersActionsPlugin } from './plugin';
|
||||
|
||||
export type { PluginStartContract } from './plugin';
|
||||
|
@ -22,6 +22,7 @@ export {
|
|||
export const config: PluginConfigDescriptor<ConfigSchema> = {
|
||||
exposeToBrowser: {
|
||||
enableGeoTrackingThresholdAlert: true,
|
||||
enableExperimental: true,
|
||||
},
|
||||
schema: configSchema,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue