Skip to content

Commit

Permalink
Create Painless Playground app (#54578)
Browse files Browse the repository at this point in the history
  • Loading branch information
kertal authored and cjcenizal committed Feb 20, 2020
1 parent a36467e commit 530b409
Show file tree
Hide file tree
Showing 25 changed files with 1,532 additions and 0 deletions.
1 change: 1 addition & 0 deletions x-pack/.i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"xpack.ml": "legacy/plugins/ml",
"xpack.monitoring": "legacy/plugins/monitoring",
"xpack.remoteClusters": "plugins/remote_clusters",
"xpack.painless_playground": "legacy/plugins/painless_playground",
"xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"],
"xpack.rollupJobs": "legacy/plugins/rollup",
"xpack.searchProfiler": "plugins/searchprofiler",
Expand Down
2 changes: 2 additions & 0 deletions x-pack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { security } from './legacy/plugins/security';
import { ml } from './legacy/plugins/ml';
import { tilemap } from './legacy/plugins/tilemap';
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';
Expand Down Expand Up @@ -51,6 +52,7 @@ module.exports = function(kibana) {
ml(kibana),
tilemap(kibana),
grokdebugger(kibana),
painlessPlayground(kibana),
dashboardMode(kibana),
logstash(kibana),
beats(kibana),
Expand Down
11 changes: 11 additions & 0 deletions x-pack/legacy/plugins/painless_playground/common/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const PLUGIN_ID = 'painless_playground';

export const API_ROUTE_EXECUTE = '/api/painless_playground/execute';

export const ADVANCED_SETTINGS_FLAG_NAME = 'devTools:enablePainlessPlayground';
50 changes: 50 additions & 0 deletions x-pack/legacy/plugins/painless_playground/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 { resolve } from 'path';
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({
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: {
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
devTools: [resolve(__dirname, 'public/register')],
},
init: (server: Legacy.Server) => {
// Register feature flag
server.newPlatform.setup.core.uiSettings.register({
[ADVANCED_SETTINGS_FLAG_NAME]: {
name: i18n.translate('xpack.painless_playground.devTools.painlessPlaygroundTitle', {
defaultMessage: 'Painless Playground',
}),
description: i18n.translate(
'xpack.painless_playground.devTools.painlessPlaygroundDescription',
{
defaultMessage: 'Enable experimental Painless Playground.',
}
),
value: false,
category: ['Dev Tools'],
},
});

registerLicenseChecker(server);
registerExecuteRoute(server);
},
});
Original file line number Diff line number Diff line change
@@ -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: (
<>
<strong>{defaultLabel}</strong>
<EuiText size="s" color="subdued">
<p className="euiTextColor--subdued">The script result will be converted to a string</p>
</EuiText>
</>
),
},
{
value: 'filter',
inputDisplay: filterLabel,
dropdownDisplay: (
<>
<strong>{filterLabel}</strong>
<EuiText size="s" color="subdued">
<p className="euiTextColor--subdued">Use the context of a filter&rsquo;s script query</p>
</EuiText>
</>
),
},
{
value: 'score',
inputDisplay: scoreLabel,
dropdownDisplay: (
<>
<strong>{scoreLabel}</strong>
<EuiText size="s" color="subdued">
<p className="euiTextColor--subdued">
Use the context of a cript_score function in function_score query
</p>
</EuiText>
</>
),
},
];
39 changes: 39 additions & 0 deletions x-pack/legacy/plugins/painless_playground/public/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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<string, unknown>;
};
context?: string;
context_setup?: {
document: Record<string, unknown>;
index: string;
};
}

export interface Response {
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* 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;
}

export function Editor({ code, setCode }: Props) {
return (
<CodeEditor
languageId="painless"
// 99% width allows the editor to resize horizontally. 100% prevents it from resizing.
width="99%"
height="100%"
value={code}
onChange={setCode}
options={{
fontSize: 12,
minimap: {
enabled: false,
},
scrollBeyondLastLine: false,
wordWrap: 'on',
wrappingIndent: 'indent',
automaticLayout: true,
}}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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 } 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 {
toggleRequestFlyout: () => void;
isRequestFlyoutOpen: boolean;
isLoading: boolean;
reset: () => void;
}

export function MainControls({
toggleRequestFlyout,
isRequestFlyoutOpen,
isLoading,
reset,
}: Props) {
const [isHelpOpen, setIsHelpOpen] = useState(false);

const items = [
<EuiContextMenuItem
key="walkthrough"
icon="popout"
href="https://www.elastic.co/guide/en/elasticsearch/painless/7.5/painless-walkthrough.html"
target="_blank"
onClick={() => setIsHelpOpen(false)}
>
{i18n.translate('xpack.painless_playground.walkthroughButtonLabel', {
defaultMessage: 'Walkthrough',
})}
</EuiContextMenuItem>,

<EuiContextMenuItem
key="api"
icon="popout"
href="https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-api-reference.html"
target="_blank"
onClick={() => setIsHelpOpen(false)}
>
{i18n.translate('xpack.painless_playground.apiReferenceButtonLabel', {
defaultMessage: 'API reference',
})}
</EuiContextMenuItem>,

<EuiContextMenuItem
key="languageSpec"
icon="popout"
href="https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-lang-spec.html"
target="_blank"
onClick={() => setIsHelpOpen(false)}
>
{i18n.translate('xpack.painless_playground.languageSpecButtonLabel', {
defaultMessage: 'Language spec',
})}
</EuiContextMenuItem>,

<EuiContextMenuItem
key="reset"
icon="bolt"
onClick={() => {
reset();
setIsHelpOpen(false);
}}
>
{i18n.translate('xpack.painless_playground.resetButtonLabel', {
defaultMessage: 'Reset script',
})}
</EuiContextMenuItem>,
];

return (
<>
<div className="painlessPlaygroundBottomBarPlaceholder" />

<EuiBottomBar paddingSize="none">
<EuiFlexGroup gutterSize="s" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" justifyContent="flexStart">
<EuiFlexItem grow={false}>
<EuiPopover
id="painlessPlaygroundHelpContextMenu"
button={
<EuiButtonEmpty
size="s"
iconType="help"
iconSide="left"
color="ghost"
onClick={() => setIsHelpOpen(!isHelpOpen)}
>
{i18n.translate('xpack.painless_playground.helpButtonLabel', {
defaultMessage: 'Help',
})}
</EuiButtonEmpty>
}
isOpen={isHelpOpen}
closePopover={() => setIsHelpOpen(false)}
panelPaddingSize="none"
withTitle
anchorPosition="upRight"
>
<EuiContextMenuPanel items={items} />
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="s"
color="ghost"
onClick={toggleRequestFlyout}
data-test-subj="btnViewRequest"
>
{isRequestFlyoutOpen
? i18n.translate('xpack.painless_playground.hideRequestButtonLabel', {
defaultMessage: 'Hide API request',
})
: i18n.translate('xpack.painless_playground.showRequestButtonLabel', {
defaultMessage: 'Show API request',
})}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiBottomBar>
</>
);
}
Loading

0 comments on commit 530b409

Please sign in to comment.