mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
EUIFication: Grok Debugger (#20027)
* Move <kbn-dev-tools-app> Angular wrapper directive usage into route template * Move sole directive into new directives folder * WIP checkin: basic conversion to React components * Fix sample custom patterns indentation * Remove custom CSS * Wrap button in EuiFormRow + define isSimulateDisabled * Wire up simulate * Cleanup * Ace formatting options * Better styling * Adding spacing between custom patterns and simulate button * Fixing form row widths * Removing form row around button * Add indentation/newlines in structured output * Error handling * Use constants * Removing no-longer-used code * Implement syntax highlighting via custom mode * Making functional tests pass * Adding trailing comma back * Removing fixed heights * Removing unnecessary styles * Make Event Output form row full width as well * Wrapping EuiCodeEditors in EuiPanels * Adding spacer before callout; making spacing around callout consistent * Clear out custom patterns from request if field is cleared out * Clear out simulation results before attempting simulation * Set state with untrimmed value
This commit is contained in:
parent
09a0ffe350
commit
30c8ccd41c
30 changed files with 319 additions and 472 deletions
|
@ -9,6 +9,4 @@ export const EDITOR = {
|
||||||
PATTERN_MAX_LINES: 10,
|
PATTERN_MAX_LINES: 10,
|
||||||
SAMPLE_DATA_MIN_LINES: 3,
|
SAMPLE_DATA_MIN_LINES: 3,
|
||||||
SAMPLE_DATA_MAX_LINES: 50,
|
SAMPLE_DATA_MAX_LINES: 50,
|
||||||
SCROLL_MARGIN_TOP_PX: 4,
|
|
||||||
SCROLL_MARGIN_BOTTOM_PX: 4,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,32 +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 { EDITOR } from '../../../common/constants';
|
|
||||||
|
|
||||||
export function applyEditorOptions(editor, minLines, maxLines) {
|
|
||||||
editor.getSession().setUseWrapMode(true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This sets the space between the editor's borders and the
|
|
||||||
* edges of the top/bottom lines to make for a less-crowded
|
|
||||||
* typing experience.
|
|
||||||
*/
|
|
||||||
editor.renderer.setScrollMargin(
|
|
||||||
EDITOR.SCROLL_MARGIN_TOP_PX,
|
|
||||||
EDITOR.SCROLL_MARGIN_BOTTOM_PX,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.setOptions({
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightGutterLine: false,
|
|
||||||
minLines,
|
|
||||||
maxLines
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.$blockScrolling = Infinity;
|
|
||||||
}
|
|
|
@ -1,57 +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 { applyEditorOptions } from './apply_editor_options';
|
|
||||||
import { EDITOR } from '../../../common/constants';
|
|
||||||
|
|
||||||
describe('applyEditorOptions', () => {
|
|
||||||
let editor;
|
|
||||||
let setUseWrapMode;
|
|
||||||
let setScrollMargin;
|
|
||||||
let setOptions;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
setUseWrapMode = jest.fn();
|
|
||||||
setScrollMargin = jest.fn();
|
|
||||||
setOptions = jest.fn();
|
|
||||||
|
|
||||||
editor = {
|
|
||||||
getSession: () => {
|
|
||||||
return { setUseWrapMode };
|
|
||||||
},
|
|
||||||
renderer: {
|
|
||||||
setScrollMargin,
|
|
||||||
},
|
|
||||||
setOptions,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates default props and given line sizes', () => {
|
|
||||||
const minLines = 14;
|
|
||||||
const maxLines = 90;
|
|
||||||
|
|
||||||
applyEditorOptions(editor, minLines, maxLines);
|
|
||||||
|
|
||||||
expect(setUseWrapMode.mock.calls).toHaveLength(1);
|
|
||||||
expect(setUseWrapMode.mock.calls[0][0]).toBe(true);
|
|
||||||
|
|
||||||
expect(setScrollMargin.mock.calls).toHaveLength(1);
|
|
||||||
expect(setScrollMargin.mock.calls[0][0]).toEqual(EDITOR.SCROLL_MARGIN_TOP_PX);
|
|
||||||
expect(setScrollMargin.mock.calls[0][1]).toEqual(EDITOR.SCROLL_MARGIN_BOTTOM_PX);
|
|
||||||
expect(setScrollMargin.mock.calls[0][2]).toEqual(0);
|
|
||||||
expect(setScrollMargin.mock.calls[0][3]).toEqual(0);
|
|
||||||
|
|
||||||
expect(setOptions.mock.calls).toHaveLength(1);
|
|
||||||
expect(setOptions.mock.calls[0][0]).toEqual({
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightGutterLine: false,
|
|
||||||
minLines: 14,
|
|
||||||
maxLines: 90,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(editor.$blockScrolling).toEqual(Infinity);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -5,4 +5,3 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { GrokMode } from './grok_mode';
|
export { GrokMode } from './grok_mode';
|
||||||
export { applyEditorOptions } from './apply_editor_options';
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<form novalidate>
|
|
||||||
<toggle-panel
|
|
||||||
toggle-panel-id="action"
|
|
||||||
button-text="Custom Patterns"
|
|
||||||
is-collapsed="customPatternsInput.isSectionCollapsed('action')"
|
|
||||||
on-toggle="customPatternsInput.onSectionToggle"
|
|
||||||
data-test-subj="btnToggleCustomPatternsInput"
|
|
||||||
>
|
|
||||||
<div class="kuiFormSection">
|
|
||||||
<div class="kuiInfoPanel kuiInfoPanel--info kuiVerticalRhythm">
|
|
||||||
<div class="kuiInfoPanelHeader">
|
|
||||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--info fa-info"></span>
|
|
||||||
<span class="kuiInfoPanelHeader__title">
|
|
||||||
Enter one custom pattern per line. For example:
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kuiInfoPanelBody">
|
|
||||||
<div class="kuiInfoPanelBody__message">
|
|
||||||
<pre><code>POSTFIX_QUEUEID [0-9A-F]{10,11}
|
|
||||||
MSG message-id=<%{GREEDYDATA}></code></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grokdebugger-ace-editor">
|
|
||||||
<div
|
|
||||||
class="custom-patterns-input-editor kuiVerticalRhythm"
|
|
||||||
require-keys="true"
|
|
||||||
ui-ace="{
|
|
||||||
onLoad: aceLoaded
|
|
||||||
}"
|
|
||||||
ng-model="customPatternsInput.customPatterns"
|
|
||||||
data-test-subj="aceCustomPatternsInput"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</toggle-panel>
|
|
||||||
</form>
|
|
|
@ -4,47 +4,59 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import React from 'react';
|
||||||
import { InitAfterBindingsWorkaround } from 'ui/compat';
|
import {
|
||||||
|
EuiAccordion,
|
||||||
|
EuiCallOut,
|
||||||
|
EuiCodeBlock,
|
||||||
|
EuiFormRow,
|
||||||
|
EuiPanel,
|
||||||
|
EuiCodeEditor,
|
||||||
|
EuiSpacer
|
||||||
|
} from '@elastic/eui';
|
||||||
import { EDITOR } from '../../../../../common/constants';
|
import { EDITOR } from '../../../../../common/constants';
|
||||||
import { applyEditorOptions } from '../../../../lib/ace';
|
|
||||||
import template from './custom_patterns_input.html';
|
|
||||||
import './custom_patterns_input.less';
|
|
||||||
import 'ui/toggle_panel';
|
|
||||||
import 'ace';
|
|
||||||
|
|
||||||
const app = uiModules.get('xpack/grokdebugger');
|
export function CustomPatternsInput({ value, onChange }) {
|
||||||
|
const sampleCustomPatterns = `POSTFIX_QUEUEID [0-9A-F]{10,11}
|
||||||
|
MSG message-id=<%{GREEDYDATA}>`;
|
||||||
|
|
||||||
app.directive('customPatternsInput', function () {
|
return (
|
||||||
return {
|
<EuiAccordion
|
||||||
restrict: 'E',
|
id="customPatternsInput"
|
||||||
template: template,
|
buttonContent="Custom Patterns"
|
||||||
scope: {
|
data-test-subj="btnToggleCustomPatternsInput"
|
||||||
onChange: '='
|
>
|
||||||
},
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: 'customPatternsInput',
|
|
||||||
controller: class CustomPatternsInputController extends InitAfterBindingsWorkaround {
|
|
||||||
initAfterBindings($scope) {
|
|
||||||
this.isCollapsed = {
|
|
||||||
action: true
|
|
||||||
};
|
|
||||||
$scope.$watch('customPatternsInput.customPatterns', () => {
|
|
||||||
this.onChange(this.customPatterns);
|
|
||||||
});
|
|
||||||
$scope.aceLoaded = (editor) => {
|
|
||||||
this.editor = editor;
|
|
||||||
applyEditorOptions(editor, EDITOR.PATTERN_MIN_LINES, EDITOR.PATTERN_MAX_LINES);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onSectionToggle = (sectionId) => {
|
<EuiSpacer size="m" />
|
||||||
this.isCollapsed[sectionId] = !this.isCollapsed[sectionId];
|
|
||||||
}
|
|
||||||
|
|
||||||
isSectionCollapsed = (sectionId) => {
|
<EuiCallOut
|
||||||
return this.isCollapsed[sectionId];
|
title="Enter one custom pattern per line. For example:"
|
||||||
}
|
>
|
||||||
}
|
<EuiCodeBlock>
|
||||||
};
|
{ sampleCustomPatterns }
|
||||||
});
|
</EuiCodeBlock>
|
||||||
|
</EuiCallOut>
|
||||||
|
|
||||||
|
<EuiSpacer size="m" />
|
||||||
|
|
||||||
|
<EuiFormRow
|
||||||
|
fullWidth
|
||||||
|
data-test-subj="aceCustomPatternsInput"
|
||||||
|
>
|
||||||
|
<EuiPanel paddingSize="s">
|
||||||
|
<EuiCodeEditor
|
||||||
|
width="100%"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
setOptions={{
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightGutterLine: false,
|
||||||
|
minLines: EDITOR.PATTERN_MIN_LINES,
|
||||||
|
maxLines: EDITOR.PATTERN_MAX_LINES,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EuiPanel>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiAccordion>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
custom-patterns-input {
|
|
||||||
.custom-patterns-input-editor {
|
|
||||||
height: 51px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,4 +4,4 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import './custom_patterns_input';
|
export { CustomPatternsInput } from './custom_patterns_input';
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<form novalidate>
|
|
||||||
<div class="kuiFormSection">
|
|
||||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
|
||||||
Sample Data
|
|
||||||
</label>
|
|
||||||
<div class="grokdebugger-ace-editor">
|
|
||||||
<div
|
|
||||||
class="event-input-editor kuiVerticalRhythmSmall"
|
|
||||||
require-keys="true"
|
|
||||||
ui-ace="{
|
|
||||||
onLoad: aceLoaded
|
|
||||||
}"
|
|
||||||
ng-model="eventInput.rawEvent"
|
|
||||||
data-test-subj="aceEventInput"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -4,34 +4,34 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
EuiFormRow,
|
||||||
|
EuiPanel,
|
||||||
|
EuiCodeEditor
|
||||||
|
} from '@elastic/eui';
|
||||||
import { EDITOR } from '../../../../../common/constants';
|
import { EDITOR } from '../../../../../common/constants';
|
||||||
import { applyEditorOptions } from '../../../../lib/ace';
|
|
||||||
import template from './event_input.html';
|
|
||||||
import './event_input.less';
|
|
||||||
import 'ace';
|
|
||||||
|
|
||||||
const app = uiModules.get('xpack/grokdebugger');
|
export function EventInput({ value, onChange }) {
|
||||||
|
return (
|
||||||
app.directive('eventInput', function () {
|
<EuiFormRow
|
||||||
return {
|
label="Sample Data"
|
||||||
restrict: 'E',
|
fullWidth
|
||||||
template: template,
|
data-test-subj="aceEventInput"
|
||||||
scope: {
|
>
|
||||||
onChange: '='
|
<EuiPanel paddingSize="s">
|
||||||
},
|
<EuiCodeEditor
|
||||||
bindToController: true,
|
width="100%"
|
||||||
controllerAs: 'eventInput',
|
value={value}
|
||||||
controller: class EventInputController {
|
onChange={onChange}
|
||||||
constructor($scope) {
|
setOptions={{
|
||||||
$scope.$watch('eventInput.rawEvent', (newRawEvent) => {
|
highlightActiveLine: false,
|
||||||
this.onChange(newRawEvent);
|
highlightGutterLine: false,
|
||||||
});
|
minLines: EDITOR.SAMPLE_DATA_MIN_LINES,
|
||||||
$scope.aceLoaded = (editor) => {
|
maxLines: EDITOR.SAMPLE_DATA_MAX_LINES
|
||||||
this.editor = editor;
|
}}
|
||||||
applyEditorOptions(editor, EDITOR.SAMPLE_DATA_MIN_LINES, EDITOR.SAMPLE_DATA_MAX_LINES);
|
/>
|
||||||
};
|
</EuiPanel>
|
||||||
}
|
</EuiFormRow>
|
||||||
}
|
);
|
||||||
};
|
}
|
||||||
});
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
event-input {
|
|
||||||
.event-input-editor {
|
|
||||||
height: 51px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,4 +4,4 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import './event_input';
|
export { EventInput } from './event_input';
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<form novalidate>
|
|
||||||
<div class="kuiFormSection">
|
|
||||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
|
||||||
Structured Data
|
|
||||||
</label>
|
|
||||||
<div
|
|
||||||
class="event-output-editor grokdebugger-ace-editor kuiVerticalRhythmSmall"
|
|
||||||
json-input
|
|
||||||
require-keys="true"
|
|
||||||
ui-ace="{
|
|
||||||
mode: 'json',
|
|
||||||
onLoad: aceLoaded
|
|
||||||
}"
|
|
||||||
ng-model="eventOutput.structuredEvent"
|
|
||||||
data-test-subj="aceEventOutput"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -4,37 +4,33 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import React from 'react';
|
||||||
import template from './event_output.html';
|
import {
|
||||||
import './event_output.less';
|
EuiFormRow,
|
||||||
import 'ace';
|
EuiPanel,
|
||||||
|
EuiCodeEditor
|
||||||
|
} from '@elastic/eui';
|
||||||
|
|
||||||
const app = uiModules.get('xpack/grokdebugger');
|
export function EventOutput({ value }) {
|
||||||
|
return (
|
||||||
app.directive('eventOutput', function () {
|
<EuiFormRow
|
||||||
return {
|
label="Structured Data"
|
||||||
restrict: 'E',
|
fullWidth
|
||||||
template: template,
|
data-test-subj="aceEventOutput"
|
||||||
scope: {
|
>
|
||||||
structuredEvent: '='
|
<EuiPanel paddingSize="s">
|
||||||
},
|
<EuiCodeEditor
|
||||||
bindToController: true,
|
mode="json"
|
||||||
controllerAs: 'eventOutput',
|
isReadOnly
|
||||||
controller: class EventOutputController {
|
width="100%"
|
||||||
constructor($scope) {
|
height="340px"
|
||||||
$scope.aceLoaded = (editor) => {
|
value={JSON.stringify(value, null, 2)}
|
||||||
this.editor = editor;
|
setOptions={{
|
||||||
editor.getSession().setUseWrapMode(true);
|
|
||||||
editor.setOptions({
|
|
||||||
readOnly: true,
|
|
||||||
highlightActiveLine: false,
|
highlightActiveLine: false,
|
||||||
highlightGutterLine: false,
|
highlightGutterLine: false,
|
||||||
minLines: 20,
|
}}
|
||||||
maxLines: 25
|
/>
|
||||||
});
|
</EuiPanel>
|
||||||
editor.$blockScrolling = Infinity;
|
</EuiFormRow>
|
||||||
};
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
event-output {
|
|
||||||
.event-output-editor {
|
|
||||||
height: 340px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,4 +4,4 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import './event_output';
|
export { EventOutput } from './event_output';
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* 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 { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
EuiForm,
|
||||||
|
EuiButton,
|
||||||
|
EuiPage,
|
||||||
|
EuiPageBody,
|
||||||
|
EuiPageContent,
|
||||||
|
EuiPageContentBody,
|
||||||
|
EuiSpacer
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { EventInput } from '../event_input';
|
||||||
|
import { PatternInput } from '../pattern_input';
|
||||||
|
import { CustomPatternsInput } from '../custom_patterns_input';
|
||||||
|
import { EventOutput } from '../event_output';
|
||||||
|
import { GrokdebuggerRequest } from '../../../../models/grokdebugger_request';
|
||||||
|
import { toastNotifications } from 'ui/notify';
|
||||||
|
|
||||||
|
export class GrokDebugger extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
rawEvent: '',
|
||||||
|
pattern: '',
|
||||||
|
customPatterns: '',
|
||||||
|
structuredEvent: {}
|
||||||
|
};
|
||||||
|
this.grokdebuggerRequest = new GrokdebuggerRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRawEventChange = (rawEvent) => {
|
||||||
|
this.setState({ rawEvent });
|
||||||
|
this.grokdebuggerRequest.rawEvent = rawEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPatternChange = (pattern) => {
|
||||||
|
this.setState({ pattern });
|
||||||
|
this.grokdebuggerRequest.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCustomPatternsChange = (customPatterns) => {
|
||||||
|
this.setState({ customPatterns });
|
||||||
|
|
||||||
|
customPatterns = customPatterns.trim();
|
||||||
|
const customPatternsObj = {};
|
||||||
|
|
||||||
|
if (!customPatterns) {
|
||||||
|
this.grokdebuggerRequest.customPatterns = customPatternsObj;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
customPatterns.split('\n').forEach(customPattern => {
|
||||||
|
// Patterns are defined like so:
|
||||||
|
// patternName patternDefinition
|
||||||
|
// For example:
|
||||||
|
// POSTGRESQL %{DATESTAMP:timestamp} %{TZ} %{DATA:user_id} %{GREEDYDATA:connection_id} %{POSINT:pid}
|
||||||
|
const [ , patternName, patternDefinition ] = customPattern.match(/(\S+)\s+(.+)/) || [];
|
||||||
|
if (patternName && patternDefinition) {
|
||||||
|
customPatternsObj[patternName] = patternDefinition;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.grokdebuggerRequest.customPatterns = customPatternsObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
simulateGrok = async () => {
|
||||||
|
try {
|
||||||
|
const simulateResponse = await this.props.grokdebuggerService.simulate(this.grokdebuggerRequest);
|
||||||
|
this.setState({
|
||||||
|
structuredEvent: simulateResponse.structuredEvent
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isEmpty(simulateResponse.error)) {
|
||||||
|
toastNotifications.addDanger(simulateResponse.error);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
toastNotifications.addDanger(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSimulateClick = () => {
|
||||||
|
this.setState({
|
||||||
|
structuredEvent: {}
|
||||||
|
}, this.simulateGrok);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSimulateDisabled = () => {
|
||||||
|
return this.state.rawEvent.trim() === ''
|
||||||
|
|| this.state.pattern.trim() === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<EuiPage>
|
||||||
|
<EuiPageBody>
|
||||||
|
<EuiPageContent>
|
||||||
|
<EuiPageContentBody>
|
||||||
|
<EuiForm
|
||||||
|
className="grokdebugger-container"
|
||||||
|
data-test-subj="grokDebugger"
|
||||||
|
>
|
||||||
|
<EventInput
|
||||||
|
value={this.state.rawEvent}
|
||||||
|
onChange={this.onRawEventChange}
|
||||||
|
/>
|
||||||
|
<PatternInput
|
||||||
|
value={this.state.pattern}
|
||||||
|
onChange={this.onPatternChange}
|
||||||
|
/>
|
||||||
|
<CustomPatternsInput
|
||||||
|
value={this.state.customPatterns}
|
||||||
|
onChange={this.onCustomPatternsChange}
|
||||||
|
/>
|
||||||
|
<EuiSpacer />
|
||||||
|
<EuiButton
|
||||||
|
fill
|
||||||
|
onClick={this.onSimulateClick}
|
||||||
|
isDisabled={this.isSimulateDisabled()}
|
||||||
|
data-test-subj="btnSimulate"
|
||||||
|
>
|
||||||
|
Simulate
|
||||||
|
</EuiButton>
|
||||||
|
<EuiSpacer />
|
||||||
|
<EventOutput value={this.state.structuredEvent} />
|
||||||
|
</EuiForm>
|
||||||
|
</EuiPageContentBody>
|
||||||
|
</EuiPageContent>
|
||||||
|
</EuiPageBody>
|
||||||
|
</EuiPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 { GrokDebugger } from './grok_debugger';
|
|
@ -1,23 +0,0 @@
|
||||||
<kbn-dev-tools-app class="grokdebuggerDevTool">
|
|
||||||
<div class="kuiViewContent kuiViewContent--constrainedWidth kuiViewContentItem">
|
|
||||||
<div
|
|
||||||
class="grokdebugger-container"
|
|
||||||
data-test-subj="grokDebugger"
|
|
||||||
>
|
|
||||||
<event-input on-change="grokdebugger.onRawEventChange"></event-input>
|
|
||||||
<pattern-input on-change="grokdebugger.onPatternChange"></pattern-input>
|
|
||||||
<custom-patterns-input on-change="grokdebugger.onCustomPatternsChange"></custom-patterns-input>
|
|
||||||
<div class="grokdebugger-simulate-button">
|
|
||||||
<button
|
|
||||||
class="kuiButton kuiButton--primary"
|
|
||||||
ng-disabled="!grokdebugger.isSimulateEnabled"
|
|
||||||
ng-click="grokdebugger.onSimulateClick()"
|
|
||||||
data-test-subj="btnSimulate"
|
|
||||||
>
|
|
||||||
Simulate
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<event-output structured-event="grokdebugger.structuredEvent"></event-output>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</kbn-dev-tools-app>
|
|
|
@ -1,87 +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 { uiModules } from 'ui/modules';
|
|
||||||
import template from './grokdebugger.html';
|
|
||||||
import { Notifier } from 'ui/notify';
|
|
||||||
import { GrokdebuggerRequest } from 'plugins/grokdebugger/models/grokdebugger_request';
|
|
||||||
import 'plugins/grokdebugger/services/grokdebugger';
|
|
||||||
import './grokdebugger.less';
|
|
||||||
import '../event_input';
|
|
||||||
import '../event_output';
|
|
||||||
import '../pattern_input';
|
|
||||||
import '../custom_patterns_input';
|
|
||||||
import { isEmpty, trim } from 'lodash';
|
|
||||||
|
|
||||||
const app = uiModules.get('xpack/grokdebugger');
|
|
||||||
|
|
||||||
app.directive('grokdebugger', function ($injector) {
|
|
||||||
const grokdebuggerService = $injector.get('grokdebuggerService');
|
|
||||||
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
template: template,
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: 'grokdebugger',
|
|
||||||
controller: class GrokdebuggerController {
|
|
||||||
constructor() {
|
|
||||||
this.structuredEvent = {};
|
|
||||||
this.grokdebuggerRequest = new GrokdebuggerRequest();
|
|
||||||
this.notifier = new Notifier({ location: 'GrokDebugger' });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSimulateClick = () => {
|
|
||||||
return grokdebuggerService.simulate(this.grokdebuggerRequest)
|
|
||||||
.then(simulateResponse => {
|
|
||||||
this.structuredEvent = simulateResponse.structuredEvent;
|
|
||||||
// this error block is for responses which are 200, but still contain
|
|
||||||
// a grok debugger error like pattern not matched.
|
|
||||||
if (!isEmpty(simulateResponse.error)) {
|
|
||||||
this.notifier.error(simulateResponse.error);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
// this error is for 4xx and 5xx responses
|
|
||||||
this.notifier.error(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCustomPatternsChange = (customPatterns = '') => {
|
|
||||||
customPatterns = customPatterns.trim();
|
|
||||||
if (!customPatterns) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const customPatternsObj = {};
|
|
||||||
customPatterns.split('\n').forEach(customPattern => {
|
|
||||||
// Patterns are defined like so:
|
|
||||||
// patternName patternDefinition
|
|
||||||
// For example:
|
|
||||||
// POSTGRESQL %{DATESTAMP:timestamp} %{TZ} %{DATA:user_id} %{GREEDYDATA:connection_id} %{POSINT:pid}
|
|
||||||
const [ , patternName, patternDefinition ] = customPattern.match(/(\S+)\s+(.+)/) || [];
|
|
||||||
if (patternName && patternDefinition) {
|
|
||||||
customPatternsObj[patternName] = patternDefinition;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.grokdebuggerRequest.customPatterns = customPatternsObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
onRawEventChange = (rawEvent) => {
|
|
||||||
this.grokdebuggerRequest.rawEvent = rawEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPatternChange = (pattern) => {
|
|
||||||
this.grokdebuggerRequest.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSimulateEnabled() {
|
|
||||||
return !(isEmpty(trim(this.grokdebuggerRequest.rawEvent)) ||
|
|
||||||
isEmpty(trim(this.grokdebuggerRequest.pattern)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -4,4 +4,4 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import './pattern_input';
|
export { PatternInput } from './pattern_input';
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<form novalidate>
|
|
||||||
<div class="kuiFormSection">
|
|
||||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
|
||||||
Grok Pattern
|
|
||||||
</label>
|
|
||||||
<div class="grokdebugger-ace-editor">
|
|
||||||
<div
|
|
||||||
class="pattern-input-editor kuiVerticalRhythmSmall"
|
|
||||||
require-keys="true"
|
|
||||||
ui-ace="{
|
|
||||||
onLoad: aceLoaded
|
|
||||||
}"
|
|
||||||
ng-model="patternInput.pattern"
|
|
||||||
data-test-subj="acePatternInput"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -4,37 +4,36 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import React from 'react';
|
||||||
import { EDITOR } from '../../../../../common/constants';
|
|
||||||
import template from './pattern_input.html';
|
|
||||||
import './pattern_input.less';
|
|
||||||
import {
|
import {
|
||||||
applyEditorOptions,
|
EuiFormRow,
|
||||||
GrokMode
|
EuiPanel,
|
||||||
} from '../../../../lib/ace';
|
EuiCodeEditor
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { EDITOR } from '../../../../../common/constants';
|
||||||
|
import { GrokMode } from '../../../../lib/ace';
|
||||||
|
|
||||||
const app = uiModules.get('xpack/grokdebugger');
|
export function PatternInput({ value, onChange }) {
|
||||||
|
return (
|
||||||
app.directive('patternInput', function () {
|
<EuiFormRow
|
||||||
return {
|
label="Grok Pattern"
|
||||||
restrict: 'E',
|
fullWidth
|
||||||
template: template,
|
data-test-subj="acePatternInput"
|
||||||
scope: {
|
>
|
||||||
onChange: '='
|
<EuiPanel paddingSize="s">
|
||||||
},
|
<EuiCodeEditor
|
||||||
bindToController: true,
|
width="100%"
|
||||||
controllerAs: 'patternInput',
|
value={value}
|
||||||
controller: class PatternInputController {
|
onChange={onChange}
|
||||||
constructor($scope) {
|
mode={new GrokMode()}
|
||||||
$scope.$watch('patternInput.pattern', (newPattern) => {
|
setOptions={{
|
||||||
this.onChange(newPattern);
|
highlightActiveLine: false,
|
||||||
});
|
highlightGutterLine: false,
|
||||||
$scope.aceLoaded = (editor) => {
|
minLines: EDITOR.PATTERN_MIN_LINES,
|
||||||
this.editor = editor;
|
maxLines: EDITOR.PATTERN_MAX_LINES,
|
||||||
applyEditorOptions(editor, EDITOR.PATTERN_MIN_LINES, EDITOR.PATTERN_MAX_LINES);
|
}}
|
||||||
editor.getSession().setMode(new GrokMode());
|
/>
|
||||||
};
|
</EuiPanel>
|
||||||
}
|
</EuiFormRow>
|
||||||
}
|
);
|
||||||
};
|
}
|
||||||
});
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
pattern-input {
|
|
||||||
.pattern-input-editor {
|
|
||||||
height: 51px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { uiModules } from 'ui/modules';
|
||||||
|
import 'plugins/grokdebugger/services/grokdebugger';
|
||||||
|
import { GrokDebugger } from '../../components/grok_debugger';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import React from 'react';
|
||||||
|
import './grokdebugger.less';
|
||||||
|
|
||||||
|
const app = uiModules.get('xpack/grokdebugger');
|
||||||
|
|
||||||
|
app.directive('grokdebugger', function ($injector) {
|
||||||
|
const grokdebuggerService = $injector.get('grokdebuggerService');
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
link: (scope, el) => {
|
||||||
|
render(<GrokDebugger grokdebuggerService={grokdebuggerService} />, el[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
|
@ -3,22 +3,6 @@
|
||||||
@import (reference) "~ui/styles/theme";
|
@import (reference) "~ui/styles/theme";
|
||||||
|
|
||||||
grokdebugger {
|
grokdebugger {
|
||||||
.grokdebugger-container {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ace_editor {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grokdebugger-ace-editor {
|
|
||||||
border: 2px solid @kibanaGray5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grokdebugger-simulate-button {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ace_grokStart,
|
.ace_grokStart,
|
||||||
.ace_grokEnd,
|
.ace_grokEnd,
|
||||||
.ace_grokSeparator,
|
.ace_grokSeparator,
|
|
@ -1 +1,3 @@
|
||||||
<grokdebugger></grokdebugger>
|
<kbn-dev-tools-app class="grokdebuggerDevTool">
|
||||||
|
<grokdebugger></grokdebugger>
|
||||||
|
</kbn-dev-tools-app>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import routes from 'ui/routes';
|
||||||
import { toastNotifications } from 'ui/notify';
|
import { toastNotifications } from 'ui/notify';
|
||||||
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
|
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
|
||||||
import template from './grokdebugger_route.html';
|
import template from './grokdebugger_route.html';
|
||||||
import './components/grokdebugger';
|
import './directives/grokdebugger';
|
||||||
|
|
||||||
routes
|
routes
|
||||||
.when('/dev_tools/grokdebugger', {
|
.when('/dev_tools/grokdebugger', {
|
||||||
|
|
|
@ -14,10 +14,10 @@ export function GrokDebuggerProvider({ getService }) {
|
||||||
// test subject selectors
|
// test subject selectors
|
||||||
const SUBJ_CONTAINER = 'grokDebugger';
|
const SUBJ_CONTAINER = 'grokDebugger';
|
||||||
|
|
||||||
const SUBJ_UI_ACE_EVENT_INPUT = `${SUBJ_CONTAINER} aceEventInput`;
|
const SUBJ_UI_ACE_EVENT_INPUT = `${SUBJ_CONTAINER} aceEventInput codeEditorContainer`;
|
||||||
const SUBJ_UI_ACE_PATTERN_INPUT = `${SUBJ_CONTAINER} acePatternInput`;
|
const SUBJ_UI_ACE_PATTERN_INPUT = `${SUBJ_CONTAINER} acePatternInput codeEditorContainer`;
|
||||||
const SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} aceCustomPatternsInput`;
|
const SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} aceCustomPatternsInput codeEditorContainer`;
|
||||||
const SUBJ_UI_ACE_EVENT_OUTPUT = `${SUBJ_CONTAINER} aceEventOutput`;
|
const SUBJ_UI_ACE_EVENT_OUTPUT = `${SUBJ_CONTAINER} aceEventOutput codeEditorContainer`;
|
||||||
|
|
||||||
const SUBJ_BTN_TOGGLE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} btnToggleCustomPatternsInput`;
|
const SUBJ_BTN_TOGGLE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} btnToggleCustomPatternsInput`;
|
||||||
const SUBJ_BTN_SIMULATE = `${SUBJ_CONTAINER} btnSimulate`;
|
const SUBJ_BTN_SIMULATE = `${SUBJ_CONTAINER} btnSimulate`;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue