diff --git a/src/core_plugins/console/public/src/autocomplete.js b/src/core_plugins/console/public/src/autocomplete.js index 9ffbe68dadf16..eb9838dbf8210 100644 --- a/src/core_plugins/console/public/src/autocomplete.js +++ b/src/core_plugins/console/public/src/autocomplete.js @@ -25,7 +25,7 @@ import { } from './kb'; import utils from './utils'; import { populateContext } from './autocomplete/engine'; -import { URL_PATH_END_MARKER } from './autocomplete/url_pattern_matcher'; +import { URL_PATH_END_MARKER } from './autocomplete/components'; import _ from 'lodash'; import ace from 'brace'; import 'brace/ext/language_tools'; @@ -529,7 +529,10 @@ export default function (editor) { context.token = ret.token; context.otherTokenValues = ret.otherTokenValues; context.urlTokenPath = ret.urlTokenPath; - populateContext(ret.urlTokenPath, context, editor, true, getTopLevelUrlCompleteComponents()); + const components = getTopLevelUrlCompleteComponents(context.method); + populateContext(ret.urlTokenPath, context, editor, true, components); + + context.autoCompleteSet = addMetaToTermsList(context.autoCompleteSet, 'endpoint'); } @@ -544,7 +547,7 @@ export default function (editor) { return context; } - populateContext(ret.urlTokenPath, context, editor, false, getTopLevelUrlCompleteComponents()); + populateContext(ret.urlTokenPath, context, editor, false, getTopLevelUrlCompleteComponents(context.method)); if (!context.endpoint) { console.log('couldn\'t resolve an endpoint.'); @@ -563,7 +566,7 @@ export default function (editor) { } populateContext(tokenPath, context, editor, true, - context.endpoint.paramsAutocomplete.getTopLevelComponents()); + context.endpoint.paramsAutocomplete.getTopLevelComponents(context.method)); return context; } @@ -579,7 +582,7 @@ export default function (editor) { return context; } - populateContext(ret.urlTokenPath, context, editor, false, getTopLevelUrlCompleteComponents()); + populateContext(ret.urlTokenPath, context, editor, false, getTopLevelUrlCompleteComponents(context.method)); context.bodyTokenPath = ret.bodyTokenPath; if (!ret.bodyTokenPath) { // zero length tokenPath is true @@ -741,6 +744,10 @@ export default function (editor) { if (t.type === 'url.part' || t.type === 'url.param' || t.type === 'url.value') { // we are on the same line as cursor and dealing with a url. Current token is not part of the context t = tokenIter.stepBackward(); + // This will force method parsing + while (t.type === 'whitespace') { + t = tokenIter.stepBackward(); + } } bodyTokenPath = null; // no not on a body line. } diff --git a/src/core_plugins/console/public/src/autocomplete/body_completer.js b/src/core_plugins/console/public/src/autocomplete/body_completer.js index 55d325ddc82aa..0fa37e3926dca 100644 --- a/src/core_plugins/console/public/src/autocomplete/body_completer.js +++ b/src/core_plugins/console/public/src/autocomplete/body_completer.js @@ -18,28 +18,124 @@ */ const _ = require('lodash'); -const engine = require('./engine'); +import { WalkingState, walkTokenPath, wrapComponentWithDefaults } from './engine'; +import { + ConstantComponent, + SharedComponent, + ObjectComponent, + ConditionalProxy, + GlobalOnlyComponent, +} from './components'; function CompilingContext(endpointId, parametrizedComponentFactories) { this.parametrizedComponentFactories = parametrizedComponentFactories; this.endpointId = endpointId; } +/** + * An object to resolve scope links (syntax endpoint.path1.path2) + * @param link the link either string (endpoint.path1.path2, or .path1.path2) or a function (context,editor) + * which returns a description to be compiled + * @constructor + * @param compilingContext + * + * + * For this to work we expect the context to include a method context.endpointComponentResolver(endpoint) + * which should return the top level components for the given endpoint + */ + + +function resolvePathToComponents(tokenPath, context, editor, components) { + const walkStates = walkTokenPath(tokenPath, [new WalkingState('ROOT', components, [])], context, editor); + const result = [].concat.apply([], _.pluck(walkStates, 'components')); + return result; +} + +class ScopeResolver extends SharedComponent { + constructor(link, compilingContext) { + super('__scope_link'); + if (_.isString(link) && link[0] === '.') { + // relative link, inject current endpoint + if (link === '.') { + link = compilingContext.endpointId; + } + else { + link = compilingContext.endpointId + link; + } + } + this.link = link; + this.compilingContext = compilingContext; + } + + resolveLinkToComponents(context, editor) { + if (_.isFunction(this.link)) { + const desc = this.link(context, editor); + return compileDescription(desc, this.compilingContext); + } + if (!_.isString(this.link)) { + throw new Error('unsupported link format', this.link); + } + + let path = this.link.replace(/\./g, '{').split(/(\{)/); + const endpoint = path[0]; + let components; + try { + if (endpoint === 'GLOBAL') { + // global rules need an extra indirection + if (path.length < 3) { + throw new Error('missing term in global link: ' + this.link); + } + const term = path[2]; + components = context.globalComponentResolver(term); + path = path.slice(3); + } + else { + path = path.slice(1); + components = context.endpointComponentResolver(endpoint); + } + } + catch (e) { + throw new Error('failed to resolve link [' + this.link + ']: ' + e); + } + return resolvePathToComponents(path, context, editor, components); + } + + getTerms(context, editor) { + const options = []; + const components = this.resolveLinkToComponents(context, editor); + _.each(components, function (component) { + options.push.apply(options, component.getTerms(context, editor)); + }); + return options; + } + + match(token, context, editor) { + const result = { + next: [] + }; + const components = this.resolveLinkToComponents(context, editor); + + _.each(components, function (component) { + const componentResult = component.match(token, context, editor); + if (componentResult && componentResult.next) { + result.next.push.apply(result.next, componentResult.next); + } + }); + + return result; + } +} function getTemplate(description) { if (description.__template) { return description.__template; - } - else if (description.__one_of) { + } else if (description.__one_of) { return getTemplate(description.__one_of[0]); - } - else if (description.__any_of) { + } else if (description.__any_of) { return []; - } - else if (description.__scope_link) { + } else if (description.__scope_link) { // assume an object for now. return {}; - } - else if (Array.isArray(description)) { + } else if (Array.isArray(description)) { if (description.length === 1) { if (_.isObject(description[0])) { // shortcut to save typing @@ -49,14 +145,11 @@ function getTemplate(description) { } } return []; - } - else if (_.isObject(description)) { + } else if (_.isObject(description)) { return {}; - } - else if (_.isString(description) && !/^\{.*\}$/.test(description)) { + } else if (_.isString(description) && !/^\{.*\}$/.test(description)) { return description; - } - else { + } else { return description; } } @@ -78,8 +171,7 @@ function getOptions(description) { function compileDescription(description, compilingContext) { if (Array.isArray(description)) { return [compileList(description, compilingContext)]; - } - else if (_.isObject(description)) { + } else if (_.isObject(description)) { // test for objects list as arrays are also objects if (description.__scope_link) { return [new ScopeResolver(description.__scope_link, compilingContext)]; @@ -88,9 +180,11 @@ function compileDescription(description, compilingContext) { return [compileList(description.__any_of, compilingContext)]; } if (description.__one_of) { - return _.flatten(_.map(description.__one_of, function (d) { - return compileDescription(d, compilingContext); - })); + return _.flatten( + _.map(description.__one_of, function (d) { + return compileDescription(d, compilingContext); + }) + ); } const obj = compileObject(description, compilingContext); if (description.__condition) { @@ -98,14 +192,11 @@ function compileDescription(description, compilingContext) { } else { return [obj]; } - } - else if (_.isString(description) && /^\{.*\}$/.test(description)) { + } else if (_.isString(description) && /^\{.*\}$/.test(description)) { return [compileParametrizedValue(description, compilingContext)]; + } else { + return [new ConstantComponent(description)]; } - else { - return [new engine.ConstantComponent(description)]; - } - } function compileParametrizedValue(value, compilingContext, template) { @@ -116,14 +207,13 @@ function compileParametrizedValue(value, compilingContext, template) { } component = component(value, null, template); if (!_.isUndefined(template)) { - component = engine.wrapComponentWithDefaults(component, { template: template }); + component = wrapComponentWithDefaults(component, { template: template }); } return component; - } function compileObject(objDescription, compilingContext) { - const objectC = new engine.ConstantComponent('{'); + const objectC = new ConstantComponent('{'); const constants = []; const patterns = []; _.each(objDescription, function (desc, key) { @@ -135,16 +225,18 @@ function compileObject(objDescription, compilingContext) { const options = getOptions(desc); let component; if (/^\{.*\}$/.test(key)) { - component = compileParametrizedValue(key, compilingContext, options.template); + component = compileParametrizedValue( + key, + compilingContext, + options.template + ); patterns.push(component); - } - else if (key === '*') { - component = new engine.SharedComponent(key); + } else if (key === '*') { + component = new SharedComponent(key); patterns.push(component); - } - else { + } else { options.name = key; - component = new engine.ConstantComponent(key, null, [options]); + component = new ConstantComponent(key, null, [options]); constants.push(component); } _.map(compileDescription(desc, compilingContext), function (subComponent) { @@ -156,7 +248,7 @@ function compileObject(objDescription, compilingContext) { } function compileList(listRule, compilingContext) { - const listC = new engine.ConstantComponent('['); + const listC = new ConstantComponent('['); _.each(listRule, function (desc) { _.each(compileDescription(desc, compilingContext), function (component) { listC.addComponent(component); @@ -169,7 +261,10 @@ function compileList(listRule, compilingContext) { function compileCondition(description, compiledObject) { if (description.lines_regex) { return new ConditionalProxy(function (context, editor) { - const lines = editor.getSession().getLines(context.requestStartRow, editor.getCursorPosition().row).join('\n'); + const lines = editor + .getSession() + .getLines(context.requestStartRow, editor.getCursorPosition().row) + .join('\n'); return new RegExp(description.lines_regex, 'm').test(lines); }, compiledObject); } else { @@ -177,228 +272,6 @@ function compileCondition(description, compiledObject) { } } -/** - * @param constants list of components that represent constant keys - * @param patternsAndWildCards list of components that represent patterns and should be matched only if - * there is no constant matches - */ -function ObjectComponent(name, constants, patternsAndWildCards) { - engine.AutocompleteComponent.call(this, name); - this.constants = constants; - this.patternsAndWildCards = patternsAndWildCards; -} - -ObjectComponent.prototype = _.create( - engine.AutocompleteComponent.prototype, - { 'constructor': ObjectComponent }); - - -(function (cls) { - cls.getTerms = function (context, editor) { - const options = []; - _.each(this.constants, function (component) { - options.push.apply(options, component.getTerms(context, editor)); - }); - _.each(this.patternsAndWildCards, function (component) { - options.push.apply(options, component.getTerms(context, editor)); - }); - return options; - }; - - cls.match = function (token, context, editor) { - const result = { - next: [] - }; - _.each(this.constants, function (component) { - const componentResult = component.match(token, context, editor); - if (componentResult && componentResult.next) { - result.next.push.apply(result.next, componentResult.next); - } - }); - - // try to link to GLOBAL rules - const globalRules = context.globalComponentResolver(token, false); - if (globalRules) { - result.next.push.apply(result.next, globalRules); - } - - if (result.next.length) { - return result; - } - _.each(this.patternsAndWildCards, function (component) { - const componentResult = component.match(token, context, editor); - if (componentResult && componentResult.next) { - result.next.push.apply(result.next, componentResult.next); - } - }); - - return result; - - }; -}(ObjectComponent.prototype)); - -/** - * An object to resolve scope links (syntax endpoint.path1.path2) - * @param link the link either string (endpoint.path1.path2, or .path1.path2) or a function (context,editor) - * which returns a description to be compiled - * @constructor - * @param compilingContext - * - * - * For this to work we expect the context to include a method context.endpointComponentResolver(endpoint) - * which should return the top level components for the given endpoint - */ -function ScopeResolver(link, compilingContext) { - engine.SharedComponent.call(this, '__scope_link', null); - if (_.isString(link) && link[0] === '.') { - // relative link, inject current endpoint - if (link === '.') { - link = compilingContext.endpointId; - } - else { - link = compilingContext.endpointId + link; - } - } - this.link = link; - this.compilingContext = compilingContext; -} - -ScopeResolver.prototype = _.create( - engine.SharedComponent.prototype, - { 'constructor': ScopeResolver }); - - -(function (cls) { - - cls.resolveLinkToComponents = function (context, editor) { - if (_.isFunction(this.link)) { - const desc = this.link(context, editor); - return compileDescription(desc, this.compilingContext); - } - if (!_.isString(this.link)) { - throw new Error('unsupported link format', this.link); - } - - let path = this.link.replace(/\./g, '{').split(/(\{)/); - const endpoint = path[0]; - let components; - try { - if (endpoint === 'GLOBAL') { - // global rules need an extra indirection - if (path.length < 3) { - throw new Error('missing term in global link: ' + this.link); - } - const term = path[2]; - components = context.globalComponentResolver(term); - path = path.slice(3); - } - else { - path = path.slice(1); - components = context.endpointComponentResolver(endpoint); - } - } - catch (e) { - throw new Error('failed to resolve link [' + this.link + ']: ' + e); - } - return engine.resolvePathToComponents(path, context, editor, components); - }; - - cls.getTerms = function (context, editor) { - const options = []; - const components = this.resolveLinkToComponents(context, editor); - _.each(components, function (component) { - options.push.apply(options, component.getTerms(context, editor)); - }); - return options; - }; - - cls.match = function (token, context, editor) { - const result = { - next: [] - }; - const components = this.resolveLinkToComponents(context, editor); - - _.each(components, function (component) { - const componentResult = component.match(token, context, editor); - if (componentResult && componentResult.next) { - result.next.push.apply(result.next, componentResult.next); - } - }); - - return result; - }; -}(ScopeResolver.prototype)); - - -function ConditionalProxy(predicate, delegate) { - engine.SharedComponent.call(this, '__condition', null); - this.predicate = predicate; - this.delegate = delegate; -} - -ConditionalProxy.prototype = _.create( - engine.SharedComponent.prototype, - { 'constructor': ConditionalProxy }); - - -(function (cls) { - - cls.getTerms = function (context, editor) { - if (this.predicate(context, editor)) { - return this.delegate.getTerms(context, editor); - } else { - return null; - } - }; - - cls.match = function (token, context, editor) { - if (this.predicate(context, editor)) { - return this.delegate.match(token, context, editor); - } else { - return false; - } - }; -}(ConditionalProxy.prototype)); - - -function GlobalOnlyComponent(name) { - engine.AutocompleteComponent.call(this, name); -} - -GlobalOnlyComponent.prototype = _.create( - engine.AutocompleteComponent.prototype, - { 'constructor': ObjectComponent }); - - -(function (cls) { - - cls.getTerms = function () { - return null; - }; - - cls.match = function (token, context) { - const result = { - next: [] - }; - - // try to link to GLOBAL rules - const globalRules = context.globalComponentResolver(token, false); - if (globalRules) { - result.next.push.apply(result.next, globalRules); - } - - if (result.next.length) { - return result; - } - // just loop back to us - result.next = [this]; - - return result; - - }; -}(GlobalOnlyComponent.prototype)); - - // a list of component that match anything but give auto complete suggestions based on global API entries. export function globalsOnlyAutocompleteComponents() { return [new GlobalOnlyComponent('__global__')]; @@ -412,11 +285,18 @@ export function globalsOnlyAutocompleteComponents() { * @param parametrizedComponentFactories a dict of the following structure * that will be used as a fall back for pattern keys (i.e.: {type} ,resolved without the $s) * { - * TYPE: function (part, parent, endpoint) { - * return new SharedComponent(part, parent) - * } - * } + * TYPE: function (part, parent, endpoint) { + * return new SharedComponent(part, parent) + * } + * } */ -export function compileBodyDescription(endpointId, description, parametrizedComponentFactories) { - return compileDescription(description, new CompilingContext(endpointId, parametrizedComponentFactories)); +export function compileBodyDescription( + endpointId, + description, + parametrizedComponentFactories +) { + return compileDescription( + description, + new CompilingContext(endpointId, parametrizedComponentFactories) + ); } diff --git a/src/core_plugins/console/public/src/autocomplete/components/accept_endpoint_component.js b/src/core_plugins/console/public/src/autocomplete/components/accept_endpoint_component.js new file mode 100644 index 0000000000000..b2d4888b34d6f --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/accept_endpoint_component.js @@ -0,0 +1,43 @@ +/* + * 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'; +import { SharedComponent } from './shared_component'; +export const URL_PATH_END_MARKER = '__url_path_end__'; + +export class AcceptEndpointComponent extends SharedComponent { + constructor(endpoint, parent) { + super(endpoint.id, parent); + this.endpoint = endpoint; + } + match(token, context, editor) { + if (token !== URL_PATH_END_MARKER) { + return null; + } + if (this.endpoint.methods && -1 === _.indexOf(this.endpoint.methods, context.method)) { + return null; + } + const r = super.match(token, context, editor); + r.context_values = r.context_values || {}; + r.context_values.endpoint = this.endpoint; + if (_.isNumber(this.endpoint.priority)) { + r.priority = this.endpoint.priority; + } + return r; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/autocomplete_component.js b/src/core_plugins/console/public/src/autocomplete/components/autocomplete_component.js new file mode 100644 index 0000000000000..06ee1d7d9f2b4 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/autocomplete_component.js @@ -0,0 +1,45 @@ +/* + * 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. + */ + +export class AutocompleteComponent { + constructor(name) { + this.name = name; + } + /** called to get the possible suggestions for tokens, when this object is at the end of + * the resolving chain (and thus can suggest possible continuation paths) + */ + getTerms() { + return []; + } + /* + if the current matcher matches this term, this method should return an object with the following keys + { + context_values: { + values extract from term that should be added to the context + } + next: AutocompleteComponent(s) to use next + priority: optional priority to solve collisions between multiple paths. Min value is used across entire chain + } + */ + match() { + return { + next: this.next, + }; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/conditional_proxy.js b/src/core_plugins/console/public/src/autocomplete/components/conditional_proxy.js new file mode 100644 index 0000000000000..9ee233d54c20a --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/conditional_proxy.js @@ -0,0 +1,43 @@ +/* + * 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 { SharedComponent } from './shared_component'; +export class ConditionalProxy extends SharedComponent { + constructor(predicate, delegate) { + super('__condition'); + this.predicate = predicate; + this.delegate = delegate; + } + + getTerms(context, editor) { + if (this.predicate(context, editor)) { + return this.delegate.getTerms(context, editor); + } else { + return null; + } + } + + match(token, context, editor) { + if (this.predicate(context, editor)) { + return this.delegate.match(token, context, editor); + } else { + return false; + } + } +} \ No newline at end of file diff --git a/src/core_plugins/console/public/src/autocomplete/components/constant_component.js b/src/core_plugins/console/public/src/autocomplete/components/constant_component.js new file mode 100644 index 0000000000000..582ae44084e0c --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/constant_component.js @@ -0,0 +1,50 @@ +/* + * 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'; +import { SharedComponent } from './shared_component'; +export class ConstantComponent extends SharedComponent { + constructor(name, parent, options) { + super(name, parent); + if (_.isString(options)) { + options = [options]; + } + this.options = options || [name]; + } + getTerms() { + return this.options; + } + + addOption(options) { + if (!Array.isArray(options)) { + options = [options]; + } + + [].push.apply(this.options, options); + this.options = _.uniq(this.options); + } + match(token, context, editor) { + if (token !== this.name) { + return null; + } + + return super.match(token, context, editor); + + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/field_autocomplete_component.js b/src/core_plugins/console/public/src/autocomplete/components/field_autocomplete_component.js new file mode 100644 index 0000000000000..b2424bebf1b9d --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/field_autocomplete_component.js @@ -0,0 +1,50 @@ +/* + * 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'; +import mappings from '../../mappings'; +import { ListComponent } from './list_component'; + +function FieldGenerator(context) { + return _.map(mappings.getFields(context.indices, context.types), function (field) { + return { name: field.name, meta: field.type }; + }); +} + +export class FieldAutocompleteComponent extends ListComponent { + constructor(name, parent, multiValued) { + super(name, FieldGenerator, parent, multiValued); + } + validateTokens(tokens) { + if (!this.multiValued && tokens.length > 1) { + return false; + } + + return !_.find(tokens, function (token) { + return token.match(/[^\w.?*]/); + }); + } + + getDefaultTermMeta() { + return 'field'; + } + + getContextKey() { + return 'fields'; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/global_only_component.js b/src/core_plugins/console/public/src/autocomplete/components/global_only_component.js new file mode 100644 index 0000000000000..ee50e3634cc61 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/global_only_component.js @@ -0,0 +1,46 @@ +/* + * 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 { SharedComponent } from './shared_component'; +export class GlobalOnlyComponent extends SharedComponent { + getTerms() { + return null; + } + + match(token, context) { + const result = { + next: [] + }; + + // try to link to GLOBAL rules + const globalRules = context.globalComponentResolver(token, false); + if (globalRules) { + result.next.push.apply(result.next, globalRules); + } + + if (result.next.length) { + return result; + } + // just loop back to us + result.next = [this]; + + return result; + + } +} \ No newline at end of file diff --git a/src/core_plugins/console/public/src/autocomplete/components/id_autocomplete_component.js b/src/core_plugins/console/public/src/autocomplete/components/id_autocomplete_component.js new file mode 100644 index 0000000000000..6fc694b92214f --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/id_autocomplete_component.js @@ -0,0 +1,45 @@ +/* + * 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'; +import { SharedComponent } from './shared_component'; +export class IdAutocompleteComponent extends SharedComponent { + constructor(name, parent, multi) { + super(name, parent); + this.multi_match = multi; + } + match(token, context, editor) { + if (!token) { + return null; + } + if (!this.multi_match && Array.isArray(token)) { + return null; + } + token = Array.isArray(token) ? token : [token]; + if (_.find(token, function (t) { + return t.match(/[\/,]/); + })) { + return null; + } + const r = super.match(token, context, editor); + r.context_values = r.context_values || {}; + r.context_values.id = token; + return r; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/index.js b/src/core_plugins/console/public/src/autocomplete/components/index.js new file mode 100644 index 0000000000000..9103212777efc --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/index.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +export { AutocompleteComponent } from './autocomplete_component'; +export { SharedComponent } from './shared_component'; +export { ConstantComponent } from './constant_component'; +export { ListComponent } from './list_component'; +export { SimpleParamComponent } from './simple_param_component'; +export { ConditionalProxy } from './conditional_proxy'; +export { GlobalOnlyComponent } from './global_only_component'; +export { ObjectComponent } from './object_component'; +export { AcceptEndpointComponent, URL_PATH_END_MARKER } from './accept_endpoint_component'; +export { UrlPatternMatcher } from './url_pattern_matcher'; +export { IndexAutocompleteComponent } from './index_autocomplete_component'; +export { FieldAutocompleteComponent } from './field_autocomplete_component'; +export { TypeAutocompleteComponent } from './type_autocomplete_component'; +export { IdAutocompleteComponent } from './id_autocomplete_component'; diff --git a/src/core_plugins/console/public/src/autocomplete/components/index_autocomplete_component.js b/src/core_plugins/console/public/src/autocomplete/components/index_autocomplete_component.js new file mode 100644 index 0000000000000..ec1cfc60f9064 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/index_autocomplete_component.js @@ -0,0 +1,43 @@ +/* + * 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'; +import mappings from '../../mappings'; +import { ListComponent } from './list_component'; +function nonValidIndexType(token) { + return !(token === '_all' || token[0] !== '_'); +} +export class IndexAutocompleteComponent extends ListComponent { + constructor(name, parent, multiValued) { + super(name, mappings.getIndices, parent, multiValued); + } + validateTokens(tokens) { + if (!this.multiValued && tokens.length > 1) { + return false; + } + return !_.find(tokens, nonValidIndexType); + } + + getDefaultTermMeta() { + return 'index'; + } + + getContextKey() { + return 'indices'; + } +} \ No newline at end of file diff --git a/src/core_plugins/console/public/src/autocomplete/components/list_component.js b/src/core_plugins/console/public/src/autocomplete/components/list_component.js new file mode 100644 index 0000000000000..d023779fac555 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/list_component.js @@ -0,0 +1,94 @@ +/* + * 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'; +import { SharedComponent } from './shared_component'; +/** A component that suggests one of the give options, but accepts anything */ +export class ListComponent extends SharedComponent { + constructor(name, list, parent, multiValued, allowNonValidValues) { + super(name, parent); + this.listGenerator = Array.isArray(list) ? function () { + return list; + } : list; + this.multiValued = _.isUndefined(multiValued) ? true : multiValued; + this.allowNonValidValues = _.isUndefined(allowNonValidValues) ? false : allowNonValidValues; + } + getTerms(context, editor) { + if (!this.multiValued && context.otherTokenValues) { + // already have a value -> no suggestions + return []; + } + let alreadySet = context.otherTokenValues || []; + if (_.isString(alreadySet)) { + alreadySet = [alreadySet]; + } + let ret = _.difference(this.listGenerator(context, editor), alreadySet); + + if (this.getDefaultTermMeta()) { + const meta = this.getDefaultTermMeta(); + ret = _.map(ret, function (term) { + if (_.isString(term)) { + term = { 'name': term }; + } + return _.defaults(term, { meta: meta }); + }); + } + + return ret; + } + + validateTokens(tokens) { + if (!this.multiValued && tokens.length > 1) { + return false; + } + + // verify we have all tokens + const list = this.listGenerator(); + const notFound = _.any(tokens, function (token) { + return list.indexOf(token) === -1; + }); + + if (notFound) { + return false; + } + return true; + } + + getContextKey() { + return this.name; + } + + getDefaultTermMeta() { + return this.name; + } + + match(token, context, editor) { + if (!Array.isArray(token)) { + token = [token]; + } + if (!this.allowNonValidValues && !this.validateTokens(token, context, editor)) { + return null; + } + + const result = super.match(token, context, editor); + result.context_values = result.context_values || {}; + result.context_values[this.getContextKey()] = token; + return result; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/object_component.js b/src/core_plugins/console/public/src/autocomplete/components/object_component.js new file mode 100644 index 0000000000000..4db392e60ff83 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/object_component.js @@ -0,0 +1,74 @@ +/* + * 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'; +import { SharedComponent } from '.'; +/** + * @param constants list of components that represent constant keys + * @param patternsAndWildCards list of components that represent patterns and should be matched only if + * there is no constant matches + */ +export class ObjectComponent extends SharedComponent { + constructor(name, constants, patternsAndWildCards) { + super(name); + this.constants = constants; + this.patternsAndWildCards = patternsAndWildCards; + } + getTerms(context, editor) { + const options = []; + _.each(this.constants, function (component) { + options.push.apply(options, component.getTerms(context, editor)); + }); + _.each(this.patternsAndWildCards, function (component) { + options.push.apply(options, component.getTerms(context, editor)); + }); + return options; + } + + match(token, context, editor) { + const result = { + next: [] + }; + _.each(this.constants, function (component) { + const componentResult = component.match(token, context, editor); + if (componentResult && componentResult.next) { + result.next.push.apply(result.next, componentResult.next); + } + }); + + // try to link to GLOBAL rules + const globalRules = context.globalComponentResolver(token, false); + if (globalRules) { + result.next.push.apply(result.next, globalRules); + } + + if (result.next.length) { + return result; + } + _.each(this.patternsAndWildCards, function (component) { + const componentResult = component.match(token, context, editor); + if (componentResult && componentResult.next) { + result.next.push.apply(result.next, componentResult.next); + } + }); + + return result; + + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/shared_component.js b/src/core_plugins/console/public/src/autocomplete/components/shared_component.js new file mode 100644 index 0000000000000..f996a5de6672f --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/shared_component.js @@ -0,0 +1,42 @@ +/* + * 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'; +import { AutocompleteComponent } from './autocomplete_component'; +export class SharedComponent extends AutocompleteComponent { + constructor(name, parent) { + super(name); + this._nextDict = {}; + if (parent) { + parent.addComponent(this); + } + // for debugging purposes + this._parent = parent; + } + /* return the first component with a given name */ + getComponent(name) { + return (this._nextDict[name] || [undefined])[0]; + } + + addComponent(component) { + const current = this._nextDict[component.name] || []; + current.push(component); + this._nextDict[component.name] = current; + this.next = [].concat.apply([], _.values(this._nextDict)); + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/simple_param_component.js b/src/core_plugins/console/public/src/autocomplete/components/simple_param_component.js new file mode 100644 index 0000000000000..6a315c45066e6 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/simple_param_component.js @@ -0,0 +1,32 @@ +/* + * 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 { SharedComponent } from './shared_component'; +export class SimpleParamComponent extends SharedComponent { + constructor(name, parent) { + super(name, parent); + } + match(token, context, editor) { + const result = super.match(token, context, editor); + result.context_values = result.context_values || {}; + result.context_values[this.name] = token; + return result; + } +} + diff --git a/src/core_plugins/console/public/src/autocomplete/components/type_autocomplete_component.js b/src/core_plugins/console/public/src/autocomplete/components/type_autocomplete_component.js new file mode 100644 index 0000000000000..4f1c85213b689 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/type_autocomplete_component.js @@ -0,0 +1,47 @@ +/* + * 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'; +import { ListComponent } from './list_component'; +import mappings from '../../mappings'; +function TypeGenerator(context) { + return mappings.getTypes(context.indices); +} +function nonValidIndexType(token) { + return !(token === '_all' || token[0] !== '_'); +} +export class TypeAutocompleteComponent extends ListComponent { + constructor(name, parent, multiValued) { + super(name, TypeGenerator, parent, multiValued); + } + validateTokens(tokens) { + if (!this.multiValued && tokens.length > 1) { + return false; + } + + return !_.find(tokens, nonValidIndexType); + } + + getDefaultTermMeta() { + return 'type'; + } + + getContextKey() { + return 'types'; + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/components/url_pattern_matcher.js b/src/core_plugins/console/public/src/autocomplete/components/url_pattern_matcher.js new file mode 100644 index 0000000000000..32f8b95040f81 --- /dev/null +++ b/src/core_plugins/console/public/src/autocomplete/components/url_pattern_matcher.js @@ -0,0 +1,136 @@ +/* + * 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'; +import { + SharedComponent, + ConstantComponent, + AcceptEndpointComponent, + ListComponent, + SimpleParamComponent, +} from '.'; + +/** + * @param parametrizedComponentFactories a dict of the following structure + * that will be used as a fall back for pattern parameters (i.e.: {indices}) + * { + * indices: function (part, parent) { + * return new SharedComponent(part, parent) + * } + * } + * @constructor + */ +export class UrlPatternMatcher { + // This is not really a component, just a handy container to make iteration logic simpler + constructor(parametrizedComponentFactories) { + // We'll group endpoints by the methods which are attached to them, + //to avoid suggesting endpoints that are incompatible with the + //method that the user has entered. + ['HEAD', 'GET', 'PUT', 'POST', 'DELETE'].forEach((method) => { + this[method] = { + rootComponent: new SharedComponent('ROOT'), + parametrizedComponentFactories: parametrizedComponentFactories || {} + }; + }); + } + addEndpoint(pattern, endpoint) { + endpoint.methods.forEach((method) => { + let c; + let activeComponent = this[method].rootComponent; + const endpointComponents = endpoint.url_components || {}; + const partList = pattern.split('/'); + _.each( + partList, + function (part, partIndex) { + if (part.search(/^{.+}$/) >= 0) { + part = part.substr(1, part.length - 2); + if (activeComponent.getComponent(part)) { + // we already have something for this, reuse + activeComponent = activeComponent.getComponent(part); + return; + } + // a new path, resolve. + + if ((c = endpointComponents[part])) { + // endpoint specific. Support list + if (Array.isArray(c)) { + c = new ListComponent(part, c, activeComponent); + } else if (_.isObject(c) && c.type === 'list') { + c = new ListComponent( + part, + c.list, + activeComponent, + c.multiValued, + c.allow_non_valid + ); + } else { + console.warn( + 'incorrectly configured url component ', + part, + ' in endpoint', + endpoint + ); + c = new SharedComponent(part); + } + } else if ((c = this[method].parametrizedComponentFactories[part])) { + // c is a f + c = c(part, activeComponent); + } else { + // just accept whatever with not suggestions + c = new SimpleParamComponent(part, activeComponent); + } + + activeComponent = c; + } else { + // not pattern + let lookAhead = part; + let s; + + for (partIndex++; partIndex < partList.length; partIndex++) { + s = partList[partIndex]; + if (s.indexOf('{') >= 0) { + break; + } + lookAhead += '/' + s; + } + + if (activeComponent.getComponent(part)) { + // we already have something for this, reuse + activeComponent = activeComponent.getComponent(part); + activeComponent.addOption(lookAhead); + } else { + c = new ConstantComponent(part, activeComponent, lookAhead); + activeComponent = c; + } + } + }, + this + ); + // mark end of endpoint path + new AcceptEndpointComponent(endpoint, activeComponent); + }); + } + + getTopLevelComponents = function (method) { + const methodRoot = this[method]; + if (!methodRoot) { + return []; + } + return methodRoot.rootComponent.next; + }; +} diff --git a/src/core_plugins/console/public/src/autocomplete/engine.js b/src/core_plugins/console/public/src/autocomplete/engine.js index 23f0d990116c8..23a27d8eeef32 100644 --- a/src/core_plugins/console/public/src/autocomplete/engine.js +++ b/src/core_plugins/console/public/src/autocomplete/engine.js @@ -19,205 +19,10 @@ const _ = require('lodash'); -export function AutocompleteComponent(name) { - this.name = name; -} - -/** called to get the possible suggestions for tokens, when this object is at the end of - * the resolving chain (and thus can suggest possible continuation paths) - */ -AutocompleteComponent.prototype.getTerms = function () { - return []; -}; - -/* - if the current matcher matches this term, this method should return an object with the following keys - { - context_values: { - values extract from term that should be added to the context - } - next: AutocompleteComponent(s) to use next - priority: optional priority to solve collisions between multiple paths. Min value is used across entire chain - } - */ -AutocompleteComponent.prototype.match = function () { - return { - next: this.next - }; -}; - -function SharedComponent(name, parent) { - AutocompleteComponent.call(this, name); - this._nextDict = {}; - if (parent) { - parent.addComponent(this); - } - // for debugging purposes - this._parent = parent; -} - -SharedComponent.prototype = _.create( - AutocompleteComponent.prototype, - { 'constructor': SharedComponent }); - -(function (cls) { - /* return the first component with a given name */ - cls.getComponent = function (name) { - return (this._nextDict[name] || [undefined])[0]; - }; - - cls.addComponent = function (component) { - const current = this._nextDict[component.name] || []; - current.push(component); - this._nextDict[component.name] = current; - this.next = [].concat.apply([], _.values(this._nextDict)); - }; - -}(SharedComponent.prototype)); - -/** A component that suggests one of the give options, but accepts anything */ -function ListComponent(name, list, parent, multiValued, allowNonValidValues) { - SharedComponent.call(this, name, parent); - this.listGenerator = Array.isArray(list) ? function () { - return list; - } : list; - this.multiValued = _.isUndefined(multiValued) ? true : multiValued; - this.allowNonValidValues = _.isUndefined(allowNonValidValues) ? false : allowNonValidValues; -} - -ListComponent.prototype = _.create(SharedComponent.prototype, { 'constructor': ListComponent }); - - -(function (cls) { - cls.getTerms = function (context, editor) { - if (!this.multiValued && context.otherTokenValues) { - // already have a value -> no suggestions - return []; - } - let alreadySet = context.otherTokenValues || []; - if (_.isString(alreadySet)) { - alreadySet = [alreadySet]; - } - let ret = _.difference(this.listGenerator(context, editor), alreadySet); - - if (this.getDefaultTermMeta()) { - const meta = this.getDefaultTermMeta(); - ret = _.map(ret, function (term) { - if (_.isString(term)) { - term = { 'name': term }; - } - return _.defaults(term, { meta: meta }); - }); - } - - return ret; - }; - - cls.validateTokens = function (tokens) { - if (!this.multiValued && tokens.length > 1) { - return false; - } - - // verify we have all tokens - const list = this.listGenerator(); - const notFound = _.any(tokens, function (token) { - return list.indexOf(token) === -1; - }); - - if (notFound) { - return false; - } - return true; - }; - - cls.getContextKey = function () { - return this.name; - }; - - cls.getDefaultTermMeta = function () { - return this.name; - }; - - cls.match = function (token, context, editor) { - if (!Array.isArray(token)) { - token = [token]; - } - if (!this.allowNonValidValues && !this.validateTokens(token, context, editor)) { - return null; - } - - const result = Object.getPrototypeOf(cls).match.call(this, token, context, editor); - result.context_values = result.context_values || {}; - result.context_values[this.getContextKey()] = token; - return result; - }; -}(ListComponent.prototype)); - -function SimpleParamComponent(name, parent) { - SharedComponent.call(this, name, parent); -} - -SimpleParamComponent.prototype = _.create(SharedComponent.prototype, { 'constructor': SimpleParamComponent }); - -(function (cls) { - cls.match = function (token, context, editor) { - const result = Object.getPrototypeOf(cls).match.call(this, token, context, editor); - result.context_values = result.context_values || {}; - result.context_values[this.name] = token; - return result; - }; - -}(SimpleParamComponent.prototype)); - -function ConstantComponent(name, parent, options) { - SharedComponent.call(this, name, parent); - if (_.isString(options)) { - options = [options]; - } - this.options = options || [name]; -} - -ConstantComponent.prototype = _.create(SharedComponent.prototype, { 'constructor': ConstantComponent }); - -export { SharedComponent, ListComponent, SimpleParamComponent, ConstantComponent }; - -(function (cls) { - cls.getTerms = function () { - return this.options; - }; - - cls.addOption = function (options) { - if (!Array.isArray(options)) { - options = [options]; - } - - [].push.apply(this.options, options); - this.options = _.uniq(this.options); - }; - cls.match = function (token, context, editor) { - if (token !== this.name) { - return null; - } - - return Object.getPrototypeOf(cls).match.call(this, token, context, editor); - - }; -}(ConstantComponent.prototype)); - export function wrapComponentWithDefaults(component, defaults) { - function Wrapper() { - - } - - Wrapper.prototype = {}; - for (const key in component) { - if (_.isFunction(component[key])) { - Wrapper.prototype[key] = _.bindKey(component, key); - } - } - - Wrapper.prototype.getTerms = function (context, editor) { - let result = component.getTerms(context, editor); + const originalGetTerms = component.getTerms; + component.getTerms = function (context, editor) { + let result = originalGetTerms.call(component, context, editor); if (!result) { return result; } @@ -229,7 +34,7 @@ export function wrapComponentWithDefaults(component, defaults) { }, this); return result; }; - return new Wrapper(); + return component; } const tracer = function () { @@ -254,7 +59,7 @@ function passThroughContext(context, extensionList) { return result; } -function WalkingState(parentName, components, contextExtensionList, depth, priority) { +export function WalkingState(parentName, components, contextExtensionList, depth, priority) { this.parentName = parentName; this.components = components; this.contextExtensionList = contextExtensionList; @@ -263,7 +68,7 @@ function WalkingState(parentName, components, contextExtensionList, depth, prior } -function walkTokenPath(tokenPath, walkingStates, context, editor) { +export function walkTokenPath(tokenPath, walkingStates, context, editor) { if (!tokenPath || tokenPath.length === 0) { return walkingStates; } @@ -321,12 +126,6 @@ function walkTokenPath(tokenPath, walkingStates, context, editor) { return walkTokenPath(tokenPath.slice(1), nextWalkingStates, context, editor); } -export function resolvePathToComponents(tokenPath, context, editor, components) { - const walkStates = walkTokenPath(tokenPath, [new WalkingState('ROOT', components, [])], context, editor); - const result = [].concat.apply([], _.pluck(walkStates, 'components')); - return result; -} - export function populateContext(tokenPath, context, editor, includeAutoComplete, components) { let walkStates = walkTokenPath(tokenPath, [new WalkingState('ROOT', components, [])], context, editor); diff --git a/src/core_plugins/console/public/src/autocomplete/url_params.js b/src/core_plugins/console/public/src/autocomplete/url_params.js index 30edd380d98c1..78b1701582df9 100644 --- a/src/core_plugins/console/public/src/autocomplete/url_params.js +++ b/src/core_plugins/console/public/src/autocomplete/url_params.js @@ -18,17 +18,14 @@ */ const _ = require('lodash'); -const engine = require('./engine'); +import { ConstantComponent, ListComponent, SharedComponent } from './components'; -export function ParamComponent(name, parent, description) { - engine.ConstantComponent.call(this, name, parent); - this.description = description; -} - -ParamComponent.prototype = _.create(engine.ConstantComponent.prototype, { 'constructor': ParamComponent }); - -(function (cls) { - cls.getTerms = function () { +export class ParamComponent extends ConstantComponent { + constructor(name, parent, description) { + super(name, parent); + this.description = description; + } + getTerms() { const t = { name: this.name }; if (this.description === '__flag__') { t.meta = 'flag'; @@ -38,38 +35,33 @@ ParamComponent.prototype = _.create(engine.ConstantComponent.prototype, { 'const t.insertValue = this.name + '='; } return [t]; - }; - -}(ParamComponent.prototype)); - -export function UrlParams(description, defaults) { - // This is not really a component, just a handy container to make iteration logic simpler - this.rootComponent = new engine.SharedComponent('ROOT'); - if (_.isUndefined(defaults)) { - defaults = { - 'pretty': '__flag__', - 'format': ['json', 'yaml'], - 'filter_path': '', - }; } - description = _.clone(description || {}); - _.defaults(description, defaults); - _.each(description, function (pDescription, param) { - const component = new ParamComponent(param, this.rootComponent, pDescription); - if (Array.isArray(pDescription)) { - new engine.ListComponent(param, pDescription, component); - } - else if (pDescription === '__flag__') { - new engine.ListComponent(param, ['true', 'false'], component); - } - }, this); - } -(function (cls) { - - cls.getTopLevelComponents = function () { +export class UrlParams { + constructor(description, defaults) { + // This is not really a component, just a handy container to make iteration logic simpler + this.rootComponent = new SharedComponent('ROOT'); + if (_.isUndefined(defaults)) { + defaults = { + 'pretty': '__flag__', + 'format': ['json', 'yaml'], + 'filter_path': '', + }; + } + description = _.clone(description || {}); + _.defaults(description, defaults); + _.each(description, function (pDescription, param) { + const component = new ParamComponent(param, this.rootComponent, pDescription); + if (Array.isArray(pDescription)) { + new ListComponent(param, pDescription, component); + } + else if (pDescription === '__flag__') { + new ListComponent(param, ['true', 'false'], component); + } + }, this); + } + getTopLevelComponents() { return this.rootComponent.next; - }; - -}(UrlParams.prototype)); + } +} diff --git a/src/core_plugins/console/public/src/autocomplete/url_pattern_matcher.js b/src/core_plugins/console/public/src/autocomplete/url_pattern_matcher.js deleted file mode 100644 index 82d8decf23b7d..0000000000000 --- a/src/core_plugins/console/public/src/autocomplete/url_pattern_matcher.js +++ /dev/null @@ -1,141 +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. - */ - -const _ = require('lodash'); -const engine = require('./engine'); - -export const URL_PATH_END_MARKER = '__url_path_end__'; - -function AcceptEndpointComponent(endpoint, parent) { - engine.SharedComponent.call(this, endpoint.id, parent); - this.endpoint = endpoint; -} - -AcceptEndpointComponent.prototype = _.create(engine.SharedComponent.prototype, { 'constructor': AcceptEndpointComponent }); - -(function (cls) { - - cls.match = function (token, context, editor) { - if (token !== URL_PATH_END_MARKER) { - return null; - } - if (this.endpoint.methods && -1 === _.indexOf(this.endpoint.methods, context.method)) { - return null; - } - const r = Object.getPrototypeOf(cls).match.call(this, token, context, editor); - r.context_values = r.context_values || {}; - r.context_values.endpoint = this.endpoint; - if (_.isNumber(this.endpoint.priority)) { - r.priority = this.endpoint.priority; - } - return r; - }; -}(AcceptEndpointComponent.prototype)); - - -/** - * @param parametrizedComponentFactories a dict of the following structure - * that will be used as a fall back for pattern parameters (i.e.: {indices}) - * { - * indices: function (part, parent) { - * return new SharedComponent(part, parent) - * } - * } - * @constructor - */ -export function UrlPatternMatcher(parametrizedComponentFactories) { - // This is not really a component, just a handy container to make iteration logic simpler - this.rootComponent = new engine.SharedComponent('ROOT'); - this.parametrizedComponentFactories = parametrizedComponentFactories || {}; -} - -(function (cls) { - cls.addEndpoint = function (pattern, endpoint) { - let c; - let activeComponent = this.rootComponent; - const endpointComponents = endpoint.url_components || {}; - const partList = pattern.split('/'); - _.each(partList, function (part, partIndex) { - if (part.search(/^{.+}$/) >= 0) { - part = part.substr(1, part.length - 2); - if (activeComponent.getComponent(part)) { - // we already have something for this, reuse - activeComponent = activeComponent.getComponent(part); - return; - } - // a new path, resolve. - - if ((c = endpointComponents[part])) { - // endpoint specific. Support list - if (Array.isArray(c)) { - c = new engine.ListComponent(part, c, activeComponent); - } - else if (_.isObject(c) && c.type === 'list') { - c = new engine.ListComponent(part, c.list, activeComponent, c.multiValued, c.allow_non_valid); - } - else { - console.warn('incorrectly configured url component ', part, ' in endpoint', endpoint); - c = new engine.SharedComponent(part); - } - } - else if ((c = this.parametrizedComponentFactories[part])) { - // c is a f - c = c(part, activeComponent); - } - else { - // just accept whatever with not suggestions - c = new engine.SimpleParamComponent(part, activeComponent); - } - - activeComponent = c; - } - else { - // not pattern - let lookAhead = part; - let s; - - for (partIndex++; partIndex < partList.length; partIndex++) { - s = partList[partIndex]; - if (s.indexOf('{') >= 0) { - break; - } - lookAhead += '/' + s; - - } - - if (activeComponent.getComponent(part)) { - // we already have something for this, reuse - activeComponent = activeComponent.getComponent(part); - activeComponent.addOption(lookAhead); - } - else { - c = new engine.ConstantComponent(part, activeComponent, lookAhead); - activeComponent = c; - } - } - }, this); - // mark end of endpoint path - new AcceptEndpointComponent(endpoint, activeComponent); - }; - - cls.getTopLevelComponents = function () { - return this.rootComponent.next; - }; - -}(UrlPatternMatcher.prototype)); diff --git a/src/core_plugins/console/public/src/kb.js b/src/core_plugins/console/public/src/kb.js index feb3d69ed0b07..4edafb2bf6037 100644 --- a/src/core_plugins/console/public/src/kb.js +++ b/src/core_plugins/console/public/src/kb.js @@ -16,173 +16,66 @@ * specific language governing permissions and limitations * under the License. */ +import { + TypeAutocompleteComponent, + IdAutocompleteComponent, + IndexAutocompleteComponent, + FieldAutocompleteComponent, + ListComponent +} from './autocomplete/components'; -const $ = require('jquery'); -const _ = require('lodash'); -const mappings = require('./mappings'); -const Api = require('./kb/api'); -const autocompleteEngine = require('./autocomplete/engine'); - -let ACTIVE_API = new Api(); - -function nonValidIndexType(token) { - return !(token === '_all' || token[0] !== '_'); -} - -function IndexAutocompleteComponent(name, parent, multiValued) { - autocompleteEngine.ListComponent.call(this, name, mappings.getIndices, parent, multiValued); -} - -IndexAutocompleteComponent.prototype = _.create( - autocompleteEngine.ListComponent.prototype, - { 'constructor': IndexAutocompleteComponent }); - -(function (cls) { - cls.validateTokens = function (tokens) { - if (!this.multiValued && tokens.length > 1) { - return false; - } - return !_.find(tokens, nonValidIndexType); - }; - - cls.getDefaultTermMeta = function () { - return 'index'; - }; - - cls.getContextKey = function () { - return 'indices'; - }; -}(IndexAutocompleteComponent.prototype)); +import $ from 'jquery'; +import _ from 'lodash'; +import Api from './kb/api'; -function TypeGenerator(context) { - return mappings.getTypes(context.indices); -} - -function TypeAutocompleteComponent(name, parent, multiValued) { - autocompleteEngine.ListComponent.call(this, name, TypeGenerator, parent, multiValued); -} - -TypeAutocompleteComponent.prototype = _.create( - autocompleteEngine.ListComponent.prototype, - { 'constructor': TypeAutocompleteComponent }); - -(function (cls) { - cls.validateTokens = function (tokens) { - if (!this.multiValued && tokens.length > 1) { - return false; - } - - return !_.find(tokens, nonValidIndexType); - }; - - cls.getDefaultTermMeta = function () { - return 'type'; - }; - - cls.getContextKey = function () { - return 'types'; - }; -}(TypeAutocompleteComponent.prototype)); - -function FieldGenerator(context) { - return _.map(mappings.getFields(context.indices, context.types), function (field) { - return { name: field.name, meta: field.type }; - }); -} - -function FieldAutocompleteComponent(name, parent, multiValued) { - autocompleteEngine.ListComponent.call(this, name, FieldGenerator, parent, multiValued); -} - -FieldAutocompleteComponent.prototype = _.create( - autocompleteEngine.ListComponent.prototype, - { 'constructor': FieldAutocompleteComponent }); - -(function (cls) { - cls.validateTokens = function (tokens) { - if (!this.multiValued && tokens.length > 1) { - return false; - } - - return !_.find(tokens, function (token) { - return token.match(/[^\w.?*]/); - }); - }; - - cls.getDefaultTermMeta = function () { - return 'field'; - }; - - cls.getContextKey = function () { - return 'fields'; - }; -}(FieldAutocompleteComponent.prototype)); - - -function IdAutocompleteComponent(name, parent, multi) { - autocompleteEngine.SharedComponent.call(this, name, parent); - this.multi_match = multi; -} - -IdAutocompleteComponent.prototype = _.create( - autocompleteEngine.SharedComponent.prototype, - { 'constructor': IdAutocompleteComponent }); - -(function (cls) { - cls.match = function (token, context, editor) { - if (!token) { - return null; - } - if (!this.multi_match && Array.isArray(token)) { - return null; - } - token = Array.isArray(token) ? token : [token]; - if (_.find(token, function (t) { - return t.match(/[\/,]/); - })) { - return null; - } - const r = Object.getPrototypeOf(cls).match.call(this, token, context, editor); - r.context_values = r.context_values || {}; - r.context_values.id = token; - return r; - }; -}(IdAutocompleteComponent.prototype)); +let ACTIVE_API = new Api(); +const isNotAnIndexName = name => name[0] === '_' && name !== '_all'; const parametrizedComponentFactories = { - - 'index': function (name, parent) { + index: function (name, parent) { + if (isNotAnIndexName(name)) return; return new IndexAutocompleteComponent(name, parent, false); }, - 'indices': function (name, parent) { + indices: function (name, parent) { + if (isNotAnIndexName(name)) return; return new IndexAutocompleteComponent(name, parent, true); }, - 'type': function (name, parent) { + type: function (name, parent) { return new TypeAutocompleteComponent(name, parent, false); }, - 'types': function (name, parent) { + types: function (name, parent) { return new TypeAutocompleteComponent(name, parent, true); }, - 'id': function (name, parent) { + id: function (name, parent) { return new IdAutocompleteComponent(name, parent); }, - 'ids': function (name, parent) { + ids: function (name, parent) { return new IdAutocompleteComponent(name, parent, true); }, - 'fields': function (name, parent) { + fields: function (name, parent) { return new FieldAutocompleteComponent(name, parent, true); }, - 'field': function (name, parent) { + field: function (name, parent) { return new FieldAutocompleteComponent(name, parent, false); }, - 'nodes': function (name, parent) { - return new autocompleteEngine.ListComponent(name, ['_local', '_master', 'data:true', 'data:false', - 'master:true', 'master:false'], parent); + nodes: function (name, parent) { + return new ListComponent( + name, + [ + '_local', + '_master', + 'data:true', + 'data:false', + 'master:true', + 'master:false', + ], + parent + ); + }, + node: function (name, parent) { + return new ListComponent(name, [], parent, false); }, - 'node': function (name, parent) { - return new autocompleteEngine.ListComponent(name, [], parent, false); - } }; export function getUnmatchedEndpointComponents() { @@ -201,18 +94,27 @@ export function getEndpointBodyCompleteComponents(endpoint) { return desc.bodyAutocompleteRootComponents; } -export function getTopLevelUrlCompleteComponents() { - return ACTIVE_API.getTopLevelUrlCompleteComponents(); +export function getTopLevelUrlCompleteComponents(method) { + return ACTIVE_API.getTopLevelUrlCompleteComponents(method); } export function getGlobalAutocompleteComponents(term, throwOnMissing) { return ACTIVE_API.getGlobalAutocompleteComponents(term, throwOnMissing); } -function loadApisFromJson(json, urlParametrizedComponentFactories, bodyParametrizedComponentFactories) { - urlParametrizedComponentFactories = urlParametrizedComponentFactories || parametrizedComponentFactories; - bodyParametrizedComponentFactories = bodyParametrizedComponentFactories || urlParametrizedComponentFactories; - const api = new Api(urlParametrizedComponentFactories, bodyParametrizedComponentFactories); +function loadApisFromJson( + json, + urlParametrizedComponentFactories, + bodyParametrizedComponentFactories +) { + urlParametrizedComponentFactories = + urlParametrizedComponentFactories || parametrizedComponentFactories; + bodyParametrizedComponentFactories = + bodyParametrizedComponentFactories || urlParametrizedComponentFactories; + const api = new Api( + urlParametrizedComponentFactories, + bodyParametrizedComponentFactories + ); const names = []; _.each(json, function (apiJson, name) { names.unshift(name); @@ -230,18 +132,21 @@ function loadApisFromJson(json, urlParametrizedComponentFactories, bodyParametri export function setActiveApi(api) { if (_.isString(api)) { $.ajax({ - url: '../api/console/api_server?sense_version=' + encodeURIComponent('@@SENSE_VERSION') + '&apis=' + encodeURIComponent(api), + url: + '../api/console/api_server?sense_version=' + + encodeURIComponent('@@SENSE_VERSION') + + '&apis=' + + encodeURIComponent(api), dataType: 'json', // disable automatic guessing - } - ).then( + }).then( function (data) { setActiveApi(loadApisFromJson(data)); }, function (jqXHR) { console.log('failed to load API \'' + api + '\': ' + jqXHR.responseText); - }); + } + ); return; - } console.log('setting active api to [' + api.name + ']'); @@ -251,5 +156,5 @@ export function setActiveApi(api) { setActiveApi('es_6_0'); export const _test = { - loadApisFromJson: loadApisFromJson + loadApisFromJson: loadApisFromJson, }; diff --git a/src/core_plugins/console/public/src/kb/api.js b/src/core_plugins/console/public/src/kb/api.js index 2c1824732f900..2c5becb79301c 100644 --- a/src/core_plugins/console/public/src/kb/api.js +++ b/src/core_plugins/console/public/src/kb/api.js @@ -18,7 +18,7 @@ */ const _ = require('lodash'); -import { UrlPatternMatcher } from '../autocomplete/url_pattern_matcher'; +import { UrlPatternMatcher } from '../autocomplete/components'; import { UrlParams } from '../autocomplete/url_params'; import { globalsOnlyAutocompleteComponents, compileBodyDescription } from '../autocomplete/body_completer'; @@ -78,8 +78,8 @@ function Api(urlParametrizedComponentFactories, bodyParametrizedComponentFactori }; - cls.getTopLevelUrlCompleteComponents = function () { - return this.urlPatternMatcher.getTopLevelComponents(); + cls.getTopLevelUrlCompleteComponents = function (method) { + return this.urlPatternMatcher.getTopLevelComponents(method); }; cls.getUnmatchedEndpointComponents = function () { diff --git a/src/core_plugins/console/public/tests/src/integration.test.js b/src/core_plugins/console/public/tests/src/integration.test.js index e1bf4c01fde28..126c01a9e14d8 100644 --- a/src/core_plugins/console/public/tests/src/integration.test.js +++ b/src/core_plugins/console/public/tests/src/integration.test.js @@ -50,7 +50,6 @@ describe('Integration', () => { }); function processContextTest(data, mapping, kbSchemes, requestLine, testToRun) { - test(testToRun.name, async function (done) { let rowOffset = 0; // add one for the extra method line let editorValue = data; @@ -70,19 +69,18 @@ describe('Integration', () => { const json = {}; json[test.name] = kbSchemes || {}; const testApi = kb._test.loadApisFromJson(json); - //if (kbSchemes) { + if (kbSchemes) { // if (kbSchemes.globals) { // $.each(kbSchemes.globals, function (parent, rules) { // testApi.addGlobalAutocompleteRules(parent, rules); // }); // } - // if (kbSchemes.endpoints) { - // $.each(kbSchemes.endpoints, function (endpoint, scheme) { - // _.defaults(scheme, {methods: null}); // disable method testing unless specified in test - // testApi.addEndpointDescription(endpoint, scheme); - // }); - // } - //} + if (kbSchemes.endpoints) { + $.each(kbSchemes.endpoints, function (endpoint, scheme) { + testApi.addEndpointDescription(endpoint, scheme); + }); + } + } kb.setActiveApi(testApi); const { cursor } = testToRun; const { row, column } = cursor; @@ -1021,16 +1019,19 @@ describe('Integration', () => { search_type: ['count', 'query_then_fetch'], scroll: '10m', }, + methods: ['GET'], data_autocomplete_rules: {}, }, '_cluster/stats': { patterns: ['_cluster/stats'], indices_mode: 'none', data_autocomplete_rules: {}, + methods: ['GET'], }, '_cluster/nodes/stats': { patterns: ['_cluster/nodes/stats'], data_autocomplete_rules: {}, + methods: ['GET'], }, }, }; @@ -1131,7 +1132,7 @@ describe('Integration', () => { contextTests(null, MAPPING, CLUSTER_KB, 'GET cl', [ { - name: 'Endpoints by subpart', + name: 'Endpoints by subpart GET', cursor: { row: 0, column: 6 }, autoCompleteSet: [ { name: '_cluster/nodes/stats', meta: 'endpoint' }, @@ -1143,20 +1144,15 @@ describe('Integration', () => { prefixToAdd: '', suffixToAdd: '', initialValue: 'cl', + method: 'GET' }, ]); contextTests(null, MAPPING, CLUSTER_KB, 'POST cl', [ { - name: 'Endpoints by subpart', + name: 'Endpoints by subpart POST', cursor: { row: 0, column: 7 }, - autoCompleteSet: [ - { name: '_cluster/nodes/stats', meta: 'endpoint' }, - { name: '_cluster/stats', meta: 'endpoint' }, - { name: '_search', meta: 'endpoint' }, - { name: 'index1', meta: 'index' }, - { name: 'index2', meta: 'index' }, - ], + no_context: true, prefixToAdd: '', suffixToAdd: '', initialValue: 'cl', diff --git a/src/core_plugins/console/public/tests/src/kb.test.js b/src/core_plugins/console/public/tests/src/kb.test.js index c13b0ea3df1ba..1618573f911ed 100644 --- a/src/core_plugins/console/public/tests/src/kb.test.js +++ b/src/core_plugins/console/public/tests/src/kb.test.js @@ -78,7 +78,7 @@ describe('Knowledge base', () => { context, null, expectedContext.autoCompleteSet, - kb.getTopLevelUrlCompleteComponents() + kb.getTopLevelUrlCompleteComponents('GET') ); // override context to just check on id diff --git a/src/core_plugins/console/public/tests/src/url_autocomplete.test.js b/src/core_plugins/console/public/tests/src/url_autocomplete.test.js index 174f939f77d80..bbb9f3e6ab98a 100644 --- a/src/core_plugins/console/public/tests/src/url_autocomplete.test.js +++ b/src/core_plugins/console/public/tests/src/url_autocomplete.test.js @@ -24,8 +24,10 @@ const _ = require('lodash'); import { URL_PATH_END_MARKER, UrlPatternMatcher, -} from '../../src/autocomplete/url_pattern_matcher'; -import { populateContext, ListComponent } from '../../src/autocomplete/engine'; + ListComponent +} from '../../src/autocomplete/components'; + +import { populateContext } from '../../src/autocomplete/engine'; describe('Url autocomplete', () => { function patternsTest( @@ -84,7 +86,7 @@ describe('Url autocomplete', () => { context, null, expectedContext.autoCompleteSet, - patternMatcher.getTopLevelComponents() + patternMatcher.getTopLevelComponents(context.method) ); // override context to just check on id @@ -111,17 +113,19 @@ describe('Url autocomplete', () => { const endpoints = { '1': { patterns: ['a/b'], + methods: ['GET'] }, }; patternsTest('simple single path - completion', endpoints, 'a/b$', { endpoint: '1', + method: 'GET' }); patternsTest( 'simple single path - completion, with auto complete', endpoints, 'a/b', - { autoCompleteSet: [] } + { method: 'GET', autoCompleteSet: [] } ); patternsTest( @@ -135,14 +139,14 @@ describe('Url autocomplete', () => { 'simple single path - partial, with auto complete', endpoints, 'a', - { autoCompleteSet: ['b'] } + { method: 'GET', autoCompleteSet: ['b'] } ); patternsTest( 'simple single path - partial, with auto complete', endpoints, [], - { autoCompleteSet: ['a/b'] } + { method: 'GET', autoCompleteSet: ['a/b'] } ); patternsTest('simple single path - different path', endpoints, 'a/c', {}); @@ -152,56 +156,61 @@ describe('Url autocomplete', () => { const endpoints = { '1': { patterns: ['a/b', 'a/b/{p}'], + methods: ['GET'] }, '2': { patterns: ['a/c'], + methods: ['GET'] }, }; patternsTest('shared path - completion 1', endpoints, 'a/b$', { endpoint: '1', + method: 'GET' }); patternsTest('shared path - completion 2', endpoints, 'a/c$', { endpoint: '2', + method: 'GET' }); patternsTest( 'shared path - completion 1 with param', endpoints, 'a/b/v$', - { endpoint: '1', p: 'v' } + { method: 'GET', endpoint: '1', p: 'v' } ); patternsTest('shared path - partial, with auto complete', endpoints, 'a', { autoCompleteSet: ['b', 'c'], + method: 'GET', }); patternsTest( 'shared path - partial, with auto complete of param, no options', endpoints, 'a/b', - { autoCompleteSet: [] } + { method: 'GET', autoCompleteSet: [] } ); patternsTest( 'shared path - partial, without auto complete', endpoints, 'a', - {} + { method: 'GET', } ); patternsTest( 'shared path - different path - with auto complete', endpoints, 'a/e', - { autoCompleteSet: [] } + { method: 'GET', autoCompleteSet: [] } ); patternsTest( 'shared path - different path - without auto complete', endpoints, 'a/e', - {} + { method: 'GET', } ); }()); @@ -212,51 +221,57 @@ describe('Url autocomplete', () => { url_components: { p: ['a', 'b'], }, + methods: [ 'GET' ] }, '2': { patterns: ['a/c'], + methods: [ 'GET' ] }, }; patternsTest('option testing - completion 1', endpoints, 'a/a$', { + method: 'GET', endpoint: '1', p: ['a'], }); patternsTest('option testing - completion 2', endpoints, 'a/b$', { + method: 'GET', endpoint: '1', p: ['b'], }); patternsTest('option testing - completion 3', endpoints, 'a/b,a$', { + method: 'GET', endpoint: '1', p: ['b', 'a'], }); patternsTest('option testing - completion 4', endpoints, 'a/c$', { + method: 'GET', endpoint: '2', }); - patternsTest('option testing - completion 5', endpoints, 'a/d$', {}); + patternsTest('option testing - completion 5', endpoints, 'a/d$', { method: 'GET', }); patternsTest( 'option testing - partial, with auto complete', endpoints, 'a', - { autoCompleteSet: [t('a', 'p'), t('b', 'p'), 'c'] } + { method: 'GET', autoCompleteSet: [t('a', 'p'), t('b', 'p'), 'c'] } ); patternsTest( 'option testing - partial, without auto complete', endpoints, 'a', - {} + { method: 'GET', } ); patternsTest( 'option testing - different path - with auto complete', endpoints, 'a/e', - { autoCompleteSet: [] } + { method: 'GET', autoCompleteSet: [] } ); }()); @@ -267,12 +282,15 @@ describe('Url autocomplete', () => { url_components: { p: ['a', 'b'], }, + methods: [ 'GET' ] }, '2': { patterns: ['b/{p}'], + methods: [ 'GET' ] }, '3': { patterns: ['b/{l}/c'], + methods: [ 'GET' ], url_components: { l: { type: 'list', @@ -292,7 +310,7 @@ describe('Url autocomplete', () => { 'global parameters testing - completion 1', endpoints, 'a/a$', - { endpoint: '1', p: ['a'] }, + { method: 'GET', endpoint: '1', p: ['a'] }, globalFactories ); @@ -300,7 +318,7 @@ describe('Url autocomplete', () => { 'global parameters testing - completion 2', endpoints, 'b/g1$', - { endpoint: '2', p: ['g1'] }, + { method: 'GET', endpoint: '2', p: ['g1'] }, globalFactories ); @@ -308,7 +326,7 @@ describe('Url autocomplete', () => { 'global parameters testing - partial, with auto complete', endpoints, 'a', - { autoCompleteSet: [t('a', 'p'), t('b', 'p')] }, + { method: 'GET', autoCompleteSet: [t('a', 'p'), t('b', 'p')] }, globalFactories ); @@ -317,6 +335,7 @@ describe('Url autocomplete', () => { endpoints, 'b', { + method: 'GET', autoCompleteSet: [ t('g1', 'p'), t('g2', 'p'), @@ -331,14 +350,14 @@ describe('Url autocomplete', () => { 'Non valid token acceptance - partial, with auto complete 1', endpoints, 'b/la', - { autoCompleteSet: ['c'], l: ['la'] }, + { method: 'GET', autoCompleteSet: ['c'], l: ['la'] }, globalFactories ); patternsTest( 'Non valid token acceptance - partial, with auto complete 2', endpoints, 'b/non_valid', - { autoCompleteSet: ['c'], l: ['non_valid'] }, + { method: 'GET', autoCompleteSet: ['c'], l: ['non_valid'] }, globalFactories ); }()); @@ -347,13 +366,16 @@ describe('Url autocomplete', () => { const endpoints = { '1': { patterns: ['a/b/{p}/c/e'], + methods: [ 'GET' ] }, }; patternsTest('look ahead - autocomplete before param 1', endpoints, 'a', { + method: 'GET', autoCompleteSet: ['b'], }); patternsTest('look ahead - autocomplete before param 2', endpoints, [], { + method: 'GET', autoCompleteSet: ['a/b'], }); @@ -361,14 +383,14 @@ describe('Url autocomplete', () => { 'look ahead - autocomplete after param 1', endpoints, 'a/b/v', - { autoCompleteSet: ['c/e'], p: 'v' } + { method: 'GET', autoCompleteSet: ['c/e'], p: 'v' } ); patternsTest( 'look ahead - autocomplete after param 2', endpoints, 'a/b/v/c', - { autoCompleteSet: ['e'], p: 'v' } + { method: 'GET', autoCompleteSet: ['e'], p: 'v' } ); }()); diff --git a/x-pack/plugins/console_extensions/spec/xpack_graph_explore.json b/x-pack/plugins/console_extensions/spec/xpack_graph_explore.json index 38dcd9c7d4cc6..8c47cdc954e99 100644 --- a/x-pack/plugins/console_extensions/spec/xpack_graph_explore.json +++ b/x-pack/plugins/console_extensions/spec/xpack_graph_explore.json @@ -1,10 +1,10 @@ { - "{index_name}/_xpack/graph/_explore": { + "{index}/_xpack/graph/_explore": { "methods": [ "POST" ], "patterns": [ - "{index_name}/_xpack/graph/_explore" + "{index}/_xpack/graph/_explore" ], "data_autocomplete_rules": { "query": {}, diff --git a/x-pack/plugins/console_extensions/spec/xpack_migration.json b/x-pack/plugins/console_extensions/spec/xpack_migration.json index 5aa54546ca3be..fe70069d17523 100644 --- a/x-pack/plugins/console_extensions/spec/xpack_migration.json +++ b/x-pack/plugins/console_extensions/spec/xpack_migration.json @@ -5,10 +5,10 @@ ], "patterns": [ "_xpack/migration/assistance", - "_xpack/migration/assistance/{index_name}" + "_xpack/migration/assistance/{index}" ] }, - "_xpack/migration/upgrade/{index_name}": { + "_xpack/migration/upgrade/{index}": { "url_params": { "wait_for_completion": ["true", "false"] }, @@ -16,7 +16,7 @@ "POST" ], "patterns": [ - "_xpack/migration/upgrade/{index_name}" + "_xpack/migration/upgrade/{index}" ] }, "_xpack/migration/deprecations": { @@ -25,7 +25,7 @@ ], "patterns": [ "_xpack/migration/deprecations", - "{index_name}/_xpack/migration/deprecations" + "{index}/_xpack/migration/deprecations" ] } } diff --git a/x-pack/plugins/console_extensions/spec/xpack_rollup.json b/x-pack/plugins/console_extensions/spec/xpack_rollup.json index df4253f6f2b8d..2accaed23ab45 100644 --- a/x-pack/plugins/console_extensions/spec/xpack_rollup.json +++ b/x-pack/plugins/console_extensions/spec/xpack_rollup.json @@ -55,17 +55,17 @@ "POST" ] }, - "_xpack/rollup/data/{index_name}": { + "_xpack/rollup/data/{index}": { "patterns": [ - "_xpack/rollup/data/{index_name}" + "_xpack/rollup/data/{index}" ], "methods": [ "GET" ] }, - "{index_name}/_rollup_search": { + "{index}/_rollup_search": { "patterns": [ - "{index_name}/_rollup_search" + "{index}/_rollup_search" ], "methods": [ "GET"