mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Migrate x-pack-kibana source to kibana
This commit is contained in:
parent
e8ac7d8d32
commit
bc5b51554d
3256 changed files with 277621 additions and 2324 deletions
0
x-pack/plugins/grokdebugger/README.md
Normal file
0
x-pack/plugins/grokdebugger/README.md
Normal file
8
x-pack/plugins/grokdebugger/common/constants/index.js
Normal file
8
x-pack/plugins/grokdebugger/common/constants/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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';
|
9
x-pack/plugins/grokdebugger/common/constants/plugin.js
Normal file
9
x-pack/plugins/grokdebugger/common/constants/plugin.js
Normal file
|
@ -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: 'grokdebugger'
|
||||
};
|
9
x-pack/plugins/grokdebugger/common/constants/routes.js
Normal file
9
x-pack/plugins/grokdebugger/common/constants/routes.js
Normal file
|
@ -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/grokdebugger',
|
||||
};
|
31
x-pack/plugins/grokdebugger/index.js
Normal file
31
x-pack/plugins/grokdebugger/index.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { registerGrokdebuggerRoutes } from './server/routes/api/grokdebugger';
|
||||
import { registerLicenseChecker } from './server/lib/register_license_checker';
|
||||
|
||||
export const grokdebugger = (kibana) => new kibana.Plugin({
|
||||
id: PLUGIN.ID,
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
require: ['kibana', 'elasticsearch', 'xpack_main'],
|
||||
configPrefix: 'xpack.grokdebugger',
|
||||
config(Joi) {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true)
|
||||
}).default();
|
||||
},
|
||||
uiExports: {
|
||||
devTools: ['plugins/grokdebugger/sections/grokdebugger'],
|
||||
hacks: ['plugins/grokdebugger/sections/grokdebugger/register'],
|
||||
home: ['plugins/grokdebugger/register_feature'],
|
||||
},
|
||||
init: (server) => {
|
||||
registerLicenseChecker(server);
|
||||
registerGrokdebuggerRoutes(server);
|
||||
}
|
||||
});
|
7
x-pack/plugins/grokdebugger/public/assets/app_grok.svg
Normal file
7
x-pack/plugins/grokdebugger/public/assets/app_grok.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g fill="none" transform="translate(2)">
|
||||
<path fill="#14A7DF" d="M25.2,14.1217391 C24.22,13.1478261 23.94,11.8956522 23.94,10.3652174 L23.94,10.1565217 L23.94,3.75652174 C23.94,2.50434783 23.45,1.53043478 22.82,0.973913043 C22.19,0.347826087 20.79,0.0695652174 19.04,4e-16 L15.54,4e-16 L9.94,32 L19.04,32 C20.72,31.9304348 22.19,31.6521739 22.82,31.026087 C23.45,30.4 23.94,29.4956522 23.94,28.2434783 L23.94,21.8434783 L23.94,21.6347826 C23.94,20.1043478 24.22,18.8521739 25.2,17.8782609 C25.69,17.3913043 26.32,16.973913 27.09,16.6956522 C27.65,16.4173913 27.65,15.6521739 27.09,15.373913 C26.32,15.026087 25.69,14.6086957 25.2,14.1217391 Z"/>
|
||||
<path fill="#00BFB3" d="M2.94,17.8782609 C3.99,18.8521739 4.34,20.1043478 4.34,21.6347826 L4.34,21.8434783 L4.34,28.2434783 C4.34,29.4956522 4.83,30.4695652 5.46,31.026087 C6.09,31.6521739 7.56,32 9.38,32 L12.74,32 L18.34,-7.1e-15 L9.38,-7.1e-15 C7.56,-7.1e-15 6.16,0.347826087 5.46,0.973913043 C4.83,1.6 4.34,2.50434783 4.34,3.75652174 L4.34,10.1565217 L4.34,10.3652174 C4.34,11.8956522 3.99,13.1478261 2.94,14.1217391 C2.38,14.6086957 1.75,15.026087 0.91,15.3043478 C0.28,15.5130435 0.28,16.3478261 0.91,16.626087 C1.68,16.973913 2.38,17.3913043 2.94,17.8782609 Z"/>
|
||||
<polygon fill="#0078A0" points="15.54 0 9.94 32 12.74 32 18.34 0"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -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 { get, pick } from 'lodash';
|
||||
|
||||
export class GrokdebuggerRequest {
|
||||
constructor(props = {}) {
|
||||
this.rawEvent = get(props, 'rawEvent', '');
|
||||
this.pattern = get(props, 'pattern', '');
|
||||
this.customPatterns = get(props, 'customPatterns', {});
|
||||
}
|
||||
|
||||
get upstreamJSON() {
|
||||
return pick(this, [ 'rawEvent', 'pattern', 'customPatterns' ]);
|
||||
}
|
||||
}
|
|
@ -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 { GrokdebuggerRequest } from './grokdebugger_request';
|
|
@ -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 { get } from 'lodash';
|
||||
|
||||
export class GrokdebuggerResponse {
|
||||
constructor(props) {
|
||||
this.structuredEvent = get(props, 'structuredEvent', {});
|
||||
this.error = get(props, 'error', {});
|
||||
}
|
||||
|
||||
static fromUpstreamJSON(grokdebuggerResponse) {
|
||||
return new GrokdebuggerResponse(grokdebuggerResponse);
|
||||
}
|
||||
}
|
|
@ -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 { GrokdebuggerResponse } from './grokdebugger_response';
|
21
x-pack/plugins/grokdebugger/public/register_feature.js
Normal file
21
x-pack/plugins/grokdebugger/public/register_feature.js
Normal file
|
@ -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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
|
||||
|
||||
FeatureCatalogueRegistryProvider.register(() => {
|
||||
return {
|
||||
id: 'grokdebugger',
|
||||
title: 'Grok Debugger',
|
||||
description: 'Simulate and debug grok patterns for data transformation on ingestion.',
|
||||
icon: '/plugins/grokdebugger/assets/app_grok.svg',
|
||||
path: '/app/kibana#/dev_tools/grokdebugger',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN
|
||||
};
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
<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="custom-patterns-input-editor grokdebugger-ace-editor kuiVerticalRhythm"
|
||||
require-keys="true"
|
||||
ui-ace="{
|
||||
onLoad: aceLoaded
|
||||
}"
|
||||
ng-model="customPatternsInput.customPatterns"
|
||||
data-test-subj="aceCustomPatternsInput"
|
||||
></div>
|
||||
</div>
|
||||
</toggle-panel>
|
||||
</form>
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { InitAfterBindingsWorkaround } from 'ui/compat';
|
||||
import template from './custom_patterns_input.html';
|
||||
import './custom_patterns_input.less';
|
||||
import 'ui/toggle_panel';
|
||||
import 'ace';
|
||||
|
||||
const app = uiModules.get('xpack/grokdebugger');
|
||||
|
||||
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;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
editor.setOptions({
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: 3,
|
||||
maxLines: 25
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
};
|
||||
}
|
||||
|
||||
onSectionToggle = (sectionId) => {
|
||||
this.isCollapsed[sectionId] = !this.isCollapsed[sectionId];
|
||||
}
|
||||
|
||||
isSectionCollapsed = (sectionId) => {
|
||||
return this.isCollapsed[sectionId];
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
custom-patterns-input {
|
||||
.custom-patterns-input-editor {
|
||||
height: 51px;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './custom_patterns_input';
|
|
@ -0,0 +1,16 @@
|
|||
<form novalidate>
|
||||
<div class="kuiFormSection">
|
||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
||||
Sample Data
|
||||
</label>
|
||||
<div
|
||||
class="event-input-editor grokdebugger-ace-editor kuiVerticalRhythmSmall"
|
||||
require-keys="true"
|
||||
ui-ace="{
|
||||
onLoad: aceLoaded
|
||||
}"
|
||||
ng-model="eventInput.rawEvent"
|
||||
data-test-subj="aceEventInput"
|
||||
></div>
|
||||
</div>
|
||||
</form>
|
|
@ -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 { uiModules } from 'ui/modules';
|
||||
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;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
editor.setOptions({
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: 3,
|
||||
maxLines: 10
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
event-input {
|
||||
.event-input-editor {
|
||||
height: 51px;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './event_input';
|
|
@ -0,0 +1,18 @@
|
|||
<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>
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 './event_output.html';
|
||||
import './event_output.less';
|
||||
import 'ace';
|
||||
|
||||
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,
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: 20,
|
||||
maxLines: 25
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
event-output {
|
||||
.event-output-editor {
|
||||
height: 340px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './event_output';
|
|
@ -0,0 +1,23 @@
|
|||
<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>
|
|
@ -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 { 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)));
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './grokdebugger';
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './pattern_input';
|
|
@ -0,0 +1,16 @@
|
|||
<form novalidate>
|
||||
<div class="kuiFormSection">
|
||||
<label class="kuiLabel kuiVerticalRhythmSmall">
|
||||
Grok Pattern
|
||||
</label>
|
||||
<div
|
||||
class="pattern-input-editor grokdebugger-ace-editor kuiVerticalRhythmSmall"
|
||||
require-keys="true"
|
||||
ui-ace="{
|
||||
onLoad: aceLoaded
|
||||
}"
|
||||
ng-model="patternInput.pattern"
|
||||
data-test-subj="acePatternInput"
|
||||
></div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 './pattern_input.html';
|
||||
import './pattern_input.less';
|
||||
|
||||
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;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
editor.setOptions({
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false,
|
||||
minLines: 3,
|
||||
maxLines: 10
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
pattern-input {
|
||||
.pattern-input-editor {
|
||||
height: 51px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<grokdebugger></grokdebugger>
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 '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';
|
||||
|
||||
routes
|
||||
.when('/dev_tools/grokdebugger', {
|
||||
template: template,
|
||||
resolve: {
|
||||
licenseCheckResults(Private) {
|
||||
const xpackInfo = Private(XPackInfoProvider);
|
||||
return {
|
||||
showPage: xpackInfo.get('features.grokdebugger.enableLink'),
|
||||
message: xpackInfo.get('features.grokdebugger.message')
|
||||
};
|
||||
}
|
||||
},
|
||||
controller: class GrokDebuggerRouteController {
|
||||
constructor($injector) {
|
||||
const $route = $injector.get('$route');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
|
||||
const licenseCheckResults = $route.current.locals.licenseCheckResults;
|
||||
if (!licenseCheckResults.showPage) {
|
||||
kbnUrl.change('/dev_tools');
|
||||
toastNotifications.addDanger(licenseCheckResults.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './grokdebugger_route';
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { DevToolsRegistryProvider } from 'ui/registry/dev_tools';
|
||||
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
|
||||
|
||||
DevToolsRegistryProvider.register((Private) => {
|
||||
const xpackInfo = Private(XPackInfoProvider);
|
||||
return {
|
||||
order: 6,
|
||||
name: 'grokdebugger',
|
||||
display: 'Grok Debugger',
|
||||
url: '#/dev_tools/grokdebugger',
|
||||
disabled: !xpackInfo.get('features.grokdebugger.enableLink', false),
|
||||
tooltipContent: xpackInfo.get('features.grokdebugger.message')
|
||||
};
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { GrokdebuggerService } from './grokdebugger_service';
|
||||
|
||||
uiModules.get('xpack/grokdebugger')
|
||||
.factory('grokdebuggerService', ($injector) => {
|
||||
const $http = $injector.get('$http');
|
||||
return new GrokdebuggerService($http);
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { ROUTES } from '../../../common/constants';
|
||||
import { GrokdebuggerResponse } from 'plugins/grokdebugger/models/grokdebugger_response';
|
||||
|
||||
export class GrokdebuggerService {
|
||||
constructor($http) {
|
||||
this.$http = $http;
|
||||
this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
|
||||
}
|
||||
|
||||
simulate(grokdebuggerRequest) {
|
||||
return this.$http.post(`${this.basePath}/simulate`, grokdebuggerRequest.upstreamJSON)
|
||||
.then(response => {
|
||||
return GrokdebuggerResponse.fromUpstreamJSON(response.data.grokdebuggerResponse);
|
||||
})
|
||||
.catch(e => {
|
||||
throw e.data.message;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import './grokdebugger_service.factory';
|
|
@ -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);
|
||||
};
|
||||
};
|
|
@ -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';
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 'expect.js';
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 function checkLicense(xpackLicenseInfo) {
|
||||
// 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: 'You cannot use the Grok Debugger 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: `You cannot use the Grok Debugger because your ${licenseType} license has expired.`
|
||||
};
|
||||
}
|
||||
|
||||
// License is valid and active
|
||||
return {
|
||||
enableLink: true,
|
||||
enableAPIRoute: true
|
||||
};
|
||||
}
|
|
@ -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';
|
|
@ -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';
|
|
@ -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) {
|
||||
return Boom.wrap(err, err.statusCode);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 'expect.js';
|
||||
import Boom from 'boom';
|
||||
import { licensePreRoutingFactory } from '../license_pre_routing_factory';
|
||||
|
||||
describe('license_pre_routing_factory', () => {
|
||||
describe('#grokDebuggerFeaturePreRoutingFactory', () => {
|
||||
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', (done) => {
|
||||
const licensePreRouting = licensePreRoutingFactory(mockServer);
|
||||
const stubRequest = {};
|
||||
licensePreRouting(stubRequest, (response) => {
|
||||
expect(response).to.be.an(Error);
|
||||
expect(response.isBoom).to.be(true);
|
||||
expect(response.output.statusCode).to.be(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAvailable is true', () => {
|
||||
beforeEach(() => {
|
||||
mockLicenseCheckResults = {
|
||||
isAvailable: true
|
||||
};
|
||||
});
|
||||
|
||||
it ('replies with nothing', (done) => {
|
||||
const licensePreRouting = licensePreRoutingFactory(mockServer);
|
||||
const stubRequest = {};
|
||||
licensePreRouting(stubRequest, (response) => {
|
||||
expect(response).to.eql(Boom.forbidden());
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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';
|
|
@ -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(request, reply) {
|
||||
const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults();
|
||||
if (!licenseCheckResults.enableAPIRoute) {
|
||||
reply(Boom.forbidden(licenseCheckResults.message));
|
||||
} else {
|
||||
reply();
|
||||
}
|
||||
}
|
||||
|
||||
return licensePreRouting;
|
||||
};
|
|
@ -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';
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status';
|
||||
import { checkLicense } from '../check_license';
|
||||
import { PLUGIN } from '../../../common/constants';
|
||||
|
||||
export function registerLicenseChecker(server) {
|
||||
const xpackMainPlugin = server.plugins.xpack_main;
|
||||
const grokdebuggerPlugin = server.plugins[PLUGIN.ID];
|
||||
|
||||
mirrorPluginStatus(xpackMainPlugin, grokdebuggerPlugin);
|
||||
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);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 'expect.js';
|
||||
import { GrokdebuggerRequest } from '../grokdebugger_request';
|
||||
|
||||
describe('grokdebugger_request', () => {
|
||||
|
||||
describe('GrokdebuggerRequest', () => {
|
||||
const downstreamRequest = {
|
||||
rawEvent: '55.3.244.1 GET /index.html',
|
||||
pattern: '%{IP:client} %{WORD:method} %{URIPATHPARAM:request}'
|
||||
};
|
||||
|
||||
const downstreamRequestWithCustomPatterns = {
|
||||
rawEvent: '55.3.244.1 GET /index.html',
|
||||
pattern: '%{IP:client} %{WORD:method} %{URIPATHPARAM:request}',
|
||||
customPatterns: '%{FOO:bar}'
|
||||
};
|
||||
|
||||
describe('fromDownstreamJSON factory method', () => {
|
||||
|
||||
it('returns correct GrokdebuggerRequest instance from downstreamRequest', () => {
|
||||
const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(downstreamRequest);
|
||||
expect(grokdebuggerRequest.rawEvent).to.eql(downstreamRequest.rawEvent);
|
||||
expect(grokdebuggerRequest.pattern).to.eql(downstreamRequest.pattern);
|
||||
expect(grokdebuggerRequest.customPatterns).to.eql({});
|
||||
});
|
||||
|
||||
it('returns correct GrokdebuggerRequest instance from downstreamRequest when custom patterns are specified', () => {
|
||||
const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(downstreamRequestWithCustomPatterns);
|
||||
expect(grokdebuggerRequest.rawEvent).to.eql(downstreamRequest.rawEvent);
|
||||
expect(grokdebuggerRequest.pattern).to.eql(downstreamRequest.pattern);
|
||||
expect(grokdebuggerRequest.customPatterns).to.eql('%{FOO:bar}');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('upstreamJSON getter method', () => {
|
||||
|
||||
it('returns the upstream simulate JSON request', () => {
|
||||
const expectedUpstreamJSON = {
|
||||
pipeline: {
|
||||
description: 'this is a grokdebugger simulation',
|
||||
processors: [
|
||||
{
|
||||
"grok": {
|
||||
"field": "rawEvent",
|
||||
"patterns": ["%{IP:client} %{WORD:method} %{URIPATHPARAM:request}"],
|
||||
"pattern_definitions": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
docs: [
|
||||
{
|
||||
"_index": "grokdebugger",
|
||||
"_type": "grokdebugger",
|
||||
"_id": "grokdebugger",
|
||||
"_source": {
|
||||
"rawEvent": "55.3.244.1 GET /index.html"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(downstreamRequest);
|
||||
const upstreamJson = grokdebuggerRequest.upstreamJSON;
|
||||
expect(upstreamJson).to.eql(expectedUpstreamJSON);
|
||||
});
|
||||
|
||||
it('returns the upstream simulate JSON request when custom patterns are specfied', () => {
|
||||
const expectedUpstreamJSON = {
|
||||
pipeline: {
|
||||
description: 'this is a grokdebugger simulation',
|
||||
processors: [
|
||||
{
|
||||
"grok": {
|
||||
"field": "rawEvent",
|
||||
"patterns": ["%{IP:client} %{WORD:method} %{URIPATHPARAM:request}"],
|
||||
"pattern_definitions": '%{FOO:bar}'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
docs: [
|
||||
{
|
||||
"_index": "grokdebugger",
|
||||
"_type": "grokdebugger",
|
||||
"_id": "grokdebugger",
|
||||
"_source": {
|
||||
"rawEvent": "55.3.244.1 GET /index.html"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(downstreamRequestWithCustomPatterns);
|
||||
const upstreamJson = grokdebuggerRequest.upstreamJSON;
|
||||
expect(upstreamJson).to.eql(expectedUpstreamJSON);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 { get } from 'lodash';
|
||||
|
||||
/**
|
||||
* Model to capture Grokdebugger request with upstream (ES) helpers.
|
||||
*/
|
||||
export class GrokdebuggerRequest {
|
||||
constructor(props) {
|
||||
this.rawEvent = get(props, 'rawEvent', "");
|
||||
this.pattern = get(props, 'pattern', "");
|
||||
this.customPatterns = get(props, 'customPatterns', {});
|
||||
}
|
||||
|
||||
get upstreamJSON() {
|
||||
return {
|
||||
pipeline: {
|
||||
description: 'this is a grokdebugger simulation',
|
||||
processors: [
|
||||
{
|
||||
grok: {
|
||||
field: 'rawEvent',
|
||||
pattern_definitions: this.customPatterns,
|
||||
patterns: [
|
||||
this.pattern.toString()
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
docs: [
|
||||
{
|
||||
_index: 'grokdebugger',
|
||||
_type: 'grokdebugger',
|
||||
_id: 'grokdebugger',
|
||||
_source: {
|
||||
rawEvent: this.rawEvent.toString()
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// generate GrokdebuggerRequest object from kibana
|
||||
static fromDownstreamJSON(downstreamGrokdebuggerRequest) {
|
||||
const opts = {
|
||||
rawEvent: downstreamGrokdebuggerRequest.rawEvent,
|
||||
pattern: downstreamGrokdebuggerRequest.pattern,
|
||||
customPatterns: downstreamGrokdebuggerRequest.customPatterns
|
||||
};
|
||||
|
||||
return new GrokdebuggerRequest(opts);
|
||||
}
|
||||
}
|
|
@ -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 { GrokdebuggerRequest } from './grokdebugger_request';
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 'expect.js';
|
||||
import { GrokdebuggerResponse } from '../grokdebugger_response';
|
||||
|
||||
describe('grokdebugger_response', () => {
|
||||
|
||||
describe('GrokdebuggerResponse', () => {
|
||||
|
||||
describe('fromUpstreamJSON factory method', () => {
|
||||
|
||||
it('returns correct GrokdebuggerResponse instance when there are no grok parse errors', () => {
|
||||
const upstreamJson = {
|
||||
docs: [
|
||||
{
|
||||
doc: {
|
||||
_index: 'grokdebugger',
|
||||
_type: 'grokdebugger',
|
||||
_id: 'grokdebugger',
|
||||
_source: {
|
||||
"request": "/index.html",
|
||||
"rawEvent": "55.3.244.1 GET /index.html",
|
||||
"method": "GET",
|
||||
"client": "55.3.244.1"
|
||||
},
|
||||
_ingest: {
|
||||
"timestamp": "2017-05-13T23:29:14.809Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// factory method should have removed rawEvent field
|
||||
const expectedStructuredEvent = {
|
||||
request: '/index.html',
|
||||
method: 'GET',
|
||||
client: '55.3.244.1'
|
||||
};
|
||||
const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON(upstreamJson);
|
||||
expect(grokdebuggerResponse.structuredEvent).to.eql(expectedStructuredEvent);
|
||||
expect(grokdebuggerResponse.error).to.eql({});
|
||||
});
|
||||
|
||||
it('returns correct GrokdebuggerResponse instance when there are valid grok parse errors', () => {
|
||||
const upstreamJson = {
|
||||
docs: [
|
||||
{
|
||||
error: {
|
||||
root_cause: [
|
||||
{
|
||||
"type": "exception",
|
||||
"reason": "java.lang.IllegalArgumentException",
|
||||
"header": {
|
||||
"processor_type": "grok"
|
||||
}
|
||||
}
|
||||
],
|
||||
type: 'exception'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON(upstreamJson);
|
||||
expect(grokdebuggerResponse.structuredEvent).to.eql({});
|
||||
expect(grokdebuggerResponse.error).to.be('Provided Grok patterns do not match data in the input');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { get, isEmpty, omit } from 'lodash';
|
||||
|
||||
/**
|
||||
* This model captures the grok debugger response from upstream to be passed to
|
||||
* the view
|
||||
*/
|
||||
export class GrokdebuggerResponse {
|
||||
constructor(props) {
|
||||
this.structuredEvent = get(props, 'structuredEvent', {});
|
||||
this.error = get(props, 'error', {});
|
||||
}
|
||||
|
||||
// generate GrokdebuggerResponse object from elasticsearch response
|
||||
static fromUpstreamJSON(upstreamGrokdebuggerResponse) {
|
||||
const docs = get(upstreamGrokdebuggerResponse, 'docs');
|
||||
const error = docs[0].error;
|
||||
if (!isEmpty(error)) {
|
||||
const opts = { 'error': 'Provided Grok patterns do not match data in the input' };
|
||||
return new GrokdebuggerResponse(opts);
|
||||
}
|
||||
const structuredEvent = omit(get(docs, '0.doc._source'), 'rawEvent');
|
||||
const opts = { structuredEvent };
|
||||
return new GrokdebuggerResponse(opts);
|
||||
}
|
||||
}
|
|
@ -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 { GrokdebuggerResponse } from './grokdebugger_response';
|
|
@ -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 { registerGrokdebuggerRoutes } from './register_grokdebugger_routes';
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import { wrapEsError } from '../../../lib/error_wrappers';
|
||||
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
|
||||
import { GrokdebuggerRequest } from '../../../models/grokdebugger_request';
|
||||
import { GrokdebuggerResponse } from '../../../models/grokdebugger_response';
|
||||
import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory';
|
||||
|
||||
function simulateGrok(callWithRequest, ingestJson) {
|
||||
return callWithRequest('ingest.simulate', {
|
||||
body: ingestJson
|
||||
});
|
||||
}
|
||||
|
||||
export function registerGrokSimulateRoute(server) {
|
||||
const licensePreRouting = licensePreRoutingFactory(server);
|
||||
|
||||
server.route({
|
||||
path: '/api/grokdebugger/simulate',
|
||||
method: 'POST',
|
||||
handler: (request, reply) => {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(request.payload);
|
||||
return simulateGrok(callWithRequest, grokdebuggerRequest.upstreamJSON)
|
||||
.then((simulateResponseFromES) => {
|
||||
const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON(simulateResponseFromES);
|
||||
reply({ grokdebuggerResponse });
|
||||
})
|
||||
.catch(e => reply(wrapEsError(e)));
|
||||
},
|
||||
config: {
|
||||
pre: [ licensePreRouting ]
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import { registerGrokSimulateRoute } from './register_grok_simulate_route';
|
||||
|
||||
export function registerGrokdebuggerRoutes(server) {
|
||||
registerGrokSimulateRoute(server);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue