Kibana Plugin API Changes in 7.3 | Elastic Blog
Engineering

Kibana Plugin API Changes in 7.3

Now using package root for root directory

New Platform plugins are now loaded relative to the Kibana installation rather than the current working directory of the process. This only affects new platform plugins. Legacy plugins are unchanged.

via #39619,

Removed vis from aggTypeFieldFilters

If you register a filter via aggTypeFieldFilters.addFilter, that filter won't get vis passed in as the 4th argument anymore.

via #39880,

More dashboard migrations

Added dashboard saved object migrations.

  • Removed uiStateJSON, properties have been migrated into the panelsJSON objects.
  • Removed any remnants of sort and columns properties that were once stored on panel objects. Moved them permanently into the embeddableConfig.
  • Added migration step for old style angular react component that used size_x, size_y, row and col properties. Moved into gridData.
  • Added migration step for the version where we added more rows and columns to the grid.
  • URL migration is now using the same logic as migrations for the panels.

related to 35864

fixes 15204

via #39387,

Moved Chrome UI to Core

The ui/registry/chrome_nav_controls registry that was no longer supported in 7.0 has been removed. To add items to the header bar, you should now use the ui/registry/chrome_header_nav_controls or call the New Platform API directly:

import {
  chromeHeaderNavControlsRegistry,
  NavControlSide,
} from 'ui/registry/chrome_header_nav_controls';

chromeHeaderNavControlsRegistry.register(() => ({
  name: 'myControl',
  order: 1000,
  side: NavControlSide.Left,
  render(el) {
    ReactDOM.render(<MyComponent />, el);
  },
}));
import { npStart } from 'ui/new_platform';

npStart.core.chrome.navControls.registerLeft({
  order: 1000,
  mount(el) {
    ReactDOM.render(<MyComponent />, el);
    () => ReactDOM.unmountComponentAtNode(el);
  }
});

via #39300,

d13n docTitle

ui/doc_title now exports docTitle instead of DocTitleProvider. docTitle has the same interface as DocTitleProvider but does not rely on Private.

via #39162,

Allow requestCert option to be set

This allows us to require an HTTP server created by Kibana to force a client to provide a certificate for authorization to the server instance. This enables PKI-based mutual TLS for client/server interactions

Creating a configuration as such:

const sslConfig = new SslConfig({
  requestCert: true,
  ca: [myCA],
  key: privateKey,
  cert: myCert
})

Would require a client to provide the same cert and key (and ca if it's a self-signed certificate) to the https request in order to the server to respond:

const agent = new https.Agent({
  key: privateKey,
  cert: myCert
  ca: myCa
})
https.request('https://kibana.local:3000/', { agent }).end()
// or
Wreck.defaults({
  agent: {
    https: agent
  }
})
await wreck.get('https://kibana.local:3000')

Without the same certificate, key and ca, the Kibana server will reject the request as being unauthorized

via #38920,

Restrict access to hapi Request in registerAuth

In the previous release, we introduced experimental registerAuth hook in New Platform that allows end users to define custom authentication logic. We changed API of the AuthenticationHandler function. In 7.2:

export type AuthenticationHandler<T> = (
  request: Request,
  sessionStorage: SessionStorage<T>,
  t: AuthToolkit
) => Promise<AuthResult>;

In 7.3

export type AuthenticationHandler = (
  request: KibanaRequest,
  t: AuthToolkit
) => AuthResult | Promise<AuthResult>;

We provide KibanaRequest instead of hapi Request object and remove sessionStorage from arguments. Now sessionStorageFactory is returned as result of registerAuth call. Note: as API is experimental is still a subject for further changes.

via #38763,

Unify base path in HttpService

All requests to Kibana server should be concerned about server.basePath option, which affects all routes when Kibana is run behind a proxy. To simplify URL manipulations to include/exclude server.basePath part, New platform collocates under basePath namespace a set of helpers as a part of CoreSetup.http contract:

// on client side
basePath: {
  get: () => string;
  prepend: (url: string) => string;
  remove: (url: string) => string;
};
// on server side
basePath {
  get: (request: KibanaRequest | Request) => string;
  set: (request: KibanaRequest | Request, basePath: string) => void;
  prepend: (url: string) => string;
  remove: (url: string) => string;
};

via #38237,

Allow passing in a signal to abort a cluster client request

callWithRequest now accepts a AbortController signal for aborting requests to Elasticsearch. Example usage:

const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
const controller = new AbortController();
req.events.once('disconnect', () => controller.abort());
return await callWithRequest(req, 'search', params, { signal: controller.signal });

via #37563,

Expose plugin contracts to Legacy platform

If you want to use a New Platform plugin in Legacy Platform you could access them

On the client

import { getNewPlatform } from 'ui/new_platform';
const plugins: Record<string, unknown> = getNewPlatform().setup.plugins

On the server

new kibana.Plugin({
  init(server){
    const plugins: Record<string, unknown> = server.newPlatform.setup.plugins;
  }
})

via #37218,

ui/public cleanup

Relocated modules

In preparation for Kibana's upcoming new platform, we are in the process of migrating away from the ui/public directory. Over time, the contents of this directory will be either deprecated or housed inside a parent plugin. If your plugin imports from any of the following ui/public modules, you will need to update your import statements as indicated below, so that you are pulling these modules from their new locations.

ui/filter_bar/query_filter #37311

// deprecated
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
const queryFilter = Private(FilterBarQueryFilterProvider);
queryFilter.addFilters(...);

// new location
import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter';
const queryFilter = Private(FilterBarQueryFilterProvider);
queryFilter.addFilters(...);

In addition, the following methods have been deprecated:

  • toggleFilter - Change the filter's disabled attribute instead.
  • pinFilter - Use FilterManager.setFiltersStore to toggle filters between global & app state.
  • pinAll
  • toggleAll
  • invertAll

ui/filter_manager/filter_manager #37311

// deprecated
import { FilterManagerProvider } from 'ui/filter_manager';
const filterManager = Private(FilterManagerProvider);

// new location
import { getFilterGenerator } from 'ui/filter_manager';
const filterGen = getFilterGenerator(queryFilter);

ui/apply_filters #36778

// deprecated
import 'ui/apply_filters';

// new location
import { data } from 'plugins/data/setup';
data.filter.loadLegacyDirectives();
...
<apply-filters-popover ...

// new location - react
import { data } from 'plugins/data/setup';
const { ApplyFiltersPopover } = data.filter.ui;
...
<ApplyFiltersPopover />

ui/utils/brush_event #35625

// deprecated
import { onBrushEvent } from 'ui/utils/brush_event';

// new location
import { onBrushEvent } from 'ui/vis/vis_filters/brush_event';

ui/management #39168

In the Management app, IndexPatternCreationConfigRegistry registry has been replaced with the addIndexPatternType function.

// deprecated
import { IndexPatternCreationConfigRegistry } from 'ui/management/index_pattern_creation';
IndexPatternCreationConfigRegistry.register(() => MyIndexPatternCreationConfig);

// new location
import { addIndexPatternType } from 'ui/management/index_pattern_creation';
addIndexPatternType(MyIndexPatternCreationConfig);

Angular Removal

Removed unused Angular items

In the process of removing Angular from the Kibana core, the following unused items have been deleted or relocated. If you have used any of those in your plugin and want to keep using them, please copy over the source from the previous Kibana version directly into your plugin code.

Directives

  • formDirective, ui/fancy_forms (via #39977)
  • modal, ui/angular-bootstrap (via #39976)
  • ngFormDirective, ui/fancy_forms (via #39977)
  • ngModelDirective, ui/fancy_forms (via #39977)
  • tableInfo, ui/table_info (via #39978)
  • toolBarPagerButtons, ui/pager_control (via #39975)
  • toolBarPagerText, ui/pager_control (via #39975)
  • paginated_selectable_list (via #39976)

Factories

  • $transition, ui/angular-bootstrap (via #39976)
  • pagerFactory, ui/pager (via #39975)

via #37007,

Allow interception of http requests from browser http service

HTTP requests made via the new platform browser HTTP service can now be intercepted by registering interceptors:

http.intercept({
  request(request: Request) {
    // Modify or return a new request.
    // Will be passed eventually to window.fetch(request)
    return new Request(newPath, request);
  },
  requestError({ request, error }: HttpErrorRequest) {
    // Handle errors that occur prior to the request being made.
    if (error.message.test(/test/) {
      // ...
    }

    // Modify or return a new request.
    return new Request(newPath, request);

    // Throw the existing error or throw a new one if necessary.
    throw new Error('Unauthorized');
  },
  response(httpResponse: HttpResponse) {
    // Modify the response or body and return as HttpResponse.
    return {
      ...httpResponse,
      body: 'hello',
    };

    // Throw if necessary.
    throw new Error('Unauthorized');
  },
  responseError({ request, response, body, error }: HttpErrorResponse) {
    // Handle errors that occur after the request has been made.
    // Either return a new HttpResponse, throw a new error, or throw the existing error.
  },
});

Any of these methods can be thrown from to trigger the interception flow. Interceptor handlers also expose an HttpInterceptController to halt continuation of queued interceptions. This will cause the fetch operation to fail with a "HTTP Intercept Halt" error.

http.intercept({
  request(request: Request, controller: HttpInterceptController) {
    controller.halt();
    // No more interceptors will be called,
    // and the fetch will now reject with an HttpInterceptHaltError
  },
  response(httpResponse: HttpResponse, controller: HttpInterceptController) {
    controller.halt();
    // No more interceptors will be called, and even though the request was already made,
    // the fetch will still reject with an HttpInterceptHaltError
  },
});

A convenience method to remove all HTTP interceptors has also been added:

http.removeAllInterceptors();

via #36939,

Created additional http servers

New Platform plugins can now create additional HTTP servers on additional ports. Example:

class MyPlugin {
  setup({ http }) {
    this.additionalServer = http.createNewServer({ port: 3000 });
    this.additionalServer.registerRouter(new Router());
  }

  async start() {
    await this.additionalServer.start();
  }
}

via #36804,

introduce pre-/post-auth request hooks for HttpServer

Kibana let define custom request interceptors for incoming requests:

  • http.registerAuth(handler, cookieOptions) => Promise<void> To define custom authentication and/or authorization mechanism for incoming requests. A handler should return a state to associate with the incoming request. The state can be retrieved later via http.auth.get(..). Only one AuthenticationHandler can be registered.
  • http.registerOnPreAuth(handler) => void To define custom logic to perform for incoming requests. Runs the handler before Auth hook performs a check that user has access to requested resources, so it's the only place when you can forward a request to another URL right on the server. Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last).
  • http.registerOnPreAuth (handler) => void To define custom logic to perform for incoming requests. Runs the handler after Auth hook did make sure a user has access to the requested resource. The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreAuth, which are called in sequence (from the first registered to the last).

via #36690,

Remove browser basePath service, move functionality into browser http service

The browser basePath service is removed in favor of handling by the browser http service. As the basePath was used for controlling the route used to make fetch requests, it makes sense to colocate this functionality into the HTTP service.

via #36611,

i18n extract untracked translations and prettier logging

Running node scripts/i18n_check.js now searches for labels in untracked files and throws an error if it finds any.

The i18n check tool now has an enhanced reporting experience with better error logging:

render1560538316757

via #35171