From c73f984e7f989d8aed124562d605a1f35ac39a97 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 13 Dec 2019 11:33:15 +0100 Subject: [PATCH] Migrate graph server and licensing handling (#52279) --- src/legacy/plugin_discovery/types.ts | 1 + .../ui/public/new_platform/new_platform.ts | 2 + x-pack/.i18nrc.json | 2 +- .../plugins/graph/{index.js => index.ts} | 23 ++-- x-pack/legacy/plugins/graph/public/app.js | 19 +-- .../public/hacks/toggle_app_link_in_nav.js | 19 --- x-pack/legacy/plugins/graph/public/index.ts | 4 +- x-pack/legacy/plugins/graph/public/plugin.ts | 7 +- .../plugins/graph/public/register_feature.js | 25 ---- .../legacy/plugins/graph/public/render_app.ts | 22 +++- .../plugins/graph/server/init_server.js | 26 ---- .../server/lib/__tests__/check_license.js | 120 ------------------ .../plugins/graph/server/lib/check_license.js | 60 --------- .../lib/es/call_es_graph_explore_api.js | 40 ------ .../graph/server/lib/es/call_es_search_api.js | 22 ---- .../plugins/graph/server/lib/es/index.js | 8 -- .../legacy/plugins/graph/server/lib/index.js | 17 --- .../server/lib/pre/get_call_cluster_pre.js | 13 -- .../plugins/graph/server/lib/pre/index.js | 8 -- .../server/lib/pre/verify_api_access_pre.js | 19 --- .../graph/server/routes/graph_explore.js | 37 ------ .../graph/server/routes/search_proxy.js | 43 ------- x-pack/plugins/graph/common/check_license.ts | 68 ++++++++++ x-pack/plugins/graph/kibana.json | 9 ++ .../graph/public/index.ts} | 4 +- x-pack/plugins/graph/public/plugin.ts | 54 ++++++++ .../graph/public/services/toggle_nav_link.ts | 26 ++++ .../graph/server/index.ts} | 5 +- .../plugins/graph/server/lib/license_state.ts | 43 +++++++ x-pack/plugins/graph/server/plugin.ts | 32 +++++ x-pack/plugins/graph/server/routes/explore.ts | 79 ++++++++++++ x-pack/plugins/graph/server/routes/search.ts | 61 +++++++++ .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 34 files changed, 419 insertions(+), 503 deletions(-) rename x-pack/legacy/plugins/graph/{index.js => index.ts} (83%) delete mode 100644 x-pack/legacy/plugins/graph/public/hacks/toggle_app_link_in_nav.js delete mode 100644 x-pack/legacy/plugins/graph/public/register_feature.js delete mode 100644 x-pack/legacy/plugins/graph/server/init_server.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/__tests__/check_license.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/check_license.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/es/call_es_graph_explore_api.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/es/call_es_search_api.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/es/index.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/index.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/pre/get_call_cluster_pre.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/pre/index.js delete mode 100644 x-pack/legacy/plugins/graph/server/lib/pre/verify_api_access_pre.js delete mode 100644 x-pack/legacy/plugins/graph/server/routes/graph_explore.js delete mode 100644 x-pack/legacy/plugins/graph/server/routes/search_proxy.js create mode 100644 x-pack/plugins/graph/common/check_license.ts create mode 100644 x-pack/plugins/graph/kibana.json rename x-pack/{legacy/plugins/graph/server/index.js => plugins/graph/public/index.ts} (73%) create mode 100644 x-pack/plugins/graph/public/plugin.ts create mode 100644 x-pack/plugins/graph/public/services/toggle_nav_link.ts rename x-pack/{legacy/plugins/graph/server/routes/index.js => plugins/graph/server/index.ts} (70%) create mode 100644 x-pack/plugins/graph/server/lib/license_state.ts create mode 100644 x-pack/plugins/graph/server/plugin.ts create mode 100644 x-pack/plugins/graph/server/routes/explore.ts create mode 100644 x-pack/plugins/graph/server/routes/search.ts diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts index c32418e1aeb6..fe886b9d1781 100644 --- a/src/legacy/plugin_discovery/types.ts +++ b/src/legacy/plugin_discovery/types.ts @@ -69,6 +69,7 @@ export interface LegacyPluginOptions { noParse: string[]; home: string[]; mappings: any; + migrations: any; savedObjectSchemas: SavedObjectsSchemaDefinition; savedObjectsManagement: SavedObjectsManagementDefinition; visTypes: string[]; diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index d80d11e1b1bd..547d22c58cfc 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -32,6 +32,7 @@ import { DevToolsSetup, DevToolsStart } from '../../../../plugins/dev_tools/publ import { KibanaLegacySetup, KibanaLegacyStart } from '../../../../plugins/kibana_legacy/public'; import { HomePublicPluginSetup, HomePublicPluginStart } from '../../../../plugins/home/public'; import { SharePluginSetup, SharePluginStart } from '../../../../plugins/share/public'; +import { LicensingPluginSetup } from '../../../../../x-pack/plugins/licensing/common/types'; export interface PluginsSetup { data: ReturnType; @@ -43,6 +44,7 @@ export interface PluginsSetup { dev_tools: DevToolsSetup; kibana_legacy: KibanaLegacySetup; share: SharePluginSetup; + licensing: LicensingPluginSetup; } export interface PluginsStart { diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 180aafe504c6..7e86d2f1dc43 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -12,7 +12,7 @@ "xpack.endpoint": "plugins/endpoint", "xpack.features": "plugins/features", "xpack.fileUpload": "legacy/plugins/file_upload", - "xpack.graph": "legacy/plugins/graph", + "xpack.graph": ["legacy/plugins/graph", "plugins/graph"], "xpack.grokDebugger": "legacy/plugins/grokdebugger", "xpack.idxMgmt": "legacy/plugins/index_management", "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management", diff --git a/x-pack/legacy/plugins/graph/index.js b/x-pack/legacy/plugins/graph/index.ts similarity index 83% rename from x-pack/legacy/plugins/graph/index.js rename to x-pack/legacy/plugins/graph/index.ts index 9ece9966b7da..601a239574e6 100644 --- a/x-pack/legacy/plugins/graph/index.js +++ b/x-pack/legacy/plugins/graph/index.ts @@ -7,11 +7,12 @@ import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; +// @ts-ignore import migrations from './migrations'; -import { initServer } from './server'; import mappings from './mappings.json'; +import { LegacyPluginInitializer } from '../../../../src/legacy/plugin_discovery/types'; -export function graph(kibana) { +export const graph: LegacyPluginInitializer = kibana => { return new kibana.Plugin({ id: 'graph', configPrefix: 'xpack.graph', @@ -26,17 +27,17 @@ export function graph(kibana) { main: 'plugins/graph/index', }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: ['plugins/graph/hacks/toggle_app_link_in_nav'], - home: ['plugins/graph/register_feature'], mappings, migrations, }, - config(Joi) { + config(Joi: any) { return Joi.object({ enabled: Joi.boolean().default(true), canEditDrillDownUrls: Joi.boolean().default(true), - savePolicy: Joi.string().valid(['config', 'configAndDataWithConsent', 'configAndData', 'none']).default('configAndData'), + savePolicy: Joi.string() + .valid(['config', 'configAndDataWithConsent', 'configAndData', 'none']) + .default('configAndData'), }).default(); }, @@ -45,7 +46,7 @@ export function graph(kibana) { const config = server.config(); return { graphSavePolicy: config.get('xpack.graph.savePolicy'), - canEditDrillDownUrls: config.get('xpack.graph.canEditDrillDownUrls') + canEditDrillDownUrls: config.get('xpack.graph.canEditDrillDownUrls'), }; }); @@ -72,11 +73,9 @@ export function graph(kibana) { read: ['index-pattern', 'graph-workspace'], }, ui: [], - } - } + }, + }, }); - - initServer(server); }, }); -} +}; diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index aa08841e03f5..353123524335 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -13,7 +13,6 @@ import { isColorDark, hexToRgb } from '@elastic/eui'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; -import { addAppRedirectMessageToUrl } from 'ui/notify'; import appTemplate from './angular/templates/index.html'; import listingTemplate from './angular/templates/listing_ng_wrapper.html'; @@ -39,10 +38,10 @@ import { hasFieldsSelector } from './state_management'; import { formatHttpError } from './helpers/format_http_error'; +import { checkLicense } from '../../../../plugins/graph/common/check_license'; export function initGraphApp(angularModule, deps) { const { - xpackInfo, chrome, savedGraphWorkspaces, toastNotifications, @@ -63,17 +62,6 @@ export function initGraphApp(angularModule, deps) { const app = angularModule; - function checkLicense(kbnBaseUrl) { - const licenseAllowsToShowThisPage = xpackInfo.get('features.graph.showAppLink') && - xpackInfo.get('features.graph.enableAppLink'); - if (!licenseAllowsToShowThisPage) { - const message = xpackInfo.get('features.graph.message'); - const newUrl = addAppRedirectMessageToUrl(addBasePath(kbnBaseUrl), message); - window.location.href = newUrl; - throw new Error('Graph license error'); - } - } - app.directive('vennDiagram', function (reactDirective) { return reactDirective(VennDiagram); }); @@ -123,7 +111,6 @@ export function initGraphApp(angularModule, deps) { template: listingTemplate, badge: getReadonlyBadge, controller($location, $scope) { - checkLicense(kbnBaseUrl); const services = savedObjectRegistry.byLoaderPropertiesName; const graphService = services['Graph workspace']; @@ -164,7 +151,6 @@ export function initGraphApp(angularModule, deps) { ) : savedGraphWorkspaces.get(); }, - //Copied from example found in wizard.js ( Kibana TODO - can't indexPatterns: function () { return savedObjectsClient.find({ type: 'index-pattern', @@ -185,10 +171,8 @@ export function initGraphApp(angularModule, deps) { //======== Controller for basic UI ================== app.controller('graphuiPlugin', function ($scope, $route, $location, confirmModal) { - checkLicense(kbnBaseUrl); function handleError(err) { - checkLicense(kbnBaseUrl); const toastTitle = i18n.translate('xpack.graph.errorToastTitle', { defaultMessage: 'Graph Error', description: '"Graph" is a product name and should not be translated.', @@ -206,7 +190,6 @@ export function initGraphApp(angularModule, deps) { } async function handleHttpError(error) { - checkLicense(kbnBaseUrl); toastNotifications.addDanger(formatHttpError(error)); } diff --git a/x-pack/legacy/plugins/graph/public/hacks/toggle_app_link_in_nav.js b/x-pack/legacy/plugins/graph/public/hacks/toggle_app_link_in_nav.js deleted file mode 100644 index 5b1eb6181fd6..000000000000 --- a/x-pack/legacy/plugins/graph/public/hacks/toggle_app_link_in_nav.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -const navLinkUpdates = {}; -navLinkUpdates.hidden = true; -const showAppLink = xpackInfo.get('features.graph.showAppLink', false); -navLinkUpdates.hidden = !showAppLink; -if (showAppLink) { - navLinkUpdates.disabled = !xpackInfo.get('features.graph.enableAppLink', false); - navLinkUpdates.tooltip = xpackInfo.get('features.graph.message'); -} - -npStart.core.chrome.navLinks.update('graph', navLinkUpdates); diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts index 712d08c10642..e0ce21032889 100644 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ b/x-pack/legacy/plugins/graph/public/index.ts @@ -12,8 +12,6 @@ import 'ui/autoload/all'; import chrome from 'ui/chrome'; import { IPrivate } from 'ui/private'; // @ts-ignore -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -// @ts-ignore import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { npSetup, npStart } from 'ui/new_platform'; @@ -45,9 +43,9 @@ async function getAngularInjectedDependencies(): Promise; @@ -18,8 +19,8 @@ export interface GraphPluginStartDependencies { export interface GraphPluginSetupDependencies { __LEGACY: { Storage: any; - xpackInfo: any; }; + licensing: LicensingPluginSetup; } export interface GraphPluginStartDependencies { @@ -34,7 +35,7 @@ export class GraphPlugin implements Plugin { private savedObjectsClient: SavedObjectsClientContract | null = null; private angularDependencies: LegacyAngularInjectedDependencies | null = null; - setup(core: CoreSetup, { __LEGACY: { xpackInfo, Storage } }: GraphPluginSetupDependencies) { + setup(core: CoreSetup, { __LEGACY: { Storage }, licensing }: GraphPluginSetupDependencies) { core.application.register({ id: 'graph', title: 'Graph', @@ -42,10 +43,10 @@ export class GraphPlugin implements Plugin { const { renderApp } = await import('./render_app'); return renderApp({ ...params, + licensing, navigation: this.navigationStart!, npData: this.npDataStart!, savedObjectsClient: this.savedObjectsClient!, - xpackInfo, addBasePath: core.http.basePath.prepend, getBasePath: core.http.basePath.get, canEditDrillDownUrls: core.injectedMetadata.getInjectedVar( diff --git a/x-pack/legacy/plugins/graph/public/register_feature.js b/x-pack/legacy/plugins/graph/public/register_feature.js deleted file mode 100644 index b3e2efc990b7..000000000000 --- a/x-pack/legacy/plugins/graph/public/register_feature.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; - -import { i18n } from '@kbn/i18n'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'graph', - title: 'Graph', - description: i18n.translate('xpack.graph.pluginDescription', { - defaultMessage: 'Surface and analyze relevant relationships in your Elasticsearch data.', - }), - icon: 'graphApp', - path: '/app/graph', - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA - }; -}); diff --git a/x-pack/legacy/plugins/graph/public/render_app.ts b/x-pack/legacy/plugins/graph/public/render_app.ts index 0f3c52d38a01..2cad8f629f2a 100644 --- a/x-pack/legacy/plugins/graph/public/render_app.ts +++ b/x-pack/legacy/plugins/graph/public/render_app.ts @@ -19,6 +19,8 @@ import { configureAppAngularModule } from 'ui/legacy_compat'; import { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; // @ts-ignore import { confirmModalFactory } from 'ui/modals/confirm_modal'; +// @ts-ignore +import { addAppRedirectMessageToUrl } from 'ui/notify'; // type imports import { @@ -36,6 +38,8 @@ import { IndexPatternsContract, } from '../../../../../src/plugins/data/public'; import { NavigationStart } from '../../../../../src/legacy/core_plugins/navigation/public'; +import { LicensingPluginSetup } from '../../../../plugins/licensing/common/types'; +import { checkLicense } from '../../../../plugins/graph/common/check_license'; /** * These are dependencies of the Graph app besides the base dependencies @@ -49,13 +53,13 @@ export interface GraphDependencies extends LegacyAngularInjectedDependencies { capabilities: Record>; coreStart: AppMountContext['core']; navigation: NavigationStart; + licensing: LicensingPluginSetup; chrome: ChromeStart; config: IUiSettingsClient; toastNotifications: ToastsStart; indexPatterns: IndexPatternsContract; npData: ReturnType; savedObjectsClient: SavedObjectsClientContract; - xpackInfo: { get(path: string): unknown }; addBasePath: (url: string) => string; getBasePath: () => string; Storage: any; @@ -82,9 +86,23 @@ export interface LegacyAngularInjectedDependencies { export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { const graphAngularModule = createLocalAngularModule(deps.navigation); configureAppAngularModule(graphAngularModule, deps.coreStart as LegacyCoreStart, true); + + const licenseSubscription = deps.licensing.license$.subscribe(license => { + const info = checkLicense(license); + const licenseAllowsToShowThisPage = info.showAppLink && info.enableAppLink; + + if (!licenseAllowsToShowThisPage) { + const newUrl = addAppRedirectMessageToUrl(deps.addBasePath(deps.kbnBaseUrl), info.message); + window.location.href = newUrl; + } + }); + initGraphApp(graphAngularModule, deps); const $injector = mountGraphApp(appBasePath, element); - return () => $injector.get('$rootScope').$destroy(); + return () => { + licenseSubscription.unsubscribe(); + $injector.get('$rootScope').$destroy(); + }; }; const mainTemplate = (basePath: string) => `
diff --git a/x-pack/legacy/plugins/graph/server/init_server.js b/x-pack/legacy/plugins/graph/server/init_server.js deleted file mode 100644 index 4647fe9c2662..000000000000 --- a/x-pack/legacy/plugins/graph/server/init_server.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; -import { checkLicense } from './lib'; -import { graphExploreRoute, searchProxyRoute } from './routes'; - -export function initServer(server) { - const graphPlugin = server.plugins.graph; - const xpackMainPlugin = server.plugins.xpack_main; - - mirrorPluginStatus(xpackMainPlugin, graphPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results - xpackMainPlugin.info.feature('graph').registerLicenseCheckResultsGenerator(checkLicense); - }); - - server.route(graphExploreRoute); - server.route(searchProxyRoute); -} diff --git a/x-pack/legacy/plugins/graph/server/lib/__tests__/check_license.js b/x-pack/legacy/plugins/graph/server/lib/__tests__/check_license.js deleted file mode 100644 index c341dfbc378c..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/__tests__/check_license.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { set } from 'lodash'; -import sinon from 'sinon'; -import { checkLicense } from '../check_license'; - -describe('check_license: ', function () { - - let mockLicenseInfo; - let licenseCheckResult; - - beforeEach(() => { - mockLicenseInfo = { - isAvailable: () => true - }; - }); - - describe('mockLicenseInfo is not set', () => { - beforeEach(() => { - mockLicenseInfo = null; - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to true', () => { - expect(licenseCheckResult.showAppLink).to.be(true); - }); - - it ('should set enableAppLink to false', () => { - expect(licenseCheckResult.enableAppLink).to.be(false); - }); - }); - - describe('mockLicenseInfo is set but not available', () => { - beforeEach(() => { - mockLicenseInfo = { isAvailable: () => false }; - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to true', () => { - expect(licenseCheckResult.showAppLink).to.be(true); - }); - - it ('should set enableAppLink to false', () => { - expect(licenseCheckResult.enableAppLink).to.be(false); - }); - }); - - describe('graph is disabled in Elasticsearch', () => { - beforeEach(() => { - set(mockLicenseInfo, 'feature', sinon.stub().withArgs('graph').returns({ isEnabled: () => false })); - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to false', () => { - expect(licenseCheckResult.showAppLink).to.be(false); - }); - }); - - describe('graph is enabled in Elasticsearch', () => { - beforeEach(() => { - set(mockLicenseInfo, 'feature', sinon.stub().withArgs('graph').returns({ isEnabled: () => true })); - }); - - describe('& license is trial or platinum', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', sinon.stub().withArgs([ 'trial', 'platinum' ]).returns(true)); - set(mockLicenseInfo, 'license.getType', () => 'trial'); - }); - - describe('& license is active', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isActive', () => true); - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to true', () => { - expect(licenseCheckResult.showAppLink).to.be(true); - }); - - it ('should set enableAppLink to true', () => { - expect(licenseCheckResult.enableAppLink).to.be(true); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isActive', () => false); - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to true', () => { - expect(licenseCheckResult.showAppLink).to.be(true); - }); - - it ('should set enableAppLink to false', () => { - expect(licenseCheckResult.enableAppLink).to.be(false); - }); - }); - - }); - - describe('& license is neither trial nor platinum', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', () => false); - set(mockLicenseInfo, 'license.getType', () => 'basic'); - set(mockLicenseInfo, 'license.isActive', () => true); - licenseCheckResult = checkLicense(mockLicenseInfo); - }); - - it ('should set showAppLink to false', () => { - expect(licenseCheckResult.showAppLink).to.be(false); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/graph/server/lib/check_license.js b/x-pack/legacy/plugins/graph/server/lib/check_license.js deleted file mode 100644 index 4ddac989a789..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/check_license.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function checkLicense(xpackLicenseInfo) { - - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - showAppLink: true, - enableAppLink: false, - message: i18n.translate('xpack.graph.serverSideErrors.unavailableLicenseInformationErrorMessage', { - defaultMessage: 'Graph is unavailable - license information is not available at this time.', - }) - }; - } - - const graphFeature = xpackLicenseInfo.feature('graph'); - if (!graphFeature.isEnabled()) { - return { - showAppLink: false, - enableAppLink: false, - message: i18n.translate('xpack.graph.serverSideErrors.unavailableGraphErrorMessage', { - defaultMessage: 'Graph is unavailable', - }) - }; - } - - const isLicenseActive = xpackLicenseInfo.license.isActive(); - let message; - if (!isLicenseActive) { - message = i18n.translate('xpack.graph.serverSideErrors.expiredLicenseErrorMessage', { - defaultMessage: 'Graph is unavailable - license has expired.', - }); - } - - if (xpackLicenseInfo.license.isOneOf([ 'trial', 'platinum' ])) { - return { - showAppLink: true, - enableAppLink: isLicenseActive, - message - }; - } - - message = i18n.translate('xpack.graph.serverSideErrors.wrongLicenseTypeErrorMessage', { - defaultMessage: 'Graph is unavailable for the current {licenseType} license. Please upgrade your license.', - values: { - licenseType: xpackLicenseInfo.license.getType(), - }, - }); - - return { - showAppLink: false, - enableAppLink: false, - message - }; -} diff --git a/x-pack/legacy/plugins/graph/server/lib/es/call_es_graph_explore_api.js b/x-pack/legacy/plugins/graph/server/lib/es/call_es_graph_explore_api.js deleted file mode 100644 index a656a4349f61..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/es/call_es_graph_explore_api.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import { get } from 'lodash'; - -export async function callEsGraphExploreApi({ callCluster, index, query }) { - try { - return { - ok: true, - resp: await callCluster('transport.request', { - 'path': '/' + encodeURIComponent(index) + '/_graph/explore', - body: query, - method: 'POST', - query: {} - }) - }; - } catch (error) { - // Extract known reasons for bad choice of field - const relevantCause = [].concat(get(error, 'body.error.root_cause', []) || []) - .find(cause => { - return ( - cause.reason.includes('Fielddata is disabled on text fields') || - cause.reason.includes('No support for examining floating point') || - cause.reason.includes('Sample diversifying key must be a single valued-field') || - cause.reason.includes('Failed to parse query') || - cause.type == 'parsing_exception' - ); - }); - - if (relevantCause) { - throw Boom.badRequest(relevantCause.reason); - } - - throw Boom.boomify(error); - } -} diff --git a/x-pack/legacy/plugins/graph/server/lib/es/call_es_search_api.js b/x-pack/legacy/plugins/graph/server/lib/es/call_es_search_api.js deleted file mode 100644 index cdc355d2bdea..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/es/call_es_search_api.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -export async function callEsSearchApi({ callCluster, index, body, queryParams }) { - try { - return { - ok: true, - resp: await callCluster('search', { - ...queryParams, - index, - body - }) - }; - } catch (error) { - throw Boom.boomify(error, { statusCode: error.statusCode || 500 }); - } -} diff --git a/x-pack/legacy/plugins/graph/server/lib/es/index.js b/x-pack/legacy/plugins/graph/server/lib/es/index.js deleted file mode 100644 index b385a1be018c..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/es/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { callEsSearchApi } from './call_es_search_api'; -export { callEsGraphExploreApi } from './call_es_graph_explore_api'; diff --git a/x-pack/legacy/plugins/graph/server/lib/index.js b/x-pack/legacy/plugins/graph/server/lib/index.js deleted file mode 100644 index 6fe112004afb..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { checkLicense } from './check_license'; - -export { - callEsGraphExploreApi, - callEsSearchApi, -} from './es'; - -export { - getCallClusterPre, - verifyApiAccessPre, -} from './pre'; diff --git a/x-pack/legacy/plugins/graph/server/lib/pre/get_call_cluster_pre.js b/x-pack/legacy/plugins/graph/server/lib/pre/get_call_cluster_pre.js deleted file mode 100644 index 383170a13d4c..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/pre/get_call_cluster_pre.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const getCallClusterPre = { - assign: 'callCluster', - method(request) { - const cluster = request.server.plugins.elasticsearch.getCluster('data'); - return (...args) => cluster.callWithRequest(request, ...args); - } -}; diff --git a/x-pack/legacy/plugins/graph/server/lib/pre/index.js b/x-pack/legacy/plugins/graph/server/lib/pre/index.js deleted file mode 100644 index 8d2e15f612bf..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/pre/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { getCallClusterPre } from './get_call_cluster_pre'; -export { verifyApiAccessPre } from './verify_api_access_pre'; diff --git a/x-pack/legacy/plugins/graph/server/lib/pre/verify_api_access_pre.js b/x-pack/legacy/plugins/graph/server/lib/pre/verify_api_access_pre.js deleted file mode 100644 index a26e7247d628..000000000000 --- a/x-pack/legacy/plugins/graph/server/lib/pre/verify_api_access_pre.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -export function verifyApiAccessPre(request, h) { - const xpackInfo = request.server.plugins.xpack_main.info; - const graph = xpackInfo.feature('graph'); - const licenseCheckResults = graph.getLicenseCheckResults(); - - if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) { - return null; - } else { - throw Boom.forbidden(licenseCheckResults.message); - } -} diff --git a/x-pack/legacy/plugins/graph/server/routes/graph_explore.js b/x-pack/legacy/plugins/graph/server/routes/graph_explore.js deleted file mode 100644 index 7f77903dd705..000000000000 --- a/x-pack/legacy/plugins/graph/server/routes/graph_explore.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; - -import { - verifyApiAccessPre, - getCallClusterPre, - callEsGraphExploreApi, -} from '../lib'; - -export const graphExploreRoute = { - path: '/api/graph/graphExplore', - method: 'POST', - config: { - pre: [ - verifyApiAccessPre, - getCallClusterPre, - ], - validate: { - payload: Joi.object().keys({ - index: Joi.string().required(), - query: Joi.object().required().unknown(true) - }).default() - }, - handler(request) { - return callEsGraphExploreApi({ - callCluster: request.pre.callCluster, - index: request.payload.index, - query: request.payload.query, - }); - } - } -}; diff --git a/x-pack/legacy/plugins/graph/server/routes/search_proxy.js b/x-pack/legacy/plugins/graph/server/routes/search_proxy.js deleted file mode 100644 index 64fc44b1e367..000000000000 --- a/x-pack/legacy/plugins/graph/server/routes/search_proxy.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Boom from 'boom'; - -import { - verifyApiAccessPre, - getCallClusterPre, - callEsSearchApi, -} from '../lib'; - -export const searchProxyRoute = { - path: '/api/graph/searchProxy', - method: 'POST', - config: { - pre: [ - getCallClusterPre, - verifyApiAccessPre, - ], - validate: { - payload: Joi.object().keys({ - index: Joi.string().required(), - body: Joi.object().unknown(true).default() - }).default() - }, - async handler(request) { - const includeFrozen = await request.getUiSettingsService().get('search:includeFrozen'); - return await callEsSearchApi({ - callCluster: request.pre.callCluster, - index: request.payload.index, - body: request.payload.body, - queryParams: { - rest_total_hits_as_int: true, - ignore_throttled: !includeFrozen, - } - }); - } - } -}; diff --git a/x-pack/plugins/graph/common/check_license.ts b/x-pack/plugins/graph/common/check_license.ts new file mode 100644 index 000000000000..a918f53776b1 --- /dev/null +++ b/x-pack/plugins/graph/common/check_license.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { ILicense, LICENSE_CHECK_STATE } from '../../licensing/common/types'; +import { assertNever } from '../../../../src/core/utils'; + +export interface GraphLicenseInformation { + showAppLink: boolean; + enableAppLink: boolean; + message: string; +} + +export function checkLicense(license: ILicense | undefined): GraphLicenseInformation { + if (!license || !license.isAvailable) { + return { + showAppLink: true, + enableAppLink: false, + message: i18n.translate( + 'xpack.graph.serverSideErrors.unavailableLicenseInformationErrorMessage', + { + defaultMessage: + 'Graph is unavailable - license information is not available at this time.', + } + ), + }; + } + + const graphFeature = license.getFeature('graph'); + if (!graphFeature.isEnabled) { + return { + showAppLink: false, + enableAppLink: false, + message: i18n.translate('xpack.graph.serverSideErrors.unavailableGraphErrorMessage', { + defaultMessage: 'Graph is unavailable', + }), + }; + } + + const check = license.check('graph', 'platinum'); + + switch (check.state) { + case LICENSE_CHECK_STATE.Expired: + return { + showAppLink: true, + enableAppLink: false, + message: check.message || '', + }; + case LICENSE_CHECK_STATE.Invalid: + case LICENSE_CHECK_STATE.Unavailable: + return { + showAppLink: false, + enableAppLink: false, + message: check.message || '', + }; + case LICENSE_CHECK_STATE.Valid: + return { + showAppLink: true, + enableAppLink: true, + message: '', + }; + default: + return assertNever(check.state); + } +} diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json new file mode 100644 index 000000000000..0d0ddc55a391 --- /dev/null +++ b/x-pack/plugins/graph/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "graph", + "version": "8.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["licensing"], + "optionalPlugins": ["home"] +} diff --git a/x-pack/legacy/plugins/graph/server/index.js b/x-pack/plugins/graph/public/index.ts similarity index 73% rename from x-pack/legacy/plugins/graph/server/index.js rename to x-pack/plugins/graph/public/index.ts index be72e9217655..ac9ca960c0c7 100644 --- a/x-pack/legacy/plugins/graph/server/index.js +++ b/x-pack/plugins/graph/public/index.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { initServer } from './init_server'; +import { GraphPlugin } from './plugin'; + +export const plugin = () => new GraphPlugin(); diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts new file mode 100644 index 000000000000..cb997ff42754 --- /dev/null +++ b/x-pack/plugins/graph/public/plugin.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { CoreSetup, CoreStart } from 'kibana/public'; +import { Plugin } from 'src/core/public'; +import { toggleNavLink } from './services/toggle_nav_link'; +import { LicensingPluginSetup } from '../../licensing/common/types'; +import { checkLicense } from '../common/check_license'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../src/plugins/home/public'; + +export interface GraphPluginSetupDependencies { + licensing: LicensingPluginSetup; + home?: HomePublicPluginSetup; +} + +export class GraphPlugin implements Plugin { + private licensing: LicensingPluginSetup | null = null; + + setup(core: CoreSetup, { licensing, home }: GraphPluginSetupDependencies) { + this.licensing = licensing; + + if (home) { + home.featureCatalogue.register({ + id: 'graph', + title: 'Graph', + description: i18n.translate('xpack.graph.pluginDescription', { + defaultMessage: 'Surface and analyze relevant relationships in your Elasticsearch data.', + }), + icon: 'graphApp', + path: '/app/graph', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }); + } + } + + start(core: CoreStart) { + if (this.licensing === null) { + throw new Error('Start called before setup'); + } + this.licensing.license$.subscribe(license => { + toggleNavLink(checkLicense(license), core.chrome.navLinks); + }); + } + + stop() {} +} diff --git a/x-pack/plugins/graph/public/services/toggle_nav_link.ts b/x-pack/plugins/graph/public/services/toggle_nav_link.ts new file mode 100644 index 000000000000..be917677d311 --- /dev/null +++ b/x-pack/plugins/graph/public/services/toggle_nav_link.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ChromeNavLink, ChromeNavLinks } from 'kibana/public'; +import { GraphLicenseInformation } from '../../common/check_license'; + +type Mutable = { -readonly [P in keyof T]: T[P] }; +type ChromeNavLinkUpdate = Mutable>; + +export function toggleNavLink( + licenseInformation: GraphLicenseInformation, + navLinks: ChromeNavLinks +) { + const navLinkUpdates: ChromeNavLinkUpdate = { + hidden: !licenseInformation.showAppLink, + }; + if (licenseInformation.showAppLink) { + navLinkUpdates.disabled = !licenseInformation.enableAppLink; + navLinkUpdates.tooltip = licenseInformation.message; + } + + navLinks.update('graph', navLinkUpdates); +} diff --git a/x-pack/legacy/plugins/graph/server/routes/index.js b/x-pack/plugins/graph/server/index.ts similarity index 70% rename from x-pack/legacy/plugins/graph/server/routes/index.js rename to x-pack/plugins/graph/server/index.ts index 6201e885fe59..ac9ca960c0c7 100644 --- a/x-pack/legacy/plugins/graph/server/routes/index.js +++ b/x-pack/plugins/graph/server/index.ts @@ -4,5 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { graphExploreRoute } from './graph_explore'; -export { searchProxyRoute } from './search_proxy'; +import { GraphPlugin } from './plugin'; + +export const plugin = () => new GraphPlugin(); diff --git a/x-pack/plugins/graph/server/lib/license_state.ts b/x-pack/plugins/graph/server/lib/license_state.ts new file mode 100644 index 000000000000..1f5744e41534 --- /dev/null +++ b/x-pack/plugins/graph/server/lib/license_state.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { Observable, Subscription } from 'rxjs'; +import { ILicense } from '../../../licensing/common/types'; +import { checkLicense, GraphLicenseInformation } from '../../common/check_license'; + +export class LicenseState { + private licenseInformation: GraphLicenseInformation = checkLicense(undefined); + private subscription: Subscription | null = null; + + private updateInformation(license: ILicense | undefined) { + this.licenseInformation = checkLicense(license); + } + + public start(license$: Observable) { + this.subscription = license$.subscribe(this.updateInformation.bind(this)); + } + + public stop() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + public getLicenseInformation() { + return this.licenseInformation; + } +} + +export function verifyApiAccess(licenseState: LicenseState) { + const licenseCheckResults = licenseState.getLicenseInformation(); + + if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) { + return; + } + + throw Boom.forbidden(licenseCheckResults.message); +} diff --git a/x-pack/plugins/graph/server/plugin.ts b/x-pack/plugins/graph/server/plugin.ts new file mode 100644 index 000000000000..4b5a17707641 --- /dev/null +++ b/x-pack/plugins/graph/server/plugin.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup } from 'src/core/server'; +import { LicensingPluginSetup } from '../../licensing/common/types'; +import { LicenseState } from './lib/license_state'; +import { registerSearchRoute } from './routes/search'; +import { registerExploreRoute } from './routes/explore'; + +export class GraphPlugin implements Plugin { + private licenseState: LicenseState | null = null; + + public async setup(core: CoreSetup, { licensing }: { licensing: LicensingPluginSetup }) { + const licenseState = new LicenseState(); + licenseState.start(licensing.license$); + this.licenseState = licenseState; + + const router = core.http.createRouter(); + registerSearchRoute({ licenseState, router }); + registerExploreRoute({ licenseState, router }); + } + + public start() {} + public stop() { + if (this.licenseState) { + this.licenseState.stop(); + } + } +} diff --git a/x-pack/plugins/graph/server/routes/explore.ts b/x-pack/plugins/graph/server/routes/explore.ts new file mode 100644 index 000000000000..0a5b9f62f12a --- /dev/null +++ b/x-pack/plugins/graph/server/routes/explore.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import Boom from 'boom'; +import { get } from 'lodash'; +import { LicenseState, verifyApiAccess } from '../lib/license_state'; + +export function registerExploreRoute({ + router, + licenseState, +}: { + router: IRouter; + licenseState: LicenseState; +}) { + router.post( + { + path: '/api/graph/graphExplore', + validate: { + body: schema.object({ + index: schema.string(), + query: schema.object({}, { allowUnknowns: true }), + }), + }, + }, + router.handleLegacyErrors( + async ( + { + core: { + elasticsearch: { + dataClient: { callAsCurrentUser: callCluster }, + }, + }, + }, + request, + response + ) => { + verifyApiAccess(licenseState); + try { + return response.ok({ + body: { + resp: await callCluster('transport.request', { + path: '/' + encodeURIComponent(request.body.index) + '/_graph/explore', + body: request.body.query, + method: 'POST', + query: {}, + }), + }, + }); + } catch (error) { + // Extract known reasons for bad choice of field + const relevantCause = get( + error, + 'body.error.root_cause', + [] as Array<{ type: string; reason: string }> + ).find(cause => { + return ( + cause.reason.includes('Fielddata is disabled on text fields') || + cause.reason.includes('No support for examining floating point') || + cause.reason.includes('Sample diversifying key must be a single valued-field') || + cause.reason.includes('Failed to parse query') || + cause.type === 'parsing_exception' + ); + }); + + if (relevantCause) { + throw Boom.badRequest(relevantCause.reason); + } + + throw Boom.boomify(error); + } + } + ) + ); +} diff --git a/x-pack/plugins/graph/server/routes/search.ts b/x-pack/plugins/graph/server/routes/search.ts new file mode 100644 index 000000000000..400cdc4e82b6 --- /dev/null +++ b/x-pack/plugins/graph/server/routes/search.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import Boom from 'boom'; +import { LicenseState, verifyApiAccess } from '../lib/license_state'; + +export function registerSearchRoute({ + router, + licenseState, +}: { + router: IRouter; + licenseState: LicenseState; +}) { + router.post( + { + path: '/api/graph/searchProxy', + validate: { + body: schema.object({ + index: schema.string(), + body: schema.object({}, { allowUnknowns: true }), + }), + }, + }, + router.handleLegacyErrors( + async ( + { + core: { + uiSettings: { client: uiSettings }, + elasticsearch: { + dataClient: { callAsCurrentUser: callCluster }, + }, + }, + }, + request, + response + ) => { + verifyApiAccess(licenseState); + const includeFrozen = await uiSettings.get('search:includeFrozen'); + try { + return response.ok({ + body: { + resp: await callCluster('search', { + index: request.body.index, + body: request.body.body, + rest_total_hits_as_int: true, + ignore_throttled: !includeFrozen, + }), + }, + }); + } catch (error) { + throw Boom.boomify(error, { statusCode: error.statusCode || 500 }); + } + } + ) + ); +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d6e72583e4d2..fce656dd42e8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5170,10 +5170,8 @@ "xpack.graph.savedWorkspaces.graphWorkspacesLabel": "グラフワークスペース", "xpack.graph.saveWorkspace.successNotification.noDataSavedText": "構成が保存されましたが、データは保存されませんでした", "xpack.graph.saveWorkspace.successNotificationTitle": "「{workspaceTitle}」が保存されました", - "xpack.graph.serverSideErrors.expiredLicenseErrorMessage": "グラフを利用できません。ライセンスが期限切れです。", "xpack.graph.serverSideErrors.unavailableGraphErrorMessage": "グラフを利用できません", "xpack.graph.serverSideErrors.unavailableLicenseInformationErrorMessage": "グラフを利用できません。現在ライセンス情報が利用できません。", - "xpack.graph.serverSideErrors.wrongLicenseTypeErrorMessage": "現在の {licenseType} ライセンスではグラフを利用できません。ライセンスをアップグレードしてください。", "xpack.graph.settings.advancedSettings.certaintyInputHelpText": "関連用語が登録される前に証拠として必要なドキュメントの最低数です", "xpack.graph.settings.advancedSettings.certaintyInputLabel": "確実性", "xpack.graph.settings.advancedSettings.diversityFieldInputHelpText1": "ドキュメントのサンプルが 1 種類に偏らないように、バイアスの原因の認識に役立つフィールドを選択してください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0f0a45ea1941..cd1835531795 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5172,10 +5172,8 @@ "xpack.graph.savedWorkspaces.graphWorkspacesLabel": "Graph 工作空间", "xpack.graph.saveWorkspace.successNotification.noDataSavedText": "配置会被保存,但不保存数据", "xpack.graph.saveWorkspace.successNotificationTitle": "已保存“{workspaceTitle}”", - "xpack.graph.serverSideErrors.expiredLicenseErrorMessage": "Graph 不可用 - 许可已过期。", "xpack.graph.serverSideErrors.unavailableGraphErrorMessage": "Graph 不可用", "xpack.graph.serverSideErrors.unavailableLicenseInformationErrorMessage": "Graph 不可用 - 许可信息当前不可用。", - "xpack.graph.serverSideErrors.wrongLicenseTypeErrorMessage": "当前{licenseType}许可的 Graph 不可用。请升级您的许可。", "xpack.graph.settings.advancedSettings.certaintyInputHelpText": "在引入相关字词之前作为证据所需的最小文档数量。", "xpack.graph.settings.advancedSettings.certaintyInputLabel": "确定性", "xpack.graph.settings.advancedSettings.diversityFieldInputHelpText1": "为避免文档示例过于雷同,请选取有助于识别偏差来源的字段。",