[7.17] Allow nested declaration for exposeToBrowser (#128864) (#128927)

* Allow nested declaration for `exposeToBrowser` (#128864)

* Allow nested declaration for `exposeToBrowser`

* update generated doc

* add utest

(cherry picked from commit dd0a19033f)

# Conflicts:
#	src/core/server/plugins/plugins_service.ts
#	src/core/server/server.api.md

* update generated odc
This commit is contained in:
Pierre Gayvallet 2022-03-30 19:42:53 +02:00 committed by GitHub
parent 6025cbc34b
commit 1f756dc166
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 347 additions and 49 deletions

View file

@ -6,6 +6,8 @@
Collect gathers event loop delays metrics from nodejs perf\_hooks.monitorEventLoopDelay the histogram calculations start from the last time `reset` was called or this EventLoopDelaysMonitor instance was created.
Returns metrics in milliseconds.
<b>Signature:</b>
```typescript

View file

@ -20,7 +20,7 @@ export declare class EventLoopDelaysMonitor
| Method | Modifiers | Description |
| --- | --- | --- |
| [collect()](./kibana-plugin-core-server.eventloopdelaysmonitor.collect.md) | | Collect gathers event loop delays metrics from nodejs perf\_hooks.monitorEventLoopDelay the histogram calculations start from the last time <code>reset</code> was called or this EventLoopDelaysMonitor instance was created. |
| [collect()](./kibana-plugin-core-server.eventloopdelaysmonitor.collect.md) | | Collect gathers event loop delays metrics from nodejs perf\_hooks.monitorEventLoopDelay the histogram calculations start from the last time <code>reset</code> was called or this EventLoopDelaysMonitor instance was created.<!-- -->Returns metrics in milliseconds. |
| [reset()](./kibana-plugin-core-server.eventloopdelaysmonitor.reset.md) | | Resets the collected histogram data. |
| [stop()](./kibana-plugin-core-server.eventloopdelaysmonitor.stop.md) | | Disables updating the interval timer for collecting new data points. |

View file

@ -0,0 +1,16 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [ExposedToBrowserDescriptor](./kibana-plugin-core-server.exposedtobrowserdescriptor.md)
## ExposedToBrowserDescriptor type
Type defining the list of configuration properties that will be exposed on the client-side Object properties can either be fully exposed
<b>Signature:</b>
```typescript
export declare type ExposedToBrowserDescriptor<T> = {
[Key in keyof T]?: T[Key] extends Maybe<any[]> ? boolean : T[Key] extends Maybe<object> ? // can be nested for objects
ExposedToBrowserDescriptor<T[Key]> | boolean : boolean;
};
```

View file

@ -4,7 +4,7 @@
## IntervalHistogram interface
an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in nanoseconds.
an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in milliseconds.
<b>Signature:</b>

View file

@ -100,7 +100,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [IExternalUrlPolicy](./kibana-plugin-core-server.iexternalurlpolicy.md) | A policy describing whether access to an external destination is allowed. |
| [IKibanaResponse](./kibana-plugin-core-server.ikibanaresponse.md) | A response data object, expected to returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution |
| [IKibanaSocket](./kibana-plugin-core-server.ikibanasocket.md) | A tiny abstraction for TCP socket. |
| [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) | an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in nanoseconds. |
| [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) | an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in milliseconds. |
| [IRenderOptions](./kibana-plugin-core-server.irenderoptions.md) | |
| [IRouter](./kibana-plugin-core-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-core-server.routeconfig.md) and [RequestHandler](./kibana-plugin-core-server.requesthandler.md) for more information about arguments to route registrations. |
| [ISavedObjectsPointInTimeFinder](./kibana-plugin-core-server.isavedobjectspointintimefinder.md) | |
@ -253,6 +253,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md) | Client used to query the elasticsearch cluster. |
| [ElasticsearchClientConfig](./kibana-plugin-core-server.elasticsearchclientconfig.md) | Configuration options to be used to create a [cluster client](./kibana-plugin-core-server.iclusterclient.md) using the [createClient API](./kibana-plugin-core-server.elasticsearchservicestart.createclient.md) |
| [ExecutionContextStart](./kibana-plugin-core-server.executioncontextstart.md) | |
| [ExposedToBrowserDescriptor](./kibana-plugin-core-server.exposedtobrowserdescriptor.md) | Type defining the list of configuration properties that will be exposed on the client-side Object properties can either be fully exposed |
| [GetAuthHeaders](./kibana-plugin-core-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. |
| [GetAuthState](./kibana-plugin-core-server.getauthstate.md) | Gets authentication state for a request. Returned by <code>auth</code> interceptor. |
| [HandlerContextType](./kibana-plugin-core-server.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md) to represent the type of the context. |

View file

@ -1,21 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [mergeSavedObjectMigrationMaps](./kibana-plugin-core-server.mergesavedobjectmigrationmaps.md)
## mergeSavedObjectMigrationMaps variable
Merges two saved object migration maps.
If there is a migration for a given version on only one of the maps, that migration function will be used:
mergeSavedObjectMigrationMaps(<!-- -->{ '1.2.3': f }<!-- -->, { '4.5.6': g }<!-- -->) -<!-- -->&gt; { '1.2.3': f, '4.5.6': g }
If there is a migration for a given version on both maps, the migrations will be composed:
mergeSavedObjectMigrationMaps(<!-- -->{ '1.2.3': f }<!-- -->, { '1.2.3': g }<!-- -->) -<!-- -->&gt; { '1.2.3': (doc, context) =<!-- -->&gt; f(g(doc, context), context) }
<b>Signature:</b>
```typescript
mergeSavedObjectMigrationMaps: (map1: SavedObjectMigrationMap, map2: SavedObjectMigrationMap) => SavedObjectMigrationMap
```

View file

@ -9,7 +9,5 @@ List of configuration properties that will be available on the client-side plugi
<b>Signature:</b>
```typescript
exposeToBrowser?: {
[P in keyof T]?: boolean;
};
exposeToBrowser?: ExposedToBrowserDescriptor<T>;
```

View file

@ -44,7 +44,7 @@ export const config: PluginConfigDescriptor<ConfigType> = {
| Property | Type | Description |
| --- | --- | --- |
| [deprecations?](./kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | <i>(Optional)</i> Provider for the to apply to the plugin configuration. |
| [exposeToBrowser?](./kibana-plugin-core-server.pluginconfigdescriptor.exposetobrowser.md) | { \[P in keyof T\]?: boolean; } | <i>(Optional)</i> List of configuration properties that will be available on the client-side plugin. |
| [exposeToBrowser?](./kibana-plugin-core-server.pluginconfigdescriptor.exposetobrowser.md) | ExposedToBrowserDescriptor&lt;T&gt; | <i>(Optional)</i> List of configuration properties that will be available on the client-side plugin. |
| [exposeToUsage?](./kibana-plugin-core-server.pluginconfigdescriptor.exposetousage.md) | MakeUsageFromSchema&lt;T&gt; | <i>(Optional)</i> Expose non-default configs to usage collection to be sent via telemetry. set a config to <code>true</code> to report the actual changed config value. set a config to <code>false</code> to report the changed config value as \[redacted\].<!-- -->All changed configs except booleans and numbers will be reported as \[redacted\] unless otherwise specified.[MakeUsageFromSchema](./kibana-plugin-core-server.makeusagefromschema.md) |
| [schema](./kibana-plugin-core-server.pluginconfigdescriptor.schema.md) | PluginConfigSchema&lt;T&gt; | Schema to use to validate the plugin configuration.[PluginConfigSchema](./kibana-plugin-core-server.pluginconfigschema.md) |

View file

@ -257,6 +257,7 @@ export type {
PluginName,
SharedGlobalConfig,
MakeUsageFromSchema,
ExposedToBrowserDescriptor,
} from './plugins';
export {

View file

@ -0,0 +1,162 @@
/*
* 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.
*/
import { ExposedToBrowserDescriptor } from './types';
import { createBrowserConfig } from './create_browser_config';
describe('createBrowserConfig', () => {
it('picks nothing by default', () => {
const config = {
foo: 'bar',
nested: {
str: 'string',
num: 42,
},
};
const descriptor: ExposedToBrowserDescriptor<typeof config> = {};
const browserConfig = createBrowserConfig(config, descriptor);
expect(browserConfig).toEqual({});
});
it('picks all the nested properties when using `true`', () => {
const config = {
foo: 'bar',
nested: {
str: 'string',
num: 42,
},
};
const descriptor: ExposedToBrowserDescriptor<typeof config> = {
foo: true,
nested: true,
};
const browserConfig = createBrowserConfig(config, descriptor);
expect(browserConfig).toEqual({
foo: 'bar',
nested: {
str: 'string',
num: 42,
},
});
});
it('picks specific nested properties when using a nested declaration', () => {
const config = {
foo: 'bar',
nested: {
str: 'string',
num: 42,
},
};
const descriptor: ExposedToBrowserDescriptor<typeof config> = {
foo: true,
nested: {
str: true,
num: false,
},
};
const browserConfig = createBrowserConfig(config, descriptor);
expect(browserConfig).toEqual({
foo: 'bar',
nested: {
str: 'string',
},
});
});
it('accepts deeply nested structures', () => {
const config = {
foo: 'bar',
deeply: {
str: 'string',
nested: {
hello: 'dolly',
structure: {
propA: 'propA',
propB: 'propB',
},
},
},
};
const descriptor: ExposedToBrowserDescriptor<typeof config> = {
foo: false,
deeply: {
str: false,
nested: {
hello: true,
structure: {
propA: true,
propB: false,
},
},
},
};
const browserConfig = createBrowserConfig(config, descriptor);
expect(browserConfig).toEqual({
deeply: {
nested: {
hello: 'dolly',
structure: {
propA: 'propA',
},
},
},
});
});
it('only includes leaf properties that are `true` when in nested structures', () => {
const config = {
foo: 'bar',
deeply: {
str: 'string',
nested: {
hello: 'dolly',
structure: {
propA: 'propA',
propB: 'propB',
},
},
},
};
const descriptor: ExposedToBrowserDescriptor<typeof config> = {
deeply: {
nested: {
hello: true,
structure: {
propA: true,
},
},
},
};
const browserConfig = createBrowserConfig(config, descriptor);
expect(browserConfig).toEqual({
deeply: {
nested: {
hello: 'dolly',
structure: {
propA: 'propA',
},
},
},
});
});
});

View file

@ -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 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.
*/
import { ExposedToBrowserDescriptor } from './types';
export const createBrowserConfig = <T = unknown>(
config: T,
descriptor: ExposedToBrowserDescriptor<T>
): unknown => {
return recursiveCreateConfig(config, descriptor);
};
const recursiveCreateConfig = <T = unknown>(
config: T,
descriptor: ExposedToBrowserDescriptor<T> = {}
): unknown => {
return Object.entries(config || {}).reduce((browserConfig, [key, value]) => {
const exposedConfig = descriptor[key as keyof ExposedToBrowserDescriptor<T>];
if (exposedConfig && typeof exposedConfig === 'object') {
browserConfig[key] = recursiveCreateConfig(value, exposedConfig);
}
if (exposedConfig === true) {
browserConfig[key] = value;
}
return browserConfig;
}, {} as Record<string, unknown>);
};

View file

@ -9,7 +9,7 @@
import Path from 'path';
import { Observable } from 'rxjs';
import { concatMap, filter, first, map, tap, toArray } from 'rxjs/operators';
import { getFlattenedObject, pick } from '@kbn/std';
import { getFlattenedObject } from '@kbn/std';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
@ -26,6 +26,7 @@ import {
} from './types';
import { PluginsConfig, PluginsConfigType } from './plugins_config';
import { PluginsSystem } from './plugins_system';
import { createBrowserConfig } from './create_browser_config';
import { InternalCorePreboot, InternalCoreSetup, InternalCoreStart } from '../internal_types';
import { IConfigService } from '../config';
import { InternalEnvironmentServicePreboot } from '../environment';
@ -228,16 +229,11 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
const configDescriptor = this.pluginConfigDescriptors.get(pluginId)!;
return [
pluginId,
this.configService.atPath(plugin.configPath).pipe(
map((config: any) =>
pick(
config || {},
Object.entries(configDescriptor.exposeToBrowser!)
.filter(([_, exposed]) => exposed)
.map(([key, _]) => key)
)
)
),
this.configService
.atPath(plugin.configPath)
.pipe(
map((config: any) => createBrowserConfig(config, configDescriptor.exposeToBrowser!))
),
];
})
);

View file

@ -0,0 +1,90 @@
/*
* 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.
*/
import { ExposedToBrowserDescriptor } from './types';
describe('ExposedToBrowserDescriptor', () => {
interface ConfigType {
str: string;
array: number[];
obj: {
sub1: string;
sub2: number;
};
deep: {
foo: number;
nested: {
str: string;
arr: number[];
};
};
}
it('allows to use recursion on objects', () => {
const exposeToBrowser: ExposedToBrowserDescriptor<ConfigType> = {
obj: {
sub1: true,
},
};
expect(exposeToBrowser).toBeDefined();
});
it('allows to use recursion at multiple levels', () => {
const exposeToBrowser: ExposedToBrowserDescriptor<ConfigType> = {
deep: {
foo: true,
nested: {
str: true,
},
},
};
expect(exposeToBrowser).toBeDefined();
});
it('does not allow to use recursion on arrays', () => {
const exposeToBrowser: ExposedToBrowserDescriptor<ConfigType> = {
// @ts-expect-error Type '{ 0: true; }' is not assignable to type 'boolean | undefined'.
array: {
0: true,
},
};
expect(exposeToBrowser).toBeDefined();
});
it('does not allow to use recursion on arrays at lower levels', () => {
const exposeToBrowser: ExposedToBrowserDescriptor<ConfigType> = {
deep: {
nested: {
// @ts-expect-error Type '{ 0: true; }' is not assignable to type 'boolean | undefined'.
arr: {
0: true,
},
},
},
};
expect(exposeToBrowser).toBeDefined();
});
it('allows to specify all the properties', () => {
const exposeToBrowser: ExposedToBrowserDescriptor<ConfigType> = {
str: true,
array: false,
obj: {
sub1: true,
},
deep: {
foo: true,
nested: {
arr: false,
str: true,
},
},
};
expect(exposeToBrowser).toBeDefined();
});
});

View file

@ -27,6 +27,23 @@ type Maybe<T> = T | undefined;
*/
export type PluginConfigSchema<T> = Type<T>;
/**
* Type defining the list of configuration properties that will be exposed on the client-side
* Object properties can either be fully exposed
*
* @public
*/
export type ExposedToBrowserDescriptor<T> = {
[Key in keyof T]?: T[Key] extends Maybe<any[]>
? // handles arrays as primitive values
boolean
: T[Key] extends Maybe<object>
? // can be nested for objects
ExposedToBrowserDescriptor<T[Key]> | boolean
: // primitives
boolean;
};
/**
* Describes a plugin configuration properties.
*
@ -65,7 +82,7 @@ export interface PluginConfigDescriptor<T = any> {
/**
* List of configuration properties that will be available on the client-side plugin.
*/
exposeToBrowser?: { [P in keyof T]?: boolean };
exposeToBrowser?: ExposedToBrowserDescriptor<T>;
/**
* Schema to use to validate the plugin configuration.
*

View file

@ -961,6 +961,14 @@ export interface ExecutionContextSetup {
// @public (undocumented)
export type ExecutionContextStart = ExecutionContextSetup;
// Warning: (ae-forgotten-export) The symbol "Maybe" needs to be exported by the entry point index.d.ts
//
// @public
export type ExposedToBrowserDescriptor<T> = {
[Key in keyof T]?: T[Key] extends Maybe<any[]> ? boolean : T[Key] extends Maybe<object> ? // can be nested for objects
ExposedToBrowserDescriptor<T[Key]> | boolean : boolean;
};
// @public
export interface FakeRequest {
headers: Headers_2;
@ -1398,8 +1406,6 @@ export { LogMeta }
export { LogRecord }
// Warning: (ae-forgotten-export) The symbol "Maybe" needs to be exported by the entry point index.d.ts
//
// @public
export type MakeUsageFromSchema<T> = {
[Key in keyof T]?: T[Key] extends Maybe<object[]> ? false : T[Key] extends Maybe<any[]> ? boolean : T[Key] extends Maybe<object> ? MakeUsageFromSchema<T[Key]> | boolean : boolean;
@ -1588,9 +1594,7 @@ export { Plugin_2 as Plugin }
export interface PluginConfigDescriptor<T = any> {
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
deprecations?: ConfigDeprecationProvider;
exposeToBrowser?: {
[P in keyof T]?: boolean;
};
exposeToBrowser?: ExposedToBrowserDescriptor<T>;
exposeToUsage?: MakeUsageFromSchema<T>;
schema: PluginConfigSchema<T>;
}
@ -3033,9 +3037,9 @@ export const validBodyOutput: readonly ["data", "stream"];
//
// src/core/server/elasticsearch/client/types.ts:94:7 - (ae-forgotten-export) The symbol "Explanation" needs to be exported by the entry point index.d.ts
// src/core/server/http/router/response.ts:302:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:377:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:377:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:380:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:486:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create"
// src/core/server/plugins/types.ts:394:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:394:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:397:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:503:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create"
```