From fba34eb3b5e23ced1e6d246a59db4d606210579f Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 13 Jan 2020 14:13:03 +0100 Subject: [PATCH 01/34] Initial commit Taking the grok debugger as skeleton and transforming it into a basic Painless Playground --- .../public/code_editor/code_editor.tsx | 2 +- x-pack/index.js | 2 + .../common/constants/editor.ts | 12 ++ .../common/constants/index.ts | 9 ++ .../common/constants/plugin.ts | 9 ++ .../common/constants/routes.ts | 9 ++ .../plugins/painless_playground/index.ts | 32 ++++ .../public/components/painless_playground.tsx | 144 ++++++++++++++++++ .../painless_playground/public/register.ts | 37 +++++ .../public/register_feature.ts | 28 ++++ .../painless_playground/public/render_app.tsx | 27 ++++ .../services/painless_playground_service.ts | 19 +++ .../call_with_request_factory.ts | 18 +++ .../lib/call_with_request_factory/index.ts | 7 + .../check_license/__tests__/check_license.js | 90 +++++++++++ .../server/lib/check_license/check_license.ts | 46 ++++++ .../server/lib/check_license/index.ts | 7 + .../server/lib/error_wrappers/index.ts | 7 + .../lib/error_wrappers/wrap_es_error.ts | 18 +++ .../__tests__/license_pre_routing_factory.js | 64 ++++++++ .../lib/license_pre_routing_factory/index.ts | 7 + .../license_pre_routing_factory.ts | 24 +++ .../lib/register_license_checker/index.ts | 7 + .../register_license_checker.ts | 21 +++ ...ster_painless_playground_simulate_route.ts | 32 ++++ 25 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/editor.ts create mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts create mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/routes.ts create mode 100644 x-pack/legacy/plugins/painless_playground/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/register.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/register_feature.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/render_app.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts create mode 100644 x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 0ae77995c0502..a1636c7cc2b4e 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -48,7 +48,7 @@ export interface Props { value: string; /** Function invoked when text in editor is changed */ - onChange: (value: string) => void; + onChange?: (value: string) => void; /** * Options for the Monaco Code Editor diff --git a/x-pack/index.js b/x-pack/index.js index 56547f89b1e90..986a1efbe3625 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -14,6 +14,7 @@ import { ml } from './legacy/plugins/ml'; import { tilemap } from './legacy/plugins/tilemap'; import { watcher } from './legacy/plugins/watcher'; import { grokdebugger } from './legacy/plugins/grokdebugger'; +import { painlessPlayground } from './legacy/plugins/painless_playground'; import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { logstash } from './legacy/plugins/logstash'; import { beats } from './legacy/plugins/beats_management'; @@ -56,6 +57,7 @@ module.exports = function(kibana) { tilemap(kibana), watcher(kibana), grokdebugger(kibana), + painlessPlayground(kibana), dashboardMode(kibana), logstash(kibana), beats(kibana), diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts b/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts new file mode 100644 index 0000000000000..7df2a6dffb268 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +export const EDITOR = { + PATTERN_MIN_LINES: 3, + PATTERN_MAX_LINES: 10, + SAMPLE_DATA_MIN_LINES: 3, + SAMPLE_DATA_MAX_LINES: 100, +}; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/index.ts b/x-pack/legacy/plugins/painless_playground/common/constants/index.ts new file mode 100644 index 0000000000000..12e440d7ed858 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/common/constants/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { ROUTES } from './routes'; +export { PLUGIN } from './plugin'; +export { EDITOR } from './editor'; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts b/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts new file mode 100644 index 0000000000000..57ebb6006207f --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts @@ -0,0 +1,9 @@ +/* + * 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 PLUGIN = { + ID: 'painlessPlayground', +}; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/routes.ts b/x-pack/legacy/plugins/painless_playground/common/constants/routes.ts new file mode 100644 index 0000000000000..ae28e758add5b --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/common/constants/routes.ts @@ -0,0 +1,9 @@ +/* + * 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 ROUTES = { + API_ROOT: '/api/painless_playground', +}; diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts new file mode 100644 index 0000000000000..b9c01a2b8b369 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/index.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 { resolve } from 'path'; +import { PLUGIN } from './common/constants'; + +import { registerLicenseChecker } from './server/lib/register_license_checker'; +import { registerPainlessPlaygroundSimulateRoute } from './server/routes/api/register_painless_playground_simulate_route'; + +export const painlessPlayground = (kibana: any) => + new kibana.Plugin({ + id: PLUGIN.ID, + publicDir: resolve(__dirname, 'public'), + require: ['kibana', 'elasticsearch', 'xpack_main'], + configPrefix: 'xpack.painless_playground', + config(Joi: any) { + return Joi.object({ + enabled: Joi.boolean().default(true), + }).default(); + }, + uiExports: { + devTools: [resolve(__dirname, 'public/register')], + home: [resolve(__dirname, 'public/register_feature')], + }, + init: (server: any) => { + registerLicenseChecker(server); + registerPainlessPlaygroundSimulateRoute(server); + }, + }); diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx new file mode 100644 index 0000000000000..9402657db6a66 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -0,0 +1,144 @@ +/* + * 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 React from 'react'; +import { + EuiForm, + EuiButton, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiSpacer, + EuiFormRow, + EuiPanel, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; + +interface Props { + service: any; +} + +interface State { + code: string; + output: any; +} +export class PainlessPlayground extends React.Component { + state = { + code: '', + output: '', + }; + + onPatternChange = (code: string) => { + this.setState({ code }); + }; + + submit = async () => { + try { + const payload = { + script: { + source: this.state.code, + }, + }; + const response = await this.props.service.simulate(payload); + this.setState({ + output: response, + }); + } catch (e) { + this.setState({ + output: e, + }); + } + }; + + onSimulateClick = () => { + this.submit(); + }; + + isSimulateDisabled = () => { + return this.state.code.trim() === ''; + }; + + render() { + return ( + + + + + + + } + fullWidth + data-test-subj="codeInput" + > + + + + + + + + + + + } + fullWidth + data-test-subj="output" + > + + + + + + + + + + ); + } +} diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts new file mode 100644 index 0000000000000..b8d77c0b91e48 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -0,0 +1,37 @@ +/* + * 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 { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; +import { npSetup, npStart } from 'ui/new_platform'; + +// !xpackInfo.get('features.painless_playground.enableLink', false) + +npSetup.plugins.dev_tools.register({ + order: 7, + title: i18n.translate('xpack.painlessPlayground.displayName', { + defaultMessage: 'Painless Playground', + }), + id: 'painless_playground', + enableRouting: false, + disabled: false, + tooltipContent: xpackInfo.get('features.painless_playground.message'), + async mount(context, { element }) { + /** + const licenseCheck = { + showPage: xpackInfo.get('features.painless_playground.enableLink'), + message: xpackInfo.get('features.painless_playground.message'), + }; + + if (!licenseCheck.showPage) { + npStart.core.notifications.toasts.addDanger(licenseCheck.message); + window.location.hash = '/dev_tools'; + return () => {}; + }**/ + const { renderApp } = await import('./render_app'); + return renderApp(element, npStart); + }, +}); diff --git a/x-pack/legacy/plugins/painless_playground/public/register_feature.ts b/x-pack/legacy/plugins/painless_playground/public/register_feature.ts new file mode 100644 index 0000000000000..d4006ce23cb18 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/register_feature.ts @@ -0,0 +1,28 @@ +/* + * 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: 'painless_playground', + title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { + defaultMessage: 'Painless Playground', + }), + description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { + defaultMessage: 'Simulate and debug painless code', + }), + icon: '', + path: '/app/kibana#/dev_tools/painless_playground', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }; +}); diff --git a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx new file mode 100644 index 0000000000000..1aa9c052de8a0 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx @@ -0,0 +1,27 @@ +/* + * 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 React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { PainlessPlayground } from './components/painless_playground'; +import { PainlessPlaygroundService } from './services/painless_playground_service'; +import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public'; + +export function renderApp(element: any, npStart: any) { + const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + uiSettings: npStart.core.uiSettings, + }); + render( + + + + + , + element + ); + return () => unmountComponentAtNode(element); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts b/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts new file mode 100644 index 0000000000000..95593c3d723d6 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ROUTES } from '../../common/constants'; + +export class PainlessPlaygroundService { + constructor(private http: any) { + this.http = http; + } + + simulate(payload: unknown) { + return this.http.post(`${ROUTES.API_ROOT}/simulate`, { + body: JSON.stringify(payload), + }); + } +} diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts new file mode 100644 index 0000000000000..7359a831994f9 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts @@ -0,0 +1,18 @@ +/* + * 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 { once } from 'lodash'; + +const callWithRequest = once(server => { + const cluster = server.plugins.elasticsearch.getCluster('data'); + return cluster.callWithRequest; +}); + +export const callWithRequestFactory = (server, request) => { + return (...args) => { + return callWithRequest(server)(request, ...args); + }; +}; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts new file mode 100644 index 0000000000000..787814d87dff9 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { callWithRequestFactory } from './call_with_request_factory'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js new file mode 100644 index 0000000000000..7e32d68a67ece --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js @@ -0,0 +1,90 @@ +/* + * 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 { checkLicense } from '../check_license'; + +describe('check_license', function() { + let mockLicenseInfo; + beforeEach(() => (mockLicenseInfo = {})); + + describe('license information is undefined', () => { + beforeEach(() => (mockLicenseInfo = undefined)); + + it('should set enableLink to false', () => { + expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); + }); + + it('should set enableAPIRoute to false', () => { + expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is not available', () => { + beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); + + it('should set enableLink to false', () => { + expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); + }); + + it('should set enableAPIRoute to false', () => { + expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is available', () => { + beforeEach( + () => + (mockLicenseInfo = { + isAvailable: () => true, + license: { + getType: () => 'foobar', + }, + }) + ); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set enableLink to true', () => { + expect(checkLicense(mockLicenseInfo).enableLink).to.be(true); + }); + + it('should set enableAPIRoute to true', () => { + expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(true); + }); + + it('should NOT set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set enableLink to false', () => { + expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); + }); + + it('should set enableAPIRoute to false', () => { + expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts new file mode 100644 index 0000000000000..a4708a3a3a5e3 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts @@ -0,0 +1,46 @@ +/* + * 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: any) { + // If, for some reason, we cannot get the license information + // from Elasticsearch, assume worst case and disable the Watcher UI + if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { + return { + enableLink: false, + enableAPIRoute: false, + message: i18n.translate('xpack.painlessPlayground.unavailableLicenseInformationMessage', { + defaultMessage: + 'You cannot use the Painless Playground because license information is not available at this time.', + }), + }; + } + + const isLicenseActive = xpackLicenseInfo.license.isActive(); + const licenseType = xpackLicenseInfo.license.getType(); + + // License is not valid + if (!isLicenseActive) { + return { + enableLink: false, + enableAPIRoute: false, + message: i18n.translate('xpack.painlessPlayground.licenseHasExpiredMessage', { + defaultMessage: + 'You cannot use the Painless Playground because your {licenseType} license has expired.', + values: { + licenseType, + }, + }), + }; + } + + // License is valid and active + return { + enableLink: true, + enableAPIRoute: true, + }; +} diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts new file mode 100644 index 0000000000000..f2c070fd44b6e --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts @@ -0,0 +1,7 @@ +/* + * 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'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts new file mode 100644 index 0000000000000..3756b0c74fb10 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { wrapEsError } from './wrap_es_error'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts new file mode 100644 index 0000000000000..e48ef500309a7 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts @@ -0,0 +1,18 @@ +/* + * 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'; + +/** + * Wraps ES errors into a Boom error response and returns it + * This also handles the permissions issue gracefully + * + * @param err Object ES error + * @return Object Boom error response + */ +export function wrapEsError(err: any) { + return Boom.boomify(err, { statusCode: err.statusCode }); +} diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js new file mode 100644 index 0000000000000..854d919b1173d --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -0,0 +1,64 @@ +/* + * 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 Boom from 'boom'; +import { licensePreRoutingFactory } from '../license_pre_routing_factory'; + +describe('license_pre_routing_factory', () => { + describe('#painlessPlaygroundFeaturePreRoutingFactory', () => { + let mockServer; + let mockLicenseCheckResults; + + beforeEach(() => { + mockServer = { + plugins: { + xpack_main: { + info: { + feature: () => ({ + getLicenseCheckResults: () => mockLicenseCheckResults, + }), + }, + }, + }, + }; + }); + + describe('isAvailable is false', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: false, + }; + }); + + it('replies with 403', async () => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + expect(() => licensePreRouting(stubRequest)).to.throwException(response => { + expect(response).to.be.an(Error); + expect(response.isBoom).to.be(true); + expect(response.output.statusCode).to.be(403); + }); + }); + }); + + describe('isAvailable is true', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: true, + }; + }); + + it('replies with forbidden', async () => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + expect(() => licensePreRouting(stubRequest)).to.throwException(response => { + expect(response).to.eql(Boom.forbidden()); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts new file mode 100644 index 0000000000000..0743e443955f4 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts new file mode 100644 index 0000000000000..88c844de3e2ca --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts @@ -0,0 +1,24 @@ +/* + * 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 { PLUGIN } from '../../../common/constants'; + +export const licensePreRoutingFactory = server => { + const xpackMainPlugin = server.plugins.xpack_main; + + // License checking and enable/disable logic + function licensePreRouting() { + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + if (!licenseCheckResults.enableAPIRoute) { + throw Boom.forbidden(licenseCheckResults.message); + } + + return null; + } + + return licensePreRouting; +}; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts new file mode 100644 index 0000000000000..7b0f97c38d129 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts b/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts new file mode 100644 index 0000000000000..bcb7a610b6294 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +// @ts-ignore +import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; +import { checkLicense } from '../check_license'; +import { PLUGIN } from '../../../common/constants'; + +export function registerLicenseChecker(server: any) { + const xpackMainPlugin = server.plugins.xpack_main; + const plugin = server.plugins[PLUGIN.ID]; + + mirrorPluginStatus(xpackMainPlugin, plugin); + xpackMainPlugin.status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); + }); +} diff --git a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts new file mode 100644 index 0000000000000..8cd8ad93223a8 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.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 { wrapEsError } from '../../lib/error_wrappers'; +import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; + +function executeScript(callWithRequest: any, painlessJson: any) { + return callWithRequest('scriptsPainlessExecute', { + body: painlessJson, + }); +} + +export function registerPainlessPlaygroundSimulateRoute(server: any) { + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/painless_playground/simulate', + method: 'POST', + handler: (request: any) => { + const callWithRequest = callWithRequestFactory(server, request); + + return executeScript(callWithRequest, request.payload).catch((e: any) => wrapEsError(e)); + }, + config: { + pre: [licensePreRouting], + }, + }); +} From 4cb4e5e524b806d556bc1cafb3402b53bf9cec67 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 13 Jan 2020 15:14:27 +0100 Subject: [PATCH 02/34] Migrate feature registration to NP --- .../public/register_feature.ts | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/register_feature.ts b/x-pack/legacy/plugins/painless_playground/public/register_feature.ts index d4006ce23cb18..c39aad88e8caa 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register_feature.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register_feature.ts @@ -4,25 +4,20 @@ * 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'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'painless_playground', - title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { - defaultMessage: 'Painless Playground', - }), - description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { - defaultMessage: 'Simulate and debug painless code', - }), - icon: '', - path: '/app/kibana#/dev_tools/painless_playground', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }; +npSetup.plugins.home.featureCatalogue.register({ + id: 'painless_playground', + title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { + defaultMessage: 'Painless Playground', + }), + description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { + defaultMessage: 'Simulate and debug painless code', + }), + icon: '', + path: '/app/kibana#/dev_tools/painless_playground', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, }); From c6e67127d9907a62b2d559fd2bbb707b14fafdc0 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 13 Jan 2020 17:45:39 +0100 Subject: [PATCH 03/34] Simplify code --- .../painless_playground/public/register.ts | 3 +-- .../call_with_request_factory.ts | 18 ---------------- .../lib/call_with_request_factory/index.ts | 7 ------- .../license_pre_routing_factory.ts | 3 ++- ...ster_painless_playground_simulate_route.ts | 21 ++++++++----------- 5 files changed, 12 insertions(+), 40 deletions(-) delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index b8d77c0b91e48..6cb55d736b96f 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -5,11 +5,10 @@ */ import { i18n } from '@kbn/i18n'; +// @ts-ignore import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { npSetup, npStart } from 'ui/new_platform'; -// !xpackInfo.get('features.painless_playground.enableLink', false) - npSetup.plugins.dev_tools.register({ order: 7, title: i18n.translate('xpack.painlessPlayground.displayName', { diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts deleted file mode 100644 index 7359a831994f9..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/call_with_request_factory.ts +++ /dev/null @@ -1,18 +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 { once } from 'lodash'; - -const callWithRequest = once(server => { - const cluster = server.plugins.elasticsearch.getCluster('data'); - return cluster.callWithRequest; -}); - -export const callWithRequestFactory = (server, request) => { - return (...args) => { - return callWithRequest(server)(request, ...args); - }; -}; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts deleted file mode 100644 index 787814d87dff9..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/call_with_request_factory/index.ts +++ /dev/null @@ -1,7 +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 { callWithRequestFactory } from './call_with_request_factory'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts index 88c844de3e2ca..4e183262f9d8f 100644 --- a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts @@ -5,9 +5,10 @@ */ import Boom from 'boom'; +import { ServerFacade } from '../../../types'; import { PLUGIN } from '../../../common/constants'; -export const licensePreRoutingFactory = server => { +export const licensePreRoutingFactory = (server: ServerFacade) => { const xpackMainPlugin = server.plugins.xpack_main; // License checking and enable/disable logic diff --git a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts index 8cd8ad93223a8..c8fd0915e774a 100644 --- a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts @@ -5,25 +5,22 @@ */ import { wrapEsError } from '../../lib/error_wrappers'; -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; +import { ServerFacade, RequestFacade } from '../../../types'; -function executeScript(callWithRequest: any, painlessJson: any) { - return callWithRequest('scriptsPainlessExecute', { - body: painlessJson, - }); -} - -export function registerPainlessPlaygroundSimulateRoute(server: any) { +export function registerPainlessPlaygroundSimulateRoute(server: ServerFacade) { const licensePreRouting = licensePreRoutingFactory(server); server.route({ path: '/api/painless_playground/simulate', method: 'POST', - handler: (request: any) => { - const callWithRequest = callWithRequestFactory(server, request); - - return executeScript(callWithRequest, request.payload).catch((e: any) => wrapEsError(e)); + handler: (request: RequestFacade) => { + const cluster = server.plugins.elasticsearch.getCluster('data'); + return cluster + .callWithRequest(request, 'scriptsPainlessExecute', { + body: request.payload, + }) + .catch((e: any) => wrapEsError(e)); }, config: { pre: [licensePreRouting], From 27fd28eb9a4b92a576280a993e7638842f96e461 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 14 Jan 2020 13:36:18 +0100 Subject: [PATCH 04/34] Add syntax highlighting --- .../common/constants/editor.ts | 12 -- .../common/constants/index.ts | 1 - .../common/constants/plugin.ts | 2 +- .../public/components/painless_playground.tsx | 196 ++++++++++-------- .../painless_playground/public/register.ts | 2 + .../public/register_painless.ts | 182 ++++++++++++++++ 6 files changed, 294 insertions(+), 101 deletions(-) delete mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/editor.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/register_painless.ts diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts b/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts deleted file mode 100644 index 7df2a6dffb268..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/common/constants/editor.ts +++ /dev/null @@ -1,12 +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 EDITOR = { - PATTERN_MIN_LINES: 3, - PATTERN_MAX_LINES: 10, - SAMPLE_DATA_MIN_LINES: 3, - SAMPLE_DATA_MAX_LINES: 100, -}; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/index.ts b/x-pack/legacy/plugins/painless_playground/common/constants/index.ts index 12e440d7ed858..85b819624b70f 100644 --- a/x-pack/legacy/plugins/painless_playground/common/constants/index.ts +++ b/x-pack/legacy/plugins/painless_playground/common/constants/index.ts @@ -6,4 +6,3 @@ export { ROUTES } from './routes'; export { PLUGIN } from './plugin'; -export { EDITOR } from './editor'; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts b/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts index 57ebb6006207f..07767dd54bd38 100644 --- a/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts +++ b/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts @@ -5,5 +5,5 @@ */ export const PLUGIN = { - ID: 'painlessPlayground', + ID: 'painless_playground', }; diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 9402657db6a66..92ac7124e0213 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiForm, EuiButton, @@ -13,7 +13,6 @@ import { EuiPageContentBody, EuiSpacer, EuiFormRow, - EuiPanel, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; @@ -24,107 +23,105 @@ interface Props { interface State { code: string; - output: any; + request?: string; + response?: string; } -export class PainlessPlayground extends React.Component { - state = { +export function PainlessPlayground(props: Props) { + const [state, setState] = useState({ code: '', - output: '', - }; - - onPatternChange = (code: string) => { - this.setState({ code }); - }; + request: '', + response: '', + }); - submit = async () => { + const submit = async () => { + const request = { + script: { + source: state.code, + }, + }; try { - const payload = { - script: { - source: this.state.code, - }, - }; - const response = await this.props.service.simulate(payload); - this.setState({ - output: response, + const response = await props.service.simulate(request); + setState({ + code: state.code, + response: JSON.stringify(response, null, 2), + request: JSON.stringify(request, null, 2), }); } catch (e) { - this.setState({ - output: e, + setState({ + code: state.code, + response: JSON.stringify(e, null, 2), + request: JSON.stringify(request, null, 2), }); } }; - onSimulateClick = () => { - this.submit(); - }; - - isSimulateDisabled = () => { - return this.state.code.trim() === ''; + const onSimulateClick = () => { + submit(); }; - - render() { - return ( - - - - - - - } - fullWidth - data-test-subj="codeInput" - > - - - - - - + return ( + + + + + + + } + fullWidth + data-test-subj="codeInput" + > +
+ setState({ code })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} /> - - +
+
+ + + + + + + {state.request && ( } fullWidth - data-test-subj="output" + data-test-subj="request" > - +
{ }, }} /> - +
-
-
-
-
-
- ); - } + )} + + + } + fullWidth + data-test-subj="response" + > +
+ +
+
+
+
+
+
+
+ ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index 6cb55d736b96f..01789fdc55cfd 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { npSetup, npStart } from 'ui/new_platform'; +import { registerPainless } from './register_painless'; npSetup.plugins.dev_tools.register({ order: 7, @@ -19,6 +20,7 @@ npSetup.plugins.dev_tools.register({ disabled: false, tooltipContent: xpackInfo.get('features.painless_playground.message'), async mount(context, { element }) { + registerPainless(); /** const licenseCheck = { showPage: xpackInfo.get('features.painless_playground.enableLink'), diff --git a/x-pack/legacy/plugins/painless_playground/public/register_painless.ts b/x-pack/legacy/plugins/painless_playground/public/register_painless.ts new file mode 100644 index 0000000000000..700e6d93dad2a --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/register_painless.ts @@ -0,0 +1,182 @@ +/* + * 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 * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +export const LANGUAGE_ID = 'painless'; + +/** + * Extends the default type for a Monarch language so we can use + * attribute references (like @keywords to reference the keywords list) + * in the defined tokenizer + */ +interface Language extends monaco.languages.IMonarchLanguage { + default: string; + brackets: any; + keywords: string[]; + symbols: RegExp; + escapes: RegExp; + digits: RegExp; + primitives: string[]; + octaldigits: RegExp; + binarydigits: RegExp; + constants: string[]; + operators: string[]; +} + +function getPainlessLanguage() { + return { + default: '', + // painless does not use < >, so we define our own + brackets: [ + ['{', '}', 'delimiter.curly'], + ['[', ']', 'delimiter.square'], + ['(', ')', 'delimiter.parenthesis'], + ], + keywords: [ + 'if', + 'in', + 'else', + 'while', + 'do', + 'for', + 'continue', + 'break', + 'return', + 'new', + 'try', + 'catch', + 'throw', + 'this', + 'instanceof', + ], + primitives: ['void', 'boolean', 'byte', 'short', 'char', 'int', 'long', 'float', 'double'], + constants: ['true', 'false'], + operators: [ + '=', + '>', + '<', + '!', + '~', + '?', + '?:', + '?.', + ':', + '==', + '===', + '<=', + '>=', + '!=', + '!==', + '&&', + '||', + '++', + '--', + '+', + '-', + '*', + '/', + '&', + '|', + '^', + '%', + '<<', + '>>', + '>>>', + '+=', + '-=', + '*=', + '/=', + '&=', + '|=', + '^=', + '%=', + '<<=', + '>>=', + '>>>=', + '->', + '::', + '=~', + '==~', + ], + symbols: /[=> Date: Tue, 14 Jan 2020 18:03:07 +0100 Subject: [PATCH 05/34] Improve error response from server --- .../public/components/painless_playground.tsx | 60 ++++++++++--------- .../services/painless_playground_service.ts | 12 ++-- ...ster_painless_playground_simulate_route.ts | 4 +- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 92ac7124e0213..c0ae7ae221b30 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -25,6 +25,7 @@ interface State { code: string; request?: string; response?: string; + responseObj: Record; } export function PainlessPlayground(props: Props) { const [state, setState] = useState({ @@ -43,14 +44,16 @@ export function PainlessPlayground(props: Props) { const response = await props.service.simulate(request); setState({ code: state.code, - response: JSON.stringify(response, null, 2), request: JSON.stringify(request, null, 2), + response: JSON.stringify(response, null, 2), + responseObj: response, }); } catch (e) { setState({ code: state.code, response: JSON.stringify(e, null, 2), request: JSON.stringify(request, null, 2), + responseObj: e, }); } }; @@ -106,6 +109,34 @@ export function PainlessPlayground(props: Props) { + + } + fullWidth + data-test-subj="response" + > +
+ {state.responseObject?.body?.error ? ( +
{state.responseObject?.body?.error}
+ ) : ( + + )} +
+
{state.request && ( )} - - - } - fullWidth - data-test-subj="response" - > -
- -
-
diff --git a/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts b/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts index 95593c3d723d6..cf633dbc468d0 100644 --- a/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts +++ b/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts @@ -11,9 +11,13 @@ export class PainlessPlaygroundService { this.http = http; } - simulate(payload: unknown) { - return this.http.post(`${ROUTES.API_ROOT}/simulate`, { - body: JSON.stringify(payload), - }); + async simulate(payload: unknown) { + try { + return await this.http.post(`${ROUTES.API_ROOT}/simulate`, { + body: JSON.stringify(payload), + }); + } catch (e) { + return e; + } } } diff --git a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts index c8fd0915e774a..84047ed55431b 100644 --- a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts @@ -20,7 +20,9 @@ export function registerPainlessPlaygroundSimulateRoute(server: ServerFacade) { .callWithRequest(request, 'scriptsPainlessExecute', { body: request.payload, }) - .catch((e: any) => wrapEsError(e)); + .catch((e: any) => { + return e.body; + }); }, config: { pre: [licensePreRouting], From 032b30c15315102e424ee5afc1d34ee9c24a892b Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 15 Jan 2020 08:43:41 +0100 Subject: [PATCH 06/34] Simplify code --- .../{constants/routes.ts => constants.ts} | 4 + .../common/constants/index.ts | 8 -- .../common/constants/plugin.ts | 9 -- .../plugins/painless_playground/index.ts | 7 +- .../public/components/painless_playground.tsx | 9 +- .../public/lib/execute_code.ts | 17 ++++ .../painless_playground/public/register.ts | 15 ++++ .../public/register_feature.ts | 23 ----- .../painless_playground/public/render_app.tsx | 4 +- .../services/painless_playground_service.ts | 23 ----- .../lib/{check_license => }/check_license.ts | 0 .../check_license/__tests__/check_license.js | 90 ------------------- .../server/lib/check_license/index.ts | 7 -- .../server/lib/error_wrappers/index.ts | 7 -- .../lib/error_wrappers/wrap_es_error.ts | 18 ---- .../license_pre_routing_factory.ts | 4 +- .../__tests__/license_pre_routing_factory.js | 64 ------------- .../lib/license_pre_routing_factory/index.ts | 7 -- .../lib/register_license_checker/index.ts | 7 -- .../register_license_checker.ts | 6 +- ...te_route.ts => register_simulate_route.ts} | 7 +- 21 files changed, 54 insertions(+), 282 deletions(-) rename x-pack/legacy/plugins/painless_playground/common/{constants/routes.ts => constants.ts} (84%) delete mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/index.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/public/register_feature.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts rename x-pack/legacy/plugins/painless_playground/server/lib/{check_license => }/check_license.ts (100%) delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts rename x-pack/legacy/plugins/painless_playground/server/lib/{license_pre_routing_factory => }/license_pre_routing_factory.ts (87%) delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts delete mode 100644 x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts rename x-pack/legacy/plugins/painless_playground/server/{lib/register_license_checker => }/register_license_checker.ts (80%) rename x-pack/legacy/plugins/painless_playground/server/{routes/api/register_painless_playground_simulate_route.ts => register_simulate_route.ts} (73%) diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/routes.ts b/x-pack/legacy/plugins/painless_playground/common/constants.ts similarity index 84% rename from x-pack/legacy/plugins/painless_playground/common/constants/routes.ts rename to x-pack/legacy/plugins/painless_playground/common/constants.ts index ae28e758add5b..f315aaab343f1 100644 --- a/x-pack/legacy/plugins/painless_playground/common/constants/routes.ts +++ b/x-pack/legacy/plugins/painless_playground/common/constants.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +export const PLUGIN = { + ID: 'painless_playground', +}; + export const ROUTES = { API_ROOT: '/api/painless_playground', }; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/index.ts b/x-pack/legacy/plugins/painless_playground/common/constants/index.ts deleted file mode 100644 index 85b819624b70f..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/common/constants/index.ts +++ /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 { ROUTES } from './routes'; -export { PLUGIN } from './plugin'; diff --git a/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts b/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts deleted file mode 100644 index 07767dd54bd38..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/common/constants/plugin.ts +++ /dev/null @@ -1,9 +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 PLUGIN = { - ID: 'painless_playground', -}; diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index b9c01a2b8b369..30ca2847f5a25 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -7,8 +7,8 @@ import { resolve } from 'path'; import { PLUGIN } from './common/constants'; -import { registerLicenseChecker } from './server/lib/register_license_checker'; -import { registerPainlessPlaygroundSimulateRoute } from './server/routes/api/register_painless_playground_simulate_route'; +import { registerLicenseChecker } from './server/register_license_checker'; +import { registerSimulateRoute } from './server/register_simulate_route'; export const painlessPlayground = (kibana: any) => new kibana.Plugin({ @@ -23,10 +23,9 @@ export const painlessPlayground = (kibana: any) => }, uiExports: { devTools: [resolve(__dirname, 'public/register')], - home: [resolve(__dirname, 'public/register_feature')], }, init: (server: any) => { registerLicenseChecker(server); - registerPainlessPlaygroundSimulateRoute(server); + registerSimulateRoute(server); }, }); diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index c0ae7ae221b30..7114c4b3beb41 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -18,20 +18,21 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; interface Props { - service: any; + executeCode: (payload: Record) => Promise; } interface State { code: string; request?: string; response?: string; - responseObj: Record; + responseObj: Record | null; } export function PainlessPlayground(props: Props) { const [state, setState] = useState({ code: '', request: '', response: '', + responseObj: null, }); const submit = async () => { @@ -41,7 +42,7 @@ export function PainlessPlayground(props: Props) { }, }; try { - const response = await props.service.simulate(request); + const response = await props.executeCode(request); setState({ code: state.code, request: JSON.stringify(request, null, 2), @@ -112,7 +113,7 @@ export function PainlessPlayground(props: Props) { } diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts new file mode 100644 index 0000000000000..6f345a1e7ea06 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts @@ -0,0 +1,17 @@ +/* + * 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 { ROUTES } from '../../common/constants'; + +export async function executeCode(http: any, payload: Record) { + try { + return await http.post(`${ROUTES.API_ROOT}/simulate`, { + body: JSON.stringify(payload), + }); + } catch (e) { + return e; + } +} diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index 01789fdc55cfd..ed76f46ee8489 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -9,6 +9,21 @@ import { i18n } from '@kbn/i18n'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { npSetup, npStart } from 'ui/new_platform'; import { registerPainless } from './register_painless'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +npSetup.plugins.home.featureCatalogue.register({ + id: 'painless_playground', + title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { + defaultMessage: 'Painless Playground', + }), + description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { + defaultMessage: 'Simulate and debug painless code', + }), + icon: '', + path: '/app/kibana#/dev_tools/painless_playground', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, +}); npSetup.plugins.dev_tools.register({ order: 7, diff --git a/x-pack/legacy/plugins/painless_playground/public/register_feature.ts b/x-pack/legacy/plugins/painless_playground/public/register_feature.ts deleted file mode 100644 index c39aad88e8caa..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/public/register_feature.ts +++ /dev/null @@ -1,23 +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'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -npSetup.plugins.home.featureCatalogue.register({ - id: 'painless_playground', - title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { - defaultMessage: 'Painless Playground', - }), - description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { - defaultMessage: 'Simulate and debug painless code', - }), - icon: '', - path: '/app/kibana#/dev_tools/painless_playground', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, -}); diff --git a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx index 1aa9c052de8a0..d58c8f09c869b 100644 --- a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { PainlessPlayground } from './components/painless_playground'; -import { PainlessPlaygroundService } from './services/painless_playground_service'; import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public'; +import { executeCode } from './lib/execute_code'; export function renderApp(element: any, npStart: any) { const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ @@ -18,7 +18,7 @@ export function renderApp(element: any, npStart: any) { render( - + executeCode(npStart.core.http, payload)} /> , element diff --git a/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts b/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts deleted file mode 100644 index cf633dbc468d0..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/public/services/painless_playground_service.ts +++ /dev/null @@ -1,23 +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 { ROUTES } from '../../common/constants'; - -export class PainlessPlaygroundService { - constructor(private http: any) { - this.http = http; - } - - async simulate(payload: unknown) { - try { - return await this.http.post(`${ROUTES.API_ROOT}/simulate`, { - body: JSON.stringify(payload), - }); - } catch (e) { - return e; - } - } -} diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts b/x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts similarity index 100% rename from x-pack/legacy/plugins/painless_playground/server/lib/check_license/check_license.ts rename to x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js deleted file mode 100644 index 7e32d68a67ece..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,90 +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 { checkLicense } from '../check_license'; - -describe('check_license', function() { - let mockLicenseInfo; - beforeEach(() => (mockLicenseInfo = {})); - - describe('license information is undefined', () => { - beforeEach(() => (mockLicenseInfo = undefined)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach( - () => - (mockLicenseInfo = { - isAvailable: () => true, - license: { - getType: () => 'foobar', - }, - }) - ); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set enableLink to true', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(true); - }); - - it('should set enableAPIRoute to true', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(true); - }); - - it('should NOT set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts deleted file mode 100644 index f2c070fd44b6e..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/check_license/index.ts +++ /dev/null @@ -1,7 +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'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts deleted file mode 100644 index 3756b0c74fb10..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/index.ts +++ /dev/null @@ -1,7 +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 { wrapEsError } from './wrap_es_error'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts b/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts deleted file mode 100644 index e48ef500309a7..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/error_wrappers/wrap_es_error.ts +++ /dev/null @@ -1,18 +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'; - -/** - * Wraps ES errors into a Boom error response and returns it - * This also handles the permissions issue gracefully - * - * @param err Object ES error - * @return Object Boom error response - */ -export function wrapEsError(err: any) { - return Boom.boomify(err, { statusCode: err.statusCode }); -} diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts similarity index 87% rename from x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts rename to x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts index 4e183262f9d8f..df135b9e0ef32 100644 --- a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts @@ -5,8 +5,8 @@ */ import Boom from 'boom'; -import { ServerFacade } from '../../../types'; -import { PLUGIN } from '../../../common/constants'; +import { PLUGIN } from '../../common/constants'; +import { ServerFacade } from '../../../index_management'; export const licensePreRoutingFactory = (server: ServerFacade) => { const xpackMainPlugin = server.plugins.xpack_main; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js deleted file mode 100644 index 854d919b1173d..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ /dev/null @@ -1,64 +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 Boom from 'boom'; -import { licensePreRoutingFactory } from '../license_pre_routing_factory'; - -describe('license_pre_routing_factory', () => { - describe('#painlessPlaygroundFeaturePreRoutingFactory', () => { - let mockServer; - let mockLicenseCheckResults; - - beforeEach(() => { - mockServer = { - plugins: { - xpack_main: { - info: { - feature: () => ({ - getLicenseCheckResults: () => mockLicenseCheckResults, - }), - }, - }, - }, - }; - }); - - describe('isAvailable is false', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: false, - }; - }); - - it('replies with 403', async () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - expect(() => licensePreRouting(stubRequest)).to.throwException(response => { - expect(response).to.be.an(Error); - expect(response.isBoom).to.be(true); - expect(response.output.statusCode).to.be(403); - }); - }); - }); - - describe('isAvailable is true', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: true, - }; - }); - - it('replies with forbidden', async () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - expect(() => licensePreRouting(stubRequest)).to.throwException(response => { - expect(response).to.eql(Boom.forbidden()); - }); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts deleted file mode 100644 index 0743e443955f4..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory/index.ts +++ /dev/null @@ -1,7 +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 { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts b/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts deleted file mode 100644 index 7b0f97c38d129..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/index.ts +++ /dev/null @@ -1,7 +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 { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts b/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts similarity index 80% rename from x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts rename to x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts index bcb7a610b6294..53932582193cf 100644 --- a/x-pack/legacy/plugins/painless_playground/server/lib/register_license_checker/register_license_checker.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ // @ts-ignore -import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../common/constants'; +import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; +import { checkLicense } from './lib/check_license'; +import { PLUGIN } from '../common/constants'; export function registerLicenseChecker(server: any) { const xpackMainPlugin = server.plugins.xpack_main; diff --git a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts similarity index 73% rename from x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts rename to x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts index 84047ed55431b..9125dd77187f9 100644 --- a/x-pack/legacy/plugins/painless_playground/server/routes/api/register_painless_playground_simulate_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { wrapEsError } from '../../lib/error_wrappers'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { ServerFacade, RequestFacade } from '../../../types'; +import { licensePreRoutingFactory } from './lib/license_pre_routing_factory'; +import { RequestFacade, ServerFacade } from '../../reporting/types'; -export function registerPainlessPlaygroundSimulateRoute(server: ServerFacade) { +export function registerSimulateRoute(server: ServerFacade) { const licensePreRouting = licensePreRoutingFactory(server); server.route({ From 78d510c6a839da2e0817aa38a255891d23a44f4d Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 15 Jan 2020 10:01:48 +0100 Subject: [PATCH 07/34] Add Tabs to output --- .../public/components/painless_playground.tsx | 155 +++++++----------- 1 file changed, 63 insertions(+), 92 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 7114c4b3beb41..3e3933d322d35 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -13,6 +13,8 @@ import { EuiPageContentBody, EuiSpacer, EuiFormRow, + EuiTabbedContent, + EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; @@ -20,48 +22,39 @@ import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; interface Props { executeCode: (payload: Record) => Promise; } +interface Response { + error?: { [key: string]: any }; + result?: string; +} +function getRequest(code: string) { + return { + script: { + source: code, + }, + }; +} -interface State { - code: string; - request?: string; - response?: string; - responseObj: Record | null; +function formatJson(text) { + try { + return JSON.stringify(text, null, 2); + } catch (e) { + return `Invalid JSON ${String(text)}`; + } } -export function PainlessPlayground(props: Props) { - const [state, setState] = useState({ - code: '', - request: '', - response: '', - responseObj: null, - }); + +export function PainlessPlayground({ executeCode }: Props) { + const [code, setCode] = useState(''); + const [response, setResponse] = useState({}); const submit = async () => { - const request = { - script: { - source: state.code, - }, - }; try { - const response = await props.executeCode(request); - setState({ - code: state.code, - request: JSON.stringify(request, null, 2), - response: JSON.stringify(response, null, 2), - responseObj: response, - }); + const res = await executeCode(getRequest(code)); + setResponse(res); } catch (e) { - setState({ - code: state.code, - response: JSON.stringify(e, null, 2), - request: JSON.stringify(request, null, 2), - responseObj: e, - }); + setResponse(e); } }; - const onSimulateClick = () => { - submit(); - }; return ( @@ -82,8 +75,8 @@ export function PainlessPlayground(props: Props) { setState({ code })} + value={code} + onChange={setCode} options={{ fontSize: 12, minimap: { @@ -99,8 +92,8 @@ export function PainlessPlayground(props: Props) { - - - } - fullWidth - data-test-subj="response" - > -
- {state.responseObject?.body?.error ? ( -
{state.responseObject?.body?.error}
- ) : ( - - )} -
-
- {state.request && ( - - } - fullWidth - data-test-subj="request" - > -
- -
-
- )} + {response.error || response.result ? ( + + {response?.result ? response?.result : formatJson(response?.error)} + + ), + }, + { + id: 'response', + name: 'Request', + content: ( + + {'POST _scripts/painless/_execute\n' + formatJson(getRequest(code))} + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + + {formatJson(response)} + + ), + }, + ]} + /> + ) : null}
From a348fce5a057b549226d330c791dcbcfe78ee810 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 15 Jan 2020 13:49:03 +0100 Subject: [PATCH 08/34] Add context API --- .../public/components/painless_playground.tsx | 157 +++++++++++++++++- 1 file changed, 152 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 3e3933d322d35..9870eb766bea1 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -5,6 +5,7 @@ */ import React, { useState } from 'react'; import { + EuiSelect, EuiForm, EuiButton, EuiPage, @@ -15,6 +16,7 @@ import { EuiFormRow, EuiTabbedContent, EuiCodeBlock, + EuiFieldText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; @@ -26,7 +28,37 @@ interface Response { error?: { [key: string]: any }; result?: string; } -function getRequest(code: string) { + +function parseJSON(text) { + try { + return JSON.parse(text); + } catch (e) { + return {}; + } +} +function getRequest(code: string, context: string, contextSetup: string) { + const params = parseJSON(contextSetup?.params); + if (context === 'painless_test' && contextSetup.params) { + return { + script: { + source: code, + params, + }, + }; + } else if (context === 'filter' || context === 'score') { + return { + script: { + source: code, + params, + }, + context, + context_setup: { + index: contextSetup.index, + document: parseJSON(contextSetup.doc), + }, + }; + } + return { script: { source: code, @@ -42,13 +74,43 @@ function formatJson(text) { } } +function getFromLocalStorage(key: key, defaultValue: any = '', parse = false) { + const value = localStorage.getItem(key); + if (value && parse) { + try { + return JSON.parse(value); + } catch (e) { + return defaultValue; + } + } else if (value) { + return value; + } else { + return defaultValue; + } +} + +const painlessContextOptions = [ + { value: 'painless_test', text: 'Default - Execute as it is' }, + { value: 'filter', text: 'Filter - Execute like inside a script query' }, + { value: 'score', text: 'Score - Execute like inside a script query' }, +]; + export function PainlessPlayground({ executeCode }: Props) { - const [code, setCode] = useState(''); + const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', '')); const [response, setResponse] = useState({}); + const [context, setContext] = useState( + getFromLocalStorage('painlessPlaygroundContext', 'painless_test') + ); + const [contextSetup, setContextSetup] = useState( + getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) + ); const submit = async () => { try { - const res = await executeCode(getRequest(code)); + const res = await executeCode(getRequest(code, context, contextSetup)); + localStorage.setItem('painlessPlaygroundCode', code); + localStorage.setItem('painlessPlaygroundContext', context); + localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); setResponse(res); } catch (e) { setResponse(e); @@ -65,10 +127,22 @@ export function PainlessPlayground({ executeCode }: Props) { label={ } fullWidth + > + ) => setContext(e.target.value)} + /> +
+ + } + fullWidth data-test-subj="codeInput" >
@@ -89,6 +163,78 @@ export function PainlessPlayground({ executeCode }: Props) { />
+ + } + fullWidth + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ + } + fullWidth + > + + setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) + } + /> + + + } + fullWidth + > +
+ + setContextSetup(Object.assign({}, contextSetup, { document: value })) + } + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
- {'POST _scripts/painless/_execute\n' + formatJson(getRequest(code))} + {'POST _scripts/painless/_execute\n' + + formatJson(getRequest(code, context, contextSetup))} ), }, From 7055abe5b942ec3bf173ad8be20f4241092baf57 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 15 Jan 2020 18:05:17 +0100 Subject: [PATCH 09/34] Redesign --- .../public/components/painless_playground.tsx | 259 +++++++++--------- 1 file changed, 131 insertions(+), 128 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 9870eb766bea1..0ac0995c49127 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -22,59 +22,61 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; interface Props { - executeCode: (payload: Record) => Promise; + executeCode: (payload: Request) => Promise; +} +interface Request { + script: { + source: string; + params?: Record; + }; + context?: string; + context_setup?: { + document: Record; + index: string; + }; } interface Response { error?: { [key: string]: any }; result?: string; } -function parseJSON(text) { +function parseJSON(text: string) { try { return JSON.parse(text); } catch (e) { return {}; } } -function getRequest(code: string, context: string, contextSetup: string) { - const params = parseJSON(contextSetup?.params); - if (context === 'painless_test' && contextSetup.params) { - return { - script: { - source: code, - params, - }, - }; - } else if (context === 'filter' || context === 'score') { - return { - script: { - source: code, - params, - }, - context, - context_setup: { - index: contextSetup.index, - document: parseJSON(contextSetup.doc), - }, - }; - } - - return { +function getRequest(code: string, context: string, contextSetup: Record) { + const request: Request = { script: { source: code, }, }; + if (context !== 'painless_test_without_params' && contextSetup.params) { + request.script.params = parseJSON(contextSetup?.params); + } + if (context === 'filter' || context === 'score') { + request.context = context; + request.context_setup = { + index: contextSetup.index, + document: parseJSON(contextSetup.doc), + }; + return request; + } + + return request; } -function formatJson(text) { +function formatJson(json: any) { try { - return JSON.stringify(text, null, 2); + return JSON.stringify(json, null, 2); } catch (e) { - return `Invalid JSON ${String(text)}`; + return `Invalid JSON ${String(json)}`; } } -function getFromLocalStorage(key: key, defaultValue: any = '', parse = false) { +function getFromLocalStorage(key: string, defaultValue: any = '', parse = false) { const value = localStorage.getItem(key); if (value && parse) { try { @@ -90,16 +92,19 @@ function getFromLocalStorage(key: key, defaultValue: any = '', parse = false) { } const painlessContextOptions = [ - { value: 'painless_test', text: 'Default - Execute as it is' }, + { value: 'painless_test_without_params', text: 'Default - Execute as it is' }, + { value: 'painless_test', text: 'Default - Execute with parameters' }, { value: 'filter', text: 'Filter - Execute like inside a script query' }, { value: 'score', text: 'Score - Execute like inside a script query' }, ]; export function PainlessPlayground({ executeCode }: Props) { - const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', '')); + const [code, setCode] = useState( + getFromLocalStorage('painlessPlaygroundCode', 'return "Hello painless world!"') + ); const [response, setResponse] = useState({}); const [context, setContext] = useState( - getFromLocalStorage('painlessPlaygroundContext', 'painless_test') + getFromLocalStorage('painlessPlaygroundContext', 'painless_test_without_params') ); const [contextSetup, setContextSetup] = useState( getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) @@ -120,6 +125,23 @@ export function PainlessPlayground({ executeCode }: Props) { return ( + + + @@ -135,106 +157,87 @@ export function PainlessPlayground({ executeCode }: Props) { ) => setContext(e.target.value)} + onChange={(e: React.ChangeEvent) => setContext(e.target.value)} /> - - } - fullWidth - data-test-subj="codeInput" - > -
- -
-
- - } - fullWidth - > -
- setContextSetup({ params: value })} - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- - } - fullWidth - > - - setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) + {context !== 'painless_test_without_params' && ( + } - /> - - - } - fullWidth - > -
- - setContextSetup(Object.assign({}, contextSetup, { document: value })) + fullWidth + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+ + )} + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + > + + setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} /> -
-
+
+ )} + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + > +
+ + setContextSetup(Object.assign({}, contextSetup, { document: value })) + } + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ )} Date: Thu, 16 Jan 2020 10:45:26 +0100 Subject: [PATCH 10/34] Redesign --- .../public/components/constants.ts | 27 + .../public/components/helpers.ts | 62 +++ .../public/components/painless_playground.tsx | 501 +++++++++--------- .../public/components/types.ts | 21 + 4 files changed, 373 insertions(+), 238 deletions(-) create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/constants.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/helpers.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/types.ts diff --git a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts new file mode 100644 index 0000000000000..650d7c231a336 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts @@ -0,0 +1,27 @@ +/* + * 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 const painlessContextOptions = [ + { + value: 'painless_test', + text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + defaultMessage: 'Default - Execute as it is', + }), + }, + { + value: 'filter', + text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + defaultMessage: 'Filter - Execute like inside a script query', + }), + }, + { + value: 'score', + text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + defaultMessage: 'Score - Execute like inside a script query', + }), + }, +]; diff --git a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts new file mode 100644 index 0000000000000..9c8b314966218 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts @@ -0,0 +1,62 @@ +/* + * 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 { Request } from './types'; + +export function parseJSON(text: string) { + try { + return JSON.parse(text); + } catch (e) { + return {}; + } +} + +export function buildRequestPayload( + code: string, + context: string, + contextSetup: Record +) { + const request: Request = { + script: { + source: code, + }, + }; + if (contextSetup.params) { + request.script.params = parseJSON(contextSetup?.params); + } + if (context === 'filter' || context === 'score') { + request.context = context; + request.context_setup = { + index: contextSetup.index, + document: parseJSON(contextSetup.doc), + }; + return request; + } + + return request; +} + +export function getFromLocalStorage(key: string, defaultValue: any = '', parse = false) { + const value = localStorage.getItem(key); + if (value && parse) { + try { + return JSON.parse(value); + } catch (e) { + return defaultValue; + } + } else if (value) { + return value; + } else { + return defaultValue; + } +} + +export function formatJson(json: any): string { + try { + return JSON.stringify(json, null, 2); + } catch (e) { + return `Invalid JSON ${String(json)}`; + } +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 0ac0995c49127..738620eac9d20 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -5,100 +5,32 @@ */ import React, { useState } from 'react'; import { - EuiSelect, - EuiForm, EuiButton, - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiSpacer, - EuiFormRow, - EuiTabbedContent, EuiCodeBlock, EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiHorizontalRule, + EuiPanel, + EuiSelect, + EuiTabbedContent, + EuiTitle, + EuiIconTip, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; +import { buildRequestPayload, formatJson, getFromLocalStorage } from './helpers'; +import { Request, Response } from './types'; +import { painlessContextOptions } from './constants'; -interface Props { - executeCode: (payload: Request) => Promise; -} -interface Request { - script: { - source: string; - params?: Record; - }; - context?: string; - context_setup?: { - document: Record; - index: string; - }; -} -interface Response { - error?: { [key: string]: any }; - result?: string; -} - -function parseJSON(text: string) { - try { - return JSON.parse(text); - } catch (e) { - return {}; - } -} -function getRequest(code: string, context: string, contextSetup: Record) { - const request: Request = { - script: { - source: code, - }, - }; - if (context !== 'painless_test_without_params' && contextSetup.params) { - request.script.params = parseJSON(contextSetup?.params); - } - if (context === 'filter' || context === 'score') { - request.context = context; - request.context_setup = { - index: contextSetup.index, - document: parseJSON(contextSetup.doc), - }; - return request; - } - - return request; -} - -function formatJson(json: any) { - try { - return JSON.stringify(json, null, 2); - } catch (e) { - return `Invalid JSON ${String(json)}`; - } -} - -function getFromLocalStorage(key: string, defaultValue: any = '', parse = false) { - const value = localStorage.getItem(key); - if (value && parse) { - try { - return JSON.parse(value); - } catch (e) { - return defaultValue; - } - } else if (value) { - return value; - } else { - return defaultValue; - } -} - -const painlessContextOptions = [ - { value: 'painless_test_without_params', text: 'Default - Execute as it is' }, - { value: 'painless_test', text: 'Default - Execute with parameters' }, - { value: 'filter', text: 'Filter - Execute like inside a script query' }, - { value: 'score', text: 'Score - Execute like inside a script query' }, -]; - -export function PainlessPlayground({ executeCode }: Props) { +export function PainlessPlayground({ + executeCode, +}: { + executeCode: (payload: Request) => Promise; +}) { const [code, setCode] = useState( getFromLocalStorage('painlessPlaygroundCode', 'return "Hello painless world!"') ); @@ -110,9 +42,11 @@ export function PainlessPlayground({ executeCode }: Props) { getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) ); + const buildRequestPayloadPreview = () => buildRequestPayload(code, context, contextSetup); + const submit = async () => { try { - const res = await executeCode(getRequest(code, context, contextSetup)); + const res = await executeCode(buildRequestPayloadPreview()); localStorage.setItem('painlessPlaygroundCode', code); localStorage.setItem('painlessPlaygroundContext', context); localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); @@ -122,60 +56,41 @@ export function PainlessPlayground({ executeCode }: Props) { } }; + const renderExecuteBtn = () => ( + + + + ); + return ( - - - - - - - - - - } - fullWidth - > - ) => setContext(e.target.value)} - /> - - {context !== 'painless_test_without_params' && ( - - } - fullWidth - > -
+ <> + + + +

+ {i18n.translate('console.pageHeading', { + defaultMessage: 'Painless Playground', + })} +

+
+ setContextSetup({ params: value })} + languageId="painless" + height={250} + value={code} + onChange={setCode} options={{ fontSize: 12, minimap: { @@ -186,109 +101,219 @@ export function PainlessPlayground({ executeCode }: Props) { wrappingIndent: 'indent', }} /> -
-
- )} - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - > - - setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) - } - /> - - )} - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - > -
- - setContextSetup(Object.assign({}, contextSetup, { document: value })) - } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- )} - - - - - -
- {response.error || response.result ? ( - + {renderExecuteBtn()} + + ), + }, + { + id: 'request', + name: 'Request', + content: ( + + + {'POST _scripts/painless/_execute\n'} + {formatJson(buildRequestPayloadPreview())} + + + {renderExecuteBtn()} + + ), + }, + { + id: 'settings', + name: 'Settings', + content: ( + + + + } + fullWidth + > + ) => + setContext(e.target.value) + } + /> + + + + } + fullWidth + labelAppend={ + + } + /> + } + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + labelAppend={ + + } + /> + } + > + + setContextSetup( + Object.assign({}, contextSetup, { index: e.target.value }) + ) + } + /> + + )} + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + labelAppend={ + + } + /> + } + > +
+ + setContextSetup( + Object.assign({}, contextSetup, { document: value }) + ) + } + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ )} + {renderExecuteBtn()} +
+
+ ), + }, + ]} + /> + + {response.error || response.result ? ( + + {response?.result ? response?.result : formatJson(response?.error)} - ), - }, - { - id: 'response', - name: 'Request', - content: ( - - {'POST _scripts/painless/_execute\n' + - formatJson(getRequest(code, context, contextSetup))} - - ), - }, - { - id: 'request', - name: 'Response', - content: ( + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + {formatJson(response)} - ), - }, - ]} - /> - ) : null} -
-
-
-
+ + ), + }, + ]} + /> + + ) : null} + + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/components/types.ts b/x-pack/legacy/plugins/painless_playground/public/components/types.ts new file mode 100644 index 0000000000000..45a06aba6ce97 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/types.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +export interface Request { + script: { + source: string; + params?: Record; + }; + context?: string; + context_setup?: { + document: Record; + index: string; + }; +} + +export interface Response { + error?: { [key: string]: any }; + result?: string; +} From b8a20640bfa29ec04076bc84e76f01810d003bb1 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 11:03:19 +0100 Subject: [PATCH 11/34] Small Adaptions --- .../painless_playground/public/components/constants.ts | 2 +- .../public/components/painless_playground.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts index 650d7c231a336..3300520215a4c 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts @@ -15,7 +15,7 @@ export const painlessContextOptions = [ { value: 'filter', text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { - defaultMessage: 'Filter - Execute like inside a script query', + defaultMessage: 'Filter - Execute like inside a filter query', }), }, { diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 738620eac9d20..a5aaff014504b 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -171,7 +171,7 @@ export function PainlessPlayground({ >
setContextSetup({ params: value })} @@ -253,7 +253,7 @@ export function PainlessPlayground({ >
From 743d60d08da049a567016fa77f594a1a3dd89daa Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 11:43:43 +0100 Subject: [PATCH 12/34] Code improvements --- .../public/components/painless_playground.tsx | 8 ++++++-- .../painless_playground/public/lib/execute_code.ts | 10 +++------- .../server/register_simulate_route.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index a5aaff014504b..4687edc491a79 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -46,13 +46,17 @@ export function PainlessPlayground({ const submit = async () => { try { - const res = await executeCode(buildRequestPayloadPreview()); localStorage.setItem('painlessPlaygroundCode', code); localStorage.setItem('painlessPlaygroundContext', context); localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); + const res = await executeCode(buildRequestPayloadPreview()); setResponse(res); } catch (e) { - setResponse(e); + setResponse({ + error: { + message: e.message, + }, + }); } }; diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts index 6f345a1e7ea06..b23d088befc65 100644 --- a/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts @@ -7,11 +7,7 @@ import { ROUTES } from '../../common/constants'; export async function executeCode(http: any, payload: Record) { - try { - return await http.post(`${ROUTES.API_ROOT}/simulate`, { - body: JSON.stringify(payload), - }); - } catch (e) { - return e; - } + return await http.post(`${ROUTES.API_ROOT}/execute`, { + body: JSON.stringify(payload), + }); } diff --git a/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts index 9125dd77187f9..207e0b4a3ffed 100644 --- a/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts @@ -11,7 +11,7 @@ export function registerSimulateRoute(server: ServerFacade) { const licensePreRouting = licensePreRoutingFactory(server); server.route({ - path: '/api/painless_playground/simulate', + path: '/api/painless_playground/execute', method: 'POST', handler: (request: RequestFacade) => { const cluster = server.plugins.elasticsearch.getCluster('data'); @@ -19,7 +19,7 @@ export function registerSimulateRoute(server: ServerFacade) { .callWithRequest(request, 'scriptsPainlessExecute', { body: request.payload, }) - .catch((e: any) => { + .catch((e: Error) => { return e.body; }); }, From c1fd0879a7501fa4b38521b2e65363a39b330270 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 13:27:31 +0100 Subject: [PATCH 13/34] Code improvements --- x-pack/legacy/plugins/painless_playground/index.ts | 4 ++-- .../painless_playground/public/render_app.tsx | 5 +---- ...r_simulate_route.ts => register_execute_route.ts} | 12 ++++++------ 3 files changed, 9 insertions(+), 12 deletions(-) rename x-pack/legacy/plugins/painless_playground/server/{register_simulate_route.ts => register_execute_route.ts} (76%) diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index 30ca2847f5a25..76d852daba085 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -8,7 +8,7 @@ import { resolve } from 'path'; import { PLUGIN } from './common/constants'; import { registerLicenseChecker } from './server/register_license_checker'; -import { registerSimulateRoute } from './server/register_simulate_route'; +import { registerExecuteRoute } from './server/register_execute_route'; export const painlessPlayground = (kibana: any) => new kibana.Plugin({ @@ -26,6 +26,6 @@ export const painlessPlayground = (kibana: any) => }, init: (server: any) => { registerLicenseChecker(server); - registerSimulateRoute(server); + registerExecuteRoute(server); }, }); diff --git a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx index d58c8f09c869b..02710d6e8cc60 100644 --- a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { I18nProvider } from '@kbn/i18n/react'; import { PainlessPlayground } from './components/painless_playground'; import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public'; import { executeCode } from './lib/execute_code'; @@ -17,9 +16,7 @@ export function renderApp(element: any, npStart: any) { }); render( - - executeCode(npStart.core.http, payload)} /> - + executeCode(npStart.core.http, payload)} /> , element ); diff --git a/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts b/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts similarity index 76% rename from x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts rename to x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts index 207e0b4a3ffed..35ed85cc6a6be 100644 --- a/x-pack/legacy/plugins/painless_playground/server/register_simulate_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts @@ -3,28 +3,28 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { ServerRoute } from 'hapi'; import { licensePreRoutingFactory } from './lib/license_pre_routing_factory'; -import { RequestFacade, ServerFacade } from '../../reporting/types'; +import { Legacy } from '../../../../../kibana'; -export function registerSimulateRoute(server: ServerFacade) { +export function registerExecuteRoute(server: any) { const licensePreRouting = licensePreRoutingFactory(server); server.route({ path: '/api/painless_playground/execute', method: 'POST', - handler: (request: RequestFacade) => { + handler: (request: Legacy.Request) => { const cluster = server.plugins.elasticsearch.getCluster('data'); return cluster .callWithRequest(request, 'scriptsPainlessExecute', { body: request.payload, }) - .catch((e: Error) => { + .catch((e: any) => { return e.body; }); }, config: { pre: [licensePreRouting], }, - }); + } as ServerRoute); } From f92dbaa096c4e4245ba70dae6a4b080f77b67d38 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 14:24:24 +0100 Subject: [PATCH 14/34] Change text --- .../painless_playground/public/components/constants.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts index 3300520215a4c..24c1ed9adf349 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts @@ -15,13 +15,14 @@ export const painlessContextOptions = [ { value: 'filter', text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { - defaultMessage: 'Filter - Execute like inside a filter query', + defaultMessage: 'Filter - Execute like inside a script query of a filter', }), }, { value: 'score', text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { - defaultMessage: 'Score - Execute like inside a script query', + defaultMessage: + 'Score - Execution like inside a script_score function in function_score query', }), }, ]; From 9fa6a27a164982a5585f556c3af22d3c1c476e86 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 17:20:33 +0100 Subject: [PATCH 15/34] Fix translations --- x-pack/.i18nrc.json | 1 + .../public/components/constants.ts | 6 ++--- .../public/components/painless_playground.tsx | 27 ++++++++++--------- .../painless_playground/public/register.ts | 6 ++--- .../server/lib/check_license.ts | 4 +-- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 7e86d2f1dc435..eba34243572a8 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -26,6 +26,7 @@ "xpack.maps": "legacy/plugins/maps", "xpack.ml": "legacy/plugins/ml", "xpack.monitoring": "legacy/plugins/monitoring", + "xpack.painless_playground": "legacy/plugins/painless_playground", "xpack.remoteClusters": "legacy/plugins/remote_clusters", "xpack.reporting": [ "plugins/reporting", "legacy/plugins/reporting" ], "xpack.rollupJobs": "legacy/plugins/rollup", diff --git a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts index 24c1ed9adf349..293f26b835470 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/constants.ts @@ -8,19 +8,19 @@ import { i18n } from '@kbn/i18n'; export const painlessContextOptions = [ { value: 'painless_test', - text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + text: i18n.translate('xpack.painless_playground.selectDefaultLabel', { defaultMessage: 'Default - Execute as it is', }), }, { value: 'filter', - text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + text: i18n.translate('xpack.painless_playground.selectFilterLabel', { defaultMessage: 'Filter - Execute like inside a script query of a filter', }), }, { value: 'score', - text: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + text: i18n.translate('xpack.painless_playground.selectScoreLabel', { defaultMessage: 'Score - Execution like inside a script_score function in function_score query', }), diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 4687edc491a79..2c30e83574378 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -62,7 +62,10 @@ export function PainlessPlayground({ const renderExecuteBtn = () => ( - + ); @@ -77,7 +80,7 @@ export function PainlessPlayground({

- {i18n.translate('console.pageHeading', { + {i18n.translate('xpack.painless_playground.title', { defaultMessage: 'Painless Playground', })}

@@ -133,7 +136,7 @@ export function PainlessPlayground({ } @@ -151,7 +154,7 @@ export function PainlessPlayground({ } @@ -159,14 +162,14 @@ export function PainlessPlayground({ labelAppend={ } @@ -196,7 +199,7 @@ export function PainlessPlayground({ } @@ -204,14 +207,14 @@ export function PainlessPlayground({ labelAppend={ } @@ -233,7 +236,7 @@ export function PainlessPlayground({ } @@ -241,14 +244,14 @@ export function PainlessPlayground({ labelAppend={ } diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index ed76f46ee8489..d9907b8fd82c7 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -13,10 +13,10 @@ import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public npSetup.plugins.home.featureCatalogue.register({ id: 'painless_playground', - title: i18n.translate('xpack.painlessPlayground.registryProviderTitle', { + title: i18n.translate('xpack.painless_playground.registryProviderTitle', { defaultMessage: 'Painless Playground', }), - description: i18n.translate('xpack.painlessPlayground.registryProviderDescription', { + description: i18n.translate('xpack.painless_playground.registryProviderDescription', { defaultMessage: 'Simulate and debug painless code', }), icon: '', @@ -27,7 +27,7 @@ npSetup.plugins.home.featureCatalogue.register({ npSetup.plugins.dev_tools.register({ order: 7, - title: i18n.translate('xpack.painlessPlayground.displayName', { + title: i18n.translate('xpack.painless_playground.displayName', { defaultMessage: 'Painless Playground', }), id: 'painless_playground', diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts b/x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts index a4708a3a3a5e3..f6a815abe9a47 100644 --- a/x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts +++ b/x-pack/legacy/plugins/painless_playground/server/lib/check_license.ts @@ -13,7 +13,7 @@ export function checkLicense(xpackLicenseInfo: any) { return { enableLink: false, enableAPIRoute: false, - message: i18n.translate('xpack.painlessPlayground.unavailableLicenseInformationMessage', { + message: i18n.translate('xpack.painless_playground.unavailableLicenseInformationMessage', { defaultMessage: 'You cannot use the Painless Playground because license information is not available at this time.', }), @@ -28,7 +28,7 @@ export function checkLicense(xpackLicenseInfo: any) { return { enableLink: false, enableAPIRoute: false, - message: i18n.translate('xpack.painlessPlayground.licenseHasExpiredMessage', { + message: i18n.translate('xpack.painless_playground.licenseHasExpiredMessage', { defaultMessage: 'You cannot use the Painless Playground because your {licenseType} license has expired.', values: { From 02b37ef79e8b124644fedf7caa39753a39db9198 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Jan 2020 22:34:30 +0100 Subject: [PATCH 16/34] Improve code --- .../kibana_react/public/code_editor/code_editor.tsx | 2 +- .../plugins/painless_playground/common/constants.ts | 8 ++------ x-pack/legacy/plugins/painless_playground/index.ts | 4 ++-- .../painless_playground/public/lib/execute_code.ts | 4 ++-- .../server/lib/license_pre_routing_factory.ts | 5 ++--- .../painless_playground/server/register_execute_route.ts | 3 ++- .../server/register_license_checker.ts | 6 +++--- 7 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index a1636c7cc2b4e..0ae77995c0502 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -48,7 +48,7 @@ export interface Props { value: string; /** Function invoked when text in editor is changed */ - onChange?: (value: string) => void; + onChange: (value: string) => void; /** * Options for the Monaco Code Editor diff --git a/x-pack/legacy/plugins/painless_playground/common/constants.ts b/x-pack/legacy/plugins/painless_playground/common/constants.ts index f315aaab343f1..91a620dcfa7a9 100644 --- a/x-pack/legacy/plugins/painless_playground/common/constants.ts +++ b/x-pack/legacy/plugins/painless_playground/common/constants.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PLUGIN = { - ID: 'painless_playground', -}; +export const PLUGIN_ID = 'painless_playground'; -export const ROUTES = { - API_ROOT: '/api/painless_playground', -}; +export const API_ROUTE_EXECUTE = '/api/painless_playground/execute'; diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index 76d852daba085..0fb8823eaf328 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -5,14 +5,14 @@ */ import { resolve } from 'path'; -import { PLUGIN } from './common/constants'; +import { PLUGIN_ID } from './common/constants'; import { registerLicenseChecker } from './server/register_license_checker'; import { registerExecuteRoute } from './server/register_execute_route'; export const painlessPlayground = (kibana: any) => new kibana.Plugin({ - id: PLUGIN.ID, + id: PLUGIN_ID, publicDir: resolve(__dirname, 'public'), require: ['kibana', 'elasticsearch', 'xpack_main'], configPrefix: 'xpack.painless_playground', diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts index b23d088befc65..c8b9f2724d38b 100644 --- a/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/execute_code.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ROUTES } from '../../common/constants'; +import { API_ROUTE_EXECUTE } from '../../common/constants'; export async function executeCode(http: any, payload: Record) { - return await http.post(`${ROUTES.API_ROOT}/execute`, { + return await http.post(API_ROUTE_EXECUTE, { body: JSON.stringify(payload), }); } diff --git a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts index df135b9e0ef32..387a263114a6e 100644 --- a/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts +++ b/x-pack/legacy/plugins/painless_playground/server/lib/license_pre_routing_factory.ts @@ -3,9 +3,8 @@ * 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 { PLUGIN } from '../../common/constants'; +import { PLUGIN_ID } from '../../common/constants'; import { ServerFacade } from '../../../index_management'; export const licensePreRoutingFactory = (server: ServerFacade) => { @@ -13,7 +12,7 @@ export const licensePreRoutingFactory = (server: ServerFacade) => { // License checking and enable/disable logic function licensePreRouting() { - const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN_ID).getLicenseCheckResults(); if (!licenseCheckResults.enableAPIRoute) { throw Boom.forbidden(licenseCheckResults.message); } diff --git a/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts b/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts index 35ed85cc6a6be..e4ffad9c21d60 100644 --- a/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_execute_route.ts @@ -6,12 +6,13 @@ import { ServerRoute } from 'hapi'; import { licensePreRoutingFactory } from './lib/license_pre_routing_factory'; import { Legacy } from '../../../../../kibana'; +import { API_ROUTE_EXECUTE } from '../common/constants'; export function registerExecuteRoute(server: any) { const licensePreRouting = licensePreRoutingFactory(server); server.route({ - path: '/api/painless_playground/execute', + path: API_ROUTE_EXECUTE, method: 'POST', handler: (request: Legacy.Request) => { const cluster = server.plugins.elasticsearch.getCluster('data'); diff --git a/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts b/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts index 53932582193cf..1ec5b33c4d9f9 100644 --- a/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts +++ b/x-pack/legacy/plugins/painless_playground/server/register_license_checker.ts @@ -6,16 +6,16 @@ // @ts-ignore import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; import { checkLicense } from './lib/check_license'; -import { PLUGIN } from '../common/constants'; +import { PLUGIN_ID } from '../common/constants'; export function registerLicenseChecker(server: any) { const xpackMainPlugin = server.plugins.xpack_main; - const plugin = server.plugins[PLUGIN.ID]; + const plugin = server.plugins[PLUGIN_ID]; mirrorPluginStatus(xpackMainPlugin, plugin); xpackMainPlugin.status.once('green', () => { // Register a function that is called whenever the xpack info changes, // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); + xpackMainPlugin.info.feature(PLUGIN_ID).registerLicenseCheckResultsGenerator(checkLicense); }); } From 987445fba0f31e58a46376368bd162d04ed26545 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 17 Jan 2020 15:34:24 +0100 Subject: [PATCH 17/34] Added feature flag Update layout of code editor slightly Fixed i18n issue (not rendering inside context) --- .../painless_playground/common/constants.ts | 2 + .../plugins/painless_playground/index.ts | 24 +- .../public/components/painless_playground.tsx | 369 +++++++++--------- .../painless_playground/public/register.ts | 19 +- .../painless_playground/public/render_app.tsx | 13 +- 5 files changed, 231 insertions(+), 196 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/common/constants.ts b/x-pack/legacy/plugins/painless_playground/common/constants.ts index 91a620dcfa7a9..d8807fcccccf0 100644 --- a/x-pack/legacy/plugins/painless_playground/common/constants.ts +++ b/x-pack/legacy/plugins/painless_playground/common/constants.ts @@ -7,3 +7,5 @@ export const PLUGIN_ID = 'painless_playground'; export const API_ROUTE_EXECUTE = '/api/painless_playground/execute'; + +export const ADVANCED_SETTINGS_FLAG_NAME = 'devTools:enablePainlessPlayground'; diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index 0fb8823eaf328..16b13c040d412 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -3,12 +3,13 @@ * 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 { resolve } from 'path'; -import { PLUGIN_ID } from './common/constants'; +import { PLUGIN_ID, ADVANCED_SETTINGS_FLAG_NAME } from './common/constants'; import { registerLicenseChecker } from './server/register_license_checker'; import { registerExecuteRoute } from './server/register_execute_route'; +import { Legacy } from '../../../../kibana'; export const painlessPlayground = (kibana: any) => new kibana.Plugin({ @@ -24,7 +25,24 @@ export const painlessPlayground = (kibana: any) => uiExports: { devTools: [resolve(__dirname, 'public/register')], }, - init: (server: any) => { + init: (server: Legacy.Server) => { + // Register feature flag + server.newPlatform.setup.core.uiSettings.register({ + [ADVANCED_SETTINGS_FLAG_NAME]: { + name: i18n.translate('kbn.advancedSettings.devTools.painlessPlaygroundTitle', { + defaultMessage: 'Painless Playground', + }), + description: i18n.translate( + 'kbn.advancedSettings.devTools.painlessPlaygroundDescription', + { + defaultMessage: 'Enable experimental Painless Playground.', + } + ), + value: false, + category: ['Dev Tools'], + }, + }); + registerLicenseChecker(server); registerExecuteRoute(server); }, diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 2c30e83574378..be9278548337b 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -18,6 +18,8 @@ import { EuiTabbedContent, EuiTitle, EuiIconTip, + EuiSpacer, + EuiPageContent, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -70,29 +72,31 @@ export function PainlessPlayground({ ); return ( - <> - - - -

- {i18n.translate('xpack.painless_playground.title', { - defaultMessage: 'Painless Playground', - })} -

-
- + + + +

+ {i18n.translate('xpack.painless_playground.title', { + defaultMessage: 'Painless Playground', + })} +

+
+ + + + {renderExecuteBtn()} - - ), - }, - { - id: 'request', - name: 'Request', - content: ( - - - {'POST _scripts/painless/_execute\n'} - {formatJson(buildRequestPayloadPreview())} - - - {renderExecuteBtn()} - - ), - }, - { - id: 'settings', - name: 'Settings', - content: ( - - + + + ), + }, + { + id: 'request', + name: 'Request', + content: ( + + + {'POST _scripts/painless/_execute\n'} + {formatJson(buildRequestPayloadPreview())} + + + {renderExecuteBtn()} + + ), + }, + { + id: 'settings', + name: 'Settings', + content: ( + + + + } + fullWidth + > + ) => + setContext(e.target.value) + } + /> + + + + } + fullWidth + labelAppend={ + + } + /> + } + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ + {['filter', 'score'].indexOf(context) !== -1 && ( } fullWidth + labelAppend={ + + } + /> + } > - ) => - setContext(e.target.value) + + setContextSetup( + Object.assign({}, contextSetup, { index: e.target.value }) + ) } /> - + )} + {['filter', 'score'].indexOf(context) !== -1 && ( } fullWidth @@ -169,8 +256,8 @@ export function PainlessPlayground({ )} content={ } /> @@ -180,8 +267,10 @@ export function PainlessPlayground({ setContextSetup({ params: value })} + value={contextSetup.document} + onChange={(value: string) => + setContextSetup(Object.assign({}, contextSetup, { document: value })) + } options={{ fontSize: 12, minimap: { @@ -194,133 +283,49 @@ export function PainlessPlayground({ />
- - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - labelAppend={ - - } - /> - } - > - - setContextSetup( - Object.assign({}, contextSetup, { index: e.target.value }) - ) - } - /> - - )} - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - labelAppend={ - - } - /> - } - > -
- - setContextSetup( - Object.assign({}, contextSetup, { document: value }) - ) - } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- )} - {renderExecuteBtn()} - + )} + {renderExecuteBtn()} + + + ), + }, + ]} + /> + + {response.error || response.result ? ( + + + + + + {response?.result ? response?.result : formatJson(response?.error)} + + + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + + + {formatJson(response)} + ), }, ]} /> - {response.error || response.result ? ( - - - - {response?.result ? response?.result : formatJson(response?.error)} - - - ), - }, - { - id: 'request', - name: 'Response', - content: ( - - - {formatJson(response)} - - - ), - }, - ]} - /> - - ) : null} - - + ) : null} + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index d9907b8fd82c7..a0812ae688943 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -10,6 +10,7 @@ import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { npSetup, npStart } from 'ui/new_platform'; import { registerPainless } from './register_painless'; import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; +import { ADVANCED_SETTINGS_FLAG_NAME } from '../common/constants'; npSetup.plugins.home.featureCatalogue.register({ id: 'painless_playground', @@ -25,6 +26,11 @@ npSetup.plugins.home.featureCatalogue.register({ category: FeatureCatalogueCategory.ADMIN, }); +npSetup.core.uiSettings.get$(ADVANCED_SETTINGS_FLAG_NAME, false).subscribe(value => { + // eslint-disable-next-line + console.log('use this to figure out whether we should register', value); +}); + npSetup.plugins.dev_tools.register({ order: 7, title: i18n.translate('xpack.painless_playground.displayName', { @@ -33,21 +39,22 @@ npSetup.plugins.dev_tools.register({ id: 'painless_playground', enableRouting: false, disabled: false, - tooltipContent: xpackInfo.get('features.painless_playground.message'), + tooltipContent: xpackInfo.get('features.painlessPlayground.message'), async mount(context, { element }) { registerPainless(); - /** + const licenseCheck = { - showPage: xpackInfo.get('features.painless_playground.enableLink'), - message: xpackInfo.get('features.painless_playground.message'), + showPage: xpackInfo.get('features.painlessPlayground.enableLink'), + message: xpackInfo.get('features.painlessPlayground.message'), }; if (!licenseCheck.showPage) { npStart.core.notifications.toasts.addDanger(licenseCheck.message); window.location.hash = '/dev_tools'; return () => {}; - }**/ + } + const { renderApp } = await import('./render_app'); - return renderApp(element, npStart); + return renderApp(element, npStart.core); }, }); diff --git a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx index 02710d6e8cc60..75adb3bbc382a 100644 --- a/x-pack/legacy/plugins/painless_playground/public/render_app.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/render_app.tsx @@ -5,19 +5,22 @@ */ import React from 'react'; +import { CoreStart } from 'kibana/public'; import { render, unmountComponentAtNode } from 'react-dom'; import { PainlessPlayground } from './components/painless_playground'; import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public'; import { executeCode } from './lib/execute_code'; -export function renderApp(element: any, npStart: any) { +export function renderApp(element: any, { http, i18n, uiSettings }: CoreStart) { const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ - uiSettings: npStart.core.uiSettings, + uiSettings, }); render( - - executeCode(npStart.core.http, payload)} /> - , + + + executeCode(http, payload)} /> + + , element ); return () => unmountComponentAtNode(element); From 4640b4d6fe9f1b27fcda744811a6d3b73798356d Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 17 Jan 2020 16:37:00 +0100 Subject: [PATCH 18/34] A number of UI tweaks and updates - Move view request to flyout - Update rendering of error output - Tweak spaces in panels --- .../public/components/helpers.ts | 13 + .../public/components/painless_playground.tsx | 519 ++++++++++-------- 2 files changed, 297 insertions(+), 235 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts index 9c8b314966218..51067b8670eef 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts @@ -60,3 +60,16 @@ export function formatJson(json: any): string { return `Invalid JSON ${String(json)}`; } } + +export function formatExecutionError(json: object) { + if (json.script_stack && json.caused_by) { + return `Unhandled Exception ${json.caused_by.type} + +${json.caused_by.reason} + +Located at: +${formatJson(json.script_stack)} +`; + } + return formatJson(json); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index be9278548337b..4ed476af6b80f 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -12,7 +12,6 @@ import { EuiFlexItem, EuiForm, EuiFormRow, - EuiHorizontalRule, EuiPanel, EuiSelect, EuiTabbedContent, @@ -20,11 +19,17 @@ import { EuiIconTip, EuiSpacer, EuiPageContent, + EuiFlyout, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; -import { buildRequestPayload, formatJson, getFromLocalStorage } from './helpers'; +import { + buildRequestPayload, + formatJson, + formatExecutionError, + getFromLocalStorage, +} from './helpers'; import { Request, Response } from './types'; import { painlessContextOptions } from './constants'; @@ -44,6 +49,8 @@ export function PainlessPlayground({ getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) ); + const [showRequestFlyout, setShowRequestFlyout] = useState(false); + const buildRequestPayloadPreview = () => buildRequestPayload(code, context, contextSetup); const submit = async () => { @@ -62,131 +69,73 @@ export function PainlessPlayground({ } }; - const renderExecuteBtn = () => ( - - - - ); + const toggleViewRequestFlyout = () => { + setShowRequestFlyout(!showRequestFlyout); + }; - return ( - + const renderMainControls = () => ( + - -

- {i18n.translate('xpack.painless_playground.title', { - defaultMessage: 'Painless Playground', - })} -

-
+ + + +
+ + + {i18n.translate('xpack.painless_playground.previewRequestButtonLabel', { + defaultMessage: 'Preview Request', + })} + + +
+ ); - - - - - - {renderExecuteBtn()} - - - ), - }, - { - id: 'request', - name: 'Request', - content: ( - - - {'POST _scripts/painless/_execute\n'} - {formatJson(buildRequestPayloadPreview())} - - - {renderExecuteBtn()} - - ), - }, - { - id: 'settings', - name: 'Settings', - content: ( - - - - } - fullWidth - > - ) => - setContext(e.target.value) - } - /> - + return ( + <> + + + +

+ {i18n.translate('xpack.painless_playground.title', { + defaultMessage: 'Painless Playground', + })} +

+
- - } - fullWidth - labelAppend={ - - } - /> - } - > -
+ + + + setContextSetup({ params: value })} + languageId="painless" + height={250} + value={code} + onChange={setCode} options={{ fontSize: 12, minimap: { @@ -197,135 +146,235 @@ export function PainlessPlayground({ wrappingIndent: 'indent', }} /> -
-
- - {['filter', 'score'].indexOf(context) !== -1 && ( - + + {renderMainControls()} + + + ), + }, + { + id: 'settings', + name: 'Settings', + content: ( + <> + + + + + } + fullWidth + > + ) => + setContext(e.target.value) + } /> - } - fullWidth - labelAppend={ - + + + } + fullWidth + labelAppend={ + } - )} - content={ + /> + } + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ + {['filter', 'score'].indexOf(context) !== -1 && ( + } - /> - } - > - - setContextSetup( - Object.assign({}, contextSetup, { index: e.target.value }) - ) - } - /> - - )} - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - labelAppend={ - + } + /> + } + > + + setContextSetup( + Object.assign({}, contextSetup, { index: e.target.value }) + ) } - )} - content={ + /> + + )} + {['filter', 'score'].indexOf(context) !== -1 && ( + } - /> - } - > -
- - setContextSetup(Object.assign({}, contextSetup, { document: value })) + fullWidth + labelAppend={ + + } + /> } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- )} - {renderExecuteBtn()} -
-
- ), - }, - ]} - /> -
- {response.error || response.result ? ( - - - - - - {response?.result ? response?.result : formatJson(response?.error)} - + > +
+ + setContextSetup( + Object.assign({}, contextSetup, { document: value }) + ) + } + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+ + )} + + {renderMainControls()} +
), }, - { - id: 'request', - name: 'Response', - content: ( - - - {formatJson(response)} - - - ), - }, ]} /> - ) : null} -
+ {response.error || response.result ? ( + + + + + + {response.result ?? formatExecutionError(response.error)} + + + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + <> + + + + {formatJson(response)} + + + + ), + }, + ]} + /> + + ) : null} + + {showRequestFlyout && ( + setShowRequestFlyout(false)}> + + +

+ {i18n.translate('xpack.painless_playground.flyoutTitle', { + defaultMessage: 'Console Request', + })} +

+
+ + + {'POST _scripts/painless/_execute\n'} + {formatJson(buildRequestPayloadPreview())} + +
+
+ )} + ); } From 39ee9e6a1557cabf907e2e5542cae8d78e11d94a Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 22 Jan 2020 21:58:36 +0100 Subject: [PATCH 19/34] Fix types --- .../public/components/helpers.ts | 31 +++++++++++++++---- .../public/components/painless_playground.tsx | 11 ++----- .../public/components/types.ts | 20 +++++++++++- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts index 51067b8670eef..2477272297a03 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Request } from './types'; +import { Response, Request, ExecutionError, JsonObject } from './types'; export function parseJSON(text: string) { try { @@ -38,7 +38,15 @@ export function buildRequestPayload( return request; } -export function getFromLocalStorage(key: string, defaultValue: any = '', parse = false) { +/** + * Retrieves a value from the browsers local storage, provides a default + * if none is given. With the parse flag you can parse textual JSON to an object + */ +export function getFromLocalStorage( + key: string, + defaultValue: string | JsonObject = '', + parse = false +) { const value = localStorage.getItem(key); if (value && parse) { try { @@ -48,12 +56,14 @@ export function getFromLocalStorage(key: string, defaultValue: any = '', parse = } } else if (value) { return value; - } else { - return defaultValue; } + return defaultValue; } -export function formatJson(json: any): string { +/** + * Stringify a given object to JSON in a formatted way + */ +export function formatJson(json: unknown): string { try { return JSON.stringify(json, null, 2); } catch (e) { @@ -61,7 +71,16 @@ export function formatJson(json: any): string { } } -export function formatExecutionError(json: object) { +export function formatResponse(response: Response) { + if (response.result) { + return response.result; + } else if (response.error) { + return formatExecutionError(response.error); + } + return formatJson(response); +} + +export function formatExecutionError(json: ExecutionError) { if (json.script_stack && json.caused_by) { return `Unhandled Exception ${json.caused_by.type} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 4ed476af6b80f..b481b0ae70fa5 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -24,12 +24,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; -import { - buildRequestPayload, - formatJson, - formatExecutionError, - getFromLocalStorage, -} from './helpers'; +import { buildRequestPayload, formatJson, getFromLocalStorage, formatResponse } from './helpers'; import { Request, Response } from './types'; import { painlessContextOptions } from './constants'; @@ -74,7 +69,7 @@ export function PainlessPlayground({ }; const renderMainControls = () => ( - + - {response.result ?? formatExecutionError(response.error)} + {formatResponse(response)} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/types.ts b/x-pack/legacy/plugins/painless_playground/public/components/types.ts index 45a06aba6ce97..825b0a7848136 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/types.ts +++ b/x-pack/legacy/plugins/painless_playground/public/components/types.ts @@ -16,6 +16,24 @@ export interface Request { } export interface Response { - error?: { [key: string]: any }; + error?: ExecutionError; result?: string; } + +export type ExecutionErrorScriptStack = string[]; + +export interface ExecutionError { + script_stack?: ExecutionErrorScriptStack; + caused_by?: { + type: string; + reason: string; + }; + message?: string; +} + +export type JsonArray = JsonValue[]; +export type JsonValue = null | boolean | number | string | JsonObject | JsonArray; + +export interface JsonObject { + [key: string]: JsonValue; +} From 56a74a9b6f136ea38a9be33feac567130f25dc93 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 23 Jan 2020 09:19:07 +0100 Subject: [PATCH 20/34] Split up components, restructure --- .../{components => common}/constants.ts | 0 .../public/{components => common}/types.ts | 0 .../public/components/editor.tsx | 43 +++ .../public/components/main_controls.tsx | 42 +++ .../public/components/output.tsx | 48 +++ .../public/components/painless_playground.tsx | 273 ++---------------- .../public/components/settings.tsx | 180 ++++++++++++ .../public/{components => lib}/helpers.ts | 6 +- 8 files changed, 336 insertions(+), 256 deletions(-) rename x-pack/legacy/plugins/painless_playground/public/{components => common}/constants.ts (100%) rename x-pack/legacy/plugins/painless_playground/public/{components => common}/types.ts (100%) create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/editor.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/output.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/settings.tsx rename x-pack/legacy/plugins/painless_playground/public/{components => lib}/helpers.ts (93%) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/constants.ts b/x-pack/legacy/plugins/painless_playground/public/common/constants.ts similarity index 100% rename from x-pack/legacy/plugins/painless_playground/public/components/constants.ts rename to x-pack/legacy/plugins/painless_playground/public/common/constants.ts diff --git a/x-pack/legacy/plugins/painless_playground/public/components/types.ts b/x-pack/legacy/plugins/painless_playground/public/common/types.ts similarity index 100% rename from x-pack/legacy/plugins/painless_playground/public/components/types.ts rename to x-pack/legacy/plugins/painless_playground/public/common/types.ts diff --git a/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx new file mode 100644 index 0000000000000..bc5fabf4f309b --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx @@ -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 React from 'react'; +import { EuiSpacer, EuiPageContent } from '@elastic/eui'; +import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; + +interface Props { + code: string; + setCode: (code: string) => void; + renderMainControls: () => React.ReactElement; +} + +export function Editor({ code, setCode, renderMainControls }: Props) { + return ( + <> + + + + + + + {renderMainControls()} + + + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx new file mode 100644 index 0000000000000..d087663c9b58e --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx @@ -0,0 +1,42 @@ +/* + * 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 React from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +interface Props { + submit: () => void; + disabled: boolean; + toggleFlyout: () => void; +} + +export function MainControls({ submit, disabled, toggleFlyout }: Props) { + return ( + + + + + + + + + {i18n.translate('xpack.painless_playground.previewRequestButtonLabel', { + defaultMessage: 'Preview Request', + })} + + + + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output.tsx new file mode 100644 index 0000000000000..ed693cd33c82c --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/output.tsx @@ -0,0 +1,48 @@ +/* + * 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 React from 'react'; +import { EuiCodeBlock, EuiPanel, EuiTabbedContent, EuiSpacer } from '@elastic/eui'; + +import { formatJson, formatResponse } from '../lib/helpers'; +import { Response } from '../common/types'; + +export function Output({ response }: { response: Response }) { + return ( + + + + + {formatResponse(response)} + + + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + <> + + + + {formatJson(response)} + + + + ), + }, + ]} + /> + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index b481b0ae70fa5..7becc127e20f7 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -5,28 +5,22 @@ */ import React, { useState } from 'react'; import { - EuiButton, EuiCodeBlock, - EuiFieldText, EuiFlexGroup, EuiFlexItem, - EuiForm, - EuiFormRow, - EuiPanel, - EuiSelect, EuiTabbedContent, EuiTitle, - EuiIconTip, EuiSpacer, EuiPageContent, EuiFlyout, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; -import { buildRequestPayload, formatJson, getFromLocalStorage, formatResponse } from './helpers'; -import { Request, Response } from './types'; -import { painlessContextOptions } from './constants'; +import { buildRequestPayload, formatJson, getFromLocalStorage } from '../lib/helpers'; +import { Request, Response } from '../common/types'; +import { Output } from './output'; +import { MainControls } from './main_controls'; +import { Settings } from './settings'; +import { Editor } from './editor'; export function PainlessPlayground({ executeCode, @@ -69,33 +63,11 @@ export function PainlessPlayground({ }; const renderMainControls = () => ( - - - - - - - - - {i18n.translate('xpack.painless_playground.previewRequestButtonLabel', { - defaultMessage: 'Preview Request', - })} - - - + ); return ( @@ -122,193 +94,20 @@ export function PainlessPlayground({ id: 'input', name: 'Code', content: ( - <> - - - - - - - {renderMainControls()} - - + ), }, { id: 'settings', name: 'Settings', content: ( - <> - - - - - } - fullWidth - > - ) => - setContext(e.target.value) - } - /> - - - - } - fullWidth - labelAppend={ - - } - /> - } - > -
- setContextSetup({ params: value })} - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - labelAppend={ - - } - /> - } - > - - setContextSetup( - Object.assign({}, contextSetup, { index: e.target.value }) - ) - } - /> - - )} - {['filter', 'score'].indexOf(context) !== -1 && ( - - } - fullWidth - labelAppend={ - - } - /> - } - > -
- - setContextSetup( - Object.assign({}, contextSetup, { document: value }) - ) - } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
- )} - - {renderMainControls()} -
-
- + ), }, ]} @@ -316,39 +115,7 @@ export function PainlessPlayground({
{response.error || response.result ? ( - - - - - {formatResponse(response)} - - - - ), - }, - { - id: 'request', - name: 'Response', - content: ( - <> - - - - {formatJson(response)} - - - - ), - }, - ]} - /> + ) : null}
diff --git a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx new file mode 100644 index 0000000000000..654831fe138e4 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx @@ -0,0 +1,180 @@ +/* + * 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 React from 'react'; +import { + EuiFieldText, + EuiForm, + EuiFormRow, + EuiPanel, + EuiSelect, + EuiIconTip, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; +import { painlessContextOptions } from '../common/constants'; + +interface Props { + context: string; + contextSetup: Record; + setContext: (context: string) => void; + setContextSetup: (contextSetup: Record) => void; + renderMainControls: () => React.ReactElement; +} + +export function Settings({ + context, + contextSetup, + setContext, + setContextSetup, + renderMainControls, +}: Props) { + return ( + <> + + + + + } + fullWidth + > + ) => setContext(e.target.value)} + /> + + + + } + fullWidth + labelAppend={ + + } + /> + } + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + labelAppend={ + + } + /> + } + > + + setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) + } + /> + + )} + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + labelAppend={ + + } + /> + } + > +
+ + setContextSetup(Object.assign({}, contextSetup, { document: value })) + } + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + }} + /> +
+
+ )} + + {renderMainControls()} +
+
+ + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts similarity index 93% rename from x-pack/legacy/plugins/painless_playground/public/components/helpers.ts rename to x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts index 2477272297a03..a6ab9a90500cc 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Response, Request, ExecutionError, JsonObject } from './types'; +import { Response, Request, ExecutionError, JsonObject } from '../common/types'; export function parseJSON(text: string) { try { @@ -71,7 +71,7 @@ export function formatJson(json: unknown): string { } } -export function formatResponse(response: Response) { +export function formatResponse(response: Response): string { if (response.result) { return response.result; } else if (response.error) { @@ -80,7 +80,7 @@ export function formatResponse(response: Response) { return formatJson(response); } -export function formatExecutionError(json: ExecutionError) { +export function formatExecutionError(json: ExecutionError): string { if (json.script_stack && json.caused_by) { return `Unhandled Exception ${json.caused_by.type} From 0554d13a2054d3bee33c27424e6d4889d27e8f93 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 23 Jan 2020 09:40:45 +0100 Subject: [PATCH 21/34] Replace newline in results --- .../legacy/plugins/painless_playground/public/lib/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts index a6ab9a90500cc..af288ea53b275 100644 --- a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts @@ -72,8 +72,8 @@ export function formatJson(json: unknown): string { } export function formatResponse(response: Response): string { - if (response.result) { - return response.result; + if (typeof response.result === 'string') { + return response.result.replace(/\\n/g, '\n'); } else if (response.error) { return formatExecutionError(response.error); } From 5a480700f15c9ca00b551e77f7b06d0828c38ed7 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 23 Jan 2020 11:32:20 +0100 Subject: [PATCH 22/34] Fix i18n --- x-pack/legacy/plugins/painless_playground/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index 16b13c040d412..6ceb5e97ad6b0 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -29,11 +29,11 @@ export const painlessPlayground = (kibana: any) => // Register feature flag server.newPlatform.setup.core.uiSettings.register({ [ADVANCED_SETTINGS_FLAG_NAME]: { - name: i18n.translate('kbn.advancedSettings.devTools.painlessPlaygroundTitle', { + name: i18n.translate('xpack.painless_playground.devTools.painlessPlaygroundTitle', { defaultMessage: 'Painless Playground', }), description: i18n.translate( - 'kbn.advancedSettings.devTools.painlessPlaygroundDescription', + 'xpack.painless_playground.devTools.painlessPlaygroundDescription', { defaultMessage: 'Enable experimental Painless Playground.', } From 98f01f0ff9fdd0d9b2566a47f1bcb5d52f8dc14b Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 30 Jan 2020 14:28:34 +0100 Subject: [PATCH 23/34] Improve issues found while preparing the demo --- .../plugins/painless_playground/public/components/editor.tsx | 2 +- .../public/components/painless_playground.tsx | 2 +- .../plugins/painless_playground/public/components/settings.tsx | 2 +- x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx index bc5fabf4f309b..1a9ba18fa7dc0 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx @@ -21,7 +21,7 @@ export function Editor({ code, setCode, renderMainControls }: Props) { - {response.error || response.result ? ( + {response.error || typeof response.result !== 'undefined' ? ( diff --git a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx index 654831fe138e4..8ea2c9929da29 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx @@ -79,7 +79,7 @@ export function Settings({
setContextSetup({ params: value })} options={{ diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts index af288ea53b275..43edd08fd043e 100644 --- a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts @@ -30,7 +30,7 @@ export function buildRequestPayload( request.context = context; request.context_setup = { index: contextSetup.index, - document: parseJSON(contextSetup.doc), + document: parseJSON(contextSetup.document), }; return request; } From 4f7c9e278f2b91ff8e019be565997e77b6595a46 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 6 Feb 2020 15:49:49 -0800 Subject: [PATCH 24/34] Change dev_tools to devTools to accommodate change to plugin name. --- x-pack/legacy/plugins/painless_playground/public/register.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.ts index a0812ae688943..74d59ab2af228 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.ts @@ -31,7 +31,7 @@ npSetup.core.uiSettings.get$(ADVANCED_SETTINGS_FLAG_NAME, false).subscribe(value console.log('use this to figure out whether we should register', value); }); -npSetup.plugins.dev_tools.register({ +npSetup.plugins.devTools.register({ order: 7, title: i18n.translate('xpack.painless_playground.displayName', { defaultMessage: 'Painless Playground', From 7785ccd9597deecdb529ebe24dcca2462aa47735 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 6 Feb 2020 17:17:37 -0800 Subject: [PATCH 25/34] Convert to 2-panel layout. Move Context tab into right panel. Use EuiBottomBar for main controls. --- .../plugins/painless_playground/index.ts | 1 + .../public/components/editor.tsx | 45 ++- .../public/components/main_controls.tsx | 59 ++-- .../public/components/output.tsx | 79 ++++-- .../public/components/painless_playground.tsx | 67 ++--- .../public/components/settings.tsx | 257 ++++++++---------- .../painless_playground/public/index.scss | 29 ++ .../painless_playground/public/lib/helpers.ts | 6 +- 8 files changed, 281 insertions(+), 262 deletions(-) create mode 100644 x-pack/legacy/plugins/painless_playground/public/index.scss diff --git a/x-pack/legacy/plugins/painless_playground/index.ts b/x-pack/legacy/plugins/painless_playground/index.ts index 6ceb5e97ad6b0..caa2cff4eb826 100644 --- a/x-pack/legacy/plugins/painless_playground/index.ts +++ b/x-pack/legacy/plugins/painless_playground/index.ts @@ -23,6 +23,7 @@ export const painlessPlayground = (kibana: any) => }).default(); }, uiExports: { + styleSheetPaths: resolve(__dirname, 'public/index.scss'), devTools: [resolve(__dirname, 'public/register')], }, init: (server: Legacy.Server) => { diff --git a/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx index 1a9ba18fa7dc0..f77f4ab888be6 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/editor.tsx @@ -10,34 +10,27 @@ import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; interface Props { code: string; setCode: (code: string) => void; - renderMainControls: () => React.ReactElement; } -export function Editor({ code, setCode, renderMainControls }: Props) { +export function Editor({ code, setCode }: Props) { return ( - <> - - - - - - - {renderMainControls()} - - + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx index d087663c9b58e..90269e74084ac 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiBottomBar, EuiButton, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -14,29 +14,40 @@ interface Props { toggleFlyout: () => void; } -export function MainControls({ submit, disabled, toggleFlyout }: Props) { +export function MainControls({ submit, disabled, toggleFlyout, isFlyoutOpen }: Props) { return ( - - - - - - - - - {i18n.translate('xpack.painless_playground.previewRequestButtonLabel', { - defaultMessage: 'Preview Request', - })} - - - + <> +
+ + + + + + {isFlyoutOpen + ? i18n.translate('xpack.painless_playground.hideRequestButtonLabel', { + defaultMessage: 'Hide request', + }) + : i18n.translate('xpack.painless_playground.showRequestButtonLabel', { + defaultMessage: 'Show request', + })} + + + + + + + + + + + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output.tsx index ed693cd33c82c..2db766420b844 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/output.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/output.tsx @@ -8,41 +8,64 @@ import { EuiCodeBlock, EuiPanel, EuiTabbedContent, EuiSpacer } from '@elastic/eu import { formatJson, formatResponse } from '../lib/helpers'; import { Response } from '../common/types'; +import { Settings } from './settings'; -export function Output({ response }: { response: Response }) { +export function Output({ + response, + context, + contextSetup, + setContext, + setContextSetup, +}: { + response?: Response; +}) { return ( - - - + + + {formatResponse(response)} - - - ), - }, - { - id: 'request', - name: 'Response', - content: ( - <> - - + + ), + }, + { + id: 'settings', + name: 'Context', + content: ( + <> + + + + ), + }, + { + id: 'request', + name: 'Response', + content: ( + <> + {formatJson(response)} - - - ), - }, - ]} - /> + + ), + }, + ]} + /> + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 292aedc54bfa1..446d1924a5300 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -19,7 +19,6 @@ import { buildRequestPayload, formatJson, getFromLocalStorage } from '../lib/hel import { Request, Response } from '../common/types'; import { Output } from './output'; import { MainControls } from './main_controls'; -import { Settings } from './settings'; import { Editor } from './editor'; export function PainlessPlayground({ @@ -62,23 +61,10 @@ export function PainlessPlayground({ setShowRequestFlyout(!showRequestFlyout); }; - const renderMainControls = () => ( - - ); - return ( <> - - + +

{i18n.translate('xpack.painless_playground.title', { @@ -87,40 +73,29 @@ export function PainlessPlayground({

- - ), - }, - { - id: 'settings', - name: 'Settings', - content: ( - - ), - }, - ]} + +
+ + + - {response.error || typeof response.result !== 'undefined' ? ( - - - - ) : null}
+ + + {showRequestFlyout && ( - setShowRequestFlyout(false)}> + setShowRequestFlyout(false)} maxWidth={640}>

diff --git a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx index 8ea2c9929da29..9adbeeb853932 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx @@ -26,155 +26,138 @@ interface Props { renderMainControls: () => React.ReactElement; } -export function Settings({ - context, - contextSetup, - setContext, - setContextSetup, - renderMainControls, -}: Props) { +export function Settings({ context, contextSetup, setContext, setContextSetup }: Props) { return ( - <> - - - - - } - fullWidth - > - ) => setContext(e.target.value)} - /> - + + + } + fullWidth + > + ) => setContext(e.target.value)} + /> + - + } + fullWidth + labelAppend={ + } - fullWidth - labelAppend={ - - } - /> - } - > -
- setContextSetup({ params: value })} - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
-
+ /> + } + > +
+ setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + automaticLayout: true, + }} + /> +
+ - {['filter', 'score'].indexOf(context) !== -1 && ( - + } + fullWidth + labelAppend={ + } - fullWidth - labelAppend={ - - } - /> - } - > - - setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) - } - /> - - )} - {['filter', 'score'].indexOf(context) !== -1 && ( - + } + > + + setContextSetup(Object.assign({}, contextSetup, { index: e.target.value })) + } + /> + + )} + {['filter', 'score'].indexOf(context) !== -1 && ( + + } + fullWidth + labelAppend={ + } - fullWidth - labelAppend={ - - } - /> + /> + } + > +
+ + setContextSetup(Object.assign({}, contextSetup, { document: value })) } - > -
- - setContextSetup(Object.assign({}, contextSetup, { document: value })) - } - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - }} - /> -
- - )} - - {renderMainControls()} - - - + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + automaticLayout: true, + }} + /> +
+
+ )} +
); } diff --git a/x-pack/legacy/plugins/painless_playground/public/index.scss b/x-pack/legacy/plugins/painless_playground/public/index.scss new file mode 100644 index 0000000000000..7e062f97c46ed --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/index.scss @@ -0,0 +1,29 @@ +// Import the EUI global scope so we can use EUI constants +@import 'src/legacy/ui/public/styles/_styling_constants'; + +/** + * 1. This is a very brittle way of preventing the editor and other content from disappearing + * behind the bottom bar. + */ +.painlessPlaygroundBottomBarPlaceholder { + height: $euiSize * 4.5; /* [1] */ +} + +.painlessPlaygroundRightPane { + border-right: none; + border-top: none; + border-bottom: none; + border-radius: 0; + padding-top: 0; + height: 100%; +} + +.painlessPlaygroundRightPane__tabs { + display: flex; + flex-direction: column; + height: 100%; + + [role="tabpanel"] { + height: 100%; + } +} diff --git a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts index 43edd08fd043e..ddc633b2e99d7 100644 --- a/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts +++ b/x-pack/legacy/plugins/painless_playground/public/lib/helpers.ts @@ -71,7 +71,11 @@ export function formatJson(json: unknown): string { } } -export function formatResponse(response: Response): string { +export function formatResponse(response?: Response): string { + if (!response) { + return ''; + } + if (typeof response.result === 'string') { return response.result.replace(/\\n/g, '\n'); } else if (response.error) { From 27e5cacaa0f2a2447db01b627ce212a33a3aca97 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 7 Feb 2020 16:33:09 -0800 Subject: [PATCH 26/34] Live-update output as the user enters input for a faster iteration loop. Use heart script as the example script. --- .../public/components/painless_playground.tsx | 101 +++++++++++++----- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 446d1924a5300..c43a7b3c75bc7 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -3,7 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; +import { debounce } from 'lodash'; import { EuiCodeBlock, EuiFlexGroup, @@ -21,41 +22,93 @@ import { Output } from './output'; import { MainControls } from './main_controls'; import { Editor } from './editor'; +let _mostRecentRequestId = 0; + +const submit = async (code, context, contextSetup, executeCode, setResponse) => { + // Prevent an older request that resolves after a more recent request from clobbering it. + // We store the resulting ID in this closure for comparison when the request resolves. + const requestId = ++_mostRecentRequestId; + + try { + localStorage.setItem('painlessPlaygroundCode', code); + localStorage.setItem('painlessPlaygroundContext', context); + localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); + const res = await executeCode(buildRequestPayload(code, context, contextSetup)); + + if (_mostRecentRequestId === requestId) { + setResponse(res); + } + } catch (error) { + if (_mostRecentRequestId === requestId) { + setResponse({ + error, + }); + } + } +}; + +const debouncedSubmit = debounce(submit, 800); + +// Render a heart as an example. +const exampleScript = ` +def result = ''; +int charCount = 0; + +int n = 10; +int threshold = n*n/2; +int dimension = 3*n/2; + +for (int i = -dimension; i <= n; i++) { + int a = -n/2-i; + + for (int j = -dimension; j <= dimension; j++) { + int b = n/2-j; + int c = -n/2-j; + + def isHeartVentricles = (Math.abs(i) + Math.abs(j) < n); + def isRightAtrium = ((a * a) + (b * b) <= threshold); + def isLeftAtrium = ((a * a) + (c * c) <= threshold); + + if (isHeartVentricles || isRightAtrium || isLeftAtrium) { + result += "* "; + } else { + result += ". "; + } + + // Make sure the heart doesn't deform as the container changes width. + charCount++; + if (charCount % 31 === 0) { + result += "\\\\n"; + } + } +} + +return result; +`; + export function PainlessPlayground({ executeCode, }: { executeCode: (payload: Request) => Promise; }) { - const [code, setCode] = useState( - getFromLocalStorage('painlessPlaygroundCode', 'return "Hello painless world!"') - ); + const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', exampleScript)); + const [response, setResponse] = useState({}); + const [context, setContext] = useState( getFromLocalStorage('painlessPlaygroundContext', 'painless_test_without_params') ); + const [contextSetup, setContextSetup] = useState( getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) ); const [showRequestFlyout, setShowRequestFlyout] = useState(false); - const buildRequestPayloadPreview = () => buildRequestPayload(code, context, contextSetup); - - const submit = async () => { - try { - localStorage.setItem('painlessPlaygroundCode', code); - localStorage.setItem('painlessPlaygroundContext', context); - localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); - const res = await executeCode(buildRequestPayloadPreview()); - setResponse(res); - } catch (e) { - setResponse({ - error: { - message: e.message, - }, - }); - } - }; + // Live-update the output as the user changes the input code. + useEffect(() => { + debouncedSubmit(code, context, contextSetup, executeCode, setResponse); + }, [code, context, contextSetup, executeCode]); const toggleViewRequestFlyout = () => { setShowRequestFlyout(!showRequestFlyout); @@ -88,7 +141,7 @@ export function PainlessPlayground({ submit(code, context, contextSetup, executeCode, setResponse)} disabled={code.trim() === ''} toggleFlyout={toggleViewRequestFlyout} isFlyoutOpen={showRequestFlyout} @@ -100,14 +153,14 @@ export function PainlessPlayground({

{i18n.translate('xpack.painless_playground.flyoutTitle', { - defaultMessage: 'Console Request', + defaultMessage: 'Test script request', })}

{'POST _scripts/painless/_execute\n'} - {formatJson(buildRequestPayloadPreview())} + {formatJson(buildRequestPayload(code, context, contextSetup))} From fb3dc4b43211bc3b036bb7050f69ee19c4c86568 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 7 Feb 2020 16:51:27 -0800 Subject: [PATCH 27/34] Move response into a tab in the request flyout. --- .../components/{settings.tsx => context.tsx} | 2 +- .../public/components/main_controls.tsx | 4 +- .../public/components/painless_playground.tsx | 31 +++------ .../public/components/request_flyout.tsx | 69 +++++++++++++++++++ .../components/{output.tsx => right_pane.tsx} | 18 +---- 5 files changed, 85 insertions(+), 39 deletions(-) rename x-pack/legacy/plugins/painless_playground/public/components/{settings.tsx => context.tsx} (98%) create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx rename x-pack/legacy/plugins/painless_playground/public/components/{output.tsx => right_pane.tsx} (77%) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx b/x-pack/legacy/plugins/painless_playground/public/components/context.tsx similarity index 98% rename from x-pack/legacy/plugins/painless_playground/public/components/settings.tsx rename to x-pack/legacy/plugins/painless_playground/public/components/context.tsx index 9adbeeb853932..508dac9d879d4 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/settings.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/context.tsx @@ -26,7 +26,7 @@ interface Props { renderMainControls: () => React.ReactElement; } -export function Settings({ context, contextSetup, setContext, setContextSetup }: Props) { +export function Context({ context, contextSetup, setContext, setContextSetup }: Props) { return ( {isFlyoutOpen ? i18n.translate('xpack.painless_playground.hideRequestButtonLabel', { - defaultMessage: 'Hide request', + defaultMessage: 'Hide API request', }) : i18n.translate('xpack.painless_playground.showRequestButtonLabel', { - defaultMessage: 'Show request', + defaultMessage: 'Show API request', })} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index c43a7b3c75bc7..f834a74c11199 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import React, { useState, useEffect } from 'react'; import { debounce } from 'lodash'; import { @@ -18,9 +19,10 @@ import { import { i18n } from '@kbn/i18n'; import { buildRequestPayload, formatJson, getFromLocalStorage } from '../lib/helpers'; import { Request, Response } from '../common/types'; -import { Output } from './output'; +import { RightPane } from './right_pane'; import { MainControls } from './main_controls'; import { Editor } from './editor'; +import { RequestFlyout } from './request_flyout'; let _mostRecentRequestId = 0; @@ -92,8 +94,8 @@ export function PainlessPlayground({ executeCode: (payload: Request) => Promise; }) { const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', exampleScript)); - const [response, setResponse] = useState({}); + const [showRequestFlyout, setShowRequestFlyout] = useState(false); const [context, setContext] = useState( getFromLocalStorage('painlessPlaygroundContext', 'painless_test_without_params') @@ -103,8 +105,6 @@ export function PainlessPlayground({ getFromLocalStorage('painlessPlaygroundContextSetup', {}, true) ); - const [showRequestFlyout, setShowRequestFlyout] = useState(false); - // Live-update the output as the user changes the input code. useEffect(() => { debouncedSubmit(code, context, contextSetup, executeCode, setResponse); @@ -130,7 +130,7 @@ export function PainlessPlayground({ - {showRequestFlyout && ( - setShowRequestFlyout(false)} maxWidth={640}> - - -

- {i18n.translate('xpack.painless_playground.flyoutTitle', { - defaultMessage: 'Test script request', - })} -

-
- - - {'POST _scripts/painless/_execute\n'} - {formatJson(buildRequestPayload(code, context, contextSetup))} - -
-
+ setShowRequestFlyout(false)} + requestBody={formatJson(buildRequestPayload(code, context, contextSetup))} + response={formatJson(response)} + /> )} ); diff --git a/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx b/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx new file mode 100644 index 0000000000000..3476c0a0cf0a4 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx @@ -0,0 +1,69 @@ +/* + * 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 React, { useState, useEffect } from 'react'; +import { + EuiCodeBlock, + EuiTabbedContent, + EuiTitle, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export function RequestFlyout({ + onClose, + requestBody, + response, +}: { + onClose: any; + requestBody: string; + response?: string; +}) { + return ( + + + +

+ {i18n.translate('xpack.painless_playground.flyoutTitle', { + defaultMessage: 'API request', + })} +

+
+
+ + + + {'POST _scripts/painless/_execute\n'} + {requestBody} + + ), + }, + { + id: 'response', + name: 'Response', + content: ( + + {response} + + ), + }, + ]} + /> + +
+ + + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output.tsx b/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx similarity index 77% rename from x-pack/legacy/plugins/painless_playground/public/components/output.tsx rename to x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx index 2db766420b844..91bfb766172cb 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/output.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx @@ -8,9 +8,9 @@ import { EuiCodeBlock, EuiPanel, EuiTabbedContent, EuiSpacer } from '@elastic/eu import { formatJson, formatResponse } from '../lib/helpers'; import { Response } from '../common/types'; -import { Settings } from './settings'; +import { Context } from './context'; -export function Output({ +export function RightPane({ response, context, contextSetup, @@ -43,7 +43,7 @@ export function Output({ content: ( <> - ), }, - { - id: 'request', - name: 'Response', - content: ( - <> - - - {formatJson(response)} - - - ), - }, ]} /> From f9eb8c385a1f97be670c16546ae87fb34bb2ec15 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 7 Feb 2020 21:53:54 -0800 Subject: [PATCH 28/34] Remove unnecessary 'Test' button. Surface loading/success/error state in 'Output' tab. --- .../public/components/main_controls.tsx | 22 ++------- .../public/components/painless_playground.tsx | 32 +++++++++---- .../public/components/right_pane.tsx | 46 +++++++++++++++++-- 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx index aad42442d6953..e054054f8b1fa 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { EuiBottomBar, EuiButton, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import { EuiBottomBar, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -12,9 +12,11 @@ interface Props { submit: () => void; disabled: boolean; toggleFlyout: () => void; + isFlyoutOpen: boolean; + isLoading: boolean; } -export function MainControls({ submit, disabled, toggleFlyout, isFlyoutOpen }: Props) { +export function MainControls({ submit, toggleFlyout, isFlyoutOpen, isLoading }: Props) { return ( <>
@@ -22,12 +24,7 @@ export function MainControls({ submit, disabled, toggleFlyout, isFlyoutOpen }: P - + {isFlyoutOpen ? i18n.translate('xpack.painless_playground.hideRequestButtonLabel', { defaultMessage: 'Hide API request', @@ -37,15 +34,6 @@ export function MainControls({ submit, disabled, toggleFlyout, isFlyoutOpen }: P })} - - - - - - diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index f834a74c11199..1be11eb155003 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -26,25 +26,39 @@ import { RequestFlyout } from './request_flyout'; let _mostRecentRequestId = 0; -const submit = async (code, context, contextSetup, executeCode, setResponse) => { +const submit = async (code, context, contextSetup, executeCode, setResponse, setIsLoading) => { // Prevent an older request that resolves after a more recent request from clobbering it. // We store the resulting ID in this closure for comparison when the request resolves. const requestId = ++_mostRecentRequestId; + setIsLoading(true); try { localStorage.setItem('painlessPlaygroundCode', code); localStorage.setItem('painlessPlaygroundContext', context); localStorage.setItem('painlessPlaygroundContextSetup', JSON.stringify(contextSetup)); - const res = await executeCode(buildRequestPayload(code, context, contextSetup)); + const response = await executeCode(buildRequestPayload(code, context, contextSetup)); if (_mostRecentRequestId === requestId) { - setResponse(res); + if (response.error) { + setResponse({ + success: undefined, + error: response.error, + }); + } else { + setResponse({ + success: response, + error: undefined, + }); + } + setIsLoading(false); } } catch (error) { if (_mostRecentRequestId === requestId) { setResponse({ - error, + success: undefined, + error: { error }, }); + setIsLoading(false); } } }; @@ -94,8 +108,9 @@ export function PainlessPlayground({ executeCode: (payload: Request) => Promise; }) { const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', exampleScript)); - const [response, setResponse] = useState({}); + const [response, setResponse] = useState({ error: undefined, success: undefined }); const [showRequestFlyout, setShowRequestFlyout] = useState(false); + const [isLoading, setIsLoading] = useState(false); const [context, setContext] = useState( getFromLocalStorage('painlessPlaygroundContext', 'painless_test_without_params') @@ -107,7 +122,7 @@ export function PainlessPlayground({ // Live-update the output as the user changes the input code. useEffect(() => { - debouncedSubmit(code, context, contextSetup, executeCode, setResponse); + debouncedSubmit(code, context, contextSetup, executeCode, setResponse, setIsLoading); }, [code, context, contextSetup, executeCode]); const toggleViewRequestFlyout = () => { @@ -136,13 +151,14 @@ export function PainlessPlayground({ setContext={setContext} contextSetup={contextSetup} setContextSetup={setContextSetup} + isLoading={isLoading} /> submit(code, context, contextSetup, executeCode, setResponse)} - disabled={code.trim() === ''} + isLoading={isLoading} toggleFlyout={toggleViewRequestFlyout} isFlyoutOpen={showRequestFlyout} /> @@ -151,7 +167,7 @@ export function PainlessPlayground({ setShowRequestFlyout(false)} requestBody={formatJson(buildRequestPayload(code, context, contextSetup))} - response={formatJson(response)} + response={formatJson(response.success || response.error)} /> )} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx b/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx index 91bfb766172cb..6d19a249cb0bc 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx @@ -3,8 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import React from 'react'; -import { EuiCodeBlock, EuiPanel, EuiTabbedContent, EuiSpacer } from '@elastic/eui'; +import { + EuiCodeBlock, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiPanel, + EuiTabbedContent, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { formatJson, formatResponse } from '../lib/helpers'; import { Response } from '../common/types'; @@ -16,9 +27,30 @@ export function RightPane({ contextSetup, setContext, setContextSetup, + isLoading, }: { response?: Response; }) { + const outputTabLabel = ( + + + {isLoading ? ( + + ) : response.error ? ( + + ) : ( + + )} + + + + {i18n.translate('xpack.painless_playground.outputTabLabel', { + defaultMessage: 'Output', + })} + + + ); + return ( - {formatResponse(response)} + {formatResponse(response.success || response.error)} ), }, { id: 'settings', - name: 'Context', + name: ( + + {i18n.translate('xpack.painless_playground.contextTabLabel', { + defaultMessage: 'Context', + })} + + ), content: ( <> From 8b09b7ec927a19de3fc407219d78ce3e5119a317 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 7 Feb 2020 21:54:39 -0800 Subject: [PATCH 29/34] Add beta label to feature name in home registry and DevTools tab. --- .../painless_playground/public/index.scss | 4 +++ .../public/{register.ts => register.tsx} | 28 +++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) rename x-pack/legacy/plugins/painless_playground/public/{register.ts => register.tsx} (68%) diff --git a/x-pack/legacy/plugins/painless_playground/public/index.scss b/x-pack/legacy/plugins/painless_playground/public/index.scss index 7e062f97c46ed..8fb97857628cd 100644 --- a/x-pack/legacy/plugins/painless_playground/public/index.scss +++ b/x-pack/legacy/plugins/painless_playground/public/index.scss @@ -27,3 +27,7 @@ height: 100%; } } + +.painlessPlayground__betaLabelContainer { + line-height: 0; +} diff --git a/x-pack/legacy/plugins/painless_playground/public/register.ts b/x-pack/legacy/plugins/painless_playground/public/register.tsx similarity index 68% rename from x-pack/legacy/plugins/painless_playground/public/register.ts rename to x-pack/legacy/plugins/painless_playground/public/register.tsx index 74d59ab2af228..4908eeb758114 100644 --- a/x-pack/legacy/plugins/painless_playground/public/register.ts +++ b/x-pack/legacy/plugins/painless_playground/public/register.tsx @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; import { i18n } from '@kbn/i18n'; +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; // @ts-ignore import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { npSetup, npStart } from 'ui/new_platform'; @@ -15,7 +17,7 @@ import { ADVANCED_SETTINGS_FLAG_NAME } from '../common/constants'; npSetup.plugins.home.featureCatalogue.register({ id: 'painless_playground', title: i18n.translate('xpack.painless_playground.registryProviderTitle', { - defaultMessage: 'Painless Playground', + defaultMessage: 'Painless Playground (beta)', }), description: i18n.translate('xpack.painless_playground.registryProviderDescription', { defaultMessage: 'Simulate and debug painless code', @@ -33,10 +35,26 @@ npSetup.core.uiSettings.get$(ADVANCED_SETTINGS_FLAG_NAME, false).subscribe(value npSetup.plugins.devTools.register({ order: 7, - title: i18n.translate('xpack.painless_playground.displayName', { - defaultMessage: 'Painless Playground', - }), - id: 'painless_playground', + title: ( + + + {i18n.translate('xpack.painless_playground.displayName', { + defaultMessage: 'Painless Playground', + })} + + + + + + + ), enableRouting: false, disabled: false, tooltipContent: xpackInfo.get('features.painlessPlayground.message'), From 32a55b23fb560588570f6a9cbf7a222becb8fce6 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 7 Feb 2020 23:15:21 -0800 Subject: [PATCH 30/34] Refine Context tab form components and help text. Add doc links. --- .../public/common/constants.ts | 28 ---- .../public/common/constants.tsx | 63 ++++++++ .../public/components/context.tsx | 143 +++++++++++------- .../public/components/request_flyout.tsx | 38 ++++- 4 files changed, 185 insertions(+), 87 deletions(-) delete mode 100644 x-pack/legacy/plugins/painless_playground/public/common/constants.ts create mode 100644 x-pack/legacy/plugins/painless_playground/public/common/constants.tsx diff --git a/x-pack/legacy/plugins/painless_playground/public/common/constants.ts b/x-pack/legacy/plugins/painless_playground/public/common/constants.ts deleted file mode 100644 index 293f26b835470..0000000000000 --- a/x-pack/legacy/plugins/painless_playground/public/common/constants.ts +++ /dev/null @@ -1,28 +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 const painlessContextOptions = [ - { - value: 'painless_test', - text: i18n.translate('xpack.painless_playground.selectDefaultLabel', { - defaultMessage: 'Default - Execute as it is', - }), - }, - { - value: 'filter', - text: i18n.translate('xpack.painless_playground.selectFilterLabel', { - defaultMessage: 'Filter - Execute like inside a script query of a filter', - }), - }, - { - value: 'score', - text: i18n.translate('xpack.painless_playground.selectScoreLabel', { - defaultMessage: - 'Score - Execution like inside a script_score function in function_score query', - }), - }, -]; diff --git a/x-pack/legacy/plugins/painless_playground/public/common/constants.tsx b/x-pack/legacy/plugins/painless_playground/public/common/constants.tsx new file mode 100644 index 0000000000000..ef3feb1738118 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/common/constants.tsx @@ -0,0 +1,63 @@ +/* + * 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 React from 'react'; + +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +const defaultLabel = i18n.translate('xpack.painless_playground.contextDefaultLabel', { + defaultMessage: 'Basic', +}); + +const filterLabel = i18n.translate('xpack.painless_playground.contextFilterLabel', { + defaultMessage: 'Filter', +}); + +const scoreLabel = i18n.translate('xpack.painless_playground.contextScoreLabel', { + defaultMessage: 'Score', +}); + +export const painlessContextOptions = [ + { + value: 'painless_test', + inputDisplay: defaultLabel, + dropdownDisplay: ( + <> + {defaultLabel} + +

The script result will be converted to a string

+
+ + ), + }, + { + value: 'filter', + inputDisplay: filterLabel, + dropdownDisplay: ( + <> + {filterLabel} + +

Use the context of a filter’s script query

+
+ + ), + }, + { + value: 'score', + inputDisplay: scoreLabel, + dropdownDisplay: ( + <> + {scoreLabel} + +

+ Use the context of a cript_score function in function_score query +

+
+ + ), + }, +]; diff --git a/x-pack/legacy/plugins/painless_playground/public/components/context.tsx b/x-pack/legacy/plugins/painless_playground/public/components/context.tsx index 508dac9d879d4..82b8eab861ad2 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/context.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/context.tsx @@ -12,6 +12,11 @@ import { EuiSelect, EuiIconTip, EuiSpacer, + EuiIcon, + EuiToolTip, + EuiLink, + EuiText, + EuiSuperSelect, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -31,43 +36,78 @@ export function Context({ context, contextSetup, setContext, setContextSetup }: + + + {' '} + + + + } + labelAppend={ + + + {i18n.translate('xpack.painless_playground.contextFieldDocLinkText', { + defaultMessage: 'Context docs', + })} + + } fullWidth > - ) => setContext(e.target.value)} + valueOfSelected={context} + onChange={(value: any) => setContext(value)} + itemLayoutAlign="top" + hasDividers + fullWidth /> + + + {' '} + + + } fullWidth labelAppend={ - - } - /> + + + {i18n.translate('xpack.painless_playground.parametersFieldDocLinkText', { + defaultMessage: 'Parameters docs', + })} + + } + helpText={i18n.translate('xpack.painless_playground.helpIconAriaLabel', { + defaultMessage: 'Use JSON format', + })} > -
+ -
+
{['filter', 'score'].indexOf(context) !== -1 && ( - } - fullWidth - labelAppend={ - + - } - /> + id="xpack.painless_playground.indexFieldLabel" + defaultMessage="Index" + />{' '} + + + } + fullWidth > - } - fullWidth - labelAppend={ - + - } - /> + id="xpack.painless_playground.documentFieldLabel" + defaultMessage="Sample document" + />{' '} + + + } + fullWidth > -
+ -
+
)} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx b/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx index 3476c0a0cf0a4..60ecdd5d4799b 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/request_flyout.tsx @@ -12,6 +12,9 @@ import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,13 +30,34 @@ export function RequestFlyout({ return ( - -

- {i18n.translate('xpack.painless_playground.flyoutTitle', { - defaultMessage: 'API request', - })} -

-
+ + + {/* We need an extra div to get out of flex grow */} +
+ +

+ {i18n.translate('xpack.painless_playground.flyoutTitle', { + defaultMessage: 'API request', + })} +

+
+
+
+ + + + {i18n.translate('xpack.painless_playground.flyoutDocLink', { + defaultMessage: 'API documentation', + })} + + +
From f9b53c4a0bd28a224a027687c782b10139368702 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sat, 8 Feb 2020 13:17:49 -0800 Subject: [PATCH 31/34] Extract out parameters tab. Refactor output pane. --- .../context_tab.tsx} | 71 ++------------- .../public/components/output_pane/index.ts | 7 ++ .../output_pane.tsx} | 58 ++++++------- .../components/output_pane/output_tab.tsx | 27 ++++++ .../components/output_pane/parameters_tab.tsx | 87 +++++++++++++++++++ .../public/components/painless_playground.tsx | 4 +- 6 files changed, 161 insertions(+), 93 deletions(-) rename x-pack/legacy/plugins/painless_playground/public/components/{context.tsx => output_pane/context_tab.tsx} (67%) create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/output_pane/index.ts rename x-pack/legacy/plugins/painless_playground/public/components/{right_pane.tsx => output_pane/output_pane.tsx} (57%) create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_tab.tsx create mode 100644 x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx diff --git a/x-pack/legacy/plugins/painless_playground/public/components/context.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/context_tab.tsx similarity index 67% rename from x-pack/legacy/plugins/painless_playground/public/components/context.tsx rename to x-pack/legacy/plugins/painless_playground/public/components/output_pane/context_tab.tsx index 82b8eab861ad2..ec0f27c005b96 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/context.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/context_tab.tsx @@ -3,14 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import React from 'react'; import { EuiFieldText, - EuiForm, EuiFormRow, EuiPanel, - EuiSelect, - EuiIconTip, EuiSpacer, EuiIcon, EuiToolTip, @@ -20,8 +18,9 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; -import { painlessContextOptions } from '../common/constants'; + +import { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public'; +import { painlessContextOptions } from '../../common/constants'; interface Props { context: string; @@ -31,9 +30,10 @@ interface Props { renderMainControls: () => React.ReactElement; } -export function Context({ context, contextSetup, setContext, setContextSetup }: Props) { +export function ContextTab({ context, contextSetup, setContext, setContextSetup }: Props) { return ( - + <> + - - - {' '} - - - - } - fullWidth - labelAppend={ - - - {i18n.translate('xpack.painless_playground.parametersFieldDocLinkText', { - defaultMessage: 'Parameters docs', - })} - - - } - helpText={i18n.translate('xpack.painless_playground.helpIconAriaLabel', { - defaultMessage: 'Use JSON format', - })} - > - - setContextSetup({ params: value })} - options={{ - fontSize: 12, - minimap: { - enabled: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', - automaticLayout: true, - }} - /> - - - {['filter', 'score'].indexOf(context) !== -1 && ( setContextSetup(Object.assign({}, contextSetup, { document: value })) @@ -197,6 +144,6 @@ export function Context({ context, contextSetup, setContext, setContextSetup }: )} - + ); } diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output_pane/index.ts b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/index.ts new file mode 100644 index 0000000000000..85b7a7816b5aa --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { OutputPane } from './output_pane'; diff --git a/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_pane.tsx similarity index 57% rename from x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx rename to x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_pane.tsx index 6d19a249cb0bc..69393ca47a66c 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/right_pane.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_pane.tsx @@ -6,22 +6,22 @@ import React from 'react'; import { - EuiCodeBlock, EuiIcon, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiPanel, EuiTabbedContent, - EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { formatJson, formatResponse } from '../lib/helpers'; -import { Response } from '../common/types'; -import { Context } from './context'; +import { formatJson, formatResponse } from '../../lib/helpers'; +import { Response } from '../../common/types'; +import { OutputTab } from './output_tab'; +import { ParametersTab } from './parameters_tab'; +import { ContextTab } from './context_tab'; -export function RightPane({ +export function OutputPane({ response, context, contextSetup, @@ -60,34 +60,34 @@ export function RightPane({ { id: 'output', name: outputTabLabel, + content: , + }, + { + id: 'parameters', + name: i18n.translate('xpack.painless_playground.parametersTabLabel', { + defaultMessage: 'Parameters', + }), content: ( - <> - - - {formatResponse(response.success || response.error)} - - + ), }, { - id: 'settings', - name: ( - - {i18n.translate('xpack.painless_playground.contextTabLabel', { - defaultMessage: 'Context', - })} - - ), + id: 'context', + name: i18n.translate('xpack.painless_playground.contextTabLabel', { + defaultMessage: 'Context', + }), content: ( - <> - - - + ), }, ]} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_tab.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_tab.tsx new file mode 100644 index 0000000000000..4b69e7969c924 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/output_tab.tsx @@ -0,0 +1,27 @@ +/* + * 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 React from 'react'; +import { EuiCodeBlock, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { formatResponse } from '../../lib/helpers'; +import { Response } from '../../common/types'; + +interface Props { + response?: Response; +} + +export function OutputTab({ response = {} }: Props) { + return ( + <> + + + {formatResponse(response.success || response.error)} + + + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx new file mode 100644 index 0000000000000..1e6688f5005a8 --- /dev/null +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx @@ -0,0 +1,87 @@ +/* + * 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 React from 'react'; +import { + EuiFormRow, + EuiPanel, + EuiSpacer, + EuiIcon, + EuiToolTip, + EuiLink, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public'; +import { painlessContextOptions } from '../../common/constants'; + +interface Props { + context: string; + contextSetup: Record; + setContext: (context: string) => void; + setContextSetup: (contextSetup: Record) => void; + renderMainControls: () => React.ReactElement; +} + +export function ParametersTab({ context, contextSetup, setContext, setContextSetup }: Props) { + return ( + <> + + + + {' '} + + + + } + fullWidth + labelAppend={ + + + {i18n.translate('xpack.painless_playground.parametersFieldDocLinkText', { + defaultMessage: 'Parameters docs', + })} + + + } + helpText={i18n.translate('xpack.painless_playground.helpIconAriaLabel', { + defaultMessage: 'Use JSON format', + })} + > + + setContextSetup({ params: value })} + options={{ + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + automaticLayout: true, + }} + /> + + + + ); +} diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index 1be11eb155003..ad38a69a04763 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -19,7 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import { buildRequestPayload, formatJson, getFromLocalStorage } from '../lib/helpers'; import { Request, Response } from '../common/types'; -import { RightPane } from './right_pane'; +import { OutputPane } from './output_pane'; import { MainControls } from './main_controls'; import { Editor } from './editor'; import { RequestFlyout } from './request_flyout'; @@ -145,7 +145,7 @@ export function PainlessPlayground({ - Date: Sun, 9 Feb 2020 15:08:22 -0800 Subject: [PATCH 32/34] Add Help menu with links to docs and a button for resetting the script. --- .../public/components/main_controls.tsx | 118 ++++++++++++++++-- .../public/components/painless_playground.tsx | 15 +-- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx index e054054f8b1fa..ea32c4719d481 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx @@ -3,29 +3,127 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiBottomBar, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import React, { useState } from 'react'; +import { + EuiPopover, + EuiBottomBar, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; interface Props { - submit: () => void; - disabled: boolean; - toggleFlyout: () => void; - isFlyoutOpen: boolean; + toggleRequestFlyout: () => void; + isRequestFlyoutOpen: boolean; isLoading: boolean; + reset: () => void; } -export function MainControls({ submit, toggleFlyout, isFlyoutOpen, isLoading }: Props) { +export function MainControls({ + toggleRequestFlyout, + isRequestFlyoutOpen, + isLoading, + reset, +}: Props) { + const [isHelpOpen, setIsHelpOpen] = useState(false); + + const items = [ + setIsHelpOpen(false)} + > + {i18n.translate('xpack.painless_playground.walkthroughButtonLabel', { + defaultMessage: 'Walkthrough', + })} + , + + setIsHelpOpen(false)} + > + {i18n.translate('xpack.painless_playground.apiReferenceButtonLabel', { + defaultMessage: 'API reference', + })} + , + + setIsHelpOpen(false)} + > + {i18n.translate('xpack.painless_playground.languageSpecButtonLabel', { + defaultMessage: 'Language spec', + })} + , + + { + reset(); + setIsHelpOpen(false); + }} + > + {i18n.translate('xpack.painless_playground.resetButtonLabel', { + defaultMessage: 'Reset script', + })} + , + ]; + return ( <>
- + + + + + setIsHelpOpen(!isHelpOpen)} + > + {i18n.translate('xpack.painless_playground.helpButtonLabel', { + defaultMessage: 'Help', + })} + + } + isOpen={isHelpOpen} + closePopover={() => setIsHelpOpen(false)} + panelPaddingSize="none" + withTitle + anchorPosition="upRight" + > + + + + + - - {isFlyoutOpen + + {isRequestFlyoutOpen ? i18n.translate('xpack.painless_playground.hideRequestButtonLabel', { defaultMessage: 'Hide API request', }) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx index ad38a69a04763..1a94d42606120 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/painless_playground.tsx @@ -109,7 +109,7 @@ export function PainlessPlayground({ }) { const [code, setCode] = useState(getFromLocalStorage('painlessPlaygroundCode', exampleScript)); const [response, setResponse] = useState({ error: undefined, success: undefined }); - const [showRequestFlyout, setShowRequestFlyout] = useState(false); + const [isRequestFlyoutOpen, setRequestFlyoutOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [context, setContext] = useState( @@ -125,8 +125,8 @@ export function PainlessPlayground({ debouncedSubmit(code, context, contextSetup, executeCode, setResponse, setIsLoading); }, [code, context, contextSetup, executeCode]); - const toggleViewRequestFlyout = () => { - setShowRequestFlyout(!showRequestFlyout); + const toggleRequestFlyout = () => { + setRequestFlyoutOpen(!isRequestFlyoutOpen); }; return ( @@ -159,13 +159,14 @@ export function PainlessPlayground({ submit(code, context, contextSetup, executeCode, setResponse)} isLoading={isLoading} - toggleFlyout={toggleViewRequestFlyout} - isFlyoutOpen={showRequestFlyout} + toggleRequestFlyout={toggleRequestFlyout} + isRequestFlyoutOpen={isRequestFlyoutOpen} + reset={() => setCode(exampleScript)} /> - {showRequestFlyout && ( + {isRequestFlyoutOpen && ( setShowRequestFlyout(false)} + onClose={() => setRequestFlyoutOpen(false)} requestBody={formatJson(buildRequestPayload(code, context, contextSetup))} response={formatJson(response.success || response.error)} /> From b7b12dfd8c206440907ec450bd2a291c3e2970b4 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sun, 9 Feb 2020 15:17:37 -0800 Subject: [PATCH 33/34] Improve parameters help text. --- .../public/components/output_pane/parameters_tab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx index 1e6688f5005a8..02dd5b0971e2c 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/output_pane/parameters_tab.tsx @@ -34,7 +34,7 @@ export function ParametersTab({ context, contextSetup, setContext, setContextSet label={ From 31659601532c789356d1add9d571b3a8674efe57 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Mon, 10 Feb 2020 13:26:05 -0800 Subject: [PATCH 34/34] Make bottom bar smaller. --- .../painless_playground/public/components/main_controls.tsx | 4 +++- x-pack/legacy/plugins/painless_playground/public/index.scss | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx index ea32c4719d481..98e8f25f171b1 100644 --- a/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx +++ b/x-pack/legacy/plugins/painless_playground/public/components/main_controls.tsx @@ -87,7 +87,7 @@ export function MainControls({ <>
- + @@ -96,6 +96,7 @@ export function MainControls({ id="painlessPlaygroundHelpContextMenu" button={