From 304b322a47cb6f9d697a8516da23497764bb8a32 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 19 Mar 2020 17:32:39 +0100 Subject: [PATCH] [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 --- .../legacy_core_editor/legacy_core_editor.ts | 56 ++++++- .../__tests__/integration.test.js | 145 +++++++++-------- .../models/sense_editor/sense_editor.ts | 1 + .../url_autocomplete.test.js | 1 - .../url_params.test.js | 4 - .../public/lib/autocomplete/autocomplete.ts | 71 ++------- .../public/lib/autocomplete/body_completer.js | 1 - .../console/public/lib/autocomplete/engine.js | 2 +- src/plugins/console/public/lib/kb/kb.js | 4 + .../console/public/types/core_editor.ts | 12 ++ src/plugins/console/server/index.ts | 2 +- src/plugins/console/server/lib/index.ts | 2 +- .../server/lib/spec_definitions/api.js | 72 --------- .../console/server/lib/spec_definitions/es.js | 47 ------ .../{js/query/index.js => index.ts} | 2 +- .../js/{aggregations.js => aggregations.ts} | 28 ++-- .../js/{aliases.js => aliases.ts} | 8 +- .../js/{document.js => document.ts} | 12 +- .../js/{filter.js => filter.ts} | 10 +- .../js/{globals.js => globals.ts} | 11 +- .../{index.d.ts => js/index.ts} | 35 ++-- .../js/{ingest.js => ingest.ts} | 15 +- .../js/{mappings.js => mappings.ts} | 11 +- .../js/query/{dsl.js => dsl.ts} | 21 ++- .../{server.js => js/query/index.ts} | 8 +- .../js/query/{templates.js => templates.ts} | 12 ++ .../js/{reindex.js => reindex.ts} | 9 +- .../js/{search.js => search.ts} | 14 +- .../js/{settings.js => settings.ts} | 9 +- .../js/{shared.js => shared.ts} | 1 + .../server/lib/spec_definitions/json/index.js | 59 ------- src/plugins/console/server/plugin.ts | 26 +-- .../api/console/spec_definitions/index.ts | 24 ++- .../index.js => services/index.ts} | 8 +- .../services/spec_definitions_service.ts | 150 ++++++++++++++++++ src/plugins/console/server/types.ts | 5 + .../console_extensions/server/plugin.ts | 24 +-- 37 files changed, 497 insertions(+), 425 deletions(-) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_autocomplete.test.js (99%) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_params.test.js (95%) delete mode 100644 src/plugins/console/server/lib/spec_definitions/api.js delete mode 100644 src/plugins/console/server/lib/spec_definitions/es.js rename src/plugins/console/server/lib/spec_definitions/{js/query/index.js => index.ts} (94%) rename src/plugins/console/server/lib/spec_definitions/js/{aggregations.js => aggregations.ts} (95%) rename src/plugins/console/server/lib/spec_definitions/js/{aliases.js => aliases.ts} (80%) rename src/plugins/console/server/lib/spec_definitions/js/{document.js => document.ts} (85%) rename src/plugins/console/server/lib/spec_definitions/js/{filter.js => filter.ts} (94%) rename src/plugins/console/server/lib/spec_definitions/js/{globals.js => globals.ts} (85%) rename src/plugins/console/server/lib/spec_definitions/{index.d.ts => js/index.ts} (54%) rename src/plugins/console/server/lib/spec_definitions/js/{ingest.js => ingest.ts} (96%) rename src/plugins/console/server/lib/spec_definitions/js/{mappings.js => mappings.ts} (96%) rename src/plugins/console/server/lib/spec_definitions/js/query/{dsl.js => dsl.ts} (97%) rename src/plugins/console/server/lib/spec_definitions/{server.js => js/query/index.ts} (89%) rename src/plugins/console/server/lib/spec_definitions/js/query/{templates.js => templates.ts} (97%) rename src/plugins/console/server/lib/spec_definitions/js/{reindex.js => reindex.ts} (88%) rename src/plugins/console/server/lib/spec_definitions/js/{search.js => search.ts} (92%) rename src/plugins/console/server/lib/spec_definitions/js/{settings.js => settings.ts} (90%) rename src/plugins/console/server/lib/spec_definitions/js/{shared.js => shared.ts} (94%) delete mode 100644 src/plugins/console/server/lib/spec_definitions/json/index.js rename src/plugins/console/server/{lib/spec_definitions/index.js => services/index.ts} (81%) create mode 100644 src/plugins/console/server/services/spec_definitions_service.ts diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 47947e985092b..fc419b0f10dca 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -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); + }, + }, + ]); + } } diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index 1a09b6b00da9c..c5a0c2ebddf71 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -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; - } + senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function( + err, + terms + ) { + if (testToRun.assertThrows) { + done(); + return; + } - if (err) { - throw err; - } + 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 (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 (!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); + 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 - ); + 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 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'); - } + function posCompare(actual, expected) { + expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); + expect(actual.column).toEqual(expected.column); + } - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); + 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(); + }); }); } diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index f559f5dfcd707..b1444bdf2bbab 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -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) diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js similarity index 99% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js index 40fcd551fb6f7..0758a75695566 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js @@ -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 { diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js similarity index 95% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js index ce2a2553b19ee..72fce53c4f1fe 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js @@ -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'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index e09024ccfc859..d4f10ff4e4277 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -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), diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index e23a58780a362..1aa315c50b9bf 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -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) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index f4df8af871eba..7b64d91c95374 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -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); } }; diff --git a/src/plugins/console/public/lib/kb/kb.js b/src/plugins/console/public/lib/kb/kb.js index 053b82bd81d0a..ef921fa7f476e 100644 --- a/src/plugins/console/public/lib/kb/kb.js +++ b/src/plugins/console/public/lib/kb/kb.js @@ -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({ diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 79dc3ca74200b..b71f4fff44ca5 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -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; } diff --git a/src/plugins/console/server/index.ts b/src/plugins/console/server/index.ts index b603deee12e23..62e5bd6bf8d95 100644 --- a/src/plugins/console/server/index.ts +++ b/src/plugins/console/server/index.ts @@ -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); diff --git a/src/plugins/console/server/lib/index.ts b/src/plugins/console/server/lib/index.ts index 2347084b73a66..0c8fc125874cf 100644 --- a/src/plugins/console/server/lib/index.ts +++ b/src/plugins/console/server/lib/index.ts @@ -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'; diff --git a/src/plugins/console/server/lib/spec_definitions/api.js b/src/plugins/console/server/lib/spec_definitions/api.js deleted file mode 100644 index 9c3835013bce9..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/api.js +++ /dev/null @@ -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; diff --git a/src/plugins/console/server/lib/spec_definitions/es.js b/src/plugins/console/server/lib/spec_definitions/es.js deleted file mode 100644 index fc24a64f8a6f4..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/es.js +++ /dev/null @@ -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; diff --git a/src/plugins/console/server/lib/spec_definitions/js/query/index.js b/src/plugins/console/server/lib/spec_definitions/index.ts similarity index 94% rename from src/plugins/console/server/lib/spec_definitions/js/query/index.js rename to src/plugins/console/server/lib/spec_definitions/index.ts index cbe4e7ed2dd5f..7c70c406d8c22 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/query/index.js +++ b/src/plugins/console/server/lib/spec_definitions/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { queryDsl as default } from './dsl'; +export { jsSpecLoaders } from './js'; diff --git a/src/plugins/console/server/lib/spec_definitions/js/aggregations.js b/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts similarity index 95% rename from src/plugins/console/server/lib/spec_definitions/js/aggregations.js rename to src/plugins/console/server/lib/spec_definitions/js/aggregations.ts index 629e143aa2b43..1170c9edd2366 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/aggregations.js +++ b/src/plugins/console/server/lib/spec_definitions/js/aggregations.ts @@ -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 }, + }); +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/aliases.js b/src/plugins/console/server/lib/spec_definitions/js/aliases.ts similarity index 80% rename from src/plugins/console/server/lib/spec_definitions/js/aliases.js rename to src/plugins/console/server/lib/spec_definitions/js/aliases.ts index f46713fb8dd3f..c7d51b70ab3e3 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/aliases.js +++ b/src/plugins/console/server/lib/spec_definitions/js/aliases.ts @@ -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, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/document.js b/src/plugins/console/server/lib/spec_definitions/js/document.ts similarity index 85% rename from src/plugins/console/server/lib/spec_definitions/js/document.js rename to src/plugins/console/server/lib/spec_definitions/js/document.ts index 2bdaa2ec2af9b..f8214faab2681 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/document.js +++ b/src/plugins/console/server/lib/spec_definitions/js/document.ts @@ -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) { }, }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/filter.js b/src/plugins/console/server/lib/spec_definitions/js/filter.ts similarity index 94% rename from src/plugins/console/server/lib/spec_definitions/js/filter.js rename to src/plugins/console/server/lib/spec_definitions/js/filter.ts index bf669cff788e8..27e02f7cf1837 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/filter.js +++ b/src/plugins/console/server/lib/spec_definitions/js/filter.ts @@ -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 = {}; 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); +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/globals.js b/src/plugins/console/server/lib/spec_definitions/js/globals.ts similarity index 85% rename from src/plugins/console/server/lib/spec_definitions/js/globals.js rename to src/plugins/console/server/lib/spec_definitions/js/globals.ts index 316a76c8c9434..32e1957f74d0b 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/globals.js +++ b/src/plugins/console/server/lib/spec_definitions/js/globals.ts @@ -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: {}, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/index.d.ts b/src/plugins/console/server/lib/spec_definitions/js/index.ts similarity index 54% rename from src/plugins/console/server/lib/spec_definitions/index.d.ts rename to src/plugins/console/server/lib/spec_definitions/js/index.ts index da0125a186c15..234ccd22aaa8b 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.d.ts +++ b/src/plugins/console/server/lib/spec_definitions/js/index.ts @@ -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, +]; diff --git a/src/plugins/console/server/lib/spec_definitions/js/ingest.js b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts similarity index 96% rename from src/plugins/console/server/lib/spec_definitions/js/ingest.js rename to src/plugins/console/server/lib/spec_definitions/js/ingest.ts index edc9cc7b3e45c..1182dc075f42f 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/ingest.js +++ b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts @@ -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); -}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/mappings.js b/src/plugins/console/server/lib/spec_definitions/js/mappings.ts similarity index 96% rename from src/plugins/console/server/lib/spec_definitions/js/mappings.js rename to src/plugins/console/server/lib/spec_definitions/js/mappings.ts index 5884d14d4dc8b..8491bc17a2ff6 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/mappings.js +++ b/src/plugins/console/server/lib/spec_definitions/js/mappings.ts @@ -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) { }, }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/js/query/dsl.ts similarity index 97% rename from src/plugins/console/server/lib/spec_definitions/js/query/dsl.js rename to src/plugins/console/server/lib/spec_definitions/js/query/dsl.ts index 16b952fe0fe4f..d6e5030fb6928 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/query/dsl.js +++ b/src/plugins/console/server/lib/spec_definitions/js/query/dsl.ts @@ -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: '', }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/server.js b/src/plugins/console/server/lib/spec_definitions/js/query/index.ts similarity index 89% rename from src/plugins/console/server/lib/spec_definitions/server.js rename to src/plugins/console/server/lib/spec_definitions/js/query/index.ts index cb855958d403a..f4f896fd7814c 100644 --- a/src/plugins/console/server/lib/spec_definitions/server.js +++ b/src/plugins/console/server/lib/spec_definitions/js/query/index.ts @@ -17,10 +17,4 @@ * under the License. */ -import es from './es'; - -export function resolveApi() { - return { - es: es.asJson(), - }; -} +export { query } from './dsl'; diff --git a/src/plugins/console/server/lib/spec_definitions/js/query/templates.js b/src/plugins/console/server/lib/spec_definitions/js/query/templates.ts similarity index 97% rename from src/plugins/console/server/lib/spec_definitions/js/query/templates.js rename to src/plugins/console/server/lib/spec_definitions/js/query/templates.ts index 9b6311bf5712e..60192f81fec80 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/query/templates.js +++ b/src/plugins/console/server/lib/spec_definitions/js/query/templates.ts @@ -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', diff --git a/src/plugins/console/server/lib/spec_definitions/js/reindex.js b/src/plugins/console/server/lib/spec_definitions/js/reindex.ts similarity index 88% rename from src/plugins/console/server/lib/spec_definitions/js/reindex.js rename to src/plugins/console/server/lib/spec_definitions/js/reindex.ts index 45163d2b3c4c3..862a4323f7bf3 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/reindex.js +++ b/src/plugins/console/server/lib/spec_definitions/js/reindex.ts @@ -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' }, }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/search.js b/src/plugins/console/server/lib/spec_definitions/js/search.ts similarity index 92% rename from src/plugins/console/server/lib/spec_definitions/js/search.js rename to src/plugins/console/server/lib/spec_definitions/js/search.ts index 19ce30d9929a5..e319870d7be5c 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/search.js +++ b/src/plugins/console/server/lib/spec_definitions/js/search.ts @@ -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', }, }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/settings.js b/src/plugins/console/server/lib/spec_definitions/js/settings.ts similarity index 90% rename from src/plugins/console/server/lib/spec_definitions/js/settings.js rename to src/plugins/console/server/lib/spec_definitions/js/settings.ts index 26cd0987c34a5..88c58e618533b 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/settings.js +++ b/src/plugins/console/server/lib/spec_definitions/js/settings.ts @@ -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) { }, }, }); -} +}; diff --git a/src/plugins/console/server/lib/spec_definitions/js/shared.js b/src/plugins/console/server/lib/spec_definitions/js/shared.ts similarity index 94% rename from src/plugins/console/server/lib/spec_definitions/js/shared.js rename to src/plugins/console/server/lib/spec_definitions/js/shared.ts index ace189e2d0913..a884e1aebe2e7 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/shared.js +++ b/src/plugins/console/server/lib/spec_definitions/js/shared.ts @@ -17,6 +17,7 @@ * under the License. */ +/* eslint-disable @typescript-eslint/camelcase */ export const BOOLEAN = Object.freeze({ __one_of: [true, false], }); diff --git a/src/plugins/console/server/lib/spec_definitions/json/index.js b/src/plugins/console/server/lib/spec_definitions/json/index.js deleted file mode 100644 index 19f075e897dbb..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/json/index.js +++ /dev/null @@ -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); -} diff --git a/src/plugins/console/server/plugin.ts b/src/plugins/console/server/plugin.ts index 1954918f4d74f..85b728ea83891 100644 --- a/src/plugins/console/server/plugin.ts +++ b/src/plugins/console/server/plugin.ts @@ -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 { +export class ConsoleServerPlugin implements Plugin { log: Logger; + specDefinitionsService = new SpecDefinitionsService(); + constructor(private readonly ctx: PluginInitializerContext) { this.log = this.ctx.logger.get(); } @@ -72,15 +70,19 @@ export class ConsoleServerPlugin implements Plugin { router, }); - registerSpecDefinitionsRoute({ router }); + registerSpecDefinitionsRoute({ + router, + services: { specDefinitions: this.specDefinitionsService }, + }); return { - addExtensionSpecFilePath, - addProcessorDefinition, + ...this.specDefinitionsService.setup(), }; } start() { - loadSpec(); + return { + ...this.specDefinitionsService.start(), + }; } } diff --git a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts index 88bc250bbfce6..5c7e679cd0d35 100644 --- a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts +++ b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts @@ -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; + endpoints: Record; + }; +} + +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', }, diff --git a/src/plugins/console/server/lib/spec_definitions/index.js b/src/plugins/console/server/services/index.ts similarity index 81% rename from src/plugins/console/server/lib/spec_definitions/index.js rename to src/plugins/console/server/services/index.ts index abf55639fbee8..c8dfeccd23070 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.js +++ b/src/plugins/console/server/services/index.ts @@ -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'; diff --git a/src/plugins/console/server/services/spec_definitions_service.ts b/src/plugins/console/server/services/spec_definitions_service.ts new file mode 100644 index 0000000000000..39a8d5094bd5c --- /dev/null +++ b/src/plugins/console/server/services/spec_definitions_service.ts @@ -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 = {}; + private readonly endpoints: Record = {}; + 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)); + } +} diff --git a/src/plugins/console/server/types.ts b/src/plugins/console/server/types.ts index adafcd4d30526..4f026555ada7b 100644 --- a/src/plugins/console/server/types.ts +++ b/src/plugins/console/server/types.ts @@ -25,6 +25,11 @@ export type ConsoleSetup = ReturnType extends Prom ? U : ReturnType; +/** @public */ +export type ConsoleStart = ReturnType extends Promise + ? U + : ReturnType; + /** @internal */ export interface ESConfigForProxy { hosts: string[]; diff --git a/x-pack/plugins/console_extensions/server/plugin.ts b/x-pack/plugins/console_extensions/server/plugin.ts index f4c41aa0a0ad5..8c2cb4d0db42b 100644 --- a/x-pack/plugins/console_extensions/server/plugin.ts +++ b/x-pack/plugins/console_extensions/server/plugin.ts @@ -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 { log: Logger; constructor(private readonly ctx: PluginInitializerContext) { this.log = this.ctx.logger.get(); } - setup( - core: CoreSetup, - { console: { addProcessorDefinition, addExtensionSpecFilePath } }: SetupDependencies - ) { - addExtensionSpecFilePath(join(__dirname, 'spec/')); + 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('Installed console autocomplete extensions.'); + this.log.debug('Added processor definition extensions.'); } - start() {} }