[Console] Refactor and cleanup of public and server (#60513)
* Clean up use of ace in autocomplete in public Remove ace from lib/autocomplete.ts and set up hooking up of ace in legacy_core_editor. Also remove use of ace mocks in tests. * Added TODO in lib/kb (console public) * Server-side cleanup Refactored the loading of spec into a new SpecDefinitionsService. In this way, state can be contained inside of the service as much as possible. Also converted all JS spec to TS and updated the Console plugin contract so that processors (which alter loaded spec) happen at plugin "start" phase. * Fix types * Small refactor - Updated naming of argument variable in registerAutocompleter - Refactored the SpecDefinitionsService to handle binding of it's own functions
This commit is contained in:
parent
d5ed93ee63
commit
304b322a47
|
@ -18,9 +18,17 @@
|
|||
*/
|
||||
|
||||
import ace from 'brace';
|
||||
import { Editor as IAceEditor } from 'brace';
|
||||
import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace';
|
||||
import $ from 'jquery';
|
||||
import { CoreEditor, Position, Range, Token, TokensProvider, EditorEvent } from '../../../types';
|
||||
import {
|
||||
CoreEditor,
|
||||
Position,
|
||||
Range,
|
||||
Token,
|
||||
TokensProvider,
|
||||
EditorEvent,
|
||||
AutoCompleterFunction,
|
||||
} from '../../../types';
|
||||
import { AceTokensProvider } from '../../../lib/ace_token_provider';
|
||||
import * as curl from '../sense_editor/curl';
|
||||
import smartResize from './smart_resize';
|
||||
|
@ -354,4 +362,48 @@ export class LegacyCoreEditor implements CoreEditor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerAutocompleter(autocompleter: AutoCompleterFunction): void {
|
||||
// Hook into Ace
|
||||
|
||||
// disable standard context based autocompletion.
|
||||
// @ts-ignore
|
||||
ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function(
|
||||
require: any,
|
||||
exports: any
|
||||
) {
|
||||
exports.getCompletions = function(
|
||||
innerEditor: any,
|
||||
session: any,
|
||||
pos: any,
|
||||
prefix: any,
|
||||
callback: any
|
||||
) {
|
||||
callback(null, []);
|
||||
};
|
||||
});
|
||||
|
||||
const langTools = ace.acequire('ace/ext/language_tools');
|
||||
|
||||
langTools.setCompleters([
|
||||
{
|
||||
identifierRegexps: [
|
||||
/[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character
|
||||
],
|
||||
getCompletions: (
|
||||
DO_NOT_USE_1: IAceEditor,
|
||||
DO_NOT_USE_2: IAceEditSession,
|
||||
pos: { row: number; column: number },
|
||||
prefix: string,
|
||||
callback: (...args: any[]) => void
|
||||
) => {
|
||||
const position: Position = {
|
||||
lineNumber: pos.row + 1,
|
||||
column: pos.column + 1,
|
||||
};
|
||||
autocompleter(position, prefix, callback);
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,93 +84,90 @@ describe('Integration', () => {
|
|||
changeListener: function() {},
|
||||
}; // mimic auto complete
|
||||
|
||||
senseEditor.autocomplete._test.getCompletions(
|
||||
senseEditor,
|
||||
null,
|
||||
{ row: cursor.lineNumber - 1, column: cursor.column - 1 },
|
||||
'',
|
||||
function(err, terms) {
|
||||
if (testToRun.assertThrows) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (testToRun.no_context) {
|
||||
expect(!terms || terms.length === 0).toBeTruthy();
|
||||
} else {
|
||||
expect(terms).not.toBeNull();
|
||||
expect(terms.length).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
if (!terms || terms.length === 0) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (testToRun.autoCompleteSet) {
|
||||
const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) {
|
||||
if (typeof t !== 'object') {
|
||||
t = { name: t };
|
||||
}
|
||||
return t;
|
||||
});
|
||||
if (terms.length !== expectedTerms.length) {
|
||||
expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name'));
|
||||
} else {
|
||||
const filteredActualTerms = _.map(terms, function(actualTerm, i) {
|
||||
const expectedTerm = expectedTerms[i];
|
||||
const filteredTerm = {};
|
||||
_.each(expectedTerm, function(v, p) {
|
||||
filteredTerm[p] = actualTerm[p];
|
||||
});
|
||||
return filteredTerm;
|
||||
});
|
||||
expect(filteredActualTerms).toEqual(expectedTerms);
|
||||
}
|
||||
}
|
||||
|
||||
const context = terms[0].context;
|
||||
const {
|
||||
cursor: { lineNumber, column },
|
||||
} = testToRun;
|
||||
senseEditor.autocomplete._test.addReplacementInfoToContext(
|
||||
context,
|
||||
{ lineNumber, column },
|
||||
terms[0].value
|
||||
);
|
||||
|
||||
function ac(prop, propTest) {
|
||||
if (typeof testToRun[prop] !== 'undefined') {
|
||||
if (propTest) {
|
||||
propTest(context[prop], testToRun[prop], prop);
|
||||
} else {
|
||||
expect(context[prop]).toEqual(testToRun[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function posCompare(actual, expected) {
|
||||
expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset);
|
||||
expect(actual.column).toEqual(expected.column);
|
||||
}
|
||||
|
||||
function rangeCompare(actual, expected, name) {
|
||||
posCompare(actual.start, expected.start, name + '.start');
|
||||
posCompare(actual.end, expected.end, name + '.end');
|
||||
}
|
||||
|
||||
ac('prefixToAdd');
|
||||
ac('suffixToAdd');
|
||||
ac('addTemplate');
|
||||
ac('textBoxPosition', posCompare);
|
||||
ac('rangeToReplace', rangeCompare);
|
||||
senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function(
|
||||
err,
|
||||
terms
|
||||
) {
|
||||
if (testToRun.assertThrows) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (testToRun.no_context) {
|
||||
expect(!terms || terms.length === 0).toBeTruthy();
|
||||
} else {
|
||||
expect(terms).not.toBeNull();
|
||||
expect(terms.length).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
if (!terms || terms.length === 0) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (testToRun.autoCompleteSet) {
|
||||
const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) {
|
||||
if (typeof t !== 'object') {
|
||||
t = { name: t };
|
||||
}
|
||||
return t;
|
||||
});
|
||||
if (terms.length !== expectedTerms.length) {
|
||||
expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name'));
|
||||
} else {
|
||||
const filteredActualTerms = _.map(terms, function(actualTerm, i) {
|
||||
const expectedTerm = expectedTerms[i];
|
||||
const filteredTerm = {};
|
||||
_.each(expectedTerm, function(v, p) {
|
||||
filteredTerm[p] = actualTerm[p];
|
||||
});
|
||||
return filteredTerm;
|
||||
});
|
||||
expect(filteredActualTerms).toEqual(expectedTerms);
|
||||
}
|
||||
}
|
||||
|
||||
const context = terms[0].context;
|
||||
const {
|
||||
cursor: { lineNumber, column },
|
||||
} = testToRun;
|
||||
senseEditor.autocomplete._test.addReplacementInfoToContext(
|
||||
context,
|
||||
{ lineNumber, column },
|
||||
terms[0].value
|
||||
);
|
||||
|
||||
function ac(prop, propTest) {
|
||||
if (typeof testToRun[prop] !== 'undefined') {
|
||||
if (propTest) {
|
||||
propTest(context[prop], testToRun[prop], prop);
|
||||
} else {
|
||||
expect(context[prop]).toEqual(testToRun[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function posCompare(actual, expected) {
|
||||
expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset);
|
||||
expect(actual.column).toEqual(expected.column);
|
||||
}
|
||||
|
||||
function rangeCompare(actual, expected, name) {
|
||||
posCompare(actual.start, expected.start, name + '.start');
|
||||
posCompare(actual.end, expected.end, name + '.end');
|
||||
}
|
||||
|
||||
ac('prefixToAdd');
|
||||
ac('suffixToAdd');
|
||||
ac('addTemplate');
|
||||
ac('textBoxPosition', posCompare);
|
||||
ac('rangeToReplace', rangeCompare);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ export class SenseEditor {
|
|||
coreEditor,
|
||||
parser: this.parser,
|
||||
});
|
||||
this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions);
|
||||
this.coreEditor.on(
|
||||
'tokenizerUpdate',
|
||||
this.highlightCurrentRequestsAndUpdateActionBar.bind(this)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import '../../../application/models/sense_editor/sense_editor.test.mocks';
|
||||
|
||||
const _ = require('lodash');
|
||||
import {
|
|
@ -16,10 +16,6 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import '../../../application/models/sense_editor/sense_editor.test.mocks';
|
||||
import 'brace';
|
||||
import 'brace/mode/javascript';
|
||||
import 'brace/mode/json';
|
||||
const _ = require('lodash');
|
||||
import { UrlParams } from '../../autocomplete/url_params';
|
||||
import { populateContext } from '../../autocomplete/engine';
|
|
@ -18,9 +18,9 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import ace, { Editor as AceEditor, IEditSession } from 'brace';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// TODO: All of these imports need to be moved to the core editor so that it can inject components from there.
|
||||
import {
|
||||
getTopLevelUrlCompleteComponents,
|
||||
getEndpointBodyCompleteComponents,
|
||||
|
@ -39,7 +39,7 @@ import { createTokenIterator } from '../../application/factories';
|
|||
|
||||
import { Position, Token, Range, CoreEditor } from '../../types';
|
||||
|
||||
let LAST_EVALUATED_TOKEN: any = null;
|
||||
let lastEvaluatedToken: any = null;
|
||||
|
||||
function isUrlParamsToken(token: any) {
|
||||
switch ((token || {}).type) {
|
||||
|
@ -889,7 +889,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor
|
|||
|
||||
if (!currentToken) {
|
||||
if (pos.lineNumber === 1) {
|
||||
LAST_EVALUATED_TOKEN = null;
|
||||
lastEvaluatedToken = null;
|
||||
return;
|
||||
}
|
||||
currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row
|
||||
|
@ -902,26 +902,26 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor
|
|||
if (parser.isEmptyToken(nextToken)) {
|
||||
// Empty line, or we're not on the edge of current token. Save the current position as base
|
||||
currentToken.position.column = pos.column;
|
||||
LAST_EVALUATED_TOKEN = currentToken;
|
||||
lastEvaluatedToken = currentToken;
|
||||
} else {
|
||||
nextToken.position.lineNumber = pos.lineNumber;
|
||||
LAST_EVALUATED_TOKEN = nextToken;
|
||||
lastEvaluatedToken = nextToken;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LAST_EVALUATED_TOKEN) {
|
||||
LAST_EVALUATED_TOKEN = currentToken;
|
||||
if (!lastEvaluatedToken) {
|
||||
lastEvaluatedToken = currentToken;
|
||||
return; // wait for the next typing.
|
||||
}
|
||||
|
||||
if (
|
||||
LAST_EVALUATED_TOKEN.position.column !== currentToken.position.column ||
|
||||
LAST_EVALUATED_TOKEN.position.lineNumber !== currentToken.position.lineNumber ||
|
||||
LAST_EVALUATED_TOKEN.value === currentToken.value
|
||||
lastEvaluatedToken.position.column !== currentToken.position.column ||
|
||||
lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber ||
|
||||
lastEvaluatedToken.value === currentToken.value
|
||||
) {
|
||||
// not on the same place or nothing changed, cache and wait for the next time
|
||||
LAST_EVALUATED_TOKEN = currentToken;
|
||||
lastEvaluatedToken = currentToken;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -935,7 +935,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor
|
|||
return;
|
||||
}
|
||||
|
||||
LAST_EVALUATED_TOKEN = currentToken;
|
||||
lastEvaluatedToken = currentToken;
|
||||
editor.execCommand('startAutocomplete');
|
||||
},
|
||||
100);
|
||||
|
@ -947,17 +947,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor
|
|||
}
|
||||
}
|
||||
|
||||
function getCompletions(
|
||||
DO_NOT_USE: AceEditor,
|
||||
DO_NOT_USE_SESSION: IEditSession,
|
||||
pos: { row: number; column: number },
|
||||
prefix: string,
|
||||
callback: (...args: any[]) => void
|
||||
) {
|
||||
const position: Position = {
|
||||
lineNumber: pos.row + 1,
|
||||
column: pos.column + 1,
|
||||
};
|
||||
function getCompletions(position: Position, prefix: string, callback: (...args: any[]) => void) {
|
||||
try {
|
||||
const context = getAutoCompleteContext(editor, position);
|
||||
if (!context) {
|
||||
|
@ -1028,39 +1018,12 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor
|
|||
|
||||
editor.on('changeSelection', editorChangeListener);
|
||||
|
||||
// Hook into Ace
|
||||
|
||||
// disable standard context based autocompletion.
|
||||
// @ts-ignore
|
||||
ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function(
|
||||
require: any,
|
||||
exports: any
|
||||
) {
|
||||
exports.getCompletions = function(
|
||||
innerEditor: any,
|
||||
session: any,
|
||||
pos: any,
|
||||
prefix: any,
|
||||
callback: any
|
||||
) {
|
||||
callback(null, []);
|
||||
};
|
||||
});
|
||||
|
||||
const langTools = ace.acequire('ace/ext/language_tools');
|
||||
|
||||
langTools.setCompleters([
|
||||
{
|
||||
identifierRegexps: [
|
||||
/[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character
|
||||
],
|
||||
getCompletions,
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
getCompletions,
|
||||
// TODO: This needs to be cleaned up
|
||||
_test: {
|
||||
getCompletions,
|
||||
getCompletions: (_editor: any, _editSession: any, pos: any, prefix: any, callback: any) =>
|
||||
getCompletions(pos, prefix, callback),
|
||||
addReplacementInfoToContext,
|
||||
addChangeListener: () => editor.on('changeSelection', editorChangeListener),
|
||||
removeChangeListener: () => editor.off('changeSelection', editorChangeListener),
|
||||
|
|
|
@ -115,7 +115,6 @@ class ScopeResolver extends SharedComponent {
|
|||
next: [],
|
||||
};
|
||||
const components = this.resolveLinkToComponents(context, editor);
|
||||
|
||||
_.each(components, function(component) {
|
||||
const componentResult = component.match(token, context, editor);
|
||||
if (componentResult && componentResult.next) {
|
||||
|
|
|
@ -43,7 +43,7 @@ export function wrapComponentWithDefaults(component, defaults) {
|
|||
|
||||
const tracer = function() {
|
||||
if (window.engine_trace) {
|
||||
console.log.call(console, arguments);
|
||||
console.log.call(console, ...arguments);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -146,6 +146,10 @@ function loadApisFromJson(
|
|||
return api;
|
||||
}
|
||||
|
||||
// TODO: clean up setting up of active API and use of jQuery.
|
||||
// This function should be attached to a class that holds the current state, not setup
|
||||
// when the file is required. Also, jQuery should not be used to make network requests
|
||||
// like this, it looks like a minor security issue.
|
||||
export function setActiveApi(api) {
|
||||
if (!api) {
|
||||
$.ajax({
|
||||
|
|
|
@ -29,6 +29,12 @@ export type EditorEvent =
|
|||
| 'change'
|
||||
| 'changeSelection';
|
||||
|
||||
export type AutoCompleterFunction = (
|
||||
pos: Position,
|
||||
prefix: string,
|
||||
callback: (...args: any[]) => void
|
||||
) => void;
|
||||
|
||||
export interface Position {
|
||||
/**
|
||||
* The line number, not zero-indexed.
|
||||
|
@ -256,4 +262,10 @@ export interface CoreEditor {
|
|||
* Register a keyboard shortcut and provide a function to be called.
|
||||
*/
|
||||
registerKeyboardShortcut(opts: { keys: any; fn: () => void; name: string }): void;
|
||||
|
||||
/**
|
||||
* Register a completions function that will be called when the editor
|
||||
* detects a change
|
||||
*/
|
||||
registerAutocompleter(autocompleter: AutoCompleterFunction): void;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server'
|
|||
import { ConfigType, config as configSchema } from './config';
|
||||
import { ConsoleServerPlugin } from './plugin';
|
||||
|
||||
export { ConsoleSetup } from './types';
|
||||
export { ConsoleSetup, ConsoleStart } from './types';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => new ConsoleServerPlugin(ctx);
|
||||
|
||||
|
|
|
@ -22,4 +22,4 @@ export { ProxyConfigCollection } from './proxy_config_collection';
|
|||
export { proxyRequest } from './proxy_request';
|
||||
export { getElasticsearchProxyConfig } from './elasticsearch_proxy_config';
|
||||
export { setHeaders } from './set_headers';
|
||||
export { addProcessorDefinition, addExtensionSpecFilePath, loadSpec } from './spec_definitions';
|
||||
export { jsSpecLoaders } from './spec_definitions';
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
class Api {
|
||||
constructor(name) {
|
||||
this.globalRules = {};
|
||||
this.endpoints = {};
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
addGlobalAutocompleteRules = (parentNode, rules) => {
|
||||
this.globalRules[parentNode] = rules;
|
||||
};
|
||||
|
||||
addEndpointDescription = (endpoint, description = {}) => {
|
||||
let copiedDescription = {};
|
||||
if (this.endpoints[endpoint]) {
|
||||
copiedDescription = { ...this.endpoints[endpoint] };
|
||||
}
|
||||
let urlParamsDef;
|
||||
_.each(description.patterns || [], function(p) {
|
||||
if (p.indexOf('{indices}') >= 0) {
|
||||
urlParamsDef = urlParamsDef || {};
|
||||
urlParamsDef.ignore_unavailable = '__flag__';
|
||||
urlParamsDef.allow_no_indices = '__flag__';
|
||||
urlParamsDef.expand_wildcards = ['open', 'closed'];
|
||||
}
|
||||
});
|
||||
|
||||
if (urlParamsDef) {
|
||||
description.url_params = _.extend(description.url_params || {}, copiedDescription.url_params);
|
||||
_.defaults(description.url_params, urlParamsDef);
|
||||
}
|
||||
|
||||
_.extend(copiedDescription, description);
|
||||
_.defaults(copiedDescription, {
|
||||
id: endpoint,
|
||||
patterns: [endpoint],
|
||||
methods: ['GET'],
|
||||
});
|
||||
|
||||
this.endpoints[endpoint] = copiedDescription;
|
||||
};
|
||||
|
||||
asJson() {
|
||||
return {
|
||||
name: this.name,
|
||||
globals: this.globalRules,
|
||||
endpoints: this.endpoints,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Api;
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import Api from './api';
|
||||
import { getSpec } from './json';
|
||||
import { register } from './js/ingest';
|
||||
const ES = new Api('es');
|
||||
|
||||
export const loadSpec = () => {
|
||||
const spec = getSpec();
|
||||
|
||||
// adding generated specs
|
||||
Object.keys(spec).forEach(endpoint => {
|
||||
ES.addEndpointDescription(endpoint, spec[endpoint]);
|
||||
});
|
||||
|
||||
// adding globals and custom API definitions
|
||||
require('./js/aliases')(ES);
|
||||
require('./js/aggregations')(ES);
|
||||
require('./js/document')(ES);
|
||||
require('./js/filter')(ES);
|
||||
require('./js/globals')(ES);
|
||||
register(ES);
|
||||
require('./js/mappings')(ES);
|
||||
require('./js/settings')(ES);
|
||||
require('./js/query')(ES);
|
||||
require('./js/reindex')(ES);
|
||||
require('./js/search')(ES);
|
||||
};
|
||||
|
||||
export default ES;
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { queryDsl as default } from './dsl';
|
||||
export { jsSpecLoaders } from './js';
|
|
@ -16,8 +16,9 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
/*eslint camelcase: 0*/
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const significantTermsArgs = {
|
||||
__template: {
|
||||
field: '',
|
||||
|
@ -77,7 +78,7 @@ const simple_pipeline = {
|
|||
},
|
||||
buckets_path: '',
|
||||
format: '',
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
};
|
||||
const rules = {
|
||||
'*': {
|
||||
|
@ -461,7 +462,7 @@ const rules = {
|
|||
},
|
||||
buckets_path: '',
|
||||
format: '',
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
window: 5,
|
||||
model: { __one_of: ['simple', 'linear', 'ewma', 'holt', 'holt_winters'] },
|
||||
settings: {
|
||||
|
@ -485,7 +486,7 @@ const rules = {
|
|||
lag: 7,
|
||||
},
|
||||
lag: 7,
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
buckets_path: '',
|
||||
format: '',
|
||||
},
|
||||
|
@ -496,7 +497,7 @@ const rules = {
|
|||
},
|
||||
buckets_path: {},
|
||||
format: '',
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
script: '',
|
||||
},
|
||||
bucket_selector: {
|
||||
|
@ -505,7 +506,7 @@ const rules = {
|
|||
script: '',
|
||||
},
|
||||
buckets_path: {},
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
script: '',
|
||||
},
|
||||
bucket_sort: {
|
||||
|
@ -515,7 +516,7 @@ const rules = {
|
|||
sort: ['{field}'],
|
||||
from: 0,
|
||||
size: 0,
|
||||
gap_policy: gap_policy,
|
||||
gap_policy,
|
||||
},
|
||||
matrix_stats: {
|
||||
__template: {
|
||||
|
@ -526,8 +527,11 @@ const rules = {
|
|||
},
|
||||
};
|
||||
const { terms, histogram, date_histogram } = rules['*'];
|
||||
export default function(api) {
|
||||
api.addGlobalAutocompleteRules('aggregations', rules);
|
||||
api.addGlobalAutocompleteRules('aggs', rules);
|
||||
api.addGlobalAutocompleteRules('groupByAggs', { '*': { terms, histogram, date_histogram } });
|
||||
}
|
||||
|
||||
export const aggs = (specService: SpecDefinitionsService) => {
|
||||
specService.addGlobalAutocompleteRules('aggregations', rules);
|
||||
specService.addGlobalAutocompleteRules('aggs', rules);
|
||||
specService.addGlobalAutocompleteRules('groupByAggs', {
|
||||
'*': { terms, histogram, date_histogram },
|
||||
});
|
||||
};
|
|
@ -16,15 +16,17 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
export default function(api) {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const aliases = (specService: SpecDefinitionsService) => {
|
||||
const aliasRules = {
|
||||
filter: {},
|
||||
routing: '1',
|
||||
search_routing: '1,2',
|
||||
index_routing: '1',
|
||||
};
|
||||
api.addGlobalAutocompleteRules('aliases', {
|
||||
specService.addGlobalAutocompleteRules('aliases', {
|
||||
'*': aliasRules,
|
||||
});
|
||||
}
|
||||
};
|
|
@ -16,9 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
export default function(api) {
|
||||
api.addEndpointDescription('update', {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const document = (specService: SpecDefinitionsService) => {
|
||||
specService.addEndpointDescription('update', {
|
||||
data_autocomplete_rules: {
|
||||
script: {
|
||||
// populated by a global rule
|
||||
|
@ -29,7 +31,7 @@ export default function(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.addEndpointDescription('put_script', {
|
||||
specService.addEndpointDescription('put_script', {
|
||||
methods: ['POST', 'PUT'],
|
||||
patterns: ['_scripts/{lang}/{id}', '_scripts/{lang}/{id}/_create'],
|
||||
url_components: {
|
||||
|
@ -40,7 +42,7 @@ export default function(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.addEndpointDescription('termvectors', {
|
||||
specService.addEndpointDescription('termvectors', {
|
||||
data_autocomplete_rules: {
|
||||
fields: ['{field}'],
|
||||
offsets: { __one_of: [false, true] },
|
||||
|
@ -68,4 +70,4 @@ export default function(api) {
|
|||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -16,8 +16,10 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
const filters = {};
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const filters: Record<string, any> = {};
|
||||
|
||||
filters.and = {
|
||||
__template: {
|
||||
|
@ -324,6 +326,6 @@ filters.nested = {
|
|||
_name: '',
|
||||
};
|
||||
|
||||
export default function(api) {
|
||||
api.addGlobalAutocompleteRules('filter', filters);
|
||||
}
|
||||
export const filter = (specService: SpecDefinitionsService) => {
|
||||
specService.addGlobalAutocompleteRules('filter', filters);
|
||||
};
|
|
@ -16,7 +16,9 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const highlightOptions = {
|
||||
boundary_chars: {},
|
||||
boundary_max_scan: 20,
|
||||
|
@ -48,8 +50,9 @@ const highlightOptions = {
|
|||
},
|
||||
tags_schema: {},
|
||||
};
|
||||
export default function(api) {
|
||||
api.addGlobalAutocompleteRules('highlight', {
|
||||
|
||||
export const globals = (specService: SpecDefinitionsService) => {
|
||||
specService.addGlobalAutocompleteRules('highlight', {
|
||||
...highlightOptions,
|
||||
fields: {
|
||||
'{field}': {
|
||||
|
@ -60,7 +63,7 @@ export default function(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.addGlobalAutocompleteRules('script', {
|
||||
specService.addGlobalAutocompleteRules('script', {
|
||||
__template: {
|
||||
source: 'SCRIPT',
|
||||
},
|
||||
|
@ -70,4 +73,4 @@ export default function(api) {
|
|||
lang: '',
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -17,15 +17,30 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export declare function addProcessorDefinition(...args: any[]): any;
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
export declare function resolveApi(): object;
|
||||
import { aggs } from './aggregations';
|
||||
import { aliases } from './aliases';
|
||||
import { document } from './document';
|
||||
import { filter } from './filter';
|
||||
import { globals } from './globals';
|
||||
import { ingest } from './ingest';
|
||||
import { mappings } from './mappings';
|
||||
import { settings } from './settings';
|
||||
import { query } from './query';
|
||||
import { reindex } from './reindex';
|
||||
import { search } from './search';
|
||||
|
||||
export declare function addExtensionSpecFilePath(...args: any[]): any;
|
||||
|
||||
/**
|
||||
* A function that synchronously reads files JSON from disk and builds
|
||||
* the autocomplete structures served to the client. This must be called
|
||||
* after any extensions have been loaded.
|
||||
*/
|
||||
export declare function loadSpec(): any;
|
||||
export const jsSpecLoaders: Array<(registry: SpecDefinitionsService) => void> = [
|
||||
aggs,
|
||||
aliases,
|
||||
document,
|
||||
filter,
|
||||
globals,
|
||||
ingest,
|
||||
mappings,
|
||||
settings,
|
||||
query,
|
||||
reindex,
|
||||
search,
|
||||
];
|
|
@ -17,6 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const commonPipelineParams = {
|
||||
on_failure: [],
|
||||
ignore_failure: {
|
||||
|
@ -427,27 +430,23 @@ const pipelineDefinition = {
|
|||
version: 123,
|
||||
};
|
||||
|
||||
export const register = api => {
|
||||
export const ingest = (specService: SpecDefinitionsService) => {
|
||||
// Note: this isn't an actual API endpoint. It exists so the forEach processor's "processor" field
|
||||
// may recursively use the autocomplete rules for any processor.
|
||||
api.addEndpointDescription('_processor', {
|
||||
specService.addEndpointDescription('_processor', {
|
||||
data_autocomplete_rules: processorDefinition,
|
||||
});
|
||||
|
||||
api.addEndpointDescription('ingest.put_pipeline', {
|
||||
specService.addEndpointDescription('ingest.put_pipeline', {
|
||||
methods: ['PUT'],
|
||||
patterns: ['_ingest/pipeline/{id}'],
|
||||
data_autocomplete_rules: pipelineDefinition,
|
||||
});
|
||||
|
||||
api.addEndpointDescription('ingest.simulate', {
|
||||
specService.addEndpointDescription('ingest.simulate', {
|
||||
data_autocomplete_rules: {
|
||||
pipeline: pipelineDefinition,
|
||||
docs: [],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const addProcessorDefinition = processor => {
|
||||
processorDefinition.__one_of.push(processor);
|
||||
};
|
|
@ -17,12 +17,15 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash';
|
||||
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
import { BOOLEAN } from './shared';
|
||||
|
||||
export default function(api) {
|
||||
api.addEndpointDescription('put_mapping', {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const mappings = (specService: SpecDefinitionsService) => {
|
||||
specService.addEndpointDescription('put_mapping', {
|
||||
priority: 10, // collides with put doc by id
|
||||
data_autocomplete_rules: {
|
||||
__template: {
|
||||
|
@ -249,4 +252,4 @@ export default function(api) {
|
|||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { SpecDefinitionsService } from '../../../../services';
|
||||
|
||||
import {
|
||||
spanFirstTemplate,
|
||||
spanNearTemplate,
|
||||
|
@ -32,6 +35,8 @@ import {
|
|||
rangeTemplate,
|
||||
regexpTemplate,
|
||||
} from './templates';
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const matchOptions = {
|
||||
cutoff_frequency: 0.001,
|
||||
query: '',
|
||||
|
@ -57,6 +62,7 @@ const matchOptions = {
|
|||
prefix_length: 1,
|
||||
minimum_should_match: 1,
|
||||
};
|
||||
|
||||
const innerHits = {
|
||||
docvalue_fields: ['FIELD'],
|
||||
from: {},
|
||||
|
@ -84,6 +90,7 @@ const innerHits = {
|
|||
__one_of: ['true', 'false'],
|
||||
},
|
||||
};
|
||||
|
||||
const SPAN_QUERIES_NO_FIELD_MASK = {
|
||||
// TODO add one_of for objects
|
||||
span_first: {
|
||||
|
@ -115,6 +122,7 @@ const SPAN_QUERIES_NO_FIELD_MASK = {
|
|||
__scope_link: '.span_within',
|
||||
},
|
||||
};
|
||||
|
||||
const SPAN_QUERIES = {
|
||||
...SPAN_QUERIES_NO_FIELD_MASK,
|
||||
field_masking_span: {
|
||||
|
@ -165,13 +173,14 @@ const DECAY_FUNC_DESC = {
|
|||
decay: 0.5,
|
||||
},
|
||||
};
|
||||
|
||||
const SCORING_FUNCS = {
|
||||
script_score: {
|
||||
__template: {
|
||||
script: "_score * doc['f'].value",
|
||||
},
|
||||
script: {
|
||||
//populated by a global rule
|
||||
// populated by a global rule
|
||||
},
|
||||
},
|
||||
boost_factor: 2.0,
|
||||
|
@ -204,8 +213,8 @@ const SCORING_FUNCS = {
|
|||
},
|
||||
};
|
||||
|
||||
export function queryDsl(api) {
|
||||
api.addGlobalAutocompleteRules('query', {
|
||||
export const query = (specService: SpecDefinitionsService) => {
|
||||
specService.addGlobalAutocompleteRules('query', {
|
||||
match: {
|
||||
__template: {
|
||||
FIELD: 'TEXT',
|
||||
|
@ -631,7 +640,7 @@ export function queryDsl(api) {
|
|||
filter: {},
|
||||
boost: 2.0,
|
||||
script: {
|
||||
//populated by a global rule
|
||||
// populated by a global rule
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -695,7 +704,7 @@ export function queryDsl(api) {
|
|||
script: "_score * doc['f'].value",
|
||||
},
|
||||
script: {
|
||||
//populated by a global rule
|
||||
// populated by a global rule
|
||||
},
|
||||
},
|
||||
wrapper: {
|
||||
|
@ -705,4 +714,4 @@ export function queryDsl(api) {
|
|||
query: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -17,10 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import es from './es';
|
||||
|
||||
export function resolveApi() {
|
||||
return {
|
||||
es: es.asJson(),
|
||||
};
|
||||
}
|
||||
export { query } from './dsl';
|
|
@ -17,23 +17,28 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const regexpTemplate = {
|
||||
FIELD: 'REGEXP',
|
||||
};
|
||||
|
||||
export const fuzzyTemplate = {
|
||||
FIELD: {},
|
||||
};
|
||||
|
||||
export const prefixTemplate = {
|
||||
FIELD: {
|
||||
value: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const rangeTemplate = {
|
||||
FIELD: {
|
||||
gte: 10,
|
||||
lte: 20,
|
||||
},
|
||||
};
|
||||
|
||||
export const spanFirstTemplate = {
|
||||
match: {
|
||||
span_term: {
|
||||
|
@ -42,6 +47,7 @@ export const spanFirstTemplate = {
|
|||
},
|
||||
end: 3,
|
||||
};
|
||||
|
||||
export const spanNearTemplate = {
|
||||
clauses: [
|
||||
{
|
||||
|
@ -55,11 +61,13 @@ export const spanNearTemplate = {
|
|||
slop: 12,
|
||||
in_order: false,
|
||||
};
|
||||
|
||||
export const spanTermTemplate = {
|
||||
FIELD: {
|
||||
value: 'VALUE',
|
||||
},
|
||||
};
|
||||
|
||||
export const spanNotTemplate = {
|
||||
include: {
|
||||
span_term: {
|
||||
|
@ -76,6 +84,7 @@ export const spanNotTemplate = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const spanOrTemplate = {
|
||||
clauses: [
|
||||
{
|
||||
|
@ -87,6 +96,7 @@ export const spanOrTemplate = {
|
|||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const spanContainingTemplate = {
|
||||
little: {
|
||||
span_term: {
|
||||
|
@ -118,6 +128,7 @@ export const spanContainingTemplate = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const spanWithinTemplate = {
|
||||
little: {
|
||||
span_term: {
|
||||
|
@ -149,6 +160,7 @@ export const spanWithinTemplate = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const wildcardTemplate = {
|
||||
FIELD: {
|
||||
value: 'VALUE',
|
|
@ -17,8 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export default function(api) {
|
||||
api.addEndpointDescription('reindex', {
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const reindex = (specService: SpecDefinitionsService) => {
|
||||
specService.addEndpointDescription('reindex', {
|
||||
methods: ['POST'],
|
||||
patterns: ['_reindex'],
|
||||
data_autocomplete_rules: {
|
||||
|
@ -62,4 +65,4 @@ export default function(api) {
|
|||
script: { __scope_link: 'GLOBAL.script' },
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -16,9 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
|
||||
export default function(api) {
|
||||
api.addEndpointDescription('search', {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const search = (specService: SpecDefinitionsService) => {
|
||||
specService.addEndpointDescription('search', {
|
||||
priority: 10, // collides with get doc by id
|
||||
data_autocomplete_rules: {
|
||||
query: {
|
||||
|
@ -191,7 +193,7 @@ export default function(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.addEndpointDescription('search_template', {
|
||||
specService.addEndpointDescription('search_template', {
|
||||
data_autocomplete_rules: {
|
||||
template: {
|
||||
__one_of: [{ __scope_link: 'search' }, { __scope_link: 'GLOBAL.script' }],
|
||||
|
@ -200,18 +202,18 @@ export default function(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.addEndpointDescription('render_search_template', {
|
||||
specService.addEndpointDescription('render_search_template', {
|
||||
data_autocomplete_rules: {
|
||||
__one_of: [{ source: { __scope_link: 'search' } }, { __scope_link: 'GLOBAL.script' }],
|
||||
params: {},
|
||||
},
|
||||
});
|
||||
|
||||
api.addEndpointDescription('_search/template/{id}', {
|
||||
specService.addEndpointDescription('_search/template/{id}', {
|
||||
data_autocomplete_rules: {
|
||||
template: {
|
||||
__scope_link: 'search',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -16,11 +16,12 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SpecDefinitionsService } from '../../../services';
|
||||
import { BOOLEAN } from './shared';
|
||||
|
||||
export default function(api) {
|
||||
api.addEndpointDescription('put_settings', {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const settings = (specService: SpecDefinitionsService) => {
|
||||
specService.addEndpointDescription('put_settings', {
|
||||
data_autocomplete_rules: {
|
||||
refresh_interval: '1s',
|
||||
number_of_shards: 1,
|
||||
|
@ -71,4 +72,4 @@ export default function(api) {
|
|||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
export const BOOLEAN = Object.freeze({
|
||||
__one_of: [true, false],
|
||||
});
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import glob from 'glob';
|
||||
import { join, basename } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
const extensionSpecFilePaths = [];
|
||||
function _getSpec(dirname = __dirname) {
|
||||
const generatedFiles = glob.sync(join(dirname, 'generated', '*.json'));
|
||||
const overrideFiles = glob.sync(join(dirname, 'overrides', '*.json'));
|
||||
|
||||
return generatedFiles.reduce((acc, file) => {
|
||||
const overrideFile = overrideFiles.find(f => basename(f) === basename(file));
|
||||
const loadedSpec = JSON.parse(readFileSync(file, 'utf8'));
|
||||
if (overrideFile) {
|
||||
merge(loadedSpec, JSON.parse(readFileSync(overrideFile, 'utf8')));
|
||||
}
|
||||
const spec = {};
|
||||
Object.entries(loadedSpec).forEach(([key, value]) => {
|
||||
if (acc[key]) {
|
||||
// add time to remove key collision
|
||||
spec[`${key}${Date.now()}`] = value;
|
||||
} else {
|
||||
spec[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return { ...acc, ...spec };
|
||||
}, {});
|
||||
}
|
||||
export function getSpec() {
|
||||
const result = _getSpec();
|
||||
extensionSpecFilePaths.forEach(extensionSpecFilePath => {
|
||||
merge(result, _getSpec(extensionSpecFilePath));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function addExtensionSpecFilePath(extensionSpecFilePath) {
|
||||
extensionSpecFilePaths.push(extensionSpecFilePath);
|
||||
}
|
|
@ -21,20 +21,18 @@ import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/serv
|
|||
|
||||
import { readLegacyEsConfig } from '../../../legacy/core_plugins/console_legacy';
|
||||
|
||||
import {
|
||||
ProxyConfigCollection,
|
||||
addExtensionSpecFilePath,
|
||||
addProcessorDefinition,
|
||||
loadSpec,
|
||||
} from './lib';
|
||||
import { ProxyConfigCollection } from './lib';
|
||||
import { SpecDefinitionsService } from './services';
|
||||
import { ConfigType } from './config';
|
||||
import { registerProxyRoute } from './routes/api/console/proxy';
|
||||
import { registerSpecDefinitionsRoute } from './routes/api/console/spec_definitions';
|
||||
import { ESConfigForProxy, ConsoleSetup } from './types';
|
||||
import { ESConfigForProxy, ConsoleSetup, ConsoleStart } from './types';
|
||||
|
||||
export class ConsoleServerPlugin implements Plugin<ConsoleSetup> {
|
||||
export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
|
||||
log: Logger;
|
||||
|
||||
specDefinitionsService = new SpecDefinitionsService();
|
||||
|
||||
constructor(private readonly ctx: PluginInitializerContext<ConfigType>) {
|
||||
this.log = this.ctx.logger.get();
|
||||
}
|
||||
|
@ -72,15 +70,19 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup> {
|
|||
router,
|
||||
});
|
||||
|
||||
registerSpecDefinitionsRoute({ router });
|
||||
registerSpecDefinitionsRoute({
|
||||
router,
|
||||
services: { specDefinitions: this.specDefinitionsService },
|
||||
});
|
||||
|
||||
return {
|
||||
addExtensionSpecFilePath,
|
||||
addProcessorDefinition,
|
||||
...this.specDefinitionsService.setup(),
|
||||
};
|
||||
}
|
||||
|
||||
start() {
|
||||
loadSpec();
|
||||
return {
|
||||
...this.specDefinitionsService.start(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,30 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { IRouter, RequestHandler } from 'kibana/server';
|
||||
import { resolveApi } from '../../../../lib/spec_definitions';
|
||||
import { SpecDefinitionsService } from '../../../../services';
|
||||
|
||||
export const registerSpecDefinitionsRoute = ({ router }: { router: IRouter }) => {
|
||||
interface SpecDefinitionsRouteResponse {
|
||||
es: {
|
||||
name: string;
|
||||
globals: Record<string, any>;
|
||||
endpoints: Record<string, any>;
|
||||
};
|
||||
}
|
||||
|
||||
export const registerSpecDefinitionsRoute = ({
|
||||
router,
|
||||
services,
|
||||
}: {
|
||||
router: IRouter;
|
||||
services: { specDefinitions: SpecDefinitionsService };
|
||||
}) => {
|
||||
const handler: RequestHandler = async (ctx, request, response) => {
|
||||
const specResponse: SpecDefinitionsRouteResponse = {
|
||||
es: services.specDefinitions.asJson(),
|
||||
};
|
||||
|
||||
return response.ok({
|
||||
body: resolveApi(),
|
||||
body: specResponse,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
|
|
@ -17,10 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { addProcessorDefinition } from './js/ingest';
|
||||
|
||||
export { addExtensionSpecFilePath } from './json';
|
||||
|
||||
export { loadSpec } from './es';
|
||||
|
||||
export { resolveApi } from './server';
|
||||
export { SpecDefinitionsService } from './spec_definitions_service';
|
150
src/plugins/console/server/services/spec_definitions_service.ts
Normal file
150
src/plugins/console/server/services/spec_definitions_service.ts
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import _, { merge } from 'lodash';
|
||||
import glob from 'glob';
|
||||
import { basename, join, resolve } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
import { jsSpecLoaders } from '../lib';
|
||||
|
||||
const PATH_TO_OSS_JSON_SPEC = resolve(__dirname, '../lib/spec_definitions/json');
|
||||
|
||||
export class SpecDefinitionsService {
|
||||
private readonly name = 'es';
|
||||
|
||||
private readonly globalRules: Record<string, any> = {};
|
||||
private readonly endpoints: Record<string, any> = {};
|
||||
private readonly extensionSpecFilePaths: string[] = [];
|
||||
|
||||
private hasLoadedSpec = false;
|
||||
|
||||
public addGlobalAutocompleteRules(parentNode: string, rules: any) {
|
||||
this.globalRules[parentNode] = rules;
|
||||
}
|
||||
|
||||
public addEndpointDescription(endpoint: string, description: any = {}) {
|
||||
let copiedDescription: any = {};
|
||||
if (this.endpoints[endpoint]) {
|
||||
copiedDescription = { ...this.endpoints[endpoint] };
|
||||
}
|
||||
let urlParamsDef: any;
|
||||
_.each(description.patterns || [], function(p) {
|
||||
if (p.indexOf('{indices}') >= 0) {
|
||||
urlParamsDef = urlParamsDef || {};
|
||||
urlParamsDef.ignore_unavailable = '__flag__';
|
||||
urlParamsDef.allow_no_indices = '__flag__';
|
||||
urlParamsDef.expand_wildcards = ['open', 'closed'];
|
||||
}
|
||||
});
|
||||
|
||||
if (urlParamsDef) {
|
||||
description.url_params = _.extend(description.url_params || {}, copiedDescription.url_params);
|
||||
_.defaults(description.url_params, urlParamsDef);
|
||||
}
|
||||
|
||||
_.extend(copiedDescription, description);
|
||||
_.defaults(copiedDescription, {
|
||||
id: endpoint,
|
||||
patterns: [endpoint],
|
||||
methods: ['GET'],
|
||||
});
|
||||
|
||||
this.endpoints[endpoint] = copiedDescription;
|
||||
}
|
||||
|
||||
public asJson() {
|
||||
return {
|
||||
name: this.name,
|
||||
globals: this.globalRules,
|
||||
endpoints: this.endpoints,
|
||||
};
|
||||
}
|
||||
|
||||
public addExtensionSpecFilePath(path: string) {
|
||||
this.extensionSpecFilePaths.push(path);
|
||||
}
|
||||
|
||||
public addProcessorDefinition(processor: any) {
|
||||
if (!this.hasLoadedSpec) {
|
||||
throw new Error(
|
||||
'Cannot add a processor definition because spec definitions have not loaded!'
|
||||
);
|
||||
}
|
||||
this.endpoints._processor!.data_autocomplete_rules.__one_of.push(processor);
|
||||
}
|
||||
|
||||
public setup() {
|
||||
return {
|
||||
addExtensionSpecFilePath: this.addExtensionSpecFilePath.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (!this.hasLoadedSpec) {
|
||||
this.loadJsonSpec();
|
||||
this.loadJSSpec();
|
||||
this.hasLoadedSpec = true;
|
||||
return {
|
||||
addProcessorDefinition: this.addProcessorDefinition.bind(this),
|
||||
};
|
||||
} else {
|
||||
throw new Error('Service has already started!');
|
||||
}
|
||||
}
|
||||
|
||||
private loadJSONSpecInDir(dirname: string) {
|
||||
const generatedFiles = glob.sync(join(dirname, 'generated', '*.json'));
|
||||
const overrideFiles = glob.sync(join(dirname, 'overrides', '*.json'));
|
||||
|
||||
return generatedFiles.reduce((acc, file) => {
|
||||
const overrideFile = overrideFiles.find(f => basename(f) === basename(file));
|
||||
const loadedSpec = JSON.parse(readFileSync(file, 'utf8'));
|
||||
if (overrideFile) {
|
||||
merge(loadedSpec, JSON.parse(readFileSync(overrideFile, 'utf8')));
|
||||
}
|
||||
const spec: any = {};
|
||||
Object.entries(loadedSpec).forEach(([key, value]) => {
|
||||
if (acc[key]) {
|
||||
// add time to remove key collision
|
||||
spec[`${key}${Date.now()}`] = value;
|
||||
} else {
|
||||
spec[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return { ...acc, ...spec };
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
private loadJsonSpec() {
|
||||
const result = this.loadJSONSpecInDir(PATH_TO_OSS_JSON_SPEC);
|
||||
this.extensionSpecFilePaths.forEach(extensionSpecFilePath => {
|
||||
merge(result, this.loadJSONSpecInDir(extensionSpecFilePath));
|
||||
});
|
||||
|
||||
Object.keys(result).forEach(endpoint => {
|
||||
this.addEndpointDescription(endpoint, result[endpoint]);
|
||||
});
|
||||
}
|
||||
|
||||
private loadJSSpec() {
|
||||
jsSpecLoaders.forEach(addJsSpec => addJsSpec(this));
|
||||
}
|
||||
}
|
|
@ -25,6 +25,11 @@ export type ConsoleSetup = ReturnType<ConsoleServerPlugin['setup']> extends Prom
|
|||
? U
|
||||
: ReturnType<ConsoleServerPlugin['setup']>;
|
||||
|
||||
/** @public */
|
||||
export type ConsoleStart = ReturnType<ConsoleServerPlugin['start']> extends Promise<infer U>
|
||||
? U
|
||||
: ReturnType<ConsoleServerPlugin['start']>;
|
||||
|
||||
/** @internal */
|
||||
export interface ESConfigForProxy {
|
||||
hosts: string[];
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { join } from 'path';
|
||||
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
|
||||
import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
|
||||
|
||||
import { ConsoleSetup } from '../../../../src/plugins/console/server';
|
||||
import { ConsoleSetup, ConsoleStart } from '../../../../src/plugins/console/server';
|
||||
|
||||
import { processors } from './spec/ingest/index';
|
||||
|
||||
|
@ -14,19 +14,25 @@ interface SetupDependencies {
|
|||
console: ConsoleSetup;
|
||||
}
|
||||
|
||||
interface StartDependencies {
|
||||
console: ConsoleStart;
|
||||
}
|
||||
|
||||
const CONSOLE_XPACK_JSON_SPEC_PATH = join(__dirname, 'spec/');
|
||||
|
||||
export class ConsoleExtensionsServerPlugin implements Plugin<void, void, SetupDependencies> {
|
||||
log: Logger;
|
||||
constructor(private readonly ctx: PluginInitializerContext) {
|
||||
this.log = this.ctx.logger.get();
|
||||
}
|
||||
|
||||
setup(
|
||||
core: CoreSetup,
|
||||
{ console: { addProcessorDefinition, addExtensionSpecFilePath } }: SetupDependencies
|
||||
) {
|
||||
addExtensionSpecFilePath(join(__dirname, 'spec/'));
|
||||
processors.forEach(processor => addProcessorDefinition(processor));
|
||||
this.log.debug('Installed console autocomplete extensions.');
|
||||
setup(core: CoreSetup, { console: { addExtensionSpecFilePath } }: SetupDependencies) {
|
||||
addExtensionSpecFilePath(CONSOLE_XPACK_JSON_SPEC_PATH);
|
||||
this.log.debug(`Added extension path to ${CONSOLE_XPACK_JSON_SPEC_PATH}...`);
|
||||
}
|
||||
|
||||
start(core: CoreStart, { console: { addProcessorDefinition } }: StartDependencies) {
|
||||
processors.forEach(processor => addProcessorDefinition(processor));
|
||||
this.log.debug('Added processor definition extensions.');
|
||||
}
|
||||
start() {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue