mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Feature Flags Service] Hello world 👋 (#188562)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Jean-Louis Leysens <jloleysens@gmail.com>
This commit is contained in:
parent
38d6143f72
commit
02ce1b9101
164 changed files with 3605 additions and 1941 deletions
5
examples/feature_flags_example/README.md
Executable file
5
examples/feature_flags_example/README.md
Executable file
|
@ -0,0 +1,5 @@
|
|||
# featureFlagsExample
|
||||
|
||||
This plugin's goal is to demonstrate how to use the core feature flags service.
|
||||
|
||||
Refer to [the docs](../../packages/core/feature-flags/README.mdx) to know more.
|
12
examples/feature_flags_example/common/feature_flags.ts
Normal file
12
examples/feature_flags_example/common/feature_flags.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export const FeatureFlagExampleBoolean = 'example-boolean';
|
||||
export const FeatureFlagExampleString = 'example-string';
|
||||
export const FeatureFlagExampleNumber = 'example-number';
|
11
examples/feature_flags_example/common/index.ts
Normal file
11
examples/feature_flags_example/common/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export const PLUGIN_ID = 'featureFlagsExample';
|
||||
export const PLUGIN_NAME = 'Feature Flags Example';
|
13
examples/feature_flags_example/kibana.jsonc
Normal file
13
examples/feature_flags_example/kibana.jsonc
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/feature-flags-example-plugin",
|
||||
"owner": "@elastic/kibana-core",
|
||||
"description": "Plugin that shows how to make use of the feature flags core service.",
|
||||
"plugin": {
|
||||
"id": "featureFlagsExample",
|
||||
"server": true,
|
||||
"browser": true,
|
||||
"requiredPlugins": ["developerExamples"],
|
||||
"optionalPlugins": []
|
||||
}
|
||||
}
|
33
examples/feature_flags_example/public/application.tsx
Normal file
33
examples/feature_flags_example/public/application.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root';
|
||||
import { FeatureFlagsExampleApp } from './components/app';
|
||||
|
||||
export const renderApp = (coreStart: CoreStart, { element }: AppMountParameters) => {
|
||||
const { notifications, http, featureFlags } = coreStart;
|
||||
ReactDOM.render(
|
||||
<KibanaRootContextProvider {...coreStart}>
|
||||
<KibanaPageTemplate>
|
||||
<FeatureFlagsExampleApp
|
||||
featureFlags={featureFlags}
|
||||
notifications={notifications}
|
||||
http={http}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaRootContextProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
91
examples/feature_flags_example/public/components/app.tsx
Normal file
91
examples/feature_flags_example/public/components/app.tsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiHorizontalRule,
|
||||
EuiPageTemplate,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiListGroup,
|
||||
EuiListGroupItem,
|
||||
} from '@elastic/eui';
|
||||
import type { CoreStart, FeatureFlagsStart } from '@kbn/core/public';
|
||||
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import {
|
||||
FeatureFlagExampleBoolean,
|
||||
FeatureFlagExampleNumber,
|
||||
FeatureFlagExampleString,
|
||||
} from '../../common/feature_flags';
|
||||
import { PLUGIN_NAME } from '../../common';
|
||||
|
||||
interface FeatureFlagsExampleAppDeps {
|
||||
featureFlags: FeatureFlagsStart;
|
||||
notifications: CoreStart['notifications'];
|
||||
http: CoreStart['http'];
|
||||
}
|
||||
|
||||
export const FeatureFlagsExampleApp = ({ featureFlags }: FeatureFlagsExampleAppDeps) => {
|
||||
// Fetching the feature flags synchronously
|
||||
const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false);
|
||||
const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red');
|
||||
const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1);
|
||||
|
||||
// Use React Hooks to observe feature flags changes
|
||||
const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false));
|
||||
const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red'));
|
||||
const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1));
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPageTemplate>
|
||||
<EuiPageTemplate.Header>
|
||||
<EuiTitle size="l">
|
||||
<h1>{PLUGIN_NAME}</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageTemplate.Header>
|
||||
<EuiPageTemplate.Section>
|
||||
<EuiTitle>
|
||||
<h2>Demo of the feature flags service</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
To learn more, refer to{' '}
|
||||
<EuiLink
|
||||
href={'https://docs.elastic.dev/kibana-dev-docs/tutorials/feature-flags-service'}
|
||||
>
|
||||
the docs
|
||||
</EuiLink>
|
||||
.
|
||||
</p>
|
||||
<EuiHorizontalRule />
|
||||
<EuiListGroup>
|
||||
<p>
|
||||
The feature flags are:
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleBoolean}: ${bool}`} />
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleString}: ${str}`} />
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleNumber}: ${num}`} />
|
||||
</p>
|
||||
</EuiListGroup>
|
||||
<EuiListGroup>
|
||||
<p>
|
||||
The <strong>observed</strong> feature flags are:
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleBoolean}: ${bool$}`} />
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleString}: ${str$}`} />
|
||||
<EuiListGroupItem label={`${FeatureFlagExampleNumber}: ${num$}`} />
|
||||
</p>
|
||||
</EuiListGroup>
|
||||
</EuiText>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageTemplate>
|
||||
</>
|
||||
);
|
||||
};
|
14
examples/feature_flags_example/public/index.ts
Normal file
14
examples/feature_flags_example/public/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { FeatureFlagsExamplePlugin } from './plugin';
|
||||
|
||||
export function plugin() {
|
||||
return new FeatureFlagsExamplePlugin();
|
||||
}
|
40
examples/feature_flags_example/public/plugin.ts
Normal file
40
examples/feature_flags_example/public/plugin.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { AppPluginSetupDependencies } from './types';
|
||||
import { PLUGIN_NAME } from '../common';
|
||||
|
||||
export class FeatureFlagsExamplePlugin implements Plugin {
|
||||
public setup(core: CoreSetup, deps: AppPluginSetupDependencies) {
|
||||
// Register an application into the side navigation menu
|
||||
core.application.register({
|
||||
id: 'featureFlagsExample',
|
||||
title: PLUGIN_NAME,
|
||||
async mount(params: AppMountParameters) {
|
||||
// Load application bundle
|
||||
const { renderApp } = await import('./application');
|
||||
// Get start services as specified in kibana.json
|
||||
const [coreStart] = await core.getStartServices();
|
||||
// Render the application
|
||||
return renderApp(coreStart, params);
|
||||
},
|
||||
});
|
||||
|
||||
deps.developerExamples.register({
|
||||
appId: 'featureFlagsExample',
|
||||
title: PLUGIN_NAME,
|
||||
description: 'Plugin that shows how to make use of the feature flags core service.',
|
||||
});
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {}
|
||||
|
||||
public stop() {}
|
||||
}
|
14
examples/feature_flags_example/public/types.ts
Normal file
14
examples/feature_flags_example/public/types.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
|
||||
export interface AppPluginSetupDependencies {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
}
|
77
examples/feature_flags_example/server/index.ts
Normal file
77
examples/feature_flags_example/server/index.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { FeatureFlagDefinitions } from '@kbn/core-feature-flags-server';
|
||||
import type { PluginInitializerContext } from '@kbn/core-plugins-server';
|
||||
import {
|
||||
FeatureFlagExampleBoolean,
|
||||
FeatureFlagExampleNumber,
|
||||
FeatureFlagExampleString,
|
||||
} from '../common/feature_flags';
|
||||
|
||||
export const featureFlags: FeatureFlagDefinitions = [
|
||||
{
|
||||
key: FeatureFlagExampleBoolean,
|
||||
name: 'Example boolean',
|
||||
description: 'This is a demo of a boolean flag',
|
||||
tags: ['example', 'my-plugin'],
|
||||
variationType: 'boolean',
|
||||
variations: [
|
||||
{
|
||||
name: 'On',
|
||||
description: 'Auto-hides the bar',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
name: 'Off',
|
||||
description: 'Static always-on',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: FeatureFlagExampleString,
|
||||
name: 'Example string',
|
||||
description: 'This is a demo of a string flag',
|
||||
tags: ['example', 'my-plugin'],
|
||||
variationType: 'string',
|
||||
variations: [
|
||||
{
|
||||
name: 'Pink',
|
||||
value: '#D75489',
|
||||
},
|
||||
{
|
||||
name: 'Turquoise',
|
||||
value: '#65BAAF',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: FeatureFlagExampleNumber,
|
||||
name: 'Example Number',
|
||||
description: 'This is a demo of a number flag',
|
||||
tags: ['example', 'my-plugin'],
|
||||
variationType: 'number',
|
||||
variations: [
|
||||
{
|
||||
name: 'Five',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
name: 'Ten',
|
||||
value: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export async function plugin(initializerContext: PluginInitializerContext) {
|
||||
const { FeatureFlagsExamplePlugin } = await import('./plugin');
|
||||
return new FeatureFlagsExamplePlugin(initializerContext);
|
||||
}
|
69
examples/feature_flags_example/server/plugin.ts
Normal file
69
examples/feature_flags_example/server/plugin.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type {
|
||||
PluginInitializerContext,
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin,
|
||||
Logger,
|
||||
} from '@kbn/core/server';
|
||||
import { combineLatest } from 'rxjs';
|
||||
|
||||
import {
|
||||
FeatureFlagExampleBoolean,
|
||||
FeatureFlagExampleNumber,
|
||||
FeatureFlagExampleString,
|
||||
} from '../common/feature_flags';
|
||||
import { defineRoutes } from './routes';
|
||||
|
||||
export class FeatureFlagsExamplePlugin implements Plugin {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.logger = initializerContext.logger.get();
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup) {
|
||||
const router = core.http.createRouter();
|
||||
|
||||
// Register server side APIs
|
||||
defineRoutes(router);
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
// Promise form: when we need to fetch it once, like in an HTTP request
|
||||
void Promise.all([
|
||||
core.featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false),
|
||||
core.featureFlags.getStringValue(FeatureFlagExampleString, 'white'),
|
||||
core.featureFlags.getNumberValue(FeatureFlagExampleNumber, 1),
|
||||
]).then(([bool, str, num]) => {
|
||||
this.logger.info(`The feature flags are:
|
||||
- ${FeatureFlagExampleBoolean}: ${bool}
|
||||
- ${FeatureFlagExampleString}: ${str}
|
||||
- ${FeatureFlagExampleNumber}: ${num}
|
||||
`);
|
||||
});
|
||||
|
||||
// Observable form: when we need to react to the changes
|
||||
combineLatest([
|
||||
core.featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false),
|
||||
core.featureFlags.getStringValue$(FeatureFlagExampleString, 'red'),
|
||||
core.featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1),
|
||||
]).subscribe(([bool, str, num]) => {
|
||||
this.logger.info(`The observed feature flags are:
|
||||
- ${FeatureFlagExampleBoolean}: ${bool}
|
||||
- ${FeatureFlagExampleString}: ${str}
|
||||
- ${FeatureFlagExampleNumber}: ${num}
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
44
examples/feature_flags_example/server/routes/index.ts
Normal file
44
examples/feature_flags_example/server/routes/index.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { IRouter } from '@kbn/core/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { FeatureFlagExampleNumber } from '../../common/feature_flags';
|
||||
|
||||
export function defineRoutes(router: IRouter) {
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/api/feature_flags_example/example',
|
||||
access: 'public',
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: '2023-10-31',
|
||||
validate: {
|
||||
response: {
|
||||
200: {
|
||||
body: () =>
|
||||
schema.object({
|
||||
number: schema.number(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const { featureFlags } = await context.core;
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
number: await featureFlags.getNumberValue(FeatureFlagExampleNumber, 1),
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
24
examples/feature_flags_example/tsconfig.json
Normal file
24
examples/feature_flags_example/tsconfig.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"common/**/*.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../typings/**/*"
|
||||
],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
"@kbn/react-kibana-context-root",
|
||||
"@kbn/core-feature-flags-server",
|
||||
"@kbn/core-plugins-server",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/developer-examples-plugin",
|
||||
]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue