Skip to content

Commit

Permalink
Add UiSettings validation & Kibana default route redirection (#59694)
Browse files Browse the repository at this point in the history
* add schema to ui settings params

* add validation for defaults and overrides

* validate in ui settings client

* ui settings routes validation

* clean up tests

* use schema for defaultRoutes

* move URL redirection to NP

* fix spaces test

* update docs

* update kbn pm

* fix karma test

* fix tests

* address comments

* get rid of getDEfaultRoute

* regen docs

* fix tests

* fix enter-spaces test

* validate on relative url format

* update i18n

* fix enter-spoace test

* move relative url validation to utils

* add CoreApp containing application logic

* extract public uiSettings params in a separate type

* make schema required

* update docs
  • Loading branch information
mshustov authored Mar 16, 2020
1 parent 271c959 commit dd7531d
Show file tree
Hide file tree
Showing 63 changed files with 724 additions and 372 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Gets the metadata about all uiSettings, including the type, default value, and u
<b>Signature:</b>

```typescript
getAll: () => Readonly<Record<string, UiSettingsParams & UserProvidedValues>>;
getAll: () => Readonly<Record<string, PublicUiSettingsParams & UserProvidedValues>>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface IUiSettingsClient
| --- | --- | --- |
| [get](./kibana-plugin-core-public.iuisettingsclient.get.md) | <code>&lt;T = any&gt;(key: string, defaultOverride?: T) =&gt; T</code> | Gets the value for a specific uiSetting. If this setting has no user-defined value then the <code>defaultOverride</code> parameter is returned (and parsed if setting is of type "json" or "number). If the parameter is not defined and the key is not registered by any plugin then an error is thrown, otherwise reads the default value defined by a plugin. |
| [get$](./kibana-plugin-core-public.iuisettingsclient.get_.md) | <code>&lt;T = any&gt;(key: string, defaultOverride?: T) =&gt; Observable&lt;T&gt;</code> | Gets an observable of the current value for a config key, and all updates to that config key in the future. Providing a <code>defaultOverride</code> argument behaves the same as it does in \#get() |
| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | <code>() =&gt; Readonly&lt;Record&lt;string, UiSettingsParams &amp; UserProvidedValues&gt;&gt;</code> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. |
| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | <code>() =&gt; Readonly&lt;Record&lt;string, PublicUiSettingsParams &amp; UserProvidedValues&gt;&gt;</code> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. |
| [getSaved$](./kibana-plugin-core-public.iuisettingsclient.getsaved_.md) | <code>&lt;T = any&gt;() =&gt; Observable&lt;{</code><br/><code> key: string;</code><br/><code> newValue: T;</code><br/><code> oldValue: T;</code><br/><code> }&gt;</code> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. |
| [getUpdate$](./kibana-plugin-core-public.iuisettingsclient.getupdate_.md) | <code>&lt;T = any&gt;() =&gt; Observable&lt;{</code><br/><code> key: string;</code><br/><code> newValue: T;</code><br/><code> oldValue: T;</code><br/><code> }&gt;</code> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. |
| [getUpdateErrors$](./kibana-plugin-core-public.iuisettingsclient.getupdateerrors_.md) | <code>() =&gt; Observable&lt;Error&gt;</code> | Returns an Observable that notifies subscribers of each error while trying to update the settings, containing the actual Error class. |
Expand Down
1 change: 1 addition & 0 deletions docs/development/core/public/kibana-plugin-core-public.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [MountPoint](./kibana-plugin-core-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. |
| [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>public</code> directory should conform to this interface. |
| [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | |
| [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. |
| [RecursiveReadonly](./kibana-plugin-core-public.recursivereadonly.md) | |
| [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value |
| [SavedObjectAttributeSingle](./kibana-plugin-core-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md)

## PublicUiSettingsParams type

A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side.

<b>Signature:</b>

```typescript
export declare type PublicUiSettingsParams = Omit<UiSettingsParams, 'schema'>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins.
<b>Signature:</b>

```typescript
export interface UiSettingsParams
export interface UiSettingsParams<T = unknown>
```

## Properties
Expand All @@ -24,7 +24,8 @@ export interface UiSettingsParams
| [options](./kibana-plugin-core-public.uisettingsparams.options.md) | <code>string[]</code> | array of permitted values for this setting |
| [readonly](./kibana-plugin-core-public.uisettingsparams.readonly.md) | <code>boolean</code> | a flag indicating that value cannot be changed |
| [requiresPageReload](./kibana-plugin-core-public.uisettingsparams.requirespagereload.md) | <code>boolean</code> | a flag indicating whether new value applying requires page reloading |
| [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) | <code>Type&lt;T&gt;</code> | |
| [type](./kibana-plugin-core-public.uisettingsparams.type.md) | <code>UiSettingsType</code> | defines a type of UI element [UiSettingsType](./kibana-plugin-core-public.uisettingstype.md) |
| [validation](./kibana-plugin-core-public.uisettingsparams.validation.md) | <code>ImageValidation &#124; StringValidation</code> | |
| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | <code>SavedObjectAttribute</code> | default value to fall back to if a user doesn't provide any |
| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | <code>T</code> | default value to fall back to if a user doesn't provide any |

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) &gt; [schema](./kibana-plugin-core-public.uisettingsparams.schema.md)

## UiSettingsParams.schema property

<b>Signature:</b>

```typescript
schema: Type<T>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any
<b>Signature:</b>

```typescript
value?: SavedObjectAttribute;
value?: T;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-ser
<b>Signature:</b>

```typescript
getRegistered: () => Readonly<Record<string, UiSettingsParams>>;
getRegistered: () => Readonly<Record<string, PublicUiSettingsParams>>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface IUiSettingsClient
| --- | --- | --- |
| [get](./kibana-plugin-core-server.iuisettingsclient.get.md) | <code>&lt;T = any&gt;(key: string) =&gt; Promise&lt;T&gt;</code> | Retrieves uiSettings values set by the user with fallbacks to default values if not specified. |
| [getAll](./kibana-plugin-core-server.iuisettingsclient.getall.md) | <code>&lt;T = any&gt;() =&gt; Promise&lt;Record&lt;string, T&gt;&gt;</code> | Retrieves a set of all uiSettings values set by the user with fallbacks to default values if not specified. |
| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | <code>() =&gt; Readonly&lt;Record&lt;string, UiSettingsParams&gt;&gt;</code> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) |
| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | <code>() =&gt; Readonly&lt;Record&lt;string, PublicUiSettingsParams&gt;&gt;</code> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) |
| [getUserProvided](./kibana-plugin-core-server.iuisettingsclient.getuserprovided.md) | <code>&lt;T = any&gt;() =&gt; Promise&lt;Record&lt;string, UserProvidedValues&lt;T&gt;&gt;&gt;</code> | Retrieves a set of all uiSettings values set by the user. |
| [isOverridden](./kibana-plugin-core-server.iuisettingsclient.isoverridden.md) | <code>(key: string) =&gt; boolean</code> | Shows whether the uiSettings value set by the user. |
| [remove](./kibana-plugin-core-server.iuisettingsclient.remove.md) | <code>(key: string) =&gt; Promise&lt;void&gt;</code> | Removes uiSettings value by key. |
Expand Down
1 change: 1 addition & 0 deletions docs/development/core/server/kibana-plugin-core-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PluginInitializer](./kibana-plugin-core-server.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>server</code> directory should conform to this interface. |
| [PluginName](./kibana-plugin-core-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. |
| [PluginOpaqueId](./kibana-plugin-core-server.pluginopaqueid.md) | |
| [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side. |
| [RecursiveReadonly](./kibana-plugin-core-server.recursivereadonly.md) | |
| [RedirectResponseOptions](./kibana-plugin-core-server.redirectresponseoptions.md) | HTTP response parameters for redirection response |
| [RequestHandler](./kibana-plugin-core-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) functions. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- 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; [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md)

## PublicUiSettingsParams type

A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side.

<b>Signature:</b>

```typescript
export declare type PublicUiSettingsParams = Omit<UiSettingsParams, 'schema'>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins.
<b>Signature:</b>

```typescript
export interface UiSettingsParams
export interface UiSettingsParams<T = unknown>
```

## Properties
Expand All @@ -24,7 +24,8 @@ export interface UiSettingsParams
| [options](./kibana-plugin-core-server.uisettingsparams.options.md) | <code>string[]</code> | array of permitted values for this setting |
| [readonly](./kibana-plugin-core-server.uisettingsparams.readonly.md) | <code>boolean</code> | a flag indicating that value cannot be changed |
| [requiresPageReload](./kibana-plugin-core-server.uisettingsparams.requirespagereload.md) | <code>boolean</code> | a flag indicating whether new value applying requires page reloading |
| [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) | <code>Type&lt;T&gt;</code> | |
| [type](./kibana-plugin-core-server.uisettingsparams.type.md) | <code>UiSettingsType</code> | defines a type of UI element [UiSettingsType](./kibana-plugin-core-server.uisettingstype.md) |
| [validation](./kibana-plugin-core-server.uisettingsparams.validation.md) | <code>ImageValidation &#124; StringValidation</code> | |
| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | <code>SavedObjectAttribute</code> | default value to fall back to if a user doesn't provide any |
| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | <code>T</code> | default value to fall back to if a user doesn't provide any |

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- 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; [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) &gt; [schema](./kibana-plugin-core-server.uisettingsparams.schema.md)

## UiSettingsParams.schema property

<b>Signature:</b>

```typescript
schema: Type<T>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any
<b>Signature:</b>

```typescript
value?: SavedObjectAttribute;
value?: T;
```
3 changes: 3 additions & 0 deletions packages/kbn-config-schema/src/errors/validation_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,8 @@ export class ValidationError extends SchemaError {

constructor(error: SchemaTypeError, namespace?: string) {
super(ValidationError.extractMessage(error, namespace), error);

// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(this, ValidationError.prototype);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class KbnClientUiSettings {
* Replace all uiSettings with the `doc` values, `doc` is merged
* with some defaults
*/
async replace(doc: UiSettingValues) {
async replace(doc: UiSettingValues, { retries = 5 }: { retries?: number } = {}) {
this.log.debug('replacing kibana config doc: %j', doc);

const changes: Record<string, any> = {
Expand All @@ -85,7 +85,7 @@ export class KbnClientUiSettings {
method: 'POST',
path: '/api/kibana/settings',
body: { changes },
retries: 5,
retries,
});
}

Expand Down
4 changes: 2 additions & 2 deletions packages/kbn-pm/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43920,7 +43920,7 @@ class KbnClientUiSettings {
* Replace all uiSettings with the `doc` values, `doc` is merged
* with some defaults
*/
async replace(doc) {
async replace(doc, { retries = 5 } = {}) {
this.log.debug('replacing kibana config doc: %j', doc);
const changes = {
...this.defaults,
Expand All @@ -43935,7 +43935,7 @@ class KbnClientUiSettings {
method: 'POST',
path: '/api/kibana/settings',
body: { changes },
retries: 5,
retries,
});
}
/**
Expand Down
2 changes: 1 addition & 1 deletion src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export {
ErrorToastOptions,
} from './notifications';

export { MountPoint, UnmountCallback } from './types';
export { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types';

/**
* Core services exposed to the `Plugin` setup lifecycle
Expand Down
16 changes: 11 additions & 5 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import { Location } from 'history';
import { LocationDescriptorObject } from 'history';
import { MaybePromise } from '@kbn/utility-types';
import { Observable } from 'rxjs';
import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types';
import React from 'react';
import * as Rx from 'rxjs';
import { ShallowPromise } from '@kbn/utility-types';
import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types';
import { Type } from '@kbn/config-schema';
import { UnregisterCallback } from 'history';
import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types';

Expand Down Expand Up @@ -784,7 +785,7 @@ export type IToasts = Pick<ToastsApi, 'get$' | 'add' | 'remove' | 'addSuccess' |
export interface IUiSettingsClient {
get$: <T = any>(key: string, defaultOverride?: T) => Observable<T>;
get: <T = any>(key: string, defaultOverride?: T) => T;
getAll: () => Readonly<Record<string, UiSettingsParams_2 & UserProvidedValues_2>>;
getAll: () => Readonly<Record<string, PublicUiSettingsParams_2 & UserProvidedValues_2>>;
getSaved$: <T = any>() => Observable<{
key: string;
newValue: T;
Expand Down Expand Up @@ -933,6 +934,9 @@ export interface PluginInitializerContext<ConfigSchema extends object = object>
// @public (undocumented)
export type PluginOpaqueId = symbol;

// @public
export type PublicUiSettingsParams = Omit<UiSettingsParams, 'schema'>;

// Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
Expand Down Expand Up @@ -1291,7 +1295,7 @@ export type ToastsSetup = IToasts;
export type ToastsStart = IToasts;

// @public
export interface UiSettingsParams {
export interface UiSettingsParams<T = unknown> {
category?: string[];
// Warning: (ae-forgotten-export) The symbol "DeprecationSettings" needs to be exported by the entry point index.d.ts
deprecation?: DeprecationSettings;
Expand All @@ -1301,16 +1305,18 @@ export interface UiSettingsParams {
options?: string[];
readonly?: boolean;
requiresPageReload?: boolean;
// (undocumented)
schema: Type<T>;
type?: UiSettingsType;
// (undocumented)
validation?: ImageValidation | StringValidation;
value?: SavedObjectAttribute;
value?: T;
}

// @public (undocumented)
export interface UiSettingsState {
// (undocumented)
[key: string]: UiSettingsParams_2 & UserProvidedValues_2;
[key: string]: PublicUiSettingsParams_2 & UserProvidedValues_2;
}

// @public
Expand Down
1 change: 1 addition & 0 deletions src/core/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

export {
UiSettingsParams,
PublicUiSettingsParams,
UserProvidedValues,
UiSettingsType,
ImageValidation,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/core/public/ui_settings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
*/

import { Observable } from 'rxjs';
import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types';
import { PublicUiSettingsParams, UserProvidedValues } from 'src/core/server/types';

/** @public */
export interface UiSettingsState {
[key: string]: UiSettingsParams & UserProvidedValues;
[key: string]: PublicUiSettingsParams & UserProvidedValues;
}

/**
Expand Down Expand Up @@ -53,7 +53,7 @@ export interface IUiSettingsClient {
* Gets the metadata about all uiSettings, including the type, default value, and user value
* for each key.
*/
getAll: () => Readonly<Record<string, UiSettingsParams & UserProvidedValues>>;
getAll: () => Readonly<Record<string, PublicUiSettingsParams & UserProvidedValues>>;

/**
* Sets the value for a uiSetting. If the setting is not registered by any plugin
Expand Down
2 changes: 1 addition & 1 deletion src/core/public/ui_settings/ui_settings_api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ describe('#batchSet', () => {
'*',
{
status: 400,
body: 'invalid',
body: { message: 'invalid' },
},
{
overwriteRoutes: false,
Expand Down
10 changes: 7 additions & 3 deletions src/core/public/ui_settings/ui_settings_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ export class UiSettingsApi {
},
});
} catch (err) {
if (err.response && err.response.status >= 300) {
throw new Error(`Request failed with status code: ${err.response.status}`);
if (err.response) {
if (err.response.status === 400) {
throw new Error(err.body.message);
}
if (err.response.status > 400) {
throw new Error(`Request failed with status code: ${err.response.status}`);
}
}

throw err;
} finally {
this.loadingCount$.next(this.loadingCount$.getValue() - 1);
Expand Down
Loading

0 comments on commit dd7531d

Please sign in to comment.