[7.x] [Core] create deprecations service (#94845) (#95815)

* fix conflicts

* eslint

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Ahmad Bamieh 2021-03-31 15:21:07 +03:00 committed by GitHub
parent 111ff99eab
commit da5edcc055
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 3269 additions and 353 deletions

View file

@ -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; [CoreStart](./kibana-plugin-core-public.corestart.md) &gt; [deprecations](./kibana-plugin-core-public.corestart.deprecations.md)
## CoreStart.deprecations property
[DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md)
<b>Signature:</b>
```typescript
deprecations: DeprecationsServiceStart;
```

View file

@ -18,6 +18,7 @@ export interface CoreStart
| --- | --- | --- |
| [application](./kibana-plugin-core-public.corestart.application.md) | <code>ApplicationStart</code> | [ApplicationStart](./kibana-plugin-core-public.applicationstart.md) |
| [chrome](./kibana-plugin-core-public.corestart.chrome.md) | <code>ChromeStart</code> | [ChromeStart](./kibana-plugin-core-public.chromestart.md) |
| [deprecations](./kibana-plugin-core-public.corestart.deprecations.md) | <code>DeprecationsServiceStart</code> | [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) |
| [docLinks](./kibana-plugin-core-public.corestart.doclinks.md) | <code>DocLinksStart</code> | [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) |
| [fatalErrors](./kibana-plugin-core-public.corestart.fatalerrors.md) | <code>FatalErrorsStart</code> | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) |
| [http](./kibana-plugin-core-public.corestart.http.md) | <code>HttpStart</code> | [HttpStart](./kibana-plugin-core-public.httpstart.md) |

View file

@ -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; [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) &gt; [getAllDeprecations](./kibana-plugin-core-public.deprecationsservicestart.getalldeprecations.md)
## DeprecationsServiceStart.getAllDeprecations property
Grabs deprecations details for all domains.
<b>Signature:</b>
```typescript
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
```

View file

@ -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; [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) &gt; [getDeprecations](./kibana-plugin-core-public.deprecationsservicestart.getdeprecations.md)
## DeprecationsServiceStart.getDeprecations property
Grabs deprecations for a specific domain.
<b>Signature:</b>
```typescript
getDeprecations: (domainId: string) => Promise<DomainDeprecationDetails[]>;
```

View file

@ -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; [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) &gt; [isDeprecationResolvable](./kibana-plugin-core-public.deprecationsservicestart.isdeprecationresolvable.md)
## DeprecationsServiceStart.isDeprecationResolvable property
Returns a boolean if the provided deprecation can be automatically resolvable.
<b>Signature:</b>
```typescript
isDeprecationResolvable: (details: DomainDeprecationDetails) => boolean;
```

View file

@ -0,0 +1,23 @@
<!-- 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; [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md)
## DeprecationsServiceStart interface
DeprecationsService provides methods to fetch domain deprecation details from the Kibana server.
<b>Signature:</b>
```typescript
export interface DeprecationsServiceStart
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [getAllDeprecations](./kibana-plugin-core-public.deprecationsservicestart.getalldeprecations.md) | <code>() =&gt; Promise&lt;DomainDeprecationDetails[]&gt;</code> | Grabs deprecations details for all domains. |
| [getDeprecations](./kibana-plugin-core-public.deprecationsservicestart.getdeprecations.md) | <code>(domainId: string) =&gt; Promise&lt;DomainDeprecationDetails[]&gt;</code> | Grabs deprecations for a specific domain. |
| [isDeprecationResolvable](./kibana-plugin-core-public.deprecationsservicestart.isdeprecationresolvable.md) | <code>(details: DomainDeprecationDetails) =&gt; boolean</code> | Returns a boolean if the provided deprecation can be automatically resolvable. |
| [resolveDeprecation](./kibana-plugin-core-public.deprecationsservicestart.resolvedeprecation.md) | <code>(details: DomainDeprecationDetails) =&gt; Promise&lt;ResolveDeprecationResponse&gt;</code> | Calls the correctiveActions.api to automatically resolve the depprecation. |

View file

@ -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; [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) &gt; [resolveDeprecation](./kibana-plugin-core-public.deprecationsservicestart.resolvedeprecation.md)
## DeprecationsServiceStart.resolveDeprecation property
Calls the correctiveActions.api to automatically resolve the depprecation.
<b>Signature:</b>
```typescript
resolveDeprecation: (details: DomainDeprecationDetails) => Promise<ResolveDeprecationResponse>;
```

View file

@ -59,6 +59,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ChromeUserBanner](./kibana-plugin-core-public.chromeuserbanner.md) | |
| [CoreSetup](./kibana-plugin-core-public.coresetup.md) | Core services exposed to the <code>Plugin</code> setup lifecycle |
| [CoreStart](./kibana-plugin-core-public.corestart.md) | Core services exposed to the <code>Plugin</code> start lifecycle |
| [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) | DeprecationsService provides methods to fetch domain deprecation details from the Kibana server. |
| [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | |
| [ErrorToastOptions](./kibana-plugin-core-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) error APIs. |
| [FatalErrorInfo](./kibana-plugin-core-public.fatalerrorinfo.md) | Represents the <code>message</code> and <code>stack</code> of a fatal Error |
@ -164,6 +165,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PublicAppMetaInfo](./kibana-plugin-core-public.publicappmetainfo.md) | Public information about a registered app's [keywords](./kibana-plugin-core-public.appmeta.md) |
| [PublicAppSearchDeepLinkInfo](./kibana-plugin-core-public.publicappsearchdeeplinkinfo.md) | Public information about a registered app's [searchDeepLinks](./kibana-plugin-core-public.appsearchdeeplink.md) |
| [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. |
| [ResolveDeprecationResponse](./kibana-plugin-core-public.resolvedeprecationresponse.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) |
| [SavedObjectsClientContract](./kibana-plugin-core-public.savedobjectsclientcontract.md) | SavedObjectsClientContract as implemented by the [SavedObjectsClient](./kibana-plugin-core-public.savedobjectsclient.md) |

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-public](./kibana-plugin-core-public.md) &gt; [ResolveDeprecationResponse](./kibana-plugin-core-public.resolvedeprecationresponse.md)
## ResolveDeprecationResponse type
<b>Signature:</b>
```typescript
export declare type ResolveDeprecationResponse = {
status: 'ok';
} | {
status: 'fail';
reason: string;
};
```

View file

@ -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; [CoreSetup](./kibana-plugin-core-server.coresetup.md) &gt; [deprecations](./kibana-plugin-core-server.coresetup.deprecations.md)
## CoreSetup.deprecations property
[DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md)
<b>Signature:</b>
```typescript
deprecations: DeprecationsServiceSetup;
```

View file

@ -18,6 +18,7 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
| --- | --- | --- |
| [capabilities](./kibana-plugin-core-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) |
| [context](./kibana-plugin-core-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) |
| [deprecations](./kibana-plugin-core-server.coresetup.deprecations.md) | <code>DeprecationsServiceSetup</code> | [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md) |
| [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) |
| [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart, TStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) |
| [http](./kibana-plugin-core-server.coresetup.http.md) | <code>HttpServiceSetup &amp; {</code><br/><code> resources: HttpResources;</code><br/><code> }</code> | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |

View file

@ -0,0 +1,20 @@
<!-- 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; [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) &gt; [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md)
## DeprecationsDetails.correctiveActions property
<b>Signature:</b>
```typescript
correctiveActions: {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
};
manualSteps?: string[];
};
```

View file

@ -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; [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) &gt; [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md)
## DeprecationsDetails.documentationUrl property
<b>Signature:</b>
```typescript
documentationUrl?: string;
```

View file

@ -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; [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) &gt; [level](./kibana-plugin-core-server.deprecationsdetails.level.md)
## DeprecationsDetails.level property
levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain.
<b>Signature:</b>
```typescript
level: 'warning' | 'critical' | 'fetch_error';
```

View file

@ -0,0 +1,21 @@
<!-- 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; [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md)
## DeprecationsDetails interface
<b>Signature:</b>
```typescript
export interface DeprecationsDetails
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md) | <code>{</code><br/><code> api?: {</code><br/><code> path: string;</code><br/><code> method: 'POST' &#124; 'PUT';</code><br/><code> body?: {</code><br/><code> [key: string]: any;</code><br/><code> };</code><br/><code> };</code><br/><code> manualSteps?: string[];</code><br/><code> }</code> | |
| [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md) | <code>string</code> | |
| [level](./kibana-plugin-core-server.deprecationsdetails.level.md) | <code>'warning' &#124; 'critical' &#124; 'fetch_error'</code> | levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. |
| [message](./kibana-plugin-core-server.deprecationsdetails.message.md) | <code>string</code> | |

View file

@ -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; [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) &gt; [message](./kibana-plugin-core-server.deprecationsdetails.message.md)
## DeprecationsDetails.message property
<b>Signature:</b>
```typescript
message: string;
```

View file

@ -0,0 +1,95 @@
<!-- 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; [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md)
## DeprecationsServiceSetup interface
The deprecations service provides a way for the Kibana platform to communicate deprecated features and configs with its users. These deprecations are only communicated if the deployment is using these features. Allowing for a user tailored experience for upgrading the stack version.
The Deprecation service is consumed by the upgrade assistant to assist with the upgrade experience.
If a deprecated feature can be resolved without manual user intervention. Using correctiveActions.api allows the Upgrade Assistant to use this api to correct the deprecation upon a user trigger.
<b>Signature:</b>
```typescript
export interface DeprecationsServiceSetup
```
## Example
```ts
import { DeprecationsDetails, GetDeprecationsContext, CoreSetup } from 'src/core/server';
async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecationsContext): Promise<DeprecationsDetails[]> {
const deprecations: DeprecationsDetails[] = [];
const count = await getTimelionSheetsCount(savedObjectsClient);
if (count > 0) {
// Example of a manual correctiveAction
deprecations.push({
message: `You have ${count} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`,
documentationUrl:
'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html',
level: 'warning',
correctiveActions: {
manualSteps: [
'Navigate to the Kibana Dashboard and click "Create dashboard".',
'Select Timelion from the "New Visualization" window.',
'Open a new tab, open the Timelion app, select the chart you want to copy, then copy the chart expression.',
'Go to Timelion, paste the chart expression in the Timelion expression field, then click Update.',
'In the toolbar, click Save.',
'On the Save visualization window, enter the visualization Title, then click Save and return.',
],
},
});
}
// Example of an api correctiveAction
deprecations.push({
"message": "User 'test_dashboard_user' is using a deprecated role: 'kibana_user'",
"documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-user.html",
"level": "critical",
"correctiveActions": {
"api": {
"path": "/internal/security/users/test_dashboard_user",
"method": "POST",
"body": {
"username": "test_dashboard_user",
"roles": [
"machine_learning_user",
"enrich_user",
"kibana_admin"
],
"full_name": "Alison Goryachev",
"email": "alisongoryachev@gmail.com",
"metadata": {},
"enabled": true
}
},
"manualSteps": [
"Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.",
"Using Kibana role-mapping management, change all role-mappings which assing the kibana_user role to the kibana_admin role."
]
},
});
return deprecations;
}
export class Plugin() {
setup: (core: CoreSetup) => {
core.deprecations.registerDeprecations({ getDeprecations });
}
}
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [registerDeprecations](./kibana-plugin-core-server.deprecationsservicesetup.registerdeprecations.md) | <code>(deprecationContext: RegisterDeprecationsConfig) =&gt; void</code> | |

View file

@ -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; [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md) &gt; [registerDeprecations](./kibana-plugin-core-server.deprecationsservicesetup.registerdeprecations.md)
## DeprecationsServiceSetup.registerDeprecations property
<b>Signature:</b>
```typescript
registerDeprecations: (deprecationContext: RegisterDeprecationsConfig) => void;
```

View file

@ -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; [GetDeprecationsContext](./kibana-plugin-core-server.getdeprecationscontext.md) &gt; [esClient](./kibana-plugin-core-server.getdeprecationscontext.esclient.md)
## GetDeprecationsContext.esClient property
<b>Signature:</b>
```typescript
esClient: IScopedClusterClient;
```

View file

@ -0,0 +1,19 @@
<!-- 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; [GetDeprecationsContext](./kibana-plugin-core-server.getdeprecationscontext.md)
## GetDeprecationsContext interface
<b>Signature:</b>
```typescript
export interface GetDeprecationsContext
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [esClient](./kibana-plugin-core-server.getdeprecationscontext.esclient.md) | <code>IScopedClusterClient</code> | |
| [savedObjectsClient](./kibana-plugin-core-server.getdeprecationscontext.savedobjectsclient.md) | <code>SavedObjectsClientContract</code> | |

View file

@ -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; [GetDeprecationsContext](./kibana-plugin-core-server.getdeprecationscontext.md) &gt; [savedObjectsClient](./kibana-plugin-core-server.getdeprecationscontext.savedobjectsclient.md)
## GetDeprecationsContext.savedObjectsClient property
<b>Signature:</b>
```typescript
savedObjectsClient: SavedObjectsClientContract;
```

View file

@ -69,13 +69,16 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [DeprecationAPIClientParams](./kibana-plugin-core-server.deprecationapiclientparams.md) | |
| [DeprecationAPIResponse](./kibana-plugin-core-server.deprecationapiresponse.md) | |
| [DeprecationInfo](./kibana-plugin-core-server.deprecationinfo.md) | |
| [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | |
| [DeprecationSettings](./kibana-plugin-core-server.deprecationsettings.md) | UiSettings deprecation field options. |
| [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md) | The deprecations service provides a way for the Kibana platform to communicate deprecated features and configs with its users. These deprecations are only communicated if the deployment is using these features. Allowing for a user tailored experience for upgrading the stack version.<!-- -->The Deprecation service is consumed by the upgrade assistant to assist with the upgrade experience.<!-- -->If a deprecated feature can be resolved without manual user intervention. Using correctiveActions.api allows the Upgrade Assistant to use this api to correct the deprecation upon a user trigger. |
| [DiscoveredPlugin](./kibana-plugin-core-server.discoveredplugin.md) | Small container object used to expose information about discovered plugins that may or may not have been started. |
| [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | |
| [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | |
| [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | |
| [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters |
| [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. |
| [GetDeprecationsContext](./kibana-plugin-core-server.getdeprecationscontext.md) | |
| [GetResponse](./kibana-plugin-core-server.getresponse.md) | |
| [HttpAuth](./kibana-plugin-core-server.httpauth.md) | |
| [HttpResources](./kibana-plugin-core-server.httpresources.md) | HttpResources service is responsible for serving static &amp; dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file. |
@ -128,6 +131,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. |
| [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. |
| [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. |
| [RegisterDeprecationsConfig](./kibana-plugin-core-server.registerdeprecationsconfig.md) | |
| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.<!-- -->Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.legacy.client](./kibana-plugin-core-server.legacyscopedclusterclient.md) - The legacy Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
| [ResolveCapabilitiesOptions](./kibana-plugin-core-server.resolvecapabilitiesoptions.md) | Defines a set of additional options for the <code>resolveCapabilities</code> method of [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md)<!-- -->. |
| [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. |

View file

@ -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; [RegisterDeprecationsConfig](./kibana-plugin-core-server.registerdeprecationsconfig.md) &gt; [getDeprecations](./kibana-plugin-core-server.registerdeprecationsconfig.getdeprecations.md)
## RegisterDeprecationsConfig.getDeprecations property
<b>Signature:</b>
```typescript
getDeprecations: (context: GetDeprecationsContext) => MaybePromise<DeprecationsDetails[]>;
```

View file

@ -0,0 +1,18 @@
<!-- 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; [RegisterDeprecationsConfig](./kibana-plugin-core-server.registerdeprecationsconfig.md)
## RegisterDeprecationsConfig interface
<b>Signature:</b>
```typescript
export interface RegisterDeprecationsConfig
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [getDeprecations](./kibana-plugin-core-server.registerdeprecationsconfig.getdeprecations.md) | <code>(context: GetDeprecationsContext) =&gt; MaybePromise&lt;DeprecationsDetails[]&gt;</code> | |

View file

@ -25,13 +25,16 @@ const createConfigServiceMock = ({
setSchema: jest.fn(),
addDeprecationProvider: jest.fn(),
validate: jest.fn(),
getHandledDeprecatedConfigs: jest.fn(),
};
mocked.atPath.mockReturnValue(new BehaviorSubject(atPath));
mocked.atPathSync.mockReturnValue(atPath);
mocked.getConfig$.mockReturnValue(new BehaviorSubject(new ObjectToConfigAdapter(getConfig$)));
mocked.getUsedPaths.mockResolvedValue([]);
mocked.getUnusedPaths.mockResolvedValue([]);
mocked.isEnabledAtPath.mockResolvedValue(true);
mocked.getHandledDeprecatedConfigs.mockReturnValue([]);
return mocked;
};

View file

@ -7,9 +7,15 @@
*/
export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
import type { applyDeprecations } from './deprecation/apply_deprecations';
jest.mock('../../../package.json', () => mockPackage);
export const mockApplyDeprecations = jest.fn((config, deprecations, log) => config);
export const mockApplyDeprecations = jest.fn<
Record<string, any>,
Parameters<typeof applyDeprecations>
>((config, deprecations, createAddDeprecation) => config);
jest.mock('./deprecation/apply_deprecations', () => ({
applyDeprecations: mockApplyDeprecations,
}));

View file

@ -72,10 +72,10 @@ test('throws if config at path does not match schema', async () => {
);
await expect(valuesReceived).toMatchInlineSnapshot(`
Array [
[Error: [config validation of [key]]: expected value of type [string] but got [number]],
]
`);
Array [
[Error: [config validation of [key]]: expected value of type [string] but got [number]],
]
`);
});
test('re-validate config when updated', async () => {
@ -97,11 +97,11 @@ test('re-validate config when updated', async () => {
rawConfig$.next({ key: 123 });
await expect(valuesReceived).toMatchInlineSnapshot(`
Array [
"value",
[Error: [config validation of [key]]: expected value of type [string] but got [number]],
]
expect(valuesReceived).toMatchInlineSnapshot(`
Array [
"value",
[Error: [config validation of [key]]: expected value of type [string] but got [number]],
]
`);
});
@ -416,10 +416,10 @@ test('throws during validation is any schema is invalid', async () => {
test('logs deprecation warning during validation', async () => {
const rawConfig = getRawConfigProvider({});
const configService = new ConfigService(rawConfig, defaultEnv, logger);
mockApplyDeprecations.mockImplementationOnce((config, deprecations, log) => {
log('some deprecation message');
log('another deprecation message');
mockApplyDeprecations.mockImplementationOnce((config, deprecations, createAddDeprecation) => {
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'some deprecation message' });
addDeprecation({ message: 'another deprecation message' });
return config;
});
@ -437,6 +437,37 @@ test('logs deprecation warning during validation', async () => {
`);
});
test('does not log warnings for silent deprecations during validation', async () => {
const rawConfig = getRawConfigProvider({});
const configService = new ConfigService(rawConfig, defaultEnv, logger);
mockApplyDeprecations
.mockImplementationOnce((config, deprecations, createAddDeprecation) => {
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'some deprecation message', silent: true });
addDeprecation({ message: 'another deprecation message' });
return config;
})
.mockImplementationOnce((config, deprecations, createAddDeprecation) => {
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'I am silent', silent: true });
return config;
});
loggerMock.clear(logger);
await configService.validate();
expect(loggerMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"another deprecation message",
],
]
`);
loggerMock.clear(logger);
await configService.validate();
expect(loggerMock.collect(logger).warn).toMatchInlineSnapshot(`Array []`);
});
describe('atPathSync', () => {
test('returns the value at path', async () => {
const rawConfig = getRawConfigProvider({ key: 'foo' });
@ -477,3 +508,36 @@ describe('atPathSync', () => {
expect(configService.atPathSync('key')).toEqual('new-value');
});
});
describe('getHandledDeprecatedConfigs', () => {
it('returns all handled deprecated configs', async () => {
const rawConfig = getRawConfigProvider({ base: { unused: 'unusedConfig' } });
const configService = new ConfigService(rawConfig, defaultEnv, logger);
configService.addDeprecationProvider('base', ({ unused }) => [unused('unused')]);
mockApplyDeprecations.mockImplementationOnce((config, deprecations, createAddDeprecation) => {
deprecations.forEach((deprecation) => {
const addDeprecation = createAddDeprecation!(deprecation.path);
addDeprecation({ message: `some deprecation message`, documentationUrl: 'some-url' });
});
return config;
});
await configService.validate();
expect(configService.getHandledDeprecatedConfigs()).toMatchInlineSnapshot(`
Array [
Array [
"base",
Array [
Object {
"documentationUrl": "some-url",
"message": "some deprecation message",
},
],
],
]
`);
});
});

View file

@ -21,6 +21,7 @@ import {
ConfigDeprecationWithContext,
ConfigDeprecationProvider,
configDeprecationFactory,
DeprecatedConfigDetails,
} from './deprecation';
import { LegacyObjectToConfigAdapter } from './legacy';
@ -43,6 +44,7 @@ export class ConfigService {
private readonly handledPaths: Set<ConfigPath> = new Set();
private readonly schemas = new Map<string, Type<unknown>>();
private readonly deprecations = new BehaviorSubject<ConfigDeprecationWithContext[]>([]);
private readonly handledDeprecatedConfigs = new Map<string, DeprecatedConfigDetails[]>();
constructor(
private readonly rawConfigProvider: RawConfigurationProvider,
@ -91,6 +93,13 @@ export class ConfigService {
]);
}
/**
* returns all handled deprecated configs
*/
public getHandledDeprecatedConfigs() {
return [...this.handledDeprecatedConfigs.entries()];
}
/**
* Validate the whole configuration and log the deprecation warnings.
*
@ -186,8 +195,16 @@ export class ConfigService {
const rawConfig = await this.rawConfigProvider.getConfig$().pipe(take(1)).toPromise();
const deprecations = await this.deprecations.pipe(take(1)).toPromise();
const deprecationMessages: string[] = [];
const logger = (msg: string) => deprecationMessages.push(msg);
applyDeprecations(rawConfig, deprecations, logger);
const createAddDeprecation = (domainId: string) => (context: DeprecatedConfigDetails) => {
if (!context.silent) {
deprecationMessages.push(context.message);
}
const handledDeprecatedConfig = this.handledDeprecatedConfigs.get(domainId) || [];
handledDeprecatedConfig.push(context);
this.handledDeprecatedConfigs.set(domainId, handledDeprecatedConfig);
};
applyDeprecations(rawConfig, deprecations, createAddDeprecation);
deprecationMessages.forEach((msg) => {
this.deprecationLog.warn(msg);
});

View file

@ -32,8 +32,9 @@ describe('applyDeprecations', () => {
expect(handlerC).toHaveBeenCalledTimes(1);
});
it('calls handlers with correct arguments', () => {
const logger = () => undefined;
it('passes path to addDeprecation factory', () => {
const addDeprecation = jest.fn();
const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
const alteredConfig = { foo: 'bar' };
@ -43,11 +44,33 @@ describe('applyDeprecations', () => {
applyDeprecations(
initialConfig,
[wrapHandler(handlerA, 'pathA'), wrapHandler(handlerB, 'pathB')],
logger
createAddDeprecation
);
expect(handlerA).toHaveBeenCalledWith(initialConfig, 'pathA', logger);
expect(handlerB).toHaveBeenCalledWith(alteredConfig, 'pathB', logger);
expect(handlerA).toHaveBeenCalledWith(initialConfig, 'pathA', addDeprecation);
expect(handlerB).toHaveBeenCalledWith(alteredConfig, 'pathB', addDeprecation);
expect(createAddDeprecation).toBeCalledTimes(2);
expect(createAddDeprecation).toHaveBeenNthCalledWith(1, 'pathA');
expect(createAddDeprecation).toHaveBeenNthCalledWith(2, 'pathB');
});
it('calls handlers with correct arguments', () => {
const addDeprecation = jest.fn();
const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
const alteredConfig = { foo: 'bar' };
const handlerA = jest.fn().mockReturnValue(alteredConfig);
const handlerB = jest.fn().mockImplementation((conf) => conf);
applyDeprecations(
initialConfig,
[wrapHandler(handlerA, 'pathA'), wrapHandler(handlerB, 'pathB')],
createAddDeprecation
);
expect(handlerA).toHaveBeenCalledWith(initialConfig, 'pathA', addDeprecation);
expect(handlerB).toHaveBeenCalledWith(alteredConfig, 'pathB', addDeprecation);
});
it('returns the migrated config', () => {

View file

@ -7,23 +7,24 @@
*/
import { cloneDeep } from 'lodash';
import { ConfigDeprecationWithContext, ConfigDeprecationLogger } from './types';
const noopLogger = (msg: string) => undefined;
import { ConfigDeprecationWithContext, AddConfigDeprecation } from './types';
const noopAddDeprecationFactory: () => AddConfigDeprecation = () => () => undefined;
/**
* Applies deprecations on given configuration and logs any deprecation warning using provided logger.
* Applies deprecations on given configuration and passes addDeprecation hook.
* This hook is used for logging any deprecation warning using provided logger.
* This hook is used for exposing deprecated configs that must be handled by the user before upgrading to next major.
*
* @internal
*/
export const applyDeprecations = (
config: Record<string, any>,
deprecations: ConfigDeprecationWithContext[],
logger: ConfigDeprecationLogger = noopLogger
createAddDeprecation: (pluginId: string) => AddConfigDeprecation = noopAddDeprecationFactory
) => {
let processed = cloneDeep(config);
deprecations.forEach(({ deprecation, path }) => {
processed = deprecation(processed, path, logger);
processed = deprecation(processed, path, createAddDeprecation(path));
});
return processed;
};

View file

@ -6,17 +6,16 @@
* Side Public License, v 1.
*/
import { ConfigDeprecationLogger } from './types';
import { DeprecatedConfigDetails } from './types';
import { configDeprecationFactory } from './deprecation_factory';
describe('DeprecationFactory', () => {
const { rename, unused, renameFromRoot, unusedFromRoot } = configDeprecationFactory;
let deprecationMessages: string[];
const logger: ConfigDeprecationLogger = (msg) => deprecationMessages.push(msg);
const addDeprecation = jest.fn<void, [DeprecatedConfigDetails]>();
beforeEach(() => {
deprecationMessages = [];
addDeprecation.mockClear();
});
describe('rename', () => {
@ -30,7 +29,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', logger);
const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
renamed: 'toberenamed',
@ -40,9 +39,18 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Replace \\"myplugin.deprecated\\" with \\"myplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).",
],
},
"message": "\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\"",
},
],
]
`);
});
@ -56,7 +64,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = rename('deprecated', 'new')(rawConfig, 'myplugin', logger);
const processed = rename('deprecated', 'new')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
new: 'new',
@ -66,7 +74,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
expect(addDeprecation).toHaveBeenCalledTimes(0);
});
it('handles nested keys', () => {
const rawConfig = {
@ -83,7 +91,7 @@ describe('DeprecationFactory', () => {
const processed = rename('oldsection.deprecated', 'newsection.renamed')(
rawConfig,
'myplugin',
logger
addDeprecation
);
expect(processed).toEqual({
myplugin: {
@ -97,9 +105,18 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"myplugin.oldsection.deprecated\\" is deprecated and has been replaced by \\"myplugin.newsection.renamed\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Replace \\"myplugin.oldsection.deprecated\\" with \\"myplugin.newsection.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).",
],
},
"message": "\\"myplugin.oldsection.deprecated\\" is deprecated and has been replaced by \\"myplugin.newsection.renamed\\"",
},
],
]
`);
});
@ -110,15 +127,25 @@ describe('DeprecationFactory', () => {
renamed: 'renamed',
},
};
const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', logger);
const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
renamed: 'renamed',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\". However both key are present, ignoring \\"myplugin.deprecated\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Make sure \\"myplugin.renamed\\" contains the correct value in the config file, CLI flag, or environment variable (in Docker only).",
"Remove \\"myplugin.deprecated\\" from the config.",
],
},
"message": "\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\". However both key are present, ignoring \\"myplugin.deprecated\\"",
},
],
]
`);
});
@ -138,7 +165,7 @@ describe('DeprecationFactory', () => {
const processed = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
rawConfig,
'does-not-matter',
logger
addDeprecation
);
expect(processed).toEqual({
myplugin: {
@ -149,9 +176,18 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Replace \\"myplugin.deprecated\\" with \\"myplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).",
],
},
"message": "\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\"",
},
],
]
`);
});
@ -169,7 +205,7 @@ describe('DeprecationFactory', () => {
const processed = renameFromRoot('oldplugin.deprecated', 'newplugin.renamed')(
rawConfig,
'does-not-matter',
logger
addDeprecation
);
expect(processed).toEqual({
oldplugin: {
@ -180,9 +216,18 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"oldplugin.deprecated\\" is deprecated and has been replaced by \\"newplugin.renamed\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Replace \\"oldplugin.deprecated\\" with \\"newplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).",
],
},
"message": "\\"oldplugin.deprecated\\" is deprecated and has been replaced by \\"newplugin.renamed\\"",
},
],
]
`);
});
@ -200,7 +245,7 @@ describe('DeprecationFactory', () => {
const processed = renameFromRoot('myplugin.deprecated', 'myplugin.new')(
rawConfig,
'does-not-matter',
logger
addDeprecation
);
expect(processed).toEqual({
myplugin: {
@ -211,7 +256,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
expect(addDeprecation).toBeCalledTimes(0);
});
it('remove the old property but does not overrides the new one if they both exist, and logs a specific message', () => {
@ -224,16 +269,27 @@ describe('DeprecationFactory', () => {
const processed = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
rawConfig,
'does-not-matter',
logger
addDeprecation
);
expect(processed).toEqual({
myplugin: {
renamed: 'renamed',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\". However both key are present, ignoring \\"myplugin.deprecated\\"",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Make sure \\"myplugin.renamed\\" contains the correct value in the config file, CLI flag, or environment variable (in Docker only).",
"Remove \\"myplugin.deprecated\\" from the config.",
],
},
"message": "\\"myplugin.deprecated\\" is deprecated and has been replaced by \\"myplugin.renamed\\". However both key are present, ignoring \\"myplugin.deprecated\\"",
},
],
]
`);
});
@ -250,7 +306,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = unused('deprecated')(rawConfig, 'myplugin', logger);
const processed = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
valid: 'valid',
@ -259,9 +315,18 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"myplugin.deprecated is deprecated and is no longer used",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only)",
],
},
"message": "myplugin.deprecated is deprecated and is no longer used",
},
],
]
`);
});
@ -278,7 +343,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = unused('section.deprecated')(rawConfig, 'myplugin', logger);
const processed = unused('section.deprecated')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
valid: 'valid',
@ -288,9 +353,19 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"myplugin.section.deprecated is deprecated and is no longer used",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Remove \\"myplugin.section.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only)",
],
},
"message": "myplugin.section.deprecated is deprecated and is no longer used",
},
],
]
`);
});
@ -304,7 +379,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = unused('deprecated')(rawConfig, 'myplugin', logger);
const processed = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
expect(processed).toEqual({
myplugin: {
valid: 'valid',
@ -313,7 +388,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
expect(addDeprecation).toBeCalledTimes(0);
});
});
@ -328,7 +403,11 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = unusedFromRoot('myplugin.deprecated')(rawConfig, 'does-not-matter', logger);
const processed = unusedFromRoot('myplugin.deprecated')(
rawConfig,
'does-not-matter',
addDeprecation
);
expect(processed).toEqual({
myplugin: {
valid: 'valid',
@ -337,9 +416,19 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages).toMatchInlineSnapshot(`
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
"myplugin.deprecated is deprecated and is no longer used",
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only)",
],
},
"message": "myplugin.deprecated is deprecated and is no longer used",
},
],
]
`);
});
@ -353,7 +442,11 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
const processed = unusedFromRoot('myplugin.deprecated')(rawConfig, 'does-not-matter', logger);
const processed = unusedFromRoot('myplugin.deprecated')(
rawConfig,
'does-not-matter',
addDeprecation
);
expect(processed).toEqual({
myplugin: {
valid: 'valid',
@ -362,7 +455,7 @@ describe('DeprecationFactory', () => {
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
expect(addDeprecation).toBeCalledTimes(0);
});
});
});

View file

@ -9,15 +9,20 @@
import { get } from 'lodash';
import { set } from '@elastic/safer-lodash-set';
import { unset } from '@kbn/std';
import { ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationFactory } from './types';
import {
ConfigDeprecation,
AddConfigDeprecation,
ConfigDeprecationFactory,
DeprecatedConfigDetails,
} from './types';
const _rename = (
config: Record<string, any>,
rootPath: string,
log: ConfigDeprecationLogger,
addDeprecation: AddConfigDeprecation,
oldKey: string,
newKey: string,
silent?: boolean
details?: Partial<DeprecatedConfigDetails>
) => {
const fullOldPath = getPath(rootPath, oldKey);
const oldValue = get(config, fullOldPath);
@ -32,48 +37,80 @@ const _rename = (
if (newValue === undefined) {
set(config, fullNewPath, oldValue);
if (!silent) {
log(`"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}"`);
}
addDeprecation({
message: `"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}"`,
correctiveActions: {
manualSteps: [
`Replace "${fullOldPath}" with "${fullNewPath}" in the Kibana config file, CLI flag, or environment variable (in Docker only).`,
],
},
...details,
});
} else {
if (!silent) {
log(
`"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}". However both key are present, ignoring "${fullOldPath}"`
);
}
addDeprecation({
message: `"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}". However both key are present, ignoring "${fullOldPath}"`,
correctiveActions: {
manualSteps: [
`Make sure "${fullNewPath}" contains the correct value in the config file, CLI flag, or environment variable (in Docker only).`,
`Remove "${fullOldPath}" from the config.`,
],
},
...details,
});
}
return config;
};
const _unused = (
config: Record<string, any>,
rootPath: string,
log: ConfigDeprecationLogger,
unusedKey: string
addDeprecation: AddConfigDeprecation,
unusedKey: string,
details?: Partial<DeprecatedConfigDetails>
) => {
const fullPath = getPath(rootPath, unusedKey);
if (get(config, fullPath) === undefined) {
return config;
}
unset(config, fullPath);
log(`${fullPath} is deprecated and is no longer used`);
addDeprecation({
message: `${fullPath} is deprecated and is no longer used`,
correctiveActions: {
manualSteps: [
`Remove "${fullPath}" from the Kibana config file, CLI flag, or environment variable (in Docker only)`,
],
},
...details,
});
return config;
};
const rename = (oldKey: string, newKey: string): ConfigDeprecation => (config, rootPath, log) =>
_rename(config, rootPath, log, oldKey, newKey);
const rename = (
oldKey: string,
newKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation => (config, rootPath, addDeprecation) =>
_rename(config, rootPath, addDeprecation, oldKey, newKey, details);
const renameFromRoot = (oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation => (
config,
rootPath,
log
) => _rename(config, '', log, oldKey, newKey, silent);
const renameFromRoot = (
oldKey: string,
newKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation => (config, rootPath, addDeprecation) =>
_rename(config, '', addDeprecation, oldKey, newKey, details);
const unused = (unusedKey: string): ConfigDeprecation => (config, rootPath, log) =>
_unused(config, rootPath, log, unusedKey);
const unused = (
unusedKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation => (config, rootPath, addDeprecation) =>
_unused(config, rootPath, addDeprecation, unusedKey, details);
const unusedFromRoot = (unusedKey: string): ConfigDeprecation => (config, rootPath, log) =>
_unused(config, '', log, unusedKey);
const unusedFromRoot = (
unusedKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation => (config, rootPath, addDeprecation) =>
_unused(config, '', addDeprecation, unusedKey, details);
const getPath = (rootPath: string, subPath: string) =>
rootPath !== '' ? `${rootPath}.${subPath}` : subPath;

View file

@ -6,12 +6,13 @@
* Side Public License, v 1.
*/
export {
export type {
ConfigDeprecation,
ConfigDeprecationWithContext,
ConfigDeprecationLogger,
ConfigDeprecationFactory,
AddConfigDeprecation,
ConfigDeprecationProvider,
DeprecatedConfigDetails,
} from './types';
export { configDeprecationFactory } from './deprecation_factory';
export { applyDeprecations } from './apply_deprecations';

View file

@ -7,11 +7,33 @@
*/
/**
* Logger interface used when invoking a {@link ConfigDeprecation}
* Config deprecation hook used when invoking a {@link ConfigDeprecation}
*
* @public
*/
export type ConfigDeprecationLogger = (message: string) => void;
export type AddConfigDeprecation = (details: DeprecatedConfigDetails) => void;
/**
* Deprecated Config Details
*
* @public
*/
export interface DeprecatedConfigDetails {
/* The message to be displayed for the deprecation. */
message: string;
/* (optional) set false to prevent the config service from logging the deprecation message. */
silent?: boolean;
/* (optional) link to the documentation for more details on the deprecation. */
documentationUrl?: string;
/* (optional) corrective action needed to fix this deprecation. */
correctiveActions?: {
/**
* Specify a list of manual steps our users need to follow
* to fix the deprecation before upgrade.
*/
manualSteps: string[];
};
}
/**
* Configuration deprecation returned from {@link ConfigDeprecationProvider} that handles a single deprecation from the configuration.
@ -25,7 +47,7 @@ export type ConfigDeprecationLogger = (message: string) => void;
export type ConfigDeprecation = (
config: Record<string, any>,
fromPath: string,
logger: ConfigDeprecationLogger
addDeprecation: AddConfigDeprecation
) => Record<string, any>;
/**
@ -62,6 +84,7 @@ export type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => C
*
* @public
*/
export interface ConfigDeprecationFactory {
/**
* Rename a configuration property from inside a plugin's configuration path.
@ -75,7 +98,11 @@ export interface ConfigDeprecationFactory {
* ]
* ```
*/
rename(oldKey: string, newKey: string): ConfigDeprecation;
rename(
oldKey: string,
newKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation;
/**
* Rename a configuration property from the root configuration.
* Will log a deprecation warning if the oldKey was found and deprecation applied.
@ -91,7 +118,11 @@ export interface ConfigDeprecationFactory {
* ]
* ```
*/
renameFromRoot(oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation;
renameFromRoot(
oldKey: string,
newKey: string,
details?: Partial<DeprecatedConfigDetails>
): ConfigDeprecation;
/**
* Remove a configuration property from inside a plugin's configuration path.
* Will log a deprecation warning if the unused key was found and deprecation applied.
@ -104,7 +135,7 @@ export interface ConfigDeprecationFactory {
* ]
* ```
*/
unused(unusedKey: string): ConfigDeprecation;
unused(unusedKey: string, details?: Partial<DeprecatedConfigDetails>): ConfigDeprecation;
/**
* Remove a configuration property from the root configuration.
* Will log a deprecation warning if the unused key was found and deprecation applied.
@ -120,7 +151,7 @@ export interface ConfigDeprecationFactory {
* ]
* ```
*/
unusedFromRoot(unusedKey: string): ConfigDeprecation;
unusedFromRoot(unusedKey: string, details?: Partial<DeprecatedConfigDetails>): ConfigDeprecation;
}
/** @internal */

View file

@ -6,16 +6,16 @@
* Side Public License, v 1.
*/
export {
applyDeprecations,
ConfigDeprecation,
export type {
ConfigDeprecationFactory,
configDeprecationFactory,
ConfigDeprecationLogger,
AddConfigDeprecation,
ConfigDeprecationProvider,
ConfigDeprecationWithContext,
ConfigDeprecation,
} from './deprecation';
export { applyDeprecations, configDeprecationFactory } from './deprecation';
export {
RawConfigurationProvider,
RawConfigService,

View file

@ -28,6 +28,7 @@ import { DocLinksService } from './doc_links';
import { RenderingService } from './rendering';
import { SavedObjectsService } from './saved_objects';
import { IntegrationsService } from './integrations';
import { DeprecationsService } from './deprecations';
import { CoreApp } from './core_app';
import type { InternalApplicationSetup, InternalApplicationStart } from './application/types';
@ -82,7 +83,7 @@ export class CoreSystem {
private readonly rendering: RenderingService;
private readonly integrations: IntegrationsService;
private readonly coreApp: CoreApp;
private readonly deprecations: DeprecationsService;
private readonly rootDomElement: HTMLElement;
private readonly coreContext: CoreContext;
private fatalErrorsSetup: FatalErrorsSetup | null = null;
@ -113,6 +114,7 @@ export class CoreSystem {
this.rendering = new RenderingService();
this.application = new ApplicationService();
this.integrations = new IntegrationsService();
this.deprecations = new DeprecationsService();
this.coreContext = { coreId: Symbol('core'), env: injectedMetadata.env };
this.plugins = new PluginsService(this.coreContext, injectedMetadata.uiPlugins);
@ -195,6 +197,7 @@ export class CoreSystem {
injectedMetadata,
notifications,
});
const deprecations = this.deprecations.start({ http });
this.coreApp.start({ application, http, notifications, uiSettings });
@ -210,6 +213,7 @@ export class CoreSystem {
overlays,
uiSettings,
fatalErrors,
deprecations,
};
await this.plugins.start(core);
@ -252,6 +256,7 @@ export class CoreSystem {
this.chrome.stop();
this.i18n.stop();
this.application.stop();
this.deprecations.stop();
this.rootDomElement.textContent = '';
}
}

View file

@ -0,0 +1,187 @@
/*
* 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 { httpServiceMock } from '../http/http_service.mock';
import { DeprecationsClient } from './deprecations_client';
import type { DomainDeprecationDetails } from '../../server/types';
describe('DeprecationsClient', () => {
const http = httpServiceMock.createSetupContract();
const mockDeprecations = [
{ domainId: 'testPluginId-1' },
{ domainId: 'testPluginId-1' },
{ domainId: 'testPluginId-2' },
];
beforeEach(() => {
http.fetch.mockReset();
http.fetch.mockResolvedValue({ deprecations: mockDeprecations });
});
describe('getAllDeprecations', () => {
it('returns a list of deprecations', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const deprecations = await deprecationsClient.getAllDeprecations();
expect(http.fetch).toBeCalledTimes(1);
expect(http.fetch).toBeCalledWith('/api/deprecations/', {
asSystemRequest: true,
});
expect(deprecations).toEqual(mockDeprecations);
});
});
describe('getDeprecations', () => {
it('returns deprecations for a single domainId', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const deprecations = await deprecationsClient.getDeprecations('testPluginId-1');
expect(deprecations.length).toBe(2);
expect(deprecations).toEqual([
{ domainId: 'testPluginId-1' },
{ domainId: 'testPluginId-1' },
]);
});
it('returns [] if the domainId does not have any deprecations', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const deprecations = await deprecationsClient.getDeprecations('testPluginId-4');
expect(deprecations).toEqual([]);
});
it('calls the fetch api', async () => {
const deprecationsClient = new DeprecationsClient({ http });
http.fetch.mockResolvedValueOnce({
deprecations: [{ domainId: 'testPluginId-1' }, { domainId: 'testPluginId-1' }],
});
http.fetch.mockResolvedValueOnce({
deprecations: [{ domainId: 'testPluginId-2' }, { domainId: 'testPluginId-2' }],
});
const results = [
...(await deprecationsClient.getDeprecations('testPluginId-1')),
...(await deprecationsClient.getDeprecations('testPluginId-2')),
];
expect(http.fetch).toBeCalledTimes(2);
expect(results).toEqual([
{ domainId: 'testPluginId-1' },
{ domainId: 'testPluginId-1' },
{ domainId: 'testPluginId-2' },
{ domainId: 'testPluginId-2' },
]);
});
});
describe('isDeprecationResolvable', () => {
it('returns true if deprecation has correctiveActions.api', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const mockDeprecationDetails: DomainDeprecationDetails = {
domainId: 'testPluginId-1',
message: 'some-message',
level: 'warning',
correctiveActions: {
api: {
path: 'some-path',
method: 'POST',
},
},
};
const isResolvable = deprecationsClient.isDeprecationResolvable(mockDeprecationDetails);
expect(isResolvable).toBe(true);
});
it('returns false if deprecation is missing correctiveActions.api', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const mockDeprecationDetails: DomainDeprecationDetails = {
domainId: 'testPluginId-1',
message: 'some-message',
level: 'warning',
correctiveActions: {},
};
const isResolvable = deprecationsClient.isDeprecationResolvable(mockDeprecationDetails);
expect(isResolvable).toBe(false);
});
});
describe('resolveDeprecation', () => {
it('fails if deprecation is not resolvable', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const mockDeprecationDetails: DomainDeprecationDetails = {
domainId: 'testPluginId-1',
message: 'some-message',
level: 'warning',
correctiveActions: {},
};
const result = await deprecationsClient.resolveDeprecation(mockDeprecationDetails);
expect(result).toEqual({
status: 'fail',
reason: 'deprecation has no correctiveAction via api.',
});
});
it('fetches the deprecation api', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const mockDeprecationDetails: DomainDeprecationDetails = {
domainId: 'testPluginId-1',
message: 'some-message',
level: 'warning',
correctiveActions: {
api: {
path: 'some-path',
method: 'POST',
body: {
extra_param: 123,
},
},
},
};
const result = await deprecationsClient.resolveDeprecation(mockDeprecationDetails);
expect(http.fetch).toBeCalledTimes(1);
expect(http.fetch).toBeCalledWith({
path: 'some-path',
method: 'POST',
asSystemRequest: true,
body: JSON.stringify({
extra_param: 123,
deprecationDetails: { domainId: 'testPluginId-1' },
}),
});
expect(result).toEqual({ status: 'ok' });
});
it('fails when fetch fails', async () => {
const deprecationsClient = new DeprecationsClient({ http });
const mockResponse = 'Failed to fetch';
const mockDeprecationDetails: DomainDeprecationDetails = {
domainId: 'testPluginId-1',
message: 'some-message',
level: 'warning',
correctiveActions: {
api: {
path: 'some-path',
method: 'POST',
body: {
extra_param: 123,
},
},
},
};
http.fetch.mockRejectedValue({ body: { message: mockResponse } });
const result = await deprecationsClient.resolveDeprecation(mockDeprecationDetails);
expect(result).toEqual({ status: 'fail', reason: mockResponse });
});
});
});

View file

@ -0,0 +1,78 @@
/*
* 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 type { HttpStart } from '../http';
import type { DomainDeprecationDetails, DeprecationsGetResponse } from '../../server/types';
/* @internal */
export interface DeprecationsClientDeps {
http: Pick<HttpStart, 'fetch'>;
}
/* @internal */
export type ResolveDeprecationResponse = { status: 'ok' } | { status: 'fail'; reason: string };
export class DeprecationsClient {
private readonly http: Pick<HttpStart, 'fetch'>;
constructor({ http }: DeprecationsClientDeps) {
this.http = http;
}
private fetchDeprecations = async (): Promise<DomainDeprecationDetails[]> => {
const { deprecations } = await this.http.fetch<DeprecationsGetResponse>('/api/deprecations/', {
asSystemRequest: true,
});
return deprecations;
};
public getAllDeprecations = async () => {
return await this.fetchDeprecations();
};
public getDeprecations = async (domainId: string) => {
const deprecations = await this.fetchDeprecations();
return deprecations.filter((deprecation) => deprecation.domainId === domainId);
};
public isDeprecationResolvable = (details: DomainDeprecationDetails) => {
return typeof details.correctiveActions.api === 'object';
};
public resolveDeprecation = async (
details: DomainDeprecationDetails
): Promise<ResolveDeprecationResponse> => {
const { domainId, correctiveActions } = details;
// explicit check required for TS type guard
if (typeof correctiveActions.api !== 'object') {
return {
status: 'fail',
reason: 'deprecation has no correctiveAction via api.',
};
}
const { body, method, path } = correctiveActions.api;
try {
await this.http.fetch<void>({
path,
method,
asSystemRequest: true,
body: JSON.stringify({
...body,
deprecationDetails: { domainId },
}),
});
return { status: 'ok' };
} catch (err) {
return {
status: 'fail',
reason: err.body.message,
};
}
};
}

View file

@ -0,0 +1,36 @@
/*
* 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 type { PublicMethodsOf } from '@kbn/utility-types';
import { DeprecationsService } from './deprecations_service';
import type { DeprecationsServiceStart } from './deprecations_service';
const createServiceMock = (): jest.Mocked<DeprecationsServiceStart> => ({
getAllDeprecations: jest.fn().mockResolvedValue([]),
getDeprecations: jest.fn().mockResolvedValue([]),
isDeprecationResolvable: jest.fn().mockReturnValue(false),
resolveDeprecation: jest.fn().mockResolvedValue({ status: 'ok', payload: {} }),
});
const createMock = () => {
const mocked: jest.Mocked<PublicMethodsOf<DeprecationsService>> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
};
mocked.setup.mockReturnValue(void 0);
mocked.start.mockReturnValue(createServiceMock());
return mocked;
};
export const deprecationsServiceMock = {
create: createMock,
createSetupContract: () => void 0,
createStartContract: createServiceMock,
};

View file

@ -0,0 +1,60 @@
/*
* 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 type { CoreService } from '../../types';
import type { HttpStart } from '../http';
import { DeprecationsClient, ResolveDeprecationResponse } from './deprecations_client';
import type { DomainDeprecationDetails } from '../../server/types';
/**
* DeprecationsService provides methods to fetch domain deprecation details from
* the Kibana server.
*
* @public
*/
export interface DeprecationsServiceStart {
/**
* Grabs deprecations details for all domains.
*/
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
/**
* Grabs deprecations for a specific domain.
*
* @param {string} domainId
*/
getDeprecations: (domainId: string) => Promise<DomainDeprecationDetails[]>;
/**
* Returns a boolean if the provided deprecation can be automatically resolvable.
*
* @param {DomainDeprecationDetails} details
*/
isDeprecationResolvable: (details: DomainDeprecationDetails) => boolean;
/**
* Calls the correctiveActions.api to automatically resolve the depprecation.
*
* @param {DomainDeprecationDetails} details
*/
resolveDeprecation: (details: DomainDeprecationDetails) => Promise<ResolveDeprecationResponse>;
}
export class DeprecationsService implements CoreService<void, DeprecationsServiceStart> {
public setup(): void {}
public start({ http }: { http: HttpStart }): DeprecationsServiceStart {
const deprecationsClient = new DeprecationsClient({ http });
return {
getAllDeprecations: deprecationsClient.getAllDeprecations,
getDeprecations: deprecationsClient.getDeprecations,
isDeprecationResolvable: deprecationsClient.isDeprecationResolvable,
resolveDeprecation: deprecationsClient.resolveDeprecation,
};
}
public stop(): void {}
}

View 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 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.
*/
export { DeprecationsService } from './deprecations_service';
export type { DeprecationsServiceStart } from './deprecations_service';
export type { ResolveDeprecationResponse } from './deprecations_client';

View file

@ -65,6 +65,7 @@ import { UiSettingsState, IUiSettingsClient } from './ui_settings';
import { ApplicationSetup, Capabilities, ApplicationStart } from './application';
import { DocLinksStart } from './doc_links';
import { SavedObjectsStart } from './saved_objects';
import { DeprecationsServiceStart } from './deprecations';
export type { PackageInfo, EnvironmentMode, IExternalUrlPolicy } from '../server/types';
export type { CoreContext, CoreSystem } from './core_system';
@ -184,6 +185,8 @@ export type {
ErrorToastOptions,
} from './notifications';
export type { DeprecationsServiceStart, ResolveDeprecationResponse } from './deprecations';
export type { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types';
export { URL_MAX_LENGTH } from './core_app';
@ -268,6 +271,8 @@ export interface CoreStart {
uiSettings: IUiSettingsClient;
/** {@link FatalErrorsStart} */
fatalErrors: FatalErrorsStart;
/** {@link DeprecationsServiceStart} */
deprecations: DeprecationsServiceStart;
/**
* exposed temporarily until https://github.com/elastic/kibana/issues/41990 done
* use *only* to retrieve config values. There is no way to set injected values

View file

@ -24,6 +24,7 @@ import { overlayServiceMock } from './overlays/overlay_service.mock';
import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
import { injectedMetadataServiceMock } from './injected_metadata/injected_metadata_service.mock';
import { deprecationsServiceMock } from './deprecations/deprecations_service.mock';
export { chromeServiceMock } from './chrome/chrome_service.mock';
export { docLinksServiceMock } from './doc_links/doc_links_service.mock';
@ -37,6 +38,7 @@ export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
export { scopedHistoryMock } from './application/scoped_history.mock';
export { applicationServiceMock } from './application/application_service.mock';
export { deprecationsServiceMock } from './deprecations/deprecations_service.mock';
function createCoreSetupMock({
basePath = '',
@ -57,6 +59,7 @@ function createCoreSetupMock({
http: httpServiceMock.createSetupContract({ basePath }),
notifications: notificationServiceMock.createSetupContract(),
uiSettings: uiSettingsServiceMock.createSetupContract(),
deprecations: deprecationsServiceMock.createSetupContract(),
injectedMetadata: {
getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar,
},
@ -76,6 +79,7 @@ function createCoreStartMock({ basePath = '' } = {}) {
overlays: overlayServiceMock.createStartContract(),
uiSettings: uiSettingsServiceMock.createStartContract(),
savedObjects: savedObjectsServiceMock.createStartContract(),
deprecations: deprecationsServiceMock.createStartContract(),
injectedMetadata: {
getInjectedVar: injectedMetadataServiceMock.createStartContract().getInjectedVar,
},

View file

@ -139,5 +139,6 @@ export function createPluginStartContext<
getInjectedVar: deps.injectedMetadata.getInjectedVar,
},
fatalErrors: deps.fatalErrors,
deprecations: deps.deprecations,
};
}

View file

@ -34,6 +34,7 @@ import { httpServiceMock } from '../http/http_service.mock';
import { CoreSetup, CoreStart, PluginInitializerContext } from '..';
import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
import { deprecationsServiceMock } from '../deprecations/deprecations_service.mock';
export let mockPluginInitializers: Map<PluginName, MockedPluginInitializer>;
@ -101,6 +102,7 @@ describe('PluginsService', () => {
uiSettings: uiSettingsServiceMock.createStartContract(),
savedObjects: savedObjectsServiceMock.createStartContract(),
fatalErrors: fatalErrorsServiceMock.createStartContract(),
deprecations: deprecationsServiceMock.createStartContract(),
};
mockStartContext = {
...mockStartDeps,

View file

@ -432,6 +432,8 @@ export interface CoreStart {
// (undocumented)
chrome: ChromeStart;
// (undocumented)
deprecations: DeprecationsServiceStart;
// (undocumented)
docLinks: DocLinksStart;
// (undocumented)
fatalErrors: FatalErrorsStart;
@ -472,6 +474,15 @@ export class CoreSystem {
// @internal (undocumented)
export const DEFAULT_APP_CATEGORIES: Record<string, AppCategory>;
// @public
export interface DeprecationsServiceStart {
// Warning: (ae-forgotten-export) The symbol "DomainDeprecationDetails" needs to be exported by the entry point index.d.ts
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
getDeprecations: (domainId: string) => Promise<DomainDeprecationDetails[]>;
isDeprecationResolvable: (details: DomainDeprecationDetails) => boolean;
resolveDeprecation: (details: DomainDeprecationDetails) => Promise<ResolveDeprecationResponse>;
}
// @public (undocumented)
export interface DocLinksStart {
// (undocumented)
@ -1075,6 +1086,16 @@ export type PublicAppSearchDeepLinkInfo = Omit<AppSearchDeepLink, 'searchDeepLin
// @public
export type PublicUiSettingsParams = Omit<UiSettingsParams, 'schema'>;
// Warning: (ae-missing-release-tag) "ResolveDeprecationResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ResolveDeprecationResponse = {
status: 'ok';
} | {
status: 'fail';
reason: string;
};
// Warning: (ae-missing-release-tag) "SavedObject" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -1586,6 +1607,6 @@ export interface UserProvidedValues<T = any> {
// Warnings were encountered during analysis:
//
// src/core/public/core_system.ts:164:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts
// src/core/public/core_system.ts:166:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts
```

View file

@ -20,7 +20,7 @@ const applyCoreDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: '',
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,
@ -325,7 +325,7 @@ describe('core deprecations', () => {
});
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"logging.dest\\" has been deprecated and will be removed in 8.0. To set the destination moving forward, you can use the \\"console\\" appender in your logging configuration or define a custom one. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx.",
"\\"logging.dest\\" has been deprecated and will be removed in 8.0. To set the destination moving forward, you can use the \\"console\\" appender in your logging configuration or define a custom one. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx",
]
`);
});
@ -335,7 +335,7 @@ describe('core deprecations', () => {
});
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"logging.dest\\" has been deprecated and will be removed in 8.0. To set the destination moving forward, you can use the \\"console\\" appender in your logging configuration or define a custom one. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx.",
"\\"logging.dest\\" has been deprecated and will be removed in 8.0. To set the destination moving forward, you can use the \\"console\\" appender in your logging configuration or define a custom one. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx",
]
`);
});
@ -381,7 +381,7 @@ describe('core deprecations', () => {
});
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"logging.json\\" has been deprecated and will be removed in 8.0. To specify log message format moving forward, you can configure the \\"appender.layout\\" property for every custom appender in your logging configuration. There is currently no default layout for custom appenders and each one must be declared explicitly. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx.",
"\\"logging.json\\" has been deprecated and will be removed in 8.0. To specify log message format moving forward, you can configure the \\"appender.layout\\" property for every custom appender in your logging configuration. There is currently no default layout for custom appenders and each one must be declared explicitly. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx",
]
`);
});
@ -466,7 +466,7 @@ describe('core deprecations', () => {
});
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"logging.filter\\" has been deprecated and will be removed in 8.0. ",
"\\"logging.filter\\" has been deprecated and will be removed in 8.0.",
]
`);
});
@ -477,7 +477,7 @@ describe('core deprecations', () => {
});
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"logging.filter\\" has been deprecated and will be removed in 8.0. ",
"\\"logging.filter\\" has been deprecated and will be removed in 8.0.",
]
`);
});

View file

@ -9,40 +9,43 @@
import { has, get } from 'lodash';
import { ConfigDeprecationProvider, ConfigDeprecation } from '@kbn/config';
const configPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const configPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(process.env, 'CONFIG_PATH')) {
log(
`Environment variable CONFIG_PATH is deprecated. It has been replaced with KBN_PATH_CONF pointing to a config folder`
);
addDeprecation({
message: `Environment variable CONFIG_PATH is deprecated. It has been replaced with KBN_PATH_CONF pointing to a config folder`,
});
}
return settings;
};
const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(process.env, 'DATA_PATH')) {
log(
`Environment variable "DATA_PATH" will be removed. It has been replaced with kibana.yml setting "path.data"`
);
addDeprecation({
message: `Environment variable "DATA_PATH" will be removed. It has been replaced with kibana.yml setting "path.data"`,
});
}
return settings;
};
const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'server.basePath') && !has(settings, 'server.rewriteBasePath')) {
log(
'You should set server.basePath along with server.rewriteBasePath. Starting in 7.0, Kibana ' +
addDeprecation({
message:
'You should set server.basePath along with server.rewriteBasePath. Starting in 7.0, Kibana ' +
'will expect that all requests start with server.basePath rather than expecting you to rewrite ' +
'the requests in your reverse proxy. Set server.rewriteBasePath to false to preserve the ' +
'current behavior and silence this warning.'
);
'current behavior and silence this warning.',
});
}
return settings;
};
const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, log) => {
const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
const corsSettings = get(settings, 'server.cors');
if (typeof get(settings, 'server.cors') === 'boolean') {
log('"server.cors" is deprecated and has been replaced by "server.cors.enabled"');
addDeprecation({
message: '"server.cors" is deprecated and has been replaced by "server.cors.enabled"',
});
settings.server.cors = {
enabled: corsSettings,
};
@ -50,7 +53,7 @@ const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, log) => {
return settings;
};
const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
const NONCE_STRING = `{nonce}`;
// Policies that should include the 'self' source
const SELF_POLICIES = Object.freeze(['script-src', 'style-src']);
@ -67,7 +70,9 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
settings.csp.rules = [...parsed].map(([policy, sourceList]) => {
if (sourceList.find((source) => source.includes(NONCE_STRING))) {
log(`csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`);
addDeprecation({
message: `csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`,
});
sourceList = sourceList.filter((source) => !source.includes(NONCE_STRING));
// Add 'self' if not present
@ -80,7 +85,9 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
SELF_POLICIES.includes(policy) &&
!sourceList.find((source) => source.includes(SELF_STRING))
) {
log(`csp.rules must contain the 'self' source. Automatically adding to ${policy}.`);
addDeprecation({
message: `csp.rules must contain the 'self' source. Automatically adding to ${policy}.`,
});
sourceList.push(SELF_STRING);
}
@ -91,159 +98,202 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
return settings;
};
const mapManifestServiceUrlDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const mapManifestServiceUrlDeprecation: ConfigDeprecation = (
settings,
fromPath,
addDeprecation
) => {
if (has(settings, 'map.manifestServiceUrl')) {
log(
'You should no longer use the map.manifestServiceUrl setting in kibana.yml to configure the location ' +
addDeprecation({
message:
'You should no longer use the map.manifestServiceUrl setting in kibana.yml to configure the location ' +
'of the Elastic Maps Service settings. These settings have moved to the "map.emsTileApiUrl" and ' +
'"map.emsFileApiUrl" settings instead. These settings are for development use only and should not be ' +
'modified for use in production environments.'
);
'modified for use in production environments.',
});
}
return settings;
};
const serverHostZeroDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const serverHostZeroDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (get(settings, 'server.host') === '0') {
log(
'Support for setting server.host to "0" in kibana.yml is deprecated and will be removed in Kibana version 8.0.0. ' +
'Instead use "0.0.0.0" to bind to all interfaces.'
);
addDeprecation({
message:
'Support for setting server.host to "0" in kibana.yml is deprecated and will be removed in Kibana version 8.0.0. ' +
'Instead use "0.0.0.0" to bind to all interfaces.',
});
}
return settings;
};
const opsLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const opsLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.events.ops')) {
log(
'"logging.events.ops" has been deprecated and will be removed ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents',
message:
'"logging.events.ops" has been deprecated and will be removed ' +
'in 8.0. To access ops data moving forward, please enable debug logs for the ' +
'"metrics.ops" context in your logging configuration. For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
});
}
return settings;
};
const requestLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const requestLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.events.request') || has(settings, 'logging.events.response')) {
log(
'"logging.events.request" and "logging.events.response" have been deprecated and will be removed ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents',
message:
'"logging.events.request" and "logging.events.response" have been deprecated and will be removed ' +
'in 8.0. To access request and/or response data moving forward, please enable debug logs for the ' +
'"http.server.response" context in your logging configuration. For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
});
}
return settings;
};
const timezoneLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const timezoneLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.timezone')) {
log(
'"logging.timezone" has been deprecated and will be removed ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingtimezone',
message:
'"logging.timezone" has been deprecated and will be removed ' +
'in 8.0. To set the timezone moving forward, please add a timezone date modifier to the log pattern ' +
'in your logging configuration. For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
});
}
return settings;
};
const destLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const destLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.dest')) {
log(
'"logging.dest" has been deprecated and will be removed ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingdest',
message:
'"logging.dest" has been deprecated and will be removed ' +
'in 8.0. To set the destination moving forward, you can use the "console" appender ' +
'in your logging configuration or define a custom one. For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx.'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
});
}
return settings;
};
const quietLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const quietLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.quiet')) {
log(
'"logging.quiet" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:error" in your logging configuration. '
);
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingquiet',
message:
'"logging.quiet" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:error" in your logging configuration. ',
});
}
return settings;
};
const silentLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const silentLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.silent')) {
log(
'"logging.silent" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:off" in your logging configuration. '
);
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingsilent',
message:
'"logging.silent" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:off" in your logging configuration. ',
});
}
return settings;
};
const verboseLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const verboseLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.verbose')) {
log(
'"logging.verbose" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:all" in your logging configuration. '
);
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingverbose',
message:
'"logging.verbose" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level:all" in your logging configuration. ',
});
}
return settings;
};
const jsonLoggingDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const jsonLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
// We silence the deprecation warning when running in development mode because
// the dev CLI code in src/dev/cli_dev_mode/using_server_process.ts manually
// specifies `--logging.json=false`. Since it's executed in a child process, the
// ` legacyLoggingConfigSchema` returns `true` for the TTY check on `process.stdout.isTTY`
if (has(settings, 'logging.json') && settings.env !== 'development') {
log(
'"logging.json" has been deprecated and will be removed ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
message:
'"logging.json" has been deprecated and will be removed ' +
'in 8.0. To specify log message format moving forward, ' +
'you can configure the "appender.layout" property for every custom appender in your logging configuration. ' +
'There is currently no default layout for custom appenders and each one must be declared explicitly. ' +
'For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx.'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx',
});
}
return settings;
};
const logRotateDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const logRotateDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.rotate')) {
log(
'"logging.rotate" and sub-options have been deprecated and will be removed in 8.0. ' +
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender',
message:
'"logging.rotate" and sub-options have been deprecated and will be removed in 8.0. ' +
'Moving forward, you can enable log rotation using the "rolling-file" appender for a logger ' +
'in your logging configuration. For more details, see ' +
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender'
);
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender',
});
}
return settings;
};
const logEventsLogDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const logEventsLogDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.events.log')) {
log(
'"logging.events.log" has been deprecated and will be removed ' +
'in 8.0. Moving forward, log levels can be customized on a per-logger basis using the new logging configuration. '
);
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents',
message:
'"logging.events.log" has been deprecated and will be removed ' +
'in 8.0. Moving forward, log levels can be customized on a per-logger basis using the new logging configuration. ',
});
}
return settings;
};
const logEventsErrorDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const logEventsErrorDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.events.error')) {
log(
'"logging.events.error" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level: error" in your logging configuration. '
);
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents',
message:
'"logging.events.error" has been deprecated and will be removed ' +
'in 8.0. Moving forward, you can use "logging.root.level: error" in your logging configuration. ',
});
}
return settings;
};
const logFilterDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
const logFilterDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (has(settings, 'logging.filter')) {
log('"logging.filter" has been deprecated and will be removed ' + 'in 8.0. ');
addDeprecation({
documentationUrl:
'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingfilter',
message: '"logging.filter" has been deprecated and will be removed in 8.0.',
});
}
return settings;
};

View file

@ -24,7 +24,7 @@ export type {
ConfigPath,
CliArgs,
ConfigDeprecation,
ConfigDeprecationLogger,
AddConfigDeprecation,
ConfigDeprecationProvider,
ConfigDeprecationFactory,
EnvironmentMode,

View file

@ -0,0 +1,248 @@
/*
* 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 { GetDeprecationsContext } from './types';
import { DeprecationsFactory } from './deprecations_factory';
import { loggerMock } from '../logging/logger.mock';
describe('DeprecationsFactory', () => {
const logger = loggerMock.create();
beforeEach(() => {
loggerMock.clear(logger);
});
describe('getRegistry', () => {
const domainId = 'test-plugin';
it('creates a registry for a domainId', async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const registry = deprecationsFactory.getRegistry(domainId);
expect(registry).toHaveProperty('registerDeprecations');
expect(registry).toHaveProperty('getDeprecations');
});
it('creates one registry for a domainId', async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const registry = deprecationsFactory.getRegistry(domainId);
const sameRegistry = deprecationsFactory.getRegistry(domainId);
expect(registry).toStrictEqual(sameRegistry);
});
it('returns a registered registry', () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const mockRegistry = 'mock-reg';
const mockRegistries = {
set: jest.fn(),
get: jest.fn().mockReturnValue(mockRegistry),
};
// @ts-expect-error
deprecationsFactory.registries = mockRegistries;
const result = deprecationsFactory.getRegistry(domainId);
expect(mockRegistries.get).toBeCalledTimes(1);
expect(mockRegistries.get).toBeCalledWith(domainId);
expect(mockRegistries.set).toBeCalledTimes(0);
expect(result).toStrictEqual(mockRegistry);
});
});
describe('getAllDeprecations', () => {
const mockDependencies = ({
esClient: jest.fn(),
savedObjectsClient: jest.fn(),
} as unknown) as GetDeprecationsContext;
it('returns a flattened array of deprecations', async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const mockPluginDeprecationsInfo = [
{
message: 'mockPlugin message',
level: 'critical',
correctiveActions: {
manualSteps: ['mockPlugin step 1', 'mockPlugin step 2'],
},
},
{
message: 'hello there!',
level: 'warning',
correctiveActions: {
manualSteps: ['mockPlugin step a', 'mockPlugin step b'],
},
},
];
const anotherMockPluginDeprecationsInfo = [
{
message: 'anotherMockPlugin message',
level: 'critical',
correctiveActions: {
manualSteps: ['anotherMockPlugin step 1', 'anotherMockPlugin step 2'],
},
},
];
const mockPluginRegistry = deprecationsFactory.getRegistry('mockPlugin');
const anotherMockPluginRegistry = deprecationsFactory.getRegistry('anotherMockPlugin');
mockPluginRegistry.registerDeprecations({
getDeprecations: jest.fn().mockResolvedValue(mockPluginDeprecationsInfo),
});
anotherMockPluginRegistry.registerDeprecations({
getDeprecations: jest.fn().mockResolvedValue(anotherMockPluginDeprecationsInfo),
});
const derpecations = await deprecationsFactory.getAllDeprecations(mockDependencies);
expect(derpecations).toStrictEqual(
[
mockPluginDeprecationsInfo.map((info) => ({ ...info, domainId: 'mockPlugin' })),
anotherMockPluginDeprecationsInfo.map((info) => ({
...info,
domainId: 'anotherMockPlugin',
})),
].flat()
);
});
it(`returns a failure message for failed getDeprecations functions`, async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const domainId = 'mockPlugin';
const mockError = new Error();
const deprecationsRegistry = deprecationsFactory.getRegistry(domainId);
deprecationsRegistry.registerDeprecations({
getDeprecations: jest.fn().mockRejectedValue(mockError),
});
const derpecations = await deprecationsFactory.getAllDeprecations(mockDependencies);
expect(logger.warn).toBeCalledTimes(1);
expect(logger.warn).toBeCalledWith(
`Failed to get deprecations info for plugin "${domainId}".`,
mockError
);
expect(derpecations).toStrictEqual([
{
domainId,
message: `Failed to get deprecations info for plugin "${domainId}".`,
level: 'fetch_error',
correctiveActions: {
manualSteps: ['Check Kibana server logs for error message.'],
},
},
]);
});
it(`returns successful results even when some getDeprecations fail`, async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const mockPluginRegistry = deprecationsFactory.getRegistry('mockPlugin');
const anotherMockPluginRegistry = deprecationsFactory.getRegistry('anotherMockPlugin');
const mockError = new Error();
const mockPluginDeprecationsInfo = [
{
message: 'mockPlugin message',
level: 'critical',
correctiveActions: {
manualSteps: ['mockPlugin step 1', 'mockPlugin step 2'],
},
},
];
mockPluginRegistry.registerDeprecations({
getDeprecations: jest.fn().mockResolvedValue(mockPluginDeprecationsInfo),
});
anotherMockPluginRegistry.registerDeprecations({
getDeprecations: jest.fn().mockRejectedValue(mockError),
});
const derpecations = await deprecationsFactory.getAllDeprecations(mockDependencies);
expect(logger.warn).toBeCalledTimes(1);
expect(logger.warn).toBeCalledWith(
`Failed to get deprecations info for plugin "anotherMockPlugin".`,
mockError
);
expect(derpecations).toStrictEqual([
...mockPluginDeprecationsInfo.map((info) => ({ ...info, domainId: 'mockPlugin' })),
{
domainId: 'anotherMockPlugin',
message: `Failed to get deprecations info for plugin "anotherMockPlugin".`,
level: 'fetch_error',
correctiveActions: {
manualSteps: ['Check Kibana server logs for error message.'],
},
},
]);
});
});
describe('getDeprecations', () => {
const mockDependencies = ({
esClient: jest.fn(),
savedObjectsClient: jest.fn(),
} as unknown) as GetDeprecationsContext;
it('returns a flattened array of DeprecationInfo', async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const deprecationsRegistry = deprecationsFactory.getRegistry('mockPlugin');
const deprecationsBody = [
{
message: 'mockPlugin message',
level: 'critical',
correctiveActions: {
manualSteps: ['mockPlugin step 1', 'mockPlugin step 2'],
},
},
[
{
message: 'hello there!',
level: 'warning',
correctiveActions: {
manualSteps: ['mockPlugin step a', 'mockPlugin step b'],
},
},
],
];
deprecationsRegistry.registerDeprecations({
getDeprecations: jest.fn().mockResolvedValue(deprecationsBody),
});
const derpecations = await deprecationsFactory.getDeprecations(
'mockPlugin',
mockDependencies
);
expect(derpecations).toStrictEqual(
deprecationsBody.flat().map((body) => ({ ...body, domainId: 'mockPlugin' }))
);
});
it('removes empty entries from the returned array', async () => {
const deprecationsFactory = new DeprecationsFactory({ logger });
const deprecationsRegistry = deprecationsFactory.getRegistry('mockPlugin');
const deprecationsBody = [
{
message: 'mockPlugin message',
level: 'critical',
correctiveActions: {
manualSteps: ['mockPlugin step 1', 'mockPlugin step 2'],
},
},
[undefined],
undefined,
];
deprecationsRegistry.registerDeprecations({
getDeprecations: jest.fn().mockResolvedValue(deprecationsBody),
});
const derpecations = await deprecationsFactory.getDeprecations(
'mockPlugin',
mockDependencies
);
expect(derpecations).toHaveLength(1);
expect(derpecations).toStrictEqual([{ ...deprecationsBody[0], domainId: 'mockPlugin' }]);
});
});
});

View file

@ -0,0 +1,108 @@
/*
* 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 { DeprecationsRegistry } from './deprecations_registry';
import type { Logger } from '../logging';
import type {
DomainDeprecationDetails,
DeprecationsDetails,
GetDeprecationsContext,
} from './types';
export interface DeprecationsFactoryDeps {
logger: Logger;
}
export class DeprecationsFactory {
private readonly registries: Map<string, DeprecationsRegistry> = new Map();
private readonly logger: Logger;
constructor({ logger }: DeprecationsFactoryDeps) {
this.logger = logger;
}
public getRegistry = (domainId: string): DeprecationsRegistry => {
const existing = this.registries.get(domainId);
if (existing) {
return existing;
}
const registry = new DeprecationsRegistry();
this.registries.set(domainId, registry);
return registry;
};
public getDeprecations = async (
domainId: string,
dependencies: GetDeprecationsContext
): Promise<DomainDeprecationDetails[]> => {
const infoBody = await this.getDeprecationsBody(domainId, dependencies);
return this.createDeprecationInfo(domainId, infoBody).flat();
};
public getAllDeprecations = async (
dependencies: GetDeprecationsContext
): Promise<DomainDeprecationDetails[]> => {
const domainIds = [...this.registries.keys()];
const deprecationsInfo = await Promise.all(
domainIds.map(async (domainId) => {
const infoBody = await this.getDeprecationsBody(domainId, dependencies);
return this.createDeprecationInfo(domainId, infoBody);
})
);
return deprecationsInfo.flat();
};
private createDeprecationInfo = (
domainId: string,
deprecationInfoBody: DeprecationsDetails[]
): DomainDeprecationDetails[] => {
return deprecationInfoBody
.flat()
.filter(Boolean)
.map((pluginDeprecation) => ({
...pluginDeprecation,
domainId,
}));
};
private getDeprecationsBody = async (
domainId: string,
dependencies: GetDeprecationsContext
): Promise<DeprecationsDetails[]> => {
const deprecationsRegistry = this.registries.get(domainId);
if (!deprecationsRegistry) {
return [];
}
try {
const settledResults = await deprecationsRegistry.getDeprecations(dependencies);
return settledResults.flatMap((settledResult) => {
if (settledResult.status === 'rejected') {
this.logger.warn(
`Failed to get deprecations info for plugin "${domainId}".`,
settledResult.reason
);
return [
{
message: `Failed to get deprecations info for plugin "${domainId}".`,
level: 'fetch_error',
correctiveActions: {
manualSteps: ['Check Kibana server logs for error message.'],
},
},
];
}
return settledResult.value;
});
} catch (err) {
this.logger.warn(`Failed to get deprecations info for plugin "${domainId}".`, err);
return [];
}
};
}

View file

@ -0,0 +1,78 @@
/*
* 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.
*/
/* eslint-disable dot-notation */
import { RegisterDeprecationsConfig, GetDeprecationsContext } from './types';
import { DeprecationsRegistry } from './deprecations_registry';
describe('DeprecationsRegistry', () => {
describe('registerDeprecations', () => {
it('throws if getDeprecations is not a function', async () => {
const deprecationsRegistry = new DeprecationsRegistry();
const deprecationsConfig = ({
getDeprecations: null,
} as unknown) as RegisterDeprecationsConfig;
expect(() => deprecationsRegistry.registerDeprecations(deprecationsConfig)).toThrowError(
/getDeprecations must be a function/
);
});
it('registers deprecation context', () => {
const deprecationsRegistry = new DeprecationsRegistry();
const getDeprecations = jest.fn();
const deprecationsConfig = { getDeprecations };
deprecationsRegistry.registerDeprecations(deprecationsConfig);
expect(deprecationsRegistry['deprecationContexts']).toStrictEqual([deprecationsConfig]);
});
it('allows registering multiple contexts', async () => {
const deprecationsRegistry = new DeprecationsRegistry();
const deprecationsConfigA = { getDeprecations: jest.fn() };
const deprecationsConfigB = { getDeprecations: jest.fn() };
deprecationsRegistry.registerDeprecations(deprecationsConfigA);
deprecationsRegistry.registerDeprecations(deprecationsConfigB);
expect(deprecationsRegistry['deprecationContexts']).toStrictEqual([
deprecationsConfigA,
deprecationsConfigB,
]);
});
});
describe('getDeprecations', () => {
it('returns all settled deprecations', async () => {
const deprecationsRegistry = new DeprecationsRegistry();
const mockContext = ({} as unknown) as GetDeprecationsContext;
const mockError = new Error();
const deprecationsConfigA = { getDeprecations: jest.fn().mockResolvedValue('hi') };
const deprecationsConfigB = { getDeprecations: jest.fn().mockRejectedValue(mockError) };
deprecationsRegistry.registerDeprecations(deprecationsConfigA);
deprecationsRegistry.registerDeprecations(deprecationsConfigB);
const deprecations = await deprecationsRegistry.getDeprecations(mockContext);
expect(deprecations).toStrictEqual([
{
status: 'fulfilled',
value: 'hi',
},
{
status: 'rejected',
reason: mockError,
},
]);
});
it('passes dependencies to registered getDeprecations function', async () => {
const deprecationsRegistry = new DeprecationsRegistry();
const mockContext = ({} as unknown) as GetDeprecationsContext;
const deprecationsConfig = { getDeprecations: jest.fn().mockResolvedValue('hi') };
deprecationsRegistry.registerDeprecations(deprecationsConfig);
const deprecations = await deprecationsRegistry.getDeprecations(mockContext);
expect(deprecations).toHaveLength(1);
expect(deprecationsConfig.getDeprecations).toBeCalledWith(mockContext);
});
});
});

View file

@ -0,0 +1,31 @@
/*
* 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 { DeprecationsDetails, RegisterDeprecationsConfig, GetDeprecationsContext } from './types';
export class DeprecationsRegistry {
private readonly deprecationContexts: RegisterDeprecationsConfig[] = [];
public registerDeprecations = (deprecationContext: RegisterDeprecationsConfig) => {
if (typeof deprecationContext.getDeprecations !== 'function') {
throw new Error(`getDeprecations must be a function in registerDeprecations(context)`);
}
this.deprecationContexts.push(deprecationContext);
};
public getDeprecations = async (
dependencies: GetDeprecationsContext
): Promise<Array<PromiseSettledResult<DeprecationsDetails[]>>> => {
return await Promise.allSettled(
this.deprecationContexts.map(
async (deprecationContext) => await deprecationContext.getDeprecations(dependencies)
)
);
};
}

View file

@ -0,0 +1,49 @@
/*
* 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 type { PublicMethodsOf } from '@kbn/utility-types';
import {
DeprecationsService,
InternalDeprecationsServiceSetup,
DeprecationsServiceSetup,
} from './deprecations_service';
type DeprecationsServiceContract = PublicMethodsOf<DeprecationsService>;
const createSetupContractMock = () => {
const setupContract: jest.Mocked<DeprecationsServiceSetup> = {
registerDeprecations: jest.fn(),
};
return setupContract;
};
const createInternalSetupContractMock = () => {
const internalSetupContract: jest.Mocked<InternalDeprecationsServiceSetup> = {
getRegistry: jest.fn(),
};
internalSetupContract.getRegistry.mockReturnValue(createSetupContractMock());
return internalSetupContract;
};
const createDeprecationsServiceMock = () => {
const mocked: jest.Mocked<DeprecationsServiceContract> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
};
mocked.setup.mockReturnValue(createInternalSetupContractMock());
return mocked;
};
export const deprecationsServiceMock = {
create: createDeprecationsServiceMock,
createInternalSetupContract: createInternalSetupContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -0,0 +1,168 @@
/*
* 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 { DeprecationsFactory } from './deprecations_factory';
import { RegisterDeprecationsConfig } from './types';
import { registerRoutes } from './routes';
import { CoreContext } from '../core_context';
import { CoreUsageDataSetup } from '../core_usage_data';
import { InternalElasticsearchServiceSetup } from '../elasticsearch';
import { CoreService } from '../../types';
import { InternalHttpServiceSetup } from '../http';
import { Logger } from '../logging';
/**
* The deprecations service provides a way for the Kibana platform to communicate deprecated
* features and configs with its users. These deprecations are only communicated
* if the deployment is using these features. Allowing for a user tailored experience
* for upgrading the stack version.
*
* The Deprecation service is consumed by the upgrade assistant to assist with the upgrade
* experience.
*
* If a deprecated feature can be resolved without manual user intervention.
* Using correctiveActions.api allows the Upgrade Assistant to use this api to correct the
* deprecation upon a user trigger.
*
* @example
* ```ts
* import { DeprecationsDetails, GetDeprecationsContext, CoreSetup } from 'src/core/server';
*
* async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecationsContext): Promise<DeprecationsDetails[]> {
* const deprecations: DeprecationsDetails[] = [];
* const count = await getTimelionSheetsCount(savedObjectsClient);
*
* if (count > 0) {
* // Example of a manual correctiveAction
* deprecations.push({
* message: `You have ${count} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`,
* documentationUrl:
* 'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html',
* level: 'warning',
* correctiveActions: {
* manualSteps: [
* 'Navigate to the Kibana Dashboard and click "Create dashboard".',
* 'Select Timelion from the "New Visualization" window.',
* 'Open a new tab, open the Timelion app, select the chart you want to copy, then copy the chart expression.',
* 'Go to Timelion, paste the chart expression in the Timelion expression field, then click Update.',
* 'In the toolbar, click Save.',
* 'On the Save visualization window, enter the visualization Title, then click Save and return.',
* ],
* },
* });
* }
*
* // Example of an api correctiveAction
* deprecations.push({
* "message": "User 'test_dashboard_user' is using a deprecated role: 'kibana_user'",
* "documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-user.html",
* "level": "critical",
* "correctiveActions": {
* "api": {
* "path": "/internal/security/users/test_dashboard_user",
* "method": "POST",
* "body": {
* "username": "test_dashboard_user",
* "roles": [
* "machine_learning_user",
* "enrich_user",
* "kibana_admin"
* ],
* "full_name": "Alison Goryachev",
* "email": "alisongoryachev@gmail.com",
* "metadata": {},
* "enabled": true
* }
* },
* "manualSteps": [
* "Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.",
* "Using Kibana role-mapping management, change all role-mappings which assing the kibana_user role to the kibana_admin role."
* ]
* },
* });
*
* return deprecations;
* }
*
*
* export class Plugin() {
* setup: (core: CoreSetup) => {
* core.deprecations.registerDeprecations({ getDeprecations });
* }
* }
* ```
*
* @public
*/
export interface DeprecationsServiceSetup {
registerDeprecations: (deprecationContext: RegisterDeprecationsConfig) => void;
}
/** @internal */
export interface InternalDeprecationsServiceSetup {
getRegistry: (domainId: string) => DeprecationsServiceSetup;
}
/** @internal */
export interface DeprecationsSetupDeps {
http: InternalHttpServiceSetup;
elasticsearch: InternalElasticsearchServiceSetup;
coreUsageData: CoreUsageDataSetup;
}
/** @internal */
export class DeprecationsService implements CoreService<InternalDeprecationsServiceSetup> {
private readonly logger: Logger;
constructor(private readonly coreContext: CoreContext) {
this.logger = coreContext.logger.get('deprecations-service');
}
public setup({ http }: DeprecationsSetupDeps): InternalDeprecationsServiceSetup {
this.logger.debug('Setting up Deprecations service');
const deprecationsFactory = new DeprecationsFactory({
logger: this.logger,
});
registerRoutes({ http, deprecationsFactory });
this.registerConfigDeprecationsInfo(deprecationsFactory);
return {
getRegistry: (domainId: string): DeprecationsServiceSetup => {
const registry = deprecationsFactory.getRegistry(domainId);
return {
registerDeprecations: registry.registerDeprecations,
};
},
};
}
public start() {}
public stop() {}
private registerConfigDeprecationsInfo(deprecationsFactory: DeprecationsFactory) {
const handledDeprecatedConfigs = this.coreContext.configService.getHandledDeprecatedConfigs();
for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) {
const deprecationsRegistry = deprecationsFactory.getRegistry(domainId);
deprecationsRegistry.registerDeprecations({
getDeprecations: () => {
return deprecationsContexts.map(({ message, correctiveActions, documentationUrl }) => {
return {
level: 'critical',
message,
correctiveActions: correctiveActions ?? {},
documentationUrl,
};
});
},
});
}
}
}

View file

@ -0,0 +1,21 @@
/*
* 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.
*/
export type {
DeprecationsDetails,
GetDeprecationsContext,
RegisterDeprecationsConfig,
DeprecationsGetResponse,
} from './types';
export type {
DeprecationsServiceSetup,
InternalDeprecationsServiceSetup,
} from './deprecations_service';
export { DeprecationsService } from './deprecations_service';

View file

@ -0,0 +1,35 @@
/*
* 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 { IRouter } from '../../http';
import { GetDeprecationsContext, DeprecationsGetResponse } from '../types';
import { DeprecationsFactory } from '../deprecations_factory';
interface RouteDependencies {
deprecationsFactory: DeprecationsFactory;
}
export const registerGetRoute = (router: IRouter, { deprecationsFactory }: RouteDependencies) => {
router.get(
{
path: '/',
validate: false,
},
async (context, req, res) => {
const dependencies: GetDeprecationsContext = {
esClient: context.core.elasticsearch.client,
savedObjectsClient: context.core.savedObjects.client,
};
const body: DeprecationsGetResponse = {
deprecations: await deprecationsFactory.getAllDeprecations(dependencies),
};
return res.ok({ body });
}
);
};

View file

@ -0,0 +1,22 @@
/*
* 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 { InternalHttpServiceSetup } from '../../http';
import { registerGetRoute } from './get';
import { DeprecationsFactory } from '../deprecations_factory';
export function registerRoutes({
http,
deprecationsFactory,
}: {
http: InternalHttpServiceSetup;
deprecationsFactory: DeprecationsFactory;
}) {
const router = http.createRouter('/api/deprecations');
registerGetRoute(router, { deprecationsFactory });
}

View file

@ -0,0 +1,67 @@
/*
* 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 type { SavedObjectsClientContract } from '../saved_objects/types';
import type { IScopedClusterClient } from '../elasticsearch';
type MaybePromise<T> = T | Promise<T>;
export interface DomainDeprecationDetails extends DeprecationsDetails {
domainId: string;
}
export interface DeprecationsDetails {
/* The message to be displayed for the deprecation. */
message: string;
/**
* levels:
* - warning: will not break deployment upon upgrade
* - critical: needs to be addressed before upgrade.
* - fetch_error: Deprecations service failed to grab the deprecation details for the domain.
*/
level: 'warning' | 'critical' | 'fetch_error';
/* (optional) link to the documentation for more details on the deprecation. */
documentationUrl?: string;
/* corrective action needed to fix this deprecation. */
correctiveActions: {
/**
* (optional) The api to be called to automatically fix the deprecation
* Each domain should implement a POST/PUT route for their plugin to
* handle their deprecations.
*/
api?: {
/* Kibana route path. Passing a query string is allowed */
path: string;
/* Kibana route method: 'POST' or 'PUT'. */
method: 'POST' | 'PUT';
/* Additional details to be passed to the route. */
body?: {
[key: string]: any;
};
};
/**
* (optional) If this deprecation cannot be automtically fixed
* via an API corrective action. Specify a list of manual steps
* users need to follow to fix the deprecation before upgrade.
*/
manualSteps?: string[];
};
}
export interface RegisterDeprecationsConfig {
getDeprecations: (context: GetDeprecationsContext) => MaybePromise<DeprecationsDetails[]>;
}
export interface GetDeprecationsContext {
esClient: IScopedClusterClient;
savedObjectsClient: SavedObjectsClientContract;
}
export interface DeprecationsGetResponse {
deprecations: DomainDeprecationDetails[];
}

View file

@ -28,7 +28,7 @@ const applyElasticsearchDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: CONFIG_PATH,
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,

View file

@ -111,32 +111,32 @@ export const configSchema = schema.object({
});
const deprecations: ConfigDeprecationProvider = () => [
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
const es = settings[fromPath];
if (!es) {
return settings;
}
if (es.username === 'elastic') {
log(
`Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`
);
addDeprecation({
message: `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`,
});
} else if (es.username === 'kibana') {
log(
`Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`
);
addDeprecation({
message: `Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`,
});
}
if (es.ssl?.key !== undefined && es.ssl?.certificate === undefined) {
log(
`Setting [${fromPath}.ssl.key] without [${fromPath}.ssl.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
addDeprecation({
message: `Setting [${fromPath}.ssl.key] without [${fromPath}.ssl.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`,
});
} else if (es.ssl?.certificate !== undefined && es.ssl?.key === undefined) {
log(
`Setting [${fromPath}.ssl.certificate] without [${fromPath}.ssl.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
addDeprecation({
message: `Setting [${fromPath}.ssl.certificate] without [${fromPath}.ssl.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`,
});
} else if (es.logQueries === true) {
log(
`Setting [${fromPath}.logQueries] is deprecated and no longer used. You should set the log level to "debug" for the "elasticsearch.queries" context in "logging.loggers" or use "logging.verbose: true".`
);
addDeprecation({
message: `Setting [${fromPath}.logQueries] is deprecated and no longer used. You should set the log level to "debug" for the "elasticsearch.queries" context in "logging.loggers" or use "logging.verbose: true".`,
});
}
return settings;
},

View file

@ -57,7 +57,7 @@ import { StatusServiceSetup } from './status';
import { AppenderConfigType, appendersSchema, LoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { I18nServiceSetup } from './i18n';
import { DeprecationsServiceSetup } from './deprecations';
// Because of #79265 we need to explicity import, then export these types for
// scripts/telemetry_check.js to work as expected
import {
@ -88,8 +88,8 @@ export type {
ConfigService,
ConfigDeprecation,
ConfigDeprecationProvider,
ConfigDeprecationLogger,
ConfigDeprecationFactory,
AddConfigDeprecation,
EnvironmentMode,
PackageInfo,
} from './config';
@ -381,6 +381,12 @@ export type {
} from './metrics';
export type { I18nServiceSetup } from './i18n';
export type {
DeprecationsDetails,
RegisterDeprecationsConfig,
GetDeprecationsContext,
DeprecationsServiceSetup,
} from './deprecations';
export type { AppCategory } from '../types';
export { DEFAULT_APP_CATEGORIES } from '../utils';
@ -481,6 +487,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
status: StatusServiceSetup;
/** {@link UiSettingsServiceSetup} */
uiSettings: UiSettingsServiceSetup;
/** {@link DeprecationsServiceSetup} */
deprecations: DeprecationsServiceSetup;
/** {@link StartServicesAccessor} */
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
}

View file

@ -29,6 +29,7 @@ import { InternalStatusServiceSetup } from './status';
import { InternalLoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { I18nServiceSetup } from './i18n';
import { InternalDeprecationsServiceSetup } from './deprecations';
/** @internal */
export interface InternalCoreSetup {
@ -45,6 +46,7 @@ export interface InternalCoreSetup {
httpResources: InternalHttpResourcesSetup;
logging: InternalLoggingServiceSetup;
metrics: InternalMetricsServiceSetup;
deprecations: InternalDeprecationsServiceSetup;
}
/**

View file

@ -22,7 +22,7 @@ const applyKibanaDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: CONFIG_PATH,
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,

View file

@ -12,12 +12,13 @@ import { ConfigDeprecationProvider } from '@kbn/config';
export type KibanaConfigType = TypeOf<typeof config.schema>;
const deprecations: ConfigDeprecationProvider = () => [
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
const kibana = settings[fromPath];
if (kibana?.index) {
log(
`"kibana.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`
);
addDeprecation({
message: `"kibana.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`,
documentationUrl: 'https://ela.st/kbn-remove-legacy-multitenancy',
});
}
return settings;
},

View file

@ -32,6 +32,7 @@ import { statusServiceMock } from '../status/status_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { metricsServiceMock } from '../metrics/metrics_service.mock';
import { i18nServiceMock } from '../i18n/i18n_service.mock';
import { deprecationsServiceMock } from '../deprecations/deprecations_service.mock';
const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
@ -80,6 +81,7 @@ beforeEach(() => {
status: statusServiceMock.createInternalSetupContract(),
logging: loggingServiceMock.createInternalSetupContract(),
metrics: metricsServiceMock.createInternalSetupContract(),
deprecations: deprecationsServiceMock.createInternalSetupContract(),
},
plugins: { 'plugin-id': 'plugin-value' },
uiPlugins: {

View file

@ -257,6 +257,11 @@ export class LegacyService implements CoreService {
uiSettings: {
register: setupDeps.core.uiSettings.register,
},
deprecations: {
registerDeprecations: () => {
throw new Error('core.setup.deprecations.registerDeprecations is unsupported in legacy');
},
},
getStartServices: () => Promise.resolve([coreStart, startDeps.plugins, {}]),
};

View file

@ -29,6 +29,7 @@ import { environmentServiceMock } from './environment/environment_service.mock';
import { statusServiceMock } from './status/status_service.mock';
import { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_service.mock';
import { i18nServiceMock } from './i18n/i18n_service.mock';
import { deprecationsServiceMock } from './deprecations/deprecations_service.mock';
export { configServiceMock } from './config/mocks';
export { httpServerMock } from './http/http_server.mocks';
@ -49,6 +50,7 @@ export { contextServiceMock } from './context/context_service.mock';
export { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
export { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_service.mock';
export { i18nServiceMock } from './i18n/i18n_service.mock';
export { deprecationsServiceMock } from './deprecations/deprecations_service.mock';
export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
@ -137,6 +139,7 @@ function createCoreSetupMock({
uiSettings: uiSettingsMock,
logging: loggingServiceMock.createSetupContract(),
metrics: metricsServiceMock.createSetupContract(),
deprecations: deprecationsServiceMock.createSetupContract(),
getStartServices: jest
.fn<Promise<[ReturnType<typeof createCoreStartMock>, object, any]>, []>()
.mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]),
@ -174,6 +177,7 @@ function createInternalCoreSetupMock() {
uiSettings: uiSettingsServiceMock.createSetupContract(),
logging: loggingServiceMock.createInternalSetupContract(),
metrics: metricsServiceMock.createInternalSetupContract(),
deprecations: deprecationsServiceMock.createInternalSetupContract(),
};
return setupDeps;
}

View file

@ -165,6 +165,7 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
register: deps.uiSettings.register,
},
getStartServices: () => plugin.startDependencies,
deprecations: deps.deprecations.getRegistry(plugin.name),
};
}

View file

@ -4,6 +4,7 @@
```ts
import { AddConfigDeprecation } from '@kbn/config';
import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
import Boom from '@hapi/boom';
import { BulkIndexDocumentsParams } from 'elasticsearch';
@ -35,7 +36,6 @@ import { ClusterStateParams } from 'elasticsearch';
import { ClusterStatsParams } from 'elasticsearch';
import { ConfigDeprecation } from '@kbn/config';
import { ConfigDeprecationFactory } from '@kbn/config';
import { ConfigDeprecationLogger } from '@kbn/config';
import { ConfigDeprecationProvider } from '@kbn/config';
import { ConfigOptions } from 'elasticsearch';
import { ConfigPath } from '@kbn/config';
@ -169,6 +169,8 @@ import { UpdateDocumentByQueryParams } from 'elasticsearch';
import { UpdateDocumentParams } from 'elasticsearch';
import { URL } from 'url';
export { AddConfigDeprecation }
// @public
export interface AppCategory {
ariaLabel?: string;
@ -374,8 +376,6 @@ export { ConfigDeprecation }
export { ConfigDeprecationFactory }
export { ConfigDeprecationLogger }
export { ConfigDeprecationProvider }
export { ConfigPath }
@ -491,6 +491,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
// (undocumented)
context: ContextSetup;
// (undocumented)
deprecations: DeprecationsServiceSetup;
// (undocumented)
elasticsearch: ElasticsearchServiceSetup;
// (undocumented)
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
@ -830,12 +832,40 @@ export interface DeprecationInfo {
url: string;
}
// Warning: (ae-missing-release-tag) "DeprecationsDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface DeprecationsDetails {
// (undocumented)
correctiveActions: {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
};
manualSteps?: string[];
};
// (undocumented)
documentationUrl?: string;
level: 'warning' | 'critical' | 'fetch_error';
// (undocumented)
message: string;
}
// @public
export interface DeprecationSettings {
docLinksKey: string;
message: string;
}
// @public
export interface DeprecationsServiceSetup {
// (undocumented)
registerDeprecations: (deprecationContext: RegisterDeprecationsConfig) => void;
}
// @public
export type DestructiveRouteMethod = 'post' | 'put' | 'delete' | 'patch';
@ -939,6 +969,16 @@ export type GetAuthState = <T = unknown>(request: KibanaRequest | LegacyRequest)
state: T;
};
// Warning: (ae-missing-release-tag) "GetDeprecationsContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface GetDeprecationsContext {
// (undocumented)
esClient: IScopedClusterClient;
// (undocumented)
savedObjectsClient: SavedObjectsClientContract;
}
// @public (undocumented)
export interface GetResponse<T> {
// (undocumented)
@ -1912,6 +1952,16 @@ export type RedirectResponseOptions = HttpResponseOptions & {
};
};
// Warning: (ae-missing-release-tag) "RegisterDeprecationsConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface RegisterDeprecationsConfig {
// Warning: (ae-forgotten-export) The symbol "MaybePromise" needs to be exported by the entry point index.d.ts
//
// (undocumented)
getDeprecations: (context: GetDeprecationsContext) => MaybePromise<DeprecationsDetails[]>;
}
// @public
export type RequestHandler<P = unknown, Q = unknown, B = unknown, Context extends RequestHandlerContext = RequestHandlerContext, Method extends RouteMethod = any, ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory> = (context: Context, request: KibanaRequest<P, Q, B, Method>, response: ResponseFactory) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>;

View file

@ -41,6 +41,7 @@ import { ContextService } from './context';
import { RequestHandlerContext } from '.';
import { InternalCoreSetup, InternalCoreStart, ServiceConfigDescriptor } from './internal_types';
import { CoreUsageDataService } from './core_usage_data';
import { DeprecationsService } from './deprecations';
import { CoreRouteHandlerContext } from './core_route_handler_context';
import { config as externalUrlConfig } from './external_url';
@ -67,6 +68,7 @@ export class Server {
private readonly coreApp: CoreApp;
private readonly coreUsageData: CoreUsageDataService;
private readonly i18n: I18nService;
private readonly deprecations: DeprecationsService;
private readonly savedObjectsStartPromise: Promise<SavedObjectsServiceStart>;
private resolveSavedObjectsStartPromise?: (value: SavedObjectsServiceStart) => void;
@ -102,6 +104,7 @@ export class Server {
this.logging = new LoggingService(core);
this.coreUsageData = new CoreUsageDataService(core);
this.i18n = new I18nService(core);
this.deprecations = new DeprecationsService(core);
this.savedObjectsStartPromise = new Promise((resolve) => {
this.resolveSavedObjectsStartPromise = resolve;
@ -192,6 +195,12 @@ export class Server {
loggingSystem: this.loggingSystem,
});
const deprecationsSetup = this.deprecations.setup({
http: httpSetup,
elasticsearch: elasticsearchServiceSetup,
coreUsageData: coreUsageDataSetup,
});
const coreSetup: InternalCoreSetup = {
capabilities: capabilitiesSetup,
context: contextServiceSetup,
@ -206,6 +215,7 @@ export class Server {
httpResources: httpResourcesSetup,
logging: loggingSetup,
metrics: metricsSetup,
deprecations: deprecationsSetup,
};
const pluginsSetup = await this.plugins.setup(coreSetup);
@ -285,6 +295,7 @@ export class Server {
await this.metrics.stop();
await this.status.stop();
await this.logging.stop();
this.deprecations.stop();
}
private registerCoreContext(coreSetup: InternalCoreSetup) {

View file

@ -37,6 +37,7 @@ export type {
SavedObjectsClientContract,
SavedObjectsNamespaceType,
} from './saved_objects/types';
export type { DomainDeprecationDetails, DeprecationsGetResponse } from './deprecations/types';
export * from './ui_settings/types';
export * from './legacy/types';
export type { EnvironmentMode, PackageInfo } from '@kbn/config';

View file

@ -6,12 +6,7 @@
* Side Public License, v 1.
*/
import {
ConfigDeprecationLogger,
CoreSetup,
CoreStart,
PluginConfigDescriptor,
} from 'kibana/server';
import { AddConfigDeprecation, CoreSetup, CoreStart, PluginConfigDescriptor } from 'kibana/server';
import { get } from 'lodash';
import { configSchema, ConfigSchema } from '../config';
@ -23,17 +18,28 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
// TODO: Remove deprecation once defaultAppId is deleted
renameFromRoot('kibana.defaultAppId', 'kibana_legacy.defaultAppId', true),
(completeConfig: Record<string, any>, rootPath: string, log: ConfigDeprecationLogger) => {
renameFromRoot('kibana.defaultAppId', 'kibana_legacy.defaultAppId', { silent: true }),
(
completeConfig: Record<string, any>,
rootPath: string,
addDeprecation: AddConfigDeprecation
) => {
if (
get(completeConfig, 'kibana.defaultAppId') === undefined &&
get(completeConfig, 'kibana_legacy.defaultAppId') === undefined
) {
return completeConfig;
}
log(
`kibana.defaultAppId is deprecated and will be removed in 8.0. Please use the \`defaultRoute\` advanced setting instead`
);
addDeprecation({
message: `kibana.defaultAppId is deprecated and will be removed in 8.0. Please use the \`defaultRoute\` advanced setting instead`,
correctiveActions: {
manualSteps: [
'Go to Stack Management > Advanced Settings',
'Update the "defaultRoute" setting under the General section',
'Remove "kibana.defaultAppId" from the kibana.yml config file',
],
},
});
return completeConfig;
},
],

View file

@ -0,0 +1,70 @@
/*
* 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 {
CoreStart,
SavedObjectsClient,
Logger,
GetDeprecationsContext,
DeprecationsDetails,
} from 'src/core/server';
export const getTimelionSheetsCount = async (
savedObjectsClient: Pick<SavedObjectsClient, 'find'>
) => {
const { total } = await savedObjectsClient.find({ type: 'timelion-sheet', perPage: 1 });
return total;
};
export const showWarningMessageIfTimelionSheetWasFound = async (
core: CoreStart,
logger: Logger
) => {
const { savedObjects } = core;
const savedObjectsClient = savedObjects.createInternalRepository();
const count = await getTimelionSheetsCount(savedObjectsClient);
if (count > 0) {
logger.warn(
'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.'
);
}
};
/**
* Deprecated since 7.0, the Timelion app will be removed in 8.0.
* To continue using your Timelion worksheets, migrate them to a dashboard.
*
* @link https://www.elastic.co/guide/en/kibana/master/timelion.html#timelion-deprecation
**/
export async function getDeprecations({
savedObjectsClient,
}: GetDeprecationsContext): Promise<DeprecationsDetails[]> {
const deprecations: DeprecationsDetails[] = [];
const count = await getTimelionSheetsCount(savedObjectsClient);
if (count > 0) {
deprecations.push({
message: `You have ${count} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`,
documentationUrl:
'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html',
level: 'warning',
correctiveActions: {
manualSteps: [
'Navigate to the Kibana Dashboard and click "Create dashboard".',
'Select Timelion from the "New Visualization" window.',
'Open a new tab, open the Timelion app, select the chart you want to copy, then copy the chart expression.',
'Go to Timelion, paste the chart expression in the Timelion expression field, then click Update.',
'In the toolbar, click Save.',
'On the Save visualization window, enter the visualization Title, then click Save and return.',
],
},
});
}
return deprecations;
}

View file

@ -11,30 +11,7 @@ import { i18n } from '@kbn/i18n';
import { schema } from '@kbn/config-schema';
import { TimelionConfigType } from './config';
import { timelionSheetSavedObjectType } from './saved_objects';
/**
* Deprecated since 7.0, the Timelion app will be removed in 8.0.
* To continue using your Timelion worksheets, migrate them to a dashboard.
*
* @link https://www.elastic.co/guide/en/kibana/master/timelion.html#timelion-deprecation
**/
const showWarningMessageIfTimelionSheetWasFound = (core: CoreStart, logger: Logger) => {
const { savedObjects } = core;
const savedObjectsClient = savedObjects.createInternalRepository();
savedObjectsClient
.find({
type: 'timelion-sheet',
perPage: 1,
})
.then(
({ total }) =>
total &&
logger.warn(
'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.'
)
);
};
import { getDeprecations, showWarningMessageIfTimelionSheetWasFound } from './deprecations';
export class TimelionPlugin implements Plugin {
private logger: Logger;
@ -87,6 +64,8 @@ export class TimelionPlugin implements Plugin {
schema: schema.number(),
},
});
core.deprecations.registerDeprecations({ getDeprecations });
}
start(core: CoreStart) {
showWarningMessageIfTimelionSheetWasFound(core, this.logger);

View file

@ -21,7 +21,7 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled'),
renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled'),
renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls'),
renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled', true),
renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled', { silent: true }),
],
};
export const plugin = (initializerContext: PluginInitializerContext) =>

View file

@ -15,9 +15,13 @@ export { VisTypeTimeseriesSetup } from './plugin';
export const config: PluginConfigDescriptor<VisTypeTimeseriesConfig> = {
deprecations: ({ unused, renameFromRoot }) => [
// In Kibana v7.8 plugin id was renamed from 'metrics' to 'vis_type_timeseries':
renameFromRoot('metrics.enabled', 'vis_type_timeseries.enabled', true),
renameFromRoot('metrics.chartResolution', 'vis_type_timeseries.chartResolution', true),
renameFromRoot('metrics.minimumBucketSize', 'vis_type_timeseries.minimumBucketSize', true),
renameFromRoot('metrics.enabled', 'vis_type_timeseries.enabled', { silent: true }),
renameFromRoot('metrics.chartResolution', 'vis_type_timeseries.chartResolution', {
silent: true,
}),
renameFromRoot('metrics.minimumBucketSize', 'vis_type_timeseries.minimumBucketSize', {
silent: true,
}),
// Unused properties which should be removed after releasing Kibana v8.0:
unused('chartResolution'),

View file

@ -0,0 +1,14 @@
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "test-deprecations-plugin:ff3733a0-9fty-11e7-ahb3-3dcb94193fab",
"source": {
"type": "test-deprecations-plugin",
"updated_at": "2021-02-11T18:51:23.794Z",
"test-deprecations-plugin": {
"title": "Test saved object"
}
}
}
}

View file

@ -0,0 +1,289 @@
{
"type": "index",
"value": {
"index": ".kibana",
"mappings": {
"properties": {
"config": {
"dynamic": "true",
"properties": {
"buildNum": {
"type": "keyword"
},
"dateFormat:tz": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
}
}
},
"dashboard": {
"dynamic": "strict",
"properties": {
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"optionsJSON": {
"type": "text"
},
"panelsJSON": {
"type": "text"
},
"refreshInterval": {
"properties": {
"display": {
"type": "keyword"
},
"pause": {
"type": "boolean"
},
"section": {
"type": "integer"
},
"value": {
"type": "integer"
}
}
},
"timeFrom": {
"type": "keyword"
},
"timeRestore": {
"type": "boolean"
},
"timeTo": {
"type": "keyword"
},
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"index-pattern": {
"dynamic": "strict",
"properties": {
"fieldFormatMap": {
"type": "text"
},
"fields": {
"type": "text"
},
"intervalName": {
"type": "keyword"
},
"notExpandable": {
"type": "boolean"
},
"sourceFilters": {
"type": "text"
},
"timeFieldName": {
"type": "keyword"
},
"title": {
"type": "text"
}
}
},
"search": {
"dynamic": "strict",
"properties": {
"columns": {
"type": "keyword"
},
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"sort": {
"type": "keyword"
},
"title": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"server": {
"dynamic": "strict",
"properties": {
"uuid": {
"type": "keyword"
}
}
},
"timelion-sheet": {
"dynamic": "strict",
"properties": {
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"timelion_chart_height": {
"type": "integer"
},
"timelion_columns": {
"type": "integer"
},
"timelion_interval": {
"type": "keyword"
},
"timelion_other_interval": {
"type": "keyword"
},
"timelion_rows": {
"type": "integer"
},
"timelion_sheet": {
"type": "text"
},
"title": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"type": {
"type": "keyword"
},
"url": {
"dynamic": "strict",
"properties": {
"accessCount": {
"type": "long"
},
"accessDate": {
"type": "date"
},
"createDate": {
"type": "date"
},
"url": {
"fields": {
"keyword": {
"ignore_above": 2048,
"type": "keyword"
}
},
"type": "text"
}
}
},
"visualization": {
"dynamic": "strict",
"properties": {
"description": {
"type": "text"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"savedSearchId": {
"type": "keyword"
},
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
},
"visState": {
"type": "text"
}
}
},
"query": {
"properties": {
"title": {
"type": "text"
},
"description": {
"type": "text"
},
"query": {
"properties": {
"language": {
"type": "keyword"
},
"query": {
"type": "keyword",
"index": false
}
}
},
"filters": {
"type": "object",
"enabled": false
},
"timefilter": {
"type": "object",
"enabled": false
}
}
},
"test-deprecations-plugin": {
"properties": {
"title": {
"type": "text"
}
}
}
}
},
"settings": {
"index": {
"number_of_replicas": "0",
"number_of_shards": "1"
}
}
}
}

View file

@ -56,6 +56,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
// Required to load new platform plugins via `--plugin-path` flag.
'--env.name=development',
'--corePluginDeprecations.oldProperty=hello',
'--corePluginDeprecations.secret=100',
'--corePluginDeprecations.noLongerUsed=still_using',
...plugins.map(
(pluginDir) => `--plugin-path=${path.resolve(__dirname, 'plugins', pluginDir)}`
),

View file

@ -0,0 +1,8 @@
{
"id": "corePluginDeprecations",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["corePluginDeprecations"],
"server": true,
"ui": false
}

View file

@ -0,0 +1,14 @@
{
"name": "core_plugin_deprecations",
"version": "1.0.0",
"main": "target/test/plugin_functional/plugins/core_plugin_deprecations",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
},
"license": "SSPL-1.0 OR Elastic License 2.0",
"scripts": {
"kbn": "node ../../../../scripts/kbn.js",
"build": "rm -rf './target' && ../../../../node_modules/.bin/tsc"
}
}

View file

@ -0,0 +1,19 @@
/*
* 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 React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters } from 'kibana/public';
const DeprecationsApp = () => <div>Deprcations App</div>;
export const renderApp = ({ element }: AppMountParameters) => {
ReactDOM.render(<DeprecationsApp />, element);
return () => ReactDOM.unmountComponentAtNode(element);
};

View file

@ -0,0 +1,19 @@
/*
* 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 { PluginInitializer, PluginInitializerContext } from 'kibana/public';
import {
CorePluginDeprecationsPlugin,
CorePluginDeprecationsPluginSetup,
CorePluginDeprecationsPluginStart,
} from './plugin';
export const plugin: PluginInitializer<
CorePluginDeprecationsPluginSetup,
CorePluginDeprecationsPluginStart
> = (context: PluginInitializerContext) => new CorePluginDeprecationsPlugin(context);

View 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 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 { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/public';
declare global {
interface Window {
env?: PluginInitializerContext['env'];
}
}
export class CorePluginDeprecationsPlugin
implements Plugin<CorePluginDeprecationsPluginSetup, CorePluginDeprecationsPluginStart> {
constructor(pluginContext: PluginInitializerContext) {
window.env = pluginContext.env;
}
public setup(core: CoreSetup) {
core.application.register({
id: 'core-plugin-deprecations',
title: 'Core Plugin Deprecations',
async mount(params) {
const { renderApp } = await import('./application');
await core.getStartServices();
return renderApp(params);
},
});
}
public start() {}
public stop() {}
}
export type CorePluginDeprecationsPluginSetup = ReturnType<CorePluginDeprecationsPlugin['setup']>;
export type CorePluginDeprecationsPluginStart = ReturnType<CorePluginDeprecationsPlugin['start']>;

View file

@ -0,0 +1,41 @@
/*
* 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 { schema, TypeOf } from '@kbn/config-schema';
import { get } from 'lodash';
import type { PluginConfigDescriptor } from 'kibana/server';
import type { ConfigDeprecation } from '@kbn/config';
const configSchema = schema.object({
newProperty: schema.maybe(schema.string({ defaultValue: 'Some string' })),
noLongerUsed: schema.maybe(schema.string()),
secret: schema.maybe(schema.number({ defaultValue: 42 })),
});
type ConfigType = TypeOf<typeof configSchema>;
const configSecretDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
if (get(settings, 'corePluginDeprecations.secret') !== 42) {
addDeprecation({
documentationUrl: 'config-secret-doc-url',
message:
'Kibana plugin funcitonal tests will no longer allow corePluginDeprecations.secret ' +
'config to be set to anything except 42.',
});
}
return settings;
};
export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
deprecations: ({ rename, unused }) => [
rename('oldProperty', 'newProperty'),
unused('noLongerUsed'),
configSecretDeprecation,
],
};

View 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 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 { CorePluginDeprecationsPlugin } from './plugin';
export { config } from './config';
export const plugin = () => new CorePluginDeprecationsPlugin();

View file

@ -0,0 +1,57 @@
/*
* 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 type { Plugin, CoreSetup, GetDeprecationsContext, DeprecationsDetails } from 'kibana/server';
import { registerRoutes } from './routes';
async function getDeprecations({
savedObjectsClient,
}: GetDeprecationsContext): Promise<DeprecationsDetails[]> {
const deprecations: DeprecationsDetails[] = [];
const { total } = await savedObjectsClient.find({ type: 'test-deprecations-plugin', perPage: 1 });
deprecations.push({
message: `CorePluginDeprecationsPlugin is a deprecated feature for testing.`,
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
manualSteps: ['Step a', 'Step b'],
},
});
if (total > 0) {
deprecations.push({
message: `SavedObject test-deprecations-plugin is still being used.`,
documentationUrl: 'another-test-url',
level: 'critical',
correctiveActions: {},
});
}
return deprecations;
}
export class CorePluginDeprecationsPlugin implements Plugin {
public setup(core: CoreSetup, deps: {}) {
registerRoutes(core.http);
core.savedObjects.registerType({
name: 'test-deprecations-plugin',
hidden: false,
namespaceType: 'single',
mappings: {
properties: {
title: { type: 'text' },
},
},
});
core.deprecations.registerDeprecations({ getDeprecations });
}
public start() {}
public stop() {}
}

View file

@ -0,0 +1,45 @@
/*
* 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 type { HttpServiceSetup } from 'kibana/server';
import { schema } from '@kbn/config-schema';
export function registerRoutes(http: HttpServiceSetup) {
const router = http.createRouter();
router.post(
{
path: '/api/core_deprecations_resolve/',
validate: {
body: schema.object({
mockFail: schema.maybe(schema.boolean()),
keyId: schema.maybe(schema.string()),
deprecationDetails: schema.object({
domainId: schema.string(),
}),
}),
},
},
async (context, req, res) => {
const { mockFail, keyId } = req.body;
if (mockFail === true) {
return res.badRequest({
body: new Error('Mocking api failure'),
});
}
if (keyId) {
const client = context.core.savedObjects.getClient();
await client.delete('test-deprecations-plugin', keyId, {
refresh: true,
});
}
return res.ok();
}
);
}

View file

@ -0,0 +1,18 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
"../../../../typings/**/*",
],
"exclude": [],
"references": [
{ "path": "../../../../src/core/tsconfig.json" }
]
}

View file

@ -0,0 +1,247 @@
/*
* 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 expect from '@kbn/expect';
import type { DomainDeprecationDetails, DeprecationsGetResponse } from 'src/core/server/types';
import type { ResolveDeprecationResponse } from 'src/core/public';
import { PluginFunctionalProviderContext } from '../../services';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common']);
const browser = getService('browser');
const CorePluginDeprecationsPluginDeprecations = [
{
level: 'critical',
message:
'"corePluginDeprecations.oldProperty" is deprecated and has been replaced by "corePluginDeprecations.newProperty"',
correctiveActions: {
manualSteps: [
'Replace "corePluginDeprecations.oldProperty" with "corePluginDeprecations.newProperty" in the Kibana config file, CLI flag, or environment variable (in Docker only).',
],
},
domainId: 'corePluginDeprecations',
},
{
level: 'critical',
message: 'corePluginDeprecations.noLongerUsed is deprecated and is no longer used',
correctiveActions: {
manualSteps: [
'Remove "corePluginDeprecations.noLongerUsed" from the Kibana config file, CLI flag, or environment variable (in Docker only)',
],
},
domainId: 'corePluginDeprecations',
},
{
level: 'critical',
message:
'Kibana plugin funcitonal tests will no longer allow corePluginDeprecations.secret config to be set to anything except 42.',
correctiveActions: {},
documentationUrl: 'config-secret-doc-url',
domainId: 'corePluginDeprecations',
},
{
message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.',
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
manualSteps: ['Step a', 'Step b'],
},
domainId: 'corePluginDeprecations',
},
{
message: 'SavedObject test-deprecations-plugin is still being used.',
documentationUrl: 'another-test-url',
level: 'critical',
correctiveActions: {},
domainId: 'corePluginDeprecations',
},
];
describe('deprecations service', () => {
before(() => esArchiver.load('../functional/fixtures/es_archiver/deprecations_service'));
after(() => esArchiver.unload('../functional/fixtures/es_archiver/deprecations_service'));
describe('GET /api/deprecations/', async () => {
it('returns registered config deprecations and feature deprecations', async () => {
const { body } = await supertest.get('/api/deprecations/').set('kbn-xsrf', 'true');
const { deprecations } = body as DeprecationsGetResponse;
expect(Array.isArray(deprecations)).to.be(true);
const corePluginDeprecations = deprecations.filter(
({ domainId }) => domainId === 'corePluginDeprecations'
);
expect(corePluginDeprecations).to.eql(CorePluginDeprecationsPluginDeprecations);
});
});
describe('Public API', () => {
before(async () => await PageObjects.common.navigateToApp('home'));
it('#getAllDeprecations returns all deprecations plugin deprecations', async () => {
const result = await browser.executeAsync<DomainDeprecationDetails[]>((cb) => {
return window._coreProvider.start.core.deprecations.getAllDeprecations().then(cb);
});
const corePluginDeprecations = result.filter(
({ domainId }) => domainId === 'corePluginDeprecations'
);
expect(corePluginDeprecations).to.eql(CorePluginDeprecationsPluginDeprecations);
});
it('#getDeprecations returns domain deprecations', async () => {
const corePluginDeprecations = await browser.executeAsync<DomainDeprecationDetails[]>(
(cb) => {
return window._coreProvider.start.core.deprecations
.getDeprecations('corePluginDeprecations')
.then(cb);
}
);
expect(corePluginDeprecations).to.eql(CorePluginDeprecationsPluginDeprecations);
});
describe('resolveDeprecation', () => {
it('fails on missing correctiveActions.api', async () => {
const resolveResult = await browser.executeAsync<ResolveDeprecationResponse>((cb) => {
return window._coreProvider.start.core.deprecations
.resolveDeprecation({
message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.',
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
manualSteps: ['Step a', 'Step b'],
},
domainId: 'corePluginDeprecations',
})
.then(cb);
});
expect(resolveResult).to.eql({
reason: 'deprecation has no correctiveAction via api.',
status: 'fail',
});
});
it('fails on bad request from correctiveActions.api', async () => {
const resolveResult = await browser.executeAsync<ResolveDeprecationResponse>((cb) => {
return window._coreProvider.start.core.deprecations
.resolveDeprecation({
message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.',
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
api: {
method: 'POST',
path: '/api/core_deprecations_resolve/',
body: {
mockFail: true,
},
},
},
domainId: 'corePluginDeprecations',
})
.then(cb);
});
expect(resolveResult).to.eql({
reason: 'Mocking api failure',
status: 'fail',
});
});
it('fails on 404 request from correctiveActions.api', async () => {
const resolveResult = await browser.executeAsync<ResolveDeprecationResponse>((cb) => {
return window._coreProvider.start.core.deprecations
.resolveDeprecation({
message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.',
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
api: {
method: 'POST',
path: '/api/invalid_route_not_registered/',
body: {
mockFail: true,
},
},
},
domainId: 'corePluginDeprecations',
})
.then(cb);
});
expect(resolveResult).to.eql({
reason: 'Not Found',
status: 'fail',
});
});
it('returns { status: ok } on successful correctiveActions.api', async () => {
const savedObjectId = await supertest
.get('/api/saved_objects/_find?type=test-deprecations-plugin')
.set('kbn-xsrf', 'true')
.expect(200)
.then(({ body }) => {
expect(body.total).to.be(1);
return body.saved_objects[0].id;
});
const resolveResult = await browser.executeAsync<ResolveDeprecationResponse>(
(keyId, cb) => {
return window._coreProvider.start.core.deprecations
.resolveDeprecation({
message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.',
documentationUrl: 'test-url',
level: 'warning',
correctiveActions: {
api: {
method: 'POST',
path: '/api/core_deprecations_resolve/',
body: { keyId },
},
},
domainId: 'corePluginDeprecations',
})
.then(cb);
},
savedObjectId
);
expect(resolveResult).to.eql({ status: 'ok' });
await supertest
.get('/api/saved_objects/_find?type=test-deprecations-plugin')
.set('kbn-xsrf', 'true')
.expect(200)
.then(({ body }) => {
expect(body.total).to.be(0);
});
const { deprecations } = await supertest
.get('/api/deprecations/')
.set('kbn-xsrf', 'true')
.then(
({ body }): Promise<DeprecationsGetResponse> => {
return body;
}
);
const deprecation = deprecations.find(
({ message }) => message === 'SavedObject test-deprecations-plugin is still being used.'
);
expect(deprecation).to.eql(undefined);
});
});
});
});
}

View file

@ -10,6 +10,7 @@ import { PluginFunctionalProviderContext } from '../../services';
export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
describe('core', function () {
loadTestFile(require.resolve('./deprecations'));
loadTestFile(require.resolve('./route'));
});
}

View file

@ -17,8 +17,8 @@ describe.skip('monitoring plugin deprecations', function () {
beforeAll(function () {
const deprecations = deprecationsModule({ rename, renameFromRoot });
transformDeprecations = (settings, fromPath, log = noop) => {
deprecations.forEach((deprecation) => deprecation(settings, fromPath, log));
transformDeprecations = (settings, fromPath, addDeprecation = noop) => {
deprecations.forEach((deprecation) => deprecation(settings, fromPath, addDeprecation));
};
});
@ -32,9 +32,9 @@ describe.skip('monitoring plugin deprecations', function () {
},
};
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).not.toHaveBeenCalled();
});
it(`shouldn't log when email_address is specified`, function () {
@ -47,9 +47,9 @@ describe.skip('monitoring plugin deprecations', function () {
},
};
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).not.toHaveBeenCalled();
});
it(`should log when email_address is missing, but alerts/notifications are both enabled`, function () {
@ -61,9 +61,9 @@ describe.skip('monitoring plugin deprecations', function () {
},
};
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).toHaveBeenCalled();
});
});
@ -71,65 +71,65 @@ describe.skip('monitoring plugin deprecations', function () {
it('logs a warning if elasticsearch.username is set to "elastic"', () => {
const settings = { elasticsearch: { username: 'elastic' } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).toHaveBeenCalled();
});
it('logs a warning if elasticsearch.username is set to "kibana"', () => {
const settings = { elasticsearch: { username: 'kibana' } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).toHaveBeenCalled();
});
it('does not log a warning if elasticsearch.username is set to something besides "elastic" or "kibana"', () => {
const settings = { elasticsearch: { username: 'otheruser' } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).not.toHaveBeenCalled();
});
it('does not log a warning if elasticsearch.username is unset', () => {
const settings = { elasticsearch: { username: undefined } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).not.toHaveBeenCalled();
});
it('logs a warning if ssl.key is set and ssl.certificate is not', () => {
const settings = { elasticsearch: { ssl: { key: '' } } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).toHaveBeenCalled();
});
it('logs a warning if ssl.certificate is set and ssl.key is not', () => {
const settings = { elasticsearch: { ssl: { certificate: '' } } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).toHaveBeenCalled();
});
it('does not log a warning if both ssl.key and ssl.certificate are set', () => {
const settings = { elasticsearch: { ssl: { key: '', certificate: '' } } };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(addDeprecation).not.toHaveBeenCalled();
});
});
describe('xpack_api_polling_frequency_millis', () => {
it('should call rename for this renamed config key', () => {
const settings = { xpack_api_polling_frequency_millis: 30000 };
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
const addDeprecation = jest.fn();
transformDeprecations(settings, fromPath, addDeprecation);
expect(rename).toHaveBeenCalled();
});
});

View file

@ -44,7 +44,7 @@ export const deprecations = ({
'monitoring.ui.elasticsearch.logFetchCount'
),
renameFromRoot('xpack.monitoring', 'monitoring'),
(config, fromPath, logger) => {
(config, fromPath, addDeprecation) => {
const clusterAlertsEnabled = get(config, 'monitoring.cluster_alerts.enabled', true);
const emailNotificationsEnabled =
clusterAlertsEnabled &&
@ -52,38 +52,38 @@ export const deprecations = ({
const updatedKey = get(config, `monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`);
const legacyKey = get(config, `xpack.monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`);
if (emailNotificationsEnabled && !updatedKey && !legacyKey) {
logger(
`Config key [${fromPath}.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}] will be required for email notifications to work in 8.0."`
);
addDeprecation({
message: `Config key [${fromPath}.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}] will be required for email notifications to work in 8.0."`,
});
}
return config;
},
(config, fromPath, logger) => {
(config, fromPath, addDeprecation) => {
const es: Record<string, any> = get(config, 'elasticsearch');
if (es) {
if (es.username === 'elastic') {
logger(
`Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`
);
addDeprecation({
message: `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`,
});
} else if (es.username === 'kibana') {
logger(
`Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`
);
addDeprecation({
message: `Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`,
});
}
}
return config;
},
(config, fromPath, logger) => {
(config, fromPath, addDeprecation) => {
const ssl: Record<string, any> = get(config, 'elasticsearch.ssl');
if (ssl) {
if (ssl.key !== undefined && ssl.certificate === undefined) {
logger(
`Setting [${fromPath}.key] without [${fromPath}.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
addDeprecation({
message: `Setting [${fromPath}.key] without [${fromPath}.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`,
});
} else if (ssl.certificate !== undefined && ssl.key === undefined) {
logger(
`Setting [${fromPath}.certificate] without [${fromPath}.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
addDeprecation({
message: `Setting [${fromPath}.certificate] without [${fromPath}.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`,
});
}
}
return config;

View file

@ -21,7 +21,7 @@ const applyReportingDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: CONFIG_PATH,
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,

View file

@ -24,12 +24,12 @@ export const config: PluginConfigDescriptor<ReportingConfigType> = {
unused('poll.jobCompletionNotifier.intervalErrorMultiplier'),
unused('poll.jobsRefresh.intervalErrorMultiplier'),
unused('kibanaApp'),
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
const reporting = get(settings, fromPath);
if (reporting?.index) {
log(
`"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`
);
addDeprecation({
message: `"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`,
});
}
return settings;
},

View file

@ -20,7 +20,7 @@ const applyConfigDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: 'xpack.security',
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,

View file

@ -23,16 +23,17 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({
unused('authorization.legacyFallback.enabled'),
unused('authc.saml.maxRedirectURLSize'),
// Deprecation warning for the old array-based format of `xpack.security.authc.providers`.
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
if (Array.isArray(settings?.xpack?.security?.authc?.providers)) {
log(
'Defining `xpack.security.authc.providers` as an array of provider types is deprecated. Use extended `object` format instead.'
);
addDeprecation({
message:
'Defining `xpack.security.authc.providers` as an array of provider types is deprecated. Use extended `object` format instead.',
});
}
return settings;
},
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
const hasProviderType = (providerType: string) => {
const providers = settings?.xpack?.security?.authc?.providers;
if (Array.isArray(providers)) {
@ -45,31 +46,34 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({
};
if (hasProviderType('basic') && hasProviderType('token')) {
log(
'Enabling both `basic` and `token` authentication providers in `xpack.security.authc.providers` is deprecated. Login page will only use `token` provider.'
);
addDeprecation({
message:
'Enabling both `basic` and `token` authentication providers in `xpack.security.authc.providers` is deprecated. Login page will only use `token` provider.',
});
}
return settings;
},
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
const samlProviders = (settings?.xpack?.security?.authc?.providers?.saml ?? {}) as Record<
string,
any
>;
if (Object.values(samlProviders).find((provider) => !!provider.maxRedirectURLSize)) {
log(
'`xpack.security.authc.providers.saml.<provider-name>.maxRedirectURLSize` is deprecated and is no longer used'
);
addDeprecation({
message:
'`xpack.security.authc.providers.saml.<provider-name>.maxRedirectURLSize` is deprecated and is no longer used',
});
}
return settings;
},
(settings, fromPath, log) => {
(settings, fromPath, addDeprecation) => {
if (settings?.xpack?.security?.enabled === false) {
log(
'Disabling the security plugin (`xpack.security.enabled`) will not be supported in the next major version (8.0). ' +
'To turn off security features, disable them in Elasticsearch instead.'
);
addDeprecation({
message:
'Disabling the security plugin (`xpack.security.enabled`) will not be supported in the next major version (8.0). ' +
'To turn off security features, disable them in Elasticsearch instead.',
});
}
return settings;
},

View file

@ -19,7 +19,7 @@ const applyConfigDeprecations = (settings: Record<string, any> = {}) => {
deprecation,
path: '',
})),
(msg) => deprecationMessages.push(msg)
() => ({ message }) => deprecationMessages.push(message)
);
return {
messages: deprecationMessages,

View file

@ -24,11 +24,11 @@ export function createConfig$(context: PluginInitializerContext) {
return context.config.create<TypeOf<typeof ConfigSchema>>();
}
const disabledDeprecation: ConfigDeprecation = (config, fromPath, log) => {
const disabledDeprecation: ConfigDeprecation = (config, fromPath, addDeprecation) => {
if (config.xpack?.spaces?.enabled === false) {
log(
`Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)`
);
addDeprecation({
message: `Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)`,
});
}
return config;
};

Some files were not shown because too many files have changed in this diff Show more