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

Add UiSettings validation & Kibana default route redirection #59694

Merged
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b20ecc7
add schema to ui settings params
mshustov Mar 9, 2020
7e46aef
add validation for defaults and overrides
mshustov Mar 9, 2020
5b55b83
validate in ui settings client
mshustov Mar 9, 2020
19dae10
ui settings routes validation
mshustov Mar 9, 2020
012a715
clean up tests
mshustov Mar 9, 2020
cc96364
use schema for defaultRoutes
mshustov Mar 9, 2020
269e34e
move URL redirection to NP
mshustov Mar 9, 2020
f2b2dea
fix spaces test
mshustov Mar 10, 2020
39e7405
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 10, 2020
b736ada
update docs
mshustov Mar 10, 2020
b01a668
update kbn pm
mshustov Mar 10, 2020
5518bd0
fix karma test
mshustov Mar 10, 2020
7a5a354
fix tests
mshustov Mar 10, 2020
850d78c
address comments
mshustov Mar 10, 2020
a7d0100
get rid of getDEfaultRoute
mshustov Mar 10, 2020
8ec9103
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 10, 2020
b21efe2
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 11, 2020
cb40a2b
regen docs
mshustov Mar 11, 2020
2f8f956
fix tests
mshustov Mar 11, 2020
f17a82f
fix enter-spaces test
mshustov Mar 11, 2020
6b5f699
validate on relative url format
mshustov Mar 11, 2020
9856d0b
update i18n
mshustov Mar 11, 2020
6e389a6
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 11, 2020
84e3e5a
fix enter-spoace test
mshustov Mar 11, 2020
c72b5cc
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 12, 2020
ed8a9a9
move relative url validation to utils
mshustov Mar 12, 2020
1036d77
add CoreApp containing application logic
mshustov Mar 12, 2020
075fe07
extract public uiSettings params in a separate type
mshustov Mar 12, 2020
3b8ad0c
make schema required
mshustov Mar 12, 2020
57c9aef
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 12, 2020
2ad3292
Merge branch 'master' into issue-50658-default-route-redirection
mshustov Mar 16, 2020
400a167
update docs
mshustov Mar 16, 2020
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 @@ -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 @@ -231,6 +231,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
7 changes: 5 additions & 2 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Observable } from 'rxjs';
import React from 'react';
import * as Rx from 'rxjs';
import { ShallowPromise } from '@kbn/utility-types';
import { Type } from '@kbn/config-schema';
import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types';
import { UnregisterCallback } from 'history';
import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types';
Expand Down Expand Up @@ -1289,7 +1290,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 @@ -1299,10 +1300,12 @@ 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)
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,
Comment on lines 20 to 25
Copy link
Contributor

Choose a reason for hiding this comment

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

Not introduced by this PR, but we may want to rename the exposed types to use the UiSettings prefix at some point. Outside of the scope of the PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will create an issue

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added to #48925

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}`);
Comment on lines +156 to +160
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably related to the changes in src/core/server/ui_settings/routes/set.ts , but why the difference between 400 and 4xx?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Request failed with status code: ${err.response.status} is not really useful. I'd rather remove them completely and provide a more descriptive message, but BWC...So I added this for BadRequest only to provide an actionable message.

Copy link
Contributor

Choose a reason for hiding this comment

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

We really need typing for our errors so that we can evolve these 😢

}
}

throw err;
} finally {
this.loadingCount$.next(this.loadingCount$.getValue() - 1);
Expand Down
8 changes: 4 additions & 4 deletions src/core/public/ui_settings/ui_settings_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import { cloneDeep, defaultsDeep } from 'lodash';
import { Observable, Subject, concat, defer, of } from 'rxjs';
import { filter, map } from 'rxjs/operators';

import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types';
import { UserProvidedValues, PublicUiSettingsParams } from 'src/core/server/types';
import { IUiSettingsClient, UiSettingsState } from './types';

import { UiSettingsApi } from './ui_settings_api';

interface UiSettingsClientParams {
api: UiSettingsApi;
defaults: Record<string, UiSettingsParams>;
defaults: Record<string, PublicUiSettingsParams>;
initialSettings?: UiSettingsState;
done$: Observable<unknown>;
}
Expand All @@ -39,8 +39,8 @@ export class UiSettingsClient implements IUiSettingsClient {
private readonly updateErrors$ = new Subject<Error>();

private readonly api: UiSettingsApi;
private readonly defaults: Record<string, UiSettingsParams>;
private cache: Record<string, UiSettingsParams & UserProvidedValues>;
private readonly defaults: Record<string, PublicUiSettingsParams>;
private cache: Record<string, PublicUiSettingsParams & UserProvidedValues>;

constructor(params: UiSettingsClientParams) {
this.api = params.api;
Expand Down
52 changes: 52 additions & 0 deletions src/core/server/core_app/core_app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 { InternalCoreSetup } from '../internal_types';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';

/** @internal */
export class CoreApp {
private readonly logger: Logger;
constructor(core: CoreContext) {
this.logger = core.logger.get('core-app');
}
setup(coreSetup: InternalCoreSetup) {
this.logger.debug('Setting up core app.');
this.registerDefaultRoutes(coreSetup);
}

private registerDefaultRoutes(coreSetup: InternalCoreSetup) {
const httpSetup = coreSetup.http;
const router = httpSetup.createRouter('/');
router.get({ path: '/', validate: false }, async (context, req, res) => {
const defaultRoute = await context.core.uiSettings.client.get<string>('defaultRoute');
const basePath = httpSetup.basePath.get(req);
const url = `${basePath}${defaultRoute}`;

return res.redirected({
headers: {
location: url,
},
});
});
router.get({ path: '/core', validate: false }, async (context, req, res) =>
res.ok({ body: { version: '0.0.1' } })
);
}
}
20 changes: 20 additions & 0 deletions src/core/server/core_app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/

export { CoreApp } from './core_app';
Loading