mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -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,
|
||||
SAMPLE_DATA_MIN_LINES: 3,
|
||||
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 { 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.
|
||||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { InitAfterBindingsWorkaround } from 'ui/compat';
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiAccordion,
|
||||
EuiCallOut,
|
||||
EuiCodeBlock,
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiCodeEditor,
|
||||
EuiSpacer
|
||||
} from '@elastic/eui';
|
||||
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 {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
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);
|
||||
};
|
||||
}
|
||||
return (
|
||||
<EuiAccordion
|
||||
id="customPatternsInput"
|
||||
buttonContent="Custom Patterns"
|
||||
data-test-subj="btnToggleCustomPatternsInput"
|
||||
>
|
||||
|
||||
onSectionToggle = (sectionId) => {
|
||||
this.isCollapsed[sectionId] = !this.isCollapsed[sectionId];
|
||||
}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
isSectionCollapsed = (sectionId) => {
|
||||
return this.isCollapsed[sectionId];
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
<EuiCallOut
|
||||
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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiCodeEditor
|
||||
} from '@elastic/eui';
|
||||
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');
|
||||
|
||||
app.directive('eventInput', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
onChange: '='
|
||||
},
|
||||
bindToController: true,
|
||||
controllerAs: 'eventInput',
|
||||
controller: class EventInputController {
|
||||
constructor($scope) {
|
||||
$scope.$watch('eventInput.rawEvent', (newRawEvent) => {
|
||||
this.onChange(newRawEvent);
|
||||
});
|
||||
$scope.aceLoaded = (editor) => {
|
||||
this.editor = editor;
|
||||
applyEditorOptions(editor, EDITOR.SAMPLE_DATA_MIN_LINES, EDITOR.SAMPLE_DATA_MAX_LINES);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
export function EventInput({ value, onChange }) {
|
||||
return (
|
||||
<EuiFormRow
|
||||
label="Sample Data"
|
||||
fullWidth
|
||||
data-test-subj="aceEventInput"
|
||||
>
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiCodeEditor
|
||||
width="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
setOptions={{
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: EDITOR.SAMPLE_DATA_MIN_LINES,
|
||||
maxLines: 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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import template from './event_output.html';
|
||||
import './event_output.less';
|
||||
import 'ace';
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiCodeEditor
|
||||
} from '@elastic/eui';
|
||||
|
||||
const app = uiModules.get('xpack/grokdebugger');
|
||||
|
||||
app.directive('eventOutput', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
structuredEvent: '='
|
||||
},
|
||||
bindToController: true,
|
||||
controllerAs: 'eventOutput',
|
||||
controller: class EventOutputController {
|
||||
constructor($scope) {
|
||||
$scope.aceLoaded = (editor) => {
|
||||
this.editor = editor;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
editor.setOptions({
|
||||
readOnly: true,
|
||||
export function EventOutput({ value }) {
|
||||
return (
|
||||
<EuiFormRow
|
||||
label="Structured Data"
|
||||
fullWidth
|
||||
data-test-subj="aceEventOutput"
|
||||
>
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
isReadOnly
|
||||
width="100%"
|
||||
height="340px"
|
||||
value={JSON.stringify(value, null, 2)}
|
||||
setOptions={{
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: 20,
|
||||
maxLines: 25
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { EDITOR } from '../../../../../common/constants';
|
||||
import template from './pattern_input.html';
|
||||
import './pattern_input.less';
|
||||
import React from 'react';
|
||||
import {
|
||||
applyEditorOptions,
|
||||
GrokMode
|
||||
} from '../../../../lib/ace';
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiCodeEditor
|
||||
} from '@elastic/eui';
|
||||
import { EDITOR } from '../../../../../common/constants';
|
||||
import { GrokMode } from '../../../../lib/ace';
|
||||
|
||||
const app = uiModules.get('xpack/grokdebugger');
|
||||
|
||||
app.directive('patternInput', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
onChange: '='
|
||||
},
|
||||
bindToController: true,
|
||||
controllerAs: 'patternInput',
|
||||
controller: class PatternInputController {
|
||||
constructor($scope) {
|
||||
$scope.$watch('patternInput.pattern', (newPattern) => {
|
||||
this.onChange(newPattern);
|
||||
});
|
||||
$scope.aceLoaded = (editor) => {
|
||||
this.editor = editor;
|
||||
applyEditorOptions(editor, EDITOR.PATTERN_MIN_LINES, EDITOR.PATTERN_MAX_LINES);
|
||||
editor.getSession().setMode(new GrokMode());
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
export function PatternInput({ value, onChange }) {
|
||||
return (
|
||||
<EuiFormRow
|
||||
label="Grok Pattern"
|
||||
fullWidth
|
||||
data-test-subj="acePatternInput"
|
||||
>
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiCodeEditor
|
||||
width="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
mode={new GrokMode()}
|
||||
setOptions={{
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: EDITOR.PATTERN_MIN_LINES,
|
||||
maxLines: EDITOR.PATTERN_MAX_LINES,
|
||||
}}
|
||||
/>
|
||||
</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";
|
||||
|
||||
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_grokEnd,
|
||||
.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 { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
|
||||
import template from './grokdebugger_route.html';
|
||||
import './components/grokdebugger';
|
||||
import './directives/grokdebugger';
|
||||
|
||||
routes
|
||||
.when('/dev_tools/grokdebugger', {
|
||||
|
|
|
@ -14,10 +14,10 @@ export function GrokDebuggerProvider({ getService }) {
|
|||
// test subject selectors
|
||||
const SUBJ_CONTAINER = 'grokDebugger';
|
||||
|
||||
const SUBJ_UI_ACE_EVENT_INPUT = `${SUBJ_CONTAINER} aceEventInput`;
|
||||
const SUBJ_UI_ACE_PATTERN_INPUT = `${SUBJ_CONTAINER} acePatternInput`;
|
||||
const SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} aceCustomPatternsInput`;
|
||||
const SUBJ_UI_ACE_EVENT_OUTPUT = `${SUBJ_CONTAINER} aceEventOutput`;
|
||||
const SUBJ_UI_ACE_EVENT_INPUT = `${SUBJ_CONTAINER} aceEventInput codeEditorContainer`;
|
||||
const SUBJ_UI_ACE_PATTERN_INPUT = `${SUBJ_CONTAINER} acePatternInput codeEditorContainer`;
|
||||
const SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} aceCustomPatternsInput codeEditorContainer`;
|
||||
const SUBJ_UI_ACE_EVENT_OUTPUT = `${SUBJ_CONTAINER} aceEventOutput codeEditorContainer`;
|
||||
|
||||
const SUBJ_BTN_TOGGLE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} btnToggleCustomPatternsInput`;
|
||||
const SUBJ_BTN_SIMULATE = `${SUBJ_CONTAINER} btnSimulate`;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue