From 7e77e0bdb42ba35ac4ecbd4889a54b2b54bf7750 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 10:39:51 +0200 Subject: [PATCH 01/33] Update dependency @elastic/charts to ^12.1.0 (#47267) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fb65efdde6912..e60819102eb91 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "dependencies": { "@babel/core": "^7.5.5", "@babel/register": "^7.5.5", - "@elastic/charts": "^12.0.2", + "@elastic/charts": "^12.1.0", "@elastic/datemath": "5.0.2", "@elastic/eui": "14.4.0", "@elastic/filesaver": "1.1.2", diff --git a/yarn.lock b/yarn.lock index 620acf64598a0..b96aa7f3b32d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1064,10 +1064,10 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@elastic/charts@^12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-12.0.2.tgz#576fafccd9e9f6ca751b6e846be3a5c954e8865b" - integrity sha512-BxdJVXUkYE11X+n5QWfu6ntDCm6wbkvLRNWrJG30pgGv9QEDhEbraQ8ql9Vx1454EuEjgXP6xOM0X+3rCO4Nqw== +"@elastic/charts@^12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-12.1.0.tgz#a1ad871cf1d90efc2450d29aa41bc9872434616a" + integrity sha512-sXZ0KHN29icVbeCU0x6LsiYI++w55kUJA0bpH7vpGExny70UBHNqjnLcBui0DBhlS3Bsz64xW5c4i9TKWeqXLA== dependencies: "@types/d3-shape" "^1.3.1" "@types/luxon" "^1.11.1" From 9cf13c9a58e9147be2155ccfd95905e247410496 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 4 Oct 2019 11:16:04 +0200 Subject: [PATCH 02/33] [Graph] App state management (#46133) --- .../graph/public/angular/templates/index.html | 10 +- x-pack/legacy/plugins/graph/public/app.js | 598 +++++------------- .../plugins/graph/public/components/app.tsx | 57 +- .../components/field_manager/field_editor.tsx | 3 +- .../field_manager/field_manager.test.tsx | 84 +-- .../field_manager/field_manager.tsx | 67 +- .../guidance_panel/guidance_panel.tsx | 46 +- .../public/components/search_bar.test.tsx | 97 +-- .../graph/public/components/search_bar.tsx | 76 ++- .../settings/advanced_settings_form.tsx | 6 +- .../components/settings/settings.test.tsx | 220 ++++--- .../public/components/settings/settings.tsx | 69 +- .../components/settings/url_template_list.tsx | 12 +- .../graph/public/helpers/url_template.ts | 6 +- .../public/services/index_pattern_cache.ts | 23 + .../services/persistence/deserialize.test.ts | 13 +- .../services/persistence/deserialize.ts | 16 +- .../services/persistence/serialize.test.ts | 46 +- .../public/services/persistence/serialize.ts | 18 +- .../graph/public/services/save_modal.tsx | 18 +- .../plugins/graph/public/services/url.ts | 11 +- .../state_management/advanced_settings.ts | 54 ++ .../public/state_management/datasource.ts | 110 ++++ .../graph/public/state_management/fields.ts | 93 ++- .../graph/public/state_management/global.ts | 10 + .../graph/public/state_management/helpers.ts | 28 + .../graph/public/state_management/index.ts | 7 + .../public/state_management/meta_data.ts | 61 ++ .../graph/public/state_management/mocks.ts | 57 ++ .../public/state_management/persistence.ts | 203 ++++++ .../graph/public/state_management/store.ts | 87 ++- .../public/state_management/url_templates.ts | 92 +++ .../plugins/graph/public/types/app_state.ts | 10 +- .../graph/public/types/workspace_state.ts | 4 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 36 files changed, 1527 insertions(+), 787 deletions(-) create mode 100644 x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/datasource.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/global.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/helpers.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/meta_data.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/mocks.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/persistence.ts create mode 100644 x-pack/legacy/plugins/graph/public/state_management/url_templates.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 07b57ee322548..9e9356f30642e 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -9,20 +9,20 @@ -
+
{ - // TODO this should be wrapped into canWipeWorkspace, - // but the check is too simple right now. Change this - // once actual state-diffing is in place. - $scope.$evalAsync(() => { - kbnUrl.changePath(getHomePath()); - }); - } - }); - } - - const store = createGraphStore(); - - $scope.title = 'Graph'; - $scope.spymode = 'request'; - - $scope.iconChoices = iconChoices; - $scope.drillDownIconChoices = urlTemplateIconChoices; - $scope.colors = colorChoices; - $scope.iconChoicesByClass = iconChoicesByClass; - - $scope.outlinkEncoders = outlinkEncoders; - - $scope.fields = []; - $scope.canEditDrillDownUrls = chrome.getInjected('canEditDrillDownUrls'); - - $scope.graphSavePolicy = chrome.getInjected('graphSavePolicy'); - $scope.allSavingDisabled = $scope.graphSavePolicy === 'none'; - $scope.searchTerm = ''; - - $scope.reduxDispatch = (action) => { - store.dispatch(action); - - // patch updated icons and fields on the nodes in the workspace state - // this workaround is necessary because the nodes are still managed by - // angular - once they are moved over to redux, this can be handled in - // the reducer - if (action.type === 'x-pack/graph/fields/UPDATE_FIELD_PROPERTIES' && - action.payload.fieldProperties.color && $scope.workspace) { - $scope.workspace.nodes.forEach(function (node) { - if (node.data.field === action.payload.fieldName) { - node.color = action.payload.fieldProperties.color; + // Replacement function for graphClientWorkspace's comms so + // that it works with Kibana. + function callNodeProxy(indexName, query, responseHandler) { + const request = { + index: indexName, + query: query + }; + $scope.loading = true; + return $http.post('../api/graph/graphExplore', request) + .then(function (resp) { + if (resp.data.resp.timed_out) { + toastNotifications.addWarning( + i18n.translate('xpack.graph.exploreGraph.timedOutWarningText', { + defaultMessage: 'Exploration timed out', + }) + ); } + responseHandler(resp.data.resp); + }) + .catch(handleHttpError) + .finally(() => { + $scope.loading = false; }); - } + } - if (action.type === 'x-pack/graph/fields/UPDATE_FIELD_PROPERTIES' && - action.payload.fieldProperties.icon && $scope.workspace) { - $scope.workspace.nodes.forEach(function (node) { - if (node.data.field === action.payload.fieldName) { - node.icon = action.payload.fieldProperties.icon; - } + + //Helper function for the graphClientWorkspace to perform a query + const callSearchNodeProxy = function (indexName, query, responseHandler) { + const request = { + index: indexName, + body: query + }; + $scope.loading = true; + $http.post('../api/graph/searchProxy', request) + .then(function (resp) { + responseHandler(resp.data.resp); + }) + .catch(handleHttpError) + .finally(() => { + $scope.loading = false; }); - } }; + $scope.indexPatternProvider = createCachedIndexPatternProvider($route.current.locals.GetIndexPatternProvider.get); + + const store = createGraphStore({ + basePath: chrome.getBasePath(), + indexPatternProvider: $scope.indexPatternProvider, + indexPatterns: $route.current.locals.indexPatterns, + createWorkspace: (indexPattern, exploreControls) => { + const options = { + indexName: indexPattern, + vertex_fields: [], + // Here we have the opportunity to look up labels for nodes... + nodeLabeller: function () { + // console.log(newNodes); + }, + changeHandler: function () { + //Allows DOM to update with graph layout changes. + $scope.$apply(); + }, + graphExploreProxy: callNodeProxy, + searchProxy: callSearchNodeProxy, + exploreControls, + }; + $scope.workspace = gws.createWorkspace(options); + }, + setLiveResponseFields: (fields) => { + $scope.liveResponseFields = fields; + }, + getWorkspace: () => { + return $scope.workspace; + }, + getSavedWorkspace: () => { + return $route.current.locals.savedWorkspace; + }, + notifications: npStart.core.notifications, + showSaveModal, + savePolicy: chrome.getInjected('graphSavePolicy'), + changeUrl: (newUrl) => { + $scope.$evalAsync(() => { + kbnUrl.change(newUrl, {}); + }); + }, + notifyAngular: () => { + $scope.$digest(); + }, + chrome, + }); $scope.store = new Storage(window.localStorage); $scope.coreStart = npStart.core; $scope.autocompleteStart = npStart.plugins.data.autocomplete; $scope.loading = false; - const updateScope = () => { - const newState = store.getState(); - $scope.reduxState = newState; - $scope.allFields = fieldsSelector(newState); - $scope.selectedFields = selectedFieldsSelector(newState); - $scope.liveResponseFields = liveResponseFieldsSelector(newState); - if ($scope.workspace) { - $scope.workspace.options.vertex_fields = $scope.selectedFields; - } - }; - store.subscribe(updateScope); - updateScope(); + $scope.spymode = 'request'; - //So scope properties can be used consistently with ng-model - $scope.grr = $scope; + const allSavingDisabled = chrome.getInjected('graphSavePolicy') === 'none'; - $scope.toggleDrillDownIcon = function (urlTemplate, icon) { - urlTemplate.icon === icon ? urlTemplate.icon = null : urlTemplate.icon = icon; - }; + $scope.reduxStore = store; $scope.nodeClick = function (n, $event) { @@ -350,14 +351,14 @@ app.controller('graphuiPlugin', function ( } }; - function canWipeWorkspace(yesFn, noFn) { - if ($scope.selectedFields.length === 0 && $scope.workspace === null) { - yesFn(); + function canWipeWorkspace(callback) { + if (!hasFieldsSelector(store.getState())) { + callback(); return; } const confirmModalOptions = { - onConfirm: yesFn, - onCancel: noFn || (() => {}), + onConfirm: callback, + onCancel: (() => {}), confirmButtonText: i18n.translate('xpack.graph.clearWorkspace.confirmButtonLabel', { defaultMessage: 'Continue', }), @@ -369,30 +370,7 @@ app.controller('graphuiPlugin', function ( defaultMessage: 'Once you discard changes made to a workspace, there is no getting them back.', }), confirmModalOptions); } - - $scope.uiSelectIndex = function (proposedIndex) { - canWipeWorkspace(function () { - $scope.indexSelected(proposedIndex); - }); - }; - - $scope.indexSelected = function (selectedIndex) { - $scope.clearWorkspace(); - $scope.allFields = []; - $scope.selectedFields = []; - $scope.basicModeSelectedSingleField = null; - $scope.selectedField = null; - $scope.selectedFieldConfig = null; - - return $route.current.locals.GetIndexPatternProvider.get(selectedIndex.id) - .then(handleSuccess) - .then(function (indexPattern) { - $scope.selectedIndex = indexPattern; - store.dispatch(loadFields(mapFields(indexPattern))); - $scope.$digest(); - }, handleError); - }; - + $scope.confirmWipeWorkspace = canWipeWorkspace; $scope.clickEdge = function (edge) { if (edge.inferred) { @@ -402,62 +380,20 @@ app.controller('graphuiPlugin', function ( } }; - // Replacement function for graphClientWorkspace's comms so - // that it works with Kibana. - function callNodeProxy(indexName, query, responseHandler) { - const request = { - index: indexName, - query: query - }; - $scope.loading = true; - return $http.post('../api/graph/graphExplore', request) - .then(function (resp) { - if (resp.data.resp.timed_out) { - toastNotifications.addWarning( - i18n.translate('xpack.graph.exploreGraph.timedOutWarningText', { - defaultMessage: 'Exploration timed out', - }) - ); - } - responseHandler(resp.data.resp); - }) - .catch(handleHttpError) - .finally(() => { - $scope.loading = false; - }); - } - - - //Helper function for the graphClientWorkspace to perform a query - const callSearchNodeProxy = function (indexName, query, responseHandler) { - const request = { - index: indexName, - body: query - }; - $scope.loading = true; - $http.post('../api/graph/searchProxy', request) - .then(function (resp) { - responseHandler(resp.data.resp); - }) - .catch(handleHttpError) - .finally(() => { - $scope.loading = false; - }); - }; $scope.fillWorkspace = async () => { try { const fields = selectedFieldsSelector(store.getState()); const topTermNodes = await fetchTopNodes( npStart.core.http.post, - $scope.selectedIndex.title, + datasourceSelector(store.getState()).current.title, fields ); - initWorkspaceIfRequired(); $scope.workspace.mergeGraph({ nodes: topTermNodes, edges: [] }); + $scope.workspaceInitialized = true; $scope.workspace.fillInGraph(fields.length * 10); } catch (e) { toastNotifications.addDanger({ @@ -470,7 +406,7 @@ app.controller('graphuiPlugin', function ( }; $scope.submit = function (searchTerm) { - initWorkspaceIfRequired(); + $scope.workspaceInitialized = true; const numHops = 2; if (searchTerm.startsWith('{')) { try { @@ -509,121 +445,12 @@ app.controller('graphuiPlugin', function ( return $scope.selectedSelectedVertex === node; }; - $scope.saveUrlTemplate = function (index, urlTemplate) { - const newTemplatesList = [...$scope.urlTemplates]; - if (index !== -1) { - newTemplatesList[index] = urlTemplate; - } else { - newTemplatesList.push(urlTemplate); - } - - $scope.urlTemplates = newTemplatesList; - }; - - $scope.removeUrlTemplate = function (urlTemplate) { - const newTemplatesList = [...$scope.urlTemplates]; - const i = newTemplatesList.indexOf(urlTemplate); - newTemplatesList.splice(i, 1); - $scope.urlTemplates = newTemplatesList; - }; - $scope.openUrlTemplate = function (template) { const url = template.url; const newUrl = url.replace(urlTemplateRegex, template.encoder.encode($scope.workspace)); window.open(newUrl, '_blank'); }; - - //============================ - - $scope.resetWorkspace = function () { - $scope.clearWorkspace(); - $scope.selectedIndex = null; - $scope.proposedIndex = null; - $scope.detail = null; - $scope.selectedSelectedVertex = null; - $scope.selectedField = null; - $scope.description = null; - $scope.allFields = []; - $scope.urlTemplates = []; - - $scope.fieldNamesFilterString = null; - $scope.filteredFields = []; - - $scope.selectedFields = []; - $scope.liveResponseFields = []; - - $scope.exploreControls = { - useSignificance: true, - sampleSize: 2000, - timeoutMillis: 5000, - sampleDiversityField: null, - maxValuesPerDoc: 1, - minDocCount: 3 - }; - }; - - - function initWorkspaceIfRequired() { - if ($scope.workspace) { - return; - } - const options = { - indexName: $scope.selectedIndex.title, - vertex_fields: $scope.selectedFields, - // Here we have the opportunity to look up labels for nodes... - nodeLabeller: function () { - // console.log(newNodes); - }, - changeHandler: function () { - //Allows DOM to update with graph layout changes. - $scope.$apply(); - }, - graphExploreProxy: callNodeProxy, - searchProxy: callSearchNodeProxy, - exploreControls: $scope.exploreControls - }; - $scope.workspace = gws.createWorkspace(options); - $scope.detail = null; - - // filter out default url templates because they will get re-added - $scope.urlTemplates = $scope.urlTemplates.filter(template => !template.isDefault); - - if ($scope.urlTemplates.length === 0) { - // url templates specified by users can include the `{{gquery}}` tag and - // will have the elasticsearch query for the graph nodes injected there - const tag = '{{gquery}}'; - - const kUrl = new KibanaParsedUrl({ - appId: 'kibana', - basePath: chrome.getBasePath(), - appPath: '/discover' - }); - - kUrl.addQueryParameter('_a', rison.encode({ - columns: ['_source'], - index: $scope.selectedIndex.id, - interval: 'auto', - query: { language: 'kuery', query: tag }, - sort: ['_score', 'desc'] - })); - - const discoverUrl = kUrl.getRootRelativePath() - // replace the URI encoded version of the tag with the unescaped version - // so it can be found with String.replace, regexp, etc. - .replace(encodeURIComponent(tag), tag); - - $scope.urlTemplates.push({ - url: discoverUrl, - description: i18n.translate('xpack.graph.settings.drillDowns.defaultUrlTemplateTitle', { - defaultMessage: 'Raw documents', - }), - encoder: $scope.outlinkEncoders[0], - isDefault: true - }); - } - } - $scope.aceLoaded = (editor) => { editor.$blockScrolling = Infinity; }; @@ -668,39 +495,6 @@ app.controller('graphuiPlugin', function ( $scope.detail = { mergeCandidates }; }; - //initialize all the state - $scope.resetWorkspace(); - - const managementUrl = npStart.core.chrome.navLinks.get('kibana:management').url; - const url = `${managementUrl}/kibana/index_patterns`; - - if ($route.current.locals.indexPatterns.length === 0) { - toastNotifications.addWarning({ - title: i18n.translate('xpack.graph.noDataSourceNotificationMessageTitle', { - defaultMessage: 'No data source', - }), - text: ( -

- - - - ) - }} - /> -

- ), - }); - } - - // ===== Menubar configuration ========= $scope.topNavMenu = []; $scope.topNavMenu.push({ @@ -737,7 +531,7 @@ app.controller('graphuiPlugin', function ( defaultMessage: 'Save workspace', }), tooltip: () => { - if ($scope.allSavingDisabled) { + if (allSavingDisabled) { return i18n.translate('xpack.graph.topNavMenu.saveWorkspace.disabledTooltip', { defaultMessage: 'No changes to saved workspaces are permitted by the current save policy', }); @@ -748,15 +542,12 @@ app.controller('graphuiPlugin', function ( } }, disableButton: function () { - return $scope.allSavingDisabled || $scope.selectedFields.length === 0; + return allSavingDisabled || !hasFieldsSelector(store.getState()); }, run: () => { - openSaveModal({ - savePolicy: $scope.graphSavePolicy, - hasData: $scope.workspace && ($scope.workspace.nodes.length > 0 || $scope.workspace.blacklistedNodes.length > 0), - workspace: $scope.savedWorkspace, - saveWorkspace: $scope.saveWorkspace, - showSaveModal + store.dispatch({ + type: 'x-pack/graph/SAVE_WORKSPACE', + payload: $route.current.locals.savedWorkspace, }); }, testId: 'graphSaveButton', @@ -780,10 +571,9 @@ app.controller('graphuiPlugin', function ( }, }); - let currentSettingsFlyout; $scope.topNavMenu.push({ key: 'settings', - disableButton: function () { return $scope.selectedIndex === null; }, + disableButton: function () { return datasourceSelector(store.getState()).type === 'none'; }, label: i18n.translate('xpack.graph.topNavMenu.settingsLabel', { defaultMessage: 'Settings', }), @@ -791,40 +581,25 @@ app.controller('graphuiPlugin', function ( defaultMessage: 'Settings', }), run: () => { - if (currentSettingsFlyout) { - currentSettingsFlyout.close(); - return; - } const settingsObservable = asAngularSyncedObservable(() => ({ - advancedSettings: { ...$scope.exploreControls }, - updateAdvancedSettings: (updatedSettings) => { - $scope.exploreControls = updatedSettings; - if ($scope.workspace) { - $scope.workspace.options.exploreControls = updatedSettings; - } - }, blacklistedNodes: $scope.workspace ? [...$scope.workspace.blacklistedNodes] : undefined, unblacklistNode: $scope.workspace ? $scope.workspace.unblacklist : undefined, - urlTemplates: [...$scope.urlTemplates], - removeUrlTemplate: $scope.removeUrlTemplate, - saveUrlTemplate: $scope.saveUrlTemplate, - allFields: [...$scope.allFields], - canEditDrillDownUrls: $scope.canEditDrillDownUrls + canEditDrillDownUrls: chrome.getInjected('canEditDrillDownUrls') }), $scope.$digest.bind($scope)); - currentSettingsFlyout = npStart.core.overlays.openFlyout(, { - size: 'm', - closeButtonAriaLabel: i18n.translate('xpack.graph.settings.closeLabel', { defaultMessage: 'Close' }), - 'data-test-subj': 'graphSettingsFlyout', - ownFocus: true, - className: 'gphSettingsFlyout', - maxWidth: 520, - }); - currentSettingsFlyout.onClose.then(() => { currentSettingsFlyout = null; }); + npStart.core.overlays.openFlyout( + + + , { + size: 'm', + closeButtonAriaLabel: i18n.translate('xpack.graph.settings.closeLabel', { defaultMessage: 'Close' }), + 'data-test-subj': 'graphSettingsFlyout', + ownFocus: true, + className: 'gphSettingsFlyout', + maxWidth: 520, + }); }, }); - updateBreadcrumbs(); - $scope.menus = { showSettings: false, }; @@ -836,99 +611,42 @@ app.controller('graphuiPlugin', function ( }; // Deal with situation of request to open saved workspace - if ($route.current.locals.savedWorkspace) { - $scope.savedWorkspace = $route.current.locals.savedWorkspace; - const selectedIndex = lookupIndexPattern($scope.savedWorkspace, $route.current.locals.indexPatterns); - if(!selectedIndex) { - toastNotifications.addDanger( - i18n.translate('xpack.graph.loadWorkspace.missingIndexPatternErrorMessage', { - defaultMessage: 'Index pattern not found', - }) - ); - return; - } - $route.current.locals.GetIndexPatternProvider.get(selectedIndex.id).then(indexPattern => { - $scope.selectedIndex = indexPattern; - initWorkspaceIfRequired(); - const { - urlTemplates, - advancedSettings, - allFields, - } = savedWorkspaceToAppState($scope.savedWorkspace, indexPattern, $scope.workspace); - - // wire up stuff to angular - store.dispatch(loadFields(allFields)); - $scope.exploreControls = advancedSettings; - $scope.workspace.options.exploreControls = advancedSettings; - $scope.urlTemplates = urlTemplates; - $scope.workspace.runLayout(); - // Allow URLs to include a user-defined text query - if ($route.current.params.query) { - $scope.initialQuery = $route.current.params.query; - $scope.submit($route.current.params.query); - } - - $scope.$digest(); + if ($route.current.locals.savedWorkspace.id) { + store.dispatch({ + type: 'x-pack/graph/LOAD_WORKSPACE', + payload: $route.current.locals.savedWorkspace, }); } else { - $route.current.locals.SavedWorkspacesProvider.get().then(function (newWorkspace) { - $scope.savedWorkspace = newWorkspace; - }); - } + const managementUrl = npStart.core.chrome.navLinks.get('kibana:management').url; + const url = `${managementUrl}/kibana/index_patterns`; - $scope.saveWorkspace = function (saveOptions, userHasConfirmedSaveWorkspaceData) { - if ($scope.allSavingDisabled) { - // It should not be possible to navigate to this function if allSavingDisabled is set - // but adding check here as a safeguard. - toastNotifications.addWarning( - i18n.translate('xpack.graph.saveWorkspace.disabledWarning', { defaultMessage: 'Saving is disabled' }) - ); - return; + if ($route.current.locals.indexPatterns.length === 0) { + toastNotifications.addWarning({ + title: i18n.translate('xpack.graph.noDataSourceNotificationMessageTitle', { + defaultMessage: 'No data source', + }), + text: ( +

+ + + + ) + }} + /> +

+ ), + }); } - initWorkspaceIfRequired(); - const canSaveData = $scope.graphSavePolicy === 'configAndData' || - ($scope.graphSavePolicy === 'configAndDataWithConsent' && userHasConfirmedSaveWorkspaceData); - - appStateToSavedWorkspace( - $scope.savedWorkspace, - { - workspace: $scope.workspace, - urlTemplates: $scope.urlTemplates, - advancedSettings: $scope.exploreControls, - selectedIndex: $scope.selectedIndex, - selectedFields: $scope.selectedFields - }, - canSaveData - ); - - return $scope.savedWorkspace.save(saveOptions).then(function (id) { - if (id) { - const title = i18n.translate('xpack.graph.saveWorkspace.successNotificationTitle', { - defaultMessage: 'Saved "{workspaceTitle}"', - values: { workspaceTitle: $scope.savedWorkspace.title }, - }); - let text; - if (!canSaveData && $scope.workspace.nodes.length > 0) { - text = i18n.translate('xpack.graph.saveWorkspace.successNotification.noDataSavedText', { - defaultMessage: 'The configuration was saved, but the data was not saved', - }); - } - - toastNotifications.addSuccess({ - title, - text, - 'data-test-subj': 'saveGraphSuccess', - }); - if ($scope.savedWorkspace.id !== $route.current.params.id) { - kbnUrl.change(getEditPath($scope.savedWorkspace)); - } - } - return { id }; - }, fatalError); - - }; - - + } + $scope.savedWorkspace = $route.current.locals.savedWorkspace; }); -//End controller + diff --git a/x-pack/legacy/plugins/graph/public/components/app.tsx b/x-pack/legacy/plugins/graph/public/components/app.tsx index 894c6b9ef45ac..efc8c1bfcc00a 100644 --- a/x-pack/legacy/plugins/graph/public/components/app.tsx +++ b/x-pack/legacy/plugins/graph/public/components/app.tsx @@ -5,29 +5,31 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { Provider } from 'react-redux'; import React, { useState } from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { Storage } from 'ui/storage'; import { CoreStart } from 'kibana/public'; import { AutocompletePublicPluginStart } from 'src/plugins/data/public'; -import { FieldManagerProps, FieldManager } from './field_manager'; +import { FieldManager } from './field_manager'; import { SearchBarProps, SearchBar } from './search_bar'; +import { GraphStore } from '../state_management'; import { GuidancePanel } from './guidance_panel'; -import { selectedFieldsSelector } from '../state_management'; -import { openSourceModal } from '../services/source_modal'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -export interface GraphAppProps extends FieldManagerProps, SearchBarProps { +export interface GraphAppProps extends SearchBarProps { coreStart: CoreStart; autocompleteStart: AutocompletePublicPluginStart; store: Storage; - onFillWorkspace: () => void; + reduxStore: GraphStore; isInitialized: boolean; + onFillWorkspace: () => void; } export function GraphApp(props: GraphAppProps) { const [pickerOpen, setPickerOpen] = useState(false); + const { coreStart, autocompleteStart, store, reduxStore, ...searchBarProps } = props; return ( @@ -39,29 +41,28 @@ export function GraphApp(props: GraphAppProps) { ...props.coreStart, }} > -
- - - - - - - - -
- {!props.isInitialized && ( - 0} - onFillWorkspace={props.onFillWorkspace} - onOpenFieldPicker={() => { - setPickerOpen(true); - }} - onOpenDatasourcePicker={() => { - openSourceModal(props.coreStart, props.onIndexPatternSelected); - }} - /> - )} + + <> +
+ + + + + + + + +
+ {!props.isInitialized && ( + { + setPickerOpen(true); + }} + /> + )} + +
); diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx index b9de9b9c628a0..519e41e846051 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx @@ -30,9 +30,10 @@ import { WorkspaceField } from '../../types'; import { iconChoices } from '../../helpers/style_choices'; import { LegacyIcon } from '../legacy_icon'; import { FieldIcon } from './field_icon'; +import { UpdateableFieldProperties } from './field_manager'; + import { isEqual } from '../helpers'; -type UpdateableFieldProperties = 'hopSize' | 'lastValidHopSize' | 'color' | 'icon'; export interface FieldPickerProps { field: WorkspaceField; allFields: WorkspaceField[]; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx index fb715e759c62d..e9d3f3738ea6e 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -5,23 +5,25 @@ */ import React, { ReactElement } from 'react'; -import { EuiColorPicker, EuiSelectable, EuiContextMenu, EuiPopover, EuiButton } from '@elastic/eui'; +import { EuiColorPicker, EuiSelectable, EuiContextMenu, EuiButton } from '@elastic/eui'; import { FieldPicker } from './field_picker'; import { FieldEditor } from './field_editor'; -import { GraphStore, createGraphStore, loadFields } from '../../state_management'; +import { GraphStore, loadFields } from '../../state_management'; import { getSuitableIcon } from '../../helpers/style_choices'; import { shallow, ShallowWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { FieldManager } from './field_manager'; +import { Provider } from 'react-redux'; +import { createMockGraphStore } from '../../state_management/mocks'; describe('field_manager', () => { let store: GraphStore; let instance: ShallowWrapper; + let getInstance: () => ShallowWrapper; let dispatchSpy: jest.Mock; - let openSpy: jest.Mock; beforeEach(() => { - store = createGraphStore(); + store = createMockGraphStore({ includeSagas: false }).store; store.dispatch( loadFields([ { @@ -53,35 +55,31 @@ describe('field_manager', () => { ); dispatchSpy = jest.fn(store.dispatch); - openSpy = jest.fn(); + store.dispatch = dispatchSpy; instance = shallow( - + + {}} /> + ); - }); - function update() { - instance.setProps({ - state: store.getState(), - dispatch: dispatchSpy, - }); - } + getInstance = () => + instance + .find(FieldManager) + .dive() + .dive(); + }); it('should list editors for all selected fields', () => { - expect(instance.find(FieldEditor).length).toEqual(2); + expect(getInstance().find(FieldEditor).length).toEqual(2); expect( - instance + getInstance() .find(FieldEditor) .at(0) .prop('field').name ).toEqual('field1'); expect( - instance + getInstance() .find(FieldEditor) .at(1) .prop('field').name @@ -89,29 +87,21 @@ describe('field_manager', () => { }); it('should select fields from picker', () => { - act(() => { - (instance + expect( + getInstance() .find(FieldPicker) .dive() - .find(EuiPopover) - .prop('button')! as ReactElement).props.onClick(); - }); - - expect(openSpy).toHaveBeenCalled(); - - instance.setProps({ pickerOpen: true }); - - const fieldPicker = instance.find(FieldPicker).dive(); - - expect( - fieldPicker .find(EuiSelectable) .prop('options') .map((option: { label: string }) => option.label) ).toEqual(['field1', 'field2', 'field3']); act(() => { - fieldPicker.find(EuiSelectable).prop('onChange')([{ checked: 'on', label: 'field3' }]); + getInstance() + .find(FieldPicker) + .dive() + .find(EuiSelectable) + .prop('onChange')([{ checked: 'on', label: 'field3' }]); }); expect(dispatchSpy).toHaveBeenCalledWith({ @@ -119,13 +109,12 @@ describe('field_manager', () => { payload: 'field3', }); - update(); - expect(instance.find(FieldEditor).length).toEqual(3); + expect(getInstance().find(FieldEditor).length).toEqual(3); }); it('should deselect field', () => { act(() => { - instance + getInstance() .find(FieldEditor) .at(0) .dive() @@ -138,12 +127,11 @@ describe('field_manager', () => { payload: 'field1', }); - update(); - expect(instance.find(FieldEditor).length).toEqual(1); + expect(getInstance().find(FieldEditor).length).toEqual(1); }); it('should disable field', () => { - const toggleItem = instance + const toggleItem = getInstance() .find(FieldEditor) .at(0) .dive() @@ -165,10 +153,8 @@ describe('field_manager', () => { }, }); - update(); - expect( - instance + getInstance() .find(FieldEditor) .at(0) .dive() @@ -178,7 +164,7 @@ describe('field_manager', () => { }); it('should enable field', () => { - const toggleItem = instance + const toggleItem = getInstance() .find(FieldEditor) .at(1) .dive() @@ -200,10 +186,8 @@ describe('field_manager', () => { }, }); - update(); - expect( - instance + getInstance() .find(FieldEditor) .at(1) .dive() @@ -213,7 +197,7 @@ describe('field_manager', () => { }); it('should change color', () => { - const fieldEditor = instance + const fieldEditor = getInstance() .find(FieldEditor) .at(1) .dive(); diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx index e44ad248e279d..89b325d737bd5 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx @@ -7,55 +7,62 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; import { FieldPicker } from './field_picker'; import { FieldEditor } from './field_editor'; import { selectedFieldsSelector, fieldsSelector, + fieldMapSelector, updateFieldProperties, selectField, deselectField, - GraphDispatch, GraphState, - fieldMapSelector, } from '../../state_management'; +import { WorkspaceField } from '../../types'; + +export type UpdateableFieldProperties = 'hopSize' | 'lastValidHopSize' | 'color' | 'icon'; -export interface FieldManagerProps { - state: GraphState; - dispatch: GraphDispatch; +export function FieldManagerComponent(props: { + allFields: WorkspaceField[]; + fieldMap: Record; + selectedFields: WorkspaceField[]; + updateFieldProperties: (props: { + fieldName: string; + fieldProperties: Partial>; + }) => void; + selectField: (fieldName: string) => void; + deselectField: (fieldName: string) => void; pickerOpen: boolean; setPickerOpen: (open: boolean) => void; -} - -export function FieldManager({ state, dispatch, pickerOpen, setPickerOpen }: FieldManagerProps) { - const fieldMap = fieldMapSelector(state); - const allFields = fieldsSelector(state); - const selectedFields = selectedFieldsSelector(state); - - const actionCreators = bindActionCreators( - { - updateFieldProperties, - selectField, - deselectField, - }, - dispatch - ); - +}) { return ( - - {selectedFields.map(field => ( + + {props.selectedFields.map(field => ( - + ))} - + ); } + +export const FieldManager = connect( + (state: GraphState) => ({ + fieldMap: fieldMapSelector(state), + allFields: fieldsSelector(state), + selectedFields: selectedFieldsSelector(state), + }), + dispatch => + bindActionCreators( + { + updateFieldProperties, + selectField, + deselectField, + }, + dispatch + ) +)(FieldManagerComponent); diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index 62d8bbb03bc3f..840280a754154 100644 --- a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -9,13 +9,25 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiLink } from ' import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { FormattedMessage } from '@kbn/i18n/react'; +import { connect } from 'react-redux'; +import { IDataPluginServices } from 'src/legacy/core_plugins/data/public/types'; +import { + GraphState, + hasDatasourceSelector, + hasFieldsSelector, + requestDatasource, +} from '../../state_management'; +import { IndexPatternSavedObject } from '../../types'; +import { openSourceModal } from '../../services/source_modal'; + +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; export interface GuidancePanelProps { onFillWorkspace: () => void; onOpenFieldPicker: () => void; - onOpenDatasourcePicker: () => void; hasDatasource: boolean; hasFields: boolean; + onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => void; } function ListItem({ @@ -47,15 +59,23 @@ function ListItem({ ); } -export function GuidancePanel(props: GuidancePanelProps) { +function GuidancePanelComponent(props: GuidancePanelProps) { const { onFillWorkspace, onOpenFieldPicker, - onOpenDatasourcePicker, + onIndexPatternSelected, hasDatasource, hasFields, } = props; + const kibana = useKibana(); + const { overlays, savedObjects, uiSettings } = kibana.services; + if (!overlays) return null; + + const onOpenDatasourcePicker = () => { + openSourceModal({ overlays, savedObjects, uiSettings }, onIndexPatternSelected); + }; + return ( @@ -141,3 +161,23 @@ export function GuidancePanel(props: GuidancePanelProps) { ); } + +export const GuidancePanel = connect( + (state: GraphState) => { + return { + hasDatasource: hasDatasourceSelector(state), + hasFields: hasFieldsSelector(state), + }; + }, + dispatch => ({ + onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => { + dispatch( + requestDatasource({ + type: 'indexpattern', + id: indexPattern.id, + title: indexPattern.attributes.title, + }) + ); + }, + }) +)(GuidancePanelComponent); diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx index dbad0e01078fd..3c37c77e6d450 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchBar, SearchBarProps } from './search_bar'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { SearchBar, OuterSearchBarProps } from './search_bar'; import React, { ReactElement } from 'react'; import { CoreStart } from 'src/core/public'; import { act } from 'react-dom/test-utils'; -import { IndexPattern, QueryBarInput } from 'src/legacy/core_plugins/data/public'; +import { QueryBarInput, IndexPattern } from 'src/legacy/core_plugins/data/public'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { I18nProvider } from '@kbn/i18n/react'; @@ -16,11 +17,19 @@ import { I18nProvider } from '@kbn/i18n/react'; jest.mock('ui/new_platform'); import { openSourceModal } from '../services/source_modal'; -import { mount } from 'enzyme'; +import { GraphStore, setDatasource } from '../state_management'; +import { ReactWrapper } from 'enzyme'; +import { createMockGraphStore } from '../state_management/mocks'; +import { Provider } from 'react-redux'; jest.mock('../services/source_modal', () => ({ openSourceModal: jest.fn() })); +jest.mock('../../../../../../src/legacy/core_plugins/data/public', () => ({ + QueryBarInput: () => null, +})); -function wrapSearchBarInContext(testProps: SearchBarProps) { +const waitForIndexPatternFetch = () => new Promise(r => setTimeout(r)); + +function wrapSearchBarInContext(testProps: OuterSearchBarProps) { const services = { uiSettings: { get: (key: string) => { @@ -43,17 +52,47 @@ function wrapSearchBarInContext(testProps: SearchBarProps) { } describe('search_bar', () => { - it('should render search bar and submit queryies', () => { - const querySubmit = jest.fn(); - const instance = mount( - wrapSearchBarInContext({ - isLoading: false, - onIndexPatternSelected: () => {}, - onQuerySubmit: querySubmit, - currentIndexPattern: { title: 'Testpattern' } as IndexPattern, - coreStart: {} as CoreStart, + const defaultProps = { + isLoading: false, + onQuerySubmit: jest.fn(), + indexPatternProvider: { + get: jest.fn(() => Promise.resolve(({ fields: [] } as unknown) as IndexPattern)), + }, + confirmWipeWorkspace: (callback: () => void) => { + callback(); + }, + }; + let instance: ReactWrapper; + let store: GraphStore; + + beforeEach(() => { + store = createMockGraphStore({ includeSagas: false }).store; + store.dispatch( + setDatasource({ + type: 'indexpattern', + id: '123', + title: 'test-index', }) ); + }); + + function mountSearchBar() { + jest.clearAllMocks(); + const wrappedSearchBar = wrapSearchBarInContext({ ...defaultProps }); + instance = mountWithIntl({wrappedSearchBar}); + } + + it('should render search bar and fetch index pattern', () => { + mountSearchBar(); + + expect(defaultProps.indexPatternProvider.get).toHaveBeenCalledWith('123'); + }); + + it('should render search bar and submit queries', async () => { + mountSearchBar(); + + await waitForIndexPatternFetch(); + act(() => { instance.find(QueryBarInput).prop('onChange')!({ language: 'lucene', query: 'testQuery' }); }); @@ -62,20 +101,14 @@ describe('search_bar', () => { instance.find('form').simulate('submit', { preventDefault: () => {} }); }); - expect(querySubmit).toHaveBeenCalledWith('testQuery'); + expect(defaultProps.onQuerySubmit).toHaveBeenCalledWith('testQuery'); }); - it('should translate kql query into JSON dsl', () => { - const querySubmit = jest.fn(); - const instance = mount( - wrapSearchBarInContext({ - isLoading: false, - onIndexPatternSelected: () => {}, - onQuerySubmit: querySubmit, - currentIndexPattern: { title: 'Testpattern', fields: [{ name: 'test' }] } as IndexPattern, - coreStart: {} as CoreStart, - }) - ); + it('should translate kql query into JSON dsl', async () => { + mountSearchBar(); + + await waitForIndexPatternFetch(); + act(() => { instance.find(QueryBarInput).prop('onChange')!({ language: 'kuery', query: 'test: abc' }); }); @@ -84,24 +117,14 @@ describe('search_bar', () => { instance.find('form').simulate('submit', { preventDefault: () => {} }); }); - const parsedQuery = JSON.parse(querySubmit.mock.calls[0][0]); + const parsedQuery = JSON.parse(defaultProps.onQuerySubmit.mock.calls[0][0]); expect(parsedQuery).toEqual({ bool: { should: [{ match: { test: 'abc' } }], minimum_should_match: 1 }, }); }); it('should open index pattern picker', () => { - const indexPatternSelected = jest.fn(); - - const instance = mount( - wrapSearchBarInContext({ - isLoading: false, - onIndexPatternSelected: indexPatternSelected, - onQuerySubmit: () => {}, - currentIndexPattern: { title: 'Testpattern' } as IndexPattern, - coreStart: {} as CoreStart, - }) - ); + mountSearchBar(); // pick the button component out of the tree because // it's part of a popover and thus not covered by enzyme diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx index 18eca326776f5..ae0d32cd5f686 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx @@ -5,28 +5,39 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; +import { connect } from 'react-redux'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { IDataPluginServices } from 'src/legacy/core_plugins/data/public/types'; -import { CoreStart } from 'kibana/public'; +import { IndexPatternSavedObject, IndexPatternProvider } from '../types'; import { QueryBarInput, Query, IndexPattern, } from '../../../../../../src/legacy/core_plugins/data/public'; -import { IndexPatternSavedObject } from '../types/app_state'; import { openSourceModal } from '../services/source_modal'; +import { + GraphState, + datasourceSelector, + requestDatasource, + IndexpatternDatasource, +} from '../state_management'; + import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -export interface SearchBarProps { - coreStart: CoreStart; +export interface OuterSearchBarProps { isLoading: boolean; - currentIndexPattern?: IndexPattern; initialQuery?: string; - onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => void; onQuerySubmit: (query: string) => void; + confirmWipeWorkspace: (onConfirm: () => void) => void; + indexPatternProvider: IndexPatternProvider; +} + +export interface SearchBarProps extends OuterSearchBarProps { + currentDatasource?: IndexpatternDatasource; + onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => void; } function queryToString(query: Query, indexPattern: IndexPattern) { @@ -45,17 +56,34 @@ function queryToString(query: Query, indexPattern: IndexPattern) { return JSON.stringify(query.query); } -export function SearchBar(props: SearchBarProps) { +export function SearchBarComponent(props: SearchBarProps) { const { - currentIndexPattern, + currentDatasource, onQuerySubmit, isLoading, onIndexPatternSelected, initialQuery, + indexPatternProvider, + confirmWipeWorkspace, } = props; const [query, setQuery] = useState({ language: 'kuery', query: initialQuery || '' }); + const [currentIndexPattern, setCurrentIndexPattern] = useState( + undefined + ); + + useEffect(() => { + async function fetchPattern() { + if (currentDatasource) { + setCurrentIndexPattern(await indexPatternProvider.get(currentDatasource.id)); + } else { + setCurrentIndexPattern(undefined); + } + } + fetchPattern(); + }, [currentDatasource]); + const kibana = useKibana(); - const { overlays } = kibana.services; + const { overlays, savedObjects, uiSettings } = kibana.services; if (!overlays) return null; return ( @@ -88,7 +116,12 @@ export function SearchBar(props: SearchBarProps) { className="gphSearchBar__datasourceButton" data-test-subj="graphDatasourceButton" onClick={() => { - openSourceModal(props.coreStart, onIndexPatternSelected); + confirmWipeWorkspace(() => + openSourceModal( + { overlays, savedObjects, uiSettings }, + onIndexPatternSelected + ) + ); }} > {currentIndexPattern @@ -113,3 +146,24 @@ export function SearchBar(props: SearchBarProps) { ); } + +export const SearchBar = connect( + (state: GraphState) => { + const datasource = datasourceSelector(state); + return { + currentDatasource: + datasource.current.type === 'indexpattern' ? datasource.current : undefined, + }; + }, + dispatch => ({ + onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => { + dispatch( + requestDatasource({ + type: 'indexpattern', + id: indexPattern.id, + title: indexPattern.attributes.title, + }) + ); + }, + }) +)(SearchBarComponent); diff --git a/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx b/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx index f963d53c639c4..e6a99e909c69e 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx @@ -23,11 +23,11 @@ type NumberKeys = Exclude< export function AdvancedSettingsForm({ advancedSettings, - updateAdvancedSettings, + updateSettings, allFields, -}: Pick) { +}: Pick) { function updateSetting(key: K, value: AdvancedSettings[K]) { - updateAdvancedSettings({ ...advancedSettings, [key]: value }); + updateSettings({ ...advancedSettings, [key]: value }); } function getNumberUpdater>(key: K) { diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx b/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx index a0c95a894ee84..782b26a558249 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx @@ -8,21 +8,44 @@ import React from 'react'; import { EuiTab, EuiListGroupItem, EuiButton, EuiAccordion, EuiFieldText } from '@elastic/eui'; import * as Rx from 'rxjs'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { Settings, SettingsProps } from './settings'; +import { Settings, AngularProps } from './settings'; import { act } from 'react-testing-library'; import { ReactWrapper } from 'enzyme'; import { UrlTemplateForm } from './url_template_form'; +import { + GraphStore, + updateSettings, + loadFields, + saveTemplate, + removeTemplate, +} from '../../state_management'; +import { createMockGraphStore } from '../../state_management/mocks'; +import { Provider } from 'react-redux'; +import { UrlTemplate } from '../../types'; describe('settings', () => { - const props: jest.Mocked = { - advancedSettings: { - maxValuesPerDoc: 5, - minDocCount: 10, - sampleSize: 12, - useSignificance: true, - timeoutMillis: 10000, + let store: GraphStore; + let dispatchSpy: jest.Mock; + + const initialTemplate: UrlTemplate = { + description: 'template', + encoder: { + description: 'test encoder description', + encode: jest.fn(), + id: 'test', + title: 'test encoder', + type: 'esq', + }, + url: 'http://example.org', + icon: { + class: 'test', + code: '1', + label: 'test', }, - updateAdvancedSettings: jest.fn(), + isDefault: false, + }; + + const angularProps: jest.Mocked = { blacklistedNodes: [ { x: 0, @@ -60,59 +83,63 @@ describe('settings', () => { }, ], unblacklistNode: jest.fn(), - urlTemplates: [ - { - description: 'template', - encoder: { - description: 'test encoder description', - encode: jest.fn(), - id: 'test', - title: 'test encoder', - type: 'esq', - }, - url: 'http://example.org', - icon: { - class: 'test', - code: '1', - label: 'test', - }, - }, - ], - removeUrlTemplate: jest.fn(), - saveUrlTemplate: jest.fn(), - allFields: [ - { - selected: false, - color: 'black', - name: 'B', - type: 'string', - icon: { - class: 'test', - code: '1', - label: 'test', - }, - }, - { - selected: false, - color: 'red', - name: 'C', - type: 'string', - icon: { - class: 'test', - code: '1', - label: 'test', - }, - }, - ], canEditDrillDownUrls: true, }; - let subject: Rx.BehaviorSubject>; + let subject: Rx.BehaviorSubject>; let instance: ReactWrapper; beforeEach(() => { - subject = new Rx.BehaviorSubject(props); - instance = mountWithIntl(); + store = createMockGraphStore({ includeSagas: false }).store; + store.dispatch( + updateSettings({ + maxValuesPerDoc: 5, + minDocCount: 10, + sampleSize: 12, + useSignificance: true, + timeoutMillis: 10000, + }) + ); + store.dispatch( + loadFields([ + { + selected: false, + color: 'black', + name: 'B', + type: 'string', + icon: { + class: 'test', + code: '1', + label: 'test', + }, + }, + { + selected: false, + color: 'red', + name: 'C', + type: 'string', + icon: { + class: 'test', + code: '1', + label: 'test', + }, + }, + ]) + ); + store.dispatch( + saveTemplate({ + index: -1, + template: initialTemplate, + }) + ); + dispatchSpy = jest.fn(store.dispatch); + store.dispatch = dispatchSpy; + subject = new Rx.BehaviorSubject(angularProps); + instance = mountWithIntl( + + + + ); }); function toTab(tab: string) { @@ -139,25 +166,14 @@ describe('settings', () => { HTMLInputElement >); - expect(props.updateAdvancedSettings).toHaveBeenCalledWith( - expect.objectContaining({ sampleSize: 13 }) - ); - }); - - it('should update on new data', () => { - act(() => { - subject.next({ - ...props, - advancedSettings: { - ...props.advancedSettings, + expect(dispatchSpy).toHaveBeenCalledWith( + updateSettings( + expect.objectContaining({ + timeoutMillis: 10000, sampleSize: 13, - }, - }); - }); - - instance.update(); - - expect(input('Sample size').prop('value')).toEqual(13); + }) + ) + ); }); }); @@ -173,13 +189,46 @@ describe('settings', () => { ]); }); + it('should update on new data', () => { + act(() => { + subject.next({ + ...angularProps, + blacklistedNodes: [ + { + x: 0, + y: 0, + scaledSize: 10, + parent: null, + color: 'black', + data: { + field: 'A', + term: '1', + }, + label: 'blacklisted node 3', + icon: { + class: 'test', + code: '1', + label: 'test', + }, + }, + ], + }); + }); + + instance.update(); + + expect(instance.find(EuiListGroupItem).map(item => item.prop('label'))).toEqual([ + 'blacklisted node 3', + ]); + }); + it('should delete node', () => { instance .find(EuiListGroupItem) .at(0) .prop('extraAction')!.onClick!({} as any); - expect(props.unblacklistNode).toHaveBeenCalledWith(props.blacklistedNodes![0]); + expect(angularProps.unblacklistNode).toHaveBeenCalledWith(angularProps.blacklistedNodes![0]); }); it('should delete all nodes', () => { @@ -188,8 +237,8 @@ describe('settings', () => { .find(EuiButton) .simulate('click'); - expect(props.unblacklistNode).toHaveBeenCalledWith(props.blacklistedNodes![0]); - expect(props.unblacklistNode).toHaveBeenCalledWith(props.blacklistedNodes![1]); + expect(angularProps.unblacklistNode).toHaveBeenCalledWith(angularProps.blacklistedNodes![0]); + expect(angularProps.unblacklistNode).toHaveBeenCalledWith(angularProps.blacklistedNodes![1]); }); }); @@ -226,7 +275,7 @@ describe('settings', () => { templateForm(0) .find('EuiButtonEmpty[data-test-subj="graphRemoveUrlTemplate"]') .simulate('click'); - expect(props.removeUrlTemplate).toHaveBeenCalledWith(props.urlTemplates[0]); + expect(dispatchSpy).toHaveBeenCalledWith(removeTemplate(initialTemplate)); }); it('should update url template', () => { @@ -236,10 +285,9 @@ describe('settings', () => { .find('form') .simulate('submit'); }); - expect(props.saveUrlTemplate).toHaveBeenCalledWith(0, { - ...props.urlTemplates[0], - description: 'Updated title', - }); + expect(dispatchSpy).toHaveBeenCalledWith( + saveTemplate({ index: 0, template: { ...initialTemplate, description: 'Updated title' } }) + ); }); it('should add url template', async () => { @@ -256,9 +304,11 @@ describe('settings', () => { .find('form') .simulate('submit'); }); - expect(props.saveUrlTemplate).toHaveBeenCalledWith( - -1, - expect.objectContaining({ description: 'Title', url: 'test-url' }) + expect(dispatchSpy).toHaveBeenCalledWith( + saveTemplate({ + index: -1, + template: expect.objectContaining({ description: 'Title', url: 'test-url' }), + }) ); }); }); diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx b/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx index 4cb4b473f7372..4dab0ece5b52d 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx @@ -8,10 +8,21 @@ import { i18n } from '@kbn/i18n'; import React, { useState, useEffect } from 'react'; import { EuiFlyoutHeader, EuiTitle, EuiTabs, EuiFlyoutBody, EuiTab } from '@elastic/eui'; import * as Rx from 'rxjs'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { AdvancedSettingsForm } from './advanced_settings_form'; import { BlacklistForm } from './blacklist_form'; import { UrlTemplateList } from './url_template_list'; import { WorkspaceNode, AdvancedSettings, UrlTemplate, WorkspaceField } from '../../types'; +import { + GraphState, + settingsSelector, + templatesSelector, + fieldsSelector, + updateSettings, + saveTemplate, + removeTemplate, +} from '../../state_management'; const tabs = [ { @@ -35,31 +46,46 @@ const tabs = [ }, ]; -export interface SettingsProps { +/** + * These props are wired in the angular scope and are passed in via observable + * to catch update outside updates + */ +export interface AngularProps { + blacklistedNodes: WorkspaceNode[]; + unblacklistNode: (node: WorkspaceNode) => void; + canEditDrillDownUrls: boolean; +} + +export interface StateProps { advancedSettings: AdvancedSettings; - updateAdvancedSettings: (advancedSettings: AdvancedSettings) => void; - blacklistedNodes?: WorkspaceNode[]; - unblacklistNode?: (node: WorkspaceNode) => void; urlTemplates: UrlTemplate[]; - removeUrlTemplate: (urlTemplate: UrlTemplate) => void; - saveUrlTemplate: (index: number, urlTemplate: UrlTemplate) => void; allFields: WorkspaceField[]; - canEditDrillDownUrls: boolean; +} + +export interface DispatchProps { + updateSettings: (advancedSettings: AdvancedSettings) => void; + removeTemplate: (urlTemplate: UrlTemplate) => void; + saveTemplate: (props: { index: number; template: UrlTemplate }) => void; } interface AsObservable

{ observable: Readonly>; } -export function Settings({ observable }: AsObservable) { - const [currentProps, setCurrentProps] = useState(undefined); +export interface SettingsProps extends AngularProps, StateProps, DispatchProps {} + +export function SettingsComponent({ + observable, + ...props +}: AsObservable & StateProps & DispatchProps) { + const [angularProps, setAngularProps] = useState(undefined); const [activeTab, setActiveTab] = useState(0); useEffect(() => { - observable.subscribe(setCurrentProps); + observable.subscribe(setAngularProps); }, [observable]); - if (!currentProps) return null; + if (!angularProps) return null; const ActiveTabContent = tabs[activeTab].component; return ( @@ -70,7 +96,7 @@ export function Settings({ observable }: AsObservable) { {tabs - .filter(({ id }) => id !== 'drillDowns' || currentProps.canEditDrillDownUrls) + .filter(({ id }) => id !== 'drillDowns' || angularProps.canEditDrillDownUrls) .map(({ title }, index) => ( ) { - + ); } + +export const Settings = connect, GraphState>( + (state: GraphState) => ({ + advancedSettings: settingsSelector(state), + urlTemplates: templatesSelector(state), + allFields: fieldsSelector(state), + }), + dispatch => + bindActionCreators( + { + updateSettings, + saveTemplate, + removeTemplate, + }, + dispatch + ) +)(SettingsComponent); diff --git a/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx b/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx index 2e41f78bb9403..1946c18b41425 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx @@ -14,10 +14,10 @@ import { useListKeys } from './use_list_keys'; const generateId = htmlIdGenerator(); export function UrlTemplateList({ - removeUrlTemplate, - saveUrlTemplate, + removeTemplate, + saveTemplate, urlTemplates, -}: Pick) { +}: Pick) { const [uncommittedForms, setUncommittedForms] = useState([]); const getListKey = useListKeys(urlTemplates); @@ -40,10 +40,10 @@ export function UrlTemplateList({ id={getListKey(template)} initialTemplate={template} onSubmit={newTemplate => { - saveUrlTemplate(index, newTemplate); + saveTemplate({ index, template: newTemplate }); }} onRemove={() => { - removeUrlTemplate(template); + removeTemplate(template); }} /> ))} @@ -53,7 +53,7 @@ export function UrlTemplateList({ id={`accordion-new-${id}`} key={id} onSubmit={newTemplate => { - saveUrlTemplate(-1, newTemplate); + saveTemplate({ index: -1, template: newTemplate }); removeUncommittedForm(id); }} onRemove={removeUncommittedForm.bind(undefined, id)} diff --git a/x-pack/legacy/plugins/graph/public/helpers/url_template.ts b/x-pack/legacy/plugins/graph/public/helpers/url_template.ts index bb85d2e8c5836..f982d0443c39a 100644 --- a/x-pack/legacy/plugins/graph/public/helpers/url_template.ts +++ b/x-pack/legacy/plugins/graph/public/helpers/url_template.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export const urlTemplatePlaceholder = '{{gquery}}'; export const urlTemplateRegex = /\{\{gquery\}\}/g; const defaultKibanaQuery = /,query:\(language:kuery,query:'.*?'\)/g; @@ -34,5 +35,8 @@ export function isKibanaUrl(url: string) { * @param url The url to turn into an url template */ export function replaceKibanaUrlParam(url: string) { - return url.replace(defaultKibanaQuery, ',query:(language:kuery,query:{{gquery}})'); + return url.replace( + defaultKibanaQuery, + `,query:(language:kuery,query:{{${urlTemplatePlaceholder}}})` + ); } diff --git a/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts b/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts new file mode 100644 index 0000000000000..e4e02f860db14 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts @@ -0,0 +1,23 @@ +/* + * 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 { IndexPattern } from 'src/legacy/core_plugins/data/public'; +import { IndexPatternProvider } from '../types'; + +export function createCachedIndexPatternProvider( + indexPatternGetter: (id: string) => Promise +): IndexPatternProvider { + const cache = new Map(); + + return { + get: async (id: string) => { + if (!cache.has(id)) { + cache.set(id, await indexPatternGetter(id)); + } + return Promise.resolve(cache.get(id)!); + }, + }; +} diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts index 4575b596d65d9..6aa9fd671ffcb 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { GraphWorkspaceSavedObject } from '../../types'; +import { GraphWorkspaceSavedObject, Workspace } from '../../types'; import { savedWorkspaceToAppState } from './deserialize'; import { IndexPattern } from 'src/legacy/core_plugins/data/public'; import { createWorkspace } from '../../angular/graph_client_workspace'; @@ -12,6 +12,7 @@ import { outlinkEncoders } from '../../helpers/outlink_encoders'; describe('deserialize', () => { let savedWorkspace: GraphWorkspaceSavedObject; + let workspace: Workspace; beforeEach(() => { savedWorkspace = { @@ -110,6 +111,7 @@ describe('deserialize', () => { }, }), } as GraphWorkspaceSavedObject; + workspace = createWorkspace({}); }); function callSavedWorkspaceToAppState() { @@ -122,7 +124,7 @@ describe('deserialize', () => { { name: 'field3', type: 'string' }, ], } as IndexPattern, - createWorkspace({}) + workspace ); } @@ -133,7 +135,7 @@ describe('deserialize', () => { }); it('should deserialize fields', () => { - const { allFields, selectedFields } = callSavedWorkspaceToAppState(); + const { allFields } = callSavedWorkspaceToAppState(); expect(allFields).toMatchInlineSnapshot(` Array [ @@ -175,9 +177,6 @@ describe('deserialize', () => { }, ] `); - - expect(selectedFields.length).toEqual(2); - selectedFields.forEach(field => expect(allFields.includes(field)).toEqual(true)); }); it('should deserialize url templates', () => { @@ -188,7 +187,7 @@ describe('deserialize', () => { }); it('should deserialize nodes and edges', () => { - const { workspace } = callSavedWorkspaceToAppState(); + callSavedWorkspaceToAppState(); expect(workspace.blacklistedNodes.length).toEqual(1); expect(workspace.nodes.length).toEqual(5); diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts index 39c7ef841dcb6..b1879ec92c131 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts @@ -6,7 +6,6 @@ import { IndexPattern } from 'src/legacy/core_plugins/data/public/index_patterns/index_patterns'; import { - AppState, SerializedNode, UrlTemplate, SerializedUrlTemplate, @@ -188,10 +187,11 @@ export function savedWorkspaceToAppState( savedWorkspace: GraphWorkspaceSavedObject, indexPattern: IndexPattern, workspaceInstance: Workspace -): Pick< - AppState, - 'urlTemplates' | 'advancedSettings' | 'workspace' | 'allFields' | 'selectedFields' -> { +): { + urlTemplates: UrlTemplate[]; + advancedSettings: AdvancedSettings; + allFields: WorkspaceField[]; +} { const persistedWorkspaceState: SerializedWorkspaceState = JSON.parse(savedWorkspace.wsState); // ================== url templates ============================= @@ -205,9 +205,11 @@ export function savedWorkspaceToAppState( persistedWorkspaceState.selectedFields ); const selectedFields = allFields.filter(field => field.selected); + workspaceInstance.options.vertex_fields = selectedFields; // ================== advanced settings ============================= const advancedSettings = Object.assign( + {}, defaultAdvancedSettings, persistedWorkspaceState.exploreControls ); @@ -220,6 +222,8 @@ export function savedWorkspaceToAppState( ); } + workspaceInstance.options.exploreControls = advancedSettings; + // ================== nodes and edges ============================= const graph = getNodesAndEdges(persistedWorkspaceState, allFields); workspaceInstance.mergeGraph(graph); @@ -232,8 +236,6 @@ export function savedWorkspaceToAppState( return { urlTemplates, advancedSettings, - workspace: workspaceInstance, allFields, - selectedFields, }; } diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts index b3450cce05c0b..95f55bcc87eb7 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts @@ -5,12 +5,25 @@ */ import { appStateToSavedWorkspace } from './serialize'; -import { GraphWorkspaceSavedObject, Workspace, WorkspaceEdge, AppState } from '../../types'; +import { + GraphWorkspaceSavedObject, + Workspace, + WorkspaceEdge, + UrlTemplate, + AdvancedSettings, + WorkspaceField, +} from '../../types'; import { outlinkEncoders } from '../../helpers/outlink_encoders'; -import { IndexPattern } from 'src/legacy/core_plugins/data/public'; +import { IndexpatternDatasource } from '../../state_management'; describe('serialize', () => { - let appState: AppState; + let appState: { + workspace: Workspace; + urlTemplates: UrlTemplate[]; + advancedSettings: AdvancedSettings; + selectedIndex: IndexpatternDatasource; + selectedFields: WorkspaceField[]; + }; beforeEach(() => { appState = { @@ -21,29 +34,6 @@ describe('serialize', () => { maxValuesPerDoc: 1, minDocCount: 3, }, - allFields: [ - { - color: 'black', - icon: { class: 'a', code: '', label: '' }, - name: 'field1', - selected: true, - type: 'string', - }, - { - color: 'black', - icon: { class: 'b', code: '', label: '' }, - name: 'field2', - selected: true, - type: 'string', - }, - { - color: 'black', - icon: { class: 'c', code: '', label: '' }, - name: 'field3', - selected: false, - type: 'string', - }, - ], selectedFields: [ { color: 'black', @@ -61,8 +51,10 @@ describe('serialize', () => { }, ], selectedIndex: { + type: 'indexpattern', + id: '123', title: 'Testindexpattern', - } as IndexPattern, + }, urlTemplates: [ { description: 'Template', diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts index 21583781cb14b..3a94136d26e7c 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts @@ -5,7 +5,6 @@ */ import { - AppState, SerializedNode, WorkspaceNode, WorkspaceEdge, @@ -15,7 +14,10 @@ import { WorkspaceField, GraphWorkspaceSavedObject, SerializedWorkspaceState, + Workspace, + AdvancedSettings, } from '../../types'; +import { IndexpatternDatasource } from '../../state_management'; function serializeNode( { data, scaledSize, parent, x, y, label, color }: WorkspaceNode, @@ -80,7 +82,19 @@ function serializeField({ export function appStateToSavedWorkspace( currentSavedWorkspace: GraphWorkspaceSavedObject, - { workspace, urlTemplates, advancedSettings, selectedIndex, selectedFields }: AppState, + { + workspace, + urlTemplates, + advancedSettings, + selectedIndex, + selectedFields, + }: { + workspace: Workspace; + urlTemplates: UrlTemplate[]; + advancedSettings: AdvancedSettings; + selectedIndex: IndexpatternDatasource; + selectedFields: WorkspaceField[]; + }, canSaveData: boolean ) { const blacklist: SerializedNode[] = canSaveData diff --git a/x-pack/legacy/plugins/graph/public/services/save_modal.tsx b/x-pack/legacy/plugins/graph/public/services/save_modal.tsx index bb453bd95df5e..5930d2283b7c0 100644 --- a/x-pack/legacy/plugins/graph/public/services/save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/services/save_modal.tsx @@ -9,6 +9,15 @@ import { SaveResult } from 'ui/saved_objects/show_saved_object_save_modal'; import { GraphWorkspaceSavedObject, GraphSavePolicy } from '../types'; import { SaveModal, OnSaveGraphProps } from '../components/save_modal'; +export type SaveWorkspaceHandler = ( + saveOptions: { + confirmOverwrite: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + }, + dataConsent: boolean +) => Promise; + export function openSaveModal({ savePolicy, hasData, @@ -19,14 +28,7 @@ export function openSaveModal({ savePolicy: GraphSavePolicy; hasData: boolean; workspace: GraphWorkspaceSavedObject; - saveWorkspace: ( - saveOptions: { - confirmOverwrite: boolean; - isTitleDuplicateConfirmed: boolean; - onTitleDuplicate: () => void; - }, - dataConsent: boolean - ) => Promise; + saveWorkspace: SaveWorkspaceHandler; showSaveModal: (el: React.ReactNode) => void; }) { const currentTitle = workspace.title; diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/legacy/plugins/graph/public/services/url.ts index 97a30e26c25f3..ff14faf3e350c 100644 --- a/x-pack/legacy/plugins/graph/public/services/url.ts +++ b/x-pack/legacy/plugins/graph/public/services/url.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { Chrome } from 'ui/chrome'; import { GraphWorkspaceSavedObject } from '../types'; +import { MetaDataState } from '../state_management'; export function getHomePath() { return '/home'; @@ -30,12 +31,12 @@ export type SetBreadcrumbOptions = } | { chrome: Chrome; - savedWorkspace?: GraphWorkspaceSavedObject; + metaData: MetaDataState; navigateTo: (path: string) => void; }; export function setBreadcrumbs(options: SetBreadcrumbOptions) { - if ('savedWorkspace' in options) { + if ('metaData' in options) { options.chrome.breadcrumbs.set([ { text: i18n.translate('xpack.graph.home.breadcrumb', { @@ -47,11 +48,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { 'data-test-subj': 'graphHomeBreadcrumb', }, { - text: options.savedWorkspace - ? options.savedWorkspace.title - : i18n.translate('xpack.graph.newWorkspaceTitle', { - defaultMessage: 'Unsaved workspace', - }), + text: options.metaData.title, 'data-test-subj': 'graphCurrentWorkspaceBreadcrumb', }, ]); diff --git a/x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts b/x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts new file mode 100644 index 0000000000000..44950f8a45f85 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts @@ -0,0 +1,54 @@ +/* + * 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 actionCreatorFactory, { Action } from 'typescript-fsa'; +import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; +import { takeLatest } from 'redux-saga/effects'; +import { GraphState, GraphStoreDependencies } from './store'; +import { AdvancedSettings } from '../types'; +import { reset } from './global'; + +const actionCreator = actionCreatorFactory('x-pack/graph/advancedSettings'); + +export type AdvancedSettingsState = AdvancedSettings; + +export const updateSettings = actionCreator('UPDATE_SETTINGS'); + +const initialSettings: AdvancedSettingsState = { + useSignificance: true, + sampleSize: 2000, + timeoutMillis: 5000, + sampleDiversityField: undefined, + maxValuesPerDoc: 1, + minDocCount: 3, +}; + +export const advancedSettingsReducer = reducerWithInitialState(initialSettings) + .case(reset, () => initialSettings) + .case(updateSettings, (_oldSettings, newSettings) => newSettings) + .build(); + +export const settingsSelector = (state: GraphState) => state.advancedSettings; + +/** + * Saga making sure the advanced settings are always synced up to the workspace instance. + * + * Won't be necessary once the workspace is moved to redux + */ +export const syncSettingsSaga = ({ getWorkspace, notifyAngular }: GraphStoreDependencies) => { + function* syncSettings(action: Action): IterableIterator { + const workspace = getWorkspace(); + if (!workspace) { + return; + } + workspace.options.exploreControls = action.payload; + notifyAngular(); + } + + return function*() { + yield takeLatest(updateSettings.match, syncSettings); + }; +}; diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.ts b/x-pack/legacy/plugins/graph/public/state_management/datasource.ts new file mode 100644 index 0000000000000..801dd315df30e --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/datasource.ts @@ -0,0 +1,110 @@ +/* + * 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 actionCreatorFactory, { Action } from 'typescript-fsa'; +import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; +import { takeLatest, put, call, select } from 'redux-saga/effects'; +import { i18n } from '@kbn/i18n'; +import { IndexPattern } from 'src/legacy/core_plugins/data/public'; +import { createSelector } from 'reselect'; +import { GraphState, GraphStoreDependencies } from './store'; +import { reset } from './global'; +import { loadFields } from './fields'; +import { mapFields } from '../services/persistence'; +import { settingsSelector } from './advanced_settings'; + +const actionCreator = actionCreatorFactory('x-pack/graph/datasource'); + +export interface NoDatasource { + type: 'none'; +} +export interface IndexpatternDatasource { + type: 'indexpattern'; + id: string; + title: string; +} + +export interface DatasourceState { + current: NoDatasource | IndexpatternDatasource; + loading: boolean; +} + +/** + * Sets the current datasource. This will not trigger a load of fields + */ +export const setDatasource = actionCreator('SET_DATASOURCE'); + +/** + * Sets the current datasource. This will trigger a load of fields and overwrite the current + * fields configuration + */ +export const requestDatasource = actionCreator('SET_DATASOURCE_REQUEST'); + +/** + * Datasource loading finished successfully. + */ +export const datasourceLoaded = actionCreator('SET_DATASOURCE_SUCCESS'); + +const initialDatasource: DatasourceState = { + current: { type: 'none' }, + loading: false, +}; + +export const datasourceReducer = reducerWithInitialState(initialDatasource) + .case(reset, () => initialDatasource) + .case(setDatasource, (_oldDatasource, newDatasource) => ({ + current: newDatasource, + loading: false, + })) + .case(requestDatasource, (_oldDatasource, newDatasource) => ({ + current: newDatasource, + loading: true, + })) + .case(datasourceLoaded, datasource => ({ + ...datasource, + loading: false, + })) + .build(); + +export const datasourceSelector = (state: GraphState) => state.datasource; +export const hasDatasourceSelector = createSelector( + datasourceSelector, + datasource => datasource.current.type !== 'none' +); + +/** + * Saga loading field information when the datasource is switched. This will overwrite current settings + * in fields. + * + * TODO: Carry over fields than can be carried over because they also exist in the target index pattern + */ +export const datasourceSaga = ({ + indexPatternProvider, + notifications, + createWorkspace, +}: GraphStoreDependencies) => { + function* fetchFields(action: Action) { + try { + const indexPattern: IndexPattern = yield call(indexPatternProvider.get, action.payload.id); + yield put(loadFields(mapFields(indexPattern))); + yield put(datasourceLoaded()); + const advancedSettings = settingsSelector(yield select()); + createWorkspace(indexPattern.title, advancedSettings); + } catch (e) { + // in case of errors, reset the datasource and show notification + yield put(setDatasource({ type: 'none' })); + notifications.toasts.addDanger( + i18n.translate('xpack.graph.loadWorkspace.missingIndexPatternErrorMessage', { + defaultMessage: 'Index pattern not found', + }) + ); + } + } + + return function*() { + yield takeLatest(requestDatasource.match, fetchFields); + }; +}; diff --git a/x-pack/legacy/plugins/graph/public/state_management/fields.ts b/x-pack/legacy/plugins/graph/public/state_management/fields.ts index 4708d17734ad0..62bb62c1f6125 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/fields.ts +++ b/x-pack/legacy/plugins/graph/public/state_management/fields.ts @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import actionCreatorFactory from 'typescript-fsa'; +import actionCreatorFactory, { Action } from 'typescript-fsa'; import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; import { createSelector } from 'reselect'; +import { select, takeLatest, takeEvery } from 'redux-saga/effects'; import { WorkspaceField } from '../types'; -import { GraphState } from './store'; +import { GraphState, GraphStoreDependencies } from './store'; +import { reset } from './global'; +import { setDatasource } from './datasource'; +import { matchesOne, InferActionType } from './helpers'; const actionCreator = actionCreatorFactory('x-pack/graph/fields'); @@ -25,6 +29,8 @@ export type FieldsState = Record; const initialFields: FieldsState = {}; export const fieldsReducer = reducerWithInitialState(initialFields) + .case(reset, () => initialFields) + .case(setDatasource, () => initialFields) .case(loadFields, (_currentFields, newFields) => { const newFieldMap: Record = {}; newFields.forEach(field => { @@ -57,3 +63,86 @@ export const liveResponseFieldsSelector = createSelector( selectedFieldsSelector, fields => fields.filter(field => field.hopSize && field.hopSize > 0) ); +export const hasFieldsSelector = createSelector( + selectedFieldsSelector, + fields => fields.length > 0 +); + +/** + * Saga making notifying angular when fields are selected to re-calculate the state of the save button. + * + * Won't be necessary once the workspace is moved to redux + */ +export const updateSaveButtonSaga = ({ notifyAngular }: GraphStoreDependencies) => { + function* notify(): IterableIterator { + notifyAngular(); + } + return function*() { + yield takeLatest(matchesOne(selectField, deselectField), notify); + }; +}; + +/** + * Saga making sure the fields in the store are always synced with the fields + * known to the workspace. + * + * Won't be necessary once the workspace is moved to redux + */ +export const syncFieldsSaga = ({ getWorkspace, setLiveResponseFields }: GraphStoreDependencies) => { + function* syncFields() { + const workspace = getWorkspace(); + if (!workspace) { + return; + } + + const currentState = yield select(); + workspace.options.vertex_fields = selectedFieldsSelector(currentState); + setLiveResponseFields(liveResponseFieldsSelector(currentState)); + } + return function*() { + yield takeEvery( + matchesOne(loadFields, selectField, deselectField, updateFieldProperties), + syncFields + ); + }; +}; + +/** + * Saga making sure the field styles (icons and colors) are applied to nodes currently active + * in the workspace. + * + * Won't be necessary once the workspace is moved to redux + */ +export const syncNodeStyleSaga = ({ getWorkspace, notifyAngular }: GraphStoreDependencies) => { + function* syncNodeStyle(action: Action>) { + const workspace = getWorkspace(); + if (!workspace) { + return; + } + const newColor = action.payload.fieldProperties.color; + if (newColor) { + workspace.nodes.forEach(function(node) { + if (node.data.field === action.payload.fieldName) { + node.color = newColor; + } + }); + } + const newIcon = action.payload.fieldProperties.icon; + + if (newIcon) { + workspace.nodes.forEach(function(node) { + if (node.data.field === action.payload.fieldName) { + node.icon = newIcon; + } + }); + } + notifyAngular(); + + const selectedFields = selectedFieldsSelector(yield select()); + workspace.options.vertex_fields = selectedFields; + } + + return function*() { + yield takeLatest(updateFieldProperties.match, syncNodeStyle); + }; +}; diff --git a/x-pack/legacy/plugins/graph/public/state_management/global.ts b/x-pack/legacy/plugins/graph/public/state_management/global.ts new file mode 100644 index 0000000000000..cfd1493a7e353 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/global.ts @@ -0,0 +1,10 @@ +/* + * 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 actionCreatorFactory from 'typescript-fsa'; +const actionCreator = actionCreatorFactory('x-pack/graph'); + +export const reset = actionCreator('RESET'); diff --git a/x-pack/legacy/plugins/graph/public/state_management/helpers.ts b/x-pack/legacy/plugins/graph/public/state_management/helpers.ts new file mode 100644 index 0000000000000..215691d454484 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/helpers.ts @@ -0,0 +1,28 @@ +/* + * 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 { ActionCreator, AnyAction } from 'typescript-fsa'; + +/** + * Infers the type of an action out of a given action type. + * This makes it easier to distribute the action types because they come + * along with the creators: `type MyAction = InferActionType`. + * + * This isn't expected to be used in a lot of places - if it is, naming the individual + * action types might make more sense. + */ +export type InferActionType = X extends ActionCreator ? T : never; + +/** + * Helper to create a matcher that matches all passed in action creators. + * + * This is helpful to create a saga that takes multiple actions: + * `yield takeEvery(matchesOne(actionCreator1, actionCreator2), handler);` + * + * @param actionCreators The action creators to create a unified matcher for + */ +export const matchesOne = (...actionCreators: Array>) => (action: AnyAction) => + actionCreators.some(actionCreator => actionCreator.match(action)); diff --git a/x-pack/legacy/plugins/graph/public/state_management/index.ts b/x-pack/legacy/plugins/graph/public/state_management/index.ts index 546af1673ea92..e577fed63a504 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/index.ts +++ b/x-pack/legacy/plugins/graph/public/state_management/index.ts @@ -5,4 +5,11 @@ */ export * from './fields'; +export * from './url_templates'; +export * from './advanced_settings'; +export * from './datasource'; +export * from './meta_data'; +export * from './persistence'; + +export * from './global'; export * from './store'; diff --git a/x-pack/legacy/plugins/graph/public/state_management/meta_data.ts b/x-pack/legacy/plugins/graph/public/state_management/meta_data.ts new file mode 100644 index 0000000000000..7d4ad4814081d --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/meta_data.ts @@ -0,0 +1,61 @@ +/* + * 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 actionCreatorFactory from 'typescript-fsa'; +import { reducerWithInitialState } from 'typescript-fsa-reducers'; +import { select, takeLatest, call } from 'redux-saga/effects'; +import { i18n } from '@kbn/i18n'; +import { GraphState, GraphStoreDependencies } from './store'; +import { reset } from './global'; +import { setBreadcrumbs } from '../services/url'; + +const actionCreator = actionCreatorFactory('x-pack/graph/metaData'); + +export interface MetaDataState { + title: string; + description: string; + savedObjectId?: string; +} + +export const updateMetaData = actionCreator>('UPDATE_META_DATA'); + +const initialMetaData: MetaDataState = { + title: i18n.translate('xpack.graph.newWorkspaceTitle', { + defaultMessage: 'Unsaved workspace', + }), + description: '', +}; + +export const metaDataReducer = reducerWithInitialState(initialMetaData) + .case(reset, () => initialMetaData) + .case(updateMetaData, (oldMetaData, newMetaData) => ({ ...oldMetaData, ...newMetaData })) + .build(); + +export const metaDataSelector = (state: GraphState) => state.metaData; + +/** + * Saga updating the breadcrumb when the shown workspace changes. + */ +export const syncBreadcrumbSaga = ({ chrome, changeUrl }: GraphStoreDependencies) => { + function* syncBreadcrumb() { + const metaData = metaDataSelector(yield select()); + setBreadcrumbs({ + chrome, + metaData, + navigateTo: (path: string) => { + // TODO this should be wrapped into canWipeWorkspace, + // but the check is too simple right now. Change this + // once actual state-diffing is in place. + changeUrl(path); + }, + }); + } + return function*() { + // initial sync + yield call(syncBreadcrumb); + yield takeLatest(updateMetaData.match, syncBreadcrumb); + }; +}; diff --git a/x-pack/legacy/plugins/graph/public/state_management/mocks.ts b/x-pack/legacy/plugins/graph/public/state_management/mocks.ts new file mode 100644 index 0000000000000..9d22fd521a96f --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/mocks.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Chrome } from 'ui/chrome'; +import { IndexPattern } from 'src/legacy/core_plugins/data/public'; +import { NotificationsStart } from 'kibana/public'; +import createSagaMiddleware from 'redux-saga'; +import { createStore, applyMiddleware } from 'redux'; +import { GraphStoreDependencies, createRootReducer, registerSagas } from './store'; +import { Workspace, GraphWorkspaceSavedObject, IndexPatternSavedObject } from '../types'; + +jest.mock('ui/new_platform'); + +export function createMockGraphStore({ includeSagas }: { includeSagas: boolean }) { + const mockedDeps: jest.Mocked = { + basePath: '', + changeUrl: jest.fn(), + chrome: ({ + breadcrumbs: { + set: jest.fn(), + }, + } as unknown) as Chrome, + createWorkspace: jest.fn(), + getWorkspace: jest.fn(() => (({} as unknown) as Workspace)), + getSavedWorkspace: jest.fn(() => (({} as unknown) as GraphWorkspaceSavedObject)), + indexPatternProvider: { + get: jest.fn(() => Promise.resolve(({} as unknown) as IndexPattern)), + }, + indexPatterns: [ + ({ id: '123', attributes: { title: 'test-pattern' } } as unknown) as IndexPatternSavedObject, + ], + notifications: ({ + toasts: { + addDanger: jest.fn(), + addSuccess: jest.fn(), + }, + } as unknown) as NotificationsStart, + notifyAngular: jest.fn(), + savePolicy: 'configAndDataWithConsent', + showSaveModal: jest.fn(), + setLiveResponseFields: jest.fn(), + }; + const sagaMiddleware = createSagaMiddleware(); + + const rootReducer = createRootReducer(''); + + const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); + + if (includeSagas) { + registerSagas(sagaMiddleware, mockedDeps); + } + + return { store, mockedDeps }; +} diff --git a/x-pack/legacy/plugins/graph/public/state_management/persistence.ts b/x-pack/legacy/plugins/graph/public/state_management/persistence.ts new file mode 100644 index 0000000000000..0b8130a766cd3 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/persistence.ts @@ -0,0 +1,203 @@ +/* + * 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 actionCreatorFactory, { Action } from 'typescript-fsa'; +import { i18n } from '@kbn/i18n'; +import { takeLatest, call, put, select, cps } from 'redux-saga/effects'; +import { GraphWorkspaceSavedObject, Workspace } from '../types'; +import { GraphStoreDependencies, GraphState } from '.'; +import { setDatasource, datasourceSelector, IndexpatternDatasource } from './datasource'; +import { loadFields, selectedFieldsSelector } from './fields'; +import { updateSettings, settingsSelector } from './advanced_settings'; +import { loadTemplates, templatesSelector } from './url_templates'; +import { + lookupIndexPattern, + savedWorkspaceToAppState, + appStateToSavedWorkspace, +} from '../services/persistence'; +import { updateMetaData, metaDataSelector } from './meta_data'; +import { openSaveModal, SaveWorkspaceHandler } from '../services/save_modal'; +import { getEditPath } from '../services/url'; +const actionCreator = actionCreatorFactory('x-pack/graph'); + +export const loadSavedWorkspace = actionCreator('LOAD_WORKSPACE'); +export const saveWorkspace = actionCreator('SAVE_WORKSPACE'); + +/** + * Saga handling loading of a saved workspace. + * + * It will load the index pattern associated with the saved object and deserialize all properties + * into the store. Existing state will be overwritten. + */ +export const loadingSaga = ({ + createWorkspace, + getWorkspace, + indexPatterns, + notifications, + indexPatternProvider, +}: GraphStoreDependencies) => { + function* deserializeWorkspace(action: Action) { + const selectedIndex = lookupIndexPattern(action.payload, indexPatterns); + if (!selectedIndex) { + notifications.toasts.addDanger( + i18n.translate('xpack.graph.loadWorkspace.missingIndexPatternErrorMessage', { + defaultMessage: 'Index pattern not found', + }) + ); + return; + } + + const indexPattern = yield call(indexPatternProvider.get, selectedIndex.id); + const initialSettings = settingsSelector(yield select()); + + createWorkspace(selectedIndex.attributes.title, initialSettings); + + const { urlTemplates, advancedSettings, allFields } = savedWorkspaceToAppState( + action.payload, + indexPattern, + // workspace won't be null because it's created in the same call stack + getWorkspace()! + ); + + // put everything in the store + yield put( + updateMetaData({ + title: action.payload.title, + description: action.payload.description, + savedObjectId: action.payload.id, + }) + ); + yield put( + setDatasource({ + type: 'indexpattern', + id: selectedIndex.id, + title: selectedIndex.attributes.title, + }) + ); + yield put(loadFields(allFields)); + yield put(updateSettings(advancedSettings)); + yield put(loadTemplates(urlTemplates)); + + // workspace won't be null because it's created in the same call stack + getWorkspace()!.runLayout(); + } + + return function*() { + yield takeLatest(loadSavedWorkspace.match, deserializeWorkspace); + }; +}; + +/** + * Saga handling saving of current state. + * + * It will serialize everything and save it using the saved objects client + */ +export const savingSaga = (deps: GraphStoreDependencies) => { + function* persistWorkspace() { + const savedWorkspace = deps.getSavedWorkspace(); + const state: GraphState = yield select(); + const workspace = deps.getWorkspace(); + const selectedDatasource = datasourceSelector(state).current; + if (!workspace || selectedDatasource.type === 'none') { + return; + } + + const savedObjectId = yield cps(showModal, { + deps, + workspace, + savedWorkspace, + state, + selectedDatasource, + }); + if (savedObjectId) { + yield put(updateMetaData({ savedObjectId })); + } + } + + return function*() { + yield takeLatest(saveWorkspace.match, persistWorkspace); + }; +}; + +function showModal( + { + deps, + workspace, + savedWorkspace, + state, + selectedDatasource, + }: { + deps: GraphStoreDependencies; + workspace: Workspace; + savedWorkspace: GraphWorkspaceSavedObject; + state: GraphState; + selectedDatasource: IndexpatternDatasource; + }, + savingCallback: (error: unknown, id?: string) => void +) { + const saveWorkspaceHandler: SaveWorkspaceHandler = async ( + saveOptions, + userHasConfirmedSaveWorkspaceData + ) => { + const canSaveData = + deps.savePolicy === 'configAndData' || + (deps.savePolicy === 'configAndDataWithConsent' && userHasConfirmedSaveWorkspaceData); + appStateToSavedWorkspace( + savedWorkspace, + { + workspace, + urlTemplates: templatesSelector(state), + advancedSettings: settingsSelector(state), + selectedIndex: selectedDatasource, + selectedFields: selectedFieldsSelector(state), + }, + canSaveData + ); + try { + const id = await savedWorkspace.save(saveOptions); + if (id) { + const title = i18n.translate('xpack.graph.saveWorkspace.successNotificationTitle', { + defaultMessage: 'Saved "{workspaceTitle}"', + values: { workspaceTitle: savedWorkspace.title }, + }); + let text; + if (!canSaveData && workspace.nodes.length > 0) { + text = i18n.translate('xpack.graph.saveWorkspace.successNotification.noDataSavedText', { + defaultMessage: 'The configuration was saved, but the data was not saved', + }); + } + deps.notifications.toasts.addSuccess({ + title, + text, + 'data-test-subj': 'saveGraphSuccess', + }); + if (savedWorkspace.id !== metaDataSelector(state).savedObjectId) { + deps.changeUrl(getEditPath(savedWorkspace)); + } + } + savingCallback(null, id); + return { id }; + } catch (error) { + deps.notifications.toasts.addDanger( + i18n.translate('xpack.graph.saveWorkspace.savingErrorMessage', { + defaultMessage: 'Failed to save workspace: {message}', + values: { + message: error, + }, + }) + ); + return { error }; + } + }; + + openSaveModal({ + savePolicy: deps.savePolicy, + hasData: workspace.nodes.length > 0 || workspace.blacklistedNodes.length > 0, + workspace: savedWorkspace, + showSaveModal: deps.showSaveModal, + saveWorkspace: saveWorkspaceHandler, + }); +} diff --git a/x-pack/legacy/plugins/graph/public/state_management/store.ts b/x-pack/legacy/plugins/graph/public/state_management/store.ts index f5317e9c5b6ab..4161a3c48593e 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/store.ts +++ b/x-pack/legacy/plugins/graph/public/state_management/store.ts @@ -4,16 +4,95 @@ * you may not use this file except in compliance with the Elastic License. */ -import { combineReducers, createStore, Store, AnyAction, Dispatch } from 'redux'; -import { fieldsReducer, FieldsState } from './fields'; +import createSagaMiddleware, { SagaMiddleware } from 'redux-saga'; +import { combineReducers, createStore, Store, AnyAction, Dispatch, applyMiddleware } from 'redux'; +import { CoreStart } from 'src/core/public'; +import { Chrome } from 'ui/chrome'; +import { + fieldsReducer, + FieldsState, + syncNodeStyleSaga, + syncFieldsSaga, + updateSaveButtonSaga, +} from './fields'; +import { UrlTemplatesState, urlTemplatesReducer } from './url_templates'; +import { + AdvancedSettingsState, + advancedSettingsReducer, + syncSettingsSaga, +} from './advanced_settings'; +import { DatasourceState, datasourceReducer, datasourceSaga } from './datasource'; +import { + IndexPatternProvider, + Workspace, + IndexPatternSavedObject, + GraphSavePolicy, + GraphWorkspaceSavedObject, + AdvancedSettings, + WorkspaceField, +} from '../types'; +import { loadingSaga, savingSaga } from './persistence'; +import { metaDataReducer, MetaDataState, syncBreadcrumbSaga } from './meta_data'; export interface GraphState { fields: FieldsState; + urlTemplates: UrlTemplatesState; + advancedSettings: AdvancedSettingsState; + datasource: DatasourceState; + metaData: MetaDataState; } -const rootReducer = combineReducers({ fields: fieldsReducer }); +export interface GraphStoreDependencies { + basePath: string; + indexPatternProvider: IndexPatternProvider; + indexPatterns: IndexPatternSavedObject[]; + createWorkspace: (index: string, advancedSettings: AdvancedSettings) => void; + getWorkspace: () => Workspace | null; + getSavedWorkspace: () => GraphWorkspaceSavedObject; + notifications: CoreStart['notifications']; + showSaveModal: (el: React.ReactNode) => void; + savePolicy: GraphSavePolicy; + changeUrl: (newUrl: string) => void; + notifyAngular: () => void; + setLiveResponseFields: (fields: WorkspaceField[]) => void; + chrome: Chrome; +} + +export function createRootReducer(basePath: string) { + return combineReducers({ + fields: fieldsReducer, + urlTemplates: urlTemplatesReducer(basePath), + advancedSettings: advancedSettingsReducer, + datasource: datasourceReducer, + metaData: metaDataReducer, + }); +} + +export function registerSagas( + sagaMiddleware: SagaMiddleware, + deps: GraphStoreDependencies +) { + sagaMiddleware.run(datasourceSaga(deps)); + sagaMiddleware.run(loadingSaga(deps)); + sagaMiddleware.run(savingSaga(deps)); + sagaMiddleware.run(syncFieldsSaga(deps)); + sagaMiddleware.run(syncNodeStyleSaga(deps)); + sagaMiddleware.run(syncSettingsSaga(deps)); + sagaMiddleware.run(updateSaveButtonSaga(deps)); + sagaMiddleware.run(syncBreadcrumbSaga(deps)); +} + +export const createGraphStore = (deps: GraphStoreDependencies) => { + const sagaMiddleware = createSagaMiddleware(); + + const rootReducer = createRootReducer(deps.basePath); + + const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); + + registerSagas(sagaMiddleware, deps); -export const createGraphStore = () => createStore(rootReducer); + return store; +}; export type GraphStore = Store; export type GraphDispatch = Dispatch; diff --git a/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts b/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts new file mode 100644 index 0000000000000..3bb97c343ba34 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts @@ -0,0 +1,92 @@ +/* + * 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 actionCreatorFactory from 'typescript-fsa'; +import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; +import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; +import { i18n } from '@kbn/i18n'; +import rison from 'rison-node'; +import { GraphState } from './store'; +import { UrlTemplate } from '../types'; +import { reset } from './global'; +import { setDatasource, IndexpatternDatasource, requestDatasource } from './datasource'; +import { outlinkEncoders } from '../helpers/outlink_encoders'; +import { urlTemplatePlaceholder } from '../helpers/url_template'; + +const actionCreator = actionCreatorFactory('x-pack/graph/urlTemplates'); + +export const loadTemplates = actionCreator('LOAD_TEMPLATES'); +export const saveTemplate = actionCreator<{ index: number; template: UrlTemplate }>( + 'SAVE_TEMPLATE' +); +export const removeTemplate = actionCreator('REMOVE_TEMPLATE'); + +export type UrlTemplatesState = UrlTemplate[]; + +const initialTemplates: UrlTemplatesState = []; + +function generateDefaultTemplate( + datasource: IndexpatternDatasource, + basePath: string +): UrlTemplate { + const kUrl = new KibanaParsedUrl({ + appId: 'kibana', + basePath, + appPath: '/discover', + }); + + kUrl.addQueryParameter( + '_a', + rison.encode({ + columns: ['_source'], + index: datasource.title, + interval: 'auto', + query: { language: 'kuery', query: urlTemplatePlaceholder }, + sort: ['_score', 'desc'], + }) + ); + + // replace the URI encoded version of the tag with the unescaped version + // so it can be found with String.replace, regexp, etc. + const discoverUrl = kUrl + .getRootRelativePath() + .replace(encodeURIComponent(urlTemplatePlaceholder), urlTemplatePlaceholder); + + return { + url: discoverUrl, + description: i18n.translate('xpack.graph.settings.drillDowns.defaultUrlTemplateTitle', { + defaultMessage: 'Raw documents', + }), + encoder: outlinkEncoders[0], + isDefault: true, + icon: null, + }; +} + +export const urlTemplatesReducer = (basePath: string) => + reducerWithInitialState(initialTemplates) + .case(reset, () => initialTemplates) + .cases([requestDatasource, setDatasource], (templates, datasource) => { + if (datasource.type === 'none') { + return initialTemplates; + } + const customTemplates = templates.filter(template => !template.isDefault); + return [...customTemplates, generateDefaultTemplate(datasource, basePath)]; + }) + .case(loadTemplates, (_currentTemplates, newTemplates) => newTemplates) + .case(saveTemplate, (templates, { index: indexToUpdate, template: updatedTemplate }) => { + // set default flag to false as soon as template is overwritten. + const newTemplate = { ...updatedTemplate, isDefault: false }; + return indexToUpdate === -1 + ? [...templates, newTemplate] + : templates.map((template, index) => (index === indexToUpdate ? newTemplate : template)); + }) + .case(removeTemplate, (templates, templateToDelete) => + templates.filter(template => template !== templateToDelete) + ) + .build(); + +export const templatesSelector = (state: GraphState) => state.urlTemplates; diff --git a/x-pack/legacy/plugins/graph/public/types/app_state.ts b/x-pack/legacy/plugins/graph/public/types/app_state.ts index f72c4057f4c2f..24b1876bb713f 100644 --- a/x-pack/legacy/plugins/graph/public/types/app_state.ts +++ b/x-pack/legacy/plugins/graph/public/types/app_state.ts @@ -6,7 +6,6 @@ import { SimpleSavedObject } from 'src/core/public'; import { IndexPattern } from 'src/legacy/core_plugins/data/public'; -import { Workspace } from './workspace_state'; import { FontawesomeIcon } from '../helpers/style_choices'; import { OutlinkEncoder } from '../helpers/outlink_encoders'; @@ -39,11 +38,6 @@ export interface AdvancedSettings { export type IndexPatternSavedObject = SimpleSavedObject<{ title: string }>; -export interface AppState { - urlTemplates: UrlTemplate[]; - advancedSettings: AdvancedSettings; - workspace: Workspace; - allFields: WorkspaceField[]; - selectedFields: WorkspaceField[]; - selectedIndex: IndexPattern; +export interface IndexPatternProvider { + get(id: string): Promise; } diff --git a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts b/x-pack/legacy/plugins/graph/public/types/workspace_state.ts index fab093535cb63..c23ab49de496d 100644 --- a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts +++ b/x-pack/legacy/plugins/graph/public/types/workspace_state.ts @@ -69,6 +69,7 @@ export interface GraphData { } export interface Workspace { + options: WorkspaceOptions; nodesMap: Record; nodes: WorkspaceNode[]; edges: WorkspaceEdge[]; @@ -89,6 +90,9 @@ export interface Workspace { * @param newData */ mergeGraph(newData: GraphData): void; + + runLayout(): void; + stopLayout(): void; } export type ExploreRequest = any; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 676cf78de9ff9..b7a6b24242924 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4517,7 +4517,6 @@ "xpack.graph.savedWorkspace.workspaceNameTitle": "新規グラフワークスペース", "xpack.graph.savedWorkspaces.graphWorkspaceLabel": "グラフワークスペース", "xpack.graph.savedWorkspaces.graphWorkspacesLabel": "グラフワークスペース", - "xpack.graph.saveWorkspace.disabledWarning": "保存が無効になっています", "xpack.graph.saveWorkspace.successNotification.noDataSavedText": "構成が保存されましたが、データは保存されませんでした", "xpack.graph.saveWorkspace.successNotificationTitle": "「{workspaceTitle}」が保存されました", "xpack.graph.serverSideErrors.expiredLicenseErrorMessage": "グラフを利用できません。ライセンスが期限切れです。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c2054497c96f6..79cba3ee696be 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4520,7 +4520,6 @@ "xpack.graph.savedWorkspace.workspaceNameTitle": "新建 Graph 工作空间", "xpack.graph.savedWorkspaces.graphWorkspaceLabel": "Graph 工作空间", "xpack.graph.savedWorkspaces.graphWorkspacesLabel": "Graph 工作空间", - "xpack.graph.saveWorkspace.disabledWarning": "已禁用保存", "xpack.graph.saveWorkspace.successNotification.noDataSavedText": "配置会被保存,但不保存数据", "xpack.graph.saveWorkspace.successNotificationTitle": "已保存“{workspaceTitle}”", "xpack.graph.serverSideErrors.expiredLicenseErrorMessage": "Graph 不可用 - 许可已过期。", From ba8e8fb1bea7c958eebae200362b97865b226f5b Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Fri, 4 Oct 2019 11:00:26 +0100 Subject: [PATCH 03/33] [SIEM] Chart enhancement (#47130) * chart styling * rename variable * styling for bar chart * add unit test * clean up * fix for code review --- .../components/charts/areachart.test.tsx | 293 +++++++++--------- .../public/components/charts/areachart.tsx | 61 ++-- .../components/charts/barchart.test.tsx | 262 +++++++--------- .../public/components/charts/barchart.tsx | 63 ++-- .../charts/chart_place_holder.test.tsx | 92 ++++++ .../components/charts/chart_place_holder.tsx | 40 +++ .../public/components/charts/common.test.tsx | 83 +++-- .../siem/public/components/charts/common.tsx | 51 +-- .../public/components/charts/translation.ts | 15 + .../components/matrix_over_time/index.tsx | 2 +- .../page/network/kpi_network/mock.ts | 11 +- .../__snapshots__/index.test.tsx.snap | 3 + .../public/components/stat_items/index.tsx | 2 + 13 files changed, 545 insertions(+), 433 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/charts/translation.ts diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx index 7338a959495f8..910e576e6e1e7 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx @@ -7,13 +7,135 @@ import { ShallowWrapper, shallow } from 'enzyme'; import * as React from 'react'; -import { AreaChartBaseComponent, AreaChartWithCustomPrompt, AreaChart } from './areachart'; -import { ChartHolder, ChartSeriesData } from './common'; +import { AreaChartBaseComponent, AreaChart } from './areachart'; +import { ChartSeriesData } from './common'; import { ScaleType, AreaSeries, Axis } from '@elastic/charts'; -jest.mock('@elastic/charts'); const customHeight = '100px'; const customWidth = '120px'; +const chartDataSets = [ + [ + [ + { + key: 'uniqueSourceIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: null }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, + ], + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, + ], + color: '#490092', + }, + ], + ], + [ + [ + { + key: 'uniqueSourceIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1096175 }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, + ], + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, + ], + color: '#490092', + }, + ], + ], + [ + [ + { + key: 'uniqueSourceIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: {} }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, + ], + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, + ], + color: '#490092', + }, + ], + ], + [ + [ + { + key: 'uniqueSourceIpsHistogram', + value: [], + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, + ], + color: '#490092', + }, + ], + ], +]; + +const chartHolderDataSets = [ + [null], + [[]], + [ + { + key: 'uniqueSourceIpsHistogram', + value: null, + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: null, + color: '#490092', + }, + ], + [ + { + key: 'uniqueSourceIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf() }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf() }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf() }, + ], + color: '#DB1374', + }, + { + key: 'uniqueDestinationIpsHistogram', + value: [ + { x: new Date('2019-05-03T13:00:00.000Z').valueOf() }, + { x: new Date('2019-05-04T01:00:00.000Z').valueOf() }, + { x: new Date('2019-05-04T13:00:00.000Z').valueOf() }, + ], + color: '#490092', + }, + ], +]; describe('AreaChartBaseComponent', () => { let shallowWrapper: ShallowWrapper; const mockAreaChartData: ChartSeriesData[] = [ @@ -186,137 +308,6 @@ describe('AreaChartBaseComponent', () => { }); }); -describe('AreaChartWithCustomPrompt', () => { - let shallowWrapper: ShallowWrapper; - describe.each([ - [ - [ - { - key: 'uniqueSourceIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1096175 }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, - ], - color: '#DB1374', - }, - { - key: 'uniqueDestinationIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, - ], - color: '#490092', - }, - ], - ], - ] as Array<[ChartSeriesData[]]>)('renders areachart', data => { - beforeAll(() => { - shallowWrapper = shallow( - - ); - }); - - it('render AreaChartBaseComponent', () => { - expect(shallowWrapper.find(AreaChartBaseComponent)).toHaveLength(1); - expect(shallowWrapper.find(ChartHolder)).toHaveLength(0); - }); - }); - - describe.each([ - null, - [], - [ - { - key: 'uniqueSourceIpsHistogram', - value: null, - color: '#DB1374', - }, - { - key: 'uniqueDestinationIpsHistogram', - value: null, - color: '#490092', - }, - ], - [ - { - key: 'uniqueSourceIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf() }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf() }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf() }, - ], - color: '#DB1374', - }, - { - key: 'uniqueDestinationIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf() }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf() }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf() }, - ], - color: '#490092', - }, - ], - [ - [ - { - key: 'uniqueSourceIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: null }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, - ], - color: '#DB1374', - }, - { - key: 'uniqueDestinationIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, - ], - color: '#490092', - }, - ], - ], - [ - [ - { - key: 'uniqueSourceIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 580213 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: {} }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12382 }, - ], - color: '#DB1374', - }, - { - key: 'uniqueDestinationIpsHistogram', - value: [ - { x: new Date('2019-05-03T13:00:00.000Z').valueOf(), y: 565975 }, - { x: new Date('2019-05-04T01:00:00.000Z').valueOf(), y: 1084366 }, - { x: new Date('2019-05-04T13:00:00.000Z').valueOf(), y: 12280 }, - ], - color: '#490092', - }, - ], - ], - ] as Array<[ChartSeriesData[] | null | undefined]>)('renders prompt', data => { - beforeAll(() => { - shallowWrapper = shallow( - - ); - }); - - it('render Chart Holder', () => { - expect(shallowWrapper.find(AreaChartBaseComponent)).toHaveLength(0); - expect(shallowWrapper.find(ChartHolder)).toHaveLength(1); - }); - }); -}); - describe('AreaChart', () => { let shallowWrapper: ShallowWrapper; const mockConfig = { @@ -332,20 +323,28 @@ describe('AreaChart', () => { }, customHeight: 324, }; + describe.each(chartDataSets as Array<[ChartSeriesData[]]>)('with valid data [%o]', data => { + beforeAll(() => { + shallowWrapper = shallow(); + }); - it('should render if data exist', () => { - const mockData = [ - { key: 'uniqueSourceIps', value: [{ y: 100, x: 100, g: 'group' }], color: '#DB1374' }, - ]; - shallowWrapper = shallow(); - expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); - expect(shallowWrapper.find('ChartHolder')).toHaveLength(0); + it(`should render area chart`, () => { + expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); + expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0); + }); }); - it('should render a chartHolder if no data given', () => { - const mockData = [{ key: 'uniqueSourceIps', value: [], color: '#DB1374' }]; - shallowWrapper = shallow(); - expect(shallowWrapper.find('AutoSizer')).toHaveLength(0); - expect(shallowWrapper.find('ChartHolder')).toHaveLength(1); - }); + describe.each(chartHolderDataSets as Array<[ChartSeriesData[] | null | undefined]>)( + 'with invalid data [%o]', + data => { + beforeAll(() => { + shallowWrapper = shallow(); + }); + + it(`should render a chart place holder`, () => { + expect(shallowWrapper.find('AutoSizer')).toHaveLength(0); + expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1); + }); + } + ); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx index c4bb01a66753b..6347b93772b4e 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx @@ -17,19 +17,19 @@ import { AreaSeriesStyle, RecursivePartial, } from '@elastic/charts'; -import { getOr, get } from 'lodash/fp'; +import { getOr, get, isNull, isNumber } from 'lodash/fp'; +import { AutoSizer } from '../auto_sizer'; +import { ChartPlaceHolder } from './chart_place_holder'; import { - ChartSeriesData, - ChartHolder, - getSeriesStyle, - WrappedByAutoSizer, - ChartSeriesConfigs, browserTimezone, chartDefaultSettings, + ChartSeriesConfigs, + ChartSeriesData, getChartHeight, getChartWidth, + getSeriesStyle, + WrappedByAutoSizer, } from './common'; -import { AutoSizer } from '../auto_sizer'; // custom series styles: https://ela.st/areachart-styling const getSeriesLineStyle = (): RecursivePartial => { @@ -51,6 +51,17 @@ const getSeriesLineStyle = (): RecursivePartial => { }; }; +const checkIfAllTheDataInTheSeriesAreValid = (series: unknown): series is ChartSeriesData => + !!get('value.length', series) && + get('value', series).every( + ({ x, y }: { x: unknown; y: unknown }) => !isNull(x) && isNumber(y) && y > 0 + ); + +const checkIfAnyValidSeriesExist = ( + data: ChartSeriesData[] | null | undefined +): data is ChartSeriesData[] => + Array.isArray(data) && data.some(checkIfAllTheDataInTheSeriesAreValid); + // https://ela.st/multi-areaseries export const AreaChartBaseComponent = React.memo<{ data: ChartSeriesData[]; @@ -73,12 +84,12 @@ export const AreaChartBaseComponent = React.memo<{ {data.map(series => { const seriesKey = series.key; const seriesSpecId = getSpecId(seriesKey); - return series.value != null ? ( + return checkIfAllTheDataInTheSeriesAreValid(series) ? ( (({ data, height, width, configs }) => { - return data != null && - data.length && - data.every( - ({ value }) => - value != null && - value.length > 0 && - value.every(chart => chart.x != null && chart.y != null) - ) ? ( - - ) : ( - - ); -}); - -AreaChartWithCustomPrompt.displayName = 'AreaChartWithCustomPrompt'; - export const AreaChart = React.memo<{ areaChart: ChartSeriesData[] | null | undefined; configs?: ChartSeriesConfigs | undefined; @@ -135,11 +124,11 @@ export const AreaChart = React.memo<{ const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); - return get(`0.value.length`, areaChart) ? ( + return checkIfAnyValidSeriesExist(areaChart) ? ( {({ measureRef, content: { height, width } }) => ( - ) : ( - + ); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx index 527556842126c..4b3ec577e6488 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx @@ -7,13 +7,113 @@ import { shallow, ShallowWrapper } from 'enzyme'; import * as React from 'react'; -import { BarChartBaseComponent, BarChartWithCustomPrompt, BarChart } from './barchart'; -import { ChartSeriesData, ChartHolder } from './common'; +import { BarChartBaseComponent, BarChart } from './barchart'; +import { ChartSeriesData } from './common'; import { BarSeries, ScaleType, Axis } from '@elastic/charts'; -jest.mock('@elastic/charts'); const customHeight = '100px'; const customWidth = '120px'; +const chartDataSets = [ + [ + [ + { key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{ y: 2354, x: 'uniqueDestinationIps' }], + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [{ y: 1714, x: '' }], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{ y: 2354, x: '' }], + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{ y: 0, x: 'uniqueDestinationIps' }], + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [{ y: null, x: 'uniqueSourceIps' }], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{ y: 2354, x: 'uniqueDestinationIps' }], + color: '#490092', + }, + ], + ], +]; + +const chartHolderDataSets: Array<[ChartSeriesData[] | undefined | null]> = [ + [[]], + [null], + [ + [ + { key: 'uniqueSourceIps', color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [], + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [{}], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{}], + color: '#490092', + }, + ], + ], + [ + [ + { key: 'uniqueSourceIps', value: [{ y: 0, x: 'uniqueSourceIps' }], color: '#DB1374' }, + { + key: 'uniqueDestinationIps', + value: [{ y: 0, x: 'uniqueDestinationIps' }], + color: '#490092', + }, + ], + ], +] as any; // eslint-disable-line @typescript-eslint/no-explicit-any + +const mockConfig = { + series: { + xScaleType: ScaleType.Time, + yScaleType: ScaleType.Linear, + stackAccessors: ['g'], + }, + axis: { + xTickFormatter: jest.fn(), + yTickFormatter: jest.fn(), + tickSize: 8, + }, + customHeight: 324, +}; + describe('BarChartBaseComponent', () => { let shallowWrapper: ShallowWrapper; const mockBarChartData: ChartSeriesData[] = [ @@ -168,164 +268,28 @@ describe('BarChartBaseComponent', () => { }); }); -describe.each([ - [ - [ - { key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: 2354, x: 'uniqueDestinationIps' }], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{ y: 1714, x: '' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: 2354, x: '' }], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: 0, x: 'uniqueDestinationIps' }], - color: '#490092', - }, - ], - ], -])('BarChartWithCustomPrompt', mockBarChartData => { +describe.each(chartDataSets)('BarChart with valid data [%o]', data => { let shallowWrapper: ShallowWrapper; - describe('renders barchart', () => { - beforeAll(() => { - shallowWrapper = shallow( - - ); - }); - - it('render BarChartBaseComponent', () => { - expect(shallowWrapper.find(BarChartBaseComponent)).toHaveLength(1); - expect(shallowWrapper.find(ChartHolder)).toHaveLength(0); - }); - }); -}); -const table: Array<[ChartSeriesData[] | undefined | null]> = [ - [], - null, - [ - [ - { key: 'uniqueSourceIps', color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{}], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{}], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{ y: 0, x: 'uniqueSourceIps' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: 0, x: 'uniqueDestinationIps' }], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{ y: null, x: 'uniqueSourceIps' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: 2354, x: 'uniqueDestinationIps' }], - color: '#490092', - }, - ], - ], - [ - [ - { key: 'uniqueSourceIps', value: [{ y: null, x: 'uniqueSourceIps' }], color: '#DB1374' }, - { - key: 'uniqueDestinationIps', - value: [{ y: null, x: 'uniqueDestinationIps' }], - color: '#490092', - }, - ], - ], -] as any; // eslint-disable-line @typescript-eslint/no-explicit-any - -describe.each(table)('renders prompt', data => { - let shallowWrapper: ShallowWrapper; beforeAll(() => { - shallowWrapper = shallow( - - ); + shallowWrapper = shallow(); }); - it('render Chart Holder', () => { - expect(shallowWrapper.find(BarChartBaseComponent)).toHaveLength(0); - expect(shallowWrapper.find(ChartHolder)).toHaveLength(1); + it(`should render chart`, () => { + expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); + expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0); }); }); -describe('BarChart', () => { +describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => { let shallowWrapper: ShallowWrapper; - const mockConfig = { - series: { - xScaleType: ScaleType.Time, - yScaleType: ScaleType.Linear, - stackAccessors: ['g'], - }, - axis: { - xTickFormatter: jest.fn(), - yTickFormatter: jest.fn(), - tickSize: 8, - }, - customHeight: 324, - }; - it('should render if data exist', () => { - const mockData = [ - { key: 'uniqueSourceIps', value: [{ y: 100, x: 100, g: 'group' }], color: '#DB1374' }, - ]; - shallowWrapper = shallow(); - expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); - expect(shallowWrapper.find('ChartHolder')).toHaveLength(0); + beforeAll(() => { + shallowWrapper = shallow(); }); - it('should render a chartHolder if no data given', () => { - const mockData = [{ key: 'uniqueSourceIps', value: [], color: '#DB1374' }]; - shallowWrapper = shallow(); + it(`should render chart holder`, () => { expect(shallowWrapper.find('AutoSizer')).toHaveLength(0); - expect(shallowWrapper.find('ChartHolder')).toHaveLength(1); + expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index 02345fc149c2a..9ef26c690c56b 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -16,20 +16,33 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import { getOr, get } from 'lodash/fp'; +import { getOr, get, isNumber } from 'lodash/fp'; +import { AutoSizer } from '../auto_sizer'; +import { ChartPlaceHolder } from './chart_place_holder'; import { - ChartSeriesData, - WrappedByAutoSizer, - ChartHolder, - SeriesType, - getSeriesStyle, - ChartSeriesConfigs, browserTimezone, chartDefaultSettings, + ChartSeriesConfigs, + ChartSeriesData, + checkIfAllValuesAreZero, + getSeriesStyle, getChartHeight, getChartWidth, + SeriesType, + WrappedByAutoSizer, } from './common'; -import { AutoSizer } from '../auto_sizer'; + +const checkIfAllTheDataInTheSeriesAreValid = (series: ChartSeriesData): series is ChartSeriesData => + series != null && + !!get('value.length', series) && + (series.value || []).every(({ x, y }) => isNumber(y) && y >= 0); + +const checkIfAnyValidSeriesExist = ( + data: ChartSeriesData[] | null | undefined +): data is ChartSeriesData[] => + Array.isArray(data) && + !checkIfAllValuesAreZero(data) && + data.some(checkIfAllTheDataInTheSeriesAreValid); // Bar chart rotation: https://ela.st/chart-rotations export const BarChartBaseComponent = React.memo<{ @@ -54,7 +67,7 @@ export const BarChartBaseComponent = React.memo<{ const barSeriesKey = series.key; const barSeriesSpecId = getSpecId(barSeriesKey); const seriesType = SeriesType.BAR; - return ( + return checkIfAllTheDataInTheSeriesAreValid ? ( - ); + ) : null; })} (({ data, height, width, configs }) => { - return data && - data.length && - data.some( - ({ value }) => - value != null && value.length > 0 && value.every(chart => chart.y != null && chart.y >= 0) - ) ? ( - - ) : ( - - ); -}); - -BarChartWithCustomPrompt.displayName = 'BarChartWithCustomPrompt'; - export const BarChart = React.memo<{ barChart: ChartSeriesData[] | null | undefined; configs?: ChartSeriesConfigs | undefined; }>(({ barChart, configs }) => { const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); - return get(`0.value.length`, barChart) ? ( + return checkIfAnyValidSeriesExist(barChart) ? ( {({ measureRef, content: { height, width } }) => ( - ) : ( - + ); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.test.tsx new file mode 100644 index 0000000000000..7674fd09739f5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.test.tsx @@ -0,0 +1,92 @@ +/* + * 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 { shallow, ShallowWrapper } from 'enzyme'; +import React from 'react'; +import { ChartPlaceHolder } from './chart_place_holder'; +import { ChartSeriesData } from './common'; + +describe('ChartPlaceHolder', () => { + let shallowWrapper: ShallowWrapper; + const mockDataAllZeros = [ + { + key: 'mockKeyA', + color: 'mockColor', + value: [{ x: 'a', y: 0 }, { x: 'b', y: 0 }], + }, + { + key: 'mockKeyB', + color: 'mockColor', + value: [{ x: 'a', y: 0 }, { x: 'b', y: 0 }], + }, + ]; + const mockDataUnexpectedValue = [ + { + key: 'mockKeyA', + color: 'mockColor', + value: [{ x: 'a', y: '' }, { x: 'b', y: 0 }], + }, + { + key: 'mockKeyB', + color: 'mockColor', + value: [{ x: 'a', y: {} }, { x: 'b', y: 0 }], + }, + ]; + + it('should render with default props', () => { + const height = `100%`; + const width = `100%`; + shallowWrapper = shallow(); + expect(shallowWrapper.props()).toMatchObject({ + height, + width, + }); + }); + + it('should render with given props', () => { + const height = `100px`; + const width = `100px`; + shallowWrapper = shallow( + + ); + expect(shallowWrapper.props()).toMatchObject({ + height, + width, + }); + }); + + it('should render correct wording when all values returned zero', () => { + const height = `100px`; + const width = `100px`; + shallowWrapper = shallow( + + ); + expect( + shallowWrapper + .find(`[data-test-subj="chartHolderText"]`) + .childAt(0) + .text() + ).toEqual('All values returned zero'); + }); + + it('should render correct wording when unexpected value exists', () => { + const height = `100px`; + const width = `100px`; + shallowWrapper = shallow( + + ); + expect( + shallowWrapper + .find(`[data-test-subj="chartHolderText"]`) + .childAt(0) + .text() + ).toEqual('Chart Data Not Available'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.tsx b/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.tsx new file mode 100644 index 0000000000000..22122b5fad74a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/charts/chart_place_holder.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexItem, EuiText, EuiFlexGroup } from '@elastic/eui'; +import styled from 'styled-components'; +import { ChartSeriesData, checkIfAllValuesAreZero } from './common'; +import * as i18n from './translation'; + +const FlexGroup = styled(EuiFlexGroup)<{ height?: string | null; width?: string | null }>` + height: ${({ height }) => (height ? height : '100%')}; + width: ${({ width }) => (width ? width : '100%')}; + position: relative; + margin: 0; +`; + +FlexGroup.displayName = 'FlexGroup'; + +export const ChartPlaceHolder = ({ + height = '100%', + width = '100%', + data, +}: { + height?: string | null; + width?: string | null; + data: ChartSeriesData[] | null | undefined; +}) => ( + + + + {checkIfAllValuesAreZero(data) + ? i18n.ALL_VALUES_ZEROS_TITLE + : i18n.DATA_NOT_AVAILABLE_TITLE} + + + +); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx index f23b97d8cd5ff..0fc7bc6afc216 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx @@ -3,18 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { shallow, ShallowWrapper } from 'enzyme'; -import * as React from 'react'; +import { shallow } from 'enzyme'; +import React from 'react'; import { - ChartHolder, + checkIfAllValuesAreZero, + defaultChartHeight, getChartHeight, getChartWidth, - WrappedByAutoSizer, - defaultChartHeight, getSeriesStyle, - SeriesType, getTheme, + SeriesType, + WrappedByAutoSizer, + ChartSeriesData, } from './common'; import 'jest-styled-components'; import { mergeWithDefaultTheme, LIGHT_THEME } from '@elastic/charts'; @@ -26,30 +26,6 @@ jest.mock('@elastic/charts', () => { }; }); -describe('ChartHolder', () => { - let shallowWrapper: ShallowWrapper; - - it('should render with default props', () => { - const height = `100%`; - const width = `100%`; - shallowWrapper = shallow(); - expect(shallowWrapper.props()).toMatchObject({ - height, - width, - }); - }); - - it('should render with given props', () => { - const height = `100px`; - const width = `100px`; - shallowWrapper = shallow(); - expect(shallowWrapper.props()).toMatchObject({ - height, - width, - }); - }); -}); - describe('WrappedByAutoSizer', () => { it('should render correct default height', () => { const wrapper = shallow(); @@ -88,7 +64,7 @@ describe('getTheme', () => { chartMargins: { bottom: 0, left: 0, right: 0, top: 4 }, chartPaddings: { bottom: 0, left: 0, right: 0, top: 0 }, scales: { - barsPadding: 0.5, + barsPadding: 0.05, }, }; getTheme(); @@ -130,3 +106,46 @@ describe('getChartWidth', () => { expect(height).toEqual(defaultChartHeight); }); }); + +describe('checkIfAllValuesAreZero', () => { + const mockInvalidDataSets: Array<[ChartSeriesData[]]> = [ + [[{ key: 'mockKey', color: 'mockColor', value: [{ x: 1, y: 0 }, { x: 1, y: 1 }] }]], + [ + [ + { key: 'mockKeyA', color: 'mockColor', value: [{ x: 1, y: 0 }, { x: 1, y: 1 }] }, + { key: 'mockKeyB', color: 'mockColor', value: [{ x: 1, y: 0 }, { x: 1, y: 0 }] }, + ], + ], + ]; + const mockValidDataSets: Array<[ChartSeriesData[]]> = [ + [[{ key: 'mockKey', color: 'mockColor', value: [{ x: 0, y: 0 }, { x: 1, y: 0 }] }]], + [ + [ + { key: 'mockKeyA', color: 'mockColor', value: [{ x: 1, y: 0 }, { x: 3, y: 0 }] }, + { key: 'mockKeyB', color: 'mockColor', value: [{ x: 2, y: 0 }, { x: 4, y: 0 }] }, + ], + ], + ]; + + describe.each(mockInvalidDataSets)('with data [%o]', data => { + let result: boolean; + beforeAll(() => { + result = checkIfAllValuesAreZero(data); + }); + + it(`should return false`, () => { + expect(result).toBeFalsy(); + }); + }); + + describe.each(mockValidDataSets)('with data [%o]', data => { + let result: boolean; + beforeAll(() => { + result = checkIfAllValuesAreZero(data); + }); + + it(`should return true`, () => { + expect(result).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx index 59873b2cd6a31..7ac91437920e5 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx @@ -3,58 +3,33 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiText, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; -import styled from 'styled-components'; + +import chrome from 'ui/chrome'; import { CustomSeriesColorsMap, + DARK_THEME, DataSeriesColorsValues, getSpecId, + LIGHT_THEME, mergeWithDefaultTheme, PartialTheme, - LIGHT_THEME, - DARK_THEME, + Rendering, + Rotation, ScaleType, - TickFormatter, SettingSpecProps, - Rotation, - Rendering, + TickFormatter, } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; import moment from 'moment-timezone'; +import styled from 'styled-components'; import { DEFAULT_DATE_FORMAT_TZ, DEFAULT_DARK_MODE } from '../../../common/constants'; + export const defaultChartHeight = '100%'; export const defaultChartWidth = '100%'; const chartDefaultRotation: Rotation = 0; const chartDefaultRendering: Rendering = 'canvas'; -const FlexGroup = styled(EuiFlexGroup)<{ height?: string | null; width?: string | null }>` - height: ${({ height }) => (height ? height : '100%')}; - width: ${({ width }) => (width ? width : '100%')}; -`; - -FlexGroup.displayName = 'FlexGroup'; export type UpdateDateRange = (min: number, max: number) => void; -export const ChartHolder = ({ - height = '100%', - width = '100%', -}: { - height?: string | null; - width?: string | null; -}) => ( - - - - {i18n.translate('xpack.siem.chart.dataNotAvailableTitle', { - defaultMessage: 'Chart Data Not Available', - })} - - - -); - export interface ChartData { x: number | string | null; y: number | string | null; @@ -136,7 +111,7 @@ export const getTheme = () => { bottom: 0, }, scales: { - barsPadding: 0.5, + barsPadding: 0.05, }, }; const isDarkMode: boolean = chrome.getUiSettingsClient().get(DEFAULT_DARK_MODE); @@ -166,3 +141,9 @@ export const getChartWidth = (customWidth?: number, autoSizerWidth?: number): st const height = customWidth || autoSizerWidth; return height ? `${height}px` : defaultChartWidth; }; + +export const checkIfAllValuesAreZero = (data: ChartSeriesData[] | null | undefined): boolean => + Array.isArray(data) && + data.every(series => { + return Array.isArray(series.value) && (series.value as ChartData[]).every(({ y }) => y === 0); + }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/translation.ts b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts new file mode 100644 index 0000000000000..341cb7782f87c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts @@ -0,0 +1,15 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ALL_VALUES_ZEROS_TITLE = i18n.translate('xpack.siem.chart.dataAllValuesZerosTitle', { + defaultMessage: 'All values returned zero', +}); + +export const DATA_NOT_AVAILABLE_TITLE = i18n.translate('xpack.siem.chart.dataNotAvailableTitle', { + defaultMessage: 'Chart Data Not Available', +}); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx index f95b9e6b3ecf5..2898541a4a3d1 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx @@ -53,7 +53,7 @@ const getBarchartConfigs = (from: number, to: number, onBrushEnd: UpdateDateRang showLegend: true, theme: { scales: { - barsPadding: 0.05, + barsPadding: 0.08, }, chartMargins: { left: 0, diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/mock.ts b/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/mock.ts index bb06926ec08f4..e06bb1477bc7f 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/mock.ts +++ b/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/mock.ts @@ -184,12 +184,19 @@ export const mockEnableChartsData = { { key: 'uniqueSourcePrivateIps', color: '#DB1374', - value: [{ x: 'Src.', y: 383, g: 'uniqueSourcePrivateIps' }], + value: [ + { + x: 'Src.', + y: 383, + g: 'uniqueSourcePrivateIps', + y0: 0, + }, + ], }, { key: 'uniqueDestinationPrivateIps', color: '#490092', - value: [{ x: 'Dest.', y: 18, g: 'uniqueDestinationPrivateIps' }], + value: [{ x: 'Dest.', y: 18, g: 'uniqueDestinationPrivateIps', y0: 0 }], }, ], description: 'Unique private IPs', diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap index 9541ad4de043b..7475220b56e77 100644 --- a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap @@ -994,6 +994,9 @@ exports[`Stat Items Component rendering kpis with charts it renders the default }, "customHeight": 74, "series": Object { + "stackAccessors": Array [ + "y0", + ], "xScaleType": "ordinal", "yScaleType": "linear", }, diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/index.tsx b/x-pack/legacy/plugins/siem/public/components/stat_items/index.tsx index 110d146381709..c206a4d33270b 100644 --- a/x-pack/legacy/plugins/siem/public/components/stat_items/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/stat_items/index.tsx @@ -103,6 +103,7 @@ export const barchartConfigs = (config?: { onElementClick?: ElementClickListener series: { xScaleType: ScaleType.Ordinal, yScaleType: ScaleType.Linear, + stackAccessors: ['y0'], }, axis: { xTickFormatter: numberFormatter, @@ -145,6 +146,7 @@ export const addValueToBarChart = ( x, y, g: key, + y0: 0, }, ]; From d1a99ea6eec614d7edf6441b5b79c0b1bbda0123 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 4 Oct 2019 12:48:05 +0200 Subject: [PATCH 04/33] Convert uiSettings service to TypeScript (#47018) * tsify is_config_version_upgradeable * tsify get_upgradeable_config * tsify create_or_upgrade_saved_config * tsify ui_settings_service * tsify ui_settings_service_factory * tsify ui_settings_service_for_request * declare types on server object * tsify set route * tsify set_many route * tsify get route * tsify delete route * tsify logWithMetadata * improve ui_settings_service typings * introduce uiService mocks * remove private methods from public contract * add types for server.uiSettingsServiceFactory * rename IUiSettingsService --> IUiSettingsClient --- src/legacy/server/kbn_server.d.ts | 7 +- .../ui_settings/create_objects_client_stub.ts | 8 + .../create_or_upgrade_saved_config.test.ts | 25 ++- ...g.js => create_or_upgrade_saved_config.ts} | 49 ++--- ...le_config.js => get_upgradeable_config.ts} | 16 +- .../{index.js => index.ts} | 0 .../create_or_upgrade.test.ts | 1 - .../is_config_version_upgradeable.test.ts | 1 - ...le.js => is_config_version_upgradeable.ts} | 8 +- .../ui_settings_mixin.test.ts | 12 +- .../routes/{delete.js => delete.ts} | 11 +- .../ui/ui_settings/routes/{get.js => get.ts} | 9 +- .../ui_settings/routes/{index.js => index.ts} | 0 .../routes/integration_tests/lib/servers.ts | 3 +- .../ui/ui_settings/routes/{set.js => set.ts} | 30 +-- .../routes/{set_many.js => set_many.ts} | 24 ++- .../ui_settings/ui_settings_service.mock.ts | 40 ++++ .../ui_settings/ui_settings_service.test.ts | 8 +- ...ings_service.js => ui_settings_service.ts} | 180 ++++++++++++------ ...tory.js => ui_settings_service_factory.ts} | 35 ++-- ....js => ui_settings_service_for_request.ts} | 21 +- .../server/lib/helpers/setup_request.test.ts | 9 +- 22 files changed, 325 insertions(+), 172 deletions(-) rename src/legacy/ui/ui_settings/create_or_upgrade_saved_config/{create_or_upgrade_saved_config.js => create_or_upgrade_saved_config.ts} (55%) rename src/legacy/ui/ui_settings/create_or_upgrade_saved_config/{get_upgradeable_config.js => get_upgradeable_config.ts} (80%) rename src/legacy/ui/ui_settings/create_or_upgrade_saved_config/{index.js => index.ts} (100%) rename src/legacy/ui/ui_settings/create_or_upgrade_saved_config/{is_config_version_upgradeable.js => is_config_version_upgradeable.ts} (89%) rename src/legacy/ui/ui_settings/routes/{delete.js => delete.ts} (81%) rename src/legacy/ui/ui_settings/routes/{get.js => get.ts} (84%) rename src/legacy/ui/ui_settings/routes/{index.js => index.ts} (100%) rename src/legacy/ui/ui_settings/routes/{set.js => set.ts} (70%) rename src/legacy/ui/ui_settings/routes/{set_many.js => set_many.ts} (73%) create mode 100644 src/legacy/ui/ui_settings/ui_settings_service.mock.ts rename src/legacy/ui/ui_settings/{ui_settings_service.js => ui_settings_service.ts} (52%) rename src/legacy/ui/ui_settings/{ui_settings_service_factory.js => ui_settings_service_factory.ts} (59%) rename src/legacy/ui/ui_settings/{ui_settings_service_for_request.js => ui_settings_service_for_request.ts} (74%) diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 69bf95e57cab9..b3e7078d8b5a9 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -39,6 +39,8 @@ import { CallClusterWithRequest, ElasticsearchPlugin } from '../core_plugins/ela import { CapabilitiesModifier } from './capabilities'; import { IndexPatternsServiceFactory } from './index_patterns'; import { Capabilities } from '../../core/public'; +import { IUiSettingsClient } from '../../legacy/ui/ui_settings/ui_settings_service'; +import { UiSettingsServiceFactoryOptions } from '../../legacy/ui/ui_settings/ui_settings_service_factory'; export interface KibanaConfig { get(key: string): T; @@ -77,14 +79,15 @@ declare module 'hapi' { name: string, factoryFn: (request: Request) => Record ) => void; - uiSettingsServiceFactory: (options: any) => any; + uiSettingsServiceFactory: (options?: UiSettingsServiceFactoryOptions) => IUiSettingsClient; + logWithMetadata: (tags: string[], message: string, meta: Record) => void; } interface Request { getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract; getBasePath(): string; getDefaultRoute(): Promise; - getUiSettingsService(): any; + getUiSettingsService(): IUiSettingsClient; getCapabilities(): Promise; } diff --git a/src/legacy/ui/ui_settings/create_objects_client_stub.ts b/src/legacy/ui/ui_settings/create_objects_client_stub.ts index ebbedb761fae9..ad19b5c8bc7cf 100644 --- a/src/legacy/ui/ui_settings/create_objects_client_stub.ts +++ b/src/legacy/ui/ui_settings/create_objects_client_stub.ts @@ -26,6 +26,10 @@ export interface SavedObjectsClientStub { update: sinon.SinonStub; get: sinon.SinonStub; create: sinon.SinonStub; + bulkCreate: sinon.SinonStub; + bulkGet: sinon.SinonStub; + delete: sinon.SinonStub; + find: sinon.SinonStub; errors: typeof savedObjectsClientErrors; } @@ -35,6 +39,10 @@ export function createObjectsClientStub(esDocSource = {}): SavedObjectsClientStu get: sinon.stub().returns({ attributes: esDocSource }), create: sinon.stub(), errors: savedObjectsClientErrors, + bulkCreate: sinon.stub(), + bulkGet: sinon.stub(), + delete: sinon.stub(), + find: sinon.stub(), }; return savedObjectsClient; } diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts index 9b9a2fad39aca..654c0fbb66c8b 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts @@ -21,9 +21,7 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import Chance from 'chance'; -// @ts-ignore import * as getUpgradeableConfigNS from './get_upgradeable_config'; -// @ts-ignore import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; const chance = new Chance(); @@ -45,7 +43,7 @@ describe('uiSettings/createOrUpgradeSavedConfig', function() { id: options.id, version: 'foo', })), - }; + } as any; // mute until we have savedObjects mocks async function run(options = {}) { const resp = await createOrUpgradeSavedConfig({ @@ -103,7 +101,12 @@ describe('uiSettings/createOrUpgradeSavedConfig', function() { [chance.word()]: chance.sentence(), }; - getUpgradeableConfig.returns({ id: prevVersion, attributes: savedAttributes }); + getUpgradeableConfig.resolves({ + id: prevVersion, + attributes: savedAttributes, + type: '', + references: [], + }); await run(); @@ -125,7 +128,12 @@ describe('uiSettings/createOrUpgradeSavedConfig', function() { it('should log a message for upgrades', async () => { const { getUpgradeableConfig, logWithMetadata, run } = setup(); - getUpgradeableConfig.returns({ id: prevVersion, attributes: { buildNum: buildNum - 100 } }); + getUpgradeableConfig.resolves({ + id: prevVersion, + attributes: { buildNum: buildNum - 100 }, + type: '', + references: [], + }); await run(); sinon.assert.calledOnce(logWithMetadata); @@ -143,7 +151,12 @@ describe('uiSettings/createOrUpgradeSavedConfig', function() { it('does not log when upgrade fails', async () => { const { getUpgradeableConfig, logWithMetadata, run, savedObjectsClient } = setup(); - getUpgradeableConfig.returns({ id: prevVersion, attributes: { buildNum: buildNum - 100 } }); + getUpgradeableConfig.resolves({ + id: prevVersion, + attributes: { buildNum: buildNum - 100 }, + type: '', + references: [], + }); savedObjectsClient.create.callsFake(async () => { throw new Error('foo'); diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.js b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts similarity index 55% rename from src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.js rename to src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts index c175e583ee916..0dc3d5f50e97e 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.js +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts @@ -18,37 +18,38 @@ */ import { defaults } from 'lodash'; +import { SavedObjectsClientContract, SavedObjectAttribute } from 'src/core/server'; +import { Legacy } from 'kibana'; import { getUpgradeableConfig } from './get_upgradeable_config'; -export async function createOrUpgradeSavedConfig(options) { - const { - savedObjectsClient, - version, - buildNum, - logWithMetadata, - onWriteError, - } = options; +interface Options { + savedObjectsClient: SavedObjectsClientContract; + version: string; + buildNum: number; + logWithMetadata: Legacy.Server['logWithMetadata']; + onWriteError?: ( + error: Error, + attributes: Record + ) => Record | undefined; +} +export async function createOrUpgradeSavedConfig( + options: Options +): Promise | undefined> { + const { savedObjectsClient, version, buildNum, logWithMetadata, onWriteError } = options; // try to find an older config we can upgrade const upgradeableConfig = await getUpgradeableConfig({ savedObjectsClient, - version + version, }); // default to the attributes of the upgradeableConfig if available - const attributes = defaults( - { buildNum }, - upgradeableConfig ? upgradeableConfig.attributes : {} - ); + const attributes = defaults({ buildNum }, upgradeableConfig ? upgradeableConfig.attributes : {}); try { // create the new SavedConfig - await savedObjectsClient.create( - 'config', - attributes, - { id: version } - ); + await savedObjectsClient.create('config', attributes, { id: version }); } catch (error) { if (onWriteError) { return onWriteError(error, attributes); @@ -58,9 +59,13 @@ export async function createOrUpgradeSavedConfig(options) { } if (upgradeableConfig) { - logWithMetadata(['plugin', 'elasticsearch'], `Upgrade config from ${upgradeableConfig.id} to ${version}`, { - prevVersion: upgradeableConfig.id, - newVersion: version - }); + logWithMetadata( + ['plugin', 'elasticsearch'], + `Upgrade config from ${upgradeableConfig.id} to ${version}`, + { + prevVersion: upgradeableConfig.id, + newVersion: version, + } + ); } } diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.js b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.ts similarity index 80% rename from src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.js rename to src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.ts index 1108a01167580..350137a81a49b 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.js +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/get_upgradeable_config.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { SavedObjectsClientContract } from 'src/core/server'; import { isConfigVersionUpgradeable } from './is_config_version_upgradeable'; /** @@ -26,18 +26,22 @@ import { isConfigVersionUpgradeable } from './is_config_version_upgradeable'; * @property {string} version * @return {Promise} */ -export async function getUpgradeableConfig({ savedObjectsClient, version }) { +export async function getUpgradeableConfig({ + savedObjectsClient, + version, +}: { + savedObjectsClient: SavedObjectsClientContract; + version: string; +}) { // attempt to find a config we can upgrade const { saved_objects: savedConfigs } = await savedObjectsClient.find({ type: 'config', page: 1, perPage: 1000, sortField: 'buildNum', - sortOrder: 'desc' + sortOrder: 'desc', }); // try to find a config that we can upgrade - return savedConfigs.find(savedConfig => ( - isConfigVersionUpgradeable(savedConfig.id, version) - )); + return savedConfigs.find(savedConfig => isConfigVersionUpgradeable(savedConfig.id, version)); } diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/index.js b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/index.ts similarity index 100% rename from src/legacy/ui/ui_settings/create_or_upgrade_saved_config/index.js rename to src/legacy/ui/ui_settings/create_or_upgrade_saved_config/index.ts diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/integration_tests/create_or_upgrade.test.ts b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/integration_tests/create_or_upgrade.test.ts index 7d5f4e970638d..753e73058af2f 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/integration_tests/create_or_upgrade.test.ts +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/integration_tests/create_or_upgrade.test.ts @@ -24,7 +24,6 @@ import { SavedObjectsClientContract } from 'src/core/server'; import KbnServer from '../../../../server/kbn_server'; import { createTestServers } from '../../../../../test_utils/kbn_server'; -// @ts-ignore import { createOrUpgradeSavedConfig } from '../create_or_upgrade_saved_config'; describe('createOrUpgradeSavedConfig()', () => { diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.test.ts b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.test.ts index 91231da968227..6bb2cb3b87850 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.test.ts +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.test.ts @@ -19,7 +19,6 @@ import expect from '@kbn/expect'; -// @ts-ignore import { isConfigVersionUpgradeable } from './is_config_version_upgradeable'; // @ts-ignore import { pkg } from '../../../utils'; diff --git a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.js b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.ts similarity index 89% rename from src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.js rename to src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.ts index beeba6717f24a..8359f02ffee74 100644 --- a/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.js +++ b/src/legacy/ui/ui_settings/create_or_upgrade_saved_config/is_config_version_upgradeable.ts @@ -20,14 +20,12 @@ import semver from 'semver'; const rcVersionRegex = /^(\d+\.\d+\.\d+)\-rc(\d+)$/i; -function extractRcNumber(version) { +function extractRcNumber(version: string): [string, number] { const match = version.match(rcVersionRegex); - return match - ? [match[1], parseInt(match[2], 10)] - : [version, Infinity]; + return match ? [match[1], parseInt(match[2], 10)] : [version, Infinity]; } -export function isConfigVersionUpgradeable(savedVersion, kibanaVersion) { +export function isConfigVersionUpgradeable(savedVersion: string, kibanaVersion: string): boolean { if ( typeof savedVersion !== 'string' || typeof kibanaVersion !== 'string' || diff --git a/src/legacy/ui/ui_settings/integration_tests/ui_settings_mixin.test.ts b/src/legacy/ui/ui_settings/integration_tests/ui_settings_mixin.test.ts index f522f119a26cc..f43c6436d1c33 100644 --- a/src/legacy/ui/ui_settings/integration_tests/ui_settings_mixin.test.ts +++ b/src/legacy/ui/ui_settings/integration_tests/ui_settings_mixin.test.ts @@ -23,9 +23,7 @@ import expect from '@kbn/expect'; // @ts-ignore import { Config } from '../../../server/config'; -// @ts-ignore import * as uiSettingsServiceFactoryNS from '../ui_settings_service_factory'; -// @ts-ignore import * as getUiSettingsServiceForRequestNS from '../ui_settings_service_for_request'; // @ts-ignore import { uiSettingsMixin } from '../ui_settings_mixin'; @@ -123,7 +121,8 @@ describe('uiSettingsMixin()', () => { foo: 'bar', }); sinon.assert.calledOnce(uiSettingsServiceFactoryStub); - sinon.assert.calledWithExactly(uiSettingsServiceFactoryStub, server, { + sinon.assert.calledWithExactly(uiSettingsServiceFactoryStub, server as any, { + // @ts-ignore foo doesn't exist on Hapi.Server foo: 'bar', overrides: { foo: 'bar', @@ -162,7 +161,12 @@ describe('uiSettingsMixin()', () => { sinon.assert.notCalled(getUiSettingsServiceForRequestStub); const request = {}; decorations.request.getUiSettingsService.call(request); - sinon.assert.calledWith(getUiSettingsServiceForRequestStub, server, request); + sinon.assert.calledWith(getUiSettingsServiceForRequestStub, server as any, request as any, { + overrides: { + foo: 'bar', + }, + getDefaults: sinon.match.func, + }); }); }); diff --git a/src/legacy/ui/ui_settings/routes/delete.js b/src/legacy/ui/ui_settings/routes/delete.ts similarity index 81% rename from src/legacy/ui/ui_settings/routes/delete.js rename to src/legacy/ui/ui_settings/routes/delete.ts index 78e07bbceab01..7825204e6b99b 100644 --- a/src/legacy/ui/ui_settings/routes/delete.js +++ b/src/legacy/ui/ui_settings/routes/delete.ts @@ -16,21 +16,22 @@ * specific language governing permissions and limitations * under the License. */ +import { Legacy } from 'kibana'; -async function handleRequest(request) { +async function handleRequest(request: Legacy.Request) { const { key } = request.params; const uiSettings = request.getUiSettingsService(); await uiSettings.remove(key); return { - settings: await uiSettings.getUserProvided() + settings: await uiSettings.getUserProvided(), }; } export const deleteRoute = { path: '/api/kibana/settings/{key}', method: 'DELETE', - handler: async (request, h) => { - return h.response(await handleRequest(request)); - } + handler: async (request: Legacy.Request) => { + return await handleRequest(request); + }, }; diff --git a/src/legacy/ui/ui_settings/routes/get.js b/src/legacy/ui/ui_settings/routes/get.ts similarity index 84% rename from src/legacy/ui/ui_settings/routes/get.js rename to src/legacy/ui/ui_settings/routes/get.ts index 7e91bc46596b5..3e165a12522bb 100644 --- a/src/legacy/ui/ui_settings/routes/get.js +++ b/src/legacy/ui/ui_settings/routes/get.ts @@ -16,18 +16,19 @@ * specific language governing permissions and limitations * under the License. */ +import { Legacy } from 'kibana'; -async function handleRequest(request) { +async function handleRequest(request: Legacy.Request) { const uiSettings = request.getUiSettingsService(); return { - settings: await uiSettings.getUserProvided() + settings: await uiSettings.getUserProvided(), }; } export const getRoute = { path: '/api/kibana/settings', method: 'GET', - handler: function (request) { + handler(request: Legacy.Request) { return handleRequest(request); - } + }, }; diff --git a/src/legacy/ui/ui_settings/routes/index.js b/src/legacy/ui/ui_settings/routes/index.ts similarity index 100% rename from src/legacy/ui/ui_settings/routes/index.js rename to src/legacy/ui/ui_settings/routes/index.ts diff --git a/src/legacy/ui/ui_settings/routes/integration_tests/lib/servers.ts b/src/legacy/ui/ui_settings/routes/integration_tests/lib/servers.ts index 5b0fbf5a5f256..b076a2a86e166 100644 --- a/src/legacy/ui/ui_settings/routes/integration_tests/lib/servers.ts +++ b/src/legacy/ui/ui_settings/routes/integration_tests/lib/servers.ts @@ -23,6 +23,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import KbnServer from '../../../../../server/kbn_server'; import { createTestServers } from '../../../../../../test_utils/kbn_server'; import { CallCluster } from '../../../../../../legacy/core_plugins/elasticsearch'; +import { IUiSettingsClient } from '../../../ui_settings_service'; let kbnServer: KbnServer; let servers: ReturnType; @@ -33,7 +34,7 @@ interface AllServices { kbnServer: KbnServer; savedObjectsClient: SavedObjectsClientContract; callCluster: CallCluster; - uiSettings: any; + uiSettings: IUiSettingsClient; deleteKibanaIndex: typeof deleteKibanaIndex; } diff --git a/src/legacy/ui/ui_settings/routes/set.js b/src/legacy/ui/ui_settings/routes/set.ts similarity index 70% rename from src/legacy/ui/ui_settings/routes/set.js rename to src/legacy/ui/ui_settings/routes/set.ts index e50c9bf08de3e..1f1ab17a0daf7 100644 --- a/src/legacy/ui/ui_settings/routes/set.js +++ b/src/legacy/ui/ui_settings/routes/set.ts @@ -16,18 +16,18 @@ * specific language governing permissions and limitations * under the License. */ - +import { Legacy } from 'kibana'; import Joi from 'joi'; -async function handleRequest(request) { +async function handleRequest(request: Legacy.Request) { const { key } = request.params; - const { value } = request.payload; + const { value } = request.payload as any; const uiSettings = request.getUiSettingsService(); await uiSettings.set(key, value); return { - settings: await uiSettings.getUserProvided() + settings: await uiSettings.getUserProvided(), }; } @@ -36,16 +36,20 @@ export const setRoute = { method: 'POST', config: { validate: { - params: Joi.object().keys({ - key: Joi.string().required(), - }).default(), + params: Joi.object() + .keys({ + key: Joi.string().required(), + }) + .default(), - payload: Joi.object().keys({ - value: Joi.any().required() - }).required() + payload: Joi.object() + .keys({ + value: Joi.any().required(), + }) + .required(), }, - handler(request) { + handler(request: Legacy.Request) { return handleRequest(request); - } - } + }, + }, }; diff --git a/src/legacy/ui/ui_settings/routes/set_many.js b/src/legacy/ui/ui_settings/routes/set_many.ts similarity index 73% rename from src/legacy/ui/ui_settings/routes/set_many.js rename to src/legacy/ui/ui_settings/routes/set_many.ts index 8e7882f48ef70..18b1046417fec 100644 --- a/src/legacy/ui/ui_settings/routes/set_many.js +++ b/src/legacy/ui/ui_settings/routes/set_many.ts @@ -16,17 +16,17 @@ * specific language governing permissions and limitations * under the License. */ - +import { Legacy } from 'kibana'; import Joi from 'joi'; -async function handleRequest(request) { - const { changes } = request.payload; +async function handleRequest(request: Legacy.Request) { + const { changes } = request.payload as any; const uiSettings = request.getUiSettingsService(); await uiSettings.setMany(changes); return { - settings: await uiSettings.getUserProvided() + settings: await uiSettings.getUserProvided(), }; } @@ -35,12 +35,16 @@ export const setManyRoute = { method: 'POST', config: { validate: { - payload: Joi.object().keys({ - changes: Joi.object().unknown(true).required() - }).required() + payload: Joi.object() + .keys({ + changes: Joi.object() + .unknown(true) + .required(), + }) + .required(), }, - handler(request) { + handler(request: Legacy.Request) { return handleRequest(request); - } - } + }, + }, }; diff --git a/src/legacy/ui/ui_settings/ui_settings_service.mock.ts b/src/legacy/ui/ui_settings/ui_settings_service.mock.ts new file mode 100644 index 0000000000000..7c1a17ebd447c --- /dev/null +++ b/src/legacy/ui/ui_settings/ui_settings_service.mock.ts @@ -0,0 +1,40 @@ +/* + * 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 { IUiSettingsClient } from './ui_settings_service'; + +const createServiceMock = () => { + const mocked: jest.Mocked = { + getDefaults: jest.fn(), + get: jest.fn(), + getAll: jest.fn(), + getUserProvided: jest.fn(), + setMany: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + removeMany: jest.fn(), + isOverridden: jest.fn(), + }; + mocked.get.mockResolvedValue(false); + return mocked; +}; + +export const uiSettingsServiceMock = { + create: createServiceMock, +}; diff --git a/src/legacy/ui/ui_settings/ui_settings_service.test.ts b/src/legacy/ui/ui_settings/ui_settings_service.test.ts index bb407d7b3a91b..f37076b27ad6f 100644 --- a/src/legacy/ui/ui_settings/ui_settings_service.test.ts +++ b/src/legacy/ui/ui_settings/ui_settings_service.test.ts @@ -21,9 +21,7 @@ import expect from '@kbn/expect'; import Chance from 'chance'; import sinon from 'sinon'; -// @ts-ignore import { UiSettingsService } from './ui_settings_service'; -// @ts-ignore import * as createOrUpgradeSavedConfigNS from './create_or_upgrade_saved_config/create_or_upgrade_saved_config'; import { createObjectsClientStub, savedObjectsClientErrors } from './create_objects_client_stub'; @@ -43,7 +41,7 @@ describe('ui settings', () => { const sandbox = sinon.createSandbox(); function setup(options: SetupOptions = {}) { - const { getDefaults, defaults = {}, overrides, esDocSource = {} } = options; + const { getDefaults, defaults = {}, overrides = {}, esDocSource = {} } = options; const savedObjectsClient = createObjectsClientStub(esDocSource); @@ -233,7 +231,7 @@ describe('ui settings', () => { }); try { - await uiSettings.setMany(['bar', 'foo']); + await uiSettings.setMany({ baz: 'baz', foo: 'foo' }); } catch (error) { expect(error.message).to.be('Unable to update "foo" because it is overridden'); } @@ -489,7 +487,7 @@ describe('ui settings', () => { it('pulls user configuration from ES', async () => { const esDocSource = {}; const { uiSettings, assertGetQuery } = setup({ esDocSource }); - await uiSettings.get(); + await uiSettings.get('any'); assertGetQuery(); }); diff --git a/src/legacy/ui/ui_settings/ui_settings_service.js b/src/legacy/ui/ui_settings/ui_settings_service.ts similarity index 52% rename from src/legacy/ui/ui_settings/ui_settings_service.js rename to src/legacy/ui/ui_settings/ui_settings_service.ts index 9f79ed2dbe168..57312140b16b3 100644 --- a/src/legacy/ui/ui_settings/ui_settings_service.js +++ b/src/legacy/ui/ui_settings/ui_settings_service.ts @@ -16,28 +16,77 @@ * specific language governing permissions and limitations * under the License. */ - +import { Legacy } from 'kibana'; import { defaultsDeep } from 'lodash'; import Boom from 'boom'; +import { SavedObjectsClientContract, SavedObjectAttribute } from 'src/core/server'; import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; +export interface UiSettingsServiceOptions { + type: string; + id: string; + buildNum: number; + savedObjectsClient: SavedObjectsClientContract; + overrides?: Record; + getDefaults?: () => Record; + logWithMetadata?: Legacy.Server['logWithMetadata']; +} + +interface ReadOptions { + ignore401Errors?: boolean; + autoCreateOrUpgradeIfMissing?: boolean; +} + +interface UserProvidedValue { + userValue?: SavedObjectAttribute; + isOverridden?: boolean; +} + +type UiSettingsRawValue = UiSettingsParams & UserProvidedValue; + +type UserProvided = Record; +type UiSettingsRaw = Record; + +type UiSettingsType = 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string'; + +interface UiSettingsParams { + name: string; + value: SavedObjectAttribute; + description: string; + category: string[]; + options?: string[]; + optionLabels?: Record; + requiresPageReload?: boolean; + readonly?: boolean; + type?: UiSettingsType; +} + +export interface IUiSettingsClient { + getDefaults: () => Promise>; + get: (key: string) => Promise; + getAll: () => Promise>; + getUserProvided: () => Promise; + setMany: (changes: Record) => Promise; + set: (key: string, value: T) => Promise; + remove: (key: string) => Promise; + removeMany: (keys: string[]) => Promise; + isOverridden: (key: string) => boolean; +} /** * Service that provides access to the UiSettings stored in elasticsearch. * @class UiSettingsService */ -export class UiSettingsService { - /** - * @constructor - * @param {Object} options - * @property {string} options.type type of SavedConfig object - * @property {string} options.id id of SavedConfig object - * @property {number} options.buildNum - * @property {SavedObjectsClient} options.savedObjectsClient - * @property {Function} [options.getDefaults] - * @property {Function} [options.log] - */ - constructor(options) { +export class UiSettingsService implements IUiSettingsClient { + private readonly _type: UiSettingsServiceOptions['type']; + private readonly _id: UiSettingsServiceOptions['id']; + private readonly _buildNum: UiSettingsServiceOptions['buildNum']; + private readonly _savedObjectsClient: UiSettingsServiceOptions['savedObjectsClient']; + private readonly _overrides: NonNullable; + private readonly _getDefaults: NonNullable; + private readonly _logWithMetadata: NonNullable; + + constructor(options: UiSettingsServiceOptions) { const { type, id, @@ -65,36 +114,38 @@ export class UiSettingsService { } // returns a Promise for the value of the requested setting - async get(key) { + async get(key: string): Promise { const all = await this.getAll(); return all[key]; } - async getAll() { + async getAll() { const raw = await this.getRaw(); - return Object.keys(raw) - .reduce((all, key) => { + return Object.keys(raw).reduce( + (all, key) => { const item = raw[key]; - const hasUserValue = 'userValue' in item; - all[key] = hasUserValue ? item.userValue : item.value; + all[key] = ('userValue' in item ? item.userValue : item.value) as T; return all; - }, {}); + }, + {} as Record + ); } - async getRaw() { + // NOTE: should be a private method + async getRaw(): Promise { const userProvided = await this.getUserProvided(); return defaultsDeep(userProvided, await this.getDefaults()); } - async getUserProvided(options) { - const userProvided = {}; + async getUserProvided(options: ReadOptions = {}): Promise { + const userProvided: UserProvided = {}; // write the userValue for each key stored in the saved object that is not overridden for (const [key, userValue] of Object.entries(await this._read(options))) { if (userValue !== null && !this.isOverridden(key)) { userProvided[key] = { - userValue + userValue, }; } } @@ -102,45 +153,51 @@ export class UiSettingsService { // write all overridden keys, dropping the userValue is override is null and // adding keys for overrides that are not in saved object for (const [key, userValue] of Object.entries(this._overrides)) { - userProvided[key] = userValue === null - ? { isOverridden: true } - : { isOverridden: true, userValue }; + userProvided[key] = + userValue === null ? { isOverridden: true } : { isOverridden: true, userValue }; } return userProvided; } - async setMany(changes) { + async setMany(changes: Record) { await this._write({ changes }); } - async set(key, value) { + async set(key: string, value: T) { await this.setMany({ [key]: value }); } - async remove(key) { + async remove(key: string) { await this.set(key, null); } - async removeMany(keys) { - const changes = {}; + async removeMany(keys: string[]) { + const changes: Record = {}; keys.forEach(key => { changes[key] = null; }); await this.setMany(changes); } - isOverridden(key) { + isOverridden(key: string) { return this._overrides.hasOwnProperty(key); } - assertUpdateAllowed(key) { + // NOTE: should be private method + assertUpdateAllowed(key: string) { if (this.isOverridden(key)) { throw Boom.badRequest(`Unable to update "${key}" because it is overridden`); } } - async _write({ changes, autoCreateOrUpgradeIfMissing = true }) { + private async _write({ + changes, + autoCreateOrUpgradeIfMissing = true, + }: { + changes: Record; + autoCreateOrUpgradeIfMissing?: boolean; + }) { for (const key of Object.keys(changes)) { this.assertUpdateAllowed(key); } @@ -162,72 +219,77 @@ export class UiSettingsService { await this._write({ changes, - autoCreateOrUpgradeIfMissing: false + autoCreateOrUpgradeIfMissing: false, }); } } - async _read(options = {}) { - const { - ignore401Errors = false, - autoCreateOrUpgradeIfMissing = true - } = options; - + private async _read({ + ignore401Errors = false, + autoCreateOrUpgradeIfMissing = true, + }: ReadOptions = {}): Promise> { const { isConflictError, isNotFoundError, isForbiddenError, - isEsUnavailableError, isNotAuthorizedError, } = this._savedObjectsClient.errors; - const isIgnorableError = error => ( - isForbiddenError(error) || - isEsUnavailableError(error) || - (ignore401Errors && isNotAuthorizedError(error)) - ); - try { const resp = await this._savedObjectsClient.get(this._type, this._id); return resp.attributes; } catch (error) { if (isNotFoundError(error) && autoCreateOrUpgradeIfMissing) { - const failedUpgradeAttributes = await createOrUpgradeSavedConfig({ + const failedUpgradeAttributes = await createOrUpgradeSavedConfig({ savedObjectsClient: this._savedObjectsClient, version: this._id, buildNum: this._buildNum, logWithMetadata: this._logWithMetadata, - async onWriteError(error, attributes) { - if (isConflictError(error)) { + onWriteError(writeError, attributes) { + if (isConflictError(writeError)) { // trigger `!failedUpgradeAttributes` check below, since another // request caused the uiSettings object to be created so we can // just re-read - return false; + return; } - if (isNotAuthorizedError(error) || isForbiddenError(error)) { + if (isNotAuthorizedError(writeError) || isForbiddenError(writeError)) { return attributes; } - throw error; - } + throw writeError; + }, }); if (!failedUpgradeAttributes) { return await this._read({ - ...options, - autoCreateOrUpgradeIfMissing: false + ignore401Errors, + autoCreateOrUpgradeIfMissing: false, }); } return failedUpgradeAttributes; } - if (isIgnorableError(error)) { + if (this.isIgnorableError(error, ignore401Errors)) { return {}; } throw error; } } + + private isIgnorableError(error: Error, ignore401Errors: boolean) { + const { + isForbiddenError, + isEsUnavailableError, + isNotAuthorizedError, + } = this._savedObjectsClient.errors; + + return ( + isForbiddenError(error) || + isEsUnavailableError(error) || + (ignore401Errors && isNotAuthorizedError(error)) + ); + } } diff --git a/src/legacy/ui/ui_settings/ui_settings_service_factory.js b/src/legacy/ui/ui_settings/ui_settings_service_factory.ts similarity index 59% rename from src/legacy/ui/ui_settings/ui_settings_service_factory.js rename to src/legacy/ui/ui_settings/ui_settings_service_factory.ts index f83a0d9825557..9e1384494161c 100644 --- a/src/legacy/ui/ui_settings/ui_settings_service_factory.js +++ b/src/legacy/ui/ui_settings/ui_settings_service_factory.ts @@ -16,29 +16,30 @@ * specific language governing permissions and limitations * under the License. */ +import { Legacy } from 'kibana'; +import { + IUiSettingsClient, + UiSettingsService, + UiSettingsServiceOptions, +} from './ui_settings_service'; -import { UiSettingsService } from './ui_settings_service'; - +export type UiSettingsServiceFactoryOptions = Pick< + UiSettingsServiceOptions, + 'savedObjectsClient' | 'getDefaults' | 'overrides' +>; /** * Create an instance of UiSettingsService that will use the - * passed `callCluster` function to communicate with elasticsearch + * passed `savedObjectsClient` to communicate with elasticsearch * - * @param {Hapi.Server} server - * @param {Object} options - * @property {AsyncFunction} options.callCluster function that accepts a method name and - * param object which causes a request via some elasticsearch client - * @property {AsyncFunction} [options.getDefaults] async function that returns defaults/details about - * the uiSettings. - * @return {UiSettingsService} + * @return {IUiSettingsClient} */ -export function uiSettingsServiceFactory(server, options) { +export function uiSettingsServiceFactory( + server: Legacy.Server, + options: UiSettingsServiceFactoryOptions +): IUiSettingsClient { const config = server.config(); - const { - savedObjectsClient, - getDefaults, - overrides, - } = options; + const { savedObjectsClient, getDefaults, overrides } = options; return new UiSettingsService({ type: 'config', @@ -47,6 +48,6 @@ export function uiSettingsServiceFactory(server, options) { savedObjectsClient, getDefaults, overrides, - logWithMetadata: (...args) => server.logWithMetadata(...args), + logWithMetadata: server.logWithMetadata, }); } diff --git a/src/legacy/ui/ui_settings/ui_settings_service_for_request.js b/src/legacy/ui/ui_settings/ui_settings_service_for_request.ts similarity index 74% rename from src/legacy/ui/ui_settings/ui_settings_service_for_request.js rename to src/legacy/ui/ui_settings/ui_settings_service_for_request.ts index 422c9cc14f833..e265ad5f1e115 100644 --- a/src/legacy/ui/ui_settings/ui_settings_service_for_request.js +++ b/src/legacy/ui/ui_settings/ui_settings_service_for_request.ts @@ -17,8 +17,11 @@ * under the License. */ +import { Legacy } from 'kibana'; import { uiSettingsServiceFactory } from './ui_settings_service_factory'; +import { IUiSettingsClient, UiSettingsServiceOptions } from './ui_settings_service'; +type Options = Pick; /** * Get/create an instance of UiSettingsService bound to a specific request. * Each call is cached (keyed on the request object itself) and subsequent @@ -28,20 +31,20 @@ import { uiSettingsServiceFactory } from './ui_settings_service_factory'; * @param {Hapi.Server} server * @param {Hapi.Request} request * @param {Object} [options={}] - * @property {AsyncFunction} [options.getDefaults] async function that returns defaults/details about - * the uiSettings. - * @return {UiSettingsService} + + * @return {IUiSettingsClient} */ -export function getUiSettingsServiceForRequest(server, request, options = {}) { - const { - getDefaults, - overrides, - } = options; +export function getUiSettingsServiceForRequest( + server: Legacy.Server, + request: Legacy.Request, + options: Options +): IUiSettingsClient { + const { getDefaults, overrides } = options; const uiSettingsService = uiSettingsServiceFactory(server, { getDefaults, overrides, - savedObjectsClient: request.getSavedObjectsClient() + savedObjectsClient: request.getSavedObjectsClient(), }); return uiSettingsService; diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts index bd45cec316dfc..91809fbaede03 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -6,6 +6,7 @@ import { Legacy } from 'kibana'; import { setupRequest } from './setup_request'; +import { uiSettingsServiceMock } from '../../../../../../../src/legacy/ui/ui_settings/ui_settings_service.mock'; function getMockRequest() { const callWithRequestSpy = jest.fn(); @@ -125,8 +126,10 @@ describe('setupRequest', () => { it('should set `ignore_throttled=true` if `includeFrozen=false`', async () => { const { mockRequest, callWithRequestSpy } = getMockRequest(); + const uiSettingsService = uiSettingsServiceMock.create(); // mock includeFrozen to return false - mockRequest.getUiSettingsService = () => ({ get: async () => false }); + uiSettingsService.get.mockResolvedValue(false); + mockRequest.getUiSettingsService = () => uiSettingsService; const { client } = await setupRequest(mockRequest); await client.search({}); const params = callWithRequestSpy.mock.calls[0][2]; @@ -136,8 +139,10 @@ describe('setupRequest', () => { it('should set `ignore_throttled=false` if `includeFrozen=true`', async () => { const { mockRequest, callWithRequestSpy } = getMockRequest(); + const uiSettingsService = uiSettingsServiceMock.create(); // mock includeFrozen to return true - mockRequest.getUiSettingsService = () => ({ get: async () => true }); + uiSettingsService.get.mockResolvedValue(true); + mockRequest.getUiSettingsService = () => uiSettingsService; const { client } = await setupRequest(mockRequest); await client.search({}); const params = callWithRequestSpy.mock.calls[0][2]; From e3dc52500039b71ac7b97e50f68b8825e8ab6992 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 4 Oct 2019 17:45:32 +0300 Subject: [PATCH 05/33] TSVB doesn't allow to perform basic functions showing no fields available (#47239) Closes: #47226 --- .../vis_type_timeseries/common/es_types.js | 26 ------------------- .../public/components/aggs/filter_ratio.js | 4 +-- .../public/components/aggs/percentile.js | 4 +-- .../aggs/percentile_rank/percentile_rank.js | 4 +-- .../public/components/aggs/std_agg.js | 4 +-- .../public/components/aggs/std_deviation.js | 4 +-- .../public/components/aggs/top_hit.js | 13 +++++----- .../public/components/annotations_editor.js | 4 +-- .../public/components/index_pattern.js | 4 +-- 9 files changed, 20 insertions(+), 47 deletions(-) delete mode 100644 src/legacy/core_plugins/vis_type_timeseries/common/es_types.js diff --git a/src/legacy/core_plugins/vis_type_timeseries/common/es_types.js b/src/legacy/core_plugins/vis_type_timeseries/common/es_types.js deleted file mode 100644 index 7e6a53753fae3..0000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/common/es_types.js +++ /dev/null @@ -1,26 +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. - */ - -export const ES_TYPES = { - NUMBER: 'number', - STRING: 'string', - KEYWORD: 'keyword', - TEXT: 'text', - DATE: 'date', -}; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js index abbf2dcaafc81..92558559845be 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js @@ -35,7 +35,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; import { METRIC_TYPES } from '../../../common/metric_types'; export const FilterRatioAgg = props => { @@ -56,7 +56,7 @@ export const FilterRatioAgg = props => { const model = { ...defaults, ...props.model }; const htmlId = htmlIdGenerator(); - const restrictFields = model.metric_agg === METRIC_TYPES.CARDINALITY ? [] : [ES_FIELD_TYPES.NUMBER]; + const restrictFields = model.metric_agg === METRIC_TYPES.CARDINALITY ? [] : [KBN_FIELD_TYPES.NUMBER]; return ( { const { series, panel, fields } = props; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js index 9cbb8ffb716f5..4b8b356f2af48 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js @@ -33,14 +33,14 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; import { METRIC_TYPES } from '../../../common/metric_types'; export function StandardAgg(props) { const { model, panel, series, fields, uiRestrictions } = props; const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); - const restrictFields = model.type === METRIC_TYPES.CARDINALITY ? [] : [ES_FIELD_TYPES.NUMBER]; + const restrictFields = model.type === METRIC_TYPES.CARDINALITY ? [] : [KBN_FIELD_TYPES.NUMBER]; const indexPattern = (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js index 8ccb37adeda46..c078bac8d7f9c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js @@ -36,9 +36,9 @@ import { EuiSpacer, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; -const RESTRICT_FIELDS = [ES_FIELD_TYPES.NUMBER]; +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER]; const StandardDeviationAggUi = props => { const { series, panel, fields, intl } = props; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js index 65bec274021ed..1439768b95643 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js @@ -35,7 +35,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; import { PANEL_TYPES } from '../../../common/panel_types'; const isFieldTypeEnabled = (fieldRestrictions, fieldType) => @@ -44,7 +44,7 @@ const isFieldTypeEnabled = (fieldRestrictions, fieldType) => const getAggWithOptions = (field = {}, fieldTypesRestriction) => { if (isFieldTypeEnabled(fieldTypesRestriction, field.type)) { switch (field.type) { - case ES_FIELD_TYPES.NUMBER: + case KBN_FIELD_TYPES.NUMBER: return [ { label: i18n.translate('visTypeTimeseries.topHit.aggWithOptions.averageLabel', { @@ -71,8 +71,7 @@ const getAggWithOptions = (field = {}, fieldTypesRestriction) => { value: 'sum', }, ]; - case ES_FIELD_TYPES.KEYWORD: - case ES_FIELD_TYPES.STRING: + case KBN_FIELD_TYPES.STRING: return [ { label: i18n.translate('visTypeTimeseries.topHit.aggWithOptions.concatenate', { @@ -102,7 +101,7 @@ const getOrderOptions = () => [ }, ]; -const ORDER_DATE_RESTRICT_FIELDS = [ES_FIELD_TYPES.DATE]; +const ORDER_DATE_RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; const TopHitAggUi = props => { const { fields, series, panel } = props; @@ -120,8 +119,8 @@ const TopHitAggUi = props => { PANEL_TYPES.METRIC, PANEL_TYPES.MARKDOWN, ].includes(panel.type) - ? [ES_FIELD_TYPES.NUMBER, ES_FIELD_TYPES.KEYWORD, ES_FIELD_TYPES.STRING] - : [ES_FIELD_TYPES.NUMBER]; + ? [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING] + : [KBN_FIELD_TYPES.NUMBER]; const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/annotations_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/annotations_editor.js index cf3f5c69c8b0c..4357d4737fbef 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/annotations_editor.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/annotations_editor.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import _ from 'lodash'; import { collectionActions } from './lib/collection_actions'; -import { ES_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { AddDeleteButtons } from './add_delete_buttons'; import { ColorPicker } from './color_picker'; import { FieldSelect } from './aggs/field_select'; @@ -57,7 +57,7 @@ function newAnnotation() { }; } -const RESTRICT_FIELDS = [ES_FIELD_TYPES.DATE]; +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; export class AnnotationsEditor extends Component { constructor(props) { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/index_pattern.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/index_pattern.js index 61f67a1bde362..d954b0ab93b56 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/index_pattern.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/index_pattern.js @@ -33,7 +33,7 @@ import { FieldSelect } from './aggs/field_select'; import { createSelectHandler } from './lib/create_select_handler'; import { createTextHandler } from './lib/create_text_handler'; import { YesNo } from './yes_no'; -import { ES_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { FormValidationContext } from '../contexts/form_validation_context'; import { isGteInterval, @@ -47,7 +47,7 @@ import { PANEL_TYPES } from '../../common/panel_types'; import { isTimerangeModeEnabled } from '../lib/check_ui_restrictions'; import { VisDataContext } from '../contexts/vis_data_context'; -const RESTRICT_FIELDS = [ES_FIELD_TYPES.DATE]; +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; const validateIntervalValue = intervalValue => { const isAutoOrGteInterval = isGteInterval(intervalValue) || isAutoInterval(intervalValue); From f4c697ba241e92d690bc5cff901222a8a6cc757e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 4 Oct 2019 17:08:14 +0200 Subject: [PATCH 06/33] revert bad fix (#47200) --- src/legacy/ui/public/vislib/lib/axis/axis_labels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/ui/public/vislib/lib/axis/axis_labels.js b/src/legacy/ui/public/vislib/lib/axis/axis_labels.js index 5669b255e5dbb..5db747f8a76ba 100644 --- a/src/legacy/ui/public/vislib/lib/axis/axis_labels.js +++ b/src/legacy/ui/public/vislib/lib/axis/axis_labels.js @@ -105,7 +105,7 @@ export class AxisLabels { selection.selectAll('.tick text') .text(function (d) { const par = d3.select(this.parentNode).node(); - const myPos = scaleStartPad + self.axisScale.scale(d); + const myPos = scaleStartPad + (config.isHorizontal() ? self.axisScale.scale(d) : maxSize - self.axisScale.scale(d)); const mySize = (config.isHorizontal() ? par.getBBox().width : par.getBBox().height) * padding; const halfSize = mySize / 2; From 3bc803e371bf838c69009c056f744158fa436241 Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 4 Oct 2019 08:15:06 -0700 Subject: [PATCH 07/33] [mkdirp] remove in favor of recursive fs.mkdir (#47251) * [mkdirp] remove in favor of recursive fs.mkdir * add eslint rule to educate future contributors * reword the eslint error message to prevent copy-pasting callback code * Commit updated kbn-pm dist Signed-off-by: Tyler Smalley * Fix typo Signed-off-by: Tyler Smalley --- package.json | 2 - packages/eslint-config-kibana/.eslintrc.js | 5 +++ packages/kbn-es/package.json | 1 - packages/kbn-es/src/artifact.js | 4 +- .../src/cli_commands/build_snapshots.js | 3 +- packages/kbn-es/src/utils/cache.js | 3 +- packages/kbn-es/src/utils/decompress.js | 5 +-- packages/kbn-es/src/utils/decompress.test.js | 7 ++- .../kbn-es/src/utils/extract_config_files.js | 3 +- .../rules/module_migration.js | 20 ++++++++- packages/kbn-pm/dist/index.js | 43 +++++++++---------- packages/kbn-pm/package.json | 2 - packages/kbn-pm/src/utils/fs.ts | 10 ++--- .../src/mocha/junit_report_generation.js | 5 +-- renovate.json5 | 8 ---- .../reload_logging_config.test.js | 3 +- src/cli_plugin/install/download.test.js | 4 +- src/cli_plugin/install/install.js | 9 ++-- src/cli_plugin/install/kibana.test.js | 3 +- src/cli_plugin/install/pack.test.js | 5 ++- src/cli_plugin/install/zip.js | 7 ++- src/cli_plugin/list/list.test.js | 11 +++-- src/cli_plugin/remove/remove.test.js | 11 +++-- src/dev/build/lib/fs.js | 7 ++- src/dev/build/tasks/nodejs/download.js | 5 ++- .../tasks/nodejs/extract_node_builds_task.js | 6 +-- .../build/tasks/patch_native_modules_task.js | 5 +-- src/dev/jest/junit_reporter.js | 5 +-- src/es_archiver/actions/save.js | 7 +-- src/legacy/core_plugins/kibana/index.js | 8 ++-- src/legacy/server/sass/build.js | 7 ++- .../ui/ui_bundles/ui_bundles_controller.js | 7 ++- .../dynamic_dll_plugin/dll_compiler.js | 5 +-- test/functional/services/failure_debugging.ts | 15 +++---- test/functional/services/screenshots.ts | 19 ++++---- test/functional/services/snapshots.ts | 17 ++++---- utilities/visual_regression.js | 5 +-- .../code/server/__tests__/git_operations.ts | 5 +-- .../code/server/__tests__/lsp_service.ts | 3 +- .../server/__tests__/workspace_handler.ts | 9 ++-- .../code/server/lsp/controller.test.ts | 5 +-- .../code/server/lsp/request_expander.test.ts | 13 +++--- .../code/server/lsp/workspace_handler.test.ts | 7 ++- .../code/server/lsp/workspace_handler.ts | 14 ++---- .../plugins/code/server/repository_service.ts | 14 ++---- .../server/browsers/download/download.js | 5 +-- x-pack/package.json | 2 - .../functional/apps/code/repo_archiver.ts | 5 +-- .../reporting/functional/lib/compare_pdfs.js | 9 ++-- .../reporting/functional/lib/compare_pngs.js | 7 ++- x-pack/test/reporting/functional/reporting.js | 8 ++-- yarn.lock | 7 --- 52 files changed, 177 insertions(+), 228 deletions(-) diff --git a/package.json b/package.json index e60819102eb91..22d042dc169ef 100644 --- a/package.json +++ b/package.json @@ -195,7 +195,6 @@ "markdown-it": "^8.4.1", "mini-css-extract-plugin": "0.8.0", "minimatch": "^3.0.4", - "mkdirp": "0.5.1", "moment": "^2.20.1", "moment-timezone": "^0.5.14", "mustache": "2.3.2", @@ -322,7 +321,6 @@ "@types/lru-cache": "^5.1.0", "@types/markdown-it": "^0.0.7", "@types/minimatch": "^2.0.29", - "@types/mkdirp": "^0.5.2", "@types/mocha": "^5.2.7", "@types/moment-timezone": "^0.5.8", "@types/mustache": "^0.8.31", diff --git a/packages/eslint-config-kibana/.eslintrc.js b/packages/eslint-config-kibana/.eslintrc.js index c3e8f5601e479..4412ce81368a1 100644 --- a/packages/eslint-config-kibana/.eslintrc.js +++ b/packages/eslint-config-kibana/.eslintrc.js @@ -22,6 +22,11 @@ module.exports = { from: 'expect.js', to: '@kbn/expect', }, + { + from: 'mkdirp', + to: false, + disallowedMessage: `Don't use 'mkdirp', use the new { recursive: true } option of Fs.mkdir instead` + }, { from: 'x-pack', toRelative: 'x-pack', diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index 1064d719af837..7c0b960d18093 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -14,7 +14,6 @@ "execa": "^1.0.0", "getopts": "^2.2.4", "glob": "^7.1.2", - "mkdirp": "^0.5.1", "node-fetch": "^2.6.0", "simple-git": "^1.91.0", "tar-fs": "^1.16.3", diff --git a/packages/kbn-es/src/artifact.js b/packages/kbn-es/src/artifact.js index 3398663a2acf3..19d95e82fe480 100644 --- a/packages/kbn-es/src/artifact.js +++ b/packages/kbn-es/src/artifact.js @@ -22,7 +22,6 @@ const AbortController = require('abort-controller'); const fs = require('fs'); const { promisify } = require('util'); const { pipeline, Transform } = require('stream'); -const mkdirp = require('mkdirp'); const chalk = require('chalk'); const { createHash } = require('crypto'); const path = require('path'); @@ -285,7 +284,8 @@ exports.Artifact = class Artifact { let first500Bytes = Buffer.alloc(0); let contentLength = 0; - mkdirp.sync(path.dirname(tmpPath)); + fs.mkdirSync(path.dirname(tmpPath), { recursive: true }); + await asyncPipeline( resp.body, new Transform({ diff --git a/packages/kbn-es/src/cli_commands/build_snapshots.js b/packages/kbn-es/src/cli_commands/build_snapshots.js index eca904c144af4..7c2dcff4aa796 100644 --- a/packages/kbn-es/src/cli_commands/build_snapshots.js +++ b/packages/kbn-es/src/cli_commands/build_snapshots.js @@ -24,7 +24,6 @@ const { pipeline, Transform } = require('stream'); const Fs = require('fs'); const getopts = require('getopts'); -const mkdirp = require('mkdirp'); const del = require('del'); const { buildSnapshot, log } = require('../utils'); @@ -49,7 +48,7 @@ exports.run = async (defaults = {}) => { const outputDir = resolve(process.cwd(), options.output); del.sync(outputDir); - mkdirp.sync(outputDir); + Fs.mkdirSync(outputDir, { recursive: true }); for (const license of ['oss', 'trial']) { for (const platform of ['darwin', 'win32', 'linux']) { diff --git a/packages/kbn-es/src/utils/cache.js b/packages/kbn-es/src/utils/cache.js index 01f4b9f374c92..a8bc663ecf096 100644 --- a/packages/kbn-es/src/utils/cache.js +++ b/packages/kbn-es/src/utils/cache.js @@ -18,7 +18,6 @@ */ const fs = require('fs'); -const mkdirp = require('mkdirp'); const path = require('path'); exports.readMeta = function readMeta(file) { @@ -48,6 +47,6 @@ exports.writeMeta = function readMeta(file, details = {}) { ...details, }; - mkdirp.sync(path.dirname(file)); + fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(`${file}.meta`, JSON.stringify(meta, null, 2)); }; diff --git a/packages/kbn-es/src/utils/decompress.js b/packages/kbn-es/src/utils/decompress.js index a0e0d832ff739..b4299594c5062 100644 --- a/packages/kbn-es/src/utils/decompress.js +++ b/packages/kbn-es/src/utils/decompress.js @@ -21,7 +21,6 @@ const fs = require('fs'); const path = require('path'); const yauzl = require('yauzl'); -const mkdirp = require('mkdirp'); const zlib = require('zlib'); const tarFs = require('tar-fs'); @@ -38,7 +37,7 @@ function decompressTarball(archive, dirPath) { } function decompressZip(input, output) { - mkdirp.sync(output); + fs.mkdirSync(output, { recursive: true }); return new Promise((resolve, reject) => { yauzl.open(input, { lazyEntries: true }, (err, zipfile) => { if (err) { @@ -63,7 +62,7 @@ function decompressZip(input, output) { const fileName = path.resolve(output, zipPath); if (/\/$/.test(entry.fileName)) { - mkdirp.sync(fileName); + fs.mkdirSync(fileName, { recursive: true }); zipfile.readEntry(); } else { // file entry diff --git a/packages/kbn-es/src/utils/decompress.test.js b/packages/kbn-es/src/utils/decompress.test.js index ff36e95212576..a267950abc7a2 100644 --- a/packages/kbn-es/src/utils/decompress.test.js +++ b/packages/kbn-es/src/utils/decompress.test.js @@ -20,7 +20,6 @@ const { decompress } = require('./decompress'); const fs = require('fs'); const path = require('path'); -const mkdirp = require('mkdirp'); const del = require('del'); const os = require('os'); @@ -34,9 +33,9 @@ const zipSnapshot = path.resolve(dataFolder, 'snapshot.zip'); const tarGzSnapshot = path.resolve(dataFolder, 'snapshot.tar.gz'); beforeEach(() => { - mkdirp.sync(tmpFolder); - mkdirp.sync(dataFolder); - mkdirp.sync(esFolder); + fs.mkdirSync(tmpFolder, { recursive: true }); + fs.mkdirSync(dataFolder, { recursive: true }); + fs.mkdirSync(esFolder, { recursive: true }); fs.copyFileSync(path.resolve(fixturesFolder, 'snapshot.zip'), zipSnapshot); fs.copyFileSync(path.resolve(fixturesFolder, 'snapshot.tar.gz'), tarGzSnapshot); diff --git a/packages/kbn-es/src/utils/extract_config_files.js b/packages/kbn-es/src/utils/extract_config_files.js index 7ad75dcbbf340..a8a44a149e9b3 100644 --- a/packages/kbn-es/src/utils/extract_config_files.js +++ b/packages/kbn-es/src/utils/extract_config_files.js @@ -19,7 +19,6 @@ const path = require('path'); const fs = require('fs'); -const mkdirp = require('mkdirp'); /** * Copies config references to an absolute path to @@ -62,7 +61,7 @@ function copyFileSync(src, dest) { const destPath = path.dirname(dest); if (!fs.existsSync(destPath)) { - mkdirp(destPath); + fs.mkdirSync(destPath, { recursive: true }); } fs.writeFileSync(dest, fs.readFileSync(src)); diff --git a/packages/kbn-eslint-plugin-eslint/rules/module_migration.js b/packages/kbn-eslint-plugin-eslint/rules/module_migration.js index c5f2aff9bfdcf..8119d338ee536 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/module_migration.js +++ b/packages/kbn-eslint-plugin-eslint/rules/module_migration.js @@ -31,6 +31,14 @@ function checkModuleNameNode(context, mappings, node) { let newSource; + if (mapping.to === false) { + context.report({ + message: mapping.disallowedMessage || `Importing "${mapping.from}" is not allowed`, + loc: node.loc, + }); + return; + } + // support for toRelative added to migrate away from X-Pack being bundled // within node modules. after that migration, this can be removed. if (mapping.toRelative) { @@ -66,11 +74,21 @@ module.exports = { type: 'string', }, to: { - type: 'string', + anyOf: [ + { + type: 'string', + }, + { + const: false, + }, + ], }, toRelative: { type: 'string', }, + disallowedMessage: { + type: 'string', + }, }, anyOf: [ { diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index b054e21c856a2..5d2f5c786f2c2 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -2697,11 +2697,11 @@ async function linkProjectExecutables(projectsByName, projectGraph) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unlink", function() { return unlink; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copyDirectory", function() { return copyDirectory; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "chmod", function() { return chmod; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readFile", function() { return readFile; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "chmod", function() { return chmod; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mkdirp", function() { return mkdirp; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unlink", function() { return unlink; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copyDirectory", function() { return copyDirectory; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isSymlink", function() { return isSymlink; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isDirectory", function() { return isDirectory; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFile", function() { return isFile; }); @@ -2710,14 +2710,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var cmd_shim__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cmd_shim__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(23); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var mkdirp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31); -/* harmony import */ var mkdirp__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(mkdirp__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(33); -/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(ncp__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(16); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_4__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(29); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_5__); +/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(33); +/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ncp__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(29); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_4__); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -2741,16 +2739,17 @@ __webpack_require__.r(__webpack_exports__); - -const lstat = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.lstat); -const readFile = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFile); -const symlink = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.symlink); -const chmod = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.chmod); -const cmdShim = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(cmd_shim__WEBPACK_IMPORTED_MODULE_0___default.a); -const mkdirp = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(mkdirp__WEBPACK_IMPORTED_MODULE_2___default.a); -const unlink = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.unlink); -const copyDirectory = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(ncp__WEBPACK_IMPORTED_MODULE_3__["ncp"]); - +const lstat = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.lstat); +const readFile = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFile); +const symlink = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.symlink); +const chmod = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.chmod); +const cmdShim = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(cmd_shim__WEBPACK_IMPORTED_MODULE_0___default.a); +const mkdir = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.mkdir); +const mkdirp = async path => await mkdir(path, { + recursive: true +}); +const unlink = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.unlink); +const copyDirectory = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(ncp__WEBPACK_IMPORTED_MODULE_2__["ncp"]); async function statTest(path, block) { try { @@ -2808,7 +2807,7 @@ async function createSymlink(src, dest, type) { } } else { const posixType = type === 'exec' ? 'file' : type; - const relativeSource = Object(path__WEBPACK_IMPORTED_MODULE_4__["relative"])(Object(path__WEBPACK_IMPORTED_MODULE_4__["dirname"])(dest), src); + const relativeSource = Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(Object(path__WEBPACK_IMPORTED_MODULE_3__["dirname"])(dest), src); await forceCreate(relativeSource, dest, posixType); } } diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index a3d3baed3750b..9f80600fddcb1 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -26,7 +26,6 @@ "@types/indent-string": "^3.0.0", "@types/lodash.clonedeepwith": "^4.5.3", "@types/log-symbols": "^2.0.0", - "@types/mkdirp": "^0.5.2", "@types/ncp": "^2.0.1", "@types/node": "^10.12.27", "@types/ora": "^1.3.5", @@ -50,7 +49,6 @@ "indent-string": "^3.2.0", "lodash.clonedeepwith": "^4.5.0", "log-symbols": "^2.2.0", - "mkdirp": "^0.5.1", "ncp": "^2.0.0", "ora": "^1.4.0", "prettier": "^1.14.3", diff --git a/packages/kbn-pm/src/utils/fs.ts b/packages/kbn-pm/src/utils/fs.ts index 8ccaca29f97e0..9484c3a61e608 100644 --- a/packages/kbn-pm/src/utils/fs.ts +++ b/packages/kbn-pm/src/utils/fs.ts @@ -19,22 +19,20 @@ import cmdShimCb from 'cmd-shim'; import fs from 'fs'; -import mkdirpCb from 'mkdirp'; import { ncp } from 'ncp'; import { dirname, relative } from 'path'; import { promisify } from 'util'; const lstat = promisify(fs.lstat); -const readFile = promisify(fs.readFile); +export const readFile = promisify(fs.readFile); const symlink = promisify(fs.symlink); -const chmod = promisify(fs.chmod); +export const chmod = promisify(fs.chmod); const cmdShim = promisify(cmdShimCb); -const mkdirp = promisify(mkdirpCb); +const mkdir = promisify(fs.mkdir); +export const mkdirp = async (path: string) => await mkdir(path, { recursive: true }); export const unlink = promisify(fs.unlink); export const copyDirectory = promisify(ncp); -export { chmod, readFile, mkdirp }; - async function statTest(path: string, block: (stats: fs.Stats) => boolean) { try { return block(await lstat(path)); diff --git a/packages/kbn-test/src/mocha/junit_report_generation.js b/packages/kbn-test/src/mocha/junit_report_generation.js index 3463c738df5a5..54df5c5f33494 100644 --- a/packages/kbn-test/src/mocha/junit_report_generation.js +++ b/packages/kbn-test/src/mocha/junit_report_generation.js @@ -18,10 +18,9 @@ */ import { resolve, dirname, relative } from 'path'; -import { writeFileSync } from 'fs'; +import { writeFileSync, mkdirSync } from 'fs'; import { inspect } from 'util'; -import mkdirp from 'mkdirp'; import xmlBuilder from 'xmlbuilder'; import { getSnapshotOfRunnableLogs } from './log_cache'; @@ -150,7 +149,7 @@ export function setupJUnitReportGeneration(runner, options = {}) { spacebeforeslash: '', }); - mkdirp.sync(dirname(reportPath)); + mkdirSync(dirname(reportPath), { recursive: true }); writeFileSync(reportPath, reportXML, 'utf8'); }); } diff --git a/renovate.json5 b/renovate.json5 index cd836762b817f..30bd149ba3b38 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -475,14 +475,6 @@ '@types/minimatch', ], }, - { - groupSlug: 'mkdirp', - groupName: 'mkdirp related packages', - packageNames: [ - 'mkdirp', - '@types/mkdirp', - ], - }, { groupSlug: 'mustache', groupName: 'mustache related packages', diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.js b/src/cli/serve/integration_tests/reload_logging_config.test.js index 206118d2d1be8..8e8bdb15abc68 100644 --- a/src/cli/serve/integration_tests/reload_logging_config.test.js +++ b/src/cli/serve/integration_tests/reload_logging_config.test.js @@ -21,7 +21,6 @@ import { spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; import os from 'os'; -import mkdirp from 'mkdirp'; import rimraf from 'rimraf'; import { safeDump } from 'js-yaml'; @@ -59,7 +58,7 @@ describe('Server logging configuration', function () { isJson = true; setLoggingJson(true); - mkdirp.sync(tempDir); + fs.mkdirSync(tempDir, { recursive: true }); }); afterEach(() => { diff --git a/src/cli_plugin/install/download.test.js b/src/cli_plugin/install/download.test.js index 97586f962737c..9de6491f3c957 100644 --- a/src/cli_plugin/install/download.test.js +++ b/src/cli_plugin/install/download.test.js @@ -21,7 +21,7 @@ import sinon from 'sinon'; import nock from 'nock'; import glob from 'glob-all'; import rimraf from 'rimraf'; -import mkdirp from 'mkdirp'; +import Fs from 'fs'; import Logger from '../lib/logger'; import { UnsupportedProtocolError } from '../lib/errors'; import { download, _downloadSingle, _getFilePath, _checkFilePathDeprecation } from './download'; @@ -64,7 +64,7 @@ describe('kibana cli', function () { sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); rimraf.sync(testWorkingPath); - mkdirp.sync(testWorkingPath); + Fs.mkdirSync(testWorkingPath, { recursive: true }); }); afterEach(function () { diff --git a/src/cli_plugin/install/install.js b/src/cli_plugin/install/install.js index 57d2d485b9dd2..10816591f7d71 100644 --- a/src/cli_plugin/install/install.js +++ b/src/cli_plugin/install/install.js @@ -17,8 +17,10 @@ * under the License. */ +import Fs from 'fs'; +import { promisify } from 'util'; + import { download } from './download'; -import Promise from 'bluebird'; import path from 'path'; import { cleanPrevious, cleanArtifacts } from './cleanup'; import { extract, getPackData } from './pack'; @@ -27,9 +29,8 @@ import { sync as rimrafSync } from 'rimraf'; import { errorIfXPackInstall } from '../lib/error_if_x_pack'; import { existingInstall, assertVersion } from './kibana'; import { prepareExternalProjectDependencies } from '@kbn/pm'; -import mkdirp from 'mkdirp'; -const mkdir = Promise.promisify(mkdirp); +const mkdir = promisify(Fs.mkdir); export default async function install(settings, logger) { try { @@ -37,7 +38,7 @@ export default async function install(settings, logger) { await cleanPrevious(settings, logger); - await mkdir(settings.workingPath); + await mkdir(settings.workingPath, { recursive: true }); await download(settings, logger); diff --git a/src/cli_plugin/install/kibana.test.js b/src/cli_plugin/install/kibana.test.js index 4a4ca6ebe3034..64de96b34e807 100644 --- a/src/cli_plugin/install/kibana.test.js +++ b/src/cli_plugin/install/kibana.test.js @@ -27,7 +27,6 @@ import sinon from 'sinon'; import Logger from '../lib/logger'; import { join } from 'path'; import rimraf from 'rimraf'; -import mkdirp from 'mkdirp'; import fs from 'fs'; import { existingInstall, assertVersion } from './kibana'; @@ -55,7 +54,7 @@ describe('kibana cli', function () { beforeEach(function () { rimraf.sync(testWorkingPath); - mkdirp.sync(testWorkingPath); + fs.mkdirSync(testWorkingPath, { recursive: true }); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); }); diff --git a/src/cli_plugin/install/pack.test.js b/src/cli_plugin/install/pack.test.js index bc854f66a6925..1bc2504397fb5 100644 --- a/src/cli_plugin/install/pack.test.js +++ b/src/cli_plugin/install/pack.test.js @@ -17,10 +17,11 @@ * under the License. */ +import Fs from 'fs'; + import sinon from 'sinon'; import glob from 'glob-all'; import rimraf from 'rimraf'; -import mkdirp from 'mkdirp'; import Logger from '../lib/logger'; import { extract, getPackData } from './pack'; import { _downloadSingle } from './download'; @@ -58,7 +59,7 @@ describe('kibana cli', function () { logger = new Logger(settings); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); - mkdirp.sync(testWorkingPath); + Fs.mkdirSync(testWorkingPath, { recursive: true }); }); afterEach(function () { diff --git a/src/cli_plugin/install/zip.js b/src/cli_plugin/install/zip.js index 7f928275525d5..1d5d49b6c63b6 100644 --- a/src/cli_plugin/install/zip.js +++ b/src/cli_plugin/install/zip.js @@ -19,8 +19,7 @@ import yauzl from 'yauzl'; import path from 'path'; -import mkdirp from 'mkdirp'; -import { createWriteStream } from 'fs'; +import { createWriteStream, mkdir } from 'fs'; import { get } from 'lodash'; /** @@ -112,7 +111,7 @@ export function extractArchive(archive, targetDir, extractPath) { } if (_isDirectory(fileName)) { - mkdirp(fileName, function (err) { + mkdir(fileName, { recursive: true }, function (err) { if (err) { return reject(err); } @@ -127,7 +126,7 @@ export function extractArchive(archive, targetDir, extractPath) { } // ensure parent directory exists - mkdirp(path.dirname(fileName), function (err) { + mkdir(path.dirname(fileName), { recursive: true }, function (err) { if (err) { return reject(err); } diff --git a/src/cli_plugin/list/list.test.js b/src/cli_plugin/list/list.test.js index c72f6c330543c..425a6bee5394e 100644 --- a/src/cli_plugin/list/list.test.js +++ b/src/cli_plugin/list/list.test.js @@ -19,16 +19,15 @@ import sinon from 'sinon'; import rimraf from 'rimraf'; -import mkdirp from 'mkdirp'; import Logger from '../lib/logger'; import list from './list'; import { join } from 'path'; -import { writeFileSync, appendFileSync } from 'fs'; +import { writeFileSync, appendFileSync, mkdirSync } from 'fs'; function createPlugin(name, version, pluginBaseDir) { const pluginDir = join(pluginBaseDir, name); - mkdirp.sync(pluginDir); + mkdirSync(pluginDir, { recursive: true }); appendFileSync(join(pluginDir, 'package.json'), '{"version": "' + version + '"}'); } @@ -48,7 +47,7 @@ describe('kibana cli', function () { sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); rimraf.sync(pluginDir); - mkdirp.sync(pluginDir); + mkdirSync(pluginDir, { recursive: true }); }); afterEach(function () { @@ -97,7 +96,7 @@ describe('kibana cli', function () { it('list should throw an exception if a plugin does not have a package.json', function () { createPlugin('plugin1', '1.0.0', pluginDir); - mkdirp.sync(join(pluginDir, 'empty-plugin')); + mkdirSync(join(pluginDir, 'empty-plugin'), { recursive: true }); expect(function () { list(settings, logger); @@ -107,7 +106,7 @@ describe('kibana cli', function () { it('list should throw an exception if a plugin have an empty package.json', function () { createPlugin('plugin1', '1.0.0', pluginDir); const invalidPluginDir = join(pluginDir, 'invalid-plugin'); - mkdirp.sync(invalidPluginDir); + mkdirSync(invalidPluginDir, { recursive: true }); appendFileSync(join(invalidPluginDir, 'package.json'), ''); expect(function () { diff --git a/src/cli_plugin/remove/remove.test.js b/src/cli_plugin/remove/remove.test.js index 971881dd0b870..5d936d0278521 100644 --- a/src/cli_plugin/remove/remove.test.js +++ b/src/cli_plugin/remove/remove.test.js @@ -20,11 +20,10 @@ import sinon from 'sinon'; import glob from 'glob-all'; import rimraf from 'rimraf'; -import mkdirp from 'mkdirp'; import Logger from '../lib/logger'; import remove from './remove'; import { join } from 'path'; -import { writeFileSync, existsSync } from 'fs'; +import { writeFileSync, existsSync, mkdirSync } from 'fs'; describe('kibana cli', function () { @@ -42,7 +41,7 @@ describe('kibana cli', function () { sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); rimraf.sync(pluginDir); - mkdirp.sync(pluginDir); + mkdirSync(pluginDir, { recursive: true }); }); afterEach(function () { @@ -72,7 +71,7 @@ describe('kibana cli', function () { it('remove x-pack if it exists', () => { settings.pluginPath = join(pluginDir, 'x-pack'); settings.plugin = 'x-pack'; - mkdirp.sync(join(pluginDir, 'x-pack')); + mkdirSync(join(pluginDir, 'x-pack'), { recursive: true }); expect(existsSync(settings.pluginPath)).toEqual(true); remove(settings, logger); expect(existsSync(settings.pluginPath)).toEqual(false); @@ -88,8 +87,8 @@ describe('kibana cli', function () { it('delete the specified folder.', function () { settings.pluginPath = join(pluginDir, 'foo'); - mkdirp.sync(join(pluginDir, 'foo')); - mkdirp.sync(join(pluginDir, 'bar')); + mkdirSync(join(pluginDir, 'foo'), { recursive: true }); + mkdirSync(join(pluginDir, 'bar'), { recursive: true }); remove(settings, logger); diff --git a/src/dev/build/lib/fs.js b/src/dev/build/lib/fs.js index de0ae5fc72e90..1aeccdb76a4fb 100644 --- a/src/dev/build/lib/fs.js +++ b/src/dev/build/lib/fs.js @@ -26,14 +26,13 @@ import { inspect } from 'util'; import vfs from 'vinyl-fs'; import { promisify } from 'bluebird'; -import mkdirpCb from 'mkdirp'; import del from 'del'; import deleteEmpty from 'delete-empty'; import { createPromiseFromStreams, createMapStream } from '../../../legacy/utils'; import tar from 'tar'; -const mkdirpAsync = promisify(mkdirpCb); +const mkdirAsync = promisify(fs.mkdir); const writeFileAsync = promisify(fs.writeFile); const readFileAsync = promisify(fs.readFile); const readdirAsync = promisify(fs.readdir); @@ -66,7 +65,7 @@ function longInspect(value) { export async function mkdirp(path) { assertAbsolute(path); - await mkdirpAsync(path); + await mkdirAsync(path, { recursive: true }); } export async function write(path, contents) { @@ -185,7 +184,7 @@ export async function untar(source, destination, extractOptions = {}) { assertAbsolute(source); assertAbsolute(destination); - await mkdirpAsync(destination); + await mkdirAsync(destination, { recursive: true }); await createPromiseFromStreams([ fs.createReadStream(source), diff --git a/src/dev/build/tasks/nodejs/download.js b/src/dev/build/tasks/nodejs/download.js index ee6da41042cd2..48313c0911c24 100644 --- a/src/dev/build/tasks/nodejs/download.js +++ b/src/dev/build/tasks/nodejs/download.js @@ -23,7 +23,8 @@ import { dirname } from 'path'; import chalk from 'chalk'; import { createHash } from 'crypto'; import wreck from '@hapi/wreck'; -import mkdirp from 'mkdirp'; + +import { mkdirp } from '../../lib'; function tryUnlink(path) { try { @@ -43,7 +44,7 @@ export async function download(options) { } // mkdirp and open file outside of try/catch, we don't retry for those errors - mkdirp.sync(dirname(destination)); + await mkdirp(dirname(destination)); const fileHandle = openSync(destination, 'w'); let error; diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.js b/src/dev/build/tasks/nodejs/extract_node_builds_task.js index f8e767ac79a4b..46c6433b27f9c 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.js +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.js @@ -20,13 +20,11 @@ import { dirname, resolve } from 'path'; import fs from 'fs'; import { promisify } from 'util'; -import mkdirp from 'mkdirp'; -import { untar } from '../../lib'; +import { untar, mkdirp } from '../../lib'; import { getNodeDownloadInfo } from './node_download_info'; const statAsync = promisify(fs.stat); -const mkdirpAsync = promisify(mkdirp); const copyFileAsync = promisify(fs.copyFile); export const ExtractNodeBuildsTask = { @@ -50,7 +48,7 @@ export const ExtractNodeBuildsTask = { async copyWindows(source, destination) { // ensure source exists before creating destination directory await statAsync(source); - await mkdirpAsync(dirname(destination)); + await mkdirp(dirname(destination)); // for performance reasons, do a copy-on-write by using the fs.constants.COPYFILE_FICLONE flag return await copyFileAsync(source, destination, fs.constants.COPYFILE_FICLONE); }, diff --git a/src/dev/build/tasks/patch_native_modules_task.js b/src/dev/build/tasks/patch_native_modules_task.js index 7da51ac717c42..4d952f44626cd 100644 --- a/src/dev/build/tasks/patch_native_modules_task.js +++ b/src/dev/build/tasks/patch_native_modules_task.js @@ -18,10 +18,9 @@ */ import { scanCopy, untar, deleteAll } from '../lib'; -import { createWriteStream } from 'fs'; +import { createWriteStream, mkdirSync } from 'fs'; import { binaryInfo } from '../../../../x-pack/legacy/plugins/code/tasks/nodegit_info'; import wreck from '@hapi/wreck'; -import mkdirp from 'mkdirp'; import { dirname, join, basename } from 'path'; import { createPromiseFromStreams } from '../../../legacy/utils/streams'; @@ -31,7 +30,7 @@ async function download(url, destination, log) { if (response.statusCode !== 200) { throw new Error(`Unexpected status code ${response.statusCode} when downloading ${url}`); } - mkdirp.sync(dirname(destination)); + mkdirSync(dirname(destination), { recursive: true }); await createPromiseFromStreams([response, createWriteStream(destination)]); log.debug('Downloaded ', url); } diff --git a/src/dev/jest/junit_reporter.js b/src/dev/jest/junit_reporter.js index 4fda2315dd54b..8846beaed8b35 100644 --- a/src/dev/jest/junit_reporter.js +++ b/src/dev/jest/junit_reporter.js @@ -18,9 +18,8 @@ */ import { resolve, dirname, relative } from 'path'; -import { writeFileSync } from 'fs'; +import { writeFileSync, mkdirSync } from 'fs'; -import mkdirp from 'mkdirp'; import xmlBuilder from 'xmlbuilder'; import { escapeCdata } from '@kbn/test'; @@ -117,7 +116,7 @@ export default class JestJUnitReporter { spacebeforeslash: '', }); - mkdirp.sync(dirname(reportPath)); + mkdirSync(dirname(reportPath), { recursive: true }); writeFileSync(reportPath, reportXML, 'utf8'); } } diff --git a/src/es_archiver/actions/save.js b/src/es_archiver/actions/save.js index 610e85f3a89ac..c515a376ecd7b 100644 --- a/src/es_archiver/actions/save.js +++ b/src/es_archiver/actions/save.js @@ -18,10 +18,7 @@ */ import { resolve } from 'path'; -import { createWriteStream } from 'fs'; - -import { fromNode } from 'bluebird'; -import mkdirp from 'mkdirp'; +import { createWriteStream, mkdirSync } from 'fs'; import { createListStream, @@ -42,7 +39,7 @@ export async function saveAction({ name, indices, client, dataDir, log, raw }) { log.info('[%s] Creating archive of %j', name, indices); - await fromNode(cb => mkdirp(outputDir, cb)); + mkdirSync(outputDir, { recursive: true }); const progress = new Progress(); progress.activate(log); diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 3d9dfe6319127..bb041924215dd 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -17,9 +17,9 @@ * under the License. */ -import Promise from 'bluebird'; -import { mkdirp as mkdirpNode } from 'mkdirp'; +import Fs from 'fs'; import { resolve } from 'path'; +import { promisify } from 'util'; import { migrations } from './migrations'; import manageUuid from './server/lib/manage_uuid'; @@ -41,7 +41,7 @@ import { makeKQLUsageCollector } from './server/lib/kql_usage_collector'; import { injectVars } from './inject_vars'; import { i18n } from '@kbn/i18n'; -const mkdirp = Promise.promisify(mkdirpNode); +const mkdirAsync = promisify(Fs.mkdir); export default function (kibana) { const kbnBaseUrl = '/app/kibana'; @@ -320,7 +320,7 @@ export default function (kibana) { try { // Create the data directory (recursively, if the a parent dir doesn't exist). // If it already exists, does nothing. - await mkdirp(server.config().get('path.data')); + await mkdirAsync(server.config().get('path.data'), { recursive: true }); } catch (err) { server.log(['error', 'init'], err); // Stop the server startup with a fatal error diff --git a/src/legacy/server/sass/build.js b/src/legacy/server/sass/build.js index f33acbd1abd74..db1b26e102834 100644 --- a/src/legacy/server/sass/build.js +++ b/src/legacy/server/sass/build.js @@ -24,7 +24,6 @@ import sass from 'node-sass'; import autoprefixer from 'autoprefixer'; import postcss from 'postcss'; import postcssUrl from 'postcss-url'; -import mkdirp from 'mkdirp'; import chalk from 'chalk'; import isPathInside from 'is-path-inside'; import { PUBLIC_PATH_PLACEHOLDER } from '../../../optimize/public_path_placeholder'; @@ -33,7 +32,7 @@ const renderSass = promisify(sass.render); const writeFile = promisify(fs.writeFile); const exists = promisify(fs.exists); const copyFile = promisify(fs.copyFile); -const mkdirpAsync = promisify(mkdirp); +const mkdirAsync = promisify(fs.mkdir); const UI_ASSETS_DIR = resolve(__dirname, '../../ui/public/assets'); const DARK_THEME_IMPORTER = (url) => { @@ -165,7 +164,7 @@ export class Build { })); // write css - await mkdirpAsync(this.targetDir); + await mkdirAsync(this.targetDir, { recursive: true }); await writeFile(this.targetPath, prefixed.css); // copy non-shared urlAssets @@ -174,7 +173,7 @@ export class Build { return; } - await mkdirpAsync(dirname(asset.copyTo)); + await mkdirAsync(dirname(asset.copyTo), { recursive: true }); await copyFile(asset.path, asset.copyTo); })); diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js index f0410f57c4578..2e6436b370fbe 100644 --- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js +++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js @@ -20,11 +20,10 @@ import { resolve, relative, isAbsolute } from 'path'; import { createHash } from 'crypto'; import { promisify } from 'util'; -import { existsSync } from 'fs'; +import { existsSync, mkdir } from 'fs'; import del from 'del'; import { makeRe } from 'minimatch'; -import mkdirp from 'mkdirp'; import jsonStableStringify from 'json-stable-stringify'; import { IS_KIBANA_DISTRIBUTABLE, fromRoot } from '../../utils'; @@ -32,7 +31,7 @@ import { IS_KIBANA_DISTRIBUTABLE, fromRoot } from '../../utils'; import { UiBundle } from './ui_bundle'; import { appEntryTemplate } from './app_entry_template'; -const mkdirpAsync = promisify(mkdirp); +const mkdirAsync = promisify(mkdir); const REPO_ROOT = fromRoot(); function getWebpackAliases(pluginSpecs) { @@ -179,7 +178,7 @@ export class UiBundlesController { async resetBundleDir() { if (!existsSync(this._workingDir)) { // create a fresh working directory - await mkdirpAsync(this._workingDir); + await mkdirAsync(this._workingDir, { recursive: true }); } else { // delete all children of the working directory await del(this.resolvePath('*'), { diff --git a/src/optimize/dynamic_dll_plugin/dll_compiler.js b/src/optimize/dynamic_dll_plugin/dll_compiler.js index 46b85b46f082d..5b9dd2d04a550 100644 --- a/src/optimize/dynamic_dll_plugin/dll_compiler.js +++ b/src/optimize/dynamic_dll_plugin/dll_compiler.js @@ -22,14 +22,13 @@ import { notInNodeModulesOrWebpackShims, notInNodeModules, inDllPluginPublic } f import { fromRoot } from '../../legacy/utils'; import { PUBLIC_PATH_PLACEHOLDER } from '../public_path_placeholder'; import fs from 'fs'; -import mkdirp from 'mkdirp'; import webpack from 'webpack'; import { promisify } from 'util'; import path from 'path'; import rimraf from 'rimraf'; const readFileAsync = promisify(fs.readFile); -const mkdirpAsync = promisify(mkdirp); +const mkdirAsync = promisify(fs.mkdir); const existsAsync = promisify(fs.exists); const writeFileAsync = promisify(fs.writeFile); const rimrafAsync = promisify(rimraf); @@ -132,7 +131,7 @@ export class DllCompiler { const exists = await existsAsync(filePath); if (!exists) { - await mkdirpAsync(path.dirname(filePath)); + await mkdirAsync(path.dirname(filePath), { recursive: true }); } return exists; diff --git a/test/functional/services/failure_debugging.ts b/test/functional/services/failure_debugging.ts index bd8869ad08566..e2be69d779799 100644 --- a/test/functional/services/failure_debugging.ts +++ b/test/functional/services/failure_debugging.ts @@ -17,22 +17,19 @@ * under the License. */ -import mkdirp from 'mkdirp'; import { resolve } from 'path'; -import { writeFile } from 'fs'; +import { writeFile, mkdir } from 'fs'; +import { promisify } from 'util'; + import del from 'del'; -import Bluebird, { promisify } from 'bluebird'; import { FtrProviderContext } from '../ftr_provider_context'; interface Test { fullTitle(): string; } -type WriteFileAsync = (path: string | number | Buffer | URL, data: any) => Bluebird; -type MkDirAsync = (dirName: string) => Bluebird; - -const writeFileAsync = promisify(writeFile) as WriteFileAsync; -const mkdirAsync = promisify(mkdirp) as MkDirAsync; +const writeFileAsync = promisify(writeFile); +const mkdirAsync = promisify(mkdir); export async function FailureDebuggingProvider({ getService }: FtrProviderContext) { const screenshots = getService('screenshots'); @@ -51,7 +48,7 @@ export async function FailureDebuggingProvider({ getService }: FtrProviderContex } async function savePageHtml(name: string) { - await mkdirAsync(config.get('failureDebugging.htmlDirectory')); + await mkdirAsync(config.get('failureDebugging.htmlDirectory'), { recursive: true }); const htmlOutputFileName = resolve( config.get('failureDebugging.htmlDirectory'), `${name}.html` diff --git a/test/functional/services/screenshots.ts b/test/functional/services/screenshots.ts index ac9292da16a93..ddafa211ece7f 100644 --- a/test/functional/services/screenshots.ts +++ b/test/functional/services/screenshots.ts @@ -18,19 +18,16 @@ */ import { resolve, dirname } from 'path'; -import { writeFile, readFileSync } from 'fs'; -import Bluebird, { fromNode as fcb, promisify } from 'bluebird'; -import mkdirp from 'mkdirp'; +import { writeFile, readFileSync, mkdir } from 'fs'; +import { promisify } from 'util'; + import del from 'del'; import { comparePngs } from './lib/compare_pngs'; import { FtrProviderContext } from '../ftr_provider_context'; import { WebElementWrapper } from './lib/web_element_wrapper'; -type WriteFileAsync = (path: string | number | Buffer | URL, data: any) => Bluebird; -type MkDirAsync = (dirName: string) => Bluebird; - -const mkdirAsync = promisify(mkdirp) as MkDirAsync; -const writeFileAsync = promisify(writeFile) as WriteFileAsync; +const mkdirAsync = promisify(mkdir); +const writeFileAsync = promisify(writeFile); export async function ScreenshotsProvider({ getService }: FtrProviderContext) { const log = getService('log'); @@ -65,7 +62,7 @@ export async function ScreenshotsProvider({ getService }: FtrProviderContext) { await writeFileAsync(baselinePath, readFileSync(sessionPath)); return 0; } else { - await mkdirAsync(FAILURE_DIRECTORY); + await mkdirAsync(FAILURE_DIRECTORY, { recursive: true }); return await comparePngs(sessionPath, baselinePath, failurePath, SESSION_DIRECTORY, log); } } @@ -82,8 +79,8 @@ export async function ScreenshotsProvider({ getService }: FtrProviderContext) { try { log.info(`Taking screenshot "${path}"`); const screenshot = await (el ? el.takeScreenshot() : browser.takeScreenshot()); - await fcb(cb => mkdirp(dirname(path), cb)); - await fcb(cb => writeFile(path, screenshot, 'base64', cb)); + await mkdirAsync(dirname(path), { recursive: true }); + await writeFileAsync(path, screenshot, 'base64'); } catch (err) { log.error('SCREENSHOT FAILED'); log.error(err); diff --git a/test/functional/services/snapshots.ts b/test/functional/services/snapshots.ts index 96e14864bef89..84526878a7bb4 100644 --- a/test/functional/services/snapshots.ts +++ b/test/functional/services/snapshots.ts @@ -17,17 +17,16 @@ * under the License. */ -import expect from '@kbn/expect'; import { dirname, resolve } from 'path'; -import { writeFile, readFileSync } from 'fs'; -import Bluebird, { fromNode as fcb, promisify } from 'bluebird'; -import mkdirp from 'mkdirp'; +import { writeFile, readFileSync, mkdir } from 'fs'; +import { promisify } from 'util'; + +import expect from '@kbn/expect'; import del from 'del'; import { FtrProviderContext } from '../ftr_provider_context'; -type WriteFileAsync = (path: string | number | Buffer | URL, data: any) => Bluebird; - -const writeFileAsync = promisify(writeFile) as WriteFileAsync; +const mkdirAsync = promisify(mkdir); +const writeFileAsync = promisify(writeFile); export async function SnapshotsProvider({ getService }: FtrProviderContext) { const log = getService('log'); @@ -77,8 +76,8 @@ export async function SnapshotsProvider({ getService }: FtrProviderContext) { private async _take(path: string, snapshot?: object) { try { - await fcb(cb => mkdirp(dirname(path), cb)); - await fcb(cb => writeFile(path, JSON.stringify(snapshot), 'utf8', cb)); + await mkdirAsync(dirname(path), { recursive: true }); + await writeFileAsync(path, JSON.stringify(snapshot), 'utf8'); } catch (err) { log.error('SNAPSHOT FAILED'); log.error(err); diff --git a/utilities/visual_regression.js b/utilities/visual_regression.js index 216d218ebdc5d..c06b058d9ddf2 100644 --- a/utilities/visual_regression.js +++ b/utilities/visual_regression.js @@ -25,7 +25,6 @@ import Handlebars from 'handlebars'; import fs from 'fs'; import path from 'path'; import imageDiff from 'image-diff'; -import mkdirp from 'mkdirp'; import moment from 'moment'; import SimpleGit from 'simple-git'; @@ -77,8 +76,8 @@ async function compareScreenshots() { const SESSION_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'session'); // We don't need to create the baseline dir because it's committed. - mkdirp.sync(DIFF_SCREENSHOTS_DIR); - mkdirp.sync(SESSION_SCREENSHOTS_DIR); + fs.mkdirSync(DIFF_SCREENSHOTS_DIR, { recursive: true }); + fs.mkdirSync(SESSION_SCREENSHOTS_DIR, { recursive: true }); const files = await readDirAsync(SESSION_SCREENSHOTS_DIR); const screenshots = files.filter(file => file.indexOf('.png') !== -1); diff --git a/x-pack/legacy/plugins/code/server/__tests__/git_operations.ts b/x-pack/legacy/plugins/code/server/__tests__/git_operations.ts index 247aabdf39133..7cd9175efb9ff 100644 --- a/x-pack/legacy/plugins/code/server/__tests__/git_operations.ts +++ b/x-pack/legacy/plugins/code/server/__tests__/git_operations.ts @@ -9,7 +9,6 @@ import Git from '@elastic/nodegit'; import assert from 'assert'; import { execSync } from 'child_process'; import fs from 'fs'; -import * as mkdirp from 'mkdirp'; import path from 'path'; import rimraf from 'rimraf'; import { GitOperations } from '../git_operations'; @@ -19,7 +18,7 @@ describe('git_operations', () => { it('get default branch from a non master repo', async () => { const repoUri = 'github.com/foo/bar'; const repoDir = path.join(serverOptions.repoPath, repoUri); - mkdirp.sync(repoDir); + fs.mkdirSync(repoDir, { recursive: true }); // create a non-master using git commands const shell = ` @@ -47,7 +46,7 @@ describe('git_operations', () => { }); async function prepareProject(repoPath: string) { - mkdirp.sync(repoPath); + fs.mkdirSync(repoPath, { recursive: true }); const workDir = path.join(serverOptions.workspacePath, repoUri); const repo = await Git.Repository.init(workDir, 0); const content = ''; diff --git a/x-pack/legacy/plugins/code/server/__tests__/lsp_service.ts b/x-pack/legacy/plugins/code/server/__tests__/lsp_service.ts index efc3d75398e88..1b85c077171cc 100644 --- a/x-pack/legacy/plugins/code/server/__tests__/lsp_service.ts +++ b/x-pack/legacy/plugins/code/server/__tests__/lsp_service.ts @@ -6,7 +6,6 @@ import Git from '@elastic/nodegit'; import fs from 'fs'; -import mkdirp from 'mkdirp'; import path from 'path'; import rimraf from 'rimraf'; import sinon from 'sinon'; @@ -43,7 +42,7 @@ describe('lsp_service tests', () => { let firstCommitSha = ''; let secondCommitSha = ''; async function prepareProject(repoPath: string) { - mkdirp.sync(repoPath); + fs.mkdirSync(repoPath, { recursive: true }); const repo = await Git.Repository.init(repoPath, 0); const helloContent = "console.log('hello world');"; fs.writeFileSync(path.join(repo.workdir(), filename), helloContent, 'utf8'); diff --git a/x-pack/legacy/plugins/code/server/__tests__/workspace_handler.ts b/x-pack/legacy/plugins/code/server/__tests__/workspace_handler.ts index ac3ecc698c333..e738974b00e96 100644 --- a/x-pack/legacy/plugins/code/server/__tests__/workspace_handler.ts +++ b/x-pack/legacy/plugins/code/server/__tests__/workspace_handler.ts @@ -8,7 +8,6 @@ import path from 'path'; import Git from '@elastic/nodegit'; import assert from 'assert'; -import mkdirp from 'mkdirp'; import * as os from 'os'; import rimraf from 'rimraf'; import { ResponseMessage } from 'vscode-jsonrpc/lib/messages'; @@ -51,7 +50,7 @@ describe('workspace_handler tests', () => { file = 'src/controllers/user.ts' ) { const fullPath = path.join(workspacePath, repo, '__randomString', revision, file); - mkdirp.sync(path.dirname(fullPath)); + fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, ''); const strInUrl = fullPath .split(path.sep) @@ -119,7 +118,7 @@ describe('workspace_handler tests', () => { }); async function prepareProject(repoPath: string) { - mkdirp.sync(repoPath); + fs.mkdirSync(repoPath, { recursive: true }); const repo = await Git.Repository.init(repoPath, 0); const content = 'console.log("test")'; const subFolder = 'src'; @@ -173,8 +172,8 @@ describe('workspace_handler tests', () => { // @ts-ignore before(() => { - mkdirp.sync(workspaceDir); - mkdirp.sync(repoDir); + fs.mkdirSync(workspaceDir, { recursive: true }); + fs.mkdirSync(repoDir, { recursive: true }); }); // @ts-ignore diff --git a/x-pack/legacy/plugins/code/server/lsp/controller.test.ts b/x-pack/legacy/plugins/code/server/lsp/controller.test.ts index d3928f71abef4..212381b36ca6e 100644 --- a/x-pack/legacy/plugins/code/server/lsp/controller.test.ts +++ b/x-pack/legacy/plugins/code/server/lsp/controller.test.ts @@ -5,7 +5,6 @@ */ import fs from 'fs'; -import mkdirp from 'mkdirp'; import * as os from 'os'; import path from 'path'; import rimraf from 'rimraf'; @@ -83,7 +82,7 @@ JAVA.launcher = LauncherStub; let controller: typeof LanguageServerController; beforeAll(() => { - mkdirp.sync(workspaceDir); + fs.mkdirSync(workspaceDir, { recursive: true }); }); beforeEach(async () => { sinon.reset(); @@ -115,7 +114,7 @@ afterAll(() => { function mockRequest(repo: string, file: string) { const repoPath = path.join(workspaceDir, repo); - mkdirp.sync(repoPath); + fs.mkdirSync(repoPath, { recursive: true }); return { method: 'request', params: [], diff --git a/x-pack/legacy/plugins/code/server/lsp/request_expander.test.ts b/x-pack/legacy/plugins/code/server/lsp/request_expander.test.ts index c89f6f4ec485c..cf3999ec244f5 100644 --- a/x-pack/legacy/plugins/code/server/lsp/request_expander.test.ts +++ b/x-pack/legacy/plugins/code/server/lsp/request_expander.test.ts @@ -5,7 +5,6 @@ */ import fs from 'fs'; -import mkdirp from 'mkdirp'; import rimraf from 'rimraf'; import sinon from 'sinon'; import { pathToFileURL } from 'url'; @@ -22,7 +21,7 @@ const options: ServerOptions = { beforeEach(async () => { sinon.reset(); if (!fs.existsSync(options.workspacePath)) { - mkdirp.sync(options.workspacePath); + fs.mkdirSync(options.workspacePath, { recursive: true }); } }); @@ -80,8 +79,8 @@ test('be able to open multiple workspace', async () => { params: [], workspacePath: '/tmp/test/workspace/2', }; - mkdirp.sync(request1.workspacePath); - mkdirp.sync(request2.workspacePath); + fs.mkdirSync(request1.workspacePath, { recursive: true }); + fs.mkdirSync(request2.workspacePath, { recursive: true }); await expander.handleRequest(request1); await expander.handleRequest(request2); expect(proxyStub.initialize.called); @@ -125,8 +124,8 @@ test('be able to swap workspace', async () => { params: [], workspacePath: '/tmp/test/workspace/2', }; - mkdirp.sync(request1.workspacePath); - mkdirp.sync(request2.workspacePath); + fs.mkdirSync(request1.workspacePath, { recursive: true }); + fs.mkdirSync(request2.workspacePath, { recursive: true }); await expander.handleRequest(request1); await expander.handleRequest(request2); expect(proxyStub.initialize.called); @@ -166,7 +165,7 @@ test('requests should be cancelled if workspace is unloaded', async () => { params: [], workspacePath: workspace1, }; - mkdirp.sync(workspace1); + fs.mkdirSync(workspace1, { recursive: true }); const promise1 = expander.handleRequest(request); const promise2 = expander.handleRequest(request); setTimeout(() => expander.unloadWorkspace(workspace1), 1); diff --git a/x-pack/legacy/plugins/code/server/lsp/workspace_handler.test.ts b/x-pack/legacy/plugins/code/server/lsp/workspace_handler.test.ts index 6a814982c22ff..d959eda2aa6dc 100644 --- a/x-pack/legacy/plugins/code/server/lsp/workspace_handler.test.ts +++ b/x-pack/legacy/plugins/code/server/lsp/workspace_handler.test.ts @@ -6,7 +6,6 @@ import fs from 'fs'; import path from 'path'; -import mkdirp from 'mkdirp'; import * as os from 'os'; import rimraf from 'rimraf'; import { ResponseMessage } from 'vscode-jsonrpc/lib/messages'; @@ -48,7 +47,7 @@ function makeAFile( file = 'src/controllers/user.ts' ) { const fullPath = path.join(workspacePath, repo, '__randomString', revision, file); - mkdirp.sync(path.dirname(fullPath)); + fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, ''); const strInUrl = fullPath .split(path.sep) @@ -139,8 +138,8 @@ test('should throw a error if url is invalid', async () => { }); beforeAll(() => { - mkdirp.sync(workspaceDir); - mkdirp.sync(repoDir); + fs.mkdirSync(workspaceDir, { recursive: true }); + fs.mkdirSync(repoDir, { recursive: true }); }); afterAll(() => { diff --git a/x-pack/legacy/plugins/code/server/lsp/workspace_handler.ts b/x-pack/legacy/plugins/code/server/lsp/workspace_handler.ts index d8dcb12837e62..535e60ae0dadf 100644 --- a/x-pack/legacy/plugins/code/server/lsp/workspace_handler.ts +++ b/x-pack/legacy/plugins/code/server/lsp/workspace_handler.ts @@ -17,8 +17,8 @@ import Boom from 'boom'; import del from 'del'; import fs from 'fs'; import { delay } from 'lodash'; -import mkdirp from 'mkdirp'; import path from 'path'; +import { promisify } from 'util'; import { ResponseMessage } from 'vscode-jsonrpc/lib/messages'; import { Hover, Location, TextDocumentPositionParams } from 'vscode-languageserver'; @@ -35,6 +35,8 @@ import { LoggerFactory } from '../utils/log_factory'; export const MAX_RESULT_COUNT = 20; +const mkdirAsync = promisify(fs.mkdir); + export class WorkspaceHandler { private revisionMap: { [uri: string]: string } = {}; private log: Logger; @@ -396,15 +398,7 @@ export class WorkspaceHandler { this.log.info(`Create workspace ${workspaceDir} from url ${bareRepo.path()}`); const parentDir = path.dirname(workspaceDir); // on windows, git clone will failed if parent folder is not exists; - await new Promise((resolve, reject) => - mkdirp(parentDir, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }) - ); + await mkdirAsync(parentDir, { recursive: true }); const workTreeName = this.workspaceWorktreeBranchName(revision); await this.pruneWorktree(bareRepo, workTreeName); // Create the worktree and open it as Repository. diff --git a/x-pack/legacy/plugins/code/server/repository_service.ts b/x-pack/legacy/plugins/code/server/repository_service.ts index 213590285c070..1369802cb594c 100644 --- a/x-pack/legacy/plugins/code/server/repository_service.ts +++ b/x-pack/legacy/plugins/code/server/repository_service.ts @@ -7,7 +7,7 @@ import Git, { RemoteCallbacks } from '@elastic/nodegit'; import del from 'del'; import fs from 'fs'; -import mkdirp from 'mkdirp'; +import { promisify } from 'util'; import moment from 'moment'; import path from 'path'; @@ -34,6 +34,8 @@ const NODEGIT_CALLBACK_RETURN_VALUE_ERROR = -7; const GIT_INDEXER_PROGRESS_CALLBACK_RETURN_VALUE_ERROR_MSG = `indexer progress callback returned ${GIT_FETCH_PROGRESS_CANCEL}`; const SSH_AUTH_ERROR = new Error('Failed to authenticate SSH session'); +const mkdirAsync = promisify(fs.mkdir); + function isCancelled(error: any) { return ( error && @@ -77,15 +79,7 @@ export class RepositoryService { } else { const parentDir = path.dirname(localPath); // on windows, git clone will failed if parent folder is not exists; - await new Promise((resolve, reject) => - mkdirp(parentDir, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }) - ); + await mkdirAsync(parentDir, { recursive: true }); } // Go head with the actual clone. if (repo.protocol === 'ssh') { diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/download.js b/x-pack/legacy/plugins/reporting/server/browsers/download/download.js index df42f8320225a..f8436f5ce7af7 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/download.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/download.js @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { openSync, writeSync, closeSync } from 'fs'; +import { openSync, writeSync, closeSync, mkdirSync } from 'fs'; import { createHash } from 'crypto'; import { dirname } from 'path'; -import mkdirp from 'mkdirp'; import request from 'request'; import { log, readableEnd } from './util'; @@ -24,7 +23,7 @@ export async function download(url, path) { const hash = createHash('md5'); - mkdirp.sync(dirname(path)); + mkdirSync(dirname(path), { recursive: true }); const handle = openSync(path, 'w'); try { diff --git a/x-pack/package.json b/x-pack/package.json index 5a27764bdf9ee..7976dd13a07ff 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -72,7 +72,6 @@ "@types/mapbox-gl": "^0.54.1", "@types/memoize-one": "^4.1.0", "@types/mime": "^2.0.1", - "@types/mkdirp": "^0.5.2", "@types/mocha": "^5.2.7", "@types/nock": "^10.0.3", "@types/node": "^10.12.27", @@ -287,7 +286,6 @@ "markdown-it": "^8.4.1", "memoize-one": "^5.0.0", "mime": "^2.4.4", - "mkdirp": "0.5.1", "moment": "^2.20.1", "moment-duration-format": "^1.3.0", "moment-timezone": "^0.5.14", diff --git a/x-pack/test/functional/apps/code/repo_archiver.ts b/x-pack/test/functional/apps/code/repo_archiver.ts index ea09bc141ab38..fc6baaab57423 100644 --- a/x-pack/test/functional/apps/code/repo_archiver.ts +++ b/x-pack/test/functional/apps/code/repo_archiver.ts @@ -8,12 +8,11 @@ import del from 'del'; // @ts-ignore import extractZip from 'extract-zip'; import fs from 'fs'; -import mkdirp from 'mkdirp'; import path from 'path'; import { promisify } from 'util'; const asyncExtractZip = promisify(extractZip); -const asyncMkdirp = promisify(mkdirp); +const asyncMkdir = promisify(fs.mkdir); const archiveFilePath = (zipFileName: string) => { return path.resolve(__dirname, `./fixtures/${zipFileName}.zip`); @@ -29,7 +28,7 @@ const workspaceDir = (repoUri: string, kibanaDir: string) => { const unzip = async (filepath: string, target: string) => { if (!fs.existsSync(target)) { - await asyncMkdirp(target); + await asyncMkdir(target, { recursive: true }); } await asyncExtractZip(filepath, { dir: target }); }; diff --git a/x-pack/test/reporting/functional/lib/compare_pdfs.js b/x-pack/test/reporting/functional/lib/compare_pdfs.js index f7fbfe051b844..9463a96140b13 100644 --- a/x-pack/test/reporting/functional/lib/compare_pdfs.js +++ b/x-pack/test/reporting/functional/lib/compare_pdfs.js @@ -6,13 +6,12 @@ import path from 'path'; import fs from 'fs'; -import { promisify } from 'bluebird'; -import mkdirp from 'mkdirp'; +import { promisify } from 'util'; import { PDFImage } from 'pdf-image'; import PDFJS from 'pdfjs-dist'; import { comparePngs } from '../../../../../test/functional/services/lib/compare_pngs'; -const mkdirAsync = promisify(mkdirp); +const mkdirAsync = promisify(fs.mkdir); export async function checkIfPdfsMatch(actualPdfPath, baselinePdfPath, screenshotsDirectory, log) { log.debug(`checkIfPdfsMatch: ${actualPdfPath} vs ${baselinePdfPath}`); @@ -21,8 +20,8 @@ export async function checkIfPdfsMatch(actualPdfPath, baselinePdfPath, screensho const sessionDirectoryPath = path.resolve(screenshotsDirectory, 'session'); const failureDirectoryPath = path.resolve(screenshotsDirectory, 'failure'); - await mkdirAsync(sessionDirectoryPath); - await mkdirAsync(failureDirectoryPath); + await mkdirAsync(sessionDirectoryPath, { recursive: true }); + await mkdirAsync(failureDirectoryPath, { recursive: true }); const actualPdfFileName = path.basename(actualPdfPath, '.pdf'); const baselinePdfFileName = path.basename(baselinePdfPath, '.pdf'); diff --git a/x-pack/test/reporting/functional/lib/compare_pngs.js b/x-pack/test/reporting/functional/lib/compare_pngs.js index afbebc03406fa..2bde535c3b69a 100644 --- a/x-pack/test/reporting/functional/lib/compare_pngs.js +++ b/x-pack/test/reporting/functional/lib/compare_pngs.js @@ -7,10 +7,9 @@ import path from 'path'; import fs from 'fs'; import { promisify } from 'bluebird'; -import mkdirp from 'mkdirp'; import { comparePngs } from '../../../../../test/functional/services/lib/compare_pngs'; -const mkdirAsync = promisify(mkdirp); +const mkdirAsync = promisify(fs.mkdir); export async function checkIfPngsMatch(actualpngPath, baselinepngPath, screenshotsDirectory, log) { log.debug(`checkIfpngsMatch: ${actualpngPath} vs ${baselinepngPath}`); @@ -19,8 +18,8 @@ export async function checkIfPngsMatch(actualpngPath, baselinepngPath, screensho const sessionDirectoryPath = path.resolve(screenshotsDirectory, 'session'); const failureDirectoryPath = path.resolve(screenshotsDirectory, 'failure'); - await mkdirAsync(sessionDirectoryPath); - await mkdirAsync(failureDirectoryPath); + await mkdirAsync(sessionDirectoryPath, { recursive: true }); + await mkdirAsync(failureDirectoryPath, { recursive: true }); const actualpngFileName = path.basename(actualpngPath, '.png'); const baselinepngFileName = path.basename(baselinepngPath, '.png'); diff --git a/x-pack/test/reporting/functional/reporting.js b/x-pack/test/reporting/functional/reporting.js index 814e85ade1574..0c528a80b4334 100644 --- a/x-pack/test/reporting/functional/reporting.js +++ b/x-pack/test/reporting/functional/reporting.js @@ -6,12 +6,12 @@ import expect from '@kbn/expect'; import path from 'path'; -import mkdirp from 'mkdirp'; import fs from 'fs'; -import { promisify } from 'bluebird'; +import { promisify } from 'util'; import { checkIfPdfsMatch, checkIfPngsMatch } from './lib'; + const writeFileAsync = promisify(fs.writeFile); -const mkdirAsync = promisify(mkdirp); +const mkdirAsync = promisify(fs.mkdir); const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }) { const writeSessionReport = async (name, rawPdf, reportExt = 'pdf') => { const sessionDirectory = path.resolve(REPORTS_FOLDER, 'session'); - await mkdirAsync(sessionDirectory); + await mkdirAsync(sessionDirectory, { recursive: true }); const sessionReportPath = path.resolve(sessionDirectory, `${name}.${reportExt}`); await writeFileAsync(sessionReportPath, rawPdf); return sessionReportPath; diff --git a/yarn.lock b/yarn.lock index b96aa7f3b32d5..33fce33f4716c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3677,13 +3677,6 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" integrity sha1-UALhT3Xi1x5WQoHfBDHIwbSio2o= -"@types/mkdirp@^0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" - integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== - dependencies: - "@types/node" "*" - "@types/mocha@^5.2.7": version "5.2.7" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" From cc9876c1c0d929f2a05664229acce44be41eecc8 Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 4 Oct 2019 08:50:28 -0700 Subject: [PATCH 08/33] [renovate] group typescript related package upgrades --- renovate.json5 | 12 ++++++++++++ src/dev/renovate/package_groups.ts | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index 30bd149ba3b38..5b6989ae61b3b 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -259,6 +259,18 @@ '(\\b|_)storybook(\\b|_)', ], }, + { + groupSlug: 'typescript', + groupName: 'typescript related packages', + packagePatterns: [ + '(\\b|_)ts(\\b|_)', + '(\\b|_)typescript(\\b|_)', + ], + packageNames: [ + 'tslib', + '@types/tslib', + ], + }, { groupSlug: 'json-stable-stringify', groupName: 'json-stable-stringify related packages', diff --git a/src/dev/renovate/package_groups.ts b/src/dev/renovate/package_groups.ts index 185163890f745..100eb8764e087 100644 --- a/src/dev/renovate/package_groups.ts +++ b/src/dev/renovate/package_groups.ts @@ -177,6 +177,12 @@ export const RENOVATE_PACKAGE_GROUPS: PackageGroup[] = [ name: 'storybook', packageWords: ['storybook'], }, + + { + name: 'typescript', + packageWords: ['ts', 'typescript'], + packageNames: ['tslib'], + }, ]; /** From 2ae7915810b9092d513895c939a888605bdb62b7 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 4 Oct 2019 12:07:55 -0400 Subject: [PATCH 09/33] [Lens] Use EUI Chart theme and other design fixes (#46658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Passing EUI theme to XY chart * Remove legend if only single series * Fix height of config link * Remove clearing icon from field select * Better scroll position in config panel * Changed styles of popover editor operations list * Reduce spacing between icon and text in no results found * Manually import EUI Charts theme … instead of using NP * Add EUI Chart theme to field item * Use `core` instead of `chrome` for field data * Made “drop field here” clickable as well * “Level of detail” to “Time interval” * Update snap * Remove `isHorizontal` from functional test * Fix functional test --- .../editor_frame/_frame_layout.scss | 3 +- .../_workspace_panel_wrapper.scss | 3 +- .../dimension_panel/_dimension_panel.scss | 4 +- .../dimension_panel/_popover.scss | 18 +- .../dimension_panel/field_select.tsx | 1 + .../dimension_panel/popover_editor.tsx | 11 +- .../public/indexpattern_plugin/field_item.tsx | 12 +- .../definitions/date_histogram.test.tsx | 2 +- .../operations/definitions/date_histogram.tsx | 6 +- .../__snapshots__/xy_expression.test.tsx.snap | 651 ++++++++++++++++++ .../xy_visualization_plugin/xy_expression.tsx | 17 +- .../test/functional/apps/lens/smokescreen.ts | 13 +- .../es_archives/lens/reporting/data.json.gz | Bin 4359 -> 4464 bytes 13 files changed, 706 insertions(+), 35 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_frame_layout.scss b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_frame_layout.scss index e3b91ee0674c8..35c28595a59c0 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_frame_layout.scss +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_frame_layout.scss @@ -42,8 +42,7 @@ @include euiScrollBar; min-width: $lnsPanelMinWidth + $euiSize; overflow-x: hidden; - overflow-y: auto; + overflow-y: scroll; padding-top: $euiSize; - padding-right: $euiSize; max-height: 100%; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss index 30f6136c83623..b8f5f95df8f08 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss @@ -10,7 +10,8 @@ .lnsWorkspacePanelWrapper__pageContentHeader { padding: $euiSizeS; border-bottom: $euiBorderThin; - margin-bottom: 0; + // override EuiPage + margin-bottom: 0 !important; // sass-lint:disable-line no-important } .lnsWorkspacePanelWrapper__pageContentBody { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_dimension_panel.scss b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_dimension_panel.scss index 4ab4877a6feb8..ddb37505f9985 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_dimension_panel.scss +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_dimension_panel.scss @@ -1,9 +1,9 @@ .lnsIndexPatternDimensionPanel { @include euiFontSizeS; - background: $euiColorEmptyShade; + background-color: $euiColorEmptyShade; border-radius: $euiBorderRadius; display: flex; align-items: center; margin-top: $euiSizeXS; - padding: $euiSizeS; + overflow: hidden; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_popover.scss b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_popover.scss index ee25a501e5f8e..8f26ab91e0f16 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_popover.scss +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/_popover.scss @@ -13,6 +13,8 @@ width: 100%; display: flex; align-items: center; + padding: $euiSizeS; + min-height: $euiSizeXXL; } .lnsPopoverEditor__left, @@ -22,7 +24,7 @@ .lnsPopoverEditor__left { padding-top: 0; - background-color: $euiColorLightestShade; + background-color: $euiPageBackgroundColor; } .lnsPopoverEditor__right { @@ -30,14 +32,20 @@ } .lnsPopoverEditor__operation { - padding: $euiSizeXS; - font-size: .875rem; + @include euiFontSizeS; + color: $euiColorPrimary; + + // TODO: Fix in EUI or don't use EuiSideNav + .euiSideNavItemButton__label { + color: inherit; + } } .lnsPopoverEditor__operation--selected { - background-color: $euiColorLightShade; + font-weight: bold; + color: $euiTextColor; } .lnsPopoverEditor__operation--incompatible { - opacity: .7; + color: $euiColorMediumShade; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/field_select.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/field_select.tsx index b0d011412867f..0cd7329b4613d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/field_select.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/field_select.tsx @@ -140,6 +140,7 @@ export function FieldSelect({ ) : ( <> - setPopoverOpen(!isPopoverOpen)} - />{' '} - + size="xs" + > - + ) } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index af0612be8dc2f..804ea478e57f1 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -20,6 +20,7 @@ import { EuiPopoverTitle, EuiIconTip, } from '@elastic/eui'; +import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { Chart, Axis, @@ -210,6 +211,9 @@ export function FieldItem(props: FieldItemProps) { function FieldItemPopoverContents(props: State & FieldItemProps) { const { histogram, topValues, indexPattern, field, dateRange, core, sampledValues } = props; + const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); + const chartTheme = IS_DARK_THEME ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme; + if (props.isLoading) { return ; } else if ( @@ -371,7 +375,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { - + { expect(instance.find(EuiRange).prop('value')).toEqual(2); }); - it('should render disabled switch and no level of detail control for auto interval', () => { + it('should render disabled switch and no time intervals control for auto interval', () => { const instance = shallow( {intervalIsRestricted ? ( @@ -242,7 +242,7 @@ export const dateHistogramOperation: OperationDefinition )} diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap index f9a5a73eda270..fdc16f5aec6b9 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap @@ -9,6 +9,99 @@ exports[`xy_expression XYChart component it renders area 1`] = ` rotation={0} showLegend={false} showLegendDisplayValue={false} + theme={ + Object { + "areaSeriesStyle": Object { + "area": Object { + "opacity": 0.3, + }, + "line": Object { + "strokeWidth": 2, + }, + "point": Object { + "fill": "rgba(255, 255, 255, 1)", + "radius": 3, + "strokeWidth": 2, + "visible": false, + }, + }, + "axes": Object { + "axisLineStyle": Object { + "stroke": "rgba(239, 241, 244, 1)", + }, + "axisTitleStyle": Object { + "fill": "rgba(52, 55, 65, 1)", + "fontFamily": "'Inter UI', -apple-system, BlinkMacSystemFont, + 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + "fontSize": 12, + "padding": 10, + }, + "tickLabelStyle": Object { + "fill": "rgba(105, 112, 125, 1)", + "fontFamily": "'Inter UI', -apple-system, BlinkMacSystemFont, + 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + "fontSize": 10, + "padding": 8, + }, + "tickLineStyle": Object { + "stroke": "rgba(0,0,0,0)", + "strokeWidth": 0, + }, + }, + "barSeriesStyle": Object { + "displayValue": Object { + "fill": "rgba(105, 112, 125, 1)", + "fontFamily": "'Inter UI', -apple-system, BlinkMacSystemFont, + 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + "fontSize": 8, + }, + }, + "chartMargins": Object { + "bottom": 0, + "left": 0, + "right": 0, + "top": 0, + }, + "colors": Object { + "defaultVizColor": "#3185FC", + "vizColors": Array [ + "#1EA593", + "#2B70F7", + "#CE0060", + "#38007E", + "#FCA5D3", + "#F37020", + "#E49E29", + "#B0916F", + "#7B000B", + "#34130C", + ], + }, + "crosshair": Object { + "band": Object { + "fill": "rgba(245, 247, 250, 1)", + }, + "line": Object { + "dash": Array [ + 4, + 4, + ], + "stroke": "rgba(105, 112, 125, 1)", + "strokeWidth": 1, + }, + }, + "lineSeriesStyle": Object { + "line": Object { + "strokeWidth": 2, + }, + "point": Object { + "fill": "rgba(255, 255, 255, 1)", + "radius": 3, + "strokeWidth": 2, + }, + }, + } + } /> 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; return ( -

- -

+ +

1 || data.tables[layers[0].layerId].columns.length > 2; const shouldRotate = isHorizontalChart(layers); return ( diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 1abd137659d91..3cb74307c3c09 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -79,6 +79,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { dimension: '[data-test-subj="lnsXY_xDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', operation: 'date_histogram', + field: '@timestamp', }); await PageObjects.lens.configureDimension({ @@ -88,6 +89,13 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { field: 'bytes', }); + await PageObjects.lens.configureDimension({ + dimension: + '[data-test-subj="lnsXY_splitDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + operation: 'terms', + field: 'ip', + }); + await PageObjects.lens.setTitle('Afancilenstest'); await PageObjects.lens.save(); @@ -102,10 +110,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { // .echLegendItem__title is the only viable way of getting the xy chart's // legend item(s), so we're using a class selector here. - await PageObjects.lens.assertExpectedText( - '.echLegendItem__title', - legendText => !!legendText && legendText.includes('Average of bytes') - ); + expect(await find.allByCssSelector('.echLegendItem')).to.have.length(3); }); }); } diff --git a/x-pack/test/functional/es_archives/lens/reporting/data.json.gz b/x-pack/test/functional/es_archives/lens/reporting/data.json.gz index 93ceaf3d8f6f5daa843f9fd8fb1e43a0eff23805..3a55820a3551df7bd7fa783ea1fac7aa32c3c010 100644 GIT binary patch literal 4464 zcmV-$5s&U4iwFovB$ZtN17u-zVJ>QOZ*BnXUF&n(xDo%Jzk=2DQ^(MN@5hX1n!EJc zT=TfOxHoO1j0OZii5H60k(6U!s{g$J_z)>kGHJ^(JjYBDTX=Et+r?sc!G*rQP?Sl2 zyQULQnUDleE_lH;iZ;CDEBnAchJ@EzZ~ zig$ysl2y1!Q647oFEq{Ak4BwR4$nujrq&cr%8Knp)jOpDO=*m2#u|MrOFz}sRrkh- z^OR;Zy{4S{Jl)WXT@|4jPQ!IxP%*h$hMB_t=1Z!S6I8^2f0{Hqg&FcAN`9ay-=r-% z8kDexF^7uy#vzV_#mUFJf~^wMad$|QMLqs zxevoyW3{mjLK+c{z~tlCnV9fw;>{+1UC}H<3-;k+CI}X#S)PXRLX_cT6X&cDvfm39 zP{KY2D9R`=qA0~njxsA0Jey6>Vv*7X%FEKKX-X0KAx&1V68sAXBCG1Q7hM!hQ}pE# zsVYmmh~iayH05ZKNdyrmM=pFq#!-RQWsIfLh&B7-jsh~p=jxk-xpC^m*5#-3EeEi#-W@i!TGZ2HLkiiq~ z>A~{zH>QQ&@l(lO&S>Eod^uNs^KB&+o z896c`v}CQ=qa`vHD~e=Vam(aUIW`m_MvYA9>>IC7x0^k-={k^6f*h$hCB;4;h4E*J z6pkQACN8ZIUWPH9CK7?Pqe(`jQnZpq1+C~RNpIz$CPz~?U@8sAI6*0mka&zV(#@&b zWHg-yVVdPdxqQS$sEf9zldko0Etj|#A$)0sy_ECslQ`!w2$bd_j;OavNEn2a%y|@2 zjsWBlO4l1d3bW;NAT^^nmnw8{nogN^(Ib@RXdykCj7Eu(Ocq|_98g*?+hod(XSkB; z@b3tL&j3SjNyEibqNv7cI-digUqX^E}b<;F4(^GIqO1%Z-d z__*h*4~LMEB{zbbG?E!WMxMm@=G3QVIuhs74?GR$U72pQOPgpn zWA+NKZsYo;OXbL8PK~Kk zgGd@vr^eK&hoUp4PK~KkL#P;2r^eK&^CC5-PK~KkW9rnHIyI(Fjj2KmXr zrcRv!%rSLpOr4Sz$}x3nOr4Sw%`tWAJUGh!s_>XPbp`~bdYN!coq7Zm$JD7ugY)cL6yQ>UI&>J&$+{sydO zR?_4dXIEW^*#<@7?*bk#1oS4%9^sqAT94r)+Eab1_DoMx8Y9(&ALM>DxWwxVia(%h zO5XdwQk=i#Z!#28`aw|tZT04C+nYWggy{fgmhsmpyvDDyiCFXziWeKXz>3F)|pjC;tqhdk0AB6NV4eExO4 z$nLXSi2f%P*Za>(6)VdWCU**Zl)_XB&6*3j&@t_upuHEcMt{!ARoyPI=T@H>h#*Rnu3A0ReJ+AE+vIf$QJ^ zXuc0E8dy4o{^X$hXUc(WXOkYbin*gskJ3;GddJ)vyqYi$O!lg`2P0MTvtFhsqlbi4 z;&eFLKH7&eQdTe(gqPj4wo&M6vOl4H0=p)0&}6$0!;52|cE2~jp1Nyl^+qa86Vae$ zbJSE<%fizjykl2<|IZ)!0`2FAA6hHFwW#Q{Qf#2wSE}uqhNoFm-FD7>Jxme@;lgvK za0>6EXo~)AO;c=Yp&v$Je(RO%^JN^aggKQRepk7_2_y1uvtl(agtqFsnrYYubls-; zz`^-JY>;05J16g7SKqh3yuA1>^4Ashr$Smi8C1^hB!}2v<4Scr!}c^|YFp}=ITW!4 z=h;?(4JMBO2Zm9oF5lI45D>@EY#8VWtM_3aAH2;xdb3VB_ljMj7-1IwPbtU3{IKpO zR}>+;mosL_%^Ei9jBk+aKe4{}FBkm3->Ofdj{n=vCJG;wKYzZW>4MHhm@`u%bW79M zBx6HkOGOR}e1KHP1V(@;K+rNk;OmZI1r|0;Rg9x3Md!<}G~>pPA|PNR>_mXIE9{x< zW&SdX-f*KWyA;Y=S@Sp2QS@{2r78(y9BoJ`ZT|@4JiFu~tFXbp7b3yQ`zWT)OrdIG z8Ya+npaXPQW%Rm%0eszuz|m~Wrasd2z^*Z=tfpCQH_b}RsTyvDZtB*h{Zv;Krdd^6 zno%=hNX;r!s%l)^Suqpl3WK2pok>6Q6c|6F%_?Syk+*1Fv6B@72>MQE(A>BD?~d_& z8griGmMcQ3G78?Jn{c&RDM_G|w{0kum3-OrIW4cO%Q)e>1Qn}+dquBMhuu-yKAcNJ zrTkQQq5HNF|I@Kip7f9_Ept5hUj1ore4m;d-$nO)gqd|{X%t$a>$a}49&b`65_xl~ zukV_}L@d@LW=)^fd(46;r1Cwt?mOTv8}$)|L(`&VC>%VpdoKMScdU2GqN`Hl7fxNL zZ6w-;S>baWw`%LA!d>NK?ysBUqbF-xgpw+9n5H@2I>V@tQqku`J0X7J%t;vAf`-n< z2S#l<0@10ZAsYd#nJ$x(-&cG3H8LcZSL*0tl?!^m@5AWna{Xj+Zji>^UQLu`vKIP zuKBVW=6J^wF4=At%-BhpRVG~6O`de4eH8B|Z|kA@vbQn$ZXS1(cG6L({$o1*A--2@ z3+fIPw3O`;m-7zKtm&Igjz0c)$W~NWCWNrt`)KlueG-LPDx88o8(UZRo~A=ZYFBg7 zpqHNPlmyM9fen!hu%p|+FboVF$D+XUi3?3iZCy7HC8A@)d>|fTh>WHo&8lUKQrboI zBwI5t@@0d2rh$oNPy#f>Tp`1BRe%DAu~4%#!-R%uxt#;uGzaOW2v6H$`y3WQ<}S-B z%zg=(H!RdZmnNL7%gD6Y0(fBhblhewoY$0+4~GDDMn<{=`poCaayHg=m~67>_H&$k ziBTpT!gkDlx2&p1MRE34nC|y>tXq#c;a0nz0X@^1I+pe*ZnfdVKs6wsrlyw>K@^xk zW5T3TmHN=qpU|y7^qVhEd)vL-YM*Gjs~Z%U*oMHcO$YcywSlg?5FsB@$G|eV)xPCe zrf%6lvk6=9OkV@n#uONqjvOt}k!@)M-0HS{aLmK5PAU4i*o`}Bs}7rtzT@*`Ylo7* zWv|QazfD-!Eam@kBcmRIq{(_-qF6k-Jec4SfH3BgKQB&hc%53zp9g3OTT4ExASJH6X5QIfe#oNZ3|@2xOvfBA~0fjdWu9x`_s$TGKHwTrn^v(N`9R3l1M&1$|K& z1Ys-YadTV7J8rJZfQPVqm)&+R!{)YX_dSQ0S2MnExN!2&NU`l)eyzqXJ{PM=sdt`Y znN|DDWqtm+6|1~3P8wtI&y~jLuP~M&q2)ra_u9*zTwv4uz%6CI!(57My(qu;NV#d2 z7H^)iS=CNLF|U@1aLL4;2CP3(b_NC~1Eq5YDf3B4Bcj9=PA@i2ltL=65^)Y#N#!7U z>0n?f9b>%e>El&>r2Ikgsy++hhP704#tDw-%P~&S_nG}f>ItK5huU%g zbA>t5JK{FRR~{T+IctEUmuPmf0{Y0-nNJTi>}bF+9SgXQX#q#osH+_^bH|@nh8a7I2<7M*Y?`cT5b(9@nNQWU4=Hv#tX<0nZLx6!z3Va+Gz`}-V zX})0xT5xB`q}QJCyh0{Ty!%H^zC$VN*e6Kx@BchrfJB7=YFDb^8J1_5Q`0&3n;{}V z;uyLH1Na9EkU*rmno9xIEX1Rv1R#seJg`h3p#T%8nKA`PT-Q`};!xnKP~*W&l`U^* z0BSo-6=D_JhC9HON7sudeea;}bOz1+?)MH}UNakFL65w5(Cayj*9qZc1)C@u-$IbW ztLw3rALP~b5sWi>X1yNwgjW0hDjHz3GrjXo&&Wsnet>=7JU{IvgU8QLdo9WS=GZ$X z#e0(WWT#d;qs;bHb!tKQ7)F_^Yo_m#KbBEu7y;Ip*8;G^oetfBfXx#El?IlIUDI?_ zM<%1pW)iNM)CK`!?x=xW1Gt9f14DJ0yJ!)UxufH$)zDKb7}$n^947TP_wNmkDs>$Y z1We}D09!N|V(NaQQ>(tRFkEnoQ>(tH41%y_F>|v)EGx0V%BhwoMs-~cLGiO#x4ot_ zX?5vE;Y2AC_3l1EtJ`%Qpp8c_5*X=ybo+yh^gcp(?C7PBG@jAG@AbGpa#;Qdn|iT$ zJhIx&Lgpi@(KQ?3nhiO6*{Pq-gO_{j$$o09TO%6OaWVEm;FuT~F2n#~r~-s+9YMA$ z1h^xO1~i!>x$;EIXx%Pcg5KZ3hn{8ELR^5%)# z@@FXeQ`5C9)zCozO$ZD~eg0-NQwy$PST0r#&BE1{6pW77#T_EOJMv2-m}No;Uj>B^ zU0rG+#%o?X zOZCCCtXL@uidp)WMr@*6z|k>cn%wk(BIDD2Gd)mQWg+J5k zu82K5bf;$DFs-Mgbe*L6??*11IZ&vlZ%u0I7;$4#$F+t1uR|K}GI`mrx+tlX@$Ng2 zgtG6_CLJUUIIis^L47z01A>#CBxt=zIJ!0NSlVxYbUf3VLfgH2fBZjp8I_4hz5oE` CAf1T- literal 4359 zcmd6pRYMaDpg>`S0|Z8Q!;lUU{OA#qBL+xFONWFsBc!B6TBMX7NJxzyA>a@RsS#3= zqZ!@z-hXk=(|I}Xha(Ay_kRFr_7&W{7Fx5HZ-kl#!!2Tn`3&_rd(QO&140g)*mN7C z7i-9(xdmReG^|r?NV#Gj!nCw~d0*+R%QF-6aC+LK{H*20cE*=&m#UQMlke>nT8%lzXr9a@@Wt%+0#DP>*l2zJ^Jer5`+>!k8l?|W(kv8 zf`iW=3AJ8M?>ybv*w8+hD(KT%tXFV%Vi0yB8V0V}k5{yxoz!-GUb67979UO20T+oS zk;`>pi<2|B%|Fr6W+XZGkslk0em3NBK@dGSP84Q(vz8^_|MqSZko9ijq#}6Q3CroM zboGzaQ9UBvHmw=8!dObGN_pCN+H_qj=IbPK*?t&Kr!&vlB(t_h5k3TP#QD$cOi1gQ zY4Z_*zo5ow`x>>L=iS~U`lK-Y`!T1BUu_X=&O4{loCeRxDyB;87NQpyR2+Q1p(`z6 znOaub;9@MF2R*Vk{7xjgJLBZiD}L98Hudtwz2_zLx<)#OfuLl!XCy^cuWlmKtwP1K zf_k5Erx9w1UGqk{^I6w)fDA1gwj!Mi$G5nkl#QEiMmq!){K54ti;W|K_5(vJQXeR3 zHiOFs+d<7NU+_ywDWK26ens#?(O(+xN-}T>wK{_w>R&nV8Pma0@xcT7mt2 zv~Z$@32B?r`TLC)CEaH|e5D`!@)s+i=&zVkVapAv1ezCHl4O+{035J%7n%u%Ur{S> zBxhQbQM`F?#4i*=I6*DeR8byj_*#22>AOD#LPVm&b_Y7#-JlvoUpJ-T^C#2qGYlfjWQb(c ztDfQ$1u;+7-R7cYN>!rBszm4(lu>vVD8iqek7gK@&AY5lDKf<(b0>z!3v;N^49YrP zRz(z<%tl7eMzsZO4{v|8zJwqFnqthy{)Ij@=Z-{add4s@oVy>pWp4>hY~dl>tg2ZP zEG-w?57m;Ag|`1=_7ueb{q*h5+YVMOU#zov+zColrd=R!n}X&(aqrDEfQ^=S(K@%; z=8=?7#i)XIQ!b$Vz_;UI#6p{?U=2pm@D)oi3CIq{w~OBcg%~lmKHRiU!yJAL!=Ke@ zFAPpp*88~$7clEo{D07E?F!zqfaS>PTJ7AfFzOmMR0 zpS*dax3H($Lz-85s38L_tOn8DvQl`EQjNi0_4ruX zV*I`#G9XQUbOukE#%v~*DF#V5zd02ZRnu{N({vqvD)(oerHEm9)ME&#aJs>*@(9?X zi+VwYyw8VDmFxQ;XA$uhsBcIvVVa0H>Kgf_WpE7F?der@IOZsjzS}EvNMMSA42f81 zX|ZAH=Bxg&2fi{R%{?%fMaqc<0$d9Hc@0J?4Qopz@T)93zn#ta|6pba8W8GZ^|RL& z!^xX$kjcerVCy`3B||1NtH!!FxV25@*s=fiM^V)G;4x`Occ~OJ3#Bx%lR6Qi(7Bl` zwMA09uGa0b`m*Y-ER!BaBkmXA&T+RH< zu+##D1TxF5+vN%DRop)EAT?TXQP^J4*t{H-Q%AJBWMt2`F{Vn%CV6jg^>{GF$D(ZW~ zK<5LM8Q1R5m$h!$>-U`tG7A;oTBm<2|HS}5N;@=hm5?)e zqFq$OlgSNbIe&NGzU^AMsHbQj{!!d}=8E$teDT%LnF*KgNdB9&}4TF`#n z8Qj3{9O^w%=6HZF{XL1?+YPhLOv&I!lilK4=r_->s|a10{H^R{ik zbSaosrhPeU=*edTOvF#Fy4_4EW~RlFxAG2$n{^m(vow4hH(!od;H8N3uvt8ByK`N* zIhzVY>_4vbg-%7LjMYcC&(RkVt+Ks5{-Ja)DzhC{mvGfcpkDXsAO2*Ac-J_jXhY9) zEMjlh4kxTfU!)Xk6vUx!G;-*c`wPKmm=50XH#;1|m^Z)kepO}c0|_+_sF`d~)?VC$gy@Dj1dAgjBo2Qlwgr6(-gC3kJTQ4#Y+;vu z_rK~PkL<0jJSW>@3O;wTMVRy_zA2N~v^>fXTvZqsgoYN{N89swi8Nfv9S z5UV;bV~HeFiJv}o$N>OVHu~Ac#mMpW$+K@TE%N6oldK#gB(NlTm%5s;T0SErmd| zNFG7}wE>80&|}*+Nj&07Y#F~)D#|MGkTE!x-f2)QA)bk$FJDXWqR@Ov-HikTpA&uy z|0lv_uH#Co@T60cuJ6q+xUa+Z`J1JKZ027}^te0u@MoYD?K6%^EM-EMEEu6=a?~a> zINu0u(Qw+I_3%AC{%M4)$>9~tTvk-eIE17g+2FXZz@0XHr!h z9p7-bQ_i=X<3048(FsCo?JzSi;&8U(B;EanBe3`j6r6)p4vO5e>Pu<6 z9Vz=EJvhYdkxE%~56@#x%pi(xVfJke4fkO!nU65gi$XF^eYM8jJ&%NCbV)~kV9D-O z>2hISbsLb~^lBqwf^tp-k_1<%t29$+SL{gDisH>kSE{23bogSvD)zS3c&|*;Z=89h zMs^kpdsYrvxkdxnOZ#YTNn^L=7-!4(?ef~w_E%^S?a^!f$%6^5Nkt9b?JMXrsCcFz zf7+4yEV(?tev{T@AhS#vdWF5}l;euURdt)gki(5!i( zTi&yPO?aSMyGciW36~LiR0Oe<+=Hjca|WR@XXPsCh@JEO)+rY3(4B~ zn(5(6z&9&a!_{dYs?pflMzh})gCC-~dV2;eQ;lOpnzH=TNu5C~{4e;j7}{h7JxX!B z!B{(65w_RbyH`dQs-?FOff_EBRZYDAG)LJb*rb6|&QLz#pT>RT#(k=4J4`gb@8@xZ zn}UH3-mo{iz4FhAP7ow1km9*vQbxNmdI^Q^@m~1rm!d%UPa>3SeGfN9vzec7)az8o zZS!I_%%`jC16Ivp4J=#d@9ul&Gy^Q#@fr(O+>tx#?`(jj>1OZ~<$gO(ZV7-Wq}*K{ zom6uJs*#ArA9g27uo8;5f1%CCnD+aZ3`Ie^~Dyc{HYaoeU zUoz;VDti#W?7?fa6mBe~wKcV6@6e0!(S6dyT0qSUS;>(;V*={B3^|kwAZ64dgHZ;t z{k-g?dcck2jeq7gP{zbjt=8o`x4vDSRaN2HG*cR41&t%w3g4QbjPkYroq9EYf^%GS zaXVMkaeUg`*PjX{7*uzX-qBp_(pAyV?UIOyF`gNSWYF!c-*`6bK}r_`tdrk$EWpiw zEi<^tRnH3erB8s&V>khDjq&{jN=)D@rdNhD`OPX40`!5p^c`Rl)}JzA1^JTtnynk+ zl>KHgCBR2OLUbaYBmtFZ`xMpcc95I@P9+-bca;Ck(49;)p9bEA zSG-hpT=na%)T@buZSYbD$U9-B5uRnzW*^+b?3;EM(!O3>?Jq#Kb!Cl+A4CMKzTmD1q=IK9zUP-`NldOI=5DbLSsmfefBhKb!3J`JsW$K55(yPjn99jG8H*%!E zBT_Aj8bCdYbh-TTOyy6a=u5lIDDr}2vp1vbJu z`0IB}y?A-848wLb`adVA_2pVej{;{b`RljdFCpe^VrQO`B1e;O3u!kldYy~kYhF*4 zX&-U5926W$1}oIH=~QMP-@B)0HXKfZ6*%*~Ef>h5uDn5dntJC^w&Z|SglnfAR{UoB XeTrPHwRcO{Z3JU_-i12~5AS~fbnSYJ From 700a7ef5763763c34444ba10d57b141772569273 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg Date: Fri, 4 Oct 2019 11:13:58 -0500 Subject: [PATCH 10/33] Add option for Internet Explorer IE11 to FTR (#42967) * Add option for Internet Explorer IE11 * WIP trying to figure out IE options/capabilities * more attempts to get IE tests working * more work, but still can't set font size in Dev Tools Console * two changes for IE * revert skip in console test * this change *appears* to set absolute times but they don't stick * Shakespeare passing on all 3 browsers * add config.ie.js, change 'internet explorer' to 'ie' * increase notifications:lifetime so ew have a chance to see the message * fix lint error remove unused Options * updates from PR review, remove console.log * cleanup + reuse existing code * make iedriver path work for x-pack tests * try to avoid changing notification timeout * resolve path and add correct delimiter * remove unused code, make timepicker work on IE11 * work-around 45333 for IE11 * [WIP] removing some work-arounds * revert work-arounds * cleanup * remove debugging code * remove debugging code * removed unused code * reverting 2 files that didn't have any material changes --- package.json | 1 + .../lib/config/schema.ts | 2 +- test/functional/apps/console/_console.ts | 6 +- test/functional/apps/home/_navigation.js | 12 +- .../functional/apps/visualize/_chart_types.js | 2 +- test/functional/config.ie.js | 55 ++++++++ test/functional/page_objects/time_picker.js | 26 ++-- .../functional/page_objects/visualize_page.js | 7 +- test/functional/services/browser.ts | 9 +- test/functional/services/combo_box.ts | 2 +- test/functional/services/inspector.ts | 2 +- .../web_element_wrapper.ts | 13 ++ test/functional/services/remote/browsers.ts | 1 + test/functional/services/remote/webdriver.ts | 28 ++++ x-pack/test/functional/config.ie.js | 81 +++++++++++ .../page_objects/infra_home_page.ts | 2 +- yarn.lock | 129 +++++++++++++++--- 17 files changed, 331 insertions(+), 47 deletions(-) create mode 100644 test/functional/config.ie.js create mode 100644 x-pack/test/functional/config.ie.js diff --git a/package.json b/package.json index 22d042dc169ef..28dd1f1b37bb1 100644 --- a/package.json +++ b/package.json @@ -397,6 +397,7 @@ "gulp-babel": "^8.0.0", "gulp-sourcemaps": "2.6.5", "has-ansi": "^3.0.0", + "iedriver": "^3.14.1", "image-diff": "1.6.3", "intl-messageformat-parser": "^1.4.0", "is-path-inside": "^2.1.0", diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index d9cf282d8f4b6..52672d5f039fb 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -134,7 +134,7 @@ export const schema = Joi.object() browser: Joi.object() .keys({ type: Joi.string() - .valid('chrome', 'firefox') + .valid('chrome', 'firefox', 'ie') .default('chrome'), logPollingMs: Joi.number().default(100), diff --git a/test/functional/apps/console/_console.ts b/test/functional/apps/console/_console.ts index cdf2d3bf61f53..642314d1fb7f1 100644 --- a/test/functional/apps/console/_console.ts +++ b/test/functional/apps/console/_console.ts @@ -46,7 +46,11 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { it('should show the default request', async () => { // collapse the help pane because we only get the VISIBLE TEXT, not the part that is scrolled - await PageObjects.console.collapseHelp(); + // on IE11, the dialog that says 'Your browser does not meet the security requirements for Kibana.' + // blocks the close help button for several seconds so just retry until we can click it. + await retry.try(async () => { + await PageObjects.console.collapseHelp(); + }); await retry.try(async () => { const actualRequest = await PageObjects.console.getRequest(); log.debug(actualRequest); diff --git a/test/functional/apps/home/_navigation.js b/test/functional/apps/home/_navigation.js index 7582e45f74a34..12ce0e6afd03f 100644 --- a/test/functional/apps/home/_navigation.js +++ b/test/functional/apps/home/_navigation.js @@ -26,6 +26,7 @@ export default function ({ getService, getPageObjects }) { const appsMenu = getService('appsMenu'); const esArchiver = getService('esArchiver'); const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); const fromTime = '2015-09-19 06:31:44.000'; const toTime = '2015-09-23 18:31:44.000'; @@ -33,8 +34,15 @@ export default function ({ getService, getPageObjects }) { before(async () => { await esArchiver.loadIfNeeded('makelogs'); - await browser.refresh(); - await PageObjects.header.awaitKibanaChrome(); + if (browser.isInternetExplorer) { + await kibanaServer.uiSettings.replace({ 'state:storeInSessionStorage': false }); + } + }); + + after(async () => { + if (browser.isInternetExplorer) { + await kibanaServer.uiSettings.replace({ 'state:storeInSessionStorage': true }); + } }); // FLAKY: https://github.com/elastic/kibana/issues/33468 diff --git a/test/functional/apps/visualize/_chart_types.js b/test/functional/apps/visualize/_chart_types.js index 6f868b6df464f..79ea326ad982a 100644 --- a/test/functional/apps/visualize/_chart_types.js +++ b/test/functional/apps/visualize/_chart_types.js @@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) { describe('chart types', function () { before(function () { log.debug('navigateToApp visualize'); - return PageObjects.common.navigateToUrl('visualize', 'new'); + return PageObjects.visualize.navigateToNewVisualization(); }); it('should show the correct chart types', async function () { diff --git a/test/functional/config.ie.js b/test/functional/config.ie.js new file mode 100644 index 0000000000000..cf41e78e53890 --- /dev/null +++ b/test/functional/config.ie.js @@ -0,0 +1,55 @@ +/* + * 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 default async function ({ readConfigFile }) { + const defaultConfig = await readConfigFile(require.resolve('./config')); + + return { + ...defaultConfig.getAll(), + + browser: { + type: 'ie', + }, + + junit: { + reportName: 'Internet Explorer UI Functional Tests' + }, + + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + 'telemetry:optIn': false, + 'state:storeInSessionStorage': true, + 'notifications:lifetime:info': 10000, + }, + }, + + + kbnTestServer: { + ...defaultConfig.get('kbnTestServer'), + serverArgs: [ + ...defaultConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + ], + }, + + + }; +} diff --git a/test/functional/page_objects/time_picker.js b/test/functional/page_objects/time_picker.js index b5b22f4285358..054c3edd567e1 100644 --- a/test/functional/page_objects/time_picker.js +++ b/test/functional/page_objects/time_picker.js @@ -25,7 +25,7 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { const find = getService('find'); const browser = getService('browser'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['header']); + const PageObjects = getPageObjects(['header', 'common']); class TimePickerPage { @@ -48,18 +48,6 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { await find.waitForElementStale(panelElement); } - async setAbsoluteStart(startTime) { - await this.showStartEndTimes(); - - await testSubjects.click('superDatePickerstartDatePopoverButton'); - const panel = await this.getTimePickerPanel(); - await testSubjects.click('superDatePickerAbsoluteTab'); - await this.inputValue('superDatePickerAbsoluteDateInput', startTime); - await testSubjects.click('superDatePickerstartDatePopoverButton'); - await this.waitPanelIsGone(panel); - await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); - } - /** * @param {String} commonlyUsedOption 'superDatePickerCommonlyUsed_This_week' */ @@ -73,6 +61,13 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { const input = await testSubjects.find(dataTestsubj); await input.clearValue(); await input.type(value); + } else if (browser.isInternetExplorer) { + const input = await testSubjects.find(dataTestsubj); + const currentValue = await input.getAttribute('value'); + await input.type((browser.keys.ARROW_RIGHT).repeat(currentValue.length)); + await input.type((browser.keys.BACK_SPACE).repeat(currentValue.length)); + await input.type(value); + await input.click(); } else { await testSubjects.setValue(dataTestsubj, value); } @@ -90,14 +85,16 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { await testSubjects.click('superDatePickerendDatePopoverButton'); let panel = await this.getTimePickerPanel(); await testSubjects.click('superDatePickerAbsoluteTab'); + await testSubjects.click('superDatePickerAbsoluteDateInput'); await this.inputValue('superDatePickerAbsoluteDateInput', toTime); - + await PageObjects.common.sleep(500); // set from time await testSubjects.click('superDatePickerstartDatePopoverButton'); await this.waitPanelIsGone(panel); panel = await this.getTimePickerPanel(); await testSubjects.click('superDatePickerAbsoluteTab'); + await testSubjects.click('superDatePickerAbsoluteDateInput'); await this.inputValue('superDatePickerAbsoluteDateInput', fromTime); const superDatePickerApplyButtonExists = await testSubjects.exists('superDatePickerApplyTimeButton'); @@ -148,6 +145,7 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { if (isShowDatesButton) { await testSubjects.click('superDatePickerShowDatesButton'); } + await testSubjects.exists('superDatePickerstartDatePopoverButton'); } async getRefreshConfig(keepQuickSelectOpen = false) { diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index e40ce93dd5401..c1d9446febf9f 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -718,8 +718,9 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli log.debug('Click Save Visualization button'); await testSubjects.click('confirmSaveSavedObjectButton'); + // if we wait for this, the success toast message could be gone :-() // wait for save to complete before completion - await PageObjects.header.waitUntilLoadingHasFinished(); + // await PageObjects.header.waitUntilLoadingHasFinished(); } async saveVisualizationExpectSuccess(vizName, { saveAsNew = false } = {}) { @@ -834,7 +835,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli // by a bunch of 'L'ines from that point to the next. Those points are // the values we're going to use to calculate the data values we're testing. // So git rid of the one 'M' and split the rest on the 'L's. - const tempArray = data.replace('M', '').split('L'); + const tempArray = data.replace('M ', '').replace('M', '').replace(/ L /g, 'L').replace(/ /g, ',').split('L'); const chartSections = tempArray.length / 2; // log.debug('chartSections = ' + chartSections + ' height = ' + yAxisHeight + ' yAxisLabel = ' + yAxisLabel); const chartData = []; @@ -888,7 +889,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli // 1). get the range/pixel ratio const yAxisRatio = await this.getChartYAxisRatio(axis); // 3). get the visWrapper__chart elements - const svg = await find.byCssSelector('div.chart > svg'); + const svg = await find.byCssSelector('div.chart'); const $ = await svg.parseDomContent(); const chartData = $(`g > g.series > rect[data-label="${dataLabel}"]`).toArray().map(chart => { const barHeight = $(chart).attr('height'); diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 25f87b794edd0..81efbc3cb05fc 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -68,6 +68,8 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { public readonly isFirefox: boolean = browserType === Browsers.Firefox; + public readonly isInternetExplorer: boolean = browserType === Browsers.InternetExplorer; + /** * Is WebDriver instance W3C compatible */ @@ -181,7 +183,12 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { */ public async getCurrentUrl(): Promise { // strip _t=Date query param when url is read - const current = await driver.getCurrentUrl(); + let current: string; + if (this.isInternetExplorer) { + current = await driver.executeScript('return window.document.location.href'); + } else { + current = await driver.getCurrentUrl(); + } const currentWithoutTime = modifyUrl(current, parsed => { delete (parsed.query as any)._t; return void 0; diff --git a/test/functional/services/combo_box.ts b/test/functional/services/combo_box.ts index e04d9a4eb303b..d2a03e43fe5de 100644 --- a/test/functional/services/combo_box.ts +++ b/test/functional/services/combo_box.ts @@ -75,7 +75,7 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont return; } - comboBoxElement.scrollIntoViewIfNecessary(); + await comboBoxElement.scrollIntoViewIfNecessary(); await this.setFilterValue(comboBoxElement, value); await this.openOptionsList(comboBoxElement); diff --git a/test/functional/services/inspector.ts b/test/functional/services/inspector.ts index 74fc250438635..ce61530246eea 100644 --- a/test/functional/services/inspector.ts +++ b/test/functional/services/inspector.ts @@ -63,7 +63,7 @@ export function InspectorProvider({ getService }: FtrProviderContext) { if (!isOpen) { await retry.try(async () => { await testSubjects.click('openInspectorButton'); - await testSubjects.find('inspectorPanel'); + await testSubjects.exists('inspectorPanel'); }); } } diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index ee35ca9e1de79..a628f085ea708 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -233,6 +233,9 @@ export class WebElementWrapper { * @default { withJS: false } */ async clearValue(options: ClearOptions = { withJS: false }) { + if (this.browserType === Browsers.InternetExplorer) { + return this.clearValueWithKeyboard(); + } await this.retryCall(async function clearValue(wrapper) { if (wrapper.browserType === Browsers.Chrome || options.withJS) { // https://bugs.chromium.org/p/chromedriver/issues/detail?id=2702 @@ -249,6 +252,16 @@ export class WebElementWrapper { * @default { charByChar: false } */ async clearValueWithKeyboard(options: TypeOptions = { charByChar: false }) { + if (this.browserType === Browsers.InternetExplorer) { + const value = await this.getAttribute('value'); + // For IE testing, the text field gets clicked in the middle so + // first go HOME and then DELETE all chars + await this.pressKeys(this.Keys.HOME); + for (let i = 0; i <= value.length; i++) { + await this.pressKeys(this.Keys.DELETE); + } + return; + } if (options.charByChar === true) { const value = await this.getAttribute('value'); for (let i = 0; i <= value.length; i++) { diff --git a/test/functional/services/remote/browsers.ts b/test/functional/services/remote/browsers.ts index 7fb07046a3636..46d81f1737a55 100644 --- a/test/functional/services/remote/browsers.ts +++ b/test/functional/services/remote/browsers.ts @@ -20,4 +20,5 @@ export enum Browsers { Chrome = 'chrome', Firefox = 'firefox', + InternetExplorer = 'ie', } diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts index 6377d97dad28b..8c78ed852caf1 100644 --- a/test/functional/services/remote/webdriver.ts +++ b/test/functional/services/remote/webdriver.ts @@ -27,6 +27,8 @@ import { Builder, Capabilities, By, Key, logging, until } from 'selenium-webdriv import chrome from 'selenium-webdriver/chrome'; // @ts-ignore types not available import firefox from 'selenium-webdriver/firefox'; +// @ts-ignore types not available +import ie from 'selenium-webdriver/ie'; // @ts-ignore internal modules are not typed import { LegacyActionSequence } from 'selenium-webdriver/lib/actions'; // @ts-ignore internal modules are not typed @@ -34,6 +36,7 @@ import { Executor } from 'selenium-webdriver/lib/http'; // @ts-ignore internal modules are not typed import { getLogger } from 'selenium-webdriver/lib/logging'; +import { resolve, delimiter } from 'path'; import { preventParallelCalls } from './prevent_parallel_calls'; import { Browsers } from './browsers'; @@ -89,6 +92,7 @@ async function attemptToCreateCommand(log: ToolingLog, browserType: Browsers) { .withCapabilities(chromeCapabilities) .setChromeService(new chrome.ServiceBuilder(chromeDriver.path).enableVerboseLogging()) .build(); + case 'firefox': const firefoxOptions = new firefox.Options(); if (headlessBrowser === '1') { @@ -100,6 +104,30 @@ async function attemptToCreateCommand(log: ToolingLog, browserType: Browsers) { .setFirefoxOptions(firefoxOptions) .setFirefoxService(new firefox.ServiceBuilder(geckoDriver.path).enableVerboseLogging()) .build(); + + case 'ie': + // https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/ie_exports_Options.html + const driverPath = resolve( + __dirname, + '..\\..\\..\\..\\node_modules\\iedriver\\lib\\iedriver' + ); + process.env.PATH = driverPath + delimiter + process.env.PATH; + + const ieCapabilities = Capabilities.ie(); + ieCapabilities.set('se:ieOptions', { + 'ie.ensureCleanSession': true, + ignoreProtectedModeSettings: true, + ignoreZoomSetting: false, // requires us to have 100% zoom level + nativeEvents: true, // need this for values to stick but it requires 100% scaling and window focus + requireWindowFocus: true, + logLevel: 'TRACE', + }); + + return new Builder() + .forBrowser(browserType) + .withCapabilities(ieCapabilities) + .build(); + default: throw new Error(`${browserType} is not supported yet`); } diff --git a/x-pack/test/functional/config.ie.js b/x-pack/test/functional/config.ie.js new file mode 100644 index 0000000000000..7a46471726fcf --- /dev/null +++ b/x-pack/test/functional/config.ie.js @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +export default async function ({ readConfigFile }) { + const defaultConfig = await readConfigFile(require.resolve('./config')); + + return { + ...defaultConfig.getAll(), + //csp.strict: false + // testFiles: [ + // require.resolve(__dirname, './apps/advanced_settings'), + // require.resolve(__dirname, './apps/canvas'), + // require.resolve(__dirname, './apps/graph'), + // require.resolve(__dirname, './apps/monitoring'), + // require.resolve(__dirname, './apps/watcher'), + // require.resolve(__dirname, './apps/dashboard'), + // require.resolve(__dirname, './apps/dashboard_mode'), + // require.resolve(__dirname, './apps/discover'), + // require.resolve(__dirname, './apps/security'), + // require.resolve(__dirname, './apps/spaces'), + // require.resolve(__dirname, './apps/lens'), + // require.resolve(__dirname, './apps/logstash'), + // require.resolve(__dirname, './apps/grok_debugger'), + // require.resolve(__dirname, './apps/infra'), + // require.resolve(__dirname, './apps/machine_learning'), + // require.resolve(__dirname, './apps/rollup_job'), + // require.resolve(__dirname, './apps/maps'), + // require.resolve(__dirname, './apps/status_page'), + // require.resolve(__dirname, './apps/timelion'), + // require.resolve(__dirname, './apps/upgrade_assistant'), + // require.resolve(__dirname, './apps/code'), + // require.resolve(__dirname, './apps/visualize'), + // require.resolve(__dirname, './apps/uptime'), + // require.resolve(__dirname, './apps/saved_objects_management'), + // require.resolve(__dirname, './apps/dev_tools'), + // require.resolve(__dirname, './apps/apm'), + // require.resolve(__dirname, './apps/index_patterns'), + // require.resolve(__dirname, './apps/index_management'), + // require.resolve(__dirname, './apps/index_lifecycle_management'), + // require.resolve(__dirname, './apps/snapshot_restore'), + // require.resolve(__dirname, './apps/cross_cluster_replication'), + // require.resolve(__dirname, './apps/remote_clusters'), + // // This license_management file must be last because it is destructive. + // require.resolve(__dirname, './apps/license_management'), + // ], + + browser: { + type: 'ie', + }, + + junit: { + reportName: 'Internet Explorer UI Functional X-Pack Tests' + }, + + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + 'telemetry:optIn': false, + 'state:storeInSessionStorage': true, + }, + }, + + + kbnTestServer: { + ...defaultConfig.get('kbnTestServer'), + serverArgs: [ + ...defaultConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + ], + }, + + + }; +} diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts index 88501aad57b4a..67b2497cc91e3 100644 --- a/x-pack/test/functional/page_objects/infra_home_page.ts +++ b/x-pack/test/functional/page_objects/infra_home_page.ts @@ -19,7 +19,7 @@ export function InfraHomePageProvider({ getService }: FtrProviderContext) { const datePickerInput = await find.byCssSelector( `${testSubjSelector('waffleDatePicker')} .euiDatePicker.euiFieldText` ); - await datePickerInput.type(Array(30).fill(browser.keys.BACK_SPACE)); + await datePickerInput.clearValueWithKeyboard({ charByChar: true }); await datePickerInput.type([time, browser.keys.RETURN]); }, diff --git a/yarn.lock b/yarn.lock index 33fce33f4716c..9361ab685e757 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4592,6 +4592,11 @@ adm-zip@0.4.11: resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.11.tgz#2aa54c84c4b01a9d0fb89bb11982a51f13e3d62a" integrity sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA== +adm-zip@^0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.13.tgz#597e2f8cc3672151e1307d3e95cddbc75672314a" + integrity sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw== + affine-hull@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/affine-hull/-/affine-hull-1.0.0.tgz#763ff1d38d063ceb7e272f17ee4d7bbcaf905c5d" @@ -8369,21 +8374,21 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== +concat-stream@1.6.0, concat-stream@^1.4.7, concat-stream@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= dependencies: - buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.4.7, concat-stream@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= +concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: + buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" @@ -8416,6 +8421,14 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" +config-chain@~1.1.8: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + configstore@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" @@ -11928,6 +11941,16 @@ extract-stack@^1.0.0: resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-1.0.0.tgz#b97acaf9441eea2332529624b732fc5a1c8165fa" integrity sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo= +extract-zip@1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" + integrity sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw= + dependencies: + concat-stream "1.6.0" + debug "2.6.9" + mkdirp "0.5.0" + yauzl "2.4.1" + extract-zip@1.6.7, extract-zip@^1.6.6, extract-zip@^1.6.7: version "1.6.7" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" @@ -13788,6 +13811,11 @@ graceful-fs@^4.2.0, graceful-fs@^4.2.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== +graceful-fs@~1.1: + version "1.1.14" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.1.14.tgz#07078db5f6377f6321fceaaedf497de124dc9465" + integrity sha1-BweNtfY3f2Mh/Oqu30l94STclGU= + graceful-fs@~1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" @@ -15021,6 +15049,20 @@ idx@^2.5.2: resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.2.tgz#4b405c2e6d68d04136e0a368a7ab35b9caa0595f" integrity sha512-MLoGF4lQU5q/RqJJjRsuid52emu7tPVtSSZaYXsqRvSjvXdBEmIwk2urvbNvPBRU9Ox9I4WYnxiz2GjhU34Lrw== +iedriver@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/iedriver/-/iedriver-3.14.1.tgz#447c49be83c62d3f2f158283d58ccf7b35002be8" + integrity sha512-YyCi703BGK7R37A8QlSe2B87xgwDGGoPqBrlXe4Q68o/MNLJrR53/IpTs6J1+KKk51MLiTbWa57N7P3KZ11tow== + dependencies: + adm-zip "^0.4.13" + extract-zip "1.6.6" + kew "~0.1.7" + md5-file "^1.1.4" + mkdirp "0.3.5" + npmconf "^2.1.3" + request "^2.88.0" + rimraf "~2.0.2" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -15226,7 +15268,7 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.2.0, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -17402,6 +17444,11 @@ kdbush@^3.0.0: resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0" integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew== +kew@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/kew/-/kew-0.1.7.tgz#0a32a817ff1a9b3b12b8c9bacf4bc4d679af8e72" + integrity sha1-CjKoF/8amzsSuMm6z0vE1nmvjnI= + keymirror@0.1.1, keymirror@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35" @@ -18802,6 +18849,11 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.5.tgz#5292593e6754cb1bcc2b98030e4e0d6a3afc9ea1" integrity sha1-UpJZPmdUyxvMK5gDDk4Najr8nqE= +md5-file@^1.1.4: + version "1.1.10" + resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-1.1.10.tgz#d8f4fce76c92cb20b7d143a59f58ca49b4cf3174" + integrity sha1-2PT852ySyyC30UOln1jKSbTPMXQ= + md5.js@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" @@ -19368,6 +19420,18 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" +mkdirp@0.3.5, mkdirp@^0.3.5, mkdirp@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" + integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc= + +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + integrity sha1-HXMHam35hs2TROFecfzAWkyavxI= + dependencies: + minimist "0.0.8" + mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -19375,11 +19439,6 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi dependencies: minimist "0.0.8" -mkdirp@^0.3.5, mkdirp@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" - integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc= - mobx-react@^5.4.3: version "5.4.3" resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-5.4.3.tgz#6709b7dd89670c40e9815914ac2ca49cc02bfb47" @@ -20160,7 +20219,7 @@ nodemailer@^4.7.0: chalk "~0.4.0" underscore "~1.6.0" -"nopt@2 || 3", nopt@3.x, nopt@~3.0.6: +"nopt@2 || 3", nopt@3.x, nopt@~3.0.1, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= @@ -20307,6 +20366,22 @@ npm-run-path@^3.0.0: dependencies: path-key "^3.0.0" +npmconf@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.3.tgz#1cbe5dd02e899d365fed7260b54055473f90a15c" + integrity sha512-iTK+HI68GceCoGOHAQiJ/ik1iDfI7S+cgyG8A+PP18IU3X83kRhQIRhAUNj4Bp2JMx6Zrt5kCiozYa9uGWTjhA== + dependencies: + config-chain "~1.1.8" + inherits "~2.0.0" + ini "^1.2.0" + mkdirp "^0.5.0" + nopt "~3.0.1" + once "~1.3.0" + osenv "^0.1.0" + safe-buffer "^5.1.1" + semver "2 || 3 || 4" + uid-number "0.0.5" + "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -24620,6 +24695,13 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" +rimraf@~2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.0.3.tgz#f50a2965e7144e9afd998982f15df706730f56a9" + integrity sha1-9QopZecUTpr9mYmC8V33BnMPVqk= + optionalDependencies: + graceful-fs "~1.1" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" @@ -25083,6 +25165,11 @@ semver-truncate@^1.0.0: dependencies: semver "^5.3.0" +"semver@2 || 3 || 4", semver@^4.1.0: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= + "semver@2 || 3 || 4 || 5", semver@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -25098,11 +25185,6 @@ semver@5.7.0, semver@^5.4.1, semver@^5.6.0, semver@^5.7.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== -semver@^4.1.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= - semver@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" @@ -28161,6 +28243,11 @@ ui-select@0.19.8: resolved "https://registry.yarnpkg.com/ui-select/-/ui-select-0.19.8.tgz#74860848a7fd8bc494d9856d2f62776ea98637c1" integrity sha1-dIYISKf9i8SU2YVtL2J3bqmGN8E= +uid-number@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.5.tgz#5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e" + integrity sha1-Wj2yPvXb1VuB/ODsmirG/M3ruB4= + ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" From 54382fe122541ce36e9b28ae994af7dfc50b6b85 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Fri, 4 Oct 2019 11:29:54 -0500 Subject: [PATCH 11/33] [DOCS] Specifies deprecated setting for 7.x (#46162) --- docs/setup/settings.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 5b3db22a39ea6..cae44c6fc4575 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -271,9 +271,9 @@ identifies this Kibana instance. `server.port:`:: *Default: 5601* Kibana is served by a back end server. This setting specifies the port to use. -`server.rewriteBasePath:`:: *Default: true* Specifies whether Kibana should -rewrite requests that are prefixed with `server.basePath` or require that they -are rewritten by your reverse proxy. +`server.rewriteBasePath:`:: *Default: false* Deprecated setting that specifies if Kibana should +rewrite requests that are prefixed with `server.basePath`, or require that they +are rewritten by your reverse proxy. `server.socketTimeout:`:: *Default: "120000"* The number of milliseconds to wait before closing an inactive socket. From 8b1a3787cd7ec59501cb0ddcd7d6849511ba0a59 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Fri, 4 Oct 2019 10:07:38 -0700 Subject: [PATCH 12/33] Tsvb chart - unskipping the gauge test (#47155) Fixes https://github.com/elastic/kibana/issues/46677 - unskipping the tests --- test/functional/apps/visualize/_tsvb_chart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index 9143b931df975..5c6af1f245060 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -62,7 +62,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); // FLAKY: https://github.com/elastic/kibana/issues/46677 - describe.skip('gauge', () => { + describe('gauge', () => { beforeEach(async () => { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickGauge(); From 5a89c6ac5dc83a471d3ad6ec345cb85e650f0ae8 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Fri, 4 Oct 2019 10:37:54 -0700 Subject: [PATCH 13/33] [Task Manager] Ownership performance Improvements (#46997) Task Manager now uses updateByQuery to identify tasks it needs to pick up and claim ownership of them in a single call to Elasticsearch. This reduces the version conflicts that often plague Task Manager when running multiple Kibana against a single ES cluster. This has already shown small perf gains locally, and lays the ground work to a second phase (bulk processing the markTaskAsRunning step) which should allow to paralelise the task running feature further. --- .../alerting/server/alerts_client.test.ts | 4 + .../get_create_task_runner_function.test.ts | 1 + x-pack/legacy/plugins/task_manager/README.md | 10 +- .../task_manager/lib/middleware.test.ts | 75 ++-- .../legacy/plugins/task_manager/mappings.json | 3 + x-pack/legacy/plugins/task_manager/task.ts | 12 +- .../plugins/task_manager/task_manager.ts | 20 +- .../plugins/task_manager/task_pool.test.ts | 2 +- .../legacy/plugins/task_manager/task_pool.ts | 4 +- .../plugins/task_manager/task_runner.test.ts | 11 +- .../plugins/task_manager/task_runner.ts | 33 +- .../plugins/task_manager/task_store.test.ts | 338 +++++++++++++++++- .../legacy/plugins/task_manager/task_store.ts | 230 +++++++++++- .../task_manager/task_manager_integration.js | 11 + x-pack/test/plugin_api_perf/README.md | 26 +- .../plugins/task_manager_performance/index.js | 28 +- .../task_manager_performance/init_routes.js | 5 +- .../task_manager_perf_integration.ts | 2 +- 18 files changed, 748 insertions(+), 67 deletions(-) diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts index bba0051e31e08..856143c693954 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts @@ -110,6 +110,7 @@ describe('create()', () => { retryAt: null, state: {}, params: {}, + ownerId: null, }); savedObjectsClient.update.mockResolvedValueOnce({ id: '1', @@ -491,6 +492,7 @@ describe('create()', () => { retryAt: null, state: {}, params: {}, + ownerId: null, }); savedObjectsClient.update.mockResolvedValueOnce({ id: '1', @@ -568,6 +570,7 @@ describe('enable()', () => { taskType: '', startedAt: null, retryAt: null, + ownerId: null, }); await alertsClient.enable({ id: '1' }); @@ -643,6 +646,7 @@ describe('enable()', () => { taskType: '', startedAt: null, retryAt: null, + ownerId: null, }); alertsClientParams.createAPIKey.mockResolvedValueOnce({ created: true, diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts index 2490a187ee458..8875e964ef58e 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts @@ -33,6 +33,7 @@ beforeAll(() => { params: { alertId: '1', }, + ownerId: null, }; }); diff --git a/x-pack/legacy/plugins/task_manager/README.md b/x-pack/legacy/plugins/task_manager/README.md index adf7706443695..63c92102af251 100644 --- a/x-pack/legacy/plugins/task_manager/README.md +++ b/x-pack/legacy/plugins/task_manager/README.md @@ -171,9 +171,14 @@ The data stored for a task instance looks something like this: // This is incremented if a task fails or times out. attempts: 0, - // Currently, this is either idle | running. It is used to + // Currently, this is either idle | claiming | running | failed. It is used to // coordinate which Kibana instance owns / is running a specific // task instance. + // idle: Task Instance isn't being worked on + // claiming: A Kibana instance has claimed ownership but hasn't started running + // the Task Instance yet + // running: A Kibana instance has began working on the Task Instance + // failed: The last run of the Task Instance failed, waiting to retry status: 'idle', // The params specific to this task instance, which will be @@ -207,6 +212,9 @@ The data stored for a task instance looks something like this: // An application-specific designation, allowing different Kibana // plugins / apps to query for only those tasks they care about. scope: ['alerting'], + + // The Kibana UUID of the Kibana instance who last claimed ownership for running this task. + ownerId: '123e4567-e89b-12d3-a456-426655440000' } ``` diff --git a/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts b/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts index ff840061285c4..07afee1797462 100644 --- a/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts +++ b/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts @@ -31,6 +31,7 @@ const getMockConcreteTaskInstance = () => { state: any; taskType: string; params: any; + ownerId: string | null; } = { id: 'hy8o99o83', sequenceNumber: 1, @@ -44,6 +45,7 @@ const getMockConcreteTaskInstance = () => { state: {}, taskType: 'nice_task', params: { abc: 'def' }, + ownerId: null, }; return concrete; }; @@ -92,19 +94,19 @@ describe('addMiddlewareToChain', () => { .beforeSave({ taskInstance: getMockTaskInstance() }) .then((saveOpts: any) => { expect(saveOpts).toMatchInlineSnapshot(` -Object { - "taskInstance": Object { - "params": Object { - "abc": "def", - "m1": true, - "m2": true, - "m3": true, - }, - "state": Object {}, - "taskType": "nice_task", - }, -} -`); + Object { + "taskInstance": Object { + "params": Object { + "abc": "def", + "m1": true, + "m2": true, + "m3": true, + }, + "state": Object {}, + "taskType": "nice_task", + }, + } + `); }); }); @@ -145,29 +147,30 @@ Object { .beforeRun(getMockRunContext(getMockConcreteTaskInstance())) .then(contextOpts => { expect(contextOpts).toMatchInlineSnapshot(` -Object { - "kbnServer": Object {}, - "m1": true, - "m2": true, - "m3": true, - "taskInstance": Object { - "attempts": 0, - "id": "hy8o99o83", - "params": Object { - "abc": "def", - }, - "primaryTerm": 1, - "retryAt": null, - "runAt": 2018-09-18T05:33:09.588Z, - "scheduledAt": 2018-09-18T05:33:09.588Z, - "sequenceNumber": 1, - "startedAt": null, - "state": Object {}, - "status": "idle", - "taskType": "nice_task", - }, -} -`); + Object { + "kbnServer": Object {}, + "m1": true, + "m2": true, + "m3": true, + "taskInstance": Object { + "attempts": 0, + "id": "hy8o99o83", + "ownerId": null, + "params": Object { + "abc": "def", + }, + "primaryTerm": 1, + "retryAt": null, + "runAt": 2018-09-18T05:33:09.588Z, + "scheduledAt": 2018-09-18T05:33:09.588Z, + "sequenceNumber": 1, + "startedAt": null, + "state": Object {}, + "status": "idle", + "taskType": "nice_task", + }, + } + `); }); }); }); diff --git a/x-pack/legacy/plugins/task_manager/mappings.json b/x-pack/legacy/plugins/task_manager/mappings.json index 6638a75d82546..96653a4de1b31 100644 --- a/x-pack/legacy/plugins/task_manager/mappings.json +++ b/x-pack/legacy/plugins/task_manager/mappings.json @@ -36,6 +36,9 @@ }, "scope": { "type": "keyword" + }, + "ownerId": { + "type": "keyword" } } } diff --git a/x-pack/legacy/plugins/task_manager/task.ts b/x-pack/legacy/plugins/task_manager/task.ts index 5c85f692fd614..dd74acc2636e9 100644 --- a/x-pack/legacy/plugins/task_manager/task.ts +++ b/x-pack/legacy/plugins/task_manager/task.ts @@ -136,7 +136,7 @@ export interface TaskDictionary { [taskType: string]: T; } -export type TaskStatus = 'idle' | 'running' | 'failed'; +export type TaskStatus = 'idle' | 'claiming' | 'running' | 'failed'; /* * A task instance represents all of the data required to store, fetch, @@ -209,6 +209,11 @@ export interface TaskInstance { * and then query such tasks to provide a glimpse at only reporting tasks, rather than at all tasks. */ scope?: string[]; + + /** + * The random uuid of the Kibana instance which claimed ownership of the task last + */ + ownerId?: string | null; } /** @@ -268,4 +273,9 @@ export interface ConcreteTaskInstance extends TaskInstance { * any state, this will be the empy object: {} */ state: Record; + + /** + * The random uuid of the Kibana instance which claimed ownership of the task last + */ + ownerId: string | null; } diff --git a/x-pack/legacy/plugins/task_manager/task_manager.ts b/x-pack/legacy/plugins/task_manager/task_manager.ts index 01ddd1efbe762..c7cfdfb4223b8 100644 --- a/x-pack/legacy/plugins/task_manager/task_manager.ts +++ b/x-pack/legacy/plugins/task_manager/task_manager.ts @@ -9,6 +9,7 @@ import { Logger } from './types'; import { fillPool } from './lib/fill_pool'; import { addMiddlewareToChain, BeforeSaveMiddlewareParams, Middleware } from './lib/middleware'; import { sanitizeTaskDefinitions } from './lib/sanitize_task_definitions'; +import { intervalFromNow } from './lib/intervals'; import { TaskDefinition, TaskDictionary, @@ -77,6 +78,7 @@ export class TaskManager { index: opts.config.get('xpack.task_manager.index'), maxAttempts: opts.config.get('xpack.task_manager.max_attempts'), definitions: this.definitions, + kibanaId: opts.config.get('server.uuid'), }); const pool = new TaskPool({ logger: this.logger, @@ -93,9 +95,7 @@ export class TaskManager { const poller = new TaskPoller({ logger: this.logger, pollInterval: opts.config.get('xpack.task_manager.poll_interval'), - work(): Promise { - return fillPool(pool.run, store.fetchAvailableTasks, createRunner); - }, + work: (): Promise => fillPool(pool.run, () => this.claimAvailableTasks(), createRunner), }); this.pool = pool; @@ -127,6 +127,20 @@ export class TaskManager { startPoller(); } + private async claimAvailableTasks() { + const { docs, claimedTasks } = await this.store.claimAvailableTasks({ + size: this.pool.availableWorkers, + claimOwnershipUntil: intervalFromNow('30s')!, + }); + + if (docs.length !== claimedTasks) { + this.logger.warn( + `[Task Ownership error]: (${claimedTasks}) tasks were claimed by Kibana, but (${docs.length}) tasks were fetched` + ); + } + return docs; + } + private async waitUntilStarted() { if (!this.isStarted) { await new Promise(resolve => { diff --git a/x-pack/legacy/plugins/task_manager/task_pool.test.ts b/x-pack/legacy/plugins/task_manager/task_pool.test.ts index 795d1099f3c6f..e6a83dd1911bd 100644 --- a/x-pack/legacy/plugins/task_manager/task_pool.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_pool.test.ts @@ -195,7 +195,7 @@ describe('TaskPool', () => { return { isExpired: false, cancel: async () => undefined, - claimOwnership: async () => true, + markTaskAsRunning: async () => true, run: mockRun(), }; } diff --git a/x-pack/legacy/plugins/task_manager/task_pool.ts b/x-pack/legacy/plugins/task_manager/task_pool.ts index 42a0e9ad436ad..7afbec65a0d8c 100644 --- a/x-pack/legacy/plugins/task_manager/task_pool.ts +++ b/x-pack/legacy/plugins/task_manager/task_pool.ts @@ -75,7 +75,7 @@ export class TaskPool { private async attemptToRun(tasks: TaskRunner[]) { for (const task of tasks) { if (this.availableWorkers > 0) { - if (await task.claimOwnership()) { + if (await task.markTaskAsRunning()) { this.running.add(task); task .run() @@ -83,6 +83,8 @@ export class TaskPool { this.logger.warn(`Task ${task} failed in attempt to run: ${err.message}`); }) .then(() => this.running.delete(task)); + } else { + this.logger.warn(`Failed to mark Task ${task} as running`); } } else { return false; diff --git a/x-pack/legacy/plugins/task_manager/task_runner.test.ts b/x-pack/legacy/plugins/task_manager/task_runner.test.ts index eaaa6717230a2..49c55279eafe7 100644 --- a/x-pack/legacy/plugins/task_manager/task_runner.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_runner.test.ts @@ -243,7 +243,7 @@ describe('TaskManagerRunner', () => { }, }); - await runner.claimOwnership(); + await runner.markTaskAsRunning(); sinon.assert.calledOnce(store.update); const instance = store.update.args[0][0]; @@ -409,7 +409,7 @@ describe('TaskManagerRunner', () => { }, }); - await runner.claimOwnership(); + await runner.markTaskAsRunning(); sinon.assert.calledOnce(store.update); sinon.assert.calledWith(getRetryStub, initialAttempts + 1); @@ -442,7 +442,7 @@ describe('TaskManagerRunner', () => { }, }); - await runner.claimOwnership(); + await runner.markTaskAsRunning(); sinon.assert.calledOnce(store.update); sinon.assert.calledWith(getRetryStub, initialAttempts + 1); @@ -477,7 +477,7 @@ describe('TaskManagerRunner', () => { }, }); - await runner.claimOwnership(); + await runner.markTaskAsRunning(); sinon.assert.calledOnce(store.update); sinon.assert.calledWith(getRetryStub, initialAttempts + 1); @@ -510,7 +510,7 @@ describe('TaskManagerRunner', () => { }, }); - await runner.claimOwnership(); + await runner.markTaskAsRunning(); sinon.assert.calledOnce(store.update); sinon.assert.notCalled(getRetryStub); @@ -619,6 +619,7 @@ describe('TaskManagerRunner', () => { state: {}, status: 'idle', user: 'example', + ownerId: null, }, opts.instance || {} ), diff --git a/x-pack/legacy/plugins/task_manager/task_runner.ts b/x-pack/legacy/plugins/task_manager/task_runner.ts index e97f361b2ca08..a51e0c885b978 100644 --- a/x-pack/legacy/plugins/task_manager/task_runner.ts +++ b/x-pack/legacy/plugins/task_manager/task_runner.ts @@ -30,7 +30,7 @@ const defaultBackoffPerFailure = 5 * 60 * 1000; export interface TaskRunner { isExpired: boolean; cancel: CancelFunction; - claimOwnership: () => Promise; + markTaskAsRunning: () => Promise; run: () => Promise; toString?: () => string; } @@ -152,12 +152,25 @@ export class TaskManagerRunner implements TaskRunner { * * @returns {Promise} */ - public async claimOwnership(): Promise { + public async markTaskAsRunning(): Promise { const VERSION_CONFLICT_STATUS = 409; const attempts = this.instance.attempts + 1; const now = new Date(); + const ownershipClaimedUntil = this.instance.retryAt; + try { + const { id } = this.instance; + + const timeUntilClaimExpires = howManyMsUntilOwnershipClaimExpires(ownershipClaimedUntil); + if (timeUntilClaimExpires < 0) { + this.logger.debug( + `[Task Runner] Task ${id} started after ownership expired (${Math.abs( + timeUntilClaimExpires + )}ms after expiry)` + ); + } + this.instance = await this.store.update({ ...this.instance, status: 'running', @@ -174,6 +187,17 @@ export class TaskManagerRunner implements TaskRunner { }), }); + const timeUntilClaimExpiresAfterUpdate = howManyMsUntilOwnershipClaimExpires( + ownershipClaimedUntil + ); + if (timeUntilClaimExpiresAfterUpdate < 0) { + this.logger.debug( + `[Task Runner] Task ${id} ran after ownership expired (${Math.abs( + timeUntilClaimExpiresAfterUpdate + )}ms after expiry)` + ); + } + return true; } catch (error) { if (error.statusCode !== VERSION_CONFLICT_STATUS) { @@ -246,6 +270,7 @@ export class TaskManagerRunner implements TaskRunner { status, startedAt: null, retryAt: null, + ownerId: null, attempts: result.error ? this.instance.attempts : 0, }); @@ -323,3 +348,7 @@ function sanitizeInstance(instance: ConcreteTaskInstance): ConcreteTaskInstance state: instance.state || {}, }; } + +function howManyMsUntilOwnershipClaimExpires(ownershipClaimedUntil: Date | null): number { + return ownershipClaimedUntil ? ownershipClaimedUntil.getTime() - Date.now() : 0; +} diff --git a/x-pack/legacy/plugins/task_manager/task_store.test.ts b/x-pack/legacy/plugins/task_manager/task_store.test.ts index c24ffe09d18e2..47b2ed6ff48f3 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.test.ts @@ -6,8 +6,9 @@ import _ from 'lodash'; import sinon from 'sinon'; +import uuid from 'uuid'; import { TaskDictionary, TaskDefinition, TaskInstance, TaskStatus } from './task'; -import { FetchOpts, TaskStore } from './task_store'; +import { FetchOpts, StoreOpts, OwnershipClaimingOpts, TaskStore } from './task_store'; import { mockLogger } from './test_utils'; import { SavedObjectsClientMock } from 'src/core/server/mocks'; import { SavedObjectsSerializer, SavedObjectsSchema, SavedObjectAttributes } from 'src/core/server'; @@ -60,6 +61,7 @@ describe('TaskStore', () => { ); const store = new TaskStore({ index: 'tasky', + kibanaId: '', serializer, callCluster, maxAttempts: 2, @@ -159,6 +161,7 @@ describe('TaskStore', () => { const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits } })); const store = new TaskStore({ index: 'tasky', + kibanaId: '', serializer, callCluster, maxAttempts: 2, @@ -350,6 +353,7 @@ describe('TaskStore', () => { const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits: [] } })); const store = new TaskStore({ index: 'tasky', + kibanaId: '', serializer, callCluster, definitions: taskDefinitions, @@ -549,6 +553,321 @@ describe('TaskStore', () => { }); }); + describe('claimAvailableTasks', () => { + async function testClaimAvailableTasks({ + opts = {}, + hits = generateFakeTasks(1), + claimingOpts, + }: { + opts: Partial; + hits?: any[]; + claimingOpts: OwnershipClaimingOpts; + }) { + const versionConflicts = 2; + const callCluster = sinon.spy(async (name: string, params?: any) => + name === 'updateByQuery' + ? { + total: hits.length + versionConflicts, + updated: hits.length, + version_conflicts: versionConflicts, + } + : { hits: { hits } } + ); + const store = new TaskStore({ + callCluster, + maxAttempts: 2, + definitions: taskDefinitions, + serializer, + savedObjectsRepository: savedObjectsClient, + kibanaId: '', + index: '', + ...opts, + }); + + const result = await store.claimAvailableTasks(claimingOpts); + + sinon.assert.calledTwice(callCluster); + sinon.assert.calledWithMatch(callCluster, 'updateByQuery', { max_docs: claimingOpts.size }); + sinon.assert.calledWithMatch(callCluster, 'search', { body: { size: claimingOpts.size } }); + + return { + result, + args: Object.assign({}, ...callCluster.args.map(([name, args]) => ({ [name]: args }))), + }; + } + + test('it returns normally with no tasks when the index does not exist.', async () => { + const callCluster = sinon.spy(async (name: string, params?: any) => ({ + total: 0, + updated: 0, + })); + const store = new TaskStore({ + index: 'tasky', + kibanaId: '', + serializer, + callCluster, + definitions: taskDefinitions, + maxAttempts: 2, + savedObjectsRepository: savedObjectsClient, + }); + const { docs } = await store.claimAvailableTasks({ + claimOwnershipUntil: new Date(), + size: 10, + }); + sinon.assert.calledOnce(callCluster); + sinon.assert.calledWithMatch(callCluster, 'updateByQuery', { + ignoreUnavailable: true, + max_docs: 10, + }); + expect(docs.length).toBe(0); + }); + + test('it filters claimed tasks down by supported types, maxAttempts, status, and runAt', async () => { + const maxAttempts = _.random(2, 43); + const customMaxAttempts = _.random(44, 100); + const { + args: { + updateByQuery: { + body: { query }, + }, + }, + } = await testClaimAvailableTasks({ + opts: { + maxAttempts, + definitions: { + foo: { + type: 'foo', + title: '', + createTaskRunner: jest.fn(), + }, + bar: { + type: 'bar', + title: '', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + }, + }, + claimingOpts: { claimOwnershipUntil: new Date(), size: 10 }, + }); + expect(query).toMatchObject({ + bool: { + must: [ + { term: { type: 'task' } }, + { + bool: { + must: [ + { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], + }, + }, + ], + }, + }, + { + bool: { + should: [ + { exists: { field: 'task.interval' } }, + { + bool: { + must: [ + { term: { 'task.taskType': 'foo' } }, + { + range: { + 'task.attempts': { + lt: maxAttempts, + }, + }, + }, + ], + }, + }, + { + bool: { + must: [ + { term: { 'task.taskType': 'bar' } }, + { + range: { + 'task.attempts': { + lt: customMaxAttempts, + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }); + }); + + test('it claims tasks by setting their ownerId, status and retryAt', async () => { + const kibanaId = uuid.v1(); + const claimOwnershipUntil = new Date(Date.now()); + const { + args: { + updateByQuery: { + body: { script }, + }, + }, + } = await testClaimAvailableTasks({ + opts: { + kibanaId, + }, + claimingOpts: { + claimOwnershipUntil, + size: 10, + }, + }); + expect(script).toMatchObject({ + source: `ctx._source.task.ownerId=params.ownerId; ctx._source.task.status=params.status; ctx._source.task.retryAt=params.retryAt;`, + lang: 'painless', + params: { + ownerId: kibanaId, + retryAt: claimOwnershipUntil, + status: 'claiming', + }, + }); + }); + + test('it returns task objects', async () => { + const kibanaId = uuid.v1(); + const claimOwnershipUntil = new Date(Date.now()); + const runAt = new Date(); + const tasks = [ + { + _id: 'aaa', + _source: { + type: 'task', + task: { + runAt, + taskType: 'foo', + interval: undefined, + attempts: 0, + status: 'idle', + params: '{ "hello": "world" }', + state: '{ "baby": "Henhen" }', + user: 'jimbo', + scope: ['reporting'], + ownerId: kibanaId, + }, + }, + _seq_no: 1, + _primary_term: 2, + sort: ['a', 1], + }, + { + _id: 'bbb', + _source: { + type: 'task', + task: { + runAt, + taskType: 'bar', + interval: '5m', + attempts: 2, + status: 'running', + params: '{ "shazm": 1 }', + state: '{ "henry": "The 8th" }', + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: kibanaId, + }, + }, + _seq_no: 3, + _primary_term: 4, + sort: ['b', 2], + }, + ]; + const { + result: { docs }, + args: { + search: { + body: { query }, + }, + }, + } = await testClaimAvailableTasks({ + opts: { + kibanaId, + }, + claimingOpts: { + claimOwnershipUntil, + size: 10, + }, + hits: tasks, + }); + + expect(query.bool.must).toContainEqual({ + bool: { + must: [ + { + term: { + 'task.ownerId': kibanaId, + }, + }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }); + + expect(docs).toMatchObject([ + { + attempts: 0, + id: 'aaa', + interval: undefined, + params: { hello: 'world' }, + runAt, + scope: ['reporting'], + state: { baby: 'Henhen' }, + status: 'idle', + taskType: 'foo', + user: 'jimbo', + ownerId: kibanaId, + }, + { + attempts: 2, + id: 'bbb', + interval: '5m', + params: { shazm: 1 }, + runAt, + scope: ['reporting', 'ceo'], + state: { henry: 'The 8th' }, + status: 'running', + taskType: 'bar', + user: 'dabo', + ownerId: kibanaId, + }, + ]); + }); + }); + describe('update', () => { test('refreshes the index, handles versioning', async () => { const task = { @@ -563,6 +882,7 @@ describe('TaskStore', () => { attempts: 3, status: 'idle' as TaskStatus, version: '123', + ownerId: null, }; savedObjectsClient.update.mockImplementation( @@ -579,6 +899,7 @@ describe('TaskStore', () => { const store = new TaskStore({ index: 'tasky', + kibanaId: '', serializer, callCluster: jest.fn(), maxAttempts: 2, @@ -604,6 +925,7 @@ describe('TaskStore', () => { status: task.status, taskType: task.taskType, user: undefined, + ownerId: null, }, { version: '123' } ); @@ -626,6 +948,7 @@ describe('TaskStore', () => { const callCluster = jest.fn(); const store = new TaskStore({ index: 'tasky', + kibanaId: '', serializer, callCluster, maxAttempts: 2, @@ -638,3 +961,16 @@ describe('TaskStore', () => { }); }); }); + +function generateFakeTasks(count: number = 1) { + return _.times(count, () => ({ + _id: 'aaa', + _source: { + type: 'task', + task: {}, + }, + _seq_no: _.random(1, 5), + _primary_term: _.random(1, 5), + sort: ['a', _.random(1, 5)], + })); +} diff --git a/x-pack/legacy/plugins/task_manager/task_store.ts b/x-pack/legacy/plugins/task_manager/task_store.ts index ddd1b0deb6004..bf15435f54722 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.ts @@ -27,16 +27,37 @@ import { export interface StoreOpts { callCluster: ElasticJs; index: string; + kibanaId: string; maxAttempts: number; definitions: TaskDictionary; savedObjectsRepository: SavedObjectsClientContract; serializer: SavedObjectsSerializer; } -export interface FetchOpts { +export interface SearchOpts { searchAfter?: any[]; - sort?: object[]; + sort?: object | object[]; query?: object; + size?: number; + seq_no_primary_term?: boolean; + search_after?: any[]; +} + +export interface FetchOpts extends SearchOpts { + sort?: object[]; +} + +export interface UpdateByQuerySearchOpts extends SearchOpts { + script?: object; +} + +export interface UpdateByQueryOpts extends SearchOpts { + max_docs?: number; +} + +export interface OwnershipClaimingOpts { + claimOwnershipUntil: Date; + size: number; } export interface FetchResult { @@ -44,6 +65,17 @@ export interface FetchResult { docs: ConcreteTaskInstance[]; } +export interface ClaimOwnershipResult { + claimedTasks: number; + docs: ConcreteTaskInstance[]; +} + +export interface UpdateByQueryResult { + updated: number; + version_conflicts: number; + total: number; +} + /** * Wraps an elasticsearch connection and provides a task manager-specific * interface into the index. @@ -51,6 +83,7 @@ export interface FetchResult { export class TaskStore { public readonly maxAttempts: number; public readonly index: string; + public readonly kibanaId: string; private callCluster: ElasticJs; private definitions: TaskDictionary; private savedObjectsRepository: SavedObjectsClientContract; @@ -69,6 +102,7 @@ export class TaskStore { constructor(opts: StoreOpts) { this.callCluster = opts.callCluster; this.index = opts.index; + this.kibanaId = opts.kibanaId; this.maxAttempts = opts.maxAttempts; this.definitions = opts.definitions; this.serializer = opts.serializer; @@ -196,6 +230,152 @@ export class TaskStore { return docs; } + /** + * Claims available tasks from the index, which are ready to be run. + * - runAt is now or past + * - is not currently claimed by any instance of Kibana + * - has a type that is in our task definitions + * + * @param {OwnershipClaimingOpts} options + * @returns {Promise} + */ + public async claimAvailableTasks(opts: OwnershipClaimingOpts): Promise { + const claimedTasks = await this.markAvailableTasksAsClaimed(opts); + const docs = claimedTasks > 0 ? await this.sweepForClaimedTasks(opts) : []; + return { + claimedTasks, + docs, + }; + } + + private async markAvailableTasksAsClaimed({ + size, + claimOwnershipUntil, + }: OwnershipClaimingOpts): Promise { + const { updated } = await this.updateByQuery( + { + query: { + bool: { + must: [ + // Either a task with idle status and runAt <= now or + // status running or claiming with a retryAt <= now. + { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], + }, + }, + ], + }, + }, + // Either task has an interval or the attempts < the maximum configured + { + bool: { + should: [ + { exists: { field: 'task.interval' } }, + ...Object.entries(this.definitions).map(([type, definition]) => ({ + bool: { + must: [ + { term: { 'task.taskType': type } }, + { + range: { + 'task.attempts': { + lt: definition.maxAttempts || this.maxAttempts, + }, + }, + }, + ], + }, + })), + ], + }, + }, + ], + }, + }, + sort: { + _script: { + type: 'number', + order: 'asc', + script: { + lang: 'expression', + source: `doc['task.retryAt'].value || doc['task.runAt'].value`, + }, + }, + }, + seq_no_primary_term: true, + script: { + source: `ctx._source.task.ownerId=params.ownerId; ctx._source.task.status=params.status; ctx._source.task.retryAt=params.retryAt;`, + lang: 'painless', + params: { + ownerId: this.kibanaId, + retryAt: claimOwnershipUntil, + status: 'claiming', + }, + }, + }, + { + max_docs: size, + } + ); + return updated; + } + + /** + * Fetches tasks from the index, which are owned by the current Kibana instance + */ + private async sweepForClaimedTasks({ + size, + }: OwnershipClaimingOpts): Promise { + const { docs } = await this.search({ + query: { + bool: { + must: [ + { + term: { + 'task.ownerId': this.kibanaId, + }, + }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + size, + sort: { + _script: { + type: 'number', + order: 'asc', + script: { + lang: 'expression', + source: `doc['task.retryAt'].value || doc['task.runAt'].value`, + }, + }, + }, + seq_no_primary_term: true, + }); + + return docs; + } /** * Updates the specified doc in the index, returning the doc * with its version up to date. @@ -224,12 +404,8 @@ export class TaskStore { await this.savedObjectsRepository.delete('task', id); } - private async search(opts: any = {}): Promise { - const originalQuery = opts.query; - const queryOnlyTasks = { term: { type: 'task' } }; - const query = originalQuery - ? { bool: { must: [queryOnlyTasks, originalQuery] } } - : queryOnlyTasks; + private async search(opts: SearchOpts = {}): Promise { + const { query } = ensureQueryOnlyReturnsTaskObjects(opts); const result = await this.callCluster('search', { index: this.index, @@ -250,6 +426,31 @@ export class TaskStore { searchAfter: (rawDocs.length && rawDocs[rawDocs.length - 1].sort) || [], }; } + + private async updateByQuery( + opts: UpdateByQuerySearchOpts = {}, + { max_docs }: UpdateByQueryOpts = {} + ): Promise { + const { query } = ensureQueryOnlyReturnsTaskObjects(opts); + const result = await this.callCluster('updateByQuery', { + index: this.index, + ignoreUnavailable: true, + refresh: true, + max_docs, + conflicts: 'proceed', + body: { + ...opts, + query, + }, + }); + + const { total, updated, version_conflicts } = result; + return { + total, + updated, + version_conflicts, + }; + } } function paginatableSort(sort: any[] = []) { @@ -301,3 +502,16 @@ function parseJSONField(json: string, fieldName: string, id: string) { throw new Error(`Task "${id}"'s ${fieldName} field has invalid JSON: ${json}`); } } + +function ensureQueryOnlyReturnsTaskObjects(opts: SearchOpts): SearchOpts { + const originalQuery = opts.query; + const queryOnlyTasks = { term: { type: 'task' } }; + const query = originalQuery + ? { bool: { must: [queryOnlyTasks, originalQuery] } } + : queryOnlyTasks; + + return { + ...opts, + query, + }; +} diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js index 5c0b59674bded..30d830cd6c919 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js @@ -9,6 +9,8 @@ import expect from '@kbn/expect'; import url from 'url'; import supertestAsPromised from 'supertest-as-promised'; +const { task: { properties: taskManagerIndexMapping } } = require('../../../../legacy/plugins/task_manager/mappings.json'); + export default function ({ getService }) { const es = getService('es'); const log = getService('log'); @@ -30,6 +32,15 @@ export default function ({ getService }) { q: 'type:task', refresh: true, }); + } else { + await es.indices.create({ + index: testHistoryIndex, + body: { + mappings: { + properties: taskManagerIndexMapping + }, + }, + }); } }); diff --git a/x-pack/test/plugin_api_perf/README.md b/x-pack/test/plugin_api_perf/README.md index 034374e196dca..f47a2aeb7878a 100644 --- a/x-pack/test/plugin_api_perf/README.md +++ b/x-pack/test/plugin_api_perf/README.md @@ -50,4 +50,28 @@ After the test runs you should get the following output: If you look at the debug output you'll see a summary of how the test went: You'll see the average number of tasks executed per second, over a period of each 5 second window (meaning we calculate the running average based on a sliding window of 5 seconds). -You'll also see the average time it takes from the moment a task's scheduled time was reached, until Task Manager picked it up for execution. \ No newline at end of file +You'll also see the average time it takes from the moment a task's scheduled time was reached, until Task Manager picked it up for execution. + + +# Running the test against multiple Kibana and a single Elasticsearch +This is not a clean and ideal way of running this test, but it is a workaround that's worth understanding. + +## Test Description +The idea is that we would like to test performance of running multiple Kibana instances side by side, both pointing at the same Elasticsearch cluster. + +This is needed in order to verify that the distributed nature of Kibana doesn't introduce issues or break assumptions in our developed solutions. + +The challenge is that the _plugin_ used to create the Perf test is exposed in the FTS, but not in a standard Kibana build. + +Running two Kibana in FTS side by side is actually very tricky, so below is a step by step method for achieving this. +Ideally we can clean this up and make it easier and less hacky in the future, but for now, this documents how this can be achieved. + +## Method +1. You need two cloned repos of Kibana, so clone a second Kibana of your personal form along side your existing clone. Personally I have two co-located Kibana folders (`./elastic/kibana` and `./elastic/_kibana`, where the first is my working clone and the other is never used for actual dev work, but that's just me -GM). +1. You can run the FTS in the main clone of your fork by running `node scripts/functional_tests_server.js --config=test/plugin_api_perf/config.js` in the `x-pack` folder. +1. Once you've began running the default FTS, you want your second FTS to run such that it is referencing the Elasticsearch instance started by that first FTS. You achieve this by exporting a `TEST_ES_URL` Environment variable that points at it. By default, you should be able to run this: `export TEST_ES_URL=http://elastic:changeme@localhost:9220`. Do this in a terminal window opened in your **second** clone of Kibana (in my case, the `./elastic/_kibana` folder). +1. One issue I encountered with FTS is that I can't tell it _not to start its own ES instance at all_. To achieve this, in `packages/kbn-test/src/functional_tests/tasks.js` you need to comment out the line that starts up its own ES (`const es = await runElasticsearch({ config, options: opts });` [line 85] and `await es.cleanup();` shortly after) +1. Next you want each instance of Kibana to run with its own UUID as that is used to identify each Kibana's owned tasks. In the file `x-pack/test/functional/config.js` simple change the uuid on the line `--server.uuid=` into any random UUID. +1. Now that you've made these changes you can kick off your second Kibana FTS by running ths following in the second clone's `x-pack` folder: `TEST_KIBANA_PORT=5621 node scripts/functional_tests_server.js --config=test/plugin_api_perf/config.js`. This runs Kibana on a different port than the first FTS (`5621` instead of `5620`). +1. With two FTS Kibana running and both pointing at the same Elasticsearch. Now, you can run the actual perf test by running `node scripts/functional_test_runner.js --config=test/plugin_api_perf/config.js` in a third terminal + diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js index abad95729dcf7..0547069dfb469 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js @@ -62,12 +62,32 @@ export default function TaskManagerPerformanceAPI(kibana) { createTaskRunner: ({ taskInstance }) => { return { async run() { - const { state } = taskInstance; - const leadTime = Date.now() - taskInstance.runAt; + const { params, state } = taskInstance; + + const runAt = millisecondsFromNow(5000); + const now = Date.now(); + const leadTime = now - taskInstance.runAt; performanceState.leadTimeQueue.push(leadTime); + + const counter = (state.counter ? 1 + state.counter : 1); + + const stateUpdated = { + ...state, + counter + }; + + if(params.trackExecutionTimeline) { + stateUpdated.timeline = stateUpdated.timeline || []; + stateUpdated.timeline.push({ + owner: taskInstance.owner.split('-')[0], + counter, + leadTime, + ranAt: now + }); + } return { - state, - runAt: millisecondsFromNow(1000), + state: stateUpdated, + runAt, }; }, }; diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js index 316c434b33c43..b9fe788093d11 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js @@ -18,11 +18,12 @@ export function initRoutes(server, performanceState) { payload: Joi.object({ tasksToSpawn: Joi.number().required(), durationInSeconds: Joi.number().required(), + trackExecutionTimeline: Joi.boolean().default(false).required(), }), }, }, async handler(request) { - const { tasksToSpawn, durationInSeconds } = request.payload; + const { tasksToSpawn, durationInSeconds, trackExecutionTimeline } = request.payload; const tasks = []; for (let taskIndex = 0; taskIndex < tasksToSpawn; taskIndex++) { @@ -30,7 +31,7 @@ export function initRoutes(server, performanceState) { await taskManager.schedule( { taskType: 'performanceTestTask', - params: { taskIndex }, + params: { taskIndex, trackExecutionTimeline }, scope: [scope], }, { request } diff --git a/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts b/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts index 292a96baacd98..e8dbf7c509287 100644 --- a/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts +++ b/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts @@ -15,7 +15,7 @@ export default function({ getService }: { getService: (service: string) => any } const { runningAverageTasks, runningAverageLeadTime } = await supertest .post('/api/perf_tasks') .set('kbn-xsrf', 'xxx') - .send({ tasksToSpawn: 10, durationInSeconds: 60 }) + .send({ tasksToSpawn: 20, trackExecutionTimeline: true, durationInSeconds: 60 }) .expect(200) .then((response: any) => response.body); From e89425552b079c24b7ee11f04e3662a04a875e5d Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 4 Oct 2019 13:53:46 -0400 Subject: [PATCH 14/33] Add pipeline for flaky test runner job (#46740) * WIP Jenkinsfile for flaky test runner * Fix syntax * A few more jenkinsfile fixes * A few more fixes * Can't round numbers in Jenkins groovy sandbox apparently * More fixes * Only do build_kbn_tp_sample_panel_action once during flaky testing * Fix path and try setting a different JOB value * Keep track of flaky test failures * Introduce flaky test for testing * Some flaky test pipeline cleanup * Fix a couple of issues with flaky test failure tracking * Update flaky test runner build name/desc with metadata * Revert "Introduce flaky test for testing" This reverts commit 202e9d86cf130ef37aff1817781a5b694aa2aa32. * Try adding a local shared library * Move local library loading logic to kibana pipeline library * Move shared groovy code to shared library * Add missed file * Add ability to specify multiple agents for flaky test runner * Update kibana-pipeline-library version * Fix bug causing early exit for oss ciGroup1 --- .ci/Jenkinsfile_flaky | 113 +++++++ Jenkinsfile | 308 ++---------------- src/dev/ci_setup/setup_env.sh | 6 + src/dev/precommit_hook/casing_check_config.js | 3 +- ...enkins_build_kbn_tp_sample_panel_action.sh | 9 + test/scripts/jenkins_ci_group.sh | 5 +- vars/kibanaPipeline.groovy | 237 ++++++++++++++ vars/runbld.groovy | 11 + 8 files changed, 411 insertions(+), 281 deletions(-) create mode 100644 .ci/Jenkinsfile_flaky create mode 100755 test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh create mode 100644 vars/kibanaPipeline.groovy create mode 100644 vars/runbld.groovy diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky new file mode 100644 index 0000000000000..e484c8c24d790 --- /dev/null +++ b/.ci/Jenkinsfile_flaky @@ -0,0 +1,113 @@ +#!/bin/groovy + +library 'kibana-pipeline-library' +kibanaLibrary.load() + +// Looks like 'oss:ciGroup:1' or 'oss:firefoxSmoke' +def JOB_PARTS = params.CI_GROUP.split(':') +def IS_XPACK = JOB_PARTS[0] == 'xpack' +def JOB = JOB_PARTS[1] +def CI_GROUP = JOB_PARTS[2] ?: '' + +def worker = getWorkerFromParams(IS_XPACK, JOB, CI_GROUP) + +def workerFailures = [] + +currentBuild.displayName += trunc(" ${params.GITHUB_OWNER}:${params.branch_specifier}", 24) +currentBuild.description = "${params.CI_GROUP}
Executions: ${params.NUMBER_EXECUTIONS}" + +// Note: If you increase agent count, it will execute NUMBER_EXECUTIONS per agent. It will not divide them up amongst the agents +// e.g. NUMBER_EXECUTIONS = 25, agentCount = 4 results in 100 total executions +def agentCount = 1 + +stage("Kibana Pipeline") { + timeout(time: 180, unit: 'MINUTES') { + timestamps { + ansiColor('xterm') { + def agents = [:] + for(def agentNumber = 1; agentNumber <= agentCount; agentNumber++) { + agents["agent-${agentNumber}"] = { + catchError { + kibanaPipeline.withWorkers('flaky-test-runner', { + if (!IS_XPACK) { + kibanaPipeline.buildOss() + if (CI_GROUP == '1') { + runbld "./test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh" + } + } else { + kibanaPipeline.buildXpack() + } + }, getWorkerMap(agentNumber, params.NUMBER_EXECUTIONS.toInteger(), worker, workerFailures))() + } + } + } + + parallel(agents) + + currentBuild.description += ", Failures: ${workerFailures.size()}" + + if (workerFailures.size() > 0) { + print "There were ${workerFailures.size()} test suite failures." + print "The executions that failed were:" + print workerFailures.join("\n") + print "Please check 'Test Result' and 'Pipeline Steps' pages for more info" + } + } + } + } +} + +def getWorkerFromParams(isXpack, job, ciGroup) { + if (!isXpack) { + if (job == 'firefoxSmoke') { + return kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }) + } else if(job == 'visualRegression') { + return kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }) + } else { + return kibanaPipeline.getOssCiGroupWorker(ciGroup) + } + } + + if (job == 'firefoxSmoke') { + return kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }) + } else if(job == 'visualRegression') { + return kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }) + } else { + return kibanaPipeline.getXpackCiGroupWorker(ciGroup) + } +} + +def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWorkers = 14) { + def workerMap = [:] + def numberOfWorkers = Math.min(numberOfExecutions, maxWorkers) + + for(def i = 1; i <= numberOfWorkers; i++) { + def workerExecutions = numberOfExecutions/numberOfWorkers + (i <= numberOfExecutions%numberOfWorkers ? 1 : 0) + + workerMap["agent-${agentNumber}-worker-${i}"] = { workerNumber -> + for(def j = 0; j < workerExecutions; j++) { + print "Execute agent-${agentNumber} worker-${workerNumber}: ${j}" + withEnv(["JOB=agent-${agentNumber}-worker-${workerNumber}-${j}"]) { + catchError { + try { + worker(workerNumber) + } catch (ex) { + workerFailures << "agent-${agentNumber} worker-${workerNumber}-${j}" + throw ex + } + } + } + } + } + } + + return workerMap +} + +def trunc(str, length) { + if (str.size() >= length) { + return str.take(length) + "..." + } + + return str; +} diff --git a/Jenkinsfile b/Jenkinsfile index ec0bc2327321a..b4ae41b9431dc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,7 @@ #!/bin/groovy library 'kibana-pipeline-library' +kibanaLibrary.load() stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a little bit timeout(time: 180, unit: 'MINUTES') { @@ -8,288 +9,43 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a ansiColor('xterm') { catchError { parallel([ - 'kibana-intake-agent': legacyJobRunner('kibana-intake'), - 'x-pack-intake-agent': legacyJobRunner('x-pack-intake'), - 'kibana-oss-agent': withWorkers('kibana-oss-tests', { buildOss() }, [ - 'oss-ciGroup1': getOssCiGroupWorker(1), - 'oss-ciGroup2': getOssCiGroupWorker(2), - 'oss-ciGroup3': getOssCiGroupWorker(3), - 'oss-ciGroup4': getOssCiGroupWorker(4), - 'oss-ciGroup5': getOssCiGroupWorker(5), - 'oss-ciGroup6': getOssCiGroupWorker(6), - 'oss-ciGroup7': getOssCiGroupWorker(7), - 'oss-ciGroup8': getOssCiGroupWorker(8), - 'oss-ciGroup9': getOssCiGroupWorker(9), - 'oss-ciGroup10': getOssCiGroupWorker(10), - 'oss-ciGroup11': getOssCiGroupWorker(11), - 'oss-ciGroup12': getOssCiGroupWorker(12), - 'oss-firefoxSmoke': getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), - // 'oss-visualRegression': getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), + 'kibana-intake-agent': kibanaPipeline.legacyJobRunner('kibana-intake'), + 'x-pack-intake-agent': kibanaPipeline.legacyJobRunner('x-pack-intake'), + 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ + 'oss-ciGroup1': kibanaPipeline.getOssCiGroupWorker(1), + 'oss-ciGroup2': kibanaPipeline.getOssCiGroupWorker(2), + 'oss-ciGroup3': kibanaPipeline.getOssCiGroupWorker(3), + 'oss-ciGroup4': kibanaPipeline.getOssCiGroupWorker(4), + 'oss-ciGroup5': kibanaPipeline.getOssCiGroupWorker(5), + 'oss-ciGroup6': kibanaPipeline.getOssCiGroupWorker(6), + 'oss-ciGroup7': kibanaPipeline.getOssCiGroupWorker(7), + 'oss-ciGroup8': kibanaPipeline.getOssCiGroupWorker(8), + 'oss-ciGroup9': kibanaPipeline.getOssCiGroupWorker(9), + 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), + 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), + 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), + 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), + // 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), ]), - 'kibana-xpack-agent': withWorkers('kibana-xpack-tests', { buildXpack() }, [ - 'xpack-ciGroup1': getXpackCiGroupWorker(1), - 'xpack-ciGroup2': getXpackCiGroupWorker(2), - 'xpack-ciGroup3': getXpackCiGroupWorker(3), - 'xpack-ciGroup4': getXpackCiGroupWorker(4), - 'xpack-ciGroup5': getXpackCiGroupWorker(5), - 'xpack-ciGroup6': getXpackCiGroupWorker(6), - 'xpack-ciGroup7': getXpackCiGroupWorker(7), - 'xpack-ciGroup8': getXpackCiGroupWorker(8), - 'xpack-ciGroup9': getXpackCiGroupWorker(9), - 'xpack-ciGroup10': getXpackCiGroupWorker(10), - 'xpack-firefoxSmoke': getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), - // 'xpack-visualRegression': getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), + 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), + 'xpack-ciGroup2': kibanaPipeline.getXpackCiGroupWorker(2), + 'xpack-ciGroup3': kibanaPipeline.getXpackCiGroupWorker(3), + 'xpack-ciGroup4': kibanaPipeline.getXpackCiGroupWorker(4), + 'xpack-ciGroup5': kibanaPipeline.getXpackCiGroupWorker(5), + 'xpack-ciGroup6': kibanaPipeline.getXpackCiGroupWorker(6), + 'xpack-ciGroup7': kibanaPipeline.getXpackCiGroupWorker(7), + 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), + 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), + 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), + 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), + // 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), ]), ]) } - node('flyweight') { - // If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success - // The e-mail plugin for the infra e-mail depends upon this being set - currentBuild.result = currentBuild.result ?: 'SUCCESS' - - sendMail() - } + kibanaPipeline.sendMail() } } } } -def withWorkers(name, preWorkerClosure = {}, workerClosures = [:]) { - return { - jobRunner('tests-xl') { - try { - doSetup() - preWorkerClosure() - - def nextWorker = 1 - def worker = { workerClosure -> - def workerNumber = nextWorker - nextWorker++ - - return { - workerClosure(workerNumber) - } - } - - def workers = [:] - workerClosures.each { workerName, workerClosure -> - workers[workerName] = worker(workerClosure) - } - - parallel(workers) - } finally { - catchError { - uploadAllGcsArtifacts(name) - } - - catchError { - runbldJunit() - } - - catchError { - publishJunit() - } - - catchError { - runErrorReporter() - } - } - } - } -} - -def getPostBuildWorker(name, closure) { - return { workerNumber -> - def kibanaPort = "61${workerNumber}1" - def esPort = "61${workerNumber}2" - def esTransportPort = "61${workerNumber}3" - - withEnv([ - "CI_WORKER_NUMBER=${workerNumber}", - "TEST_KIBANA_HOST=localhost", - "TEST_KIBANA_PORT=${kibanaPort}", - "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}", - "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}", - "TEST_ES_TRANSPORT_PORT=${esTransportPort}", - "IS_PIPELINE_JOB=1", - ]) { - closure() - } - } -} - -def getOssCiGroupWorker(ciGroup) { - return getPostBuildWorker("ciGroup" + ciGroup, { - withEnv([ - "CI_GROUP=${ciGroup}", - "JOB=kibana-ciGroup${ciGroup}", - ]) { - runbld "./test/scripts/jenkins_ci_group.sh" - } - }) -} - -def getXpackCiGroupWorker(ciGroup) { - return getPostBuildWorker("xpack-ciGroup" + ciGroup, { - withEnv([ - "CI_GROUP=${ciGroup}", - "JOB=xpack-kibana-ciGroup${ciGroup}", - ]) { - runbld "./test/scripts/jenkins_xpack_ci_group.sh" - } - }) -} - -def legacyJobRunner(name) { - return { - parallel([ - "${name}": { - withEnv([ - "JOB=${name}", - ]) { - jobRunner('linux && immutable') { - try { - runbld('.ci/run.sh', true) - } finally { - catchError { - uploadAllGcsArtifacts(name) - } - catchError { - publishJunit() - } - catchError { - runErrorReporter() - } - } - } - } - } - ]) - } -} - -def jobRunner(label, closure) { - node(label) { - def scmVars = checkout scm - - withEnv([ - "CI=true", - "HOME=${env.JENKINS_HOME}", - "PR_SOURCE_BRANCH=${env.ghprbSourceBranch ?: ''}", - "PR_TARGET_BRANCH=${env.ghprbTargetBranch ?: ''}", - "PR_AUTHOR=${env.ghprbPullAuthorLogin ?: ''}", - "TEST_BROWSER_HEADLESS=1", - "GIT_BRANCH=${scmVars.GIT_BRANCH}", - ]) { - withCredentials([ - string(credentialsId: 'vault-addr', variable: 'VAULT_ADDR'), - string(credentialsId: 'vault-role-id', variable: 'VAULT_ROLE_ID'), - string(credentialsId: 'vault-secret-id', variable: 'VAULT_SECRET_ID'), - ]) { - // scm is configured to check out to the ./kibana directory - dir('kibana') { - closure() - } - } - } - } -} - -// TODO what should happen if GCS, Junit, or email publishing fails? Unstable build? Failed build? - -def uploadGcsArtifact(workerName, pattern) { - def storageLocation = "gs://kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" // TODO - // def storageLocation = "gs://kibana-pipeline-testing/jobs/pipeline-test/${BUILD_NUMBER}/${workerName}" - - googleStorageUpload( - credentialsId: 'kibana-ci-gcs-plugin', - bucket: storageLocation, - pattern: pattern, - sharedPublicly: true, - showInline: true, - ) -} - -def uploadAllGcsArtifacts(workerName) { - def ARTIFACT_PATTERNS = [ - 'target/kibana-*', - 'target/junit/**/*', - 'test/**/screenshots/**/*.png', - 'test/functional/failure_debug/html/*.html', - 'x-pack/test/**/screenshots/**/*.png', - 'x-pack/test/functional/failure_debug/html/*.html', - 'x-pack/test/functional/apps/reporting/reports/session/*.pdf', - ] - - ARTIFACT_PATTERNS.each { pattern -> - uploadGcsArtifact(workerName, pattern) - } -} - -def publishJunit() { - junit(testResults: 'target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true) -} - -def sendMail() { - sendInfraMail() - sendKibanaMail() -} - -def sendInfraMail() { - catchError { - step([ - $class: 'Mailer', - notifyEveryUnstableBuild: true, - recipients: 'infra-root+build@elastic.co', - sendToIndividuals: false - ]) - } -} - -def sendKibanaMail() { - catchError { - def buildStatus = buildUtils.getBuildStatus() - - if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { - emailext( - to: 'build-kibana@elastic.co', - subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}", - body: '${SCRIPT,template="groovy-html.template"}', - mimeType: 'text/html', - ) - } - } -} - -def runbld(script, enableJunitProcessing = false) { - def extraConfig = enableJunitProcessing ? "" : "--config ${env.WORKSPACE}/kibana/.ci/runbld_no_junit.yml" - - sh "/usr/local/bin/runbld -d '${pwd()}' ${extraConfig} ${script}" -} - -def runbldJunit() { - sh "/usr/local/bin/runbld -d '${pwd()}' ${env.WORKSPACE}/kibana/test/scripts/jenkins_runbld_junit.sh" -} - -def bash(script) { - sh "#!/bin/bash\n${script}" -} - -def doSetup() { - runbld "./test/scripts/jenkins_setup.sh" -} - -def buildOss() { - runbld "./test/scripts/jenkins_build_kibana.sh" -} - -def buildXpack() { - runbld "./test/scripts/jenkins_xpack_build_kibana.sh" -} - -def runErrorReporter() { - bash """ - source src/dev/ci_setup/setup_env.sh - node src/dev/failed_tests/cli - """ -} diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index aa0a5e62ee937..a79fca180ce51 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -2,6 +2,10 @@ set -e +if [[ "$CI_ENV_SETUP" ]]; then + return 0 +fi + installNode=$1 dir="$(pwd)" @@ -152,3 +156,5 @@ if [[ -d "$ES_DIR" && -f "$ES_JAVA_PROP_PATH" ]]; then echo "Setting JAVA_HOME=$HOME/.java/$ES_BUILD_JAVA" export JAVA_HOME=$HOME/.java/$ES_BUILD_JAVA fi + +export CI_ENV_SETUP=true diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index b50b00f54350b..c50b56f98a789 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -43,8 +43,9 @@ export const IGNORE_FILE_GLOBS = [ 'x-pack/docs/**/*', 'src/legacy/ui/public/assets/fonts/**/*', 'packages/kbn-utility-types/test-d/**/*', - 'Jenkinsfile', + '**/Jenkinsfile*', 'Dockerfile*', + 'vars/*', // Files in this directory must match a pre-determined name in some cases. 'x-pack/legacy/plugins/canvas/.storybook/*', diff --git a/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh b/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh new file mode 100755 index 0000000000000..4b16e3b32fefd --- /dev/null +++ b/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source src/dev/ci_setup/setup_env.sh + +cd test/plugin_functional/plugins/kbn_tp_sample_panel_action; +if [[ ! -d "target" ]]; then + checks-reporter-with-killswitch "Build kbn_tp_sample_panel_action" yarn build; +fi +cd -; diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh index 6807e318138ce..42699c4a84e1b 100755 --- a/test/scripts/jenkins_ci_group.sh +++ b/test/scripts/jenkins_ci_group.sh @@ -24,10 +24,7 @@ fi checks-reporter-with-killswitch "Functional tests / Group ${CI_GROUP}" yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}"; if [ "$CI_GROUP" == "1" ]; then - # build kbn_tp_sample_panel_action - cd test/plugin_functional/plugins/kbn_tp_sample_panel_action; - checks-reporter-with-killswitch "Build kbn_tp_sample_panel_action" yarn build; - cd -; + source test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh yarn run grunt run:pluginFunctionalTestsRelease --from=source; yarn run grunt run:interpreterFunctionalTestsRelease; fi diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy new file mode 100644 index 0000000000000..d74f7ad31c5c4 --- /dev/null +++ b/vars/kibanaPipeline.groovy @@ -0,0 +1,237 @@ +def withWorkers(name, preWorkerClosure = {}, workerClosures = [:]) { + return { + jobRunner('tests-xl') { + try { + doSetup() + preWorkerClosure() + + def nextWorker = 1 + def worker = { workerClosure -> + def workerNumber = nextWorker + nextWorker++ + + return { + workerClosure(workerNumber) + } + } + + def workers = [:] + workerClosures.each { workerName, workerClosure -> + workers[workerName] = worker(workerClosure) + } + + parallel(workers) + } finally { + catchError { + uploadAllGcsArtifacts(name) + } + + catchError { + runbld.junit() + } + + catchError { + publishJunit() + } + + catchError { + runErrorReporter() + } + } + } + } +} + +def getPostBuildWorker(name, closure) { + return { workerNumber -> + def kibanaPort = "61${workerNumber}1" + def esPort = "61${workerNumber}2" + def esTransportPort = "61${workerNumber}3" + + withEnv([ + "CI_WORKER_NUMBER=${workerNumber}", + "TEST_KIBANA_HOST=localhost", + "TEST_KIBANA_PORT=${kibanaPort}", + "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}", + "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}", + "TEST_ES_TRANSPORT_PORT=${esTransportPort}", + "IS_PIPELINE_JOB=1", + ]) { + closure() + } + } +} + +def getOssCiGroupWorker(ciGroup) { + return getPostBuildWorker("ciGroup" + ciGroup, { + withEnv([ + "CI_GROUP=${ciGroup}", + "JOB=kibana-ciGroup${ciGroup}", + ]) { + runbld "./test/scripts/jenkins_ci_group.sh" + } + }) +} + +def getXpackCiGroupWorker(ciGroup) { + return getPostBuildWorker("xpack-ciGroup" + ciGroup, { + withEnv([ + "CI_GROUP=${ciGroup}", + "JOB=xpack-kibana-ciGroup${ciGroup}", + ]) { + runbld "./test/scripts/jenkins_xpack_ci_group.sh" + } + }) +} + +def legacyJobRunner(name) { + return { + parallel([ + "${name}": { + withEnv([ + "JOB=${name}", + ]) { + jobRunner('linux && immutable') { + try { + runbld('.ci/run.sh', true) + } finally { + catchError { + uploadAllGcsArtifacts(name) + } + catchError { + publishJunit() + } + catchError { + runErrorReporter() + } + } + } + } + } + ]) + } +} + +def jobRunner(label, closure) { + node(label) { + def scmVars = checkout scm + + withEnv([ + "CI=true", + "HOME=${env.JENKINS_HOME}", + "PR_SOURCE_BRANCH=${env.ghprbSourceBranch ?: ''}", + "PR_TARGET_BRANCH=${env.ghprbTargetBranch ?: ''}", + "PR_AUTHOR=${env.ghprbPullAuthorLogin ?: ''}", + "TEST_BROWSER_HEADLESS=1", + "GIT_BRANCH=${scmVars.GIT_BRANCH}", + ]) { + withCredentials([ + string(credentialsId: 'vault-addr', variable: 'VAULT_ADDR'), + string(credentialsId: 'vault-role-id', variable: 'VAULT_ROLE_ID'), + string(credentialsId: 'vault-secret-id', variable: 'VAULT_SECRET_ID'), + ]) { + // scm is configured to check out to the ./kibana directory + dir('kibana') { + closure() + } + } + } + } +} + +// TODO what should happen if GCS, Junit, or email publishing fails? Unstable build? Failed build? + +def uploadGcsArtifact(workerName, pattern) { + def storageLocation = "gs://kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" // TODO + + googleStorageUpload( + credentialsId: 'kibana-ci-gcs-plugin', + bucket: storageLocation, + pattern: pattern, + sharedPublicly: true, + showInline: true, + ) +} + +def uploadAllGcsArtifacts(workerName) { + def ARTIFACT_PATTERNS = [ + 'target/kibana-*', + 'target/junit/**/*', + 'test/**/screenshots/**/*.png', + 'test/functional/failure_debug/html/*.html', + 'x-pack/test/**/screenshots/**/*.png', + 'x-pack/test/functional/failure_debug/html/*.html', + 'x-pack/test/functional/apps/reporting/reports/session/*.pdf', + ] + + ARTIFACT_PATTERNS.each { pattern -> + uploadGcsArtifact(workerName, pattern) + } +} + +def publishJunit() { + junit(testResults: 'target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true) +} + +def sendMail() { + // If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success + // The e-mail plugin for the infra e-mail depends upon this being set + currentBuild.result = currentBuild.result ?: 'SUCCESS' + + def buildStatus = buildUtils.getBuildStatus() + if (buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { + node('flyweight') { + sendInfraMail() + sendKibanaMail() + } + } +} + +def sendInfraMail() { + catchError { + step([ + $class: 'Mailer', + notifyEveryUnstableBuild: true, + recipients: 'infra-root+build@elastic.co', + sendToIndividuals: false + ]) + } +} + +def sendKibanaMail() { + catchError { + if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { + emailext( + to: 'build-kibana@elastic.co', + subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}", + body: '${SCRIPT,template="groovy-html.template"}', + mimeType: 'text/html', + ) + } + } +} + +def bash(script) { + sh "#!/bin/bash\n${script}" +} + +def doSetup() { + runbld "./test/scripts/jenkins_setup.sh" +} + +def buildOss() { + runbld "./test/scripts/jenkins_build_kibana.sh" +} + +def buildXpack() { + runbld "./test/scripts/jenkins_xpack_build_kibana.sh" +} + +def runErrorReporter() { + bash """ + source src/dev/ci_setup/setup_env.sh + node src/dev/failed_tests/cli + """ +} + +return this diff --git a/vars/runbld.groovy b/vars/runbld.groovy new file mode 100644 index 0000000000000..501e2421ca65b --- /dev/null +++ b/vars/runbld.groovy @@ -0,0 +1,11 @@ +def call(script, enableJunitProcessing = false) { + def extraConfig = enableJunitProcessing ? "" : "--config ${env.WORKSPACE}/kibana/.ci/runbld_no_junit.yml" + + sh "/usr/local/bin/runbld -d '${pwd()}' ${extraConfig} ${script}" +} + +def junit() { + sh "/usr/local/bin/runbld -d '${pwd()}' ${env.WORKSPACE}/kibana/test/scripts/jenkins_runbld_junit.sh" +} + +return this From 3a5c06858a2f6a5d001a1636291a5ba3e1e09826 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 4 Oct 2019 20:41:24 +0200 Subject: [PATCH 15/33] Expose injectMetadata service temporarily (#47319) * expose injectMetadata until config service provided * add functional test * update types * fix tests. injectMetadata is a part of plugin contract * add comment about injectedVars API --- ...bana-plugin-public.appmountcontext.core.md | 3 +++ .../kibana-plugin-public.appmountcontext.md | 2 +- ...lugin-public.coresetup.injectedmetadata.md | 19 +++++++++++++++ .../public/kibana-plugin-public.coresetup.md | 1 + ...lugin-public.corestart.injectedmetadata.md | 19 +++++++++++++++ .../public/kibana-plugin-public.corestart.md | 1 + src/core/public/application/types.ts | 9 +++++++ src/core/public/core_system.ts | 1 + src/core/public/index.ts | 18 ++++++++++++++ src/core/public/mocks.ts | 7 ++++++ src/core/public/plugins/plugin_context.ts | 6 +++++ .../public/plugins/plugins_service.test.ts | 10 ++++---- src/core/public/public.api.md | 11 +++++++++ .../plugins/core_plugin_b/public/plugin.tsx | 2 ++ .../test_suites/core_plugins/ui_plugins.js | 24 ++++++++++++++----- 15 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-public.coresetup.injectedmetadata.md create mode 100644 docs/development/core/public/kibana-plugin-public.corestart.injectedmetadata.md diff --git a/docs/development/core/public/kibana-plugin-public.appmountcontext.core.md b/docs/development/core/public/kibana-plugin-public.appmountcontext.core.md index 63b3ead814f00..f4dee0f29af34 100644 --- a/docs/development/core/public/kibana-plugin-public.appmountcontext.core.md +++ b/docs/development/core/public/kibana-plugin-public.appmountcontext.core.md @@ -18,5 +18,8 @@ core: { notifications: NotificationsStart; overlays: OverlayStart; uiSettings: UiSettingsClientContract; + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; }; ``` diff --git a/docs/development/core/public/kibana-plugin-public.appmountcontext.md b/docs/development/core/public/kibana-plugin-public.appmountcontext.md index c6541e3eca392..97d143d518f60 100644 --- a/docs/development/core/public/kibana-plugin-public.appmountcontext.md +++ b/docs/development/core/public/kibana-plugin-public.appmountcontext.md @@ -16,5 +16,5 @@ export interface AppMountContext | Property | Type | Description | | --- | --- | --- | -| [core](./kibana-plugin-public.appmountcontext.core.md) | {
application: Pick<ApplicationStart, 'capabilities' | 'navigateToApp'>;
chrome: ChromeStart;
docLinks: DocLinksStart;
http: HttpStart;
i18n: I18nStart;
notifications: NotificationsStart;
overlays: OverlayStart;
uiSettings: UiSettingsClientContract;
} | Core service APIs available to mounted applications. | +| [core](./kibana-plugin-public.appmountcontext.core.md) | {
application: Pick<ApplicationStart, 'capabilities' | 'navigateToApp'>;
chrome: ChromeStart;
docLinks: DocLinksStart;
http: HttpStart;
i18n: I18nStart;
notifications: NotificationsStart;
overlays: OverlayStart;
uiSettings: UiSettingsClientContract;
injectedMetadata: {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
};
} | Core service APIs available to mounted applications. | diff --git a/docs/development/core/public/kibana-plugin-public.coresetup.injectedmetadata.md b/docs/development/core/public/kibana-plugin-public.coresetup.injectedmetadata.md new file mode 100644 index 0000000000000..f9c1a283e3808 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.coresetup.injectedmetadata.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [CoreSetup](./kibana-plugin-public.coresetup.md) > [injectedMetadata](./kibana-plugin-public.coresetup.injectedmetadata.md) + +## CoreSetup.injectedMetadata property + +> Warning: This API is now obsolete. +> +> + +exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. + +Signature: + +```typescript +injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; +``` diff --git a/docs/development/core/public/kibana-plugin-public.coresetup.md b/docs/development/core/public/kibana-plugin-public.coresetup.md index 9b94e2db52831..f9335425fed4c 100644 --- a/docs/development/core/public/kibana-plugin-public.coresetup.md +++ b/docs/development/core/public/kibana-plugin-public.coresetup.md @@ -20,6 +20,7 @@ export interface CoreSetup | [context](./kibana-plugin-public.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-public.contextsetup.md) | | [fatalErrors](./kibana-plugin-public.coresetup.fatalerrors.md) | FatalErrorsSetup | [FatalErrorsSetup](./kibana-plugin-public.fatalerrorssetup.md) | | [http](./kibana-plugin-public.coresetup.http.md) | HttpSetup | [HttpSetup](./kibana-plugin-public.httpsetup.md) | +| [injectedMetadata](./kibana-plugin-public.coresetup.injectedmetadata.md) | {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
} | exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. | | [notifications](./kibana-plugin-public.coresetup.notifications.md) | NotificationsSetup | [NotificationsSetup](./kibana-plugin-public.notificationssetup.md) | | [uiSettings](./kibana-plugin-public.coresetup.uisettings.md) | UiSettingsClientContract | [UiSettingsClient](./kibana-plugin-public.uisettingsclient.md) | diff --git a/docs/development/core/public/kibana-plugin-public.corestart.injectedmetadata.md b/docs/development/core/public/kibana-plugin-public.corestart.injectedmetadata.md new file mode 100644 index 0000000000000..9224b97bc4300 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.corestart.injectedmetadata.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [CoreStart](./kibana-plugin-public.corestart.md) > [injectedMetadata](./kibana-plugin-public.corestart.injectedmetadata.md) + +## CoreStart.injectedMetadata property + +> Warning: This API is now obsolete. +> +> + +exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. + +Signature: + +```typescript +injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; +``` diff --git a/docs/development/core/public/kibana-plugin-public.corestart.md b/docs/development/core/public/kibana-plugin-public.corestart.md index 5c1626958c4df..47eba78bf43e4 100644 --- a/docs/development/core/public/kibana-plugin-public.corestart.md +++ b/docs/development/core/public/kibana-plugin-public.corestart.md @@ -21,6 +21,7 @@ export interface CoreStart | [docLinks](./kibana-plugin-public.corestart.doclinks.md) | DocLinksStart | [DocLinksStart](./kibana-plugin-public.doclinksstart.md) | | [http](./kibana-plugin-public.corestart.http.md) | HttpStart | [HttpStart](./kibana-plugin-public.httpstart.md) | | [i18n](./kibana-plugin-public.corestart.i18n.md) | I18nStart | [I18nStart](./kibana-plugin-public.i18nstart.md) | +| [injectedMetadata](./kibana-plugin-public.corestart.injectedmetadata.md) | {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
} | exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. | | [notifications](./kibana-plugin-public.corestart.notifications.md) | NotificationsStart | [NotificationsStart](./kibana-plugin-public.notificationsstart.md) | | [overlays](./kibana-plugin-public.corestart.overlays.md) | OverlayStart | [OverlayStart](./kibana-plugin-public.overlaystart.md) | | [savedObjects](./kibana-plugin-public.corestart.savedobjects.md) | SavedObjectsStart | [SavedObjectsStart](./kibana-plugin-public.savedobjectsstart.md) | diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index b2d0aff26b8b0..5b1d4affe8840 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -114,6 +114,15 @@ export interface AppMountContext { overlays: OverlayStart; /** {@link UiSettingsClient} */ uiSettings: UiSettingsClientContract; + /** + * exposed temporarily until https://github.com/elastic/kibana/issues/41990 done + * use *only* to retrieve config values. There is no way to set injected values + * in the new platform. Use the legacy platform API instead. + * @deprecated + * */ + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; }; } diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 7b9ed50f09591..7a87f97208a7a 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -236,6 +236,7 @@ export class CoreSystem { notifications, overlays, uiSettings, + injectedMetadata: pick(injectedMetadata, ['getInjectedVar']), })); const core: InternalCoreStart = { diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 1e2dfde7496ea..9640f6f510c45 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -145,6 +145,15 @@ export interface CoreSetup { notifications: NotificationsSetup; /** {@link UiSettingsClient} */ uiSettings: UiSettingsClientContract; + /** + * exposed temporarily until https://github.com/elastic/kibana/issues/41990 done + * use *only* to retrieve config values. There is no way to set injected values + * in the new platform. Use the legacy platform API instead. + * @deprecated + * */ + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; } /** @@ -175,6 +184,15 @@ export interface CoreStart { overlays: OverlayStart; /** {@link UiSettingsClient} */ uiSettings: UiSettingsClientContract; + /** + * exposed temporarily until https://github.com/elastic/kibana/issues/41990 done + * use *only* to retrieve config values. There is no way to set injected values + * in the new platform. Use the legacy platform API instead. + * @deprecated + * */ + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; } /** diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 7c99f69d6fd7a..8ce163fd59e14 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -28,6 +28,7 @@ import { overlayServiceMock } from './overlays/overlay_service.mock'; import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { savedObjectsMock } from './saved_objects/saved_objects_service.mock'; import { contextServiceMock } from './context/context_service.mock'; +import { injectedMetadataServiceMock } from './injected_metadata/injected_metadata_service.mock'; export { chromeServiceMock } from './chrome/chrome_service.mock'; export { docLinksServiceMock } from './doc_links/doc_links_service.mock'; @@ -48,6 +49,9 @@ function createCoreSetupMock() { http: httpServiceMock.createSetupContract(), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), + injectedMetadata: { + getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar, + }, }; return mock; @@ -64,6 +68,9 @@ function createCoreStartMock() { overlays: overlayServiceMock.createStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(), savedObjects: savedObjectsMock.createStartContract(), + injectedMetadata: { + getInjectedVar: injectedMetadataServiceMock.createStartContract().getInjectedVar, + }, }; return mock; diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index f4e25d27447bc..51bd118d280e3 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -86,6 +86,9 @@ export function createPluginSetupContext< http: deps.http, notifications: deps.notifications, uiSettings: deps.uiSettings, + injectedMetadata: { + getInjectedVar: deps.injectedMetadata.getInjectedVar, + }, }; } @@ -125,5 +128,8 @@ export function createPluginStartContext< overlays: deps.overlays, uiSettings: deps.uiSettings, savedObjects: deps.savedObjects, + injectedMetadata: { + getInjectedVar: deps.injectedMetadata.getInjectedVar, + }, }; } diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index d6411554e5f85..358bf71ac9188 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { omit } from 'lodash'; +import { omit, pick } from 'lodash'; import { MockedPluginInitializer, @@ -76,12 +76,12 @@ beforeEach(() => { context: contextServiceMock.createSetupContract(), fatalErrors: fatalErrorsServiceMock.createSetupContract(), http: httpServiceMock.createSetupContract(), - injectedMetadata: injectedMetadataServiceMock.createSetupContract(), + injectedMetadata: pick(injectedMetadataServiceMock.createStartContract(), 'getInjectedVar'), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), }; mockSetupContext = { - ...omit(mockSetupDeps, 'injectedMetadata'), + ...mockSetupDeps, application: expect.any(Object), }; mockStartDeps = { @@ -90,14 +90,14 @@ beforeEach(() => { http: httpServiceMock.createStartContract(), chrome: chromeServiceMock.createStartContract(), i18n: i18nServiceMock.createStartContract(), - injectedMetadata: injectedMetadataServiceMock.createStartContract(), + injectedMetadata: pick(injectedMetadataServiceMock.createStartContract(), 'getInjectedVar'), notifications: notificationServiceMock.createStartContract(), overlays: overlayServiceMock.createStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(), savedObjects: savedObjectsMock.createStartContract(), }; mockStartContext = { - ...omit(mockStartDeps, 'injectedMetadata'), + ...mockStartDeps, application: expect.any(Object), chrome: omit(mockStartDeps.chrome, 'getComponent'), }; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index e6c8f116e5782..0156cbaebb949 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -59,6 +59,9 @@ export interface AppMountContext { notifications: NotificationsStart; overlays: OverlayStart; uiSettings: UiSettingsClientContract; + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; }; } @@ -235,6 +238,10 @@ export interface CoreSetup { fatalErrors: FatalErrorsSetup; // (undocumented) http: HttpSetup; + // @deprecated + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; // (undocumented) notifications: NotificationsSetup; // (undocumented) @@ -253,6 +260,10 @@ export interface CoreStart { http: HttpStart; // (undocumented) i18n: I18nStart; + // @deprecated + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + }; // (undocumented) notifications: NotificationsStart; // (undocumented) diff --git a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx index f9285fbe4eaaf..627fd05404b24 100644 --- a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx +++ b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx @@ -23,6 +23,7 @@ import { CorePluginAPluginSetup } from '../../core_plugin_a/public/plugin'; declare global { interface Window { corePluginB?: string; + hasAccessToInjectedMetadata?: boolean; } } @@ -34,6 +35,7 @@ export class CorePluginBPlugin implements Plugin { public setup(core: CoreSetup, deps: CorePluginBDeps) { window.corePluginB = `Plugin A said: ${deps.core_plugin_a.getGreeting()}`; + window.hasAccessToInjectedMetadata = 'getInjectedVar' in core.injectedMetadata; core.application.register({ id: 'bar', diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.js b/test/plugin_functional/test_suites/core_plugins/ui_plugins.js index 3b46b60368bb1..df855f243d403 100644 --- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.js +++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.js @@ -23,14 +23,26 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common']); const browser = getService('browser'); - describe('ui plugin loading', function describeIndexTests() { - before(async () => { - await PageObjects.common.navigateToApp('settings'); + describe('ui plugins', function () { + describe('loading', function describeIndexTests() { + before(async () => { + await PageObjects.common.navigateToApp('settings'); + }); + + it('should attach string to window.corePluginB', async () => { + const corePluginB = await browser.execute('return window.corePluginB'); + expect(corePluginB).to.equal(`Plugin A said: Hello from Plugin A!`); + }); }); + describe('have injectedMetadata service provided', function describeIndexTests() { + before(async () => { + await PageObjects.common.navigateToApp('bar'); + }); - it('should attach string to window.corePluginB', async () => { - const corePluginB = await browser.execute('return window.corePluginB'); - expect(corePluginB).to.equal(`Plugin A said: Hello from Plugin A!`); + it('should attach string to window.corePluginB', async () => { + const hasAccessToInjectedMetadata = await browser.execute('return window.hasAccessToInjectedMetadata'); + expect(hasAccessToInjectedMetadata).to.equal(true); + }); }); }); } From 9c85bd364dbf2eb7881ed060dc0b823f793f8d14 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Fri, 4 Oct 2019 13:43:28 -0500 Subject: [PATCH 16/33] [DOCS] Specified default settings for server.rewriteBasePath (#45305) * [DOCS] Specified default settings for server.rewriteBasePath * Doing some version switching --- docs/setup/settings.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index cae44c6fc4575..091714cdd15e4 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -271,9 +271,9 @@ identifies this Kibana instance. `server.port:`:: *Default: 5601* Kibana is served by a back end server. This setting specifies the port to use. -`server.rewriteBasePath:`:: *Default: false* Deprecated setting that specifies if Kibana should -rewrite requests that are prefixed with `server.basePath`, or require that they -are rewritten by your reverse proxy. +`server.rewriteBasePath:`:: *Default: false* Specifies whether Kibana should +rewrite requests that are prefixed with `server.basePath` or require that they +are rewritten by your reverse proxy. `server.socketTimeout:`:: *Default: "120000"* The number of milliseconds to wait before closing an inactive socket. From 3e9b0c81b2b4738deed24c767b0040fe6225a36c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 4 Oct 2019 12:49:11 -0600 Subject: [PATCH 17/33] SearchSource: fix docvalue_fields and fields intersection logic (#46724) * SearchSource: fix docvalue_fields and fields intersection logic * update filter logic to handle docvalue_fields that are just strings --- .../search_source/filter_docvalue_fields.js | 25 ++++++++++++++ .../filter_docvalue_fields.test.js | 33 +++++++++++++++++++ .../courier/search_source/search_source.js | 10 +++--- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/legacy/ui/public/courier/search_source/filter_docvalue_fields.js create mode 100644 src/legacy/ui/public/courier/search_source/filter_docvalue_fields.test.js diff --git a/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.js b/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.js new file mode 100644 index 0000000000000..cd726709b4b5c --- /dev/null +++ b/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.js @@ -0,0 +1,25 @@ +/* + * 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 function filterDocvalueFields(docvalueFields, fields) { + return docvalueFields.filter(docValue => { + const docvalueFieldName = typeof docValue === 'string' ? docValue : docValue.field; + return fields.includes(docvalueFieldName); + }); +} diff --git a/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.test.js b/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.test.js new file mode 100644 index 0000000000000..b220361e33b3b --- /dev/null +++ b/src/legacy/ui/public/courier/search_source/filter_docvalue_fields.test.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. + */ + +import { filterDocvalueFields } from './filter_docvalue_fields'; + +test('Should exclude docvalue_fields that are not contained in fields', () => { + const docvalueFields = [ + 'my_ip_field', + { field: 'my_keyword_field' }, + { field: 'my_date_field', 'format': 'epoch_millis' } + ]; + const out = filterDocvalueFields(docvalueFields, ['my_ip_field', 'my_keyword_field']); + expect(out).toEqual([ + 'my_ip_field', + { field: 'my_keyword_field' }, + ]); +}); diff --git a/src/legacy/ui/public/courier/search_source/search_source.js b/src/legacy/ui/public/courier/search_source/search_source.js index ceb10861dc9a0..afa42a7d7c015 100644 --- a/src/legacy/ui/public/courier/search_source/search_source.js +++ b/src/legacy/ui/public/courier/search_source/search_source.js @@ -81,6 +81,7 @@ import { searchRequestQueue } from '../search_request_queue'; import { FetchSoonProvider } from '../fetch'; import { FieldWildcardProvider } from '../../field_wildcard'; import { getHighlightRequest } from '../../../../../plugins/data/common/field_formats'; +import { filterDocvalueFields } from './filter_docvalue_fields'; const FIELDS = [ 'type', @@ -551,12 +552,13 @@ export function SearchSourceProvider(Promise, Private, config) { flatData.body = flatData.body || {}; const computedFields = flatData.index.getComputedFields(); + flatData.body.stored_fields = computedFields.storedFields; flatData.body.script_fields = flatData.body.script_fields || {}; - flatData.body.docvalue_fields = flatData.body.docvalue_fields || []; - _.extend(flatData.body.script_fields, computedFields.scriptFields); - flatData.body.docvalue_fields = _.union(flatData.body.docvalue_fields, computedFields.docvalueFields); + + const defaultDocValueFields = computedFields.docvalueFields ? computedFields.docvalueFields : []; + flatData.body.docvalue_fields = flatData.body.docvalue_fields || defaultDocValueFields; if (flatData.body._source) { // exclude source fields for this index pattern specified by the user @@ -570,7 +572,7 @@ export function SearchSourceProvider(Promise, Private, config) { const fields = flatData.fields; if (fields) { // filter out the docvalue_fields, and script_fields to only include those that we are concerned with - flatData.body.docvalue_fields = _.intersection(flatData.body.docvalue_fields, fields); + flatData.body.docvalue_fields = filterDocvalueFields(flatData.body.docvalue_fields, fields); flatData.body.script_fields = _.pick(flatData.body.script_fields, fields); // request the remaining fields from both stored_fields and _source From 7d96a13fad5337f262743b51d5bdd6103d2c51a4 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 4 Oct 2019 16:55:20 -0400 Subject: [PATCH 18/33] Revert "Add pipeline for flaky test runner job (#46740)" This reverts commit e89425552b079c24b7ee11f04e3662a04a875e5d. --- .ci/Jenkinsfile_flaky | 113 ------- Jenkinsfile | 308 ++++++++++++++++-- src/dev/ci_setup/setup_env.sh | 6 - src/dev/precommit_hook/casing_check_config.js | 3 +- ...enkins_build_kbn_tp_sample_panel_action.sh | 9 - test/scripts/jenkins_ci_group.sh | 5 +- vars/kibanaPipeline.groovy | 237 -------------- vars/runbld.groovy | 11 - 8 files changed, 281 insertions(+), 411 deletions(-) delete mode 100644 .ci/Jenkinsfile_flaky delete mode 100755 test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh delete mode 100644 vars/kibanaPipeline.groovy delete mode 100644 vars/runbld.groovy diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky deleted file mode 100644 index e484c8c24d790..0000000000000 --- a/.ci/Jenkinsfile_flaky +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/groovy - -library 'kibana-pipeline-library' -kibanaLibrary.load() - -// Looks like 'oss:ciGroup:1' or 'oss:firefoxSmoke' -def JOB_PARTS = params.CI_GROUP.split(':') -def IS_XPACK = JOB_PARTS[0] == 'xpack' -def JOB = JOB_PARTS[1] -def CI_GROUP = JOB_PARTS[2] ?: '' - -def worker = getWorkerFromParams(IS_XPACK, JOB, CI_GROUP) - -def workerFailures = [] - -currentBuild.displayName += trunc(" ${params.GITHUB_OWNER}:${params.branch_specifier}", 24) -currentBuild.description = "${params.CI_GROUP}
Executions: ${params.NUMBER_EXECUTIONS}" - -// Note: If you increase agent count, it will execute NUMBER_EXECUTIONS per agent. It will not divide them up amongst the agents -// e.g. NUMBER_EXECUTIONS = 25, agentCount = 4 results in 100 total executions -def agentCount = 1 - -stage("Kibana Pipeline") { - timeout(time: 180, unit: 'MINUTES') { - timestamps { - ansiColor('xterm') { - def agents = [:] - for(def agentNumber = 1; agentNumber <= agentCount; agentNumber++) { - agents["agent-${agentNumber}"] = { - catchError { - kibanaPipeline.withWorkers('flaky-test-runner', { - if (!IS_XPACK) { - kibanaPipeline.buildOss() - if (CI_GROUP == '1') { - runbld "./test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh" - } - } else { - kibanaPipeline.buildXpack() - } - }, getWorkerMap(agentNumber, params.NUMBER_EXECUTIONS.toInteger(), worker, workerFailures))() - } - } - } - - parallel(agents) - - currentBuild.description += ", Failures: ${workerFailures.size()}" - - if (workerFailures.size() > 0) { - print "There were ${workerFailures.size()} test suite failures." - print "The executions that failed were:" - print workerFailures.join("\n") - print "Please check 'Test Result' and 'Pipeline Steps' pages for more info" - } - } - } - } -} - -def getWorkerFromParams(isXpack, job, ciGroup) { - if (!isXpack) { - if (job == 'firefoxSmoke') { - return kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }) - } else if(job == 'visualRegression') { - return kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }) - } else { - return kibanaPipeline.getOssCiGroupWorker(ciGroup) - } - } - - if (job == 'firefoxSmoke') { - return kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }) - } else if(job == 'visualRegression') { - return kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }) - } else { - return kibanaPipeline.getXpackCiGroupWorker(ciGroup) - } -} - -def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWorkers = 14) { - def workerMap = [:] - def numberOfWorkers = Math.min(numberOfExecutions, maxWorkers) - - for(def i = 1; i <= numberOfWorkers; i++) { - def workerExecutions = numberOfExecutions/numberOfWorkers + (i <= numberOfExecutions%numberOfWorkers ? 1 : 0) - - workerMap["agent-${agentNumber}-worker-${i}"] = { workerNumber -> - for(def j = 0; j < workerExecutions; j++) { - print "Execute agent-${agentNumber} worker-${workerNumber}: ${j}" - withEnv(["JOB=agent-${agentNumber}-worker-${workerNumber}-${j}"]) { - catchError { - try { - worker(workerNumber) - } catch (ex) { - workerFailures << "agent-${agentNumber} worker-${workerNumber}-${j}" - throw ex - } - } - } - } - } - } - - return workerMap -} - -def trunc(str, length) { - if (str.size() >= length) { - return str.take(length) + "..." - } - - return str; -} diff --git a/Jenkinsfile b/Jenkinsfile index b4ae41b9431dc..ec0bc2327321a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,6 @@ #!/bin/groovy library 'kibana-pipeline-library' -kibanaLibrary.load() stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a little bit timeout(time: 180, unit: 'MINUTES') { @@ -9,43 +8,288 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a ansiColor('xterm') { catchError { parallel([ - 'kibana-intake-agent': kibanaPipeline.legacyJobRunner('kibana-intake'), - 'x-pack-intake-agent': kibanaPipeline.legacyJobRunner('x-pack-intake'), - 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ - 'oss-ciGroup1': kibanaPipeline.getOssCiGroupWorker(1), - 'oss-ciGroup2': kibanaPipeline.getOssCiGroupWorker(2), - 'oss-ciGroup3': kibanaPipeline.getOssCiGroupWorker(3), - 'oss-ciGroup4': kibanaPipeline.getOssCiGroupWorker(4), - 'oss-ciGroup5': kibanaPipeline.getOssCiGroupWorker(5), - 'oss-ciGroup6': kibanaPipeline.getOssCiGroupWorker(6), - 'oss-ciGroup7': kibanaPipeline.getOssCiGroupWorker(7), - 'oss-ciGroup8': kibanaPipeline.getOssCiGroupWorker(8), - 'oss-ciGroup9': kibanaPipeline.getOssCiGroupWorker(9), - 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), - 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), - 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), - 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), - // 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), + 'kibana-intake-agent': legacyJobRunner('kibana-intake'), + 'x-pack-intake-agent': legacyJobRunner('x-pack-intake'), + 'kibana-oss-agent': withWorkers('kibana-oss-tests', { buildOss() }, [ + 'oss-ciGroup1': getOssCiGroupWorker(1), + 'oss-ciGroup2': getOssCiGroupWorker(2), + 'oss-ciGroup3': getOssCiGroupWorker(3), + 'oss-ciGroup4': getOssCiGroupWorker(4), + 'oss-ciGroup5': getOssCiGroupWorker(5), + 'oss-ciGroup6': getOssCiGroupWorker(6), + 'oss-ciGroup7': getOssCiGroupWorker(7), + 'oss-ciGroup8': getOssCiGroupWorker(8), + 'oss-ciGroup9': getOssCiGroupWorker(9), + 'oss-ciGroup10': getOssCiGroupWorker(10), + 'oss-ciGroup11': getOssCiGroupWorker(11), + 'oss-ciGroup12': getOssCiGroupWorker(12), + 'oss-firefoxSmoke': getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), + // 'oss-visualRegression': getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), ]), - 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ - 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), - 'xpack-ciGroup2': kibanaPipeline.getXpackCiGroupWorker(2), - 'xpack-ciGroup3': kibanaPipeline.getXpackCiGroupWorker(3), - 'xpack-ciGroup4': kibanaPipeline.getXpackCiGroupWorker(4), - 'xpack-ciGroup5': kibanaPipeline.getXpackCiGroupWorker(5), - 'xpack-ciGroup6': kibanaPipeline.getXpackCiGroupWorker(6), - 'xpack-ciGroup7': kibanaPipeline.getXpackCiGroupWorker(7), - 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), - 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), - 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), - 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), - // 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), + 'kibana-xpack-agent': withWorkers('kibana-xpack-tests', { buildXpack() }, [ + 'xpack-ciGroup1': getXpackCiGroupWorker(1), + 'xpack-ciGroup2': getXpackCiGroupWorker(2), + 'xpack-ciGroup3': getXpackCiGroupWorker(3), + 'xpack-ciGroup4': getXpackCiGroupWorker(4), + 'xpack-ciGroup5': getXpackCiGroupWorker(5), + 'xpack-ciGroup6': getXpackCiGroupWorker(6), + 'xpack-ciGroup7': getXpackCiGroupWorker(7), + 'xpack-ciGroup8': getXpackCiGroupWorker(8), + 'xpack-ciGroup9': getXpackCiGroupWorker(9), + 'xpack-ciGroup10': getXpackCiGroupWorker(10), + 'xpack-firefoxSmoke': getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), + // 'xpack-visualRegression': getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), ]), ]) } - kibanaPipeline.sendMail() + node('flyweight') { + // If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success + // The e-mail plugin for the infra e-mail depends upon this being set + currentBuild.result = currentBuild.result ?: 'SUCCESS' + + sendMail() + } } } } } +def withWorkers(name, preWorkerClosure = {}, workerClosures = [:]) { + return { + jobRunner('tests-xl') { + try { + doSetup() + preWorkerClosure() + + def nextWorker = 1 + def worker = { workerClosure -> + def workerNumber = nextWorker + nextWorker++ + + return { + workerClosure(workerNumber) + } + } + + def workers = [:] + workerClosures.each { workerName, workerClosure -> + workers[workerName] = worker(workerClosure) + } + + parallel(workers) + } finally { + catchError { + uploadAllGcsArtifacts(name) + } + + catchError { + runbldJunit() + } + + catchError { + publishJunit() + } + + catchError { + runErrorReporter() + } + } + } + } +} + +def getPostBuildWorker(name, closure) { + return { workerNumber -> + def kibanaPort = "61${workerNumber}1" + def esPort = "61${workerNumber}2" + def esTransportPort = "61${workerNumber}3" + + withEnv([ + "CI_WORKER_NUMBER=${workerNumber}", + "TEST_KIBANA_HOST=localhost", + "TEST_KIBANA_PORT=${kibanaPort}", + "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}", + "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}", + "TEST_ES_TRANSPORT_PORT=${esTransportPort}", + "IS_PIPELINE_JOB=1", + ]) { + closure() + } + } +} + +def getOssCiGroupWorker(ciGroup) { + return getPostBuildWorker("ciGroup" + ciGroup, { + withEnv([ + "CI_GROUP=${ciGroup}", + "JOB=kibana-ciGroup${ciGroup}", + ]) { + runbld "./test/scripts/jenkins_ci_group.sh" + } + }) +} + +def getXpackCiGroupWorker(ciGroup) { + return getPostBuildWorker("xpack-ciGroup" + ciGroup, { + withEnv([ + "CI_GROUP=${ciGroup}", + "JOB=xpack-kibana-ciGroup${ciGroup}", + ]) { + runbld "./test/scripts/jenkins_xpack_ci_group.sh" + } + }) +} + +def legacyJobRunner(name) { + return { + parallel([ + "${name}": { + withEnv([ + "JOB=${name}", + ]) { + jobRunner('linux && immutable') { + try { + runbld('.ci/run.sh', true) + } finally { + catchError { + uploadAllGcsArtifacts(name) + } + catchError { + publishJunit() + } + catchError { + runErrorReporter() + } + } + } + } + } + ]) + } +} + +def jobRunner(label, closure) { + node(label) { + def scmVars = checkout scm + + withEnv([ + "CI=true", + "HOME=${env.JENKINS_HOME}", + "PR_SOURCE_BRANCH=${env.ghprbSourceBranch ?: ''}", + "PR_TARGET_BRANCH=${env.ghprbTargetBranch ?: ''}", + "PR_AUTHOR=${env.ghprbPullAuthorLogin ?: ''}", + "TEST_BROWSER_HEADLESS=1", + "GIT_BRANCH=${scmVars.GIT_BRANCH}", + ]) { + withCredentials([ + string(credentialsId: 'vault-addr', variable: 'VAULT_ADDR'), + string(credentialsId: 'vault-role-id', variable: 'VAULT_ROLE_ID'), + string(credentialsId: 'vault-secret-id', variable: 'VAULT_SECRET_ID'), + ]) { + // scm is configured to check out to the ./kibana directory + dir('kibana') { + closure() + } + } + } + } +} + +// TODO what should happen if GCS, Junit, or email publishing fails? Unstable build? Failed build? + +def uploadGcsArtifact(workerName, pattern) { + def storageLocation = "gs://kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" // TODO + // def storageLocation = "gs://kibana-pipeline-testing/jobs/pipeline-test/${BUILD_NUMBER}/${workerName}" + + googleStorageUpload( + credentialsId: 'kibana-ci-gcs-plugin', + bucket: storageLocation, + pattern: pattern, + sharedPublicly: true, + showInline: true, + ) +} + +def uploadAllGcsArtifacts(workerName) { + def ARTIFACT_PATTERNS = [ + 'target/kibana-*', + 'target/junit/**/*', + 'test/**/screenshots/**/*.png', + 'test/functional/failure_debug/html/*.html', + 'x-pack/test/**/screenshots/**/*.png', + 'x-pack/test/functional/failure_debug/html/*.html', + 'x-pack/test/functional/apps/reporting/reports/session/*.pdf', + ] + + ARTIFACT_PATTERNS.each { pattern -> + uploadGcsArtifact(workerName, pattern) + } +} + +def publishJunit() { + junit(testResults: 'target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true) +} + +def sendMail() { + sendInfraMail() + sendKibanaMail() +} + +def sendInfraMail() { + catchError { + step([ + $class: 'Mailer', + notifyEveryUnstableBuild: true, + recipients: 'infra-root+build@elastic.co', + sendToIndividuals: false + ]) + } +} + +def sendKibanaMail() { + catchError { + def buildStatus = buildUtils.getBuildStatus() + + if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { + emailext( + to: 'build-kibana@elastic.co', + subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}", + body: '${SCRIPT,template="groovy-html.template"}', + mimeType: 'text/html', + ) + } + } +} + +def runbld(script, enableJunitProcessing = false) { + def extraConfig = enableJunitProcessing ? "" : "--config ${env.WORKSPACE}/kibana/.ci/runbld_no_junit.yml" + + sh "/usr/local/bin/runbld -d '${pwd()}' ${extraConfig} ${script}" +} + +def runbldJunit() { + sh "/usr/local/bin/runbld -d '${pwd()}' ${env.WORKSPACE}/kibana/test/scripts/jenkins_runbld_junit.sh" +} + +def bash(script) { + sh "#!/bin/bash\n${script}" +} + +def doSetup() { + runbld "./test/scripts/jenkins_setup.sh" +} + +def buildOss() { + runbld "./test/scripts/jenkins_build_kibana.sh" +} + +def buildXpack() { + runbld "./test/scripts/jenkins_xpack_build_kibana.sh" +} + +def runErrorReporter() { + bash """ + source src/dev/ci_setup/setup_env.sh + node src/dev/failed_tests/cli + """ +} diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index a79fca180ce51..aa0a5e62ee937 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -2,10 +2,6 @@ set -e -if [[ "$CI_ENV_SETUP" ]]; then - return 0 -fi - installNode=$1 dir="$(pwd)" @@ -156,5 +152,3 @@ if [[ -d "$ES_DIR" && -f "$ES_JAVA_PROP_PATH" ]]; then echo "Setting JAVA_HOME=$HOME/.java/$ES_BUILD_JAVA" export JAVA_HOME=$HOME/.java/$ES_BUILD_JAVA fi - -export CI_ENV_SETUP=true diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index c50b56f98a789..b50b00f54350b 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -43,9 +43,8 @@ export const IGNORE_FILE_GLOBS = [ 'x-pack/docs/**/*', 'src/legacy/ui/public/assets/fonts/**/*', 'packages/kbn-utility-types/test-d/**/*', - '**/Jenkinsfile*', + 'Jenkinsfile', 'Dockerfile*', - 'vars/*', // Files in this directory must match a pre-determined name in some cases. 'x-pack/legacy/plugins/canvas/.storybook/*', diff --git a/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh b/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh deleted file mode 100755 index 4b16e3b32fefd..0000000000000 --- a/test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -cd test/plugin_functional/plugins/kbn_tp_sample_panel_action; -if [[ ! -d "target" ]]; then - checks-reporter-with-killswitch "Build kbn_tp_sample_panel_action" yarn build; -fi -cd -; diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh index 42699c4a84e1b..6807e318138ce 100755 --- a/test/scripts/jenkins_ci_group.sh +++ b/test/scripts/jenkins_ci_group.sh @@ -24,7 +24,10 @@ fi checks-reporter-with-killswitch "Functional tests / Group ${CI_GROUP}" yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}"; if [ "$CI_GROUP" == "1" ]; then - source test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh + # build kbn_tp_sample_panel_action + cd test/plugin_functional/plugins/kbn_tp_sample_panel_action; + checks-reporter-with-killswitch "Build kbn_tp_sample_panel_action" yarn build; + cd -; yarn run grunt run:pluginFunctionalTestsRelease --from=source; yarn run grunt run:interpreterFunctionalTestsRelease; fi diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy deleted file mode 100644 index d74f7ad31c5c4..0000000000000 --- a/vars/kibanaPipeline.groovy +++ /dev/null @@ -1,237 +0,0 @@ -def withWorkers(name, preWorkerClosure = {}, workerClosures = [:]) { - return { - jobRunner('tests-xl') { - try { - doSetup() - preWorkerClosure() - - def nextWorker = 1 - def worker = { workerClosure -> - def workerNumber = nextWorker - nextWorker++ - - return { - workerClosure(workerNumber) - } - } - - def workers = [:] - workerClosures.each { workerName, workerClosure -> - workers[workerName] = worker(workerClosure) - } - - parallel(workers) - } finally { - catchError { - uploadAllGcsArtifacts(name) - } - - catchError { - runbld.junit() - } - - catchError { - publishJunit() - } - - catchError { - runErrorReporter() - } - } - } - } -} - -def getPostBuildWorker(name, closure) { - return { workerNumber -> - def kibanaPort = "61${workerNumber}1" - def esPort = "61${workerNumber}2" - def esTransportPort = "61${workerNumber}3" - - withEnv([ - "CI_WORKER_NUMBER=${workerNumber}", - "TEST_KIBANA_HOST=localhost", - "TEST_KIBANA_PORT=${kibanaPort}", - "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}", - "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}", - "TEST_ES_TRANSPORT_PORT=${esTransportPort}", - "IS_PIPELINE_JOB=1", - ]) { - closure() - } - } -} - -def getOssCiGroupWorker(ciGroup) { - return getPostBuildWorker("ciGroup" + ciGroup, { - withEnv([ - "CI_GROUP=${ciGroup}", - "JOB=kibana-ciGroup${ciGroup}", - ]) { - runbld "./test/scripts/jenkins_ci_group.sh" - } - }) -} - -def getXpackCiGroupWorker(ciGroup) { - return getPostBuildWorker("xpack-ciGroup" + ciGroup, { - withEnv([ - "CI_GROUP=${ciGroup}", - "JOB=xpack-kibana-ciGroup${ciGroup}", - ]) { - runbld "./test/scripts/jenkins_xpack_ci_group.sh" - } - }) -} - -def legacyJobRunner(name) { - return { - parallel([ - "${name}": { - withEnv([ - "JOB=${name}", - ]) { - jobRunner('linux && immutable') { - try { - runbld('.ci/run.sh', true) - } finally { - catchError { - uploadAllGcsArtifacts(name) - } - catchError { - publishJunit() - } - catchError { - runErrorReporter() - } - } - } - } - } - ]) - } -} - -def jobRunner(label, closure) { - node(label) { - def scmVars = checkout scm - - withEnv([ - "CI=true", - "HOME=${env.JENKINS_HOME}", - "PR_SOURCE_BRANCH=${env.ghprbSourceBranch ?: ''}", - "PR_TARGET_BRANCH=${env.ghprbTargetBranch ?: ''}", - "PR_AUTHOR=${env.ghprbPullAuthorLogin ?: ''}", - "TEST_BROWSER_HEADLESS=1", - "GIT_BRANCH=${scmVars.GIT_BRANCH}", - ]) { - withCredentials([ - string(credentialsId: 'vault-addr', variable: 'VAULT_ADDR'), - string(credentialsId: 'vault-role-id', variable: 'VAULT_ROLE_ID'), - string(credentialsId: 'vault-secret-id', variable: 'VAULT_SECRET_ID'), - ]) { - // scm is configured to check out to the ./kibana directory - dir('kibana') { - closure() - } - } - } - } -} - -// TODO what should happen if GCS, Junit, or email publishing fails? Unstable build? Failed build? - -def uploadGcsArtifact(workerName, pattern) { - def storageLocation = "gs://kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" // TODO - - googleStorageUpload( - credentialsId: 'kibana-ci-gcs-plugin', - bucket: storageLocation, - pattern: pattern, - sharedPublicly: true, - showInline: true, - ) -} - -def uploadAllGcsArtifacts(workerName) { - def ARTIFACT_PATTERNS = [ - 'target/kibana-*', - 'target/junit/**/*', - 'test/**/screenshots/**/*.png', - 'test/functional/failure_debug/html/*.html', - 'x-pack/test/**/screenshots/**/*.png', - 'x-pack/test/functional/failure_debug/html/*.html', - 'x-pack/test/functional/apps/reporting/reports/session/*.pdf', - ] - - ARTIFACT_PATTERNS.each { pattern -> - uploadGcsArtifact(workerName, pattern) - } -} - -def publishJunit() { - junit(testResults: 'target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true) -} - -def sendMail() { - // If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success - // The e-mail plugin for the infra e-mail depends upon this being set - currentBuild.result = currentBuild.result ?: 'SUCCESS' - - def buildStatus = buildUtils.getBuildStatus() - if (buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { - node('flyweight') { - sendInfraMail() - sendKibanaMail() - } - } -} - -def sendInfraMail() { - catchError { - step([ - $class: 'Mailer', - notifyEveryUnstableBuild: true, - recipients: 'infra-root+build@elastic.co', - sendToIndividuals: false - ]) - } -} - -def sendKibanaMail() { - catchError { - if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { - emailext( - to: 'build-kibana@elastic.co', - subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}", - body: '${SCRIPT,template="groovy-html.template"}', - mimeType: 'text/html', - ) - } - } -} - -def bash(script) { - sh "#!/bin/bash\n${script}" -} - -def doSetup() { - runbld "./test/scripts/jenkins_setup.sh" -} - -def buildOss() { - runbld "./test/scripts/jenkins_build_kibana.sh" -} - -def buildXpack() { - runbld "./test/scripts/jenkins_xpack_build_kibana.sh" -} - -def runErrorReporter() { - bash """ - source src/dev/ci_setup/setup_env.sh - node src/dev/failed_tests/cli - """ -} - -return this diff --git a/vars/runbld.groovy b/vars/runbld.groovy deleted file mode 100644 index 501e2421ca65b..0000000000000 --- a/vars/runbld.groovy +++ /dev/null @@ -1,11 +0,0 @@ -def call(script, enableJunitProcessing = false) { - def extraConfig = enableJunitProcessing ? "" : "--config ${env.WORKSPACE}/kibana/.ci/runbld_no_junit.yml" - - sh "/usr/local/bin/runbld -d '${pwd()}' ${extraConfig} ${script}" -} - -def junit() { - sh "/usr/local/bin/runbld -d '${pwd()}' ${env.WORKSPACE}/kibana/test/scripts/jenkins_runbld_junit.sh" -} - -return this From c505fcbd057b413e0e3031bc0b5e96334046579d Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 4 Oct 2019 19:02:19 -0500 Subject: [PATCH 19/33] [Code] Embedded Code Snippet Component (#47183) * Add Integrations page and POC CodeBlock usage * Adds new route hidden behind a config var (`xpack.code.integrations.enabld`) * Updates CodeBlock component to have a smaller API surface * Adds several example components for various pieces of the new APM integration designs. * Pare down stub data Hits and ranges were both part of our search results queries; O11y won't have that information. --- x-pack/legacy/plugins/code/index.ts | 4 + .../plugins/code/public/components/app.tsx | 7 + .../public/components/codeblock/codeblock.tsx | 89 ++++++---- .../components/editor/references_panel.tsx | 30 ++-- .../public/components/integrations/data.ts | 159 ++++++++++++++++++ .../components/integrations/frame_header.tsx | 40 +++++ .../public/components/integrations/helpers.ts | 9 + .../public/components/integrations/index.tsx | 61 +++++++ .../components/integrations/integrations.scss | 43 +++++ .../components/integrations/repo_title.tsx | 22 +++ .../plugins/code/public/components/routes.ts | 1 + .../components/search_page/code_result.tsx | 36 ++-- x-pack/legacy/plugins/code/public/index.scss | 1 + .../public/monaco/override_monaco_styles.scss | 1 + 14 files changed, 434 insertions(+), 69 deletions(-) create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/data.ts create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/helpers.ts create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/index.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/integrations.scss create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx diff --git a/x-pack/legacy/plugins/code/index.ts b/x-pack/legacy/plugins/code/index.ts index 96e2efc793834..eb902367047ea 100644 --- a/x-pack/legacy/plugins/code/index.ts +++ b/x-pack/legacy/plugins/code/index.ts @@ -38,6 +38,7 @@ export const code = (kibana: any) => const config = server.config(); return { codeUiEnabled: config.get('xpack.code.ui.enabled'), + codeIntegrationsEnabled: config.get('xpack.code.integrations.enabled'), }; }, hacks: ['plugins/code/hacks/toggle_app_link_in_nav'], @@ -49,6 +50,9 @@ export const code = (kibana: any) => ui: Joi.object({ enabled: Joi.boolean().default(true), }).default(), + integrations: Joi.object({ + enabled: Joi.boolean().default(false), + }).default(), enabled: Joi.boolean().default(true), }).default(); }, diff --git a/x-pack/legacy/plugins/code/public/components/app.tsx b/x-pack/legacy/plugins/code/public/components/app.tsx index 54363213bcad5..e87e9dd88e6a4 100644 --- a/x-pack/legacy/plugins/code/public/components/app.tsx +++ b/x-pack/legacy/plugins/code/public/components/app.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { HashRouter as Router, Redirect, Switch } from 'react-router-dom'; +import chrome from 'ui/chrome'; import { connect } from 'react-redux'; import { RootState } from '../reducers'; import { Admin } from './admin_page/admin'; @@ -17,6 +18,7 @@ import { NotFound } from './main/not_found'; import { Route } from './route'; import * as ROUTES from './routes'; import { Search } from './search_page/search'; +import { Integrations } from './integrations'; const RooComponent = (props: { setupOk?: boolean }) => { if (props.setupOk) { @@ -33,6 +35,8 @@ const Root = connect(mapStateToProps)(RooComponent); const Empty = () => null; +const integrationsEnabled = chrome.getInjected('codeIntegrationsEnabled'); + export const App = () => { return ( @@ -45,6 +49,9 @@ export const App = () => { + {integrationsEnabled && ( + + )} diff --git a/x-pack/legacy/plugins/code/public/components/codeblock/codeblock.tsx b/x-pack/legacy/plugins/code/public/components/codeblock/codeblock.tsx index 910129eb1a3c1..89c9e9137158f 100644 --- a/x-pack/legacy/plugins/code/public/components/codeblock/codeblock.tsx +++ b/x-pack/legacy/plugins/code/public/components/codeblock/codeblock.tsx @@ -5,50 +5,72 @@ */ import { EuiPanel } from '@elastic/eui'; -import { editor, IPosition, IRange } from 'monaco-editor'; +import { editor, IRange } from 'monaco-editor'; import React from 'react'; import { ResizeChecker } from '../shared/resize_checker'; import { monaco } from '../../monaco/monaco'; import { registerEditor } from '../../monaco/single_selection_helper'; -interface Props { - code: string; - fileComponent?: React.ReactNode; - startLine?: number; - language?: string; - highlightRanges?: IRange[]; - onClick?: (event: IPosition) => void; +export interface Position { + lineNumber: string; + column: number; +} + +export interface Props { + content: string; + header: React.ReactNode; + language: string; + highlightRanges: IRange[]; + onClick: (event: Position) => void; folding: boolean; - lineNumbersFunc: (line: number) => string; + /** + * Returns the line number to display for a given line. + * @param lineIndex The index of the given line (0-indexed) + */ + lineNumber: (lineIndex: number) => string; + className?: string; } export class CodeBlock extends React.PureComponent { + static defaultProps = { + header: undefined, + folding: false, + highlightRanges: [], + language: 'text', + lineNumber: String, + onClick: () => {}, + }; + private el: HTMLDivElement | null = null; private ed?: editor.IStandaloneCodeEditor; private resizeChecker?: ResizeChecker; private currentHighlightDecorations: string[] = []; public async componentDidMount() { + const { content, highlightRanges, language, onClick } = this.props; + if (this.el) { - await this.tryLoadFile(this.props.code, this.props.language || 'text'); + await this.tryLoadFile(content, language); this.ed!.onMouseDown((e: editor.IEditorMouseEvent) => { if ( - this.props.onClick && + onClick && (e.target.type === monaco.editor.MouseTargetType.GUTTER_LINE_NUMBERS || e.target.type === monaco.editor.MouseTargetType.CONTENT_TEXT) ) { const position = e.target.position || { lineNumber: 0, column: 0 }; - const lineNumber = (this.props.startLine || 0) + position.lineNumber; - this.props.onClick({ + const lineNumber = this.lineNumber(position.lineNumber); + + onClick({ lineNumber, column: position.column, }); } }); registerEditor(this.ed!); - if (this.props.highlightRanges) { - const decorations = this.props.highlightRanges.map((range: IRange) => { + + if (highlightRanges.length) { + const decorations = highlightRanges.map((range: IRange) => { return { range, options: { @@ -66,6 +88,7 @@ export class CodeBlock extends React.PureComponent { }); } } + private async tryLoadFile(code: string, language: string) { try { await monaco.editor.colorize(code, language, {}); @@ -79,7 +102,7 @@ export class CodeBlock extends React.PureComponent { this.ed = monaco.editor.create(this.el!, { value: code, language, - lineNumbers: this.lineNumbersFunc.bind(this), + lineNumbers: this.lineNumber, readOnly: true, folding: this.props.folding, minimap: { @@ -103,17 +126,16 @@ export class CodeBlock extends React.PureComponent { } public componentDidUpdate(prevProps: Readonly) { - if ( - prevProps.code !== this.props.code || - prevProps.highlightRanges !== this.props.highlightRanges - ) { + const { content, highlightRanges } = this.props; + + if (prevProps.content !== content || prevProps.highlightRanges !== highlightRanges) { if (this.ed) { const model = this.ed.getModel(); if (model) { - model.setValue(this.props.code); + model.setValue(content); - if (this.props.highlightRanges) { - const decorations = this.props.highlightRanges!.map((range: IRange) => { + if (highlightRanges.length) { + const decorations = highlightRanges!.map((range: IRange) => { return { range, options: { @@ -138,19 +160,20 @@ export class CodeBlock extends React.PureComponent { } public render() { - const linesCount = this.props.code.split('\n').length; + const { className, header } = this.props; + const height = this.lines.length * 18; + return ( - - {this.props.fileComponent} -

(this.el = r)} style={{ height: linesCount * 18 }} /> + + {header} +
(this.el = r)} style={{ height }} /> ); } - private lineNumbersFunc = (line: number) => { - if (this.props.lineNumbersFunc) { - return this.props.lineNumbersFunc(line); - } - return `${(this.props.startLine || 0) + line}`; - }; + private lineNumber = (lineIndex: number) => this.props.lineNumber(lineIndex - 1); + + private get lines(): string[] { + return this.props.content.split('\n'); + } } diff --git a/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx b/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx index 219c5e837155c..785238eb5fab7 100644 --- a/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx +++ b/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx @@ -14,13 +14,12 @@ import { EuiTitle, } from '@elastic/eui'; import classname from 'classnames'; -import { IPosition } from 'monaco-editor'; import queryString from 'querystring'; import React from 'react'; import { parseSchema } from '../../../common/uri_util'; import { GroupedFileResults, GroupedRepoResults } from '../../actions'; import { history } from '../../utils/url'; -import { CodeBlock } from '../codeblock/codeblock'; +import { CodeBlock, Position } from '../codeblock/codeblock'; interface Props { isLoading: boolean; @@ -114,12 +113,9 @@ export class ReferencesPanel extends React.Component { private renderReference(file: GroupedFileResults) { const key = `${file.uri}`; - const lineNumberFn = (l: number) => { - return file.lineNumbers[l - 1]; - }; - const fileComponent = ( + const header = ( - + {file.file} @@ -128,23 +124,22 @@ export class ReferencesPanel extends React.Component { return ( file.lineNumbers[i]} highlightRanges={file.highlights} - fileComponent={fileComponent} - onClick={this.onCodeClick.bind(this, file.lineNumbers, file.uri)} + onClick={this.onCodeClick(file.uri)} /> ); } - private onCodeClick(lineNumbers: string[], url: string, pos: IPosition) { - const line = parseInt(lineNumbers[pos.lineNumber - 1], 10); - history.push(this.computeUrl(url, line)); - } + private onCodeClick = (url: string) => (position: Position) => { + const lineNum = parseInt(position.lineNumber, 10); + history.push(this.computeUrl(url, lineNum)); + }; private computeUrl(url: string, line?: number) { const { uri } = parseSchema(url)!; @@ -158,6 +153,7 @@ export class ReferencesPanel extends React.Component { tab: 'references', refUrl: this.props.refUrl, }); + return line !== undefined ? `${uri}!L${line}:0?${query}` : `${uri}?${query}`; } } diff --git a/x-pack/legacy/plugins/code/public/components/integrations/data.ts b/x-pack/legacy/plugins/code/public/components/integrations/data.ts new file mode 100644 index 0000000000000..83137dff158ee --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/data.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface Snippet { + uri: string; + filePath: string; + language?: string; + compositeContent: { + content: string; + lineMapping: string[]; + }; +} + +export type Results = Record; + +export const results: Results = { + 'ringside.ts#L18': { + uri: 'github.com/rylnd/ringside', + filePath: 'src/ringside.ts', + language: 'typescript', + compositeContent: { + content: + "\nimport { fitsInside, fitsOutside } from './fitting';\n\nexport interface RingsideInterface {\n positions(): FittedPosition[];\n}\n\nclass Ringside implements RingsideInterface {\n readonly innerBounds: FullRect;\n readonly outerBounds: FullRect;\n\n}\n\nexport default Ringside;\n", + lineMapping: [ + '..', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '..', + '67', + '68', + '69', + '70', + ], + }, + }, + 'ringside.story.tsx#L12': { + uri: 'github.com/rylnd/ringside', + filePath: 'stories/ringside.story.tsx', + language: 'typescript', + compositeContent: { + content: + "\nimport { interpolateRainbow } from 'd3-scale-chromatic';\n\nimport { Ringside } from '../src';\nimport { XAlignment, YAlignment, XBasis, YBasis } from '../src/types';\n\nlet ringside: Ringside;\n\nconst enumKeys: (e: any) => string[] = e =>\n\n\nconst color = position => {\n const combos = ringside.positions().map(p => JSON.stringify(p));\n const hash = combos.indexOf(JSON.stringify(position)) / combos.length;\n\n\n};\n\nconst Stories = storiesOf('Ringside', module).addDecorator(withKnobs);\n\nStories.add('Ringside', () => {\n", + lineMapping: [ + '..', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '..', + '14', + '15', + '16', + '17', + '18', + '..', + '20', + '21', + '22', + '23', + '24', + '..', + ], + }, + }, + + 'ringside.story.tsx#L8': { + uri: 'github.com/rylnd/ringside', + filePath: 'stories/ringside.story.tsx', + language: 'typescript', + compositeContent: { + content: + "import { Ringside } from '../src';\n\ndescribe('Ringside', () => {\n let inner;\n let outer;\n let height;\n let width;\n let ringside: Ringside;\n\n beforeEach(() => {\n\n width = 50;\n\n ringside = new Ringside(inner, outer, height, width);\n });\n\n", + lineMapping: [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '..', + '14', + '15', + '16', + '17', + '18', + '..', + ], + }, + }, + + 'ringside.story.tsx#L14': { + uri: 'github.com/rylnd/ringside', + filePath: 'stories/ringside.story.tsx', + language: 'typescript', + compositeContent: { + content: + "import { Ringside } from '../src';\n\ndescribe('Ringside', () => {\n let inner;\n let outer;\n let height;\n let width;\n let ringside: Ringside;\n\n beforeEach(() => {\n\n width = 50;\n\n ringside = new Ringside(inner, outer, height, width);\n });\n\n", + lineMapping: [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '..', + '14', + '15', + '16', + '17', + '18', + '..', + ], + }, + }, +}; + +export interface Frame { + fileName: string; + lineNumber: number; + functionName?: string; +} + +export const frames: Frame[] = [ + { fileName: 'ringside.ts', lineNumber: 18 }, + { fileName: 'node_modules/library_code.js', lineNumber: 100 }, + { fileName: 'ringside.story.tsx', lineNumber: 8 }, + { fileName: 'node_modules/other_stuff.js', lineNumber: 58 }, + { fileName: 'node_modules/other/other.js', lineNumber: 3 }, + { fileName: 'ringside.story.tsx', lineNumber: 12 }, + { fileName: 'ringside.story.tsx', lineNumber: 14 }, +]; + +export const repos = [ + 'https://github.com/a/repo', + 'https://github.com/another/repo', + 'https://github.com/also/a_repo', +]; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx new file mode 100644 index 0000000000000..0fa482f8bce36 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiButtonIcon, EuiFlexGroup, EuiLink, EuiText, EuiTextColor } from '@elastic/eui'; + +export const FrameHeader = ({ + fileName, + lineNumber, + onClick, +}: { + fileName: string; + lineNumber: number | string; + onClick: () => void; +}) => ( + + + {fileName} + at + line {lineNumber} + + + Last updated: 14 mins ago + + + +); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/helpers.ts b/x-pack/legacy/plugins/code/public/components/integrations/helpers.ts new file mode 100644 index 0000000000000..6b55a5e2a0f45 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/helpers.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// TODO(rylnd): make this an actual external link +export const externalFileURI: (repoUri: string, filePath: string) => string = (uri, path) => + `/${uri}/blob/HEAD/${path}`; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx new file mode 100644 index 0000000000000..5052b3502748d --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx @@ -0,0 +1,61 @@ +/* + * 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 React from 'react'; +import { EuiFlexGroup, EuiText } from '@elastic/eui'; + +import { CodeBlock } from '../codeblock/codeblock'; +import { history } from '../../utils/url'; +import { FrameHeader } from './frame_header'; +import { RepoTitle } from './repo_title'; +import { externalFileURI } from './helpers'; +import { frames, results } from './data'; + +export const Integrations = () => ( +
+ {frames.map(frame => { + const { fileName, lineNumber } = frame; + const key = `${fileName}#L${lineNumber}`; + const snippet = results[key]; + + if (snippet) { + const { compositeContent, filePath, language, uri } = snippet; + const { content, lineMapping } = compositeContent; + const fileUrl = externalFileURI(uri, filePath); + + return ( +
+ + history.push(fileUrl)} + /> + } + language={language} + lineNumber={i => lineMapping[i]} + /> +
+ ); + } + + return ( +
+ + + {fileName} + at + line {lineNumber} + + +
+ ); + })} +
+); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss new file mode 100644 index 0000000000000..24fe406cd0960 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss @@ -0,0 +1,43 @@ +.integrations__container { + padding: $euiSize; +} + +.integrations__frame { + margin: $euiSizeS 0; +} + +.integrations__code { + @include euiCodeFont; +} + +.integrations__link--external { + margin-left: $euiSizeS; +} + +.integrations__preposition { + margin: 0 $euiSizeS; + color: $euiColorMediumShade; +} + +.integrations__button-icon { + padding: $euiSizeXS; + background-color: $euiColorLightestShade; + border: 1px solid $euiColorLightShade; +} + +.integrations__snippet-info { + margin-bottom: $euiSizeS; +} + +.integrations__snippet-title { + margin-bottom: $euiSizeS; +} + +.integrations__text--bold { + font-weight: $euiFontWeightBold; +} + +.integrations__popover { + margin-bottom: 1rem; + width: 300px; +} diff --git a/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx new file mode 100644 index 0000000000000..2ed3d529b699b --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx @@ -0,0 +1,22 @@ +/* + * 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 React from 'react'; + +import { EuiText } from '@elastic/eui'; +import { RepositoryUtils } from '../../../common/repository_utils'; + +export const RepoTitle = ({ uri }: { uri: string }) => { + const org = RepositoryUtils.orgNameFromUri(uri); + const name = RepositoryUtils.repoNameFromUri(uri); + + return ( + + {org}/ + {name} + + ); +}; diff --git a/x-pack/legacy/plugins/code/public/components/routes.ts b/x-pack/legacy/plugins/code/public/components/routes.ts index 0d4ccf4e5e826..d741ca1896850 100644 --- a/x-pack/legacy/plugins/code/public/components/routes.ts +++ b/x-pack/legacy/plugins/code/public/components/routes.ts @@ -15,3 +15,4 @@ export const REPO = `/:resource/:org/:repo`; export const MAIN_ROOT = `/:resource/:org/:repo/${pathTypes}/:revision`; export const ADMIN = '/admin'; export const SEARCH = '/search'; +export const INTEGRATIONS = '/integrations'; diff --git a/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx b/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx index 632c221f0680b..72aacce978671 100644 --- a/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx +++ b/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx @@ -6,13 +6,12 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { IPosition } from 'monaco-editor'; import React from 'react'; import { Link } from 'react-router-dom'; import { RepositoryUtils } from '../../../common/repository_utils'; import { history } from '../../utils/url'; -import { CodeBlock } from '../codeblock/codeblock'; +import { CodeBlock, Position } from '../codeblock/codeblock'; interface Props { query: string; @@ -22,15 +21,14 @@ interface Props { export class CodeResult extends React.PureComponent { public render() { const { results, query } = this.props; + return results.map(item => { - const { uri, filePath, hits, compositeContent } = item; + const { compositeContent, filePath, hits, language, uri } = item; const { content, lineMapping, ranges } = compositeContent; - const repoLinkUrl = `/${uri}/tree/HEAD/`; - const fileLinkUrl = `/${uri}/blob/HEAD/${filePath}`; const key = `${uri}-${filePath}-${query}`; - const lineMappingFunc = (l: number) => { - return lineMapping[l - 1]; - }; + const repoLinkUrl = `/${uri}/tree/HEAD/`; + const fileLinkUrl = `/${uri}/blob/HEAD/${filePath}`; // TODO(rylnd) move these to link helpers + return (
@@ -75,23 +73,23 @@ export class CodeResult extends React.PureComponent { lineMapping[i]} + onClick={this.onCodeClick(fileLinkUrl)} />
); }); } - private onCodeClick(lineNumbers: string[], fileUrl: string, pos: IPosition) { - const line = parseInt(lineNumbers[pos.lineNumber - 1], 10); - if (!isNaN(line)) { - history.push(`${fileUrl}!L${line}:0`); + private onCodeClick = (url: string) => (position: Position) => { + const lineNumber = parseInt(position.lineNumber, 10); + + if (!isNaN(lineNumber)) { + history.push(`${url}!L${lineNumber}:0`); } - } + }; } diff --git a/x-pack/legacy/plugins/code/public/index.scss b/x-pack/legacy/plugins/code/public/index.scss index aecaa46272ded..6d996a80f283f 100644 --- a/x-pack/legacy/plugins/code/public/index.scss +++ b/x-pack/legacy/plugins/code/public/index.scss @@ -4,6 +4,7 @@ @import "./monaco/override_monaco_styles.scss"; @import "./components/diff_page/diff.scss"; @import "./components/main/main.scss"; +@import "./components/integrations/integrations.scss"; // TODO: Cleanup everything above this line diff --git a/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss b/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss index 3e03c5693d434..b6f39a34e8653 100644 --- a/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss +++ b/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss @@ -9,6 +9,7 @@ .monaco-editor.mac .margin-view-overlays .line-numbers { cursor: pointer; + color: $euiColorMediumShade; background-color: $euiColorLightestShade; } From 02012f02fe930c647905a2697e23585941b11c9d Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Fri, 4 Oct 2019 17:16:17 -0700 Subject: [PATCH 20/33] Generate uuid in task Manager as Kibana uuid may not yet have been initialised Generate uuid for TaskManager at startup --- .../plugins/task_manager/task_manager.ts | 10 +++++- .../plugins/task_manager/task_store.test.ts | 34 +++++++++---------- .../legacy/plugins/task_manager/task_store.ts | 10 +++--- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/task_manager/task_manager.ts b/x-pack/legacy/plugins/task_manager/task_manager.ts index c7cfdfb4223b8..7d2794fa33bcc 100644 --- a/x-pack/legacy/plugins/task_manager/task_manager.ts +++ b/x-pack/legacy/plugins/task_manager/task_manager.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import uuid from 'uuid'; import { SavedObjectsClientContract, SavedObjectsSerializer } from 'src/core/server'; import { Logger } from './types'; import { fillPool } from './lib/fill_pool'; @@ -30,6 +31,12 @@ export interface TaskManagerOpts { serializer: SavedObjectsSerializer; } +function generateTaskManagerUUID(logger: Logger): string { + const taskManagerUUID = uuid.v4(); + logger.info(`Initialising Task Manager with UUID: ${taskManagerUUID}`); + return taskManagerUUID; +} + /* * The TaskManager is the public interface into the task manager system. This glues together * all of the disparate modules in one integration point. The task manager operates in two different ways: @@ -78,8 +85,9 @@ export class TaskManager { index: opts.config.get('xpack.task_manager.index'), maxAttempts: opts.config.get('xpack.task_manager.max_attempts'), definitions: this.definitions, - kibanaId: opts.config.get('server.uuid'), + taskManagerId: generateTaskManagerUUID(this.logger), }); + const pool = new TaskPool({ logger: this.logger, maxWorkers: this.maxWorkers, diff --git a/x-pack/legacy/plugins/task_manager/task_store.test.ts b/x-pack/legacy/plugins/task_manager/task_store.test.ts index 47b2ed6ff48f3..65b49820d6e6c 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.test.ts @@ -61,7 +61,7 @@ describe('TaskStore', () => { ); const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster, maxAttempts: 2, @@ -161,7 +161,7 @@ describe('TaskStore', () => { const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits } })); const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster, maxAttempts: 2, @@ -353,7 +353,7 @@ describe('TaskStore', () => { const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits: [] } })); const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster, definitions: taskDefinitions, @@ -579,7 +579,7 @@ describe('TaskStore', () => { definitions: taskDefinitions, serializer, savedObjectsRepository: savedObjectsClient, - kibanaId: '', + taskManagerId: '', index: '', ...opts, }); @@ -603,7 +603,7 @@ describe('TaskStore', () => { })); const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster, definitions: taskDefinitions, @@ -730,7 +730,7 @@ describe('TaskStore', () => { }); test('it claims tasks by setting their ownerId, status and retryAt', async () => { - const kibanaId = uuid.v1(); + const taskManagerId = uuid.v1(); const claimOwnershipUntil = new Date(Date.now()); const { args: { @@ -740,7 +740,7 @@ describe('TaskStore', () => { }, } = await testClaimAvailableTasks({ opts: { - kibanaId, + taskManagerId, }, claimingOpts: { claimOwnershipUntil, @@ -751,7 +751,7 @@ describe('TaskStore', () => { source: `ctx._source.task.ownerId=params.ownerId; ctx._source.task.status=params.status; ctx._source.task.retryAt=params.retryAt;`, lang: 'painless', params: { - ownerId: kibanaId, + ownerId: taskManagerId, retryAt: claimOwnershipUntil, status: 'claiming', }, @@ -759,7 +759,7 @@ describe('TaskStore', () => { }); test('it returns task objects', async () => { - const kibanaId = uuid.v1(); + const taskManagerId = uuid.v1(); const claimOwnershipUntil = new Date(Date.now()); const runAt = new Date(); const tasks = [ @@ -777,7 +777,7 @@ describe('TaskStore', () => { state: '{ "baby": "Henhen" }', user: 'jimbo', scope: ['reporting'], - ownerId: kibanaId, + ownerId: taskManagerId, }, }, _seq_no: 1, @@ -798,7 +798,7 @@ describe('TaskStore', () => { state: '{ "henry": "The 8th" }', user: 'dabo', scope: ['reporting', 'ceo'], - ownerId: kibanaId, + ownerId: taskManagerId, }, }, _seq_no: 3, @@ -815,7 +815,7 @@ describe('TaskStore', () => { }, } = await testClaimAvailableTasks({ opts: { - kibanaId, + taskManagerId, }, claimingOpts: { claimOwnershipUntil, @@ -829,7 +829,7 @@ describe('TaskStore', () => { must: [ { term: { - 'task.ownerId': kibanaId, + 'task.ownerId': taskManagerId, }, }, { term: { 'task.status': 'claiming' } }, @@ -849,7 +849,7 @@ describe('TaskStore', () => { status: 'idle', taskType: 'foo', user: 'jimbo', - ownerId: kibanaId, + ownerId: taskManagerId, }, { attempts: 2, @@ -862,7 +862,7 @@ describe('TaskStore', () => { status: 'running', taskType: 'bar', user: 'dabo', - ownerId: kibanaId, + ownerId: taskManagerId, }, ]); }); @@ -899,7 +899,7 @@ describe('TaskStore', () => { const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster: jest.fn(), maxAttempts: 2, @@ -948,7 +948,7 @@ describe('TaskStore', () => { const callCluster = jest.fn(); const store = new TaskStore({ index: 'tasky', - kibanaId: '', + taskManagerId: '', serializer, callCluster, maxAttempts: 2, diff --git a/x-pack/legacy/plugins/task_manager/task_store.ts b/x-pack/legacy/plugins/task_manager/task_store.ts index bf15435f54722..2314d705544d2 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.ts @@ -27,7 +27,7 @@ import { export interface StoreOpts { callCluster: ElasticJs; index: string; - kibanaId: string; + taskManagerId: string; maxAttempts: number; definitions: TaskDictionary; savedObjectsRepository: SavedObjectsClientContract; @@ -83,7 +83,7 @@ export interface UpdateByQueryResult { export class TaskStore { public readonly maxAttempts: number; public readonly index: string; - public readonly kibanaId: string; + public readonly taskManagerId: string; private callCluster: ElasticJs; private definitions: TaskDictionary; private savedObjectsRepository: SavedObjectsClientContract; @@ -102,7 +102,7 @@ export class TaskStore { constructor(opts: StoreOpts) { this.callCluster = opts.callCluster; this.index = opts.index; - this.kibanaId = opts.kibanaId; + this.taskManagerId = opts.taskManagerId; this.maxAttempts = opts.maxAttempts; this.definitions = opts.definitions; this.serializer = opts.serializer; @@ -328,7 +328,7 @@ export class TaskStore { source: `ctx._source.task.ownerId=params.ownerId; ctx._source.task.status=params.status; ctx._source.task.retryAt=params.retryAt;`, lang: 'painless', params: { - ownerId: this.kibanaId, + ownerId: this.taskManagerId, retryAt: claimOwnershipUntil, status: 'claiming', }, @@ -353,7 +353,7 @@ export class TaskStore { must: [ { term: { - 'task.ownerId': this.kibanaId, + 'task.ownerId': this.taskManagerId, }, }, { term: { 'task.status': 'claiming' } }, From aa1102d011c62ebe2ed899a6266d81c4d4742b23 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Fri, 4 Oct 2019 18:21:45 -0700 Subject: [PATCH 21/33] [Canvas][i18n] Sidebar (#46090) * This is a combination of 5 commits. Extracts i18n strings from sidebar_content Extracts i18n strings from element_settings Extracted i18n strings from workpad_config Extracts i18n strings from page_config Extracts i18n strings from group_settings Extracts i18n strings from element_config and multi_element_settings Extracted remaining strings from element_config Extracts i18n strings from sidebar_header Extracts i18n strings from custom_element_modal Fixed file picker placeholder Fixed i18n ids * Alphabetize keys * Fixed save button label * fixed merge conflict --- .../legacy/plugins/canvas/i18n/components.ts | 353 +++++++++++++++++- .../custom_element_modal.tsx | 28 +- .../element_config/element_config.js | 13 +- .../public/components/page_config/index.js | 5 +- .../components/page_config/page_config.js | 13 +- .../element_settings/element_settings.tsx | 7 +- .../components/sidebar/group_settings.tsx | 7 +- .../sidebar/multi_element_settings.tsx | 10 +- .../components/sidebar/sidebar_content.js | 12 +- .../sidebar_header/sidebar_header.tsx | 92 +++-- .../workpad_config/workpad_config.js | 27 +- 11 files changed, 461 insertions(+), 106 deletions(-) diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index 941db91c41722..b8f75a2f8c135 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -172,6 +172,48 @@ export const ComponentStrings = { defaultMessage: 'Remove Color', }), }, + CustomElementModal: { + getCancelButtonLabel: () => + i18n.translate('xpack.canvas.customElementModal.cancelButtonLabel', { + defaultMessage: 'Cancel', + }), + getCharactersRemainingDescription: (numberOfRemainingCharacter: number) => + i18n.translate('xpack.canvas.customElementModal.remainingCharactersDescription', { + defaultMessage: '{numberOfRemainingCharacter} characters remaining', + values: { + numberOfRemainingCharacter, + }, + }), + getDescriptionInputLabel: () => + i18n.translate('xpack.canvas.customElementModal.descriptionInputLabel', { + defaultMessage: 'Description', + }), + getElementPreviewTitle: () => + i18n.translate('xpack.canvas.customElementModal.elementPreviewTitle', { + defaultMessage: 'Element preview', + }), + getImageFilePickerPlaceholder: () => + i18n.translate('xpack.canvas.customElementModal.imageFilePickerPlaceholder', { + defaultMessage: 'Select or drag and drop an image', + }), + getImageInputLabel: () => + i18n.translate('xpack.canvas.customElementModal.imageInputLabel', { + defaultMessage: 'Thumbnail image', + }), + getImageInputDescription: () => + i18n.translate('xpack.canvas.customElementModal.imageInputDescription', { + defaultMessage: + 'Take a screenshot of your element and upload it here. This can also be done after saving.', + }), + getNameInputLabel: () => + i18n.translate('xpack.canvas.customElementModal.nameInputLabel', { + defaultMessage: 'Name', + }), + getSaveButtonLabel: () => + i18n.translate('xpack.canvas.customElementModal.saveButtonLabel', { + defaultMessage: 'Save', + }), + }, DatasourceDatasourceComponent: { getChangeButtonLabel: () => i18n.translate('xpack.canvas.datasourceDatasourceComponent.changeButtonLabel', { @@ -215,14 +257,64 @@ export const ComponentStrings = { defaultMessage: 'Datasource preview', }), }, - HelpMenu: { - getHelpMenuDescription: () => - i18n.translate('xpack.canvas.helpMenu.description', { - defaultMessage: 'For {CANVAS} specific information', + + ElementConfig: { + getFailedLabel: () => + i18n.translate('xpack.canvas.elementConfig.failedLabel', { + defaultMessage: 'Failed', + description: + 'The label for the total number of elements in a workpad that have thrown an error or failed to load', + }), + getLoadedLabel: () => + i18n.translate('xpack.canvas.elementConfig.loadedLabel', { + defaultMessage: 'Loaded', + description: 'The label for the number of elements in a workpad that have loaded', + }), + getProgressLabel: () => + i18n.translate('xpack.canvas.elementConfig.progressLabel', { + defaultMessage: 'Progress', + description: 'The label for the percentage of elements that have finished loading', + }), + getTitle: () => + i18n.translate('xpack.canvas.elementConfig.title', { + defaultMessage: 'Elements', + description: + '"Elements" refers to the individual text, images, or visualizations that you can add to a Canvas workpad', + }), + getTotalLabel: () => + i18n.translate('xpack.canvas.elementConfig.totalLabel', { + defaultMessage: 'Total', + description: 'The label for the total number of elements in a workpad', + }), + }, + ElementSettings: { + getDataTabLabel: () => + i18n.translate('xpack.canvas.elementSettings.dataTabLabel', { + defaultMessage: 'Data', + description: + 'This tab contains the settings for the data (i.e. Elasticsearch query) used as ' + + 'the source for a Canvas element', + }), + getDisplayTabLabel: () => + i18n.translate('xpack.canvas.elementSettings.displayTabLabel', { + defaultMessage: 'Display', + description: 'This tab contains the settings for how data is displayed in a Canvas element', + }), + }, + GroupSettings: { + getSaveGroupDescription: () => + i18n.translate('xpack.canvas.groupSettings.saveGroupDescription', { + defaultMessage: 'Save this group as a new element to re-use it throughout your workpad.', + }), + getUngroupDescription: () => + i18n.translate('xpack.canvas.groupSettings.ungroupDescription', { + defaultMessage: 'Ungroup ({uKey}) to edit individual element settings.', values: { - CANVAS, + uKey: 'U', }, }), + }, + HelpMenu: { getDocumentationLinkLabel: () => i18n.translate('xpack.canvas.helpMenu.documentationLinkLabel', { defaultMessage: '{CANVAS} documentation', @@ -230,15 +322,22 @@ export const ComponentStrings = { CANVAS, }, }), + getHelpMenuDescription: () => + i18n.translate('xpack.canvas.helpMenu.description', { + defaultMessage: 'For {CANVAS} specific information', + values: { + CANVAS, + }, + }), getKeyboardShortcutsLinkLabel: () => i18n.translate('xpack.canvas.helpMenu.keyboardShortcutsLinkLabel', { defaultMessage: 'Keyboard Shortcuts', }), }, KeyboardShortcutsDoc: { - getTitle: () => - i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle', { - defaultMessage: 'Keyboard Shortcuts', + getFlyoutCloseButtonAriaLabel: () => + i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyout.closeButtonAriaLabel', { + defaultMessage: 'Closes keyboard shortcuts reference', }), getShortcutSeparator: () => i18n.translate('xpack.canvas.keyboardShortcutsDoc.shortcutListSeparator', { @@ -246,9 +345,227 @@ export const ComponentStrings = { description: 'Separates which keyboard shortcuts can be used for a single action. Example: "{shortcut1} or {shortcut2} or {shortcut3}"', }), - getFlyoutCloseButtonAriaLabel: () => - i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyout.closeButtonAriaLabel', { - defaultMessage: 'Closes keyboard shortcuts reference', + getTitle: () => + i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle', { + defaultMessage: 'Keyboard Shortcuts', + }), + }, + MultiElementSettings: { + getMultipleElementsActionsDescription: () => + i18n.translate('xpack.canvas.groupSettings.multipleElementsActionsDescription', { + defaultMessage: + 'Deselect these elements to edit their individual settings, press ({gKey}) to group them, or save this selection as a new ' + + 'element to re-use it throughout your workpad.', + values: { + gKey: 'G', + }, + }), + getMultipleElementsDescription: () => + i18n.translate('xpack.canvas.groupSettings.multipleElementsDescription', { + defaultMessage: 'Multiple elements are currently selected.', + }), + }, + PageConfig: { + getBackgroundColorDescription: () => + i18n.translate('xpack.canvas.pageConfig.backgroundColorDescription', { + defaultMessage: 'Accepts HEX, RGB or HTML color names', + }), + getBackgroundColorLabel: () => + i18n.translate('xpack.canvas.pageConfig.backgroundColorLabel', { + defaultMessage: 'Background Color', + }), + getNoTransitionDropDownOptionLabel: () => + i18n.translate('xpack.canvas.pageConfig.transitions.noneDropDownOptionLabel', { + defaultMessage: 'None', + description: + 'This is the option the user should choose if they do not want any page transition (i.e. fade in, fade out, etc) to ' + + 'be applied to the current page.', + }), + getTitle: () => + i18n.translate('xpack.canvas.pageConfig.title', { + defaultMessage: 'Page', + }), + getTransitionLabel: () => + i18n.translate('xpack.canvas.pageConfig.transitionLabel', { + defaultMessage: 'Transition', + description: + 'This refers to the transition effect, such as fade in or rotate, applied to a page in presentation mode.', + }), + getTransitionPreviewLabel: () => + i18n.translate('xpack.canvas.pageConfig.transitionPreviewLabel', { + defaultMessage: 'Preview', + description: 'This is the label for a preview of the transition effect selected.', + }), + }, + SidebarContent: { + getGroupedElementSidebarTitle: () => + i18n.translate('xpack.canvas.sidebarContent.groupedElementSidebarTitle', { + defaultMessage: 'Grouped element', + description: + 'The title displayed when a grouped element is selected. "elements" refer to the different visualizations, images, ' + + 'text, etc that can be added in a Canvas workpad. These elements can be grouped into a larger "grouped element" ' + + 'that contains multiple individual elements.', + }), + getMultiElementSidebarTitle: () => + i18n.translate('xpack.canvas.sidebarContent.multiElementSidebarTitle', { + defaultMessage: 'Multiple elements', + description: + 'The title displayed when multiple elements are selected. "elements" refer to the different visualizations, images, ' + + 'text, etc that can be added in a Canvas workpad.', + }), + getSingleElementSidebarTitle: () => + i18n.translate('xpack.canvas.sidebarContent.singleElementSidebarTitle', { + defaultMessage: 'Selected element', + description: + 'The title displayed when a single element are selected. "element" refer to the different visualizations, images, ' + + 'text, etc that can be added in a Canvas workpad.', + }), + }, + SidebarHeader: { + getAlignmentMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.alignmentMenuItemLabel', { + defaultMessage: 'Alignment', + description: + 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' + + 'alignment options of the selected elements', + }), + getBottomAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel', { + defaultMessage: 'Bottom', + }), + getBringForwardAriaLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.bringForwardArialLabel', { + defaultMessage: 'Move element up one layer', + }), + getBringToFrontAriaLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.bringToFrontArialLabel', { + defaultMessage: 'Move element to top layer', + }), + getCenterAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.centerAlignMenuItemLabel', { + defaultMessage: 'Center', + description: 'This refers to alignment centered horizontally.', + }), + getContextMenuTitle: () => + i18n.translate('xpack.canvas.sidebarHeader.contextMenuAriaLabel', { + defaultMessage: 'Element options', + }), + getCreateElementModalTitle: () => + i18n.translate('xpack.canvas.sidebarHeader.createElementModalTitle', { + defaultMessage: 'Create new element', + }), + getDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.distributionMenutItemLabel', { + defaultMessage: 'Distribution', + description: + 'This refers to the options to evenly spacing the selected elements horizontall or vertically.', + }), + getGroupMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.groupMenuItemLabel', { + defaultMessage: 'Group', + description: 'This refers to grouping multiple selected elements.', + }), + getHorizontalDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel', { + defaultMessage: 'Horizontal', + }), + getLeftAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.leftAlignMenuItemLabel', { + defaultMessage: 'Left', + }), + getMiddleAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.middleAlignMenuItemLabel', { + defaultMessage: 'Middle', + description: 'This refers to alignment centered vertically.', + }), + getOrderMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.orderMenuItemLabel', { + defaultMessage: 'Order', + description: 'Refers to the order of the elements displayed on the page from front to back', + }), + getRightAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.rightAlignMenuItemLabel', { + defaultMessage: 'Right', + }), + getSaveElementMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.savedElementMenuItemLabel', { + defaultMessage: 'Save as new element', + }), + getSendBackwardAriaLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.sendBackwardArialLabel', { + defaultMessage: 'Move element down one layer', + }), + getSendToBackAriaLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.sendToBackArialLabel', { + defaultMessage: 'Move element to bottom layer', + }), + getTopAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.topAlignMenuItemLabel', { + defaultMessage: 'Top', + }), + getUngroupMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.ungroupMenuItemLabel', { + defaultMessage: 'Ungroup', + description: 'This refers to ungrouping a grouped element', + }), + getVerticalDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel', { + defaultMessage: 'Vertical', + }), + }, + WorkpadConfig: { + getApplyStylesheetButtonLabel: () => + i18n.translate('xpack.canvas.workpadConfig.applyStylesheetButtonLabel', { + defaultMessage: `Apply stylesheet`, + description: + '"stylesheet" refers to the collection of CSS style rules entered by the user.', + }), + getFlipDimensionAriaLabel: () => + i18n.translate('xpack.canvas.workpadConfig.swapDimensionsAriaLabel', { + defaultMessage: `Swap the page's width and height`, + }), + getFlipDimensionTooltip: () => + i18n.translate('xpack.canvas.workpadConfig.swapDimensionsTooltip', { + defaultMessage: 'Swap the width and height', + }), + getGlobalCSSLabel: () => + i18n.translate('xpack.canvas.workpadConfig.globalCSSLabel', { + defaultMessage: `Global CSS overrides`, + }), + getGlobalCSSTooltip: () => + i18n.translate('xpack.canvas.workpadConfig.globalCSSTooltip', { + defaultMessage: `Apply styles to all pages in this workpad`, + }), + getPageHeightLabel: () => + i18n.translate('xpack.canvas.workpadConfig.heightLabel', { + defaultMessage: 'Height', + }), + getPageSizeBadgeAriaLabel: (sizeName: string) => + i18n.translate('xpack.canvas.workpadConfig.pageSizeBadgeAriaLabel', { + defaultMessage: `Preset page size: {sizeName}`, + values: { + sizeName, + }, + }), + getPageSizeBadgeOnClickAriaLabel: (sizeName: string) => + i18n.translate('xpack.canvas.workpadConfig.pageSizeBadgeOnClickAriaLabel', { + defaultMessage: `Set page size to {sizeName}`, + values: { + sizeName, + }, + }), + getPageWidthLabel: () => + i18n.translate('xpack.canvas.workpadConfig.widthLabel', { + defaultMessage: 'Width', + }), + getTitle: () => + i18n.translate('xpack.canvas.workpadConfig.title', { + defaultMessage: 'Workpad', + }), + getUSLetterButtonLabel: () => + i18n.translate('xpack.canvas.workpadConfig.USLetterButtonLabel', { + defaultMessage: 'US Letter', + description: 'This is referring to the dimentions of U.S. standard letter paper.', }), }, PageManager: { @@ -737,6 +1054,13 @@ export const ComponentStrings = { }), }, WorkpadTemplates: { + getCloneTemplateLinkAriaLabel: (templateName: string) => + i18n.translate('xpack.canvas.workpadTemplate.cloneTemplateLinkAriaLabel', { + defaultMessage: `Clone workpad template '{templateName}'`, + values: { + templateName, + }, + }), getTableDescriptionColumnTitle: () => i18n.translate('xpack.canvas.workpadTemplates.table.descriptionColumnTitle', { defaultMessage: 'Description', @@ -756,12 +1080,5 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.workpadTemplate.searchPlaceholder', { defaultMessage: 'Find template', }), - getCloneTemplateLinkAriaLabel: (templateName: string) => - i18n.translate('xpack.canvas.workpadTemplate.cloneTemplateLinkAriaLabel', { - defaultMessage: `Clone workpad template '{templateName}'`, - values: { - templateName, - }, - }), }, }; diff --git a/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/custom_element_modal.tsx b/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/custom_element_modal.tsx index 5f6e0fc9e7bc6..b5d08d98072a3 100644 --- a/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/custom_element_modal.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/custom_element_modal.tsx @@ -31,10 +31,13 @@ import { import { VALID_IMAGE_TYPES } from '../../../common/lib/constants'; import { encode } from '../../../common/lib/dataurl'; import { ElementCard } from '../element_card'; +import { ComponentStrings } from '../../../i18n'; const MAX_NAME_LENGTH = 40; const MAX_DESCRIPTION_LENGTH = 100; +const { CustomElementModal: strings } = ComponentStrings; + interface Props { /** * initial value of the name of the custom element @@ -126,8 +129,8 @@ export class CustomElementModal extends PureComponent { { /> { -

- Take a screenshot of your element and upload it here. This can also be done after - saving. -

+

{strings.getImageInputDescription()}

{ grow={1} > -

Element preview

+

{strings.getElementPreviewTitle()}

@@ -187,7 +189,7 @@ export class CustomElementModal extends PureComponent { - Cancel + {strings.getCancelButtonLabel()} { onSave(name, description, image); }} > - Save + {strings.getSaveButtonLabel()} diff --git a/x-pack/legacy/plugins/canvas/public/components/element_config/element_config.js b/x-pack/legacy/plugins/canvas/public/components/element_config/element_config.js index e5ae933655802..76007994e56bf 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_config/element_config.js +++ b/x-pack/legacy/plugins/canvas/public/components/element_config/element_config.js @@ -7,6 +7,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiStat, EuiTitle } from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { Fragment } from 'react'; +import { ComponentStrings } from '../../../i18n'; + +const { ElementConfig: strings } = ComponentStrings; export const ElementConfig = ({ elementStats }) => { if (!elementStats) { @@ -19,21 +22,21 @@ export const ElementConfig = ({ elementStats }) => { return ( -

Elements

+

{strings.getTitle()}

- + - + - + - +
diff --git a/x-pack/legacy/plugins/canvas/public/components/page_config/index.js b/x-pack/legacy/plugins/canvas/public/components/page_config/index.js index fb216af667631..a0692584d4986 100644 --- a/x-pack/legacy/plugins/canvas/public/components/page_config/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/page_config/index.js @@ -9,8 +9,11 @@ import { get } from 'lodash'; import { transitionsRegistry } from '../../lib/transitions_registry'; import { getSelectedPageIndex, getPages } from '../../state/selectors/workpad'; import { stylePage, setPageTransition } from '../../state/actions/pages'; +import { ComponentStrings } from '../../../i18n'; import { PageConfig as Component } from './page_config'; +const { PageConfig: strings } = ComponentStrings; + const mapStateToProps = state => { const pageIndex = getSelectedPageIndex(state); const page = getPages(state)[pageIndex]; @@ -28,7 +31,7 @@ const mergeProps = (stateProps, dispatchProps) => { }, background: get(stateProps, 'page.style.background'), transition: transitionsRegistry.get(get(stateProps, 'page.transition.name')), - transitions: [{ value: '', text: 'None' }].concat( + transitions: [{ value: '', text: strings.getNoTransitionDropDownOptionLabel() }].concat( transitionsRegistry.toArray().map(({ name, displayName }) => ({ value: name, text: displayName, diff --git a/x-pack/legacy/plugins/canvas/public/components/page_config/page_config.js b/x-pack/legacy/plugins/canvas/public/components/page_config/page_config.js index 5c6e91621d55a..2586b4ec61f04 100644 --- a/x-pack/legacy/plugins/canvas/public/components/page_config/page_config.js +++ b/x-pack/legacy/plugins/canvas/public/components/page_config/page_config.js @@ -8,6 +8,9 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiCard, EuiFormRow, EuiTitle, EuiSpacer, EuiSelect } from '@elastic/eui'; import { WorkpadColorPicker } from '../workpad_color_picker'; +import { ComponentStrings } from '../../../i18n'; + +const { PageConfig: strings } = ComponentStrings; export const PageConfig = ({ pageIndex, @@ -20,13 +23,13 @@ export const PageConfig = ({ return ( -

Page

+

{strings.getTitle()}

@@ -35,7 +38,7 @@ export const PageConfig = ({ page, we use the second page's transition) */} {pageIndex > 0 ? ( - + {transition ? ( - + = ({ element }) => { const tabs = [ { id: 'edit', - name: 'Display', + name: strings.getDisplayTabLabel(), content: (
@@ -36,7 +39,7 @@ export const ElementSettings: FunctionComponent = ({ element }) => { }, { id: 'data', - name: 'Data', + name: strings.getDataTabLabel(), content: (
diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar/group_settings.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar/group_settings.tsx index a514ef15b6610..b46465d9ec775 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar/group_settings.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar/group_settings.tsx @@ -6,10 +6,13 @@ import React, { FunctionComponent } from 'react'; import { EuiText } from '@elastic/eui'; +import { ComponentStrings } from '../../../i18n'; + +const { GroupSettings: strings } = ComponentStrings; export const GroupSettings: FunctionComponent = () => ( -

Ungroup (U) to edit individual element settings.

-

Save this group as a new element to re-use it throughout your workpad.

+

{strings.getUngroupDescription()}

+

{strings.getSaveGroupDescription()}

); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar/multi_element_settings.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar/multi_element_settings.tsx index a3922aaac864a..2de3a805c95e9 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar/multi_element_settings.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar/multi_element_settings.tsx @@ -6,13 +6,13 @@ import React, { FunctionComponent } from 'react'; import { EuiText } from '@elastic/eui'; +import { ComponentStrings } from '../../../i18n'; + +const { MultiElementSettings: strings } = ComponentStrings; export const MultiElementSettings: FunctionComponent = () => ( -

Multiple elements are currently selected.

-

- Deselect these elements to edit their individual settings, press (G) to group them, or save - this selection as a new element to re-use it throughout your workpad. -

+

{strings.getMultipleElementsDescription()}

+

{strings.getMultipleElementsActionsDescription()}

); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js b/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js index fe53203684f5d..825ea8a4f2494 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js @@ -11,11 +11,14 @@ import { EuiSpacer } from '@elastic/eui'; import { getSelectedToplevelNodes, getSelectedElementId } from '../../state/selectors/workpad'; import { SidebarHeader } from '../sidebar_header'; import { globalStateUpdater } from '../workpad_page/integration_utils'; +import { ComponentStrings } from '../../../i18n'; import { MultiElementSettings } from './multi_element_settings'; import { GroupSettings } from './group_settings'; import { GlobalConfig } from './global_config'; import { ElementSettings } from './element_settings'; +const { SidebarContent: strings } = ComponentStrings; + const mapStateToProps = state => ({ selectedToplevelNodes: getSelectedToplevelNodes(state), selectedElementId: getSelectedElementId(state), @@ -42,7 +45,10 @@ const withGlobalState = (commit, updateGlobalState) => (type, payload) => { const MultiElementSidebar = ({ commit, updateGlobalState }) => ( - + @@ -51,7 +57,7 @@ const MultiElementSidebar = ({ commit, updateGlobalState }) => ( const GroupedElementSidebar = ({ commit, updateGlobalState }) => ( @@ -62,7 +68,7 @@ const GroupedElementSidebar = ({ commit, updateGlobalState }) => ( const SingleElementSidebar = ({ selectedElementId }) => ( - + ); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx index 5a76414019a27..3a0e191b9f4b5 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx @@ -20,9 +20,13 @@ import { import { Popover } from '../popover'; import { CustomElementModal } from '../custom_element_modal'; import { ToolTipShortcut } from '../tool_tip_shortcut/'; +import { ComponentStrings, ShortcutStrings } from '../../../i18n'; const topBorderClassName = 'canvasContextMenu--topBorder'; +const { SidebarHeader: strings } = ComponentStrings; +const shortcutHelp = ShortcutStrings.getShortcutHelp(); + interface Props { /** * title to display in the header @@ -139,7 +143,7 @@ const contextMenuButton = (handleClick: (event: MouseEvent) => void) => ( color="text" iconType="boxesVertical" onClick={handleClick} - aria-label="Element options" + aria-label={strings.getContextMenuTitle()} /> ); @@ -202,7 +206,7 @@ export class SidebarHeader extends Component { position="bottom" content={ - Bring to front + {shortcutHelp.BRING_TO_FRONT} } @@ -211,7 +215,7 @@ export class SidebarHeader extends Component { color="text" iconType="sortUp" onClick={bringToFront} - aria-label="Move element to top layer" + aria-label={strings.getBringToFrontAriaLabel()} /> @@ -220,7 +224,7 @@ export class SidebarHeader extends Component { position="bottom" content={ - Bring forward + {shortcutHelp.BRING_FORWARD} } @@ -229,7 +233,7 @@ export class SidebarHeader extends Component { color="text" iconType="arrowUp" onClick={bringForward} - aria-label="Move element up one layer" + aria-label={strings.getBringForwardAriaLabel()} /> @@ -238,7 +242,7 @@ export class SidebarHeader extends Component { position="bottom" content={ - Send backward + {shortcutHelp.SEND_BACKWARD} } @@ -247,7 +251,7 @@ export class SidebarHeader extends Component { color="text" iconType="arrowDown" onClick={sendBackward} - aria-label="Move element down one layer" + aria-label={strings.getSendBackwardAriaLabel()} /> @@ -256,7 +260,7 @@ export class SidebarHeader extends Component { position="bottom" content={ - Send to back + {shortcutHelp.SEND_TO_BACK} } @@ -265,7 +269,7 @@ export class SidebarHeader extends Component { color="text" iconType="sortDown" onClick={sendToBack} - aria-label="Move element to bottom layer" + aria-label={strings.getSendToBackAriaLabel()} /> @@ -277,28 +281,28 @@ export class SidebarHeader extends Component { const { bringToFront, bringForward, sendBackward, sendToBack } = this.props; return { - menuItem: { name: 'Order', className: topBorderClassName, panel: 1 }, + menuItem: { name: strings.getOrderMenuItemLabel(), className: topBorderClassName, panel: 1 }, panel: { id: 1, - title: 'Order', + title: strings.getOrderMenuItemLabel(), items: [ { - name: 'Bring to front', // TODO: check against current element position and disable if already top layer + name: shortcutHelp.BRING_TO_FRONT, // TODO: check against current element position and disable if already top layer icon: 'sortUp', onClick: bringToFront, }, { - name: 'Bring forward', // TODO: same as above + name: shortcutHelp.BRING_TO_FRONT, // TODO: same as above icon: 'arrowUp', onClick: bringForward, }, { - name: 'Send backward', // TODO: check against current element position and disable if already bottom layer + name: shortcutHelp.SEND_BACKWARD, // TODO: check against current element position and disable if already bottom layer icon: 'arrowDown', onClick: sendBackward, }, { - name: 'Send to back', // TODO: same as above + name: shortcutHelp.SEND_TO_BACK, // TODO: same as above icon: 'sortDown', onClick: sendToBack, }, @@ -311,38 +315,42 @@ export class SidebarHeader extends Component { const { alignLeft, alignCenter, alignRight, alignTop, alignMiddle, alignBottom } = this.props; return { - menuItem: { name: 'Align elements', className: 'canvasContextMenu', panel: 2 }, + menuItem: { + name: strings.getAlignmentMenuItemLabel(), + className: 'canvasContextMenu', + panel: 2, + }, panel: { id: 2, - title: 'Alignment', + title: strings.getAlignmentMenuItemLabel(), items: [ { - name: 'Left', + name: strings.getLeftAlignMenuItemLabel(), icon: 'editorItemAlignLeft', onClick: close(alignLeft), }, { - name: 'Center', + name: strings.getCenterAlignMenuItemLabel(), icon: 'editorItemAlignCenter', onClick: close(alignCenter), }, { - name: 'Right', + name: strings.getRightAlignMenuItemLabel(), icon: 'editorItemAlignRight', onClick: close(alignRight), }, { - name: 'Top', + name: strings.getTopAlignMenuItemLabel(), icon: 'editorItemAlignTop', onClick: close(alignTop), }, { - name: 'Middle', + name: strings.getMiddleAlignMenuItemLabel(), icon: 'editorItemAlignMiddle', onClick: close(alignMiddle), }, { - name: 'Bottom', + name: strings.getBottomAlignMenuItemLabel(), icon: 'editorItemAlignBottom', onClick: close(alignBottom), }, @@ -355,18 +363,22 @@ export class SidebarHeader extends Component { const { distributeHorizontally, distributeVertically } = this.props; return { - menuItem: { name: 'Distribute elements', className: 'canvasContextMenu', panel: 3 }, + menuItem: { + name: strings.getDistributionMenuItemLabel(), + className: 'canvasContextMenu', + panel: 3, + }, panel: { id: 3, - title: 'Distribution', + title: strings.getDistributionMenuItemLabel(), items: [ { - name: 'Horizontal', + name: strings.getHorizontalDistributionMenuItemLabel(), icon: 'editorDistributeHorizontal', onClick: close(distributeHorizontally), }, { - name: 'Vertical', + name: strings.getVerticalDistributionMenuItemLabel(), icon: 'editorDistributeVertical', onClick: close(distributeVertically), }, @@ -382,7 +394,7 @@ export class SidebarHeader extends Component { return groupIsSelected ? [ { - name: 'Ungroup', + name: strings.getUngroupMenuItemLabel(), className: topBorderClassName, onClick: close(ungroupNodes), }, @@ -390,7 +402,7 @@ export class SidebarHeader extends Component { : selectedNodes.length > 1 ? [ { - name: 'Group', + name: strings.getGroupMenuItemLabel(), className: topBorderClassName, onClick: close(groupNodes), }, @@ -416,27 +428,27 @@ export class SidebarHeader extends Component { const items: EuiContextMenuPanelItemDescriptor[] = [ { - name: 'Cut', + name: shortcutHelp.CUT, icon: 'cut', onClick: close(cutNodes), }, { - name: 'Copy', + name: shortcutHelp.COPY, icon: 'copy', onClick: copyNodes, }, { - name: 'Paste', // TODO: can this be disabled if clipboard is empty? + name: shortcutHelp.PASTE, // TODO: can this be disabled if clipboard is empty? icon: 'copyClipboard', onClick: close(pasteNodes), }, { - name: 'Delete', + name: shortcutHelp.DELETE, icon: 'trash', onClick: close(deleteNodes), }, { - name: 'Clone', + name: shortcutHelp.CLONE, onClick: close(cloneNodes), }, ...this._getGroupMenuItems(close), @@ -445,7 +457,7 @@ export class SidebarHeader extends Component { const panels: EuiContextMenuPanelDescriptor[] = [ { id: 0, - title: 'Element options', + title: strings.getContextMenuTitle(), items, }, ]; @@ -468,7 +480,7 @@ export class SidebarHeader extends Component { } items.push({ - name: 'Save as new element', + name: strings.getSaveElementMenuItemLabel(), icon: 'indexOpen', className: topBorderClassName, onClick: this._showModal, @@ -483,7 +495,7 @@ export class SidebarHeader extends Component { className="canvasContextMenu" button={contextMenuButton} panelPaddingSize="none" - tooltip="Element options" + tooltip={strings.getContextMenuTitle()} tooltipPosition="bottom" > {({ closePopover }: { closePopover: () => void }) => ( @@ -519,12 +531,12 @@ export class SidebarHeader extends Component { {showLayerControls ? this._renderLayoutControls() : null} - + @@ -535,7 +547,7 @@ export class SidebarHeader extends Component { {isModalVisible ? ( diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_config/workpad_config.js b/x-pack/legacy/plugins/canvas/public/components/workpad_config/workpad_config.js index f1073f896e120..4eb8a336d14bc 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_config/workpad_config.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_config/workpad_config.js @@ -23,6 +23,9 @@ import { EuiButton, } from '@elastic/eui'; import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants'; +import { ComponentStrings } from '../../../i18n'; + +const { WorkpadConfig: strings } = ComponentStrings; export class WorkpadConfig extends PureComponent { static propTypes = { @@ -57,7 +60,7 @@ export class WorkpadConfig extends PureComponent { size: { height: 842, width: 590 }, }, { - name: 'US Letter', + name: strings.getUSLetterButtonLabel(), size: { height: 792, width: 612 }, }, ]; @@ -65,7 +68,7 @@ export class WorkpadConfig extends PureComponent { return (
-

Workpad

+

{strings.getTitle()}

@@ -78,7 +81,7 @@ export class WorkpadConfig extends PureComponent { - + setSize({ width: Number(e.target.value), height: size.height })} @@ -88,18 +91,18 @@ export class WorkpadConfig extends PureComponent { - + - + setSize({ height: Number(e.target.value), width: size.width })} @@ -117,8 +120,8 @@ export class WorkpadConfig extends PureComponent { key={`page-size-badge-${i}`} color="hollow" onClick={() => setSize(badge.size)} - aria-label={`Preset Page Size: ${badge.name}`} - onClickAriaLabel={`Set page size to ${badge.name}`} + aria-label={strings.getPageSizeBadgeAriaLabel(badge.name)} + onClickAriaLabel={strings.getPageSizeBadgeOnClickAriaLabel(badge.name)} > {badge.name} @@ -132,19 +135,19 @@ export class WorkpadConfig extends PureComponent { className="canvasArg__accordion" buttonContent={ - Global CSS overrides + {strings.getGlobalCSSLabel()} } >
this.setState({ css: e.target.value })} @@ -152,7 +155,7 @@ export class WorkpadConfig extends PureComponent { /> setWorkpadCSS(css || DEFAULT_WORKPAD_CSS)}> - Apply stylesheet + {strings.getApplyStylesheetButtonLabel()}
From 67d026e77f6ab0b16af4fd7ce96c6f92483b5d39 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 4 Oct 2019 20:26:55 -0500 Subject: [PATCH 22/33] [Code] Code Integrator Component (#47180) * Add CodeIntegrator component * Abstracts shared ImportModal component * Uses placeholder data and callbacks in lieu of real integration --- .../components/admin_page/project_tab.tsx | 91 +++------------- .../components/editor/references_panel.scss | 3 + .../integrations/code_integrator.tsx | 56 ++++++++++ .../components/integrations/frame_header.tsx | 4 +- .../components/integrations/import_modal.tsx | 100 ++++++++++++++++++ .../public/components/integrations/index.tsx | 23 ++-- .../components/integrations/integrations.scss | 20 ++-- .../components/integrations/repo_selector.tsx | 82 ++++++++++++++ .../components/integrations/repo_title.tsx | 4 +- .../public/components/search_page/search.scss | 3 + x-pack/legacy/plugins/code/public/index.scss | 1 + 11 files changed, 292 insertions(+), 95 deletions(-) create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/search_page/search.scss diff --git a/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx b/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx index 56f518fc82aa4..3bca381d741d6 100644 --- a/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx +++ b/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx @@ -6,23 +6,13 @@ import { EuiButton, - EuiButtonEmpty, - EuiFieldText, EuiFlexGroup, EuiFlexItem, - EuiForm, EuiFormRow, EuiGlobalToastList, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiOverlayMask, EuiSpacer, EuiSuperSelect, EuiText, - EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -39,6 +29,7 @@ import { ToastType } from '../../reducers/repository_management'; import { isImportRepositoryURLInvalid } from '../../utils/url'; import { ProjectItem } from './project_item'; import { ProjectSettings } from './project_settings'; +import { ImportModal } from '../integrations/import_modal'; enum SortOptionsValue { AlphabeticalAsc = 'alphabetical_asc', @@ -169,70 +160,21 @@ class CodeProjectTab extends React.PureComponent { } }; - public updateIsInvalid = () => { - this.setState({ isInvalid: isImportRepositoryURLInvalid(this.state.repoURL) }); - }; - public renderImportModal = () => { - return ( - - - - - - - - - -

- -

-
- - - - - -
- - - - - - - - -
-
- ); + const { isInvalid, repoURL, showImportProjectModal } = this.state; + + if (showImportProjectModal) { + return ( + + ); + } }; public setSortOption = (value: string) => { @@ -242,7 +184,6 @@ class CodeProjectTab extends React.PureComponent { public render() { const { projects, status, toastMessage, showToast, toastType } = this.props; const projectsCount = projects.length; - const modal = this.state.showImportProjectModal && this.renderImportModal(); const sortedProjects = projects.sort(sortFunctionsFactory(status)[this.state.sortOption]); const repoList = sortedProjects.map((repo: Repository) => ( @@ -321,7 +262,7 @@ class CodeProjectTab extends React.PureComponent { {repoList} - {modal} + {this.renderImportModal()} {settings}
); diff --git a/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss b/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss index fc3df8bb79a45..13d1c45e61660 100644 --- a/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss +++ b/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss @@ -31,3 +31,6 @@ border-radius: $euiSizeXS $euiSizeXS 0 0; } +.referencesPanel__code-block { + margin-bottom: $euiSizeXL; +} diff --git a/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx b/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx new file mode 100644 index 0000000000000..7df7a976be69f --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx @@ -0,0 +1,56 @@ +/* + * 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 React, { useState } from 'react'; +import { EuiButtonEmpty, EuiPopover, EuiText } from '@elastic/eui'; + +import { RepoSelector } from './repo_selector'; + +interface Props { + onRepoSelect: (repo: string) => void; + onImportSuccess: (repo: string) => void; + repos: string[]; +} + +export const CodeIntegrator = ({ onRepoSelect, onImportSuccess, repos }: Props) => { + const [showSelector, setShowSelector] = useState(false); + + const handleClick = () => setShowSelector(true); + + const handleSelect = (codeId: string) => { + onRepoSelect(codeId); + setShowSelector(false); + // TODO: show success + }; + + const link = ( + + View in Code + + ); + + return ( + setShowSelector(false)} + > + +

No repository mapping found

+

+ We can't find the mapping between service and the source code. Select the repository or + import a new one. +

+
+ +
+ ); +}; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx index 0fa482f8bce36..c63faf75603da 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx @@ -18,7 +18,7 @@ export const FrameHeader = ({ onClick: () => void; }) => ( Last updated: 14 mins ago diff --git a/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx b/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx new file mode 100644 index 0000000000000..de526d4046e28 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx @@ -0,0 +1,100 @@ +/* + * 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 React, { ChangeEvent } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiForm, + EuiFormRow, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiOverlayMask, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +interface Props { + isInvalid: boolean; + isLoading: boolean; + onChange: (e: ChangeEvent) => void; + onClose: () => void; + onSubmit: () => void; + value: string; +} + +export const ImportModal = ({ + isInvalid, + isLoading, + onChange, + onClose, + onSubmit, + value, +}: Props) => ( + + + + + + + + + +

+ +

+
+ + + + + +
+ + + + + + + + +
+
+); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx index 5052b3502748d..25eeb6fbe1a2a 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx @@ -11,11 +11,17 @@ import { CodeBlock } from '../codeblock/codeblock'; import { history } from '../../utils/url'; import { FrameHeader } from './frame_header'; import { RepoTitle } from './repo_title'; +import { CodeIntegrator } from './code_integrator'; import { externalFileURI } from './helpers'; -import { frames, results } from './data'; +import { frames, Frame, repos, results } from './data'; + +const associateToService = (frame: Frame) => (repo: string) => + alert(`repo ${repo} associated with service ${JSON.stringify(frame)}`); + +const handleImport = (repo: string) => alert(`import done: ${repo}`); export const Integrations = () => ( -
+
{frames.map(frame => { const { fileName, lineNumber } = frame; const key = `${fileName}#L${lineNumber}`; @@ -27,7 +33,7 @@ export const Integrations = () => ( const fileUrl = externalFileURI(uri, filePath); return ( -
+
( } return ( -
+
- + {fileName} - at + at line {lineNumber} +
); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss index 24fe406cd0960..98330c4060826 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss +++ b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss @@ -1,43 +1,43 @@ -.integrations__container { +.codeIntegrations__container { padding: $euiSize; } -.integrations__frame { +.codeIntegrations__frame { margin: $euiSizeS 0; } -.integrations__code { +.codeIntegrations__code { @include euiCodeFont; } -.integrations__link--external { +.codeIntegrations__link--external { margin-left: $euiSizeS; } -.integrations__preposition { +.codeIntegrations__preposition { margin: 0 $euiSizeS; color: $euiColorMediumShade; } -.integrations__button-icon { +.codeIntegrations__button-icon { padding: $euiSizeXS; background-color: $euiColorLightestShade; border: 1px solid $euiColorLightShade; } -.integrations__snippet-info { +.codeIntegrations__snippet-info { margin-bottom: $euiSizeS; } -.integrations__snippet-title { +.codeIntegrations__snippet-title { margin-bottom: $euiSizeS; } -.integrations__text--bold { +.codeIntegrations__text--bold { font-weight: $euiFontWeightBold; } -.integrations__popover { +.codeIntegrations__popover { margin-bottom: 1rem; width: 300px; } diff --git a/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx b/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx new file mode 100644 index 0000000000000..8aeba2b600ed2 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx @@ -0,0 +1,82 @@ +/* + * 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 React, { useState } from 'react'; +import { EuiButton, EuiSelect } from '@elastic/eui'; +import { ImportModal } from './import_modal'; +import { isImportRepositoryURLInvalid } from '../../utils/url'; + +interface Props { + onSelect: (repo: string) => void; + onImport: (repo: string) => void; + repos: string[]; +} + +const placeHolderOption = { value: 'select_new', text: 'Select' }; +const importNewOption = { value: 'import_new', text: 'Import new' }; +const importStub: (repo: string) => Promise = repo => + new Promise(resolve => setTimeout(() => resolve(repo), 5000)); + +export const RepoSelector = ({ onImport, onSelect, repos: _repos }: Props) => { + const [selectedValue, setSelectedValue] = useState(placeHolderOption.value); + const [newRepo, setNewRepo] = useState(''); + const [isInvalid, setIsInvalid] = useState(false); + const [showModal, setShowModal] = useState(false); + + const repos = newRepo ? [..._repos, newRepo] : _repos; + const selectedRepo = repos.find(repo => repo === selectedValue); + + const options = [ + placeHolderOption, + ...repos.map(repo => ({ value: repo, text: repo })), + importNewOption, + ]; + + const handleNewRepoChange = ({ target: { value } }: React.ChangeEvent) => { + setIsInvalid(isImportRepositoryURLInvalid(value)); + setNewRepo(value); + }; + + const handleChange = ({ target: { value } }: React.ChangeEvent) => { + setSelectedValue(value); + + if (value === 'import_new') { + setShowModal(true); + } + }; + + const handleSave = () => selectedRepo && onSelect(selectedRepo); + + const handleImportSubmit = () => { + setSelectedValue(newRepo); + importStub(newRepo).then(onImport); + setShowModal(false); + }; + + const handleClose = () => { + setSelectedValue(placeHolderOption.value); + setShowModal(false); + }; + + return ( + <> + + + Save Mapping + + {showModal && ( + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx index 2ed3d529b699b..f3577d5f64266 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx @@ -14,9 +14,9 @@ export const RepoTitle = ({ uri }: { uri: string }) => { const name = RepositoryUtils.repoNameFromUri(uri); return ( - + {org}/ - {name} + {name} ); }; diff --git a/x-pack/legacy/plugins/code/public/components/search_page/search.scss b/x-pack/legacy/plugins/code/public/components/search_page/search.scss new file mode 100644 index 0000000000000..febad588af3c6 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/search_page/search.scss @@ -0,0 +1,3 @@ +.codeResult__code-block { + margin-bottom: $euiSizeXL; +} diff --git a/x-pack/legacy/plugins/code/public/index.scss b/x-pack/legacy/plugins/code/public/index.scss index 6d996a80f283f..43768f7758ea1 100644 --- a/x-pack/legacy/plugins/code/public/index.scss +++ b/x-pack/legacy/plugins/code/public/index.scss @@ -4,6 +4,7 @@ @import "./monaco/override_monaco_styles.scss"; @import "./components/diff_page/diff.scss"; @import "./components/main/main.scss"; +@import "./components/search_page/search.scss"; @import "./components/integrations/integrations.scss"; // TODO: Cleanup everything above this line From fc53d4efb37bad8c9237c2cfee9884fa811b5047 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 05:29:50 -0700 Subject: [PATCH 23/33] Update dependency tar-fs to ^1.16.3 (#47341) --- packages/kbn-test/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 6891f01bae2a9..d02b2cf41d3f8 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -21,7 +21,7 @@ "getopts": "^2.2.4", "glob": "^7.1.2", "rxjs": "^6.2.1", - "tar-fs": "^1.16.2", + "tar-fs": "^1.16.3", "tmp": "^0.1.0", "zlib": "^1.0.5" } diff --git a/yarn.lock b/yarn.lock index 9361ab685e757..7a80da2c9dfcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26845,7 +26845,7 @@ tape@^4.5.1: string.prototype.trim "~1.1.2" through "~2.3.8" -tar-fs@^1.16.2, tar-fs@^1.16.3: +tar-fs@^1.16.3: version "1.16.3" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== From 23650a0c1fee57a9f1c92c4ad185652a83d82169 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 05:30:50 -0700 Subject: [PATCH 24/33] Update dependency @elastic/elasticsearch to ^7.4.0 (#47338) --- packages/kbn-es/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index 7c0b960d18093..5521d57c22e86 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "private": true, "dependencies": { - "@elastic/elasticsearch": "^7.3.0", + "@elastic/elasticsearch": "^7.4.0", "@kbn/dev-utils": "1.0.0", "abort-controller": "^2.0.3", "chalk": "^2.4.2", diff --git a/yarn.lock b/yarn.lock index 7a80da2c9dfcb..5396bc72e51c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1106,10 +1106,10 @@ parse-gitignore "1.0.1" vscode-languageserver "^5.2.1" -"@elastic/elasticsearch@^7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.3.0.tgz#d62508cc03e91dd0676914a50af6500b45bfb199" - integrity sha512-CA8V4txIS+BPZg37ZVtOi5mN2xnXYAeQUCvgkjdtc2CzTd5pJrjdPzdmaDDATNc8nhlHMrqxMZZmpKD3OUkjAg== +"@elastic/elasticsearch@^7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.4.0.tgz#57f4066acf25e9d4e9b4f6376088433aae6f25d4" + integrity sha512-HpEKHH6mHQRvea3lw4NNJw9ZUS1KmkpwWKHucaHi1svDn+/fEAwY0wD8egL1vZJo4ZmWfCQMjVqGL+Hoy1HYRw== dependencies: debug "^4.1.1" decompress-response "^4.2.0" From fd14e201c0126ea7c2473797fe641e7fa3153a69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 05:31:25 -0700 Subject: [PATCH 25/33] Update dependency @types/puppeteer to ^1.20.1 (#47339) --- x-pack/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/package.json b/x-pack/package.json index 7976dd13a07ff..a673f09c980e6 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -82,7 +82,7 @@ "@types/pngjs": "^3.3.2", "@types/prop-types": "^15.5.3", "@types/proper-lockfile": "^3.0.1", - "@types/puppeteer": "^1.19.0", + "@types/puppeteer": "^1.20.1", "@types/react": "^16.8.0", "@types/react-dom": "^16.8.0", "@types/react-redux": "^6.0.6", diff --git a/yarn.lock b/yarn.lock index 5396bc72e51c5..e795d5ab8a6e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3821,10 +3821,10 @@ resolved "https://registry.yarnpkg.com/@types/proper-lockfile/-/proper-lockfile-3.0.1.tgz#dd770a2abce3adbcce3bd1ed892ce2f5f17fbc86" integrity sha512-ODOjqxmaNs0Zkij+BJovsNJRSX7BJrr681o8ZnNTNIcTermvVFzLpz/XFtfg3vNrlPVTJY1l4e9h2LvHoxC1lg== -"@types/puppeteer@^1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.19.0.tgz#59f0050bae019cee7c3af2bb840a25892a3078b6" - integrity sha512-Db9LWOuTm2bR/qgPE7PQCmnsCQ6flHdULuIDWTks8YdQ/SGHKg5WGWG54gl0734NDKCTF5MbqAp2qWuvBiyQ3Q== +"@types/puppeteer@^1.20.1": + version "1.20.1" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.20.1.tgz#0aba5ae3d290daa91cd3ba9f66ba5e9fba3499cc" + integrity sha512-F91CqYDHETg3pQfIPNBNZKmi7R1xS1y4yycOYX7o6Xk16KF+IV+9LqTmVuG+FIxw/53/JEy94zKjjGjg92V6bg== dependencies: "@types/node" "*" From 8fcae77856c21513c62cc7eb22a993123ff93ee3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 05:32:24 -0700 Subject: [PATCH 26/33] Update dependency prettier to ^1.18.2 (#47340) --- packages/kbn-pm/package.json | 2 +- packages/kbn-spec-to-console/package.json | 2 +- yarn.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 9f80600fddcb1..fd2d20e66fd48 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -51,7 +51,7 @@ "log-symbols": "^2.2.0", "ncp": "^2.0.0", "ora": "^1.4.0", - "prettier": "^1.14.3", + "prettier": "^1.18.2", "read-pkg": "^5.2.0", "rxjs": "^6.2.1", "spawn-sync": "^1.0.15", diff --git a/packages/kbn-spec-to-console/package.json b/packages/kbn-spec-to-console/package.json index 674a801e92861..2e5f897894a90 100644 --- a/packages/kbn-spec-to-console/package.json +++ b/packages/kbn-spec-to-console/package.json @@ -18,7 +18,7 @@ "homepage": "https://github.com/jbudz/spec-to-console#readme", "devDependencies": { "jest": "^24.9.0", - "prettier": "^1.14.3" + "prettier": "^1.18.2" }, "dependencies": { "commander": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index e795d5ab8a6e6..d93f6c60bf0cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22019,7 +22019,7 @@ prettier@1.16.4: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== -prettier@1.18.2, prettier@^1.13.7, prettier@^1.14.3: +prettier@1.18.2, prettier@^1.13.7, prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== From 445fa434d51b8ec4c01bd565d4a26549270ff1c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 07:53:47 -0700 Subject: [PATCH 27/33] Update gulp related packages (major) (#46665) * Update gulp related packages * ts-ify and gulp4-ify x-pack tasks, remove unused canvas tasks * remove unnecessary gulp.TaskFunction usage * fix old references * update renovate config * move constants into helpers directory * typo * compact tasks a bit, remove unnecessary paths * fix build directories * deprecate testonly task * rather than justifying an unjustifiable ts-ignore, ts-ify the imported module * update renovate config * update browser download tests to mock axios * add root index.d.ts to tsconfig * export BrowserType * remove unnecessary `@ts-ignore` * use consistent casing --- package.json | 1 + packages/kbn-plugin-helpers/lib/index.d.ts | 26 + packages/kbn-plugin-helpers/package.json | 2 +- packages/kbn-plugin-helpers/tsconfig.json | 4 + packages/kbn-test/index.d.ts | 20 + packages/kbn-test/src/{index.js => index.ts} | 8 + .../kbn-test/src/mocha/{index.js => index.ts} | 5 + packages/kbn-test/tsconfig.json | 4 +- renovate.json5 | 24 + ...urce.js => generate_notice_from_source.ts} | 37 +- src/dev/notice/{index.js => index.ts} | 1 + x-pack/gulpfile.js | 44 +- .../plugins/canvas/tasks/helpers/babelhook.js | 51 -- .../plugins/canvas/tasks/helpers/dom_setup.js | 23 - .../canvas/tasks/helpers/enzyme_setup.js | 10 - x-pack/legacy/plugins/canvas/tasks/index.js | 11 - .../canvas/tasks/mocks/absoluteToParsedUrl.js | 10 - .../legacy/plugins/canvas/tasks/mocks/noop.js | 7 - .../plugins/canvas/tasks/mocks/stateStore.js | 13 - x-pack/legacy/plugins/canvas/tasks/test.js | 66 -- .../browsers/{browsers.js => browsers.ts} | 2 + .../browsers/create_browser_driver_factory.ts | 6 +- ...s => default_chromium_sandbox_disabled.ts} | 2 +- .../server/browsers/download/checksum.ts | 1 - .../browsers/download/{clean.js => clean.ts} | 7 +- .../{download.test.js => download.test.ts} | 66 +- .../download/{download.js => download.ts} | 19 +- ...ure_downloaded.js => ensure_downloaded.ts} | 35 +- .../server/browsers/download/index.js | 10 - .../server/browsers/download/index.ts} | 4 +- .../browsers/download/{util.js => util.ts} | 17 +- .../server/browsers/{index.js => index.ts} | 0 x-pack/package.json | 9 +- x-pack/tasks/build.js | 36 - x-pack/tasks/build.ts | 70 ++ x-pack/tasks/clean.js | 22 - x-pack/tasks/{dev.js => dev.ts} | 12 +- x-pack/tasks/helpers/build_version.js | 29 - x-pack/tasks/helpers/build_version.ts | 12 + x-pack/tasks/helpers/flags.ts | 70 ++ x-pack/tasks/helpers/get_plugins.js | 46 - .../helpers/{git_info.js => git_info.ts} | 20 +- x-pack/tasks/helpers/globs.js | 49 - x-pack/tasks/helpers/pkg.ts | 33 + x-pack/tasks/prepare.js | 18 - .../mocks/uiStorage.js => tasks/prepare.ts} | 12 +- x-pack/tasks/report.js | 21 - x-pack/tasks/test.js | 54 -- x-pack/tasks/test.ts | 58 ++ x-pack/tsconfig.json | 3 +- yarn.lock | 846 ++++++++---------- 51 files changed, 844 insertions(+), 1112 deletions(-) create mode 100644 packages/kbn-plugin-helpers/lib/index.d.ts create mode 100644 packages/kbn-plugin-helpers/tsconfig.json create mode 100644 packages/kbn-test/index.d.ts rename packages/kbn-test/src/{index.js => index.ts} (87%) rename packages/kbn-test/src/mocha/{index.js => index.ts} (88%) rename src/dev/notice/{generate_notice_from_source.js => generate_notice_from_source.ts} (83%) rename src/dev/notice/{index.js => index.ts} (97%) delete mode 100644 x-pack/legacy/plugins/canvas/tasks/helpers/babelhook.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/helpers/dom_setup.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/helpers/enzyme_setup.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/index.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/mocks/absoluteToParsedUrl.js delete mode 100755 x-pack/legacy/plugins/canvas/tasks/mocks/noop.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/mocks/stateStore.js delete mode 100644 x-pack/legacy/plugins/canvas/tasks/test.js rename x-pack/legacy/plugins/reporting/server/browsers/{browsers.js => browsers.ts} (85%) rename x-pack/legacy/plugins/reporting/server/browsers/{default_chromium_sandbox_disabled.js => default_chromium_sandbox_disabled.ts} (94%) rename x-pack/legacy/plugins/reporting/server/browsers/download/{clean.js => clean.ts} (85%) rename x-pack/legacy/plugins/reporting/server/browsers/download/{download.test.js => download.test.ts} (55%) rename x-pack/legacy/plugins/reporting/server/browsers/download/{download.js => download.ts} (68%) rename x-pack/legacy/plugins/reporting/server/browsers/download/{ensure_downloaded.js => ensure_downloaded.ts} (68%) delete mode 100644 x-pack/legacy/plugins/reporting/server/browsers/download/index.js rename x-pack/{tasks/helpers/get_flags.js => legacy/plugins/reporting/server/browsers/download/index.ts} (72%) rename x-pack/legacy/plugins/reporting/server/browsers/download/{util.js => util.ts} (65%) rename x-pack/legacy/plugins/reporting/server/browsers/{index.js => index.ts} (100%) delete mode 100644 x-pack/tasks/build.js create mode 100644 x-pack/tasks/build.ts delete mode 100644 x-pack/tasks/clean.js rename x-pack/tasks/{dev.js => dev.ts} (55%) delete mode 100644 x-pack/tasks/helpers/build_version.js create mode 100644 x-pack/tasks/helpers/build_version.ts create mode 100644 x-pack/tasks/helpers/flags.ts delete mode 100644 x-pack/tasks/helpers/get_plugins.js rename x-pack/tasks/helpers/{git_info.js => git_info.ts} (50%) delete mode 100644 x-pack/tasks/helpers/globs.js create mode 100644 x-pack/tasks/helpers/pkg.ts delete mode 100644 x-pack/tasks/prepare.js rename x-pack/{legacy/plugins/canvas/tasks/mocks/uiStorage.js => tasks/prepare.ts} (58%) delete mode 100644 x-pack/tasks/report.js delete mode 100644 x-pack/tasks/test.js create mode 100644 x-pack/tasks/test.ts diff --git a/package.json b/package.json index 28dd1f1b37bb1..f89a2f15136d5 100644 --- a/package.json +++ b/package.json @@ -346,6 +346,7 @@ "@types/supertest": "^2.0.5", "@types/type-detect": "^4.0.1", "@types/uuid": "^3.4.4", + "@types/vinyl-fs": "^2.4.11", "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "1.13.0", "@typescript-eslint/parser": "1.13.0", diff --git a/packages/kbn-plugin-helpers/lib/index.d.ts b/packages/kbn-plugin-helpers/lib/index.d.ts new file mode 100644 index 0000000000000..1515bf6b84bfb --- /dev/null +++ b/packages/kbn-plugin-helpers/lib/index.d.ts @@ -0,0 +1,26 @@ +/* + * 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 function babelRegister(): void; +export function resolveKibanaPath(path: string): string; +export function readFtrConfigFile(path: string): any; +export function run( + task: 'build' | 'start' | 'testAll' | 'testBrowser' | 'testServer' | 'postinstall', + options: any +): Promise; diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 47af4e288afa3..d6818c4602ef4 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -21,7 +21,7 @@ "globby": "^8.0.1", "gulp-babel": "^8.0.0", "gulp-rename": "1.4.0", - "gulp-zip": "4.2.0", + "gulp-zip": "5.0.0", "inquirer": "^1.2.2", "minimatch": "^3.0.4", "node-sass": "^4.9.4", diff --git a/packages/kbn-plugin-helpers/tsconfig.json b/packages/kbn-plugin-helpers/tsconfig.json new file mode 100644 index 0000000000000..f5559aa7290c5 --- /dev/null +++ b/packages/kbn-plugin-helpers/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["lib/index.d.ts"] +} diff --git a/packages/kbn-test/index.d.ts b/packages/kbn-test/index.d.ts new file mode 100644 index 0000000000000..aa55df9215c2f --- /dev/null +++ b/packages/kbn-test/index.d.ts @@ -0,0 +1,20 @@ +/* + * 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 * from './src/index'; diff --git a/packages/kbn-test/src/index.js b/packages/kbn-test/src/index.ts similarity index 87% rename from packages/kbn-test/src/index.js rename to packages/kbn-test/src/index.ts index 8313d0c7334be..9167fd190dc86 100644 --- a/packages/kbn-test/src/index.js +++ b/packages/kbn-test/src/index.ts @@ -17,20 +17,28 @@ * under the License. */ +// @ts-ignore not typed yet export { runTestsCli, startServersCli } from './functional_tests/cli'; +// @ts-ignore not typed yet export { runTests, startServers } from './functional_tests/tasks'; +// @ts-ignore not typed yet export { OPTIMIZE_BUNDLE_DIR, KIBANA_ROOT } from './functional_tests/lib/paths'; +// @ts-ignore not typed yet export { esTestConfig, createEsTestCluster } from './es'; +// @ts-ignore not typed yet export { kbnTestConfig, kibanaServerTestUser, kibanaTestUser, adminTestUser } from './kbn'; +// @ts-ignore not typed yet export { setupUsers, DEFAULT_SUPERUSER_PASS } from './functional_tests/lib/auth'; +// @ts-ignore not typed yet export { readConfigFile } from './functional_test_runner/lib/config/read_config_file'; +// @ts-ignore not typed yet export { runFtrCli } from './functional_test_runner/cli'; export { diff --git a/packages/kbn-test/src/mocha/index.js b/packages/kbn-test/src/mocha/index.ts similarity index 88% rename from packages/kbn-test/src/mocha/index.js rename to packages/kbn-test/src/mocha/index.ts index f18282d4a8b13..99c0f6f4230b7 100644 --- a/packages/kbn-test/src/mocha/index.js +++ b/packages/kbn-test/src/mocha/index.ts @@ -17,8 +17,13 @@ * under the License. */ +// @ts-ignore not typed yet export { createAutoJUnitReporter } from './auto_junit_reporter'; +// @ts-ignore not typed yet export { setupJUnitReportGeneration } from './junit_report_generation'; +// @ts-ignore not typed yet export { runMochaCli } from './run_mocha_cli'; +// @ts-ignore not typed yet export { recordLog, snapshotLogsForRunnable } from './log_cache'; +// @ts-ignore not typed yet export { escapeCdata } from './xml'; diff --git a/packages/kbn-test/tsconfig.json b/packages/kbn-test/tsconfig.json index 825fddc0bdcc7..fdb53de52687b 100644 --- a/packages/kbn-test/tsconfig.json +++ b/packages/kbn-test/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "include": [ "types/**/*", - "src/functional_test_runner/**/*", - "src/mocha/xml.ts" + "src/**/*", + "index.d.ts" ] } diff --git a/renovate.json5 b/renovate.json5 index 5b6989ae61b3b..e8fd7b6ceda50 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -607,6 +607,14 @@ '@types/uuid', ], }, + { + groupSlug: 'vinyl-fs', + groupName: 'vinyl-fs related packages', + packageNames: [ + 'vinyl-fs', + '@types/vinyl-fs', + ], + }, { groupSlug: 'zen-observable', groupName: 'zen-observable related packages', @@ -639,6 +647,14 @@ '@types/color', ], }, + { + groupSlug: 'fancy-log', + groupName: 'fancy-log related packages', + packageNames: [ + 'fancy-log', + '@types/fancy-log', + ], + }, { groupSlug: 'file-saver', groupName: 'file-saver related packages', @@ -647,6 +663,14 @@ '@types/file-saver', ], }, + { + groupSlug: 'getos', + groupName: 'getos related packages', + packageNames: [ + 'getos', + '@types/getos', + ], + }, { groupSlug: 'git-url-parse', groupName: 'git-url-parse related packages', diff --git a/src/dev/notice/generate_notice_from_source.js b/src/dev/notice/generate_notice_from_source.ts similarity index 83% rename from src/dev/notice/generate_notice_from_source.js rename to src/dev/notice/generate_notice_from_source.ts index 75bc7c8cb2f19..6dd47db929f45 100644 --- a/src/dev/notice/generate_notice_from_source.js +++ b/src/dev/notice/generate_notice_from_source.ts @@ -18,25 +18,30 @@ */ import vfs from 'vinyl-fs'; +import { ToolingLog } from '@kbn/dev-utils'; const NOTICE_COMMENT_RE = /\/\*[\s\n\*]*@notice([\w\W]+?)\*\//g; const NEWLINE_RE = /\r?\n/g; +interface Options { + /** + * Name to print at the top of the notice + */ + productName: string; + /** + * absolute path to the repo to search for @notice comments + */ + directory: string; + log: ToolingLog; +} + /** * Generates the text for the NOTICE.txt file at the root of the * repo which details the licenses for code that is copied/vendored * into the repository. - * - * @param {Object} options - * @property {string} options.productName Name to print at the top of the notice - * @property {ToolingLog} options.log - * @property {string} options.directory absolute path to the repo to search for @notice comments - * @return {string} */ -export async function generateNoticeFromSource({ productName, directory, log }) { - const globs = [ - '**/*.{js,less,css,ts}', - ]; +export async function generateNoticeFromSource({ productName, directory, log }: Options) { + const globs = ['**/*.{js,less,css,ts}']; const options = { cwd: directory, @@ -46,7 +51,7 @@ export async function generateNoticeFromSource({ productName, directory, log }) 'packages/*/{node_modules,build,target,dist}/**', 'x-pack/{node_modules,build,target,dist,data}/**', 'x-pack/packages/*/{node_modules,build,target,dist}/**', - ] + ], }; log.debug('vfs.src globs', globs); @@ -54,10 +59,10 @@ export async function generateNoticeFromSource({ productName, directory, log }) log.info(`Searching ${directory} for multi-line comments starting with @notice`); const files = vfs.src(globs, options); - const noticeComments = []; + const noticeComments: string[] = []; await new Promise((resolve, reject) => { files - .on('data', (file) => { + .on('data', file => { log.verbose(`Checking for @notice comments in ${file.relative}`); const source = file.contents.toString('utf8'); @@ -75,19 +80,19 @@ export async function generateNoticeFromSource({ productName, directory, log }) let noticeText = ''; noticeText += `${productName}\n`; - noticeText += `Copyright 2012-${(new Date()).getUTCFullYear()} Elasticsearch B.V.\n`; + noticeText += `Copyright 2012-${new Date().getUTCFullYear()} Elasticsearch B.V.\n`; for (const comment of noticeComments.sort()) { noticeText += '\n---\n'; noticeText += comment .split(NEWLINE_RE) - .map(line => ( + .map(line => line // trim whitespace .trim() // trim leading * and a single space .replace(/(^\* ?)/, '') - )) + ) .join('\n') .trim(); noticeText += '\n'; diff --git a/src/dev/notice/index.js b/src/dev/notice/index.ts similarity index 97% rename from src/dev/notice/index.js rename to src/dev/notice/index.ts index 17fbd20c22061..9f3fd61a9831e 100644 --- a/src/dev/notice/index.js +++ b/src/dev/notice/index.ts @@ -18,4 +18,5 @@ */ export { generateNoticeFromSource } from './generate_notice_from_source'; +// @ts-ignore not typed yet export { generateBuildNoticeText } from './generate_build_notice_text'; diff --git a/x-pack/gulpfile.js b/x-pack/gulpfile.js index edf8d3c59c3b7..74e24692f59f6 100644 --- a/x-pack/gulpfile.js +++ b/x-pack/gulpfile.js @@ -4,36 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -require('@kbn/plugin-helpers').babelRegister(); -require('dotenv').config({ silent: true }); +require('../src/setup_node_env'); -const path = require('path'); -const gulp = require('gulp'); -const mocha = require('gulp-mocha'); -const multiProcess = require('gulp-multi-process'); -const fancyLog = require('fancy-log'); -const pkg = require('./package.json'); +const { buildTask } = require('./tasks/build'); +const { devTask } = require('./tasks/dev'); +const { testTask, testBrowserTask, testBrowserDevTask, testServerTask } = require('./tasks/test'); +const { prepareTask } = require('./tasks/prepare'); -const buildDir = path.resolve(__dirname, 'build'); -const buildTarget = path.resolve(buildDir, 'plugin'); -const packageDir = path.resolve(buildDir, 'distributions'); -const coverageDir = path.resolve(__dirname, 'coverage'); - -const gulpHelpers = { - buildDir, - buildTarget, - coverageDir, - log: fancyLog, - mocha, - multiProcess, - packageDir, - pkg, +// export the tasks that are runnable from the CLI +module.exports = { + build: buildTask, + dev: devTask, + prepare: prepareTask, + test: testTask, + testserver: testServerTask, + testbrowser: testBrowserTask, + 'testbrowser-dev': testBrowserDevTask, }; - -require('./tasks/build')(gulp, gulpHelpers); -require('./tasks/clean')(gulp, gulpHelpers); -require('./tasks/dev')(gulp, gulpHelpers); -require('./tasks/prepare')(gulp, gulpHelpers); -require('./tasks/report')(gulp, gulpHelpers); -require('./tasks/test')(gulp, gulpHelpers); -require('./legacy/plugins/canvas/tasks')(gulp, gulpHelpers); diff --git a/x-pack/legacy/plugins/canvas/tasks/helpers/babelhook.js b/x-pack/legacy/plugins/canvas/tasks/helpers/babelhook.js deleted file mode 100644 index dea18db918fcd..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/helpers/babelhook.js +++ /dev/null @@ -1,51 +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. - */ - -const { resolve } = require('path'); -const register = require('@babel/register'); -const options = { - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/node_preset')], - sourceMaps: false, - plugins: [ - [ - 'mock-imports', - [ - { - pattern: 'ui/chrome', - location: resolve(__dirname, '..', 'mocks', 'uiChrome'), - }, - { - pattern: 'ui/notify', - location: resolve(__dirname, '..', 'mocks', 'uiNotify'), - }, - { - pattern: 'ui/storage', - location: resolve(__dirname, '..', 'mocks', 'uiStorage'), - }, - { - pattern: 'ui/url/absolute_to_parsed_url', - location: resolve(__dirname, '..', 'mocks', 'absoluteToParsedUrl'), - }, - { - // ugly hack so that importing non-js files works, required for the function docs - pattern: '.(less|png|svg)$', - location: resolve(__dirname, '..', 'mocks', 'noop'), - }, - { - pattern: 'plugins/canvas/apps', - location: resolve(__dirname, '..', 'mocks', 'noop'), - }, - { - pattern: '/state/store', - location: resolve(__dirname, '..', 'mocks', 'stateStore'), - }, - ], - ], - ], -}; - -register(options); diff --git a/x-pack/legacy/plugins/canvas/tasks/helpers/dom_setup.js b/x-pack/legacy/plugins/canvas/tasks/helpers/dom_setup.js deleted file mode 100644 index bbc8cc52d6a68..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/helpers/dom_setup.js +++ /dev/null @@ -1,23 +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 { JSDOM } from 'jsdom'; -import { APP_ROUTE } from '../../common/lib/constants'; -import chrome from '../mocks/uiChrome'; - -const basePath = chrome.getBasePath(); -const basename = `${basePath}${APP_ROUTE}`; - -const { window } = new JSDOM('', { - url: `http://localhost:5601/${basename}`, - pretendToBeVisual: true, -}); - -global.window = window; -global.document = window.document; -global.navigator = window.navigator; -global.requestAnimationFrame = window.requestAnimationFrame; -global.HTMLElement = window.HTMLElement; diff --git a/x-pack/legacy/plugins/canvas/tasks/helpers/enzyme_setup.js b/x-pack/legacy/plugins/canvas/tasks/helpers/enzyme_setup.js deleted file mode 100644 index 290e3d220aa4b..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/helpers/enzyme_setup.js +++ /dev/null @@ -1,10 +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 { configure } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; - -configure({ adapter: new Adapter() }); diff --git a/x-pack/legacy/plugins/canvas/tasks/index.js b/x-pack/legacy/plugins/canvas/tasks/index.js deleted file mode 100644 index 48ff275877836..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/index.js +++ /dev/null @@ -1,11 +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 test from './test'; - -export default function canvasTasks(gulp, gulpHelpers) { - test(gulp, gulpHelpers); -} diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/absoluteToParsedUrl.js b/x-pack/legacy/plugins/canvas/tasks/mocks/absoluteToParsedUrl.js deleted file mode 100644 index d73885ef0cc28..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/mocks/absoluteToParsedUrl.js +++ /dev/null @@ -1,10 +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. - */ - -export const absoluteToParsedUrl = () => { - getAbsoluteUrl: () => - 'http://localhost:5601/kbp/app/canvas#/workpad/workpad-24d56dad-ae70-42b8-9ef1-c5350ecd426c/page/1'; -}; // noop diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/noop.js b/x-pack/legacy/plugins/canvas/tasks/mocks/noop.js deleted file mode 100755 index 8d6abb810be9b..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/mocks/noop.js +++ /dev/null @@ -1,7 +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. - */ - -export default function() {} diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/stateStore.js b/x-pack/legacy/plugins/canvas/tasks/mocks/stateStore.js deleted file mode 100644 index 9d3df08fffe09..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/mocks/stateStore.js +++ /dev/null @@ -1,13 +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. - */ - -export function getState() { - return { - assets: { - yay: { value: 'here is your image' }, - }, - }; -} diff --git a/x-pack/legacy/plugins/canvas/tasks/test.js b/x-pack/legacy/plugins/canvas/tasks/test.js deleted file mode 100644 index 9857e9c774e06..0000000000000 --- a/x-pack/legacy/plugins/canvas/tasks/test.js +++ /dev/null @@ -1,66 +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, join } from 'path'; - -export default function testTasks(gulp, { mocha }) { - const canvasRoot = resolve(__dirname, '..'); - - function runMocha(globs, { withEnzyme = false, withDOM = false } = {}) { - const requires = [join(canvasRoot, 'tasks/helpers/babelhook')]; - - if (withDOM) { - requires.push(join(canvasRoot, 'tasks/helpers/dom_setup')); - } - if (withEnzyme) { - requires.push(join(canvasRoot, 'tasks/helpers/enzyme_setup')); - } - - return gulp.src(globs, { read: false }).pipe( - mocha({ - ui: 'bdd', - require: requires, - }) - ); - } - - const getTestGlobs = rootPath => [ - join(canvasRoot, `${rootPath}/**/__tests__/**/*.js`), - join(canvasRoot, `!${rootPath}/**/__tests__/fixtures/**/*.js`), - ]; - - const getRootGlobs = rootPath => [join(canvasRoot, `${rootPath}/**/*.js`)]; - - gulp.task('canvas:test:common', () => { - return runMocha(getTestGlobs('common'), { withDOM: true }); - }); - - gulp.task('canvas:test:server', () => { - return runMocha(getTestGlobs('server')); - }); - - gulp.task('canvas:test:browser', () => { - return runMocha(getTestGlobs('public'), { withEnzyme: true, withDOM: true }); - }); - - gulp.task('canvas:test:plugins', () => { - return runMocha(getTestGlobs('canvas_plugin_src')); - }); - - gulp.task('canvas:test', [ - 'canvas:test:plugins', - 'canvas:test:common', - 'canvas:test:server', - 'canvas:test:browser', - ]); - - gulp.task('canvas:test:dev', () => { - gulp.watch(getRootGlobs('common'), ['canvas:test:common']); - gulp.watch(getRootGlobs('server'), ['canvas:test:server']); - gulp.watch(getRootGlobs('public'), ['canvas:test:browser']); - gulp.watch(getRootGlobs('canvas_plugin_src'), ['canvas:test:plugins']); - }); -} diff --git a/x-pack/legacy/plugins/reporting/server/browsers/browsers.js b/x-pack/legacy/plugins/reporting/server/browsers/browsers.ts similarity index 85% rename from x-pack/legacy/plugins/reporting/server/browsers/browsers.js rename to x-pack/legacy/plugins/reporting/server/browsers/browsers.ts index 75f06de9a2a45..ff9dece504ea6 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/browsers.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/browsers.ts @@ -6,6 +6,8 @@ import * as chromium from './chromium'; +export type BrowserType = keyof typeof BROWSERS_BY_TYPE; + export const BROWSERS_BY_TYPE = { chromium, }; diff --git a/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts b/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts index ada20914eccb5..cf3e2fe25b4d6 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { BROWSERS_BY_TYPE } from './browsers'; -// @ts-ignore +import { BROWSERS_BY_TYPE, BrowserType } from './browsers'; import { ensureBrowserDownloaded } from './download'; import { installBrowser } from './install'; import { LevelLogger } from '../lib/level_logger'; @@ -22,7 +20,7 @@ export async function createBrowserDriverFactory( const DATA_DIR = config.get('path.data'); const CAPTURE_CONFIG = config.get('xpack.reporting.capture'); - const BROWSER_TYPE = CAPTURE_CONFIG.browser.type; + const BROWSER_TYPE: BrowserType = CAPTURE_CONFIG.browser.type; const BROWSER_AUTO_DOWNLOAD = CAPTURE_CONFIG.browser.autoDownload; const BROWSER_CONFIG = CAPTURE_CONFIG.browser[BROWSER_TYPE]; const REPORTING_TIMEOUT = config.get('xpack.reporting.queue.timeout'); diff --git a/x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.js b/x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.ts similarity index 94% rename from x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.js rename to x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.ts index 952d6a99c4e4b..a21a4b33722ff 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/default_chromium_sandbox_disabled.ts @@ -9,7 +9,7 @@ import { promisify } from 'util'; const getos = promisify(getosSync); -const distroSupportsUnprivilegedUsernamespaces = (distro) => { +const distroSupportsUnprivilegedUsernamespaces = (distro: string) => { // Debian 7 and 8 don't support usernamespaces by default // this should be reevaluated when Debian 9 is available if (distro.toLowerCase() === 'debian') { diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/checksum.ts b/x-pack/legacy/plugins/reporting/server/browsers/download/checksum.ts index b5e50fcebd453..fd68a8e356e10 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/checksum.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/checksum.ts @@ -7,7 +7,6 @@ import { createHash } from 'crypto'; import { createReadStream } from 'fs'; -// @ts-ignore import { readableEnd } from './util'; export async function md5(path: string) { diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/clean.js b/x-pack/legacy/plugins/reporting/server/browsers/download/clean.ts similarity index 85% rename from x-pack/legacy/plugins/reporting/server/browsers/download/clean.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/clean.ts index ad487311e12df..394b76d772531 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/clean.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/clean.ts @@ -14,12 +14,9 @@ import { log, asyncMap } from './util'; /** * Delete any file in the `dir` that is not in the expectedPaths - * @param {String} dir - * @param {Array} expectedPaths - * @return {Promise} */ -export async function clean(dir, expectedPaths) { - let filenames; +export async function clean(dir: string, expectedPaths: string[]) { + let filenames: string[]; try { filenames = await readdirSync(dir); } catch (error) { diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/download.test.js b/x-pack/legacy/plugins/reporting/server/browsers/download/download.test.ts similarity index 55% rename from x-pack/legacy/plugins/reporting/server/browsers/download/download.test.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/download.test.ts index 61b505703a751..901fc6ccf9b22 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/download.test.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/download.test.ts @@ -7,6 +7,7 @@ import { createHash } from 'crypto'; import { resolve as resolvePath } from 'path'; import { readFileSync } from 'fs'; +import { Readable } from 'stream'; import del from 'del'; import { download } from './download'; @@ -14,41 +15,27 @@ import { download } from './download'; const TEMP_DIR = resolvePath(__dirname, '__tmp__'); const TEMP_FILE = resolvePath(TEMP_DIR, 'foo/bar/download'); -jest.mock('request', () => { - let resp = ''; - const sinon = require('sinon'); - const Readable = require('stream').Readable; - const request = sinon.spy(function () { - return new Readable({ - read() { - if (resp instanceof Error) { - this.emit('error', resp); - return; - } +class ReadableOf extends Readable { + constructor(private readonly responseBody: string) { + super(); + } - this.push(resp.shift()); + _read() { + this.push(this.responseBody); + this.push(null); + } +} - if (resp.length === 0) { - this.push(null); - } - } - }); - }); - - request._setResponse = (chunks) => { - if (typeof chunks === 'string') { - chunks = chunks.split(''); - } - - resp = chunks; - }; - - return request; -}); +jest.mock('axios'); +const request: jest.Mock = jest.requireMock('axios').request; test('downloads the url to the path', async () => { const BODY = 'abdcefg'; - require('request')._setResponse(BODY); + request.mockImplementationOnce(async () => { + return { + data: new ReadableOf(BODY), + }; + }); await download('url', TEMP_FILE); expect(readFileSync(TEMP_FILE, 'utf8')).toEqual(BODY); @@ -56,18 +43,25 @@ test('downloads the url to the path', async () => { test('returns the md5 hex hash of the http body', async () => { const BODY = 'foobar'; - const HASH = createHash('md5').update(BODY).digest('hex'); - require('request')._setResponse(BODY); + const HASH = createHash('md5') + .update(BODY) + .digest('hex'); + request.mockImplementationOnce(async () => { + return { + data: new ReadableOf(BODY), + }; + }); const returned = await download('url', TEMP_FILE); expect(returned).toEqual(HASH); }); test('throws if request emits an error', async () => { - require('request')._setResponse(new Error('foo')); + request.mockImplementationOnce(async () => { + throw new Error('foo'); + }); + return expect(download('url', TEMP_FILE)).rejects.toThrow('foo'); }); -afterEach(async () => ( - await del(TEMP_DIR) -)); +afterEach(async () => await del(TEMP_DIR)); diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/download.js b/x-pack/legacy/plugins/reporting/server/browsers/download/download.ts similarity index 68% rename from x-pack/legacy/plugins/reporting/server/browsers/download/download.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/download.ts index f8436f5ce7af7..a5ad69b46e46d 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/download.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/download.ts @@ -8,9 +8,9 @@ import { openSync, writeSync, closeSync, mkdirSync } from 'fs'; import { createHash } from 'crypto'; import { dirname } from 'path'; -import request from 'request'; +import Axios from 'axios'; -import { log, readableEnd } from './util'; +import { log } from './util'; /** * Download a url and calculate it's checksum @@ -18,7 +18,7 @@ import { log, readableEnd } from './util'; * @param {String} path * @return {Promise} checksum of the downloaded file */ -export async function download(url, path) { +export async function download(url: string, path: string) { log(`Downloading ${url}`); const hash = createHash('md5'); @@ -27,11 +27,20 @@ export async function download(url, path) { const handle = openSync(path, 'w'); try { - const readable = request(url).on('data', chunk => { + const resp = await Axios.request({ + url, + method: 'GET', + responseType: 'stream', + }); + + resp.data.on('data', (chunk: Buffer) => { writeSync(handle, chunk); hash.update(chunk); }); - await readableEnd(readable); + + await new Promise((resolve, reject) => { + resp.data.on('error', reject).on('end', resolve); + }); } finally { closeSync(handle); } diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.js b/x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.ts similarity index 68% rename from x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.ts index 2a8052689e02d..d322566f9aa1d 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/ensure_downloaded.ts @@ -7,21 +7,20 @@ import { resolve as resolvePath } from 'path'; import { existsSync } from 'fs'; -import { BROWSERS_BY_TYPE } from '../browsers'; +import { BROWSERS_BY_TYPE, BrowserType } from '../browsers'; import { md5 } from './checksum'; import { asyncMap } from './util'; import { download } from './download'; import { clean } from './clean'; - /** * Check for the downloaded archive of each requested browser type and * download them if they are missing or their checksum is invalid * @param {String} browserType * @return {Promise} */ -export async function ensureBrowserDownloaded(browserType) { +export async function ensureBrowserDownloaded(browserType: BrowserType) { await ensureDownloaded([BROWSERS_BY_TYPE[browserType]]); } @@ -33,7 +32,6 @@ export async function ensureAllBrowsersDownloaded() { await ensureDownloaded(Object.values(BROWSERS_BY_TYPE)); } - /** * Clears the unexpected files in the browsers archivesPath * and ensures that all packages/archives are downloaded and @@ -41,20 +39,29 @@ export async function ensureAllBrowsersDownloaded() { * @param {BrowserSpec} browsers * @return {Promise} */ -async function ensureDownloaded(browsers) { - await asyncMap(Object.values(browsers), async (browser) => { +async function ensureDownloaded( + browsers: Array<{ + paths: { + archivesPath: string; + baseUrl: string; + packages: Array<{ archiveFilename: string; archiveChecksum: string }>; + }; + }> +) { + await asyncMap(browsers, async browser => { const { archivesPath } = browser.paths; - await clean(archivesPath, browser.paths.packages.map(p => ( - resolvePath(archivesPath, p.archiveFilename) - ))); + await clean( + archivesPath, + browser.paths.packages.map(p => resolvePath(archivesPath, p.archiveFilename)) + ); - const invalidChecksums = []; + const invalidChecksums: string[] = []; await asyncMap(browser.paths.packages, async ({ archiveFilename, archiveChecksum }) => { const url = `${browser.paths.baseUrl}${archiveFilename}`; const path = resolvePath(archivesPath, archiveFilename); - if (existsSync(path) && await md5(path) === archiveChecksum) { + if (existsSync(path) && (await md5(path)) === archiveChecksum) { return; } @@ -65,7 +72,11 @@ async function ensureDownloaded(browsers) { }); if (invalidChecksums.length) { - throw new Error(`Error downloading browsers, checksums incorrect for:\n - ${invalidChecksums.join('\n - ')}`); + throw new Error( + `Error downloading browsers, checksums incorrect for:\n - ${invalidChecksums.join( + '\n - ' + )}` + ); } }); } diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/index.js b/x-pack/legacy/plugins/reporting/server/browsers/download/index.js deleted file mode 100644 index 9fbfa918e769d..0000000000000 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/index.js +++ /dev/null @@ -1,10 +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. - */ - -export { - ensureBrowserDownloaded, - ensureAllBrowsersDownloaded, -} from './ensure_downloaded'; diff --git a/x-pack/tasks/helpers/get_flags.js b/x-pack/legacy/plugins/reporting/server/browsers/download/index.ts similarity index 72% rename from x-pack/tasks/helpers/get_flags.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/index.ts index 397330daf4bda..bf7ed450b462f 100644 --- a/x-pack/tasks/helpers/get_flags.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function getFlags() { - return process.argv.slice(3); -} +export { ensureBrowserDownloaded, ensureAllBrowsersDownloaded } from './ensure_downloaded'; diff --git a/x-pack/legacy/plugins/reporting/server/browsers/download/util.js b/x-pack/legacy/plugins/reporting/server/browsers/download/util.ts similarity index 65% rename from x-pack/legacy/plugins/reporting/server/browsers/download/util.js rename to x-pack/legacy/plugins/reporting/server/browsers/download/util.ts index f9cc905e01264..679106742e3d0 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/download/util.js +++ b/x-pack/legacy/plugins/reporting/server/browsers/download/util.ts @@ -4,33 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Readable } from 'stream'; + /** * Log a message if the DEBUG environment variable is set - * @param {...any} args - * @return {undefined} */ -export function log(...args) { +export function log(...args: any[]) { if (process.env.DEBUG) { + // allow console log since this is off by default and only for debugging + // eslint-disable-next-line no-console console.log(...args); } } /** * Iterate an array asynchronously and in parallel - * @param {Array} array - * @param {Function} asyncFn - * @return {Promise} */ -export function asyncMap(array, asyncFn) { +export function asyncMap(array: T[], asyncFn: (x: T) => T2): Promise { return Promise.all(array.map(asyncFn)); } /** * Wait for a readable stream to end - * @param {Stream.Readable} stream - * @return {Promise} */ -export function readableEnd(stream) { +export function readableEnd(stream: Readable) { return new Promise((resolve, reject) => { stream.on('error', reject).on('end', resolve); }); diff --git a/x-pack/legacy/plugins/reporting/server/browsers/index.js b/x-pack/legacy/plugins/reporting/server/browsers/index.ts similarity index 100% rename from x-pack/legacy/plugins/reporting/server/browsers/index.js rename to x-pack/legacy/plugins/reporting/server/browsers/index.ts diff --git a/x-pack/package.json b/x-pack/package.json index a673f09c980e6..333d74985f9fc 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -9,7 +9,7 @@ "kbn:bootstrap": "node legacy/plugins/canvas/scripts/storybook --clean", "start": "gulp dev", "build": "gulp build", - "testonly": "gulp testonly", + "testonly": "echo 'Deprecated, use `yarn test`' && gulp test", "test": "gulp test", "test:browser:dev": "gulp testbrowser-dev", "test:browser": "gulp testbrowser", @@ -56,10 +56,13 @@ "@types/d3-time": "^1.0.10", "@types/d3-time-format": "^2.1.1", "@types/elasticsearch": "^5.0.33", + "@types/fancy-log": "^1.3.1", "@types/file-saver": "^2.0.0", + "@types/getos": "^3.0.0", "@types/git-url-parse": "^9.0.0", "@types/glob": "^7.1.1", "@types/graphql": "^0.13.1", + "@types/gulp": "^4.0.6", "@types/hapi__wreck": "^15.0.1", "@types/history": "^4.7.3", "@types/jest": "^24.0.18", @@ -122,7 +125,6 @@ "copy-webpack-plugin": "^5.0.4", "cypress": "^3.4.1", "del": "^4.1.1", - "dotenv": "2.0.0", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", "enzyme-adapter-utils": "^1.12.0", @@ -134,7 +136,7 @@ "graphql-codegen-introspection-template": "^0.13.0", "graphql-codegen-typescript-resolvers-template": "^0.13.0", "graphql-codegen-typescript-template": "^0.13.0", - "gulp": "3.9.1", + "gulp": "4.0.2", "gulp-mocha": "^7.0.1", "gulp-multi-process": "1.3.1", "hapi": "^17.5.3", @@ -162,7 +164,6 @@ "react-testing-library": "^6.0.0", "redux-test-utils": "0.2.2", "rsync": "0.6.1", - "run-sequence": "^2.2.1", "sass-loader": "^7.3.1", "sass-resources-loader": "^2.0.1", "simple-git": "1.116.0", diff --git a/x-pack/tasks/build.js b/x-pack/tasks/build.js deleted file mode 100644 index ee7f22a4f2034..0000000000000 --- a/x-pack/tasks/build.js +++ /dev/null @@ -1,36 +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'; -import { writeFileSync } from 'fs'; -import pluginHelpers from '@kbn/plugin-helpers'; -import { ToolingLog } from '@kbn/dev-utils'; -import { generateNoticeFromSource } from '../../src/dev'; - -export default (gulp, { buildTarget }) => { - gulp.task('build', ['clean', 'report', 'prepare:build'], async () => { - await pluginHelpers.run('build', { - skipArchive: true, - buildDestination: buildTarget, - }); - - const buildRoot = resolve(buildTarget, 'kibana/x-pack'); - - const log = new ToolingLog({ - level: 'info', - writeTo: process.stdout - }); - - writeFileSync( - resolve(buildRoot, 'NOTICE.txt'), - await generateNoticeFromSource({ - productName: 'Kibana X-Pack', - log, - directory: buildRoot - }) - ); - }); -}; diff --git a/x-pack/tasks/build.ts b/x-pack/tasks/build.ts new file mode 100644 index 0000000000000..6bc615613387e --- /dev/null +++ b/x-pack/tasks/build.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resolve } from 'path'; +import { writeFileSync } from 'fs'; + +import pluginHelpers from '@kbn/plugin-helpers'; +import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; +import gulp from 'gulp'; +import del from 'del'; +import fancyLog from 'fancy-log'; +import chalk from 'chalk'; + +import { generateNoticeFromSource } from '../../src/dev/notice'; +import { prepareTask } from './prepare'; +import { gitInfo } from './helpers/git_info'; +import { PKG_NAME } from './helpers/pkg'; +import { BUILD_VERSION } from './helpers/build_version'; + +const BUILD_DIR = resolve(REPO_ROOT, 'x-pack/build'); +const PLUGIN_BUILD_DIR = resolve(BUILD_DIR, 'plugin'); + +async function cleanBuildTask() { + fancyLog('Deleting', BUILD_DIR); + await del(BUILD_DIR); +} + +async function reportTask() { + const info = await gitInfo(); + + fancyLog('Package Name', chalk.yellow(PKG_NAME)); + fancyLog('Version', chalk.yellow(BUILD_VERSION)); + fancyLog('Build Number', chalk.yellow(`${info.number}`)); + fancyLog('Build SHA', chalk.yellow(info.sha)); +} + +async function pluginHelpersBuild() { + await pluginHelpers.run('build', { + skipArchive: true, + buildDestination: PLUGIN_BUILD_DIR, + }); +} + +async function generateNoticeText() { + const buildRoot = resolve(PLUGIN_BUILD_DIR, 'kibana/x-pack'); + const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout, + }); + + writeFileSync( + resolve(buildRoot, 'NOTICE.txt'), + await generateNoticeFromSource({ + productName: 'Kibana X-Pack', + log, + directory: buildRoot, + }) + ); +} + +export const buildTask = gulp.series( + cleanBuildTask, + reportTask, + prepareTask, + pluginHelpersBuild, + generateNoticeText +); diff --git a/x-pack/tasks/clean.js b/x-pack/tasks/clean.js deleted file mode 100644 index 8e2098c3b474a..0000000000000 --- a/x-pack/tasks/clean.js +++ /dev/null @@ -1,22 +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 del from 'del'; - -export default (gulp, { coverageDir, buildDir, packageDir, log }) => { - gulp.task('clean-test', () => { - log('Deleting', coverageDir); - return del([coverageDir]); - }); - - gulp.task('clean', ['clean-test'], () => { - const toDelete = [ - buildDir, - packageDir, - ]; - log('Deleting', toDelete.join(', ')); - return del(toDelete); - }); -}; diff --git a/x-pack/tasks/dev.js b/x-pack/tasks/dev.ts similarity index 55% rename from x-pack/tasks/dev.js rename to x-pack/tasks/dev.ts index 048e32ed86574..6e398f231a27c 100644 --- a/x-pack/tasks/dev.js +++ b/x-pack/tasks/dev.ts @@ -5,8 +5,12 @@ */ import pluginHelpers from '@kbn/plugin-helpers'; -import getFlags from './helpers/get_flags'; +import gulp from 'gulp'; -export default (gulp) => { - gulp.task('dev', ['prepare:dev'], () => pluginHelpers.run('start', { flags: getFlags() })); -}; +import { prepareTask } from './prepare'; + +export const devTask = gulp.series(prepareTask, async function startKibanaServer() { + await pluginHelpers.run('start', { + flags: process.argv.slice(3), + }); +}); diff --git a/x-pack/tasks/helpers/build_version.js b/x-pack/tasks/helpers/build_version.js deleted file mode 100644 index f77e1fb7168f4..0000000000000 --- a/x-pack/tasks/helpers/build_version.js +++ /dev/null @@ -1,29 +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 yargs from 'yargs'; -import semver from 'semver'; - -yargs - .alias('r', 'release').describe('r', 'Create a release build, not a snapshot') - .option('build-qualifier', { - default: null - }); -const argv = yargs.argv; - -export default function getVersion(pkg) { - const { version } = pkg; - if (!version) { - throw new Error('No version found in package.json'); - } - if (!semver.valid(version)) { - throw new Error(`Version is not valid semver: ${version}`); - } - - const snapshotText = (argv.release) ? '' : '-SNAPSHOT'; - const qualifierText = argv.buildQualifier ? '-' + argv.buildQualifier : ''; - return `${version}${qualifierText}${snapshotText}`; -} diff --git a/x-pack/tasks/helpers/build_version.ts b/x-pack/tasks/helpers/build_version.ts new file mode 100644 index 0000000000000..7e9954ed3f756 --- /dev/null +++ b/x-pack/tasks/helpers/build_version.ts @@ -0,0 +1,12 @@ +/* + * 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 { PKG_VERSION } from './pkg'; +import { FLAGS } from './flags'; + +const snapshotText = FLAGS.release ? '' : '-SNAPSHOT'; +const qualifierText = FLAGS.buildQualifier ? '-' + FLAGS.buildQualifier : ''; +export const BUILD_VERSION = `${PKG_VERSION}${qualifierText}${snapshotText}`; diff --git a/x-pack/tasks/helpers/flags.ts b/x-pack/tasks/helpers/flags.ts new file mode 100644 index 0000000000000..62a382b02ed22 --- /dev/null +++ b/x-pack/tasks/helpers/flags.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resolve } from 'path'; + +import log from 'fancy-log'; +import getopts from 'getopts'; +import { toArray } from 'rxjs/operators'; + +// @ts-ignore complicated module doesn't have types yet +import { findPluginSpecs } from '../../../src/legacy/plugin_discovery'; + +/* + Usage: + Specifying which plugins to run tests can be done with the --plugins flag. + One of more plugins can be specified, and each one should be command separated, like so: + gulp testserver --plugins monitoring,reporting + If using with yarn: + yarn test:server --plugins graph +*/ + +const opts = Object.freeze( + getopts(process.argv.slice(2), { + alias: { + release: 'r', + }, + boolean: ['release', 'flags'], + string: ['build-qualifier', 'plugins'], + }) +); + +if (opts.flags) { + log(` + X-Pack Gulpfile Flags: + + --flags Print this message + --plugins Comma-separated list of plugins + --release, -r Build to a release version + --build-qualifier Qualifier to include in the build version + `); + process.exit(0); +} + +export const FLAGS = { + release: !!opts.release, + buildQualifier: opts.buildQualifier as string | undefined, + plugins: opts.plugins + ? String(opts.plugins) + .split(',') + .map(id => id.trim()) + : undefined, +}; + +export async function getEnabledPlugins() { + if (FLAGS.plugins) { + return FLAGS.plugins; + } + + const { spec$ } = findPluginSpecs({ + plugins: { + paths: [resolve(__dirname, '..', '..')], + }, + }); + + const enabledPlugins: Array<{ getId: () => string }> = await spec$.pipe(toArray()).toPromise(); + return enabledPlugins.map(spec => spec.getId()); +} diff --git a/x-pack/tasks/helpers/get_plugins.js b/x-pack/tasks/helpers/get_plugins.js deleted file mode 100644 index a540cda27bd73..0000000000000 --- a/x-pack/tasks/helpers/get_plugins.js +++ /dev/null @@ -1,46 +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'; -import yargs from 'yargs'; -import glob from 'glob'; -import { toArray } from 'rxjs/operators'; -import { findPluginSpecs } from '../../../src/legacy/plugin_discovery'; - -/* - Usage: - Specifying which plugins to run tests can be done with the --plugins flag. - One of more plugins can be specified, and each one should be command separated, like so: - gulp testserver --plugins monitoring,reporting - If using with yarn: - yarn test:server --plugins graph -*/ - -const argv = yargs - .describe('plugins', 'Comma-separated list of plugins') - .argv; -const allPlugins = glob.sync('*', { cwd: resolve(__dirname, '..', '..', 'legacy', 'plugins') }); - -export function getPlugins() { - const plugins = argv.plugins && argv.plugins.split(','); - if (!Array.isArray(plugins) || plugins.length === 0) { - return allPlugins; - } - return plugins; -} - -const { spec$ } = findPluginSpecs({ - plugins: { paths: [resolve(__dirname, '..', '..')] } -}); - -export async function getEnabledPlugins() { - const plugins = argv.plugins && argv.plugins.split(','); - if (!Array.isArray(plugins) || plugins.length === 0) { - const enabledPlugins = await spec$.pipe(toArray()).toPromise(); - return enabledPlugins.map(spec => spec.getId()); - } - return plugins; -} diff --git a/x-pack/tasks/helpers/git_info.js b/x-pack/tasks/helpers/git_info.ts similarity index 50% rename from x-pack/tasks/helpers/git_info.js rename to x-pack/tasks/helpers/git_info.ts index 8e6f463004746..3c144e6cc10d0 100644 --- a/x-pack/tasks/helpers/git_info.js +++ b/x-pack/tasks/helpers/git_info.ts @@ -5,20 +5,24 @@ */ import path from 'path'; +// @ts-ignore barely used, untyped module import simpleGit from 'simple-git'; const gitDir = path.resolve(__dirname, '..', '..'); -export default function gitInfo() { +export async function gitInfo() { const git = simpleGit(gitDir); - return new Promise((resolve, reject) => { - git.log((err, log) => { - if (err) return reject(err); - resolve({ - number: log.total, - sha: log.latest.hash, - }); + return new Promise<{ number: number; sha: string }>((resolve, reject) => { + git.log((err: undefined | Error, log: { total: number; latest: { hash: string } }) => { + if (err) { + reject(err); + } else { + resolve({ + number: log.total, + sha: log.latest.hash, + }); + } }); }); } diff --git a/x-pack/tasks/helpers/globs.js b/x-pack/tasks/helpers/globs.js deleted file mode 100644 index 0576389c94249..0000000000000 --- a/x-pack/tasks/helpers/globs.js +++ /dev/null @@ -1,49 +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 { getPlugins } from './get_plugins'; - -/* - * Note: The path `plugins / pluginName / ** / __tests__ / ** / *.js` will match - * all public and server tests, so a special var must be used for "index" tests - * paths: `plugins / pluginName / __tests__ / ** / *.js` - */ -function getPluginPaths(plugins, opts = {}) { - const testPath = opts.tests ? '__tests__/**' : ''; - - return plugins.reduce((paths, pluginName) => { - const plugin = pluginName.trim(); - - const commonPath = `${plugin}/common`; - const serverPath = `${plugin}/**/server`; - const publicPath = `${plugin}/**/public`; - - const indexPaths = `legacy/plugins/${plugin}/${testPath}/*.js`; // index and helpers - const commonPaths = `legacy/plugins/${commonPath}/**/${testPath}/*.js`; - const serverPaths = `legacy/plugins/${serverPath}/**/${testPath}/*.js`; - const publicPaths = `legacy/plugins/${publicPath}/**/${testPath}/*.js`; - - paths = paths.concat([indexPaths, commonPaths, serverPaths]); - if (plugin === 'code') { - paths.push(`legacy/plugins/${serverPath}/**/${testPath}/*.ts`); - } - if (opts.browser) { - paths = paths.concat(publicPaths); - } - - return paths; - }, []); -} - -export function forPlugins() { - const plugins = getPlugins(); - return getPluginPaths(plugins, { browser: true }); -} - -export function forPluginServerTests() { - const plugins = getPlugins(); - return getPluginPaths(plugins, { tests: true }); -} diff --git a/x-pack/tasks/helpers/pkg.ts b/x-pack/tasks/helpers/pkg.ts new file mode 100644 index 0000000000000..8411ebcf7186a --- /dev/null +++ b/x-pack/tasks/helpers/pkg.ts @@ -0,0 +1,33 @@ +/* + * 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 Fs from 'fs'; +import semver from 'semver'; + +interface PackageJson { + name: string; + version: string; + dependencies: Record; + devDependencies: Record; + [key: string]: unknown; +} + +const PKG_PATH = require.resolve('../../package.json'); +export const PKG: PackageJson = JSON.parse(Fs.readFileSync(PKG_PATH, 'utf8')); +export const PKG_VERSION = PKG.version; +export const PKG_NAME = PKG.name; + +if (!PKG_VERSION) { + throw new Error('No "version" found in package.json'); +} + +if (!PKG_NAME) { + throw new Error('No "name" found in package.json'); +} + +if (!semver.valid(PKG_VERSION)) { + throw new Error(`Version is not valid semver: ${PKG_VERSION}`); +} diff --git a/x-pack/tasks/prepare.js b/x-pack/tasks/prepare.js deleted file mode 100644 index 287781a819400..0000000000000 --- a/x-pack/tasks/prepare.js +++ /dev/null @@ -1,18 +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 { ensureAllBrowsersDownloaded } from '../legacy/plugins/reporting/server/browsers'; - -export default gulp => { - // anything that should always happen before anything else - gulp.task('prepare', () => ensureAllBrowsersDownloaded()); - - // anything that needs to happen before development - gulp.task('prepare:dev', ['prepare']); - - // anything that needs to happen before building - gulp.task('prepare:build', ['prepare']); -}; diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/uiStorage.js b/x-pack/tasks/prepare.ts similarity index 58% rename from x-pack/legacy/plugins/canvas/tasks/mocks/uiStorage.js rename to x-pack/tasks/prepare.ts index ff10d2d4bf68e..6d4eb5603ad3a 100644 --- a/x-pack/legacy/plugins/canvas/tasks/mocks/uiStorage.js +++ b/x-pack/tasks/prepare.ts @@ -4,12 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export class Storage { - get(key) { - return this[key]; - } +import { ensureAllBrowsersDownloaded } from '../legacy/plugins/reporting/server/browsers'; - set(key, value) { - this[key] = value; - } -} +export const prepareTask = async () => { + await ensureAllBrowsersDownloaded(); +}; diff --git a/x-pack/tasks/report.js b/x-pack/tasks/report.js deleted file mode 100644 index a7e1a2e31de58..0000000000000 --- a/x-pack/tasks/report.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 buildVersion from './helpers/build_version'; -import gitInfo from './helpers/git_info'; -import chalk from 'chalk'; - -export default (gulp, { log, pkg }) => { - gulp.task('report', () => { - return gitInfo() - .then(function (info) { - log('Package Name', chalk.yellow(pkg.name)); - log('Version', chalk.yellow(buildVersion(pkg))); - log('Build Number', chalk.yellow(info.number)); - log('Build SHA', chalk.yellow(info.sha)); - }); - }); -}; diff --git a/x-pack/tasks/test.js b/x-pack/tasks/test.js deleted file mode 100644 index 5aae780dda6d7..0000000000000 --- a/x-pack/tasks/test.js +++ /dev/null @@ -1,54 +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 runSequence from 'run-sequence'; -import pluginHelpers from '@kbn/plugin-helpers'; -import { getEnabledPlugins } from './helpers/get_plugins'; -import { forPluginServerTests } from './helpers/globs'; -import { createAutoJUnitReporter } from '../../src/dev'; - -const MOCHA_OPTIONS = { - ui: 'bdd', - require: require.resolve('../../src/setup_node_env'), - reporter: createAutoJUnitReporter({ - reportName: 'X-Pack Mocha Tests', - }), -}; - -export default (gulp, { mocha }) => { - gulp.task('test', (cb) => { - const preTasks = ['clean-test']; - runSequence(preTasks, 'testserver', 'testbrowser', cb); - }); - - gulp.task('testonly', ['testserver', 'testbrowser']); - - gulp.task('testserver', () => { - const globs = [ - 'common/**/__tests__/**/*.js', - 'server/**/__tests__/**/*.js', - ].concat(forPluginServerTests()); - return gulp.src(globs, { read: false }) - .pipe(mocha(MOCHA_OPTIONS)); - }); - - gulp.task('testbrowser', () => { - return getEnabledPlugins().then(plugins => { - return pluginHelpers.run('testBrowser', { - plugins: plugins.join(','), - }); - }); - }); - - gulp.task('testbrowser-dev', () => { - return getEnabledPlugins().then(plugins => { - return pluginHelpers.run('testBrowser', { - dev: true, - plugins: plugins.join(','), - }); - }); - }); -}; diff --git a/x-pack/tasks/test.ts b/x-pack/tasks/test.ts new file mode 100644 index 0000000000000..7be64f7179f63 --- /dev/null +++ b/x-pack/tasks/test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import pluginHelpers from '@kbn/plugin-helpers'; +import { createAutoJUnitReporter } from '@kbn/test'; +// @ts-ignore no types available +import mocha from 'gulp-mocha'; +import gulp from 'gulp'; + +import { getEnabledPlugins } from './helpers/flags'; + +export const testServerTask = async () => { + const pluginIds = await getEnabledPlugins(); + + const testGlobs = ['common/**/__tests__/**/*.js', 'server/**/__tests__/**/*.js']; + + if (pluginIds.includes('code')) { + testGlobs.push(`legacy/plugins/**/server/**/__tests__/**/*.ts`); + } + + for (const pluginId of pluginIds) { + testGlobs.push( + `legacy/plugins/${pluginId}/__tests__/**/*.js`, + `legacy/plugins/${pluginId}/common/**/__tests__/**/*.js`, + `legacy/plugins/${pluginId}/**/server/**/__tests__/**/*.js` + ); + } + + return gulp.src(testGlobs, { read: false }).pipe( + mocha({ + ui: 'bdd', + require: require.resolve('../../src/setup_node_env'), + reporter: createAutoJUnitReporter({ + reportName: 'X-Pack Mocha Tests', + }), + }) + ); +}; + +export const testBrowserTask = async () => { + const plugins = await getEnabledPlugins(); + await pluginHelpers.run('testBrowser', { + plugins: plugins.join(','), + }); +}; + +export const testBrowserDevTask = async () => { + const plugins = await getEnabledPlugins(); + await pluginHelpers.run('testBrowser', { + dev: true, + plugins: plugins.join(','), + }); +}; + +export const testTask = gulp.series(testServerTask, testBrowserTask); diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index d0452ae20a694..82b2131a0a855 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -6,7 +6,8 @@ "legacy/server/**/*", "legacy/plugins/**/*", "plugins/**/*", - "test_utils/**/*" + "test_utils/**/*", + "tasks/**/*" ], "exclude": [ "test/**/*", diff --git a/yarn.lock b/yarn.lock index d93f6c60bf0cd..18dc51000d7f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3314,6 +3314,11 @@ dependencies: "@types/node" "*" +"@types/fancy-log@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.1.tgz#dd94fbc8c2e2ab8ab402ca8d04bb8c34965f0696" + integrity sha512-31Dt9JaGfHretvwVxCBrCFL5iC9MQ3zOXpu+8C4qzW0cxc5rJJVGxB5c/vZ+wmeTk/JjPz/D0gv8BZ+Ip6iCqQ== + "@types/fetch-mock@^7.3.1": version "7.3.1" resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.1.tgz#df7421e8bcb351b430bfbfa5c52bb353826ac94f" @@ -3348,11 +3353,24 @@ resolved "https://registry.yarnpkg.com/@types/getopts/-/getopts-2.0.1.tgz#b7e5478fe7571838b45aff736a59ab69b8bcda18" integrity sha512-JsQJHtzLYKunMz7acYOX6x5IJ/42CsjjHFfLCmis1Hn/qFoD/y0kJFUIAg8HoDigQpKUcWj55d8rxZQBnvz1Pw== +"@types/getos@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/getos/-/getos-3.0.0.tgz#582c758e99e9d634f31f471faf7ce59cf1c39a71" + integrity sha512-g5O9kykBPMaK5USwU+zM5AyXaztqbvHjSQ7HaBjqgO3f5lKGChkRhLP58Z/Nrr4RBGNNPrBcJkWZwnmbmi9YjQ== + "@types/git-url-parse@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@types/git-url-parse/-/git-url-parse-9.0.0.tgz#aac1315a44fa4ed5a52c3820f6c3c2fb79cbd12d" integrity sha512-kA2RxBT/r/ZuDDKwMl+vFWn1Z0lfm1/Ik6Qb91wnSzyzCDa/fkM8gIOq6ruB7xfr37n6Mj5dyivileUVKsidlg== +"@types/glob-stream@*": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/glob-stream/-/glob-stream-6.1.0.tgz#7ede8a33e59140534f8d8adfb8ac9edfb31897bc" + integrity sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg== + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/glob@*", "@types/glob@^5.0.35": version "5.0.35" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" @@ -3391,6 +3409,15 @@ resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.13.4.tgz#55ae9c29f0fd6b85ee536f5c72b4769d5c5e06b1" integrity sha512-B4yel4ro2nTb3v0pYO8vO6SjgvFJSrwUY+IO6TUSLdOSB+gQFslylrhRCHxvXMIhxB71mv5PEE9dAX+24S8sew== +"@types/gulp@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-4.0.6.tgz#68fe0e1f0ff3657cfca46fb564806b744a1bf899" + integrity sha512-0E8/iV/7FKWyQWSmi7jnUvgXXgaw+pfAzEB06Xu+l0iXVJppLbpOye5z7E2klw5akXd+8kPtYuk65YBcZPM4ow== + dependencies: + "@types/undertaker" "*" + "@types/vinyl-fs" "*" + chokidar "^2.1.2" + "@types/hapi-auth-cookie@^9.1.0": version "9.1.0" resolved "https://registry.yarnpkg.com/@types/hapi-auth-cookie/-/hapi-auth-cookie-9.1.0.tgz#cbcd2236b7d429bd0632a8cc45cfd355fdd7e7a2" @@ -4125,6 +4152,18 @@ resolved "https://registry.yarnpkg.com/@types/type-detect/-/type-detect-4.0.1.tgz#3b0f5ac82ea630090cbf57c57a1bf5a63a29b9b6" integrity sha512-0+S1S9Iq0oJ9w9IaBC5W/z1WsPNDUIAJG+THGmqR4vUAxUPCzIY+dApTvyGsaBUWjafTDL0Dg8Z9+iRuk3/BQA== +"@types/undertaker-registry@*": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz#4306d4a03d7acedb974b66530832b90729e1d1da" + integrity sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ== + +"@types/undertaker@*": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/undertaker/-/undertaker-1.2.2.tgz#927da24d0d3279830af96386862b035e040ead74" + integrity sha512-j4iepCSuY2JGW/hShVtUBagic0klYNFIXP7VweavnYnNC2EjiKxJFeaS9uaJmAT0ty9sQSqTS1aagWMZMV0HyA== + dependencies: + "@types/undertaker-registry" "*" + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" @@ -4159,6 +4198,22 @@ "@types/unist" "*" "@types/vfile-message" "*" +"@types/vinyl-fs@*", "@types/vinyl-fs@^2.4.11": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@types/vinyl-fs/-/vinyl-fs-2.4.11.tgz#b98119b8bb2494141eaf649b09fbfeb311161206" + integrity sha512-2OzQSfIr9CqqWMGqmcERE6Hnd2KY3eBVtFaulVo3sJghplUcaeMdL9ZjEiljcQQeHjheWY9RlNmumjIAvsBNaA== + dependencies: + "@types/glob-stream" "*" + "@types/node" "*" + "@types/vinyl" "*" + +"@types/vinyl@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.3.tgz#80a6ce362ab5b32a0c98e860748a31bce9bff0de" + integrity sha512-hrT6xg16CWSmndZqOTJ6BGIn2abKyTw0B58bI+7ioUoj3Sma6u8ftZ1DTI2yCaJamOVGLOnQWiPH3a74+EaqTA== + dependencies: + "@types/node" "*" + "@types/webpack-env@*": version "1.13.9" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a" @@ -4880,13 +4935,6 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.1.tgz#9638047e4213f3428a11944a7d4b31cba0a3ff95" integrity sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ== -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= - dependencies: - ansi-wrap "0.1.0" - ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -4933,13 +4981,6 @@ ansi-html@0.0.7: resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= - dependencies: - ansi-wrap "0.1.0" - ansi-regex@^0.2.0, ansi-regex@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" @@ -5330,28 +5371,29 @@ aria-query@^3.0.0: ast-types-flow "0.0.7" commander "^2.11.0" -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= +arr-filter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" + integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= + dependencies: + make-iterator "^1.0.0" + arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= +arr-map@^2.0.0, arr-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" + integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= + dependencies: + make-iterator "^1.0.0" arr-union@^3.1.0: version "3.1.0" @@ -5368,7 +5410,7 @@ array-differ@^3.0.0: resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-each@^1.0.1: +array-each@^1.0.0, array-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= @@ -5421,6 +5463,21 @@ array-includes@^3.0.3: define-properties "^1.1.2" es-abstract "^1.7.0" +array-initial@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" + integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= + dependencies: + array-slice "^1.0.0" + is-number "^4.0.0" + +array-last@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" + integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== + dependencies: + is-number "^4.0.0" + array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" @@ -5451,6 +5508,15 @@ array-slice@^1.0.0: resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + array-union@^1.0.1, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -5463,7 +5529,7 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.0, array-uniq@^1.0.1, array-uniq@^1.0.2: +array-uniq@^1.0.0, array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= @@ -5621,6 +5687,16 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +async-done@^1.2.0, async-done@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" + integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.2" + process-nextick-args "^2.0.0" + stream-exhaust "^1.0.1" + async-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -5648,6 +5724,13 @@ async-retry@^1.2.3: dependencies: retry "0.12.0" +async-settle@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" + integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= + dependencies: + async-done "^1.2.2" + async.queue@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/async.queue/-/async.queue-0.5.2.tgz#8d5d90812e1481066bc0904e8cc1712b17c3bd7c" @@ -6357,6 +6440,21 @@ babylon@^6.18.0: resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== +bach@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" + integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= + dependencies: + arr-filter "^1.1.1" + arr-flatten "^1.0.1" + arr-map "^2.0.0" + array-each "^1.0.0" + array-initial "^1.0.0" + array-last "^1.1.1" + async-done "^1.2.2" + async-settle "^1.0.0" + now-and-later "^2.0.0" + backo2@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -6457,11 +6555,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= - before-after-hook@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.1.0.tgz#83165e15a59460d13702cb8febd6a1807896db5a" @@ -6760,7 +6853,7 @@ boxen@^3.0.0: type-fest "^0.3.0" widest-line "^2.0.0" -brace-expansion@^1.0.0, brace-expansion@^1.1.7: +brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" integrity sha1-wHshHHyVLsH479Uad+8NHTmQopI= @@ -7695,6 +7788,25 @@ chokidar@3.0.2: optionalDependencies: fsevents "^2.0.6" +chokidar@^2.0.0, chokidar@^2.1.2: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + chokidar@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" @@ -8054,11 +8166,6 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= - clone@^1.0.0, clone@^1.0.1, clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -8132,6 +8239,15 @@ collapse-white-space@^1.0.2: resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.3.tgz#4b906f670e5a963a87b76b0e1689643341b6023c" integrity sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw= +collection-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" + integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= + dependencies: + arr-map "^2.0.2" + for-own "^1.0.0" + make-iterator "^1.0.0" + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -8383,7 +8499,7 @@ concat-stream@1.6.0, concat-stream@^1.4.7, concat-stream@~1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.1: +concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -8639,6 +8755,14 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-props@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" + integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== + dependencies: + each-props "^1.3.0" + is-plain-object "^2.0.1" + copy-to-clipboard@^3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9" @@ -9781,6 +9905,13 @@ deepmerge@^4.0.0: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.0.0.tgz#3e3110ca29205f120d7cb064960a39c3d2087c09" integrity sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww== +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -9796,12 +9927,17 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" +default-resolution@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" + integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= + default-uid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-uid/-/default-uid-1.0.0.tgz#fcefa9df9f5ac40c8916d912dd1fe1146aa3c59e" integrity sha1-/O+p359axAyJFtkS3R/hFGqjxZ4= -defaults@^1.0.0, defaults@^1.0.3: +defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= @@ -9931,11 +10067,6 @@ deprecated-decorator@^0.1.6: resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= -deprecated@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" - integrity sha1-+cmvVGSvoeepcUWKi97yqpTVuxk= - deprecation@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-1.0.1.tgz#2df79b79005752180816b7b6e079cbd80490d711" @@ -10386,11 +10517,6 @@ dotenv-webpack@^1.7.0: dependencies: dotenv-defaults "^1.0.2" -dotenv@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-2.0.0.tgz#bd759c357aaa70365e01c96b7b0bec08a6e0d949" - integrity sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk= - dotenv@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" @@ -10436,13 +10562,6 @@ dragselect@1.13.1: resolved "https://registry.yarnpkg.com/dragselect/-/dragselect-1.13.1.tgz#aa4166e1164b51ed5ee0cd89e0c5310a9c35be6a" integrity sha512-spfUz6/sNnlY4fF/OxPBwaKLa5hVz6V+fq5XhVuD+h47RAkA75TMkfvr4AoWUh5Ufq3V1oIAbfu+sjc9QbewoA== -duplexer2@0.0.2, duplexer2@~0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - duplexer2@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -10450,6 +10569,13 @@ duplexer2@^0.1.4: dependencies: readable-stream "^2.0.2" +duplexer2@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= + dependencies: + readable-stream "~1.1.9" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -10488,6 +10614,14 @@ each-async@^1.1.1: onetime "^1.0.0" set-immediate-shim "^1.0.0" +each-props@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" + integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== + dependencies: + is-plain-object "^2.0.1" + object.defaults "^1.1.0" + eachr@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eachr/-/eachr-3.2.0.tgz#2c35e43ea086516f7997cf80b7aa64d55a4a4484" @@ -10677,13 +10811,6 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" -end-of-stream@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" - integrity sha1-jhdyBsPICDfYVjLouTWd/osvbq8= - dependencies: - once "~1.3.0" - engine.io-client@~3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36" @@ -11822,13 +11949,6 @@ express@^4.17.0, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= - dependencies: - kind-of "^1.1.0" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -11991,7 +12111,7 @@ falafel@^2.1.0: isarray "0.0.1" object-keys "^1.0.6" -fancy-log@^1.1.0, fancy-log@^1.3.2: +fancy-log@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" integrity sha1-9BEl49hPLn2JpD0G2VjI94vha+E= @@ -12400,11 +12520,6 @@ find-cache-dir@^3.0.0: make-dir "^3.0.0" pkg-dir "^4.1.0" -find-index@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" - integrity sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ= - find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -12455,7 +12570,7 @@ find@^0.3.0: dependencies: traverse-chain "~0.1.0" -findup-sync@3.0.0: +findup-sync@3.0.0, findup-sync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== @@ -12498,11 +12613,6 @@ finity@^0.5.4: resolved "https://registry.yarnpkg.com/finity/-/finity-0.5.4.tgz#f2a8a9198e8286467328ec32c8bfcc19a2229c11" integrity sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA== -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= - first-chunk-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" @@ -13020,13 +13130,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaze@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" - integrity sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8= - dependencies: - globule "~0.1.0" - gaze@^1.0.0, gaze@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" @@ -13179,7 +13282,7 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -get-stream@^5.0.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== @@ -13312,18 +13415,6 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" -glob-stream@^3.1.5: - version "3.1.18" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" - integrity sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs= - dependencies: - glob "^4.3.1" - glob2base "^0.0.12" - minimatch "^2.0.1" - ordered-read-streams "^0.1.0" - through2 "^0.6.1" - unique-stream "^1.0.0" - glob-stream@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" @@ -13350,19 +13441,17 @@ glob-to-regexp@^0.4.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.0.tgz#49bd677b1671022bd10921c3788f23cdebf9c7e6" integrity sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ== -glob-watcher@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" - integrity sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs= - dependencies: - gaze "^0.5.1" - -glob2base@^0.0.12: - version "0.0.12" - resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" - integrity sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY= +glob-watcher@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.3.tgz#88a8abf1c4d131eb93928994bc4a593c2e5dd626" + integrity sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg== dependencies: - find-index "^0.1.1" + anymatch "^2.0.0" + async-done "^1.2.0" + chokidar "^2.0.0" + is-negated-glob "^1.0.0" + just-debounce "^1.0.0" + object.defaults "^1.1.0" glob@7.1.3, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: version "7.1.3" @@ -13388,16 +13477,6 @@ glob@7.1.4, glob@^7.1.4, glob@~7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^4.3.1: - version "4.5.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" - integrity sha1-xstz0yJsHv7wTePFbQEvAzd+4V8= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "^2.0.1" - once "^1.3.0" - glob@^5.0.15, glob@~5.0.0: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -13420,15 +13499,6 @@ glob@^6.0.1, glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~3.1.21: - version "3.1.21" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" - integrity sha1-0p4KBV3qUTj00H7UDomC6DwgZs0= - dependencies: - graceful-fs "~1.2.0" - inherits "1" - minimatch "~0.2.11" - glob@~7.0.0: version "7.0.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" @@ -13637,15 +13707,6 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" -globule@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" - integrity sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU= - dependencies: - glob "~3.1.21" - lodash "~1.0.1" - minimatch "~0.2.11" - glogg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" @@ -13794,13 +13855,6 @@ graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, g resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= -graceful-fs@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - integrity sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg= - dependencies: - natives "^1.1.0" - graceful-fs@^4.1.15, graceful-fs@^4.1.9: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" @@ -13816,11 +13870,6 @@ graceful-fs@~1.1: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.1.14.tgz#07078db5f6377f6321fceaaedf497de124dc9465" integrity sha1-BweNtfY3f2Mh/Oqu30l94STclGU= -graceful-fs@~1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" - integrity sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q= - "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -14182,6 +14231,30 @@ gulp-babel@^8.0.0: through2 "^2.0.0" vinyl-sourcemaps-apply "^0.2.0" +gulp-cli@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.2.0.tgz#5533126eeb7fe415a7e3e84a297d334d5cf70ebc" + integrity sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA== + dependencies: + ansi-colors "^1.0.1" + archy "^1.0.0" + array-sort "^1.0.0" + color-support "^1.1.3" + concat-stream "^1.6.0" + copy-props "^2.0.1" + fancy-log "^1.3.2" + gulplog "^1.0.0" + interpret "^1.1.0" + isobject "^3.0.1" + liftoff "^3.1.0" + matchdep "^2.0.0" + mute-stdout "^1.0.0" + pretty-hrtime "^1.0.0" + replace-homedir "^1.0.0" + semver-greatest-satisfied-range "^1.1.0" + v8flags "^3.0.1" + yargs "^7.1.0" + gulp-mocha@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/gulp-mocha/-/gulp-mocha-7.0.1.tgz#cd29f2fc214a8c08c7d96bf13927d539385a856d" @@ -14223,59 +14296,26 @@ gulp-sourcemaps@2.6.5: strip-bom-string "1.X" through2 "2.X" -gulp-util@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-zip@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-4.2.0.tgz#e25e738c41ad0795ad853d1d8aeb1744d2a4ca82" - integrity sha512-I+697f6jf+PncdTrqfuwoauxgnLG1yHRg3vlmvDgmJuEnlEHy4meBktJ/oHgfyg4tp6X25wuZqUOraVeVg97wQ== +gulp-zip@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.0.tgz#ba1af3247e7acc4dc01a72a77c568654cfb1dd1f" + integrity sha512-oR3t8kn+ccHkSyRcBV5kBLPXrhqTh5d6wBAR7r7wqjNQNBhYvOwPedCwlAaGcNl1qSeXNDn6qOk1Qyxvx9Wrow== dependencies: - get-stream "^3.0.0" - plugin-error "^0.1.2" - through2 "^2.0.1" + get-stream "^5.1.0" + plugin-error "^1.0.1" + through2 "^3.0.1" vinyl "^2.1.0" - yazl "^2.1.0" + yazl "^2.5.1" -gulp@3.9.1: - version "3.9.1" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" - integrity sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ= +gulp@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" + integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== dependencies: - archy "^1.0.0" - chalk "^1.0.0" - deprecated "^0.0.1" - gulp-util "^3.0.0" - interpret "^1.0.0" - liftoff "^2.1.0" - minimist "^1.1.0" - orchestrator "^0.3.0" - pretty-hrtime "^1.0.0" - semver "^4.1.0" - tildify "^1.0.0" - v8flags "^2.0.2" - vinyl-fs "^0.3.0" + glob-watcher "^5.0.3" + gulp-cli "^2.2.0" + undertaker "^1.2.1" + vinyl-fs "^3.0.0" gulplog@^1.0.0: version "1.0.0" @@ -14454,13 +14494,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" @@ -15253,11 +15286,6 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" - integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js= - inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -15503,7 +15531,7 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -interpret@1.2.0, interpret@^1.2.0: +interpret@1.2.0, interpret@^1.1.0, interpret@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== @@ -17327,6 +17355,11 @@ jszip@^3.1.5: readable-stream "~2.3.6" set-immediate-shim "~1.0.1" +just-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" + integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= + just-extend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" @@ -17466,11 +17499,6 @@ killable@^1.0.1: resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= - kind-of@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" @@ -17545,6 +17573,14 @@ kuler@1.0.x: dependencies: colornames "^1.1.1" +last-run@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" + integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= + dependencies: + default-resolution "^2.0.0" + es6-weak-map "^2.0.1" + latest-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb" @@ -17711,13 +17747,13 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" -liftoff@^2.1.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" - integrity sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew= +liftoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" + integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== dependencies: extend "^3.0.0" - findup-sync "^2.0.0" + findup-sync "^3.0.0" fined "^1.0.1" flagged-respawn "^1.0.0" is-plain-object "^2.0.4" @@ -18013,51 +18049,11 @@ lodash-es@^4.17.11, lodash-es@^4.17.4, lodash-es@^4.17.5, lodash-es@^4.2.1: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -18113,13 +18109,6 @@ lodash.difference@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= - dependencies: - lodash._root "^3.0.0" - lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" @@ -18165,16 +18154,6 @@ lodash.intersection@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.intersection/-/lodash.intersection-4.4.0.tgz#0a11ba631d0e95c23c7f2f4cbb9a692ed178e705" integrity sha1-ChG6Yx0OlcI8fy9Mu5ppLtF45wU= -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -18230,15 +18209,6 @@ lodash.keyby@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.keyby/-/lodash.keyby-4.6.0.tgz#7f6a1abda93fd24e22728a4d361ed8bcba5a4354" integrity sha1-f2oavak/0k4icopNNh7YvLpaQ1Q= -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash.lowercase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz#46515aced4acb0b7093133333af068e4c3b14e9d" @@ -18329,11 +18299,6 @@ lodash.reject@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -18359,21 +18324,6 @@ lodash.startcase@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" integrity sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg= -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - lodash.template@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -18382,14 +18332,6 @@ lodash.template@^4.4.0: lodash._reinterpolate "^3.0.0" lodash.templatesettings "^4.0.0" -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.templatesettings@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" @@ -18442,11 +18384,6 @@ lodash@^3.10.1, lodash@^3.3.1: resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana3.tgz#c0e318245219eeeff535895c429e0cef5058a9ad" integrity sha512-HMfwwT2yAkEQNzHSR1BxgE5YcDMUaZ/skhNyjy1nvM/A4m0Kh940hLZeCqKBCsSaUJz/8A/9cQGd9BaAOCIBLg== -lodash@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" - integrity sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE= - lodash@~2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" @@ -18602,11 +18539,6 @@ lowlight@~1.9.1: dependencies: highlight.js "~9.12.0" -lru-cache@2, lru-cache@^2.6.5: - version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" - integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= - lru-cache@4.1.5, lru-cache@^4.0.1, lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -18623,6 +18555,11 @@ lru-cache@4.1.x, lru-cache@^4.0.0: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^2.6.5: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -18844,6 +18781,16 @@ marky@^1.2.1: resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.1.tgz#a3fcf82ffd357756b8b8affec9fdbf3a30dc1b02" integrity sha512-md9k+Gxa3qLH6sUKpeC2CNkJK/Ld+bEz5X96nYwloqphQE0CKCVEKco/6jxEZixinqNdz5RFi/KaCyfbMDMAXQ== +matchdep@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" + integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= + dependencies: + findup-sync "^2.0.0" + micromatch "^3.0.4" + resolve "^1.4.0" + stack-trace "0.0.10" + material-colors@^1.2.1: version "1.2.5" resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.5.tgz#5292593e6754cb1bcc2b98030e4e0d6a3afc9ea1" @@ -19254,21 +19201,6 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimatch@^2.0.1: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - integrity sha1-jQh8OcazjAAbl/ynzm0OHoCvusc= - dependencies: - brace-expansion "^1.0.0" - -minimatch@~0.2.11: - version "0.2.14" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" - integrity sha1-x054BXT2PG+aCQ6Q775u9TpqdWo= - dependencies: - lru-cache "2" - sigmund "~1.0.0" - minimist-options@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" @@ -19685,13 +19617,6 @@ multimatch@^4.0.0: arrify "^2.0.1" minimatch "^3.0.4" -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= - dependencies: - duplexer2 "0.0.2" - multistream@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/multistream/-/multistream-2.1.1.tgz#629d3a29bd76623489980d04519a2c365948148c" @@ -19725,6 +19650,11 @@ mutation-observer@^1.0.3: resolved "https://registry.yarnpkg.com/mutation-observer/-/mutation-observer-1.0.3.tgz#42e9222b101bca82e5ba9d5a7acf4a14c0f263d0" integrity sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA== +mute-stdout@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" + integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== + mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" @@ -19799,11 +19729,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -natives@^1.1.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.6.tgz#a603b4a498ab77173612b9ea1acdec4d980f00bb" - integrity sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -20572,7 +20497,7 @@ object.assign@4.1.0, object.assign@^4.0.4, object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.defaults@^1.1.0: +object.defaults@^1.0.0, object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= @@ -20635,6 +20560,14 @@ object.pick@^1.2.0, object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.reduce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" + integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + object.values@^1.0.4, object.values@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" @@ -20824,20 +20757,6 @@ ora@^3.0.0, ora@^3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" -orchestrator@^0.3.0: - version "0.3.8" - resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" - integrity sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4= - dependencies: - end-of-stream "~0.1.5" - sequencify "~0.0.7" - stream-consume "~0.1.0" - -ordered-read-streams@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" - integrity sha1-/VZamvjrRHO6abbtijQ1LLVS8SY= - ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" @@ -21749,17 +21668,6 @@ platform@^1.3.0: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -23701,7 +23609,7 @@ read-pkg@^5.1.1, read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1: +readable-stream@1.0, readable-stream@~1.0.17, readable-stream@~1.0.27-1: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= @@ -24171,7 +24079,7 @@ remove-bom-stream@^1.2.0: safe-buffer "^5.1.0" through2 "^2.0.3" -remove-trailing-separator@^1.0.1: +remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= @@ -24231,6 +24139,15 @@ replace-ext@1.0.0, replace-ext@^1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +replace-homedir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" + integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= + dependencies: + homedir-polyfill "^1.0.1" + is-absolute "^1.0.0" + remove-trailing-separator "^1.1.0" + request-progress@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" @@ -24569,7 +24486,7 @@ resolve@^1.11.1: dependencies: path-parse "^1.0.6" -resolve@^1.12.0: +resolve@^1.12.0, resolve@^1.4.0: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== @@ -24809,15 +24726,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -run-sequence@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495" - integrity sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw== - dependencies: - chalk "^1.1.3" - fancy-log "^1.3.2" - plugin-error "^0.1.2" - rw@1, rw@^1.3.2, rw@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" @@ -25153,6 +25061,13 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" +semver-greatest-satisfied-range@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" + integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= + dependencies: + sver-compat "^1.5.0" + semver-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9" @@ -25165,7 +25080,7 @@ semver-truncate@^1.0.0: dependencies: semver "^5.3.0" -"semver@2 || 3 || 4", semver@^4.1.0: +"semver@2 || 3 || 4": version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= @@ -25256,11 +25171,6 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -sequencify@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" - integrity sha1-kM/xnQLgcCf9dn9erT57ldHnOAw= - serialize-javascript@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" @@ -25480,11 +25390,6 @@ shot@4.x.x: hoek "5.x.x" joi "13.x.x" -sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -26048,7 +25953,7 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-trace@0.0.x: +stack-trace@0.0.10, stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= @@ -26154,11 +26059,6 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" -stream-consume@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" - integrity sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8= - stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -26167,6 +26067,11 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" +stream-exhaust@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" + integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== + stream-http@^2.7.2: version "2.8.0" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" @@ -26432,14 +26337,6 @@ strip-bom-string@1.X: resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= -strip-bom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" - integrity sha1-hbiGLzhEtabV7IRnqTWYFzo295Q= - dependencies: - first-chunk-stream "^1.0.0" - is-utf8 "^0.2.0" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -26696,6 +26593,14 @@ suricata-sid-db@^1.0.2: dependencies: typescript "^3.3.3333" +sver-compat@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" + integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= + dependencies: + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + svgo@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -27128,7 +27033,7 @@ through2-map@^3.0.0: through2 "~2.0.0" xtend "^4.0.0" -through2@2.X, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0: +through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= @@ -27136,14 +27041,6 @@ through2@2.X, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0 readable-stream "^2.1.5" xtend "~4.0.1" -through2@^0.6.1: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - through2@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" @@ -27169,7 +27066,7 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.2.tgz#a862e018e3fb1ea2ec3fce5d55605cf57f247371" integrity sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E= -tildify@^1.0.0, tildify@^1.2.0: +tildify@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" integrity sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo= @@ -28279,6 +28176,26 @@ underscore@~1.6.0: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= +undertaker-registry@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" + integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= + +undertaker@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.2.1.tgz#701662ff8ce358715324dfd492a4f036055dfe4b" + integrity sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA== + dependencies: + arr-flatten "^1.0.1" + arr-map "^2.0.0" + bach "^1.0.0" + collection-map "^1.0.0" + es6-weak-map "^2.0.1" + last-run "^1.1.0" + object.defaults "^1.0.0" + object.reduce "^1.0.0" + undertaker-registry "^1.0.0" + unfetch@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" @@ -28392,11 +28309,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" - integrity sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs= - unique-stream@^2.0.2: version "2.2.1" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" @@ -28660,11 +28572,6 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= - user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -28809,12 +28716,12 @@ v8-compile-cache@2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== -v8flags@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" - integrity sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ= +v8flags@^3.0.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.1.3.tgz#fc9dc23521ca20c5433f81cc4eb9b3033bb105d8" + integrity sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w== dependencies: - user-home "^1.1.1" + homedir-polyfill "^1.0.1" val-loader@^1.1.1: version "1.1.1" @@ -29305,21 +29212,7 @@ vinyl-file@^2.0.0: strip-bom-stream "^2.0.0" vinyl "^1.1.0" -vinyl-fs@^0.3.0: - version "0.3.14" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" - integrity sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY= - dependencies: - defaults "^1.0.0" - glob-stream "^3.1.5" - glob-watcher "^0.0.6" - graceful-fs "^3.0.0" - mkdirp "^0.5.0" - strip-bom "^1.0.0" - through2 "^0.6.1" - vinyl "^0.4.0" - -vinyl-fs@^3.0.3: +vinyl-fs@^3.0.0, vinyl-fs@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== @@ -29362,23 +29255,6 @@ vinyl-sourcemaps-apply@^0.2.0: dependencies: source-map "^0.5.1" -vinyl@^0.4.0: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - integrity sha1-LzVsh6VQolVGHza76ypbqL94SEc= - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - vinyl@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" @@ -30287,7 +30163,7 @@ xregexp@4.2.4: dependencies: "@babel/runtime-corejs2" "^7.2.0" -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= @@ -30496,7 +30372,7 @@ yargs@^14.0.0: y18n "^4.0.0" yargs-parser "^13.1.1" -yargs@^7.0.0: +yargs@^7.0.0, yargs@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= @@ -30569,10 +30445,10 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" -yazl@^2.1.0: - version "2.4.3" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" - integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= +yazl@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" + integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== dependencies: buffer-crc32 "~0.2.3" From cbaf2ef0d410c8a85a5ee8f32aaaa876cc0670c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 14:35:19 -0700 Subject: [PATCH 28/33] Update jsonwebtoken related packages (#47400) --- x-pack/package.json | 4 ++-- yarn.lock | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/x-pack/package.json b/x-pack/package.json index 333d74985f9fc..d775b0bc26464 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -70,7 +70,7 @@ "@types/js-yaml": "^3.11.1", "@types/jsdom": "^12.2.4", "@types/json-stable-stringify": "^1.0.32", - "@types/jsonwebtoken": "^7.2.7", + "@types/jsonwebtoken": "^7.2.8", "@types/lodash": "^3.10.1", "@types/mapbox-gl": "^0.54.1", "@types/memoize-one": "^4.1.0", @@ -270,7 +270,7 @@ "jquery": "^3.4.1", "js-yaml": "3.13.1", "json-stable-stringify": "^1.0.1", - "jsonwebtoken": "^8.3.0", + "jsonwebtoken": "^8.5.1", "jsts": "^1.6.2", "lodash": "npm:@elastic/lodash@3.10.1-kibana3", "lodash.keyby": "^4.6.0", diff --git a/yarn.lock b/yarn.lock index 18dc51000d7f4..d0e35bdba67ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3589,7 +3589,7 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818" integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA== -"@types/jsonwebtoken@^7.2.7": +"@types/jsonwebtoken@^7.2.8": version "7.2.8" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz#8d199dab4ddb5bba3234f8311b804d2027af2b3a" integrity sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw== @@ -10655,6 +10655,13 @@ ecdsa-sig-formatter@1.0.10: dependencies: safe-buffer "^5.0.1" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + editions@^1.1.1, editions@^1.3.3, editions@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" @@ -17283,6 +17290,22 @@ jsonwebtoken@^8.3.0: lodash.once "^4.0.0" ms "^2.1.1" +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -17379,6 +17402,15 @@ jwa@^1.1.5: ecdsa-sig-formatter "1.0.10" safe-buffer "^5.0.1" +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + jws@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f" @@ -17387,6 +17419,14 @@ jws@^3.1.5: jwa "^1.1.5" safe-buffer "^5.0.1" +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + karma-chrome-launcher@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" From 1da0469521f8d33788acc17b840eb9459312e120 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 14:36:02 -0700 Subject: [PATCH 29/33] Update webpack related packages (#47402) --- package.json | 8 +- .../package.json | 2 +- packages/kbn-interpreter/package.json | 6 +- packages/kbn-pm/package.json | 4 +- packages/kbn-ui-framework/package.json | 6 +- yarn.lock | 213 +++++++++--------- 6 files changed, 125 insertions(+), 114 deletions(-) diff --git a/package.json b/package.json index f89a2f15136d5..ef6aba59139fb 100644 --- a/package.json +++ b/package.json @@ -249,7 +249,7 @@ "tslib": "^1.9.3", "type-detect": "^4.0.8", "ui-select": "0.19.8", - "url-loader": "2.1.0", + "url-loader": "2.2.0", "uuid": "3.3.2", "val-loader": "^1.1.1", "validate-npm-package-name": "2.2.2", @@ -258,8 +258,8 @@ "vega-schema-url-parser": "1.0.0", "vega-tooltip": "^0.9.14", "vision": "^5.3.3", - "webpack": "4.39.2", - "webpack-merge": "4.2.1", + "webpack": "4.41.0", + "webpack-merge": "4.2.2", "whatwg-fetch": "^3.0.0", "yauzl": "2.10.0" }, @@ -359,7 +359,7 @@ "chai": "3.5.0", "chance": "1.0.18", "cheerio": "0.22.0", - "chokidar": "3.0.2", + "chokidar": "3.2.1", "chromedriver": "^77.0.0", "classnames": "2.2.6", "dedent": "^0.7.0", diff --git a/packages/kbn-eslint-import-resolver-kibana/package.json b/packages/kbn-eslint-import-resolver-kibana/package.json index 14a9224924260..9fae27011767e 100755 --- a/packages/kbn-eslint-import-resolver-kibana/package.json +++ b/packages/kbn-eslint-import-resolver-kibana/package.json @@ -16,6 +16,6 @@ "glob-all": "^3.1.0", "lru-cache": "^4.1.5", "resolve": "^1.7.1", - "webpack": "^4.39.2" + "webpack": "^4.41.0" } } diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index a9b30c17e2927..f2aa2e1ced9cd 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -30,8 +30,8 @@ "sass-loader": "^7.3.1", "style-loader": "0.23.1", "supports-color": "^7.0.0", - "url-loader": "2.1.0", - "webpack": "4.39.2", - "webpack-cli": "^3.3.7" + "url-loader": "2.2.0", + "webpack": "4.41.0", + "webpack-cli": "^3.3.9" } } diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index fd2d20e66fd48..8480e74ceb3a2 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -61,8 +61,8 @@ "tempy": "^0.3.0", "typescript": "3.5.3", "unlazy-loader": "^0.1.3", - "webpack": "^4.39.2", - "webpack-cli": "^3.3.7", + "webpack": "^4.41.0", + "webpack-cli": "^3.3.9", "wrap-ansi": "^3.0.1", "write-pkg": "^4.0.0" }, diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index bd9658934100d..cb4d2e3cadf95 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -37,7 +37,7 @@ "babel-loader": "^8.0.6", "brace": "0.11.1", "chalk": "^2.4.2", - "chokidar": "3.0.2", + "chokidar": "3.2.1", "core-js": "^3.2.1", "css-loader": "^2.1.1", "expose-loader": "^0.7.5", @@ -68,8 +68,8 @@ "sass-loader": "^7.3.1", "sinon": "^7.4.2", "style-loader": "^0.23.1", - "webpack": "^4.39.2", - "webpack-dev-server": "^3.8.0", + "webpack": "^4.41.0", + "webpack-dev-server": "^3.8.2", "yeoman-generator": "1.1.1", "yo": "2.0.6" } diff --git a/yarn.lock b/yarn.lock index d0e35bdba67ef..407324bb1dd45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5078,10 +5078,10 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.2.tgz#ddb3a8495d44875423af7b919aace11e91732a41" - integrity sha512-rUe9SxpRQlVg4EM8It7JMNWWYHAirTPpbTuvaSKybb5IejNgWB3PGBBX9rrPKDx2pM/p3Wh+7+ASaWRyyAbxmQ== +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -6889,7 +6889,7 @@ braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1, braces@^3.0.2: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -7773,22 +7773,22 @@ chokidar@2.1.2, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" -chokidar@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681" - integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA== - dependencies: - anymatch "^3.0.1" - braces "^3.0.2" - glob-parent "^5.0.0" - is-binary-path "^2.1.0" - is-glob "^4.0.1" - normalize-path "^3.0.0" - readdirp "^3.1.1" +chokidar@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.1.tgz#4634772a1924512d990d4505957bf3a510611387" + integrity sha512-/j5PPkb5Feyps9e+jo07jUZGvkB5Aj953NrI4s8xSVScrAo/RHeILrtdb4uzR7N6aaFFxxJ+gt8mA8HfNpw76w== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.1.3" optionalDependencies: - fsevents "^2.0.6" + fsevents "~2.1.0" -chokidar@^2.0.0, chokidar@^2.1.2: +chokidar@^2.0.0, chokidar@^2.1.2, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -7807,25 +7807,6 @@ chokidar@^2.0.0, chokidar@^2.1.2: optionalDependencies: fsevents "^1.2.7" -chokidar@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" - integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - chownr@^1.0.1, chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" @@ -13045,10 +13026,10 @@ fsevents@^1.2.7: nan "^2.9.2" node-pre-gyp "^0.10.0" -fsevents@^2.0.6: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a" - integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ== +fsevents@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.0.tgz#ce1a5f9ac71c6d75278b0c5bd236d7dfece4cbaa" + integrity sha512-+iXhW3LuDQsno8dOIrCIT/CBjeBWuP7PXe8w9shnj9Lebny/Gx1ZjVBYwexLz36Ri2jKuXMNpV6CYNh8lHHgrQ== fstream@^1.0.0: version "1.0.11" @@ -13422,6 +13403,13 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + glob-stream@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" @@ -14951,7 +14939,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-middleware@^0.19.1: +http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== @@ -15656,10 +15644,10 @@ irregular-plurals@^1.0.0: resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= -is-absolute-url@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.1.tgz#e315cbdcbbc3d6789532d591954ac78a0e5049f6" - integrity sha512-c2QjUwuMxLsld90sj3xYzpFYWJtuxkIn1f5ua9RTEYJt/vV2IsM+Py00/6qjV7qExgifUvt7qfyBGBBKm+2iBg== +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== is-absolute@^1.0.0: version "1.0.0" @@ -15720,7 +15708,7 @@ is-binary-path@^2.0.0: dependencies: binary-extensions "^1.0.0" -is-binary-path@^2.1.0: +is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== @@ -15917,7 +15905,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.1: +is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -18495,10 +18483,10 @@ loglevel@^1.6.1: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po= -loglevel@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280" - integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA== +loglevel@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" + integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g== loglevelnext@^1.0.1: version "1.0.5" @@ -19960,10 +19948,10 @@ node-fetch@^2.6.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-forge@0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" - integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== node-forge@^0.7.6: version "0.7.6" @@ -20240,7 +20228,7 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -21797,10 +21785,10 @@ popper.js@^1.14.4: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== -portfinder@^1.0.21: - version "1.0.23" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.23.tgz#894db4bcc5daf02b6614517ce89cd21a38226b82" - integrity sha512-B729mL/uLklxtxuiJKfQ84WPxNw5a7Yhx3geQZdcA4GjNjZSTSSMMWyoennMVnTWSmAR0lMdzWYN0JLnHrg1KQ== +portfinder@^1.0.24: + version "1.0.24" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.24.tgz#11efbc6865f12f37624b6531ead1d809ed965cfa" + integrity sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg== dependencies: async "^1.5.2" debug "^2.2.0" @@ -23709,10 +23697,10 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.2.tgz#fa85d2d14d4289920e4671dead96431add2ee78a" - integrity sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw== +readdirp@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.3.tgz#d6e011ed5b9240a92f08651eeb40f7942ceb6cc1" + integrity sha512-ZOsfTGkjO2kqeR5Mzr5RYDbTGYneSkdNKX2fOX2P5jF7vMrd/GNnIAUtDldeHHumHUCQ3V05YfWUdxMPAsRu9Q== dependencies: picomatch "^2.0.4" @@ -25082,12 +25070,12 @@ selenium-webdriver@^4.0.0-alpha.4: tmp "0.0.30" xml2js "^0.4.19" -selfsigned@^1.10.4: - version "1.10.4" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" - integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== dependencies: - node-forge "0.7.5" + node-forge "0.9.0" semaphore-async-await@^1.5.1: version "1.5.1" @@ -25664,6 +25652,18 @@ sockjs-client@1.3.0: json3 "^3.3.2" url-parse "^1.4.3" +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + sockjs@0.3.19: version "0.3.19" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" @@ -28544,14 +28544,14 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.1.0.tgz#bcc1ecabbd197e913eca23f5e0378e24b4412961" - integrity sha512-kVrp/8VfEm5fUt+fl2E0FQyrpmOYgMEkBsv8+UDP1wFhszECq5JyGF33I7cajlVY90zRZ6MyfgKXngLvHYZX8A== +url-loader@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.2.0.tgz#af321aece1fd0d683adc8aaeb27829f29c75b46e" + integrity sha512-G8nk3np8ZAnwhHXas1JxJEwJyQdqFXAKJehfgZ/XrC48volFBRtO+FIKtF2u0Ma3bw+4vnDVjHPAQYlF9p2vsw== dependencies: loader-utils "^1.2.3" mime "^2.4.4" - schema-utils "^2.0.0" + schema-utils "^2.4.1" url-loader@^1.1.2: version "1.1.2" @@ -29496,10 +29496,10 @@ webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-cli@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.7.tgz#77c8580dd8e92f69d635e0238eaf9d9c15759a91" - integrity sha512-OhTUCttAsr+IZSMVwGROGRHvT+QAs8H6/mHIl4SvhAwYywjiylYjpwybGx7WQ9Hkb45FhjtsymkwiRRbGJ1SZQ== +webpack-cli@^3.3.9: + version "3.3.9" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz#79c27e71f94b7fe324d594ab64a8e396b9daa91a" + integrity sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A== dependencies: chalk "2.4.2" cross-spawn "6.0.5" @@ -29523,41 +29523,52 @@ webpack-dev-middleware@^3.7.0: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz#06cc4fc2f440428508d0e9770da1fef10e5ef28d" - integrity sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ== +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9" + integrity sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" - chokidar "^2.1.6" + chokidar "^2.1.8" compression "^1.7.4" connect-history-api-fallback "^1.6.0" debug "^4.1.1" del "^4.1.1" express "^4.17.1" html-entities "^1.2.1" - http-proxy-middleware "^0.19.1" + http-proxy-middleware "0.19.1" import-local "^2.0.0" internal-ip "^4.3.0" ip "^1.1.5" - is-absolute-url "^3.0.0" + is-absolute-url "^3.0.3" killable "^1.0.1" - loglevel "^1.6.3" + loglevel "^1.6.4" opn "^5.5.0" p-retry "^3.0.1" - portfinder "^1.0.21" + portfinder "^1.0.24" schema-utils "^1.0.0" - selfsigned "^1.10.4" + selfsigned "^1.10.7" semver "^6.3.0" serve-index "^1.9.1" sockjs "0.3.19" - sockjs-client "1.3.0" + sockjs-client "1.4.0" spdy "^4.0.1" strip-ansi "^3.0.1" supports-color "^6.1.0" url "^0.11.0" - webpack-dev-middleware "^3.7.0" + webpack-dev-middleware "^3.7.2" webpack-log "^2.0.0" ws "^6.2.1" yargs "12.0.5" @@ -29590,12 +29601,12 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4" - integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw== +webpack-merge@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== dependencies: - lodash "^4.17.5" + lodash "^4.17.15" webpack-sources@^1.1.0: version "1.3.0" @@ -29613,10 +29624,10 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.39.2, webpack@^4.39.2: - version "4.39.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.2.tgz#c9aa5c1776d7c309d1b3911764f0288c8c2816aa" - integrity sha512-AKgTfz3xPSsEibH00JfZ9sHXGUwIQ6eZ9tLN8+VLzachk1Cw2LVmy+4R7ZiwTa9cZZ15tzySjeMui/UnSCAZhA== +webpack@4.41.0, webpack@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" + integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5" From 3ea2771ce4c2bd3aac798bffc758dd77f5c47293 Mon Sep 17 00:00:00 2001 From: Spencer Date: Sat, 5 Oct 2019 20:51:07 -0700 Subject: [PATCH 30/33] try running fewer jobs in parallel on the same worker (#47403) --- Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index ec0bc2327321a..c0afcac731ff5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,6 +17,8 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'oss-ciGroup4': getOssCiGroupWorker(4), 'oss-ciGroup5': getOssCiGroupWorker(5), 'oss-ciGroup6': getOssCiGroupWorker(6), + ]), + 'kibana-oss-agent2': withWorkers('kibana-oss-tests2', { buildOss() }, [ 'oss-ciGroup7': getOssCiGroupWorker(7), 'oss-ciGroup8': getOssCiGroupWorker(8), 'oss-ciGroup9': getOssCiGroupWorker(9), @@ -32,6 +34,8 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'xpack-ciGroup3': getXpackCiGroupWorker(3), 'xpack-ciGroup4': getXpackCiGroupWorker(4), 'xpack-ciGroup5': getXpackCiGroupWorker(5), + ]), + 'kibana-xpack-agent2': withWorkers('kibana-xpack-tests2', { buildXpack() }, [ 'xpack-ciGroup6': getXpackCiGroupWorker(6), 'xpack-ciGroup7': getXpackCiGroupWorker(7), 'xpack-ciGroup8': getXpackCiGroupWorker(8), From 95fd7fdc91067de4134e3028fe408455f8103b1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 23:33:21 -0700 Subject: [PATCH 31/33] Update dependency idx to ^2.5.6 (#47399) --- x-pack/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/package.json b/x-pack/package.json index d775b0bc26464..e0e82b03ca9bf 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -259,7 +259,7 @@ "humps": "2.0.1", "i18n-iso-countries": "^4.3.1", "icalendar": "0.7.1", - "idx": "^2.5.2", + "idx": "^2.5.6", "immer": "^1.5.0", "inline-style": "^2.0.0", "intl": "^1.2.5", diff --git a/yarn.lock b/yarn.lock index 407324bb1dd45..51ab27f0acc34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15072,10 +15072,10 @@ icss-utils@^4.1.0: dependencies: postcss "^7.0.14" -idx@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.2.tgz#4b405c2e6d68d04136e0a368a7ab35b9caa0595f" - integrity sha512-MLoGF4lQU5q/RqJJjRsuid52emu7tPVtSSZaYXsqRvSjvXdBEmIwk2urvbNvPBRU9Ox9I4WYnxiz2GjhU34Lrw== +idx@^2.5.6: + version "2.5.6" + resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.6.tgz#1f824595070100ae9ad585c86db08dc74f83a59d" + integrity sha512-WFXLF7JgPytbMgelpRY46nHz5tyDcedJ76pLV+RJWdb8h33bxFq4bdZau38DhNSzk5eVniBf1K3jwfK+Lb5nYA== iedriver@^3.14.1: version "3.14.1" From 35751f9828ecf4be855d1cbe6dc2021e55c2830e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 23:38:41 -0700 Subject: [PATCH 32/33] Update gulp related packages (#47421) --- packages/kbn-plugin-helpers/package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index d6818c4602ef4..215963c0769bb 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -21,7 +21,7 @@ "globby": "^8.0.1", "gulp-babel": "^8.0.0", "gulp-rename": "1.4.0", - "gulp-zip": "5.0.0", + "gulp-zip": "5.0.1", "inquirer": "^1.2.2", "minimatch": "^3.0.4", "node-sass": "^4.9.4", diff --git a/x-pack/package.json b/x-pack/package.json index e0e82b03ca9bf..23a6e0ea0d754 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -137,7 +137,7 @@ "graphql-codegen-typescript-resolvers-template": "^0.13.0", "graphql-codegen-typescript-template": "^0.13.0", "gulp": "4.0.2", - "gulp-mocha": "^7.0.1", + "gulp-mocha": "^7.0.2", "gulp-multi-process": "1.3.1", "hapi": "^17.5.3", "jest": "^24.9.0", diff --git a/yarn.lock b/yarn.lock index 51ab27f0acc34..f2c6874078368 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14250,10 +14250,10 @@ gulp-cli@^2.2.0: v8flags "^3.0.1" yargs "^7.1.0" -gulp-mocha@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/gulp-mocha/-/gulp-mocha-7.0.1.tgz#cd29f2fc214a8c08c7d96bf13927d539385a856d" - integrity sha512-LYBEWdOw52kvP+si91iR00LYX9iKXLTBjcKh9b3ChHvVmKtpoITjeRFslPEzDubEk+z6VI1ONEwn9ABqW9/tig== +gulp-mocha@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/gulp-mocha/-/gulp-mocha-7.0.2.tgz#c7e13d133b3fde96d777e877f90b46225255e408" + integrity sha512-ZXBGN60TXYnFhttr19mfZBOtlHYGx9SvCSc+Kr/m2cMIGloUe176HBPwvPqlakPuQgeTGVRS47NmcdZUereKMQ== dependencies: dargs "^7.0.0" execa "^2.0.4" @@ -14291,10 +14291,10 @@ gulp-sourcemaps@2.6.5: strip-bom-string "1.X" through2 "2.X" -gulp-zip@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.0.tgz#ba1af3247e7acc4dc01a72a77c568654cfb1dd1f" - integrity sha512-oR3t8kn+ccHkSyRcBV5kBLPXrhqTh5d6wBAR7r7wqjNQNBhYvOwPedCwlAaGcNl1qSeXNDn6qOk1Qyxvx9Wrow== +gulp-zip@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.1.tgz#0dba5094901b0d91bcc8b53b3f6ff797c0f2002d" + integrity sha512-M/IWLh9RvOpuofDZkgDirtiyz9J3yIqnDOJ3muzk2D/XnZ1ruqPlPLRIpXnl/aZU+xXwKPdOIxjRzkUcVEQyZQ== dependencies: get-stream "^5.1.0" plugin-error "^1.0.1" From 2389fe8e1a1e1c0e9ab44e91e964a623525f99b1 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Sun, 6 Oct 2019 14:12:52 +0300 Subject: [PATCH 33/33] Remove ui/persisted_log - Part 2 (#47236) * Added storage interface to avoid importing ui/storage * Move persisted log into data plugin / query * Fix jest tests * import getQueryLog * Add store to graph search bar * Added window localStorage * Deleted persisted log folder --- src/legacy/core_plugins/data/public/plugin.ts | 1 + .../data/public/query}/persisted_log/index.ts | 3 +-- .../persisted_log/persisted_log.test.ts | 0 .../query}/persisted_log/persisted_log.ts | 6 ++--- .../components/query_bar_input.test.mocks.ts | 2 +- .../query_bar/components/query_bar_input.tsx | 11 +++++----- .../components/query_bar_top_row.tsx | 7 +++--- .../query/query_bar/lib/get_query_log.ts | 16 +++++++++----- .../components/create_search_bar.tsx | 2 +- .../public/shim/legacy_dependencies_plugin.ts | 2 ++ .../data/public/timefilter/time_history.ts | 7 +++--- .../public/timefilter/timefilter_service.ts | 6 +++-- src/legacy/core_plugins/data/public/types.ts | 7 ++++++ .../ui/public/agg_types/buckets/filters.js | 4 +++- .../public/persisted_log/recently_accessed.ts | 22 ------------------- .../public/components/search_bar.test.tsx | 3 +++ 16 files changed, 49 insertions(+), 50 deletions(-) rename src/legacy/{ui/public => core_plugins/data/public/query}/persisted_log/index.ts (88%) rename src/legacy/{ui/public => core_plugins/data/public/query}/persisted_log/persisted_log.test.ts (100%) rename src/legacy/{ui/public => core_plugins/data/public/query}/persisted_log/persisted_log.ts (95%) delete mode 100644 src/legacy/ui/public/persisted_log/recently_accessed.ts diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index a5aa55673cac6..b487651175d47 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -107,6 +107,7 @@ export class DataPlugin const timefilterService = this.timefilter.setup({ uiSettings, + store: __LEGACY.storage, }); this.setupApi = { indexPatterns: indexPatternsService, diff --git a/src/legacy/ui/public/persisted_log/index.ts b/src/legacy/core_plugins/data/public/query/persisted_log/index.ts similarity index 88% rename from src/legacy/ui/public/persisted_log/index.ts rename to src/legacy/core_plugins/data/public/query/persisted_log/index.ts index 52de69b9c50bf..9b21c748da02d 100644 --- a/src/legacy/ui/public/persisted_log/index.ts +++ b/src/legacy/core_plugins/data/public/query/persisted_log/index.ts @@ -17,5 +17,4 @@ * under the License. */ -export { PersistedLog } from './persisted_log'; -export { recentlyAccessed } from './recently_accessed'; +export * from './persisted_log'; diff --git a/src/legacy/ui/public/persisted_log/persisted_log.test.ts b/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.test.ts similarity index 100% rename from src/legacy/ui/public/persisted_log/persisted_log.test.ts rename to src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.test.ts diff --git a/src/legacy/ui/public/persisted_log/persisted_log.ts b/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts similarity index 95% rename from src/legacy/ui/public/persisted_log/persisted_log.ts rename to src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts index 0824d17757311..e0e6a0d0c44e4 100644 --- a/src/legacy/ui/public/persisted_log/persisted_log.ts +++ b/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts @@ -20,9 +20,7 @@ import _ from 'lodash'; import * as Rx from 'rxjs'; import { map } from 'rxjs/operators'; -import { Storage } from 'ui/storage'; - -const localStorage = new Storage(window.localStorage); +import { Storage } from '../../types'; const defaultIsDuplicate = (oldItem: any, newItem: any) => { return _.isEqual(oldItem, newItem); @@ -44,7 +42,7 @@ export class PersistedLog { private update$ = new Rx.BehaviorSubject(undefined); - constructor(name: string, options: PersistedLogOptions = {}, storage = localStorage) { + constructor(name: string, options: PersistedLogOptions = {}, storage: Storage) { this.name = name; this.maxLength = typeof options.maxLength === 'string' diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts index 683ced28dba97..80ee38ea1b076 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts @@ -45,7 +45,7 @@ export const mockFetchIndexPatterns = jest .fn() .mockReturnValue(Promise.resolve([mockIndexPattern])); -jest.mock('ui/persisted_log', () => ({ +jest.mock('../../persisted_log', () => ({ PersistedLog: mockPersistedLogFactory, })); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 3cdd8d4b9c40c..685a793b4de3d 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -25,7 +25,6 @@ import { EuiFieldText, EuiOutsideClickDetector, PopoverAnchorPosition } from '@e import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { debounce, compact, isEqual } from 'lodash'; -import { PersistedLog } from 'ui/persisted_log'; import { AutocompleteSuggestion, @@ -36,11 +35,11 @@ import { KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; import { IndexPattern, StaticIndexPattern } from '../../../index_patterns'; -import { Query } from '../index'; +import { Query, getQueryLog } from '../index'; import { fromUser, matchPairs, toUser } from '../lib'; import { QueryLanguageSwitcher } from './language_switcher'; import { SuggestionsComponent } from './typeahead/suggestions_component'; -import { getQueryLog } from '../lib/get_query_log'; +import { PersistedLog } from '../../persisted_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { IDataPluginServices } from '../../../types'; @@ -392,6 +391,7 @@ export class QueryBarInputUI extends Component { }; public componentDidMount() { + const { uiSettings, store, appName } = this.services; const parsedQuery = fromUser(toUser(this.props.query.query)); if (!isEqual(this.props.query.query, parsedQuery)) { this.onChange({ ...this.props.query, query: parsedQuery }); @@ -399,12 +399,13 @@ export class QueryBarInputUI extends Component { this.persistedLog = this.props.persistedLog ? this.props.persistedLog - : getQueryLog(this.services.uiSettings, this.services.appName, this.props.query.language); + : getQueryLog(uiSettings, store, appName, this.props.query.language); this.fetchIndexPatterns().then(this.updateSuggestions); } public componentDidUpdate(prevProps: Props) { + const { uiSettings, store, appName } = this.services; const parsedQuery = fromUser(toUser(this.props.query.query)); if (!isEqual(this.props.query.query, parsedQuery)) { this.onChange({ ...this.props.query, query: parsedQuery }); @@ -412,7 +413,7 @@ export class QueryBarInputUI extends Component { this.persistedLog = this.props.persistedLog ? this.props.persistedLog - : getQueryLog(this.services.uiSettings, this.services.appName, this.props.query.language); + : getQueryLog(uiSettings, store, appName, this.props.query.language); if (!isEqual(prevProps.indexPatterns, this.props.indexPatterns)) { this.fetchIndexPatterns().then(this.updateSuggestions); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx index 6895c9ecd018c..716bb677b946f 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx @@ -22,7 +22,6 @@ import { doesKueryExpressionHaveLuceneSyntaxError } from '@kbn/es-query'; import classNames from 'classnames'; import React, { useState, useEffect } from 'react'; import { documentationLinks } from 'ui/documentation_links'; -import { PersistedLog } from 'ui/persisted_log'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSuperDatePicker } from '@elastic/eui'; // @ts-ignore @@ -34,10 +33,10 @@ import { useKibana } from '../../../../../../../plugins/kibana_react/public'; import { IndexPattern } from '../../../index_patterns'; import { QueryBarInput } from './query_bar_input'; -import { getQueryLog } from '../lib/get_query_log'; -import { Query } from '../index'; +import { Query, getQueryLog } from '../index'; import { TimeHistoryContract } from '../../../timefilter'; import { IDataPluginServices } from '../../../types'; +import { PersistedLog } from '../../persisted_log'; interface Props { query?: Query; @@ -72,7 +71,7 @@ function QueryBarTopRowUI(props: Props) { useEffect(() => { if (!props.query) return; - persistedLog = getQueryLog(uiSettings!, appName, props.query.language); + persistedLog = getQueryLog(uiSettings!, store, appName, props.query.language); }, [queryLanguage]); function onClickSubmitButton(event: React.MouseEvent) { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts index 70dc1d3fe700e..8b26e14c6ed7b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts @@ -17,16 +17,22 @@ * under the License. */ -import { PersistedLog } from 'ui/persisted_log'; import { UiSettingsClientContract } from 'src/core/public'; +import { PersistedLog } from '../../persisted_log'; +import { Storage } from '../../../types'; export function getQueryLog( uiSettings: UiSettingsClientContract, + store: Storage, appName: string, language: string ) { - return new PersistedLog(`typeahead:${appName}-${language}`, { - maxLength: uiSettings.get('history:limit'), - filterDuplicates: true, - }); + return new PersistedLog( + `typeahead:${appName}-${language}`, + { + maxLength: uiSettings.get('history:limit'), + filterDuplicates: true, + }, + store + ); } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx index add49e47971d3..bf40cb8e7e0b6 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx @@ -20,8 +20,8 @@ import React from 'react'; import { Filter } from '@kbn/es-query'; import { CoreStart } from 'src/core/public'; -import { Storage } from 'ui/storage'; import { AutocompletePublicPluginStart } from 'src/plugins/data/public'; +import { Storage } from '../../../types'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { TimefilterSetup } from '../../../timefilter'; import { FilterManager, SearchBar } from '../../../'; diff --git a/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts index 126754388f13f..511482c6239fb 100644 --- a/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts +++ b/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts @@ -25,6 +25,7 @@ import { initLegacyModule } from './legacy_module'; /** @internal */ export interface LegacyDependenciesPluginSetup { savedObjectsClient: any; + storage: Storage; } export interface LegacyDependenciesPluginStart { @@ -37,6 +38,7 @@ export class LegacyDependenciesPlugin implements Plugin { return { savedObjectsClient: chrome.getSavedObjectsClient(), + storage: new Storage(window.localStorage), } as LegacyDependenciesPluginSetup; } diff --git a/src/legacy/core_plugins/data/public/timefilter/time_history.ts b/src/legacy/core_plugins/data/public/timefilter/time_history.ts index 7dcd5843ae530..22778d1adea3c 100644 --- a/src/legacy/core_plugins/data/public/timefilter/time_history.ts +++ b/src/legacy/core_plugins/data/public/timefilter/time_history.ts @@ -19,12 +19,13 @@ import moment from 'moment'; import { TimeRange } from 'src/plugins/data/public'; -import { PersistedLog } from 'ui/persisted_log'; +import { PersistedLog } from '../query/persisted_log'; +import { Storage } from '../types'; export class TimeHistory { private history: PersistedLog; - constructor() { + constructor(store: Storage) { const historyOptions = { maxLength: 10, filterDuplicates: true, @@ -32,7 +33,7 @@ export class TimeHistory { return oldItem.from === newItem.from && oldItem.to === newItem.to; }, }; - this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions); + this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions, store); } add(time: TimeRange) { diff --git a/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts b/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts index 96c490a195d3d..cda9b93ef08aa 100644 --- a/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts +++ b/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts @@ -19,6 +19,7 @@ import { UiSettingsClientContract } from 'src/core/public'; import { TimeHistory, Timefilter, TimeHistoryContract, TimefilterContract } from './index'; +import { Storage } from '../types'; /** * Filter Service @@ -27,15 +28,16 @@ import { TimeHistory, Timefilter, TimeHistoryContract, TimefilterContract } from export interface TimeFilterServiceDependencies { uiSettings: UiSettingsClientContract; + store: Storage; } export class TimefilterService { - public setup({ uiSettings }: TimeFilterServiceDependencies): TimefilterSetup { + public setup({ uiSettings, store }: TimeFilterServiceDependencies): TimefilterSetup { const timefilterConfig = { timeDefaults: uiSettings.get('timepicker:timeDefaults'), refreshIntervalDefaults: uiSettings.get('timepicker:refreshIntervalDefaults'), }; - const history = new TimeHistory(); + const history = new TimeHistory(store); const timefilter = new Timefilter(timefilterConfig, history); return { diff --git a/src/legacy/core_plugins/data/public/types.ts b/src/legacy/core_plugins/data/public/types.ts index 4b7a5c1402ea7..0ef7e483dd63f 100644 --- a/src/legacy/core_plugins/data/public/types.ts +++ b/src/legacy/core_plugins/data/public/types.ts @@ -20,6 +20,13 @@ import { UiSettingsClientContract, CoreStart } from 'src/core/public'; import { AutocompletePublicPluginStart } from 'src/plugins/data/public'; +export interface Storage { + get: (key: string) => any; + set: (key: string, value: any) => void; + remove: (key: string) => any; + clear: () => void; +} + export interface IDataPluginServices extends Partial { appName: string; uiSettings: UiSettingsClientContract; diff --git a/src/legacy/ui/public/agg_types/buckets/filters.js b/src/legacy/ui/public/agg_types/buckets/filters.js index 9b7795d8fd9fa..19be75ccf8c1e 100644 --- a/src/legacy/ui/public/agg_types/buckets/filters.js +++ b/src/legacy/ui/public/agg_types/buckets/filters.js @@ -24,6 +24,7 @@ import { BucketAggType } from './_bucket_agg_type'; import { createFilterFilters } from './create_filter/filters'; import { FiltersParamEditor } from '../../vis/editors/default/controls/filters'; import { i18n } from '@kbn/i18n'; +import { Storage } from 'ui/storage'; import chrome from 'ui/chrome'; import { buildEsQuery } from '@kbn/es-query'; @@ -31,6 +32,7 @@ import { setup as data } from '../../../../core_plugins/data/public/legacy'; const { getQueryLog } = data.query.helpers; const config = chrome.getUiSettingsClient(); +const storage = new Storage(window.localStorage); export const filtersBucketAgg = new BucketAggType({ name: 'filters', @@ -50,7 +52,7 @@ export const filtersBucketAgg = new BucketAggType({ if (!_.size(inFilters)) return; inFilters.forEach((filter) => { - const persistedLog = getQueryLog(config, 'filtersAgg', filter.input.language); + const persistedLog = getQueryLog(config, storage, 'filtersAgg', filter.input.language); persistedLog.add(filter.input.query); }); diff --git a/src/legacy/ui/public/persisted_log/recently_accessed.ts b/src/legacy/ui/public/persisted_log/recently_accessed.ts deleted file mode 100644 index 6d984d155d551..0000000000000 --- a/src/legacy/ui/public/persisted_log/recently_accessed.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { npStart } from '../new_platform'; - -export const recentlyAccessed = npStart.core.chrome.recentlyAccessed; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx index 3c37c77e6d450..760b2aba5ba16 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx @@ -40,6 +40,9 @@ function wrapSearchBarInContext(testProps: OuterSearchBarProps) { notifications: {} as CoreStart['notifications'], http: {} as CoreStart['http'], overlays: {} as CoreStart['overlays'], + store: { + get: () => {}, + }, }; return (