diff --git a/packages/kbn-interpreter/src/common/index.js b/packages/kbn-interpreter/src/common/index.js index 886106d39f4e1..fbf7d9bd0d6f1 100644 --- a/packages/kbn-interpreter/src/common/index.js +++ b/packages/kbn-interpreter/src/common/index.js @@ -17,7 +17,6 @@ * under the License. */ -export { pathsRegistry } from './lib/paths_registry'; export { functionsRegistry } from './lib/functions_registry'; export { typesRegistry } from './lib/types_registry'; export { createError } from './interpreter/create_error'; diff --git a/packages/kbn-interpreter/src/common/lib/paths_registry.js b/packages/kbn-interpreter/src/common/lib/paths_registry.js deleted file mode 100644 index 3ad2b5dddf82e..0000000000000 --- a/packages/kbn-interpreter/src/common/lib/paths_registry.js +++ /dev/null @@ -1,65 +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. - */ - -class PathsRegistry { - - constructor() { - this.paths = new Map(); - } - - register = (type, paths) => { - if (!type) { - throw new Error(`Register requires a type`); - } - const lowerCaseType = type.toLowerCase(); - - const pathArray = Array.isArray(paths) ? paths : [paths]; - if (!this.paths.has(lowerCaseType)) { - this.paths.set(lowerCaseType, []); - } - - pathArray.forEach(p => { - this.paths.get(lowerCaseType).push(p); - }); - }; - - registerAll = (paths) => { - Object.keys(paths).forEach(type => { - this.register(type, paths[type]); - }); - }; - - toArray = () => { - return [...this.paths.values()]; - }; - - get = (type) => { - if (!type) { - return []; - } - const lowerCaseType = type.toLowerCase(); - return this.paths.has(lowerCaseType) ? this.paths.get(lowerCaseType) : []; - }; - - reset = () => { - this.paths.clear(); - }; -} - -export const pathsRegistry = new PathsRegistry(); diff --git a/packages/kbn-interpreter/src/common/lib/paths_registry.test.js b/packages/kbn-interpreter/src/common/lib/paths_registry.test.js deleted file mode 100644 index ad2b9d949deb3..0000000000000 --- a/packages/kbn-interpreter/src/common/lib/paths_registry.test.js +++ /dev/null @@ -1,92 +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. - */ - -describe('pathsRegistry', () => { - let registry; - beforeEach(() => { - jest.resetModules(); - registry = require('./paths_registry').pathsRegistry; - }); - - const paths = { - foo: 'bar', - sometype: [ - 'Here', - 'be', - 'more', - 'paths!' - ], - anothertype: ['with just one lonely path'] - }; - - it('throws when no type is provided', () => { - const check = () => registry.register(null, paths.foo); - expect(check).toThrowError(/requires a type/); - }); - - it('accepts paths as a string', () => { - registry.register('foo', paths.foo); - expect(registry.get('foo')).toEqual([paths.foo]); - }); - - it('accepts paths as an array', () => { - registry.register('sometype', paths.sometype); - expect(registry.get('sometype')).toEqual(paths.sometype); - }); - - it('ignores case when setting items', () => { - registry.register('FOO', paths.foo); - expect(registry.get('foo')).toEqual([paths.foo]); - }); - - it('gets items by lookup property', () => { - registry.register('sometype', paths.sometype); - expect(registry.get('sometype')).toEqual(paths.sometype); - }); - - it('can register an object of `type: path` key-value pairs', () => { - registry.registerAll(paths); - expect(registry.get('foo')).toEqual([paths.foo]); - expect(registry.get('sometype')).toEqual(paths.sometype); - expect(registry.get('anothertype')).toEqual(paths.anothertype); - }); - - it('ignores case when getting items', () => { - registry.registerAll(paths); - expect(registry.get('FOO')).toEqual([paths.foo]); - expect(registry.get('SOmEType')).toEqual(paths.sometype); - expect(registry.get('anoThertYPE')).toEqual(paths.anothertype); - }); - - it('returns an empty array with no match', () => { - expect(registry.get('@@nope_nope')).toEqual([]); - }); - - it('returns an array of all path values', () => { - registry.registerAll(paths); - expect(registry.toArray()).toEqual([[paths.foo], paths.sometype, paths.anothertype]); - }); - - it('resets the registry', () => { - registry.registerAll(paths); - expect(registry.get('sometype')).toEqual(paths.sometype); - registry.reset(); - expect(registry.get('sometype')).toEqual([]); - }); -}); \ No newline at end of file diff --git a/packages/kbn-interpreter/src/server/get_plugin_paths.js b/packages/kbn-interpreter/src/server/get_plugin_paths.js index f6520563c912f..907d71592ea25 100644 --- a/packages/kbn-interpreter/src/server/get_plugin_paths.js +++ b/packages/kbn-interpreter/src/server/get_plugin_paths.js @@ -17,39 +17,92 @@ * under the License. */ +import path from 'path'; import fs from 'fs'; -import { resolve } from 'path'; import { promisify } from 'util'; import { flatten } from 'lodash'; -import { pathsRegistry } from '../common/lib/paths_registry'; +import { pluginPaths } from './plugin_paths'; const lstat = promisify(fs.lstat); const readdir = promisify(fs.readdir); +const canvasPluginDirectoryName = 'canvas_plugin'; + const isDirectory = path => lstat(path) .then(stat => stat.isDirectory()) .catch(() => false); +const isDirname = (p, name) => path.basename(p) === name; + +const getPackagePluginPath = () => { + let basePluginPath = path.resolve(__dirname, '..'); + + if (isDirname(basePluginPath, 'target')) { + basePluginPath = path.join(basePluginPath, '..'); + } + return basePluginPath; +}; + +const getKibanaPluginsPath = () => { + let kibanaPath = path.resolve(getPackagePluginPath(), '..', '..'); + + // in dev mode we are in kibana folder, else we are in node_modules + if (!isDirname(kibanaPath, 'kibana')) { + kibanaPath = path.join(kibanaPath, '..'); + } + + return path.join(kibanaPath, 'plugins'); +}; + +const getXPackPluginsPath = () => { + const kibanaPath = path.resolve(getPackagePluginPath(), '..', '..'); + + // in dev mode we are in kibana folder, else we are in node_modules + return path.join(kibanaPath, 'x-pack/plugins'); +}; + +// These must all exist +const paths = [ + getPackagePluginPath(), + getXPackPluginsPath(), // Canvas core plugins + getKibanaPluginsPath(), // Kibana plugin directory +].filter(Boolean); + export const getPluginPaths = type => { - const typePaths = pathsRegistry.get(type); - if (!typePaths) { - throw new Error(`Unknown type: ${type}`); + const typePath = pluginPaths[type]; + if (!typePath) throw new Error(`Unknown type: ${type}`); + + async function findPlugins(directory) { + const isDir = await isDirectory(directory); + if (!isDir) return; + + const names = await readdir(directory); // Get names of everything in the directory + return names + .filter(name => name[0] !== '.') + .map(name => path.resolve(directory, name, canvasPluginDirectoryName, ...typePath)); } - return Promise.all(typePaths.map(async path => { - const isDir = await isDirectory(path); - if (!isDir) { - return; - } - // Get the full path of all js files in the directory - return readdir(path).then(files => { - return files.reduce((acc, file) => { - if (file.endsWith('.js')) { - acc.push(resolve(path, file)); - } - return acc; - }, []); - }).catch(); - })).then(flatten); + return Promise.all(paths.map(findPlugins)) + .then(dirs => + dirs.reduce((list, dir) => { + if (!dir) return list; + return list.concat(dir); + }, []) + ) + .then(possibleCanvasPlugins => { + // Check how many are directories. If lstat fails it doesn't exist anyway. + return Promise.all( + // An array + possibleCanvasPlugins.map(pluginPath => isDirectory(pluginPath)) + ).then(isDirectory => possibleCanvasPlugins.filter((pluginPath, i) => isDirectory[i])); + }) + .then(canvasPluginDirectories => { + return Promise.all( + canvasPluginDirectories.map(dir => + // Get the full path of all files in the directory + readdir(dir).then(files => files.map(file => path.resolve(dir, file))) + ) + ).then(flatten); + }); }; diff --git a/packages/kbn-interpreter/src/server/index.js b/packages/kbn-interpreter/src/server/index.js index e481a3ec1fd60..3815cad550d07 100644 --- a/packages/kbn-interpreter/src/server/index.js +++ b/packages/kbn-interpreter/src/server/index.js @@ -19,3 +19,4 @@ export { populateServerRegistries, getServerRegistries } from './server_registries'; export { getPluginPaths } from './get_plugin_paths'; +export { pluginPaths } from './plugin_paths'; diff --git a/packages/kbn-interpreter/plugin_paths.js b/packages/kbn-interpreter/src/server/plugin_paths.js similarity index 64% rename from packages/kbn-interpreter/plugin_paths.js rename to packages/kbn-interpreter/src/server/plugin_paths.js index af05a6c3cfba4..26c22cdd2b8e9 100644 --- a/packages/kbn-interpreter/plugin_paths.js +++ b/packages/kbn-interpreter/src/server/plugin_paths.js @@ -17,9 +17,17 @@ * under the License. */ -const { resolve } = require('path'); - -exports.pluginPaths = { - commonFunctions: resolve(__dirname, 'target/plugin/functions/common'), - types: resolve(__dirname, 'target/plugin/types'), +export const pluginPaths = { + serverFunctions: ['functions', 'server'], + browserFunctions: ['functions', 'browser'], + commonFunctions: ['functions', 'common'], + types: ['types'], + elements: ['elements'], + renderers: ['renderers'], + interfaces: ['interfaces'], + transformUIs: ['uis', 'transforms'], + datasourceUIs: ['uis', 'datasources'], + modelUIs: ['uis', 'models'], + viewUIs: ['uis', 'views'], + argumentUIs: ['uis', 'arguments'], }; diff --git a/packages/kbn-interpreter/tasks/build/paths.js b/packages/kbn-interpreter/tasks/build/paths.js index 5b9128a892e70..26ccbe44551d9 100644 --- a/packages/kbn-interpreter/tasks/build/paths.js +++ b/packages/kbn-interpreter/tasks/build/paths.js @@ -24,7 +24,7 @@ exports.SOURCE_DIR = resolve(exports.ROOT_DIR, 'src'); exports.BUILD_DIR = resolve(exports.ROOT_DIR, 'target'); exports.PLUGIN_SOURCE_DIR = resolve(exports.SOURCE_DIR, 'plugin'); -exports.PLUGIN_BUILD_DIR = resolve(exports.BUILD_DIR, 'plugin'); +exports.PLUGIN_BUILD_DIR = resolve(exports.BUILD_DIR, 'canvas_plugin'); exports.WEBPACK_CONFIG_PATH = require.resolve('./webpack.config'); exports.BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); diff --git a/src/legacy/core_plugins/interpreter/index.js b/src/legacy/core_plugins/interpreter/index.js index 61ff5e1395f10..21ab23f1b8373 100644 --- a/src/legacy/core_plugins/interpreter/index.js +++ b/src/legacy/core_plugins/interpreter/index.js @@ -19,8 +19,6 @@ import { resolve } from 'path'; import init from './init'; -import { pathsRegistry } from '@kbn/interpreter/common'; -import { pluginPaths } from '@kbn/interpreter/plugin_paths'; export default function (kibana) { return new kibana.Plugin({ @@ -32,9 +30,6 @@ export default function (kibana) { 'plugins/interpreter/load_browser_plugins.js', ], }, - preInit: () => { - pathsRegistry.registerAll(pluginPaths); - }, init, }); } diff --git a/src/legacy/core_plugins/interpreter/server/routes/plugins.js b/src/legacy/core_plugins/interpreter/server/routes/plugins.js index 3d8c8614cc107..1c1596fc2ed24 100644 --- a/src/legacy/core_plugins/interpreter/server/routes/plugins.js +++ b/src/legacy/core_plugins/interpreter/server/routes/plugins.js @@ -17,15 +17,20 @@ * under the License. */ +import { pluginPaths } from '@kbn/interpreter/server'; import { getPluginStream } from '../lib/get_plugin_stream'; export function plugins(server) { server.route({ method: 'GET', path: '/api/canvas/plugins', - handler: function (request) { + handler: function (request, h) { const { type } = request.query; + if (!pluginPaths[type]) { + return h.response({ error: 'Invalid type' }).code(400); + } + return getPluginStream(type); }, config: { diff --git a/x-pack/plugins/canvas/index.js b/x-pack/plugins/canvas/index.js index 04c961bf23777..6528c9f0dd9af 100644 --- a/x-pack/plugins/canvas/index.js +++ b/x-pack/plugins/canvas/index.js @@ -5,11 +5,9 @@ */ import { resolve } from 'path'; -import { pathsRegistry } from '@kbn/interpreter/common'; import init from './init'; import { mappings } from './server/mappings'; import { CANVAS_APP } from './common/lib'; -import { pluginPaths } from './plugin_paths'; export function canvas(kibana) { return new kibana.Plugin({ @@ -41,9 +39,6 @@ export function canvas(kibana) { }).default(); }, - preInit: () => { - pathsRegistry.registerAll(pluginPaths); - }, init, }); } diff --git a/x-pack/plugins/canvas/plugin_paths.js b/x-pack/plugins/canvas/plugin_paths.js deleted file mode 100644 index 9c9f5d1c49bde..0000000000000 --- a/x-pack/plugins/canvas/plugin_paths.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { resolve } from 'path'; - -export const pluginPaths = { - serverFunctions: resolve(__dirname, 'canvas_plugin/functions/server'), - browserFunctions: resolve(__dirname, 'canvas_plugin/functions/browser'), - commonFunctions: resolve(__dirname, 'canvas_plugin/functions/common'), - elements: resolve(__dirname, 'canvas_plugin/elements'), - renderers: resolve(__dirname, 'canvas_plugin/renderers'), - interfaces: resolve(__dirname, 'canvas_plugin/interfaces'), - transformUIs: resolve(__dirname, 'canvas_plugin/uis/transforms'), - datasourceUIs: resolve(__dirname, 'canvas_plugin/uis/datasources'), - modelUIs: resolve(__dirname, 'canvas_plugin/uis/models'), - viewUIs: resolve(__dirname, 'canvas_plugin/uis/views'), - argumentUIs: resolve(__dirname, 'canvas_plugin/uis/arguments'), -};