Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NP] Expose global config to the plugins #51478

Merged
merged 31 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a7d5e97
[NP] Expose global config to the plugins
afharo Nov 20, 2019
395f26f
Merge branch 'master' into np-expose-global-config
elasticmachine Nov 25, 2019
7ea1c50
globalConfig in Plugin context: expose read-only methods only
afharo Nov 25, 2019
d9ec3d3
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 26, 2019
4e31b77
SharedGlobalConfig rework + Moving pkg, fromRoot & path utils from le…
afharo Nov 27, 2019
88bc0b1
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 27, 2019
592e935
Updated API docs
afharo Nov 27, 2019
db6b130
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 27, 2019
5f7c0e3
Fix test references to the moved utils
afharo Nov 27, 2019
7b7ea57
Replace zip with combineLatest
afharo Nov 28, 2019
82b62c8
Change tests to describe/it + remove "(deprecated)" from the test des…
afharo Nov 29, 2019
6caab53
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 29, 2019
dc9b692
Moving path files to a folder + exposing the config path in the contract
afharo Nov 29, 2019
e341ee2
deepFreeze the globalConfig in the pluginContext
afharo Nov 29, 2019
edd8760
Fix types in tests with new path.config
afharo Nov 29, 2019
03ecab3
Merge branch 'master' into np-expose-global-config
elasticmachine Dec 2, 2019
3502f9a
Move fromRoot and package_json utils to core/server/utils
afharo Dec 2, 2019
146161a
Rename globalConfig to legacy.globalConfig$
afharo Dec 2, 2019
8f67cc9
path.config renamed to path.configDir (not renaming path.data because…
afharo Dec 2, 2019
e729d5e
Change configDir in mocker as well
afharo Dec 2, 2019
2bc7916
Fix test after config renamed to configDir
afharo Dec 2, 2019
684e89d
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 3, 2019
83f1d80
Fix API docs conflicts
afharo Dec 3, 2019
23a34f8
Rename the path properties when exposing them
afharo Dec 3, 2019
4847fe9
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 3, 2019
4adebd5
path.configDir removed from the path config-schema
afharo Dec 3, 2019
31cbab2
Remove path.configDir. It is already in env.configs
afharo Dec 3, 2019
535f50e
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 4, 2019
79b2133
Add Migration documentation and examples
afharo Dec 4, 2019
5951986
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 4, 2019
9337633
Fix 'kibana/server' imports in the MIGRATION docs
afharo Dec 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

```typescript
config: {
globalConfig__deprecated$: Observable<Pick<Config, 'has' | 'get' | 'getFlattenedPaths'>>;
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface PluginInitializerContext<ConfigSchema = unknown>

| Property | Type | Description |
| --- | --- | --- |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> globalConfig__deprecated$: Observable&lt;Pick&lt;Config, 'has' &#124; 'get' &#124; 'getFlattenedPaths'&gt;&gt;;</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [env](./kibana-plugin-server.plugininitializercontext.env.md) | <code>{</code><br/><code> mode: EnvironmentMode;</code><br/><code> packageInfo: Readonly&lt;PackageInfo&gt;;</code><br/><code> }</code> | |
| [logger](./kibana-plugin-server.plugininitializercontext.logger.md) | <code>LoggerFactory</code> | |
| [opaqueId](./kibana-plugin-server.plugininitializercontext.opaqueid.md) | <code>PluginOpaqueId</code> | |
Expand Down
6 changes: 4 additions & 2 deletions src/core/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { of } from 'rxjs';
import { of, BehaviorSubject } from 'rxjs';
import { PluginInitializerContext, CoreSetup, CoreStart } from '.';
import { loggingServiceMock } from './logging/logging_service.mock';
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
import { httpServiceMock } from './http/http_service.mock';
import { contextServiceMock } from './context/context_service.mock';
import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
import { ObjectToConfigAdapter } from './config';

export { httpServerMock } from './http/http_server.mocks';
export { sessionStorageMock } from './http/cookie_session_storage.mocks';
Expand All @@ -33,8 +34,9 @@ export { loggingServiceMock } from './logging/logging_service.mock';
export { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock';
export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';

export function pluginInitializerContextConfigMock<T>(config: T) {
export function pluginInitializerContextConfigMock<T>(config: T, globalConfig = {}) {
const mock: jest.Mocked<PluginInitializerContext<T>['config']> = {
globalConfig__deprecated$: new BehaviorSubject(new ObjectToConfigAdapter(globalConfig)),
create: jest.fn().mockReturnValue(of(config)),
createIfExists: jest.fn().mockReturnValue(of(config)),
};
Expand Down
72 changes: 72 additions & 0 deletions src/core/server/plugins/plugin_context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { createPluginInitializerContext } from './plugin_context';
import { CoreContext } from '../core_context';
import { Env } from '../config';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { getEnvOptions } from '../config/__mocks__/env';
import { PluginManifest } from './types';
import { first } from 'rxjs/operators';

const logger = loggingServiceMock.create();
const configService = configServiceMock.create({ getConfig$: { globalConfig: { subpath: 1 } } });

let coreId: symbol;
let env: Env;
let coreContext: CoreContext;

function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): PluginManifest {
return {
id: 'some-plugin-id',
version: 'some-version',
configPath: 'path',
kibanaVersion: '7.0.0',
requiredPlugins: ['some-required-dep'],
optionalPlugins: ['some-optional-dep'],
server: true,
ui: true,
...manifestProps,
};
}

beforeEach(() => {
coreId = Symbol('core');
env = Env.createDefault(getEnvOptions());
coreContext = { coreId, env, logger, configService };
});

test('should return a globalConfig handler in the context (to be deprecated)', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I think the junit test-based suites is kind of the old way of structuring test files, I would prefer a jasmine describe/it structure. Can someone confirm that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originally coming from mocha I'm happier with the describe/it, but I saw it is simply an alias of test and, according to the jest documentation, they root for test https://jestjs.io/docs/en/api

I don't mind one or another. Whatever we want to go for as a team :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: don't think the test name should have the (to be deprecated) part. This is outside of the scope of testing imho

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests will need to be changed once we come up with a solution for the long-run. So they are likely to be changed as well as the behaviour. That's why I wanted to make it clear in the test name: it's kind of a warning of "this should work until we decide to rethink this"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the variable name already provides this info. doesn't it?

const manifest = createPluginManifest();
const opaqueId = Symbol();
const pluginInitializerContext = createPluginInitializerContext(coreContext, opaqueId, manifest);

expect(pluginInitializerContext.config.globalConfig__deprecated$).toBeDefined();

const configObject = await pluginInitializerContext.config.globalConfig__deprecated$
.pipe(first())
.toPromise();

const configPaths = configObject.getFlattenedPaths();
expect(configPaths).toHaveLength(1);
expect(configPaths).toStrictEqual(['globalConfig.subpath']);
expect(configObject.has('globalConfig.subpath')).toBe(true);
expect(configObject.get('globalConfig.subpath')).toBe(1);
});
18 changes: 17 additions & 1 deletion src/core/server/plugins/plugin_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
* under the License.
*/

import { map } from 'rxjs/operators';
import { CoreContext } from '../core_context';
import { PluginWrapper } from './plugin';
import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service';
import { PluginInitializerContext, PluginManifest, PluginOpaqueId } from './types';
import { CoreSetup, CoreStart } from '..';
import { CoreSetup, CoreStart, ConfigPath } from '..';

/**
* This returns a facade for `CoreContext` that will be exposed to the plugin initializer.
Expand Down Expand Up @@ -65,6 +66,19 @@ export function createPluginInitializerContext(
* Core configuration functionality, enables fetching a subset of the config.
*/
config: {
/**
* Global configuration
* Note: naming not final here, it will be renamed in a near future (https:/elastic/kibana/issues/46240)
* @deprecated
*/
globalConfig__deprecated$: coreContext.configService.getConfig$().pipe(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should only expose whitelisted fields. We don't want to expose the entire config tree to any plugin, only a handpicked list of configs that we know are needed by many plugins (I believe they should be listed in the issue).

I would expect this to look more like:

type SharedGlobalConfig = Readonly<{
  // Maybe use a recursive readonly type if we have one
  kibana: Readonly<{
   defaultAppId: string;
   index: string;
   disabledWelcomeScreen: string;
   // ...etc
 }>;
 path: Readonly<{
  data: string;
 }>;
 // ...etc
}

globalConfig__deprecated$: Observable<SharedGlobalConfig>;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! Thanks! I misunderstood the conversation in the original issue then. I'll rework this :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this is actually way better than my suggestion

map(conf => ({
has: (configPath: ConfigPath) => conf.has(configPath),
get: (configPath: ConfigPath) => conf.get(configPath),
getFlattenedPaths: () => conf.getFlattenedPaths(),
}))
),

/**
* Reads the subset of the config at the `configPath` defined in the plugin
* manifest and validates it against the schema in the static `schema` on
Expand All @@ -82,6 +96,8 @@ export function createPluginInitializerContext(
};
}

// function exposeReadOnlyMethods(conf: )

/**
* This returns a facade for `CoreContext` that will be exposed to the plugin `setup` method.
* This facade should be safe to use only within `setup` itself.
Expand Down
3 changes: 2 additions & 1 deletion src/core/server/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { Observable } from 'rxjs';
import { Type } from '@kbn/config-schema';

import { ConfigPath, EnvironmentMode, PackageInfo } from '../config';
import { ConfigPath, EnvironmentMode, PackageInfo, Config } from '../config';
import { LoggerFactory } from '../logging';
import { CoreSetup, CoreStart } from '..';

Expand Down Expand Up @@ -209,6 +209,7 @@ export interface PluginInitializerContext<ConfigSchema = unknown> {
};
logger: LoggerFactory;
config: {
globalConfig__deprecated$: Observable<Pick<Config, 'has' | 'get' | 'getFlattenedPaths'>>;
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};
Expand Down
1 change: 1 addition & 0 deletions src/core/server/server.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = obj
export interface PluginInitializerContext<ConfigSchema = unknown> {
// (undocumented)
config: {
globalConfig__deprecated$: Observable<Pick<Config, 'has' | 'get' | 'getFlattenedPaths'>>;
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};
Expand Down