From 43cf97e1f2fb7880c47df6b43d2998854d2cdeae Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Fri, 2 Oct 2020 16:46:12 -0400 Subject: [PATCH 01/10] [Security Solution] [Detections] Only display actions options if user has "read" privileges (#78812) * adds new 'can_read_actions' property to privileges api * only display rule actions piece if user has 'read' privileges for actions * display dropdown with custom text telling user they do not have read privileges for actions * fixes type error * update tests * utilize application capabilities instead of making a server request * remove changes to route tests * don't show form unless user has read permissions for actions, display text saying user is missing required privileges * pr feedback: refactor logic for rendering form fields --- .../rules/step_rule_actions/index.test.tsx | 8 ++ .../rules/step_rule_actions/index.tsx | 79 ++++++++++++------- .../rules/step_rule_actions/translations.tsx | 8 ++ 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx index 165aa5a30b0f0..aea75b5b3b796 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx @@ -14,6 +14,14 @@ jest.mock('../../../../common/lib/kibana', () => ({ services: { application: { getUrlForApp: jest.fn(), + capabilities: { + siem: { + crud: true, + }, + actions: { + read: true, + }, + }, }, triggers_actions_ui: { actionTypeRegistry: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index e6f1c25bf9dac..77168e62492c6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -11,6 +11,7 @@ import { EuiFlexItem, EuiButton, EuiSpacer, + EuiText, } from '@elastic/eui'; import { findIndex } from 'lodash/fp'; import React, { FC, memo, useCallback, useEffect, useMemo } from 'react'; @@ -141,37 +142,61 @@ const StepRuleActionsComponent: FC = ({ [isLoading, throttleOptions] ); - return isReadOnlyView ? ( - - - + if (isReadOnlyView) { + return ( + + + + ); + } + + const displayActionsOptions = + throttle !== stepActionsDefaultValue.throttle ? ( + <> + + + + ) : ( + + ); + + // only display the actions dropdown if the user has "read" privileges for actions + const displayActionsDropDown = application.capabilities.actions.show ? ( + <> + + {displayActionsOptions} + + + ) : ( + <> + {I18n.NO_ACTIONS_READ_PERMISSIONS} + + + + + + ); + + return ( <>
- - - {throttle !== stepActionsDefaultValue.throttle ? ( - <> - - - - ) : ( - - )} - - - + {displayActionsDropDown}
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index a276cb17e95f5..b2677b761aabf 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -28,6 +28,14 @@ export const NO_CONNECTOR_SELECTED = i18n.translate( } ); +export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges', + { + defaultMessage: + 'Cannot create rule actions. You do not have "Read" permissions for the "Actions" plugin.', + } +); + export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) => i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage', From 0ce802522cb254342d3de1debc59f8f483c7f720 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 2 Oct 2020 16:52:51 -0400 Subject: [PATCH 02/10] [Uptime] Synthetics UI (#77960) * Checkpoint * Various * Display synthetics steps. * Add loading state for snapshots. Add error and stack trace fields. * Lazy load screenshot images and cache screenshot GET endpoint. * Fix extra requests bug. * Improve screenshot empty state. * Switch to use of code block for stack and error. * Add onmouseenter and onmouseleave for image input/popover. * Add image overlay. * Support `skipped` state. * Add synthetics type for Ping. * Fix type references in reducer, api request, components. * Add required mapping logic to journey request function. * Modularize new components. * Delete obsolete code. * Delete unused code. * Test expanded row changes. * Add a test for ping list expand check. * Various fixes * Extract code accordion to new component * Subsume synthetics type into Ping type. * Add new journey viz for 0 steps case. * Use code block for console output. * Expand step count cap. * Improve formatting of console steps visualization. * Improve empty prompt. * Extract empty prompt to dedicated file. * Extract executed journey UI to dedicated file. * Extract console step list components to dedicated files. * Update empty journey prompt to accept only check_group. * Clean up script expanded row component. * Translate console output steps component. * Fix logic error. * Clean up console step component. * Translate empty journey component. * Translate status badge component. * Translate screenshot component. * Add experimental warning callout. * Re-introduce deleted code. * Simplify console output step list. * Support skipped step for executed journeys. * Simplify executed journey component. * Add translations for executed step. * Refresh outdated test snapshots. * Simplify journey reducer signature. * Repair types. * Fix broken i18n naming. * Add summary field to outdated ping test data. * Fix linting error. * Remove @ts-ignore comment. * Add tests for step screenshot display. * Add tests for status badge. * Rename test file. * Add tests for script expanded row. * Add tests for executed step. * Delete request and response fields from Ping's `synthetics` field. * Fix screenshot querying effect, add flag to journey step state. * Update screenshot api route to reply 404 when screenshot is null. * Simplify screenshot image fetching. * Delete obsolete code. * Rename BrowserExpandedRow component. * Remove all references to "suitejourney". * Add intentional var names. * Rename Console components to use "event" terminology instead of "step". * Employ better copy. * First names always bad names. * Rename CodeBlockAccordion component. * Add blob_mime field to Ping type. * Fix busted import path. * Update ping type for new position of errors field. * Repair broken types. * Fix summary querying * Type fixes. * Switch state object from list to KVP. * Checkpoint. * Fix screenshot display test. * Fix executed step test. * Refresh outdated test snapshots. * Repair broken types. * More typing fixes. * Fix console log and add a test. Co-authored-by: Andrew Cholakian Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../uptime/common/runtime_types/ping/ping.ts | 49 +++++ .../plugins/uptime/public/apps/uptime_app.tsx | 1 + .../ping_list/__tests__/expanded_row.test.tsx | 22 ++ .../ping_list/__tests__/ping_list.test.tsx | 12 +- .../monitor/ping_list/expanded_row.tsx | 20 ++ .../components/monitor/ping_list/index.tsx | 2 +- .../monitor/ping_list/ping_list.tsx | 85 +++++++- .../monitor/ping_list/ping_list_container.tsx | 51 ----- .../__tests__/browser_expanded_row.test.tsx | 181 ++++++++++++++++ .../__tests__/executed_step.test.tsx | 119 +++++++++++ .../__tests__/status_badge.test.tsx | 41 ++++ .../step_screenshot_display.test.tsx | 193 ++++++++++++++++++ .../synthetics/browser_expanded_row.tsx | 64 ++++++ .../synthetics/code_block_accordion.tsx | 35 ++++ .../monitor/synthetics/console_event.tsx | 37 ++++ .../synthetics/console_output_event_list.tsx | 41 ++++ .../monitor/synthetics/empty_journey.tsx | 52 +++++ .../monitor/synthetics/executed_journey.tsx | 80 ++++++++ .../monitor/synthetics/executed_step.tsx | 92 +++++++++ .../monitor/synthetics/status_badge.tsx | 52 +++++ .../synthetics/step_screenshot_display.tsx | 175 ++++++++++++++++ .../public/contexts/uptime_theme_context.tsx | 3 + .../uptime/public/state/actions/journey.ts | 24 +++ .../uptime/public/state/api/journey.ts | 22 ++ .../uptime/public/state/effects/index.ts | 2 + .../uptime/public/state/effects/journey.ts | 26 +++ .../uptime/public/state/reducers/index.ts | 2 + .../uptime/public/state/reducers/journey.ts | 98 +++++++++ .../state/selectors/__tests__/index.test.ts | 1 + .../uptime/public/state/selectors/index.ts | 2 + .../lib/requests/__tests__/get_pings.test.ts | 120 +++++++++++ .../lib/requests/get_journey_screenshot.ts | 50 +++++ .../server/lib/requests/get_journey_steps.ts | 61 ++++++ .../uptime/server/lib/requests/get_pings.ts | 44 +++- .../uptime/server/lib/requests/index.ts | 4 + .../plugins/uptime/server/rest_api/index.ts | 9 +- .../uptime/server/rest_api/pings/index.ts | 2 + .../rest_api/pings/journey_screenshots.ts | 40 ++++ .../uptime/server/rest_api/pings/journeys.ts | 34 +++ .../es_archives/uptime/pings/data.json.gz | Bin 765 -> 808 bytes 40 files changed, 1890 insertions(+), 58 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx create mode 100644 x-pack/plugins/uptime/public/state/actions/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/api/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/effects/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/reducers/journey.ts create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/pings/journeys.ts diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index f954f8ba30849..775078a7e5df1 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -180,6 +180,48 @@ export const PingType = t.intersection([ down: t.number, up: t.number, }), + synthetics: t.partial({ + index: t.number, + journey: t.type({ + id: t.string, + name: t.string, + }), + error: t.partial({ + message: t.string, + name: t.string, + stack: t.string, + }), + package_version: t.string, + step: t.type({ + index: t.number, + name: t.string, + }), + type: t.string, + // ui-related field + screenshotLoading: t.boolean, + // ui-related field + screenshotExists: t.boolean, + blob: t.string, + blob_mime: t.string, + payload: t.partial({ + duration: t.number, + index: t.number, + is_navigation_request: t.boolean, + message: t.string, + method: t.string, + name: t.string, + params: t.partial({ + homepage: t.string, + }), + source: t.string, + start: t.number, + status: t.string, + ts: t.number, + type: t.string, + url: t.string, + end: t.number, + }), + }), tags: t.array(t.string), tcp: t.partial({ rtt: t.partial({ @@ -202,6 +244,13 @@ export const PingType = t.intersection([ }), ]); +export const SyntheticsJourneyApiResponseType = t.type({ + checkGroup: t.string, + steps: t.array(PingType), +}); + +export type SyntheticsJourneyApiResponse = t.TypeOf; + export type Ping = t.TypeOf; // Convenience function for tests etc that makes an empty ping diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 4b58ba104314f..a5b8bc859ad94 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -40,6 +40,7 @@ export interface UptimeAppColors { range: string; mean: string; warning: string; + lightestShade: string; } export interface UptimeAppProps { diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx index 2c1434cfd64bd..85d1362ed3766 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx @@ -17,6 +17,7 @@ describe('PingListExpandedRow', () => { docId: 'fdeio12', timestamp: '19290310', monitor: { + check_group: 'check_group_id', duration: { us: 12345, }, @@ -87,4 +88,25 @@ describe('PingListExpandedRow', () => { expect(docLinkComponent).toHaveLength(1); }); + + it('renders a synthetics expanded row for synth monitor', () => { + ping.monitor.type = 'browser'; + expect(shallowWithIntl()).toMatchInlineSnapshot(` + + + + + + + + + `); + }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx index cb8413ba08a81..8de4b89b7880d 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { PingListComponent, toggleDetails } from '../ping_list'; +import { PingListComponent, rowShouldExpand, toggleDetails } from '../ping_list'; import { Ping, PingsResponse } from '../../../../../common/runtime_types'; import { ExpandedRowMap } from '../../../overview/monitor_list/types'; @@ -182,6 +182,7 @@ describe('PingList component', () => { to: 'now', }} getPings={jest.fn()} + pruneJourneysCallback={jest.fn()} lastRefresh={123} loading={false} locations={[]} @@ -273,5 +274,14 @@ describe('PingList component', () => { /> `); }); + + describe('rowShouldExpand', () => { + // TODO: expand for all cases + it('returns true for browser monitors', () => { + const ping = pings[0]; + ping.monitor.type = 'browser'; + expect(rowShouldExpand(ping)).toBe(true); + }); + }); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx index 201803ff3a951..6af38eca6b0e9 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + // @ts-ignore formatNumber import { formatNumber } from '@elastic/eui/lib/services/format'; import { @@ -19,6 +20,7 @@ import { i18n } from '@kbn/i18n'; import { Ping, HttpResponseBody } from '../../../../common/runtime_types'; import { DocLinkForBody } from './doc_link_body'; import { PingRedirects } from './ping_redirects'; +import { BrowserExpandedRow } from '../synthetics/browser_expanded_row'; interface Props { ping: Ping; @@ -53,6 +55,24 @@ const BodyExcerpt = ({ content }: { content: string }) => export const PingListExpandedRowComponent = ({ ping }: Props) => { const listItems = []; + if (ping.monitor.type === 'browser') { + return ( + + + + + + + + + ); + } + // Show the error block if (ping.error) { listItems.push({ diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx index 7fc19bbc9622b..da82d025f478b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx @@ -5,4 +5,4 @@ */ export { PingListComponent } from './ping_list'; -export { PingList } from './ping_list_container'; +export { PingList } from './ping_list'; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 09782c1b76edb..590b2f787bac4 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -21,14 +21,63 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; -import React, { useState, useEffect } from 'react'; +import React, { useCallback, useContext, useState, useEffect } from 'react'; import styled from 'styled-components'; +import { useDispatch, useSelector } from 'react-redux'; import { Ping, GetPingsParams, DateRange } from '../../../../common/runtime_types'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; import { LocationName } from './location_name'; import { Pagination } from '../../overview/monitor_list'; import { PingListExpandedRowComponent } from './expanded_row'; -import { PingListProps } from './ping_list_container'; +// import { PingListProps } from './ping_list_container'; +import { pruneJourneyState } from '../../../state/actions/journey'; +import { selectPingList } from '../../../state/selectors'; +import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; +import { getPings as getPingsAction } from '../../../state/actions'; + +export interface PingListProps { + monitorId: string; +} + +export const PingList = (props: PingListProps) => { + const { + error, + loading, + pingList: { locations, pings, total }, + } = useSelector(selectPingList); + + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { dateRangeStart: drs, dateRangeEnd: dre } = useContext(UptimeSettingsContext); + + const dispatch = useDispatch(); + const getPingsCallback = useCallback( + (params: GetPingsParams) => dispatch(getPingsAction(params)), + [dispatch] + ); + const pruneJourneysCallback = useCallback( + (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), + [dispatch] + ); + + return ( + + ); +}; export const AllLocationOption = { 'data-test-subj': 'xpack.uptime.pingList.locationOptions.all', @@ -63,6 +112,7 @@ interface Props extends PingListProps { dateRange: DateRange; error?: Error; getPings: (props: GetPingsParams) => void; + pruneJourneysCallback: (checkGroups: string[]) => void; lastRefresh: number; loading: boolean; locations: string[]; @@ -96,6 +146,13 @@ const statusOptions = [ }, ]; +export function rowShouldExpand(item: Ping) { + const errorPresent = !!item.error; + const httpBodyPresent = item.http?.response?.body?.bytes ?? 0 > 0; + const isBrowserMonitor = item.monitor.type === 'browser'; + return errorPresent || httpBodyPresent || isBrowserMonitor; +} + export const PingListComponent = (props: Props) => { const [selectedLocation, setSelectedLocation] = useState(''); const [status, setStatus] = useState(''); @@ -105,6 +162,7 @@ export const PingListComponent = (props: Props) => { dateRange: { from, to }, error, getPings, + pruneJourneysCallback, lastRefresh, loading, locations, @@ -129,6 +187,27 @@ export const PingListComponent = (props: Props) => { const [expandedRows, setExpandedRows] = useState>({}); + const expandedIdsToRemove = JSON.stringify( + Object.keys(expandedRows).filter((e) => !pings.some(({ docId }) => docId === e)) + ); + useEffect(() => { + const parsed = JSON.parse(expandedIdsToRemove); + if (parsed.length) { + parsed.forEach((docId: string) => { + delete expandedRows[docId]; + }); + setExpandedRows(expandedRows); + } + }, [expandedIdsToRemove, expandedRows]); + + const expandedCheckGroups = pings + .filter((p: Ping) => Object.keys(expandedRows).some((f) => p.docId === f)) + .map(({ monitor: { check_group: cg } }) => cg); + const expandedCheckGroupsStr = JSON.stringify(expandedCheckGroups); + useEffect(() => { + pruneJourneysCallback(JSON.parse(expandedCheckGroupsStr)); + }, [pruneJourneysCallback, expandedCheckGroupsStr]); + const locationOptions = !locations ? [AllLocationOption] : [AllLocationOption].concat( @@ -239,7 +318,7 @@ export const PingListComponent = (props: Props) => { toggleDetails(item, expandedRows, setExpandedRows)} - disabled={!item.error && !(item.http?.response?.body?.bytes ?? 0 > 0)} + disabled={!rowShouldExpand(item)} aria-label={ expandedRows[item.docId] ? i18n.translate('xpack.uptime.pingList.collapseRow', { diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx deleted file mode 100644 index 4d4bb175be9a8..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx +++ /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. - */ - -import { useSelector, useDispatch } from 'react-redux'; -import React, { useContext, useCallback } from 'react'; -import { selectPingList } from '../../../state/selectors'; -import { getPings } from '../../../state/actions'; -import { GetPingsParams } from '../../../../common/runtime_types'; -import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; -import { PingListComponent } from './index'; - -export interface PingListProps { - monitorId: string; -} - -export const PingList = (props: PingListProps) => { - const { - error, - loading, - pingList: { locations, pings, total }, - } = useSelector(selectPingList); - - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { dateRangeStart: drs, dateRangeEnd: dre } = useContext(UptimeSettingsContext); - - const dispatch = useDispatch(); - const getPingsCallback = useCallback((params: GetPingsParams) => dispatch(getPings(params)), [ - dispatch, - ]); - - return ( - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx new file mode 100644 index 0000000000000..191632d6ab713 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx @@ -0,0 +1,181 @@ +/* + * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { BrowserExpandedRowComponent } from '../browser_expanded_row'; +import { Ping } from '../../../../../common/runtime_types'; + +describe('BrowserExpandedRowComponent', () => { + let defStep: Ping; + beforeEach(() => { + defStep = { + docId: 'doc-id', + timestamp: '123', + monitor: { + duration: { + us: 100, + }, + id: 'mon-id', + status: 'up', + type: 'browser', + }, + }; + }); + + it('returns empty step state when no journey', () => { + expect(shallowWithIntl()).toMatchInlineSnapshot( + `` + ); + }); + + it('returns empty step state when journey has no steps', () => { + expect( + shallowWithIntl( + + ) + ).toMatchInlineSnapshot(``); + }); + + it('displays loading spinner when loading', () => { + expect( + shallowWithIntl( + + ) + ).toMatchInlineSnapshot(` +
+ +
+ `); + }); + + it('renders executed journey when step/end is present', () => { + expect( + shallowWithIntl( + + ) + ).toMatchInlineSnapshot(` + + `); + }); + + it('renders console output step list when only console steps are present', () => { + expect( + shallowWithIntl( + + ) + ).toMatchInlineSnapshot(` + + `); + }); + + it('renders null when only unsupported steps are present', () => { + expect( + shallowWithIntl( + + ) + ).toMatchInlineSnapshot(`""`); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx new file mode 100644 index 0000000000000..e3a8430cfa888 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx @@ -0,0 +1,119 @@ +/* + * 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 { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { ExecutedStep } from '../executed_step'; +import { Ping } from '../../../../../common/runtime_types'; + +describe('ExecutedStep', () => { + let step: Ping; + + beforeEach(() => { + step = { + docId: 'docID', + monitor: { + duration: { + us: 123, + }, + id: 'id', + status: 'up', + type: 'browser', + }, + synthetics: { + step: { + index: 4, + name: 'STEP_NAME', + }, + }, + timestamp: 'timestamp', + }; + }); + + it('renders correct step heading', () => { + expect(mountWithIntl().find('EuiText')) + .toMatchInlineSnapshot(` + +
+ + + 4. STEP_NAME + + +
+
+ `); + }); + + it('supplies status badge correct status', () => { + step.synthetics = { + payload: { status: 'THE_STATUS' }, + }; + expect(shallowWithIntl().find('StatusBadge')) + .toMatchInlineSnapshot(` + + `); + }); + + it('renders accordions for step, error message, and error stack script', () => { + step.synthetics = { + error: { + message: 'There was an error executing the step.', + stack: 'some.stack.trace.string', + }, + payload: { + source: 'const someVar = "the var"', + }, + step: { + index: 3, + name: 'STEP_NAME', + }, + }; + + expect(shallowWithIntl().find('CodeBlockAccordion')) + .toMatchInlineSnapshot(` + Array [ + + const someVar = "the var" + , + + There was an error executing the step. + , + + some.stack.trace.string + , + ] + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx new file mode 100644 index 0000000000000..1171c24ad899c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx @@ -0,0 +1,41 @@ +/* + * 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 { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { StatusBadge } from '../status_badge'; + +describe('StatusBadge', () => { + it('displays success message', () => { + expect(shallowWithIntl()).toMatchInlineSnapshot(` + + Succeeded + + `); + }); + + it('displays failed message', () => { + expect(shallowWithIntl()).toMatchInlineSnapshot(` + + Failed + + `); + }); + + it('displays skipped message', () => { + expect(shallowWithIntl()).toMatchInlineSnapshot(` + + Skipped + + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx new file mode 100644 index 0000000000000..16db430dbd73a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx @@ -0,0 +1,193 @@ +/* + * 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 { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import * as reactUse from 'react-use'; +import { StepScreenshotDisplay } from '../step_screenshot_display'; + +describe('StepScreenshotDisplayProps', () => { + // @ts-ignore missing fields don't matter in this test, the component in question only relies on `isIntersecting` + jest.spyOn(reactUse, 'useIntersection').mockImplementation(() => ({ + isIntersecting: true, + })); + + it('displays screenshot thumbnail when present', () => { + const wrapper = mountWithIntl( + + ); + + wrapper.update(); + + expect(wrapper.find('img')).toMatchInlineSnapshot(`null`); + + expect(wrapper.find('EuiPopover')).toMatchInlineSnapshot(` + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + > + +
+
+ +
+
+
+
+ `); + }); + + it('uses alternative text when step name not available', () => { + const wrapper = mountWithIntl( + + ); + + wrapper.update(); + + expect(wrapper.find('EuiPopover')).toMatchInlineSnapshot(` + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + > + +
+
+ +
+
+
+
+ `); + }); + + it('displays No Image message when screenshot does not exist', () => { + expect( + shallowWithIntl( + + ).find('EuiText') + ).toMatchInlineSnapshot(` + + + + + + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx new file mode 100644 index 0000000000000..2546c5fb9a5d8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx @@ -0,0 +1,64 @@ +/* + * 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 { EuiLoadingSpinner } from '@elastic/eui'; +import React, { useEffect, FC } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Ping } from '../../../../common/runtime_types'; +import { getJourneySteps } from '../../../state/actions/journey'; +import { JourneyState } from '../../../state/reducers/journey'; +import { journeySelector } from '../../../state/selectors'; +import { EmptyStepState } from './empty_journey'; +import { ExecutedJourney } from './executed_journey'; +import { ConsoleOutputEventList } from './console_output_event_list'; + +interface BrowserExpandedRowProps { + checkGroup?: string; +} + +export const BrowserExpandedRow: React.FC = ({ checkGroup }) => { + const dispatch = useDispatch(); + useEffect(() => { + if (checkGroup) { + dispatch(getJourneySteps({ checkGroup })); + } + }, [dispatch, checkGroup]); + + const journeys = useSelector(journeySelector); + const journey = journeys[checkGroup ?? '']; + + return ; +}; + +type ComponentProps = BrowserExpandedRowProps & { + journey?: JourneyState; +}; + +const stepEnd = (step: Ping) => step.synthetics?.type === 'step/end'; +const stepConsole = (step: Ping) => + ['stderr', 'cmd/status'].indexOf(step.synthetics?.type ?? '') !== -1; + +export const BrowserExpandedRowComponent: FC = ({ checkGroup, journey }) => { + if (!!journey && journey.loading) { + return ( +
+ +
+ ); + } + + if (!journey || journey.steps.length === 0) { + return ; + } + + if (journey.steps.some(stepEnd)) return ; + + if (journey.steps.some(stepConsole)) return ; + + // TODO: should not happen, this means that the journey has no step/end and no console logs, but some other steps; filmstrip, screenshot, etc. + // we should probably create an error prompt letting the user know this step is not supported yet + return null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx new file mode 100644 index 0000000000000..9f6559f0c2709 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx @@ -0,0 +1,35 @@ +/* + * 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 { EuiAccordion, EuiCodeBlock } from '@elastic/eui'; +import React, { FC } from 'react'; + +interface Props { + buttonContent: string; + id?: string; + language: 'html' | 'javascript'; + overflowHeight: number; +} + +/** + * Utility for showing `EuiAccordions` with code blocks which we use frequently in synthetics to display + * stack traces, long error messages, and synthetics journey code. + */ +export const CodeBlockAccordion: FC = ({ + buttonContent, + children, + id, + language, + overflowHeight, +}) => { + return children && id ? ( + + + {children} + + + ) : null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx new file mode 100644 index 0000000000000..0f2930db96b54 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx @@ -0,0 +1,37 @@ +/* + * 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 { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import React, { useContext, FC } from 'react'; +import { Ping } from '../../../../common/runtime_types'; +import { UptimeThemeContext } from '../../../contexts'; + +interface Props { + event: Ping; +} + +export const ConsoleEvent: FC = ({ event }) => { + const { + colors: { danger }, + } = useContext(UptimeThemeContext); + + let typeColor: string | undefined; + if (event.synthetics?.type === 'stderr') { + typeColor = danger; + } else { + typeColor = undefined; + } + + return ( + + {event.timestamp} + + {event.synthetics?.type} + + {event.synthetics?.payload?.message} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx new file mode 100644 index 0000000000000..9159c61532f15 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx @@ -0,0 +1,41 @@ +/* + * 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 { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { JourneyState } from '../../../state/reducers/journey'; +import { ConsoleEvent } from './console_event'; + +interface Props { + journey: JourneyState; +} + +export const ConsoleOutputEventList: FC = ({ journey }) => ( +
+ +

+ +

+
+ +

+ +

+ + + {journey.steps.map((consoleEvent) => ( + + ))} + +
+); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx new file mode 100644 index 0000000000000..b6fead2bbbe09 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx @@ -0,0 +1,52 @@ +/* + * 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 { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; + +interface EmptyStepStateProps { + checkGroup?: string; +} + +export const EmptyStepState: FC = ({ checkGroup }) => ( + + + + } + body={ + <> +

+ +

+ {!!checkGroup && ( +

+ {checkGroup} }} + /> +

+ )} +

+ +

+ + } + /> +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx new file mode 100644 index 0000000000000..2c37f95428d0e --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx @@ -0,0 +1,80 @@ +/* + * 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 { EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { Ping } from '../../../../common/runtime_types'; +import { JourneyState } from '../../../state/reducers/journey'; +import { ExecutedStep } from './executed_step'; + +interface StepStatusCount { + failed: number; + skipped: number; + succeeded: number; +} + +function statusMessage(count: StepStatusCount) { + const total = count.succeeded + count.failed + count.skipped; + if (count.failed + count.skipped === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', { + defaultMessage: '{total} Steps - all failed or skipped', + values: { total }, + }); + } else if (count.succeeded === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', { + defaultMessage: '{total} Steps - all succeeded', + values: { total }, + }); + } + return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', { + defaultMessage: '{total} Steps - {succeeded} succeeded', + values: { succeeded: count.succeeded, total }, + }); +} + +function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { + if (cur.synthetics?.payload?.status === 'succeeded') { + prev.succeeded += 1; + return prev; + } else if (cur.synthetics?.payload?.status === 'skipped') { + prev.skipped += 1; + return prev; + } + prev.failed += 1; + return prev; +} + +interface ExecutedJourneyProps { + journey: JourneyState; +} + +export const ExecutedJourney: FC = ({ journey }) => ( +
+ +

+ +

+

+ {statusMessage( + journey.steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }) + )} +

+
+ + + {journey.steps + .filter((step) => step.synthetics?.type === 'step/end') + .map((step, index) => ( + + ))} + +
+); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx new file mode 100644 index 0000000000000..3c26ba12eea65 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.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 { EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { CodeBlockAccordion } from './code_block_accordion'; +import { StepScreenshotDisplay } from './step_screenshot_display'; +import { StatusBadge } from './status_badge'; +import { Ping } from '../../../../common/runtime_types'; + +const CODE_BLOCK_OVERFLOW_HEIGHT = 360; + +interface ExecutedStepProps { + step: Ping; + index: number; +} + +export const ExecutedStep: FC = ({ step, index }) => ( + <> +
+
+ + + + + +
+ +
+ +
+ +
+ + + + + + + {step.synthetics?.payload?.source} + + + {step.synthetics?.error?.message} + + + {step.synthetics?.error?.stack} + + + +
+
+ + +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx new file mode 100644 index 0000000000000..f8fbeccaabf42 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx @@ -0,0 +1,52 @@ +/* + * 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 { EuiBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useContext, FC } from 'react'; +import { UptimeAppColors } from '../../../apps/uptime_app'; +import { UptimeThemeContext } from '../../../contexts'; + +interface StatusBadgeProps { + status?: string; +} + +export function colorFromStatus(color: UptimeAppColors, status?: string) { + switch (status) { + case 'succeeded': + return color.success; + case 'failed': + return color.danger; + default: + return 'default'; + } +} + +export function textFromStatus(status?: string) { + switch (status) { + case 'succeeded': + return i18n.translate('xpack.uptime.synthetics.statusBadge.succeededMessage', { + defaultMessage: 'Succeeded', + }); + case 'failed': + return i18n.translate('xpack.uptime.synthetics.statusBadge.failedMessage', { + defaultMessage: 'Failed', + }); + case 'skipped': + return i18n.translate('xpack.uptime.synthetics.statusBadge.skippedMessage', { + defaultMessage: 'Skipped', + }); + default: + return null; + } +} + +export const StatusBadge: FC = ({ status }) => { + const theme = useContext(UptimeThemeContext); + return ( + {textFromStatus(status)} + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx new file mode 100644 index 0000000000000..2e8ad4bd0c9a8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx @@ -0,0 +1,175 @@ +/* + * 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 { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiOverlayMask, + EuiPopover, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useContext, useEffect, useRef, useState, FC } from 'react'; +import { useIntersection } from 'react-use'; +import { UptimeThemeContext } from '../../../contexts'; + +interface StepScreenshotDisplayProps { + screenshotExists?: boolean; + checkGroup?: string; + stepIndex?: number; + stepName?: string; +} + +const THUMBNAIL_WIDTH = 320; +const THUMBNAIL_HEIGHT = 180; +const POPOVER_IMG_WIDTH = 640; +const POPOVER_IMG_HEIGHT = 360; + +export const StepScreenshotDisplay: FC = ({ + checkGroup, + screenshotExists, + stepIndex, + stepName, +}) => { + const containerRef = useRef(null); + const { + colors: { lightestShade: pageBackground }, + } = useContext(UptimeThemeContext); + + const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false); + const [isOverlayOpen, setIsOverlayOpen] = useState(false); + + const intersection = useIntersection(containerRef, { + root: null, + rootMargin: '0px', + threshold: 1, + }); + + const [hasIntersected, setHasIntersected] = useState(false); + const isIntersecting = intersection?.isIntersecting; + useEffect(() => { + if (hasIntersected === false && isIntersecting === true) { + setHasIntersected(true); + } + }, [hasIntersected, isIntersecting, setHasIntersected]); + + let content: JSX.Element | null = null; + const imgSrc = `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; + if (hasIntersected && screenshotExists) { + content = ( + <> + {isOverlayOpen && ( + setIsOverlayOpen(false)}> + setIsOverlayOpen(false)} + /> + + )} + setIsOverlayOpen(true)} + onMouseEnter={() => setIsImagePopoverOpen(true)} + onMouseLeave={() => setIsImagePopoverOpen(false)} + /> + } + closePopover={() => setIsImagePopoverOpen(false)} + isOpen={isImagePopoverOpen} + > + { + + + ); + } else if (screenshotExists === false) { + content = ( + + + + + + + + + + + + + ); + } + return ( +
+ {content} +
+ ); +}; diff --git a/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx index 51e8bcaed986f..f0a702b9c0b75 100644 --- a/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx +++ b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx @@ -31,6 +31,7 @@ const defaultContext: UptimeThemeContextValues = { success: euiLightVars.euiColorSuccess, warning: euiLightVars.euiColorWarning, gray: euiLightVars.euiColorLightShade, + lightestShade: euiLightVars.euiColorLightestShade, }, chartTheme: { baseTheme: LIGHT_THEME, @@ -54,6 +55,7 @@ export const UptimeThemeContextProvider: React.FC = ({ darkMo range: euiDarkVars.euiFocusBackgroundColor, success: euiDarkVars.euiColorSuccess, warning: euiDarkVars.euiColorWarning, + lightestShade: euiDarkVars.euiColorLightestShade, }; } else { colors = { @@ -63,6 +65,7 @@ export const UptimeThemeContextProvider: React.FC = ({ darkMo range: euiLightVars.euiFocusBackgroundColor, success: euiLightVars.euiColorSuccess, warning: euiLightVars.euiColorWarning, + lightestShade: euiLightVars.euiColorLightestShade, }; } const value = useMemo(() => { diff --git a/x-pack/plugins/uptime/public/state/actions/journey.ts b/x-pack/plugins/uptime/public/state/actions/journey.ts new file mode 100644 index 0000000000000..0d35559d97fc3 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/actions/journey.ts @@ -0,0 +1,24 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; + +export interface FetchJourneyStepsParams { + checkGroup: string; +} + +export interface GetJourneyFailPayload { + checkGroup: string; + error: Error; +} + +export const getJourneySteps = createAction('GET_JOURNEY_STEPS'); +export const getJourneyStepsSuccess = createAction( + 'GET_JOURNEY_STEPS_SUCCESS' +); +export const getJourneyStepsFail = createAction('GET_JOURNEY_STEPS_FAIL'); +export const pruneJourneyState = createAction('PRUNE_JOURNEY_STATE'); diff --git a/x-pack/plugins/uptime/public/state/api/journey.ts b/x-pack/plugins/uptime/public/state/api/journey.ts new file mode 100644 index 0000000000000..e9ea9d8744bc8 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/api/journey.ts @@ -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 { apiService } from './utils'; +import { FetchJourneyStepsParams } from '../actions/journey'; +import { + SyntheticsJourneyApiResponse, + SyntheticsJourneyApiResponseType, +} from '../../../common/runtime_types'; + +export async function fetchJourneySteps( + params: FetchJourneyStepsParams +): Promise { + return (await apiService.get( + `/api/uptime/journey/${params.checkGroup}`, + undefined, + SyntheticsJourneyApiResponseType + )) as SyntheticsJourneyApiResponse; +} diff --git a/x-pack/plugins/uptime/public/state/effects/index.ts b/x-pack/plugins/uptime/public/state/effects/index.ts index a2a5dd58dcf4d..4951f2102c8a7 100644 --- a/x-pack/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/plugins/uptime/public/state/effects/index.ts @@ -18,6 +18,7 @@ import { fetchMLJobEffect } from './ml_anomaly'; import { fetchIndexStatusEffect } from './index_status'; import { fetchCertificatesEffect } from '../certificates/certificates'; import { fetchAlertsEffect } from '../alerts/alerts'; +import { fetchJourneyStepsEffect } from './journey'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -35,4 +36,5 @@ export function* rootEffect() { yield fork(fetchIndexStatusEffect); yield fork(fetchCertificatesEffect); yield fork(fetchAlertsEffect); + yield fork(fetchJourneyStepsEffect); } diff --git a/x-pack/plugins/uptime/public/state/effects/journey.ts b/x-pack/plugins/uptime/public/state/effects/journey.ts new file mode 100644 index 0000000000000..2ba125535dea4 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/effects/journey.ts @@ -0,0 +1,26 @@ +/* + * 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 { Action } from 'redux-actions'; +import { call, put, takeLatest } from 'redux-saga/effects'; +import { + getJourneySteps, + getJourneyStepsSuccess, + getJourneyStepsFail, + FetchJourneyStepsParams, +} from '../actions/journey'; +import { fetchJourneySteps } from '../api/journey'; + +export function* fetchJourneyStepsEffect() { + yield takeLatest(getJourneySteps, function* (action: Action) { + try { + const response = yield call(fetchJourneySteps, action.payload); + yield put(getJourneyStepsSuccess(response)); + } catch (e) { + yield put(getJourneyStepsFail({ checkGroup: action.payload.checkGroup, error: e })); + } + }); +} diff --git a/x-pack/plugins/uptime/public/state/reducers/index.ts b/x-pack/plugins/uptime/public/state/reducers/index.ts index a1aa8b9fbaf5b..c0bab124d5f9d 100644 --- a/x-pack/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/plugins/uptime/public/state/reducers/index.ts @@ -21,6 +21,7 @@ import { mlJobsReducer } from './ml_anomaly'; import { certificatesReducer } from '../certificates/certificates'; import { selectedFiltersReducer } from './selected_filters'; import { alertsReducer } from '../alerts/alerts'; +import { journeyReducer } from './journey'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -39,4 +40,5 @@ export const rootReducer = combineReducers({ certificates: certificatesReducer, selectedFilters: selectedFiltersReducer, alerts: alertsReducer, + journeys: journeyReducer, }); diff --git a/x-pack/plugins/uptime/public/state/reducers/journey.ts b/x-pack/plugins/uptime/public/state/reducers/journey.ts new file mode 100644 index 0000000000000..e1c3dc808f1bf --- /dev/null +++ b/x-pack/plugins/uptime/public/state/reducers/journey.ts @@ -0,0 +1,98 @@ +/* + * 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 { handleActions, Action } from 'redux-actions'; +import { Ping, SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; +import { pruneJourneyState } from '../actions/journey'; +import { + FetchJourneyStepsParams, + GetJourneyFailPayload, + getJourneySteps, + getJourneyStepsFail, + getJourneyStepsSuccess, +} from '../actions/journey'; + +export interface JourneyState { + checkGroup: string; + steps: Ping[]; + loading: boolean; + error?: Error; +} + +interface JourneyKVP { + [checkGroup: string]: JourneyState; +} + +const initialState: JourneyKVP = {}; + +type Payload = FetchJourneyStepsParams & + SyntheticsJourneyApiResponse & + GetJourneyFailPayload & + string[]; + +export const journeyReducer = handleActions( + { + [String(getJourneySteps)]: ( + state: JourneyKVP, + { payload: { checkGroup } }: Action + ) => ({ + ...state, + // add an empty entry while fetching the check group, + // or update the previously-loaded entry to a new loading state + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: true, + } + : { + checkGroup, + steps: [], + loading: true, + }, + }), + + [String(getJourneyStepsSuccess)]: ( + state: JourneyKVP, + { payload: { checkGroup, steps } }: Action + ) => ({ + ...state, + [checkGroup]: { + loading: false, + checkGroup, + steps, + }, + }), + + [String(getJourneyStepsFail)]: ( + state: JourneyKVP, + { payload: { checkGroup, error } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: false, + error, + } + : { + checkGroup, + loading: false, + steps: [], + error, + }, + }), + + [String(pruneJourneyState)]: (state: JourneyKVP, action: Action) => + action.payload.reduce( + (prev, cur) => ({ + ...prev, + [cur]: state[cur], + }), + {} + ), + }, + initialState +); diff --git a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 1a8929990ab55..a59e0be5cdf3f 100644 --- a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -117,6 +117,7 @@ describe('state selectors', () => { newAlert: { data: null, loading: false }, anomalyAlertDeletion: { data: null, loading: false }, }, + journeys: {}, }; it('selects base path from state', () => { diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index 6b3b46cc15137..6bfe67468aae5 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -94,3 +94,5 @@ export const searchTextSelector = ({ ui: { searchText } }: AppState) => searchTe export const selectedFiltersSelector = ({ selectedFilters }: AppState) => selectedFilters; export const monitorIdSelector = ({ ui: { monitorId } }: AppState) => monitorId; + +export const journeySelector = ({ journeys }: AppState) => journeys; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts index a52bf86499396..cb84cc2eb05b6 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts @@ -144,6 +144,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 12, @@ -198,6 +222,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 12, @@ -252,6 +300,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, @@ -311,6 +383,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, @@ -370,6 +466,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts new file mode 100644 index 0000000000000..f726ef47915b8 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts @@ -0,0 +1,50 @@ +/* + * 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 { UMElasticsearchQueryFn } from '../adapters/framework'; + +interface GetJourneyScreenshotParams { + checkGroup: string; + stepIndex: number; +} + +export const getJourneyScreenshot: UMElasticsearchQueryFn< + GetJourneyScreenshotParams, + any +> = async ({ callES, dynamicSettings, checkGroup, stepIndex }) => { + const params: any = { + index: dynamicSettings.heartbeatIndices, + body: { + query: { + bool: { + filter: [ + { + term: { + 'monitor.check_group': checkGroup, + }, + }, + { + term: { + 'synthetics.type': 'step/screenshot', + }, + }, + { + term: { + 'synthetics.step.index': stepIndex, + }, + }, + ], + }, + }, + _source: ['synthetics.blob'], + }, + }; + const result = await callES('search', params); + if (!Array.isArray(result?.hits?.hits) || result.hits.hits.length < 1) { + return null; + } + return result.hits.hits.map(({ _source }: any) => _source?.synthetics?.blob ?? null)[0]; +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts new file mode 100644 index 0000000000000..e4392480f5b72 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.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 { UMElasticsearchQueryFn } from '../adapters/framework'; +import { Ping } from '../../../common/runtime_types'; + +interface GetJourneyStepsParams { + checkGroup: string; +} + +export const getJourneySteps: UMElasticsearchQueryFn = async ({ + callES, + dynamicSettings, + checkGroup, +}) => { + const params: any = { + index: dynamicSettings.heartbeatIndices, + body: { + query: { + bool: { + filter: [ + { + terms: { + 'synthetics.type': ['step/end', 'stderr', 'cmd/status', 'step/screenshot'], + }, + }, + { + term: { + 'monitor.check_group': checkGroup, + }, + }, + ], + }, + }, + _source: { + excludes: ['synthetics.blob'], + }, + }, + size: 500, + }; + const result = await callES('search', params); + const screenshotIndexes: number[] = result.hits.hits + .filter((h: any) => h?._source?.synthetics?.type === 'step/screenshot') + .map((h: any) => h?._source?.synthetics?.step?.index); + return result.hits.hits + .filter((h: any) => h?._source?.synthetics?.type !== 'step/screenshot') + .map( + ({ _id, _source, _source: { synthetics } }: any): Ping => ({ + ..._source, + timestamp: _source['@timestamp'], + docId: _id, + synthetics: { + ...synthetics, + screenshotExists: screenshotIndexes.some((i) => i === synthetics?.step?.index), + }, + }) + ); +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts index a6a0e3c3d6542..03ec2d7343c9a 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts @@ -14,6 +14,43 @@ import { const DEFAULT_PAGE_SIZE = 25; +/** + * This branch of filtering is used for monitors of type `browser`. This monitor + * type represents an unbounded set of steps, with each `check_group` representing + * a distinct journey. The document containing the `summary` field is indexed last, and + * contains the data necessary for querying a journey. + * + * Because of this, when querying for "pings", it is important that we treat `browser` summary + * checks as the "ping" we want. Without this filtering, we will receive >= N pings for a journey + * of N steps, because an individual step may also contain multiple documents. + */ +const REMOVE_NON_SUMMARY_BROWSER_CHECKS = { + must_not: [ + { + bool: { + filter: [ + { + term: { + 'monitor.type': 'browser', + }, + }, + { + bool: { + must_not: [ + { + exists: { + field: 'summary', + }, + }, + ], + }, + }, + ], + }, + }, + ], +}; + export const getPings: UMElasticsearchQueryFn = async ({ callES, dynamicSettings, @@ -39,7 +76,12 @@ export const getPings: UMElasticsearchQueryFn = a if (location) { postFilterClause = { post_filter: { term: { 'observer.geo.name': location } } }; } - const queryContext = { bool: { filter } }; + const queryContext = { + bool: { + filter, + ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, + }, + }; const params: any = { index: dynamicSettings.heartbeatIndices, body: { diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index 8fa4561268e8f..1806495d14cc4 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -18,6 +18,8 @@ import { getPings } from './get_pings'; import { getPingHistogram } from './get_ping_histogram'; import { getSnapshotCount } from './get_snapshot_counts'; import { getIndexStatus } from './get_index_status'; +import { getJourneySteps } from './get_journey_steps'; +import { getJourneyScreenshot } from './get_journey_screenshot'; export const requests = { getCerts, @@ -34,6 +36,8 @@ export const requests = { getPingHistogram, getSnapshotCount, getIndexStatus, + getJourneySteps, + getJourneyScreenshot, }; export type UptimeRequests = typeof requests; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 2b598be284e1c..de44b2565a2f8 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -6,7 +6,12 @@ import { createGetCertsRoute } from './certs/certs'; import { createGetOverviewFilters } from './overview_filters'; -import { createGetPingHistogramRoute, createGetPingsRoute } from './pings'; +import { + createGetPingHistogramRoute, + createGetPingsRoute, + createJourneyRoute, + createJourneyScreenshotRoute, +} from './pings'; import { createGetDynamicSettingsRoute, createPostDynamicSettingsRoute } from './dynamic_settings'; import { createLogPageViewRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; @@ -40,4 +45,6 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createLogPageViewRoute, createGetPingHistogramRoute, createGetMonitorDurationRoute, + createJourneyRoute, + createJourneyScreenshotRoute, ]; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/index.ts b/x-pack/plugins/uptime/server/rest_api/pings/index.ts index a10ab435e4b0a..88fc0a84c4621 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/index.ts @@ -6,3 +6,5 @@ export { createGetPingsRoute } from './get_pings'; export { createGetPingHistogramRoute } from './get_ping_histogram'; +export { createJourneyRoute } from './journeys'; +export { createJourneyScreenshotRoute } from './journey_screenshots'; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts new file mode 100644 index 0000000000000..1fc52dd24f9d0 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts @@ -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 { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/journey/screenshot/{checkGroup}/{stepIndex}', + validate: { + params: schema.object({ + checkGroup: schema.string(), + stepIndex: schema.number(), + }), + }, + handler: async ({ callES, dynamicSettings }, _context, request, response) => { + const { checkGroup, stepIndex } = request.params; + const result = await libs.requests.getJourneyScreenshot({ + callES, + dynamicSettings, + checkGroup, + stepIndex, + }); + + if (result === null) { + return response.notFound(); + } + return response.ok({ + body: Buffer.from(result, 'base64'), + headers: { + 'content-type': 'image/png', + 'cache-control': 'max-age=600', + }, + }); + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts new file mode 100644 index 0000000000000..b6e06850ad3b6 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -0,0 +1,34 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/journey/{checkGroup}', + validate: { + params: schema.object({ + checkGroup: schema.string(), + }), + }, + handler: async ({ callES, dynamicSettings }, _context, request, response) => { + const { checkGroup } = request.params; + const result = await libs.requests.getJourneySteps({ + callES, + dynamicSettings, + checkGroup, + }); + + return response.ok({ + body: { + checkGroup, + steps: result, + }, + }); + }, +}); diff --git a/x-pack/test/functional/es_archives/uptime/pings/data.json.gz b/x-pack/test/functional/es_archives/uptime/pings/data.json.gz index 83441218aad7340284d2fd04ae2ab71e496a9f5c..a8f7b84b7d0a86cbdb16cb0638de7d7802dd1695 100644 GIT binary patch literal 808 zcmV+@1K0c?iwFokUUOdn17u-zVJ>QOZ*BnHR?BYcFc97I6;akD*pBmD_5MRw-9&C; z8mmdnwH;_x@$c7mc{q?jAE=8fp$g_b<1=&mKJz?Zy{)L9dw#`B|IP^3r0I?1UH{NO zSy$0lV^C8f)DtCYo(3QQ)>!K$??rjE*OGh1S}GN3SH=win9d=Xhu{IGc?9z)4w4K# z?)%dBoKn>7>X;>Or?&K5T}!_8sw}Z>U0oJUI-_hH$sD@5cF?PXPe+2usHWWi=z-QISzHNzk^Swbix*sioPWXy%IwJ#T5mb4y!z(UKDHw-W zSF_V6Z33Se4&gXL&nsw#2hK04P;ALcy+1|ARbiK@AWLQUSYLbu<{+Mf@ByM60R0I7 z0FUlC$3r|(efsEuH9#~6>4Ob07GtF6nsk+{$;;GUqb$P7<+f-0zuVb%HhceVio`k2 zaufz>nthET_o+{T?HCmYqpuMr72@>l=;|eTwt)pLDO=OZ``A~{kcfu?_UP+M9_;7@8P0{}~JL}Qjh@G(`{0=)qgyUo~#zl4x0s1yM>$EBQ5jo>z@K41xcGk?- z@RBqJl{rKq&K8o2H7n_2$5FT7YMkQuB2^!1xWLlaOFid2l$c``z^~KwI9ppTyA5BD mw>(|;6idS>J0j*cS=v6qPk0(9(KuAc`05|O=9Re74FCYwL4cnC literal 765 zcmV6rp}#ed&p;_5A#u(mt3=4chbdui^D7=U(G1g zAe!x{<5gkJQT)qKP|w&XSG4BUyBj%`8{Q1HZAJA;$a+fOYY#zyh<8wK&l}ac9X2YN zYDJ^iX~E`e=}u+2D69%Yc#PU>RBChoH$G(!xz^Y(!B0z)? zoQ|P|rqxvHF!D(}>1sgE3#!?4UW6u{H`0J5lk88&)Ku$1(sv&nXD2JSSLQ3Jbh+GY zHsF=(vU>r!sF&xstwqDN7>>#-I!W~>AO;`ia|lTkCc$X(-4b}itbJJXVqe|kREd3S zx9ljvz=r?_K0pYMEs(=xPpL0CB}s~X5XZ@F1h-Fp3d^2Rb}+h)?wFLPN5fRF=&K7XSyxYdXF+tg~( z#`s6nN+Ng*w;JB6L{2M<$`oOrBumBQn&)h}W0-p|Es05Zk!TM!T;SO2rJgeuj_k1l v=ytLl=h{MSnme)Wc+1mePjM^?68sI0bp!kf%aSPgI@|mOfb?6LO$`75A}V&H From a6e9d16310eb43463a066a964bf65da281c0ca1a Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 2 Oct 2020 17:28:47 -0400 Subject: [PATCH 03/10] skip flaky suite (#75241) --- .../apis/fleet/agents/complete_flow.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts index a59b3ff0890f7..d925d77b5b93f 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts @@ -19,7 +19,8 @@ export default function (providerContext: FtrProviderContext) { const supertestWithoutAuth = getSupertestWithoutAuth(providerContext); const esClient = getService('es'); - describe('fleet_agent_flow', () => { + // Failing: See https://github.com/elastic/kibana/issues/75241 + describe.skip('fleet_agent_flow', () => { skipIfNoDockerRegistry(providerContext); before(async () => { await esArchiver.load('empty_kibana'); From f7a18e64547161a4b6d46a40fa9435092ec64c1e Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 2 Oct 2020 14:49:01 -0700 Subject: [PATCH 04/10] Revert "[babel/register] remove from build (#79176)" This reverts commit 511eb0f23bbc85d8131a1b1364a5f5e06b837858. --- package.json | 7 ++-- packages/kbn-babel-preset/node_preset.js | 8 ++++ packages/kbn-babel-preset/package.json | 1 + .../lib/babel_register_for_test_plugins.js | 39 ------------------- .../functional_tests/lib/run_kibana_server.js | 20 +--------- scripts/build_plugin_list_docs.js | 2 +- scripts/es.js | 2 +- scripts/generate_plugin.js | 2 +- scripts/plugin_helpers.js | 2 +- scripts/register_git_hook.js | 2 +- scripts/release_notes.js | 2 +- scripts/telemetry_check.js | 2 +- scripts/telemetry_extract.js | 2 +- src/cli/index.js | 3 +- .../integration_tests/invalid_config.test.ts | 22 ++++------- src/dev/build/tasks/copy_source_task.ts | 2 - src/setup_node_env/babel_register/register.js | 24 +++++++++--- src/setup_node_env/index.js | 2 +- ...pilation.js => prebuilt_dev_only_entry.js} | 0 x-pack/package.json | 2 +- yarn.lock | 18 ++++++--- 21 files changed, 63 insertions(+), 101 deletions(-) delete mode 100644 packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js rename src/setup_node_env/{no_transpilation.js => prebuilt_dev_only_entry.js} (100%) diff --git a/package.json b/package.json index cebfddbe34e94..5089e6e1a140d 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,8 @@ ] }, "dependencies": { + "@babel/core": "^7.11.1", + "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", @@ -126,6 +128,7 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/apm-config-loader": "1.0.0", + "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", @@ -211,7 +214,6 @@ "rxjs": "^6.5.5", "seedrandom": "^3.0.5", "semver": "^5.7.0", - "source-map-support": "^0.5.19", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -225,9 +227,7 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@babel/core": "^7.11.1", "@babel/parser": "^7.11.2", - "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "23.0.0", @@ -238,7 +238,6 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/ui-ace": "0.2.3", - "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/es-archiver": "1.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 86817ed253e7c..45afe5d5ebc32 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -49,5 +49,13 @@ module.exports = (_, options = {}) => { ], require('./common_preset'), ], + plugins: [ + [ + require.resolve('babel-plugin-transform-define'), + { + 'global.__BUILT_WITH_BABEL__': 'true', + }, + ], + ], }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 79d2fd8687dae..bc4e0ec338f94 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -14,6 +14,7 @@ "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", + "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" diff --git a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js deleted file mode 100644 index 44ff579411bd9..0000000000000 --- a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const Path = require('path'); - -const { REPO_ROOT } = require('@kbn/dev-utils'); - -// modifies all future calls to require() to automatically -// compile the required source with babel -require('@babel/register')({ - ignore: [/[\/\\](node_modules|target|dist)[\/\\]/], - only: [ - Path.resolve(REPO_ROOT, 'test'), - Path.resolve(REPO_ROOT, 'x-pack/test'), - Path.resolve(REPO_ROOT, 'examples'), - Path.resolve(REPO_ROOT, 'x-pack/examples'), - // TODO: should should probably remove this link back to the source - Path.resolve(REPO_ROOT, 'x-pack/plugins/task_manager/server/config.ts'), - ], - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/node_preset')], - extensions: ['.js', '.ts', '.tsx'], -}); diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js index e7ec99467ecfd..fb9f8f7a52408 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js @@ -17,26 +17,9 @@ * under the License. */ -import { resolve, relative } from 'path'; +import { resolve } from 'path'; import { KIBANA_ROOT, KIBANA_EXEC, KIBANA_EXEC_PATH } from './paths'; -function extendNodeOptions(installDir) { - if (!installDir) { - return {}; - } - - const testOnlyRegisterPath = relative( - installDir, - require.resolve('./babel_register_for_test_plugins') - ); - - return { - NODE_OPTIONS: `--require=${testOnlyRegisterPath}${ - process.env.NODE_OPTIONS ? ` ${process.env.NODE_OPTIONS}` : '' - }`, - }; -} - export async function runKibanaServer({ procs, config, options }) { const { installDir } = options; @@ -46,7 +29,6 @@ export async function runKibanaServer({ procs, config, options }) { env: { FORCE_COLOR: 1, ...process.env, - ...extendNodeOptions(installDir), }, cwd: installDir || KIBANA_ROOT, wait: /http server running/, diff --git a/scripts/build_plugin_list_docs.js b/scripts/build_plugin_list_docs.js index 6f184ca7b14c6..54821a1b10ee8 100644 --- a/scripts/build_plugin_list_docs.js +++ b/scripts/build_plugin_list_docs.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/dev-utils').runPluginListCli(); diff --git a/scripts/es.js b/scripts/es.js index 53b01d8cb4414..2d56496f2fdd2 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_plugin.js b/scripts/generate_plugin.js index af3d31048ecfc..f695eabb30f21 100644 --- a/scripts/generate_plugin.js +++ b/scripts/generate_plugin.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/plugin-generator').runCli(); diff --git a/scripts/plugin_helpers.js b/scripts/plugin_helpers.js index f28bf8fcfff90..a07ba7a9185f8 100644 --- a/scripts/plugin_helpers.js +++ b/scripts/plugin_helpers.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/plugin-helpers').runCli(); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index 50dfeaf46109f..af3f54619bcec 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/scripts/release_notes.js b/scripts/release_notes.js index ee9275194ae94..f46ee5823d70d 100644 --- a/scripts/release_notes.js +++ b/scripts/release_notes.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/release-notes').runReleaseNotesCli(); diff --git a/scripts/telemetry_check.js b/scripts/telemetry_check.js index 22a22b401cb15..06b3ed46bdba6 100644 --- a/scripts/telemetry_check.js +++ b/scripts/telemetry_check.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/telemetry-tools').runTelemetryCheck(); diff --git a/scripts/telemetry_extract.js b/scripts/telemetry_extract.js index e2fbb64c26719..051bee26537b9 100644 --- a/scripts/telemetry_extract.js +++ b/scripts/telemetry_extract.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/telemetry-tools').runTelemetryExtract(); diff --git a/src/cli/index.js b/src/cli/index.js index e5480d2137624..45f88eaf82a5b 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -18,6 +18,5 @@ */ require('../apm')(); -require('../setup_node_env/no_transpilation'); -require('../setup_node_env/babel_register/polyfill'); +require('../setup_node_env'); require('./cli'); diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index a72142faa22fe..fd6fa1bf192fc 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -18,10 +18,10 @@ */ import { spawnSync } from 'child_process'; +import { resolve } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; - -const INVALID_CONFIG_PATH = require.resolve('./__fixtures__/invalid_config.yml'); +const ROOT_DIR = resolve(__dirname, '../../../../'); +const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml'); interface LogEntry { message: string; @@ -35,11 +35,11 @@ describe('cli invalid config support', function () { function () { // Unused keys only throw once LegacyService starts, so disable migrations so that Core // will finish the start lifecycle without a running Elasticsearch instance. - const { error, status, stdout, stderr } = spawnSync( + const { error, status, stdout } = spawnSync( process.execPath, - ['scripts/kibana', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], + ['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], { - cwd: REPO_ROOT, + cwd: ROOT_DIR, } ); @@ -57,21 +57,13 @@ describe('cli invalid config support', function () { })); expect(error).toBe(undefined); - - if (!fatalLogLine) { - throw new Error( - `cli did not log the expected fatal error message:\n\nstdout: \n${stdout}\n\nstderr:\n${stderr}` - ); - } - + expect(status).toBe(64); expect(fatalLogLine.message).toContain( 'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' + '"some.array". Check for spelling errors and ensure that expected plugins are installed.' ); expect(fatalLogLine.tags).toEqual(['fatal', 'root']); expect(fatalLogLine.type).toEqual('log'); - - expect(status).toBe(64); }, 20 * 1000 ); diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 78e1395586a17..948e2357effb0 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -37,8 +37,6 @@ export const CopySource: Task = { '!src/cli/repl/**', '!src/functional_test_runner/**', '!src/dev/**', - '!src/setup_node_env/babel_register/index.js', - '!src/setup_node_env/babel_register/register.js', '!**/public/**', 'typings/**', 'config/kibana.yml', diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js index 3c0bd387c8e44..6d573d8922244 100644 --- a/src/setup_node_env/babel_register/register.js +++ b/src/setup_node_env/babel_register/register.js @@ -46,13 +46,27 @@ var ignore = [ // ignore paths matching `/canvas/canvas_plugin/` /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, - - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, ]; +if (global.__BUILT_WITH_BABEL__) { + // when building the Kibana source we replace the statement + // `global.__BUILT_WITH_BABEL__` with the value `true` so that + // when @babel/register is required for the first time by users + // it will exclude kibana's `src` directory. + // + // We still need @babel/register for plugins though, we've been + // building their server code at require-time since version 4.2 + // TODO: the plugin install process could transpile plugin server code... + ignore.push(resolve(__dirname, '../../../src')); +} else { + ignore.push( + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/ + ); +} + // modifies all future calls to require() to automatically // compile the required source with babel require('@babel/register')({ diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index 60f0982f50d20..d84249df7fd8f 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -17,5 +17,5 @@ * under the License. */ -require('./no_transpilation'); +require('./prebuilt_dev_only_entry'); require('./babel_register'); diff --git a/src/setup_node_env/no_transpilation.js b/src/setup_node_env/prebuilt_dev_only_entry.js similarity index 100% rename from src/setup_node_env/no_transpilation.js rename to src/setup_node_env/prebuilt_dev_only_entry.js diff --git a/x-pack/package.json b/x-pack/package.json index 4145d8d72cc63..5742200b55d9f 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -32,7 +32,6 @@ "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", - "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/expect": "1.0.0", @@ -281,6 +280,7 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/safer-lodash-set": "0.0.0", + "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 806424b222ad3..971a94bfe56c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7545,6 +7545,14 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= +babel-plugin-transform-define@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" + integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== + dependencies: + lodash "^4.17.11" + traverse "0.6.6" + babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -27102,10 +27110,10 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -28928,7 +28936,7 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -traverse@^0.6.6, traverse@~0.6.6: +traverse@0.6.6, traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= From ed10d9f8ddd127ad6078747acc793ce36a2dcba2 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg Date: Fri, 2 Oct 2020 17:07:14 -0500 Subject: [PATCH 05/10] =?UTF-8?q?define=20integrationTestRoot=20in=20confi?= =?UTF-8?q?g=20file=20and=20use=20to=20define=20screensho=E2=80=A6=20(#792?= =?UTF-8?q?47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apps/metricbeat/_metricbeat_dashboard.js | 3 +-- .../config.stack_functional_integration_base.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js index 42f707fb77854..0ce09e8f59d2d 100644 --- a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js +++ b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { REPO_ROOT } from '@kbn/dev-utils'; export default function ({ getService, getPageObjects, updateBaselines }) { const screenshot = getService('screenshots'); @@ -15,7 +14,7 @@ export default function ({ getService, getPageObjects, updateBaselines }) { describe('check metricbeat Dashboard', function () { before(async function () { - await esArchiver.load(`${REPO_ROOT}/../integration-test/test/es_archives/metricbeat`); + await esArchiver.load('metricbeat'); // this navigateToActualURL takes the place of navigating to the dashboard landing page, // filtering on the dashboard name, selecting it, setting the timepicker, and going to full screen diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js index a838b129242a1..bc5b28641c28c 100644 --- a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js +++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js @@ -16,10 +16,14 @@ const log = new ToolingLog({ level: 'info', writeTo: process.stdout, }); +log.info(`REPO_ROOT = ${REPO_ROOT}`); log.info(`WORKSPACE in config file ${process.env.WORKSPACE}`); -const stateFilePath = process.env.WORKSPACE - ? `${process.env.WORKSPACE}/qa/envvars.sh` - : `${REPO_ROOT}/../integration-test/qa/envvars.sh`; + +const INTEGRATION_TEST_ROOT = process.env.WORKSPACE || resolve(REPO_ROOT, '../integration-test'); +log.info(`INTEGRATION_TEST_ROOT = ${INTEGRATION_TEST_ROOT}`); + +const stateFilePath = resolve(INTEGRATION_TEST_ROOT, 'qa/envvars.sh'); +log.info(`stateFilePath = ${stateFilePath}`); const prepend = (testFile) => require.resolve(`${testsFolder}/${testFile}`); @@ -46,11 +50,11 @@ export default async ({ readConfigFile }) => { security: { disableTestUser: true }, // choose where screenshots should be saved screenshots: { - directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/screenshots'), + directory: resolve(INTEGRATION_TEST_ROOT, 'test/screenshots'), }, // choose where esArchiver should load archives from esArchiver: { - directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/es_archives'), + directory: resolve(INTEGRATION_TEST_ROOT, 'test/es_archives'), }, }; return settings; From bd80d3c7479ce36b7a0d7bbdc3e2a07d63435e43 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Fri, 2 Oct 2020 16:18:55 -0600 Subject: [PATCH 06/10] [Security Solution] Changes rules table tag display (#77102) --- .../detection_engine/rules/all/columns.tsx | 22 ++-- .../rules/all/helpers.test.tsx | 13 ++- .../detection_engine/rules/all/helpers.ts | 4 + .../detection_engine/rules/all/index.test.tsx | 50 +++++---- .../tags_filter_popover.tsx | 60 ++++++---- .../rules/all/tag_display.test.tsx | 47 ++++++++ .../rules/all/tag_display.tsx | 104 ++++++++++++++++++ .../detection_engine/rules/translations.ts | 7 ++ 8 files changed, 248 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index 3666e16dcbaf8..c0786f5234157 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -7,7 +7,6 @@ /* eslint-disable react/display-name */ import { - EuiBadge, EuiBasicTableColumn, EuiTableActionsColumnType, EuiText, @@ -24,7 +23,6 @@ import { getEmptyTagValue } from '../../../../../common/components/empty_value'; import { FormattedDate } from '../../../../../common/components/formatted_date'; import { getRuleDetailsUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { ActionToaster } from '../../../../../common/components/toasters'; -import { TruncatableText } from '../../../../../common/components/truncatable_text'; import { getStatusColor } from '../../../../components/rules/rule_status/helpers'; import { RuleSwitch } from '../../../../components/rules/rule_switch'; import { SeverityBadge } from '../../../../components/rules/severity_badge'; @@ -39,6 +37,7 @@ import { Action } from './reducer'; import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip'; import * as detectionI18n from '../../translations'; import { LinkAnchor } from '../../../../../common/components/links'; +import { TagsDisplay } from './tag_display'; export const getActions = ( dispatch: React.Dispatch, @@ -207,22 +206,19 @@ export const getColumns = ({ ); }, truncateText: true, - width: '10%', + width: '8%', }, { field: 'tags', name: i18n.COLUMN_TAGS, - render: (value: Rule['tags']) => ( - - {value.map((tag, i) => ( - - {tag} - - ))} - - ), + render: (value: Rule['tags']) => { + if (value.length > 0) { + return ; + } + return getEmptyTagValue(); + }, truncateText: true, - width: '14%', + width: '20%', }, { align: 'center', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx index 062d7967bf301..e06aea2fb2785 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { bucketRulesResponse, showRulesTable } from './helpers'; +import { bucketRulesResponse, caseInsensitiveSort, showRulesTable } from './helpers'; import { mockRule, mockRuleError } from './__mocks__/mock'; import uuid from 'uuid'; import { Rule, RuleError } from '../../../../containers/detection_engine/rules'; @@ -86,4 +86,15 @@ describe('AllRulesTable Helpers', () => { expect(result).toBeTruthy(); }); }); + + describe('caseInsensitiveSort', () => { + describe('when an array of differently cased tags is passed', () => { + const unsortedTags = ['atest', 'Ctest', 'Btest', 'ctest', 'btest', 'Atest']; + const result = caseInsensitiveSort(unsortedTags); + it('returns an alphabetically sorted array with no regard for casing', () => { + const expected = ['atest', 'Atest', 'Btest', 'btest', 'Ctest', 'ctest']; + expect(result).toEqual(expected); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts index 0ebeb84d57468..c54c9ed9b8ef9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts @@ -33,3 +33,7 @@ export const showRulesTable = ({ }) => (rulesCustomInstalled != null && rulesCustomInstalled > 0) || (rulesInstalled != null && rulesInstalled > 0); + +export const caseInsensitiveSort = (tags: string[]): string[] => { + return tags.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase())); // Case insensitive +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 13c6985a30c2b..f8ed57aa7537e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -5,7 +5,8 @@ */ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { shallow, mount, ReactWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; import '../../../../../common/mock/match_media'; import '../../../../../common/mock/formatted_relative'; @@ -179,27 +180,34 @@ describe('AllRules', () => { expect(wrapper.find('[title="All rules"]')).toHaveLength(1); }); - it('renders rules tab', async () => { - const wrapper = mount( - - - - ); + describe('rules tab', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + wrapper = mount( + + + + ); + }); - await waitFor(() => { - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); + it('renders correctly', async () => { + await act(async () => { + await waitFor(() => { + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx index 4fe0bc8f835df..9748dde91d18b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx @@ -27,6 +27,7 @@ import { import styled from 'styled-components'; import * as i18n from '../../translations'; import { toggleSelectedGroup } from '../../../../../../common/components/ml_popover/jobs_table/filters/toggle_selected_group'; +import { caseInsensitiveSort } from '../helpers'; interface TagsFilterPopoverProps { selectedTags: string[]; @@ -36,9 +37,19 @@ interface TagsFilterPopoverProps { isLoading: boolean; // TO DO reimplement? } +const PopoverContentWrapper = styled.div` + width: 275px; +`; + const ScrollableDiv = styled.div` max-height: 250px; - overflow: auto; + overflow-y: auto; +`; + +const TagOverflowContainer = styled.span` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `; /** @@ -52,9 +63,7 @@ const TagsFilterPopoverComponent = ({ selectedTags, onSelectedTagsChanged, }: TagsFilterPopoverProps) => { - const sortedTags = useMemo(() => { - return tags.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase())); // Case insensitive - }, [tags]); + const sortedTags = useMemo(() => caseInsensitiveSort(tags), [tags]); const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); const [searchInput, setSearchInput] = useState(''); const [filterTags, setFilterTags] = useState(sortedTags); @@ -65,8 +74,9 @@ const TagsFilterPopoverComponent = ({ checked={selectedTags.includes(tag) ? 'on' : undefined} key={`${index}-${tag}`} onClick={() => toggleSelectedGroup(tag, selectedTags, onSelectedTagsChanged)} + title={tag} > - {`${tag}`} + {tag} )); }, [onSelectedTagsChanged, selectedTags, filterTags]); @@ -101,25 +111,27 @@ const TagsFilterPopoverComponent = ({ panelPaddingSize="none" repositionOnScroll > - - - - {tagsComponent} - {filterTags.length === 0 && ( - - - - {i18n.NO_TAGS_AVAILABLE} - - - - )} + + + + + {tagsComponent} + {filterTags.length === 0 && ( + + + + {i18n.NO_TAGS_AVAILABLE} + + + + )} + ); }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx new file mode 100644 index 0000000000000..3e60bb2d3168b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx @@ -0,0 +1,47 @@ +/* + * 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 { mount, ReactWrapper } from 'enzyme'; + +import { TagsDisplay } from './tag_display'; +import { TestProviders } from '../../../../../common/mock'; +import { waitFor } from '@testing-library/react'; + +const mockTags = ['Elastic', 'Endpoint', 'Data Protection', 'ML', 'Continuous Monitoring']; + +describe('When tag display loads', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + wrapper = mount( + + + + ); + }); + it('visibly renders 3 initial tags', () => { + for (let i = 0; i < 3; i++) { + expect(wrapper.exists(`[data-test-subj="rules-table-column-tags-${i}"]`)).toBeTruthy(); + } + }); + describe("when the 'see all' button is clicked", () => { + beforeEach(() => { + const seeAllButton = wrapper.find('[data-test-subj="tags-display-popover-button"] button'); + seeAllButton.simulate('click'); + }); + it('renders all the tags in the popover', async () => { + await waitFor(() => { + wrapper.update(); + expect(wrapper.exists('[data-test-subj="tags-display-popover"]')).toBeTruthy(); + for (let i = 0; i < mockTags.length; i++) { + expect( + wrapper.exists(`[data-test-subj="rules-table-column-popover-tags-${i}"]`) + ).toBeTruthy(); + } + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx new file mode 100644 index 0000000000000..9503f5b884133 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx @@ -0,0 +1,104 @@ +/* + * 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, { useMemo, useState } from 'react'; +import { EuiPopover, EuiBadgeGroup, EuiBadge, EuiButtonEmpty } from '@elastic/eui'; +import styled from 'styled-components'; +import * as i18n from '../translations'; +import { caseInsensitiveSort } from './helpers'; + +interface TagsDisplayProps { + tags: string[]; +} + +const TagWrapper = styled(EuiBadgeGroup)` + width: 100%; +`; + +const TagPopoverWrapper = styled(EuiBadgeGroup)` + max-height: 200px; + max-width: 600px; + overflow: auto; +`; + +const TagPopoverButton = styled(EuiButtonEmpty)` + font-size: ${({ theme }) => theme.eui.euiFontSizeXS} + font-weight: 500; + height: 20px; +`; + +/** + * @param tags to display for filtering + */ +const TagsDisplayComponent = ({ tags }: TagsDisplayProps) => { + const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); + const sortedTags = useMemo(() => caseInsensitiveSort(tags), [tags]); + + return ( + <> + {sortedTags.length <= 3 ? ( + + {sortedTags.map((tag: string, i: number) => ( + + {tag} + + ))} + + ) : ( + + {sortedTags.slice(0, 3).map((tag: string, i: number) => ( + + {tag} + + ))} + + setIsTagPopoverOpen(!isTagPopoverOpen)} + > + {i18n.COLUMN_SEE_ALL_POPOVER} + + } + isOpen={isTagPopoverOpen} + closePopover={() => setIsTagPopoverOpen(!isTagPopoverOpen)} + repositionOnScroll + > + + {sortedTags.map((tag: string, i: number) => ( + + {tag} + + ))} + + + + )} + + ); +}; + +export const TagsDisplay = React.memo(TagsDisplayComponent); + +TagsDisplay.displayName = 'TagsDisplay'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 09503fcf1ef0f..f2f3265ead1ba 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -315,6 +315,13 @@ export const COLUMN_TAGS = i18n.translate( } ); +export const COLUMN_SEE_ALL_POPOVER = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.tagsPopoverTitle', + { + defaultMessage: 'See all', + } +); + export const COLUMN_ACTIVATE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.activateTitle', { From dc0bccf8f881951e47fdec419d6edccf998c220f Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 2 Oct 2020 16:30:16 -0700 Subject: [PATCH 07/10] [babel/register] remove from build (take 2) (#79379) Co-authored-by: spalger --- package.json | 7 ++-- packages/kbn-babel-preset/node_preset.js | 8 ---- packages/kbn-babel-preset/package.json | 1 - .../lib/babel_register_for_test_plugins.js | 39 +++++++++++++++++++ .../functional_tests/lib/run_kibana_server.js | 20 +++++++++- scripts/build_plugin_list_docs.js | 2 +- scripts/es.js | 2 +- scripts/generate_plugin.js | 2 +- scripts/kibana.js | 4 +- scripts/plugin_helpers.js | 2 +- scripts/register_git_hook.js | 2 +- scripts/release_notes.js | 2 +- scripts/telemetry_check.js | 2 +- scripts/telemetry_extract.js | 2 +- src/cli/cluster/worker.ts | 2 +- src/cli/{index.js => dev.js} | 2 +- src/cli/dist.js | 23 +++++++++++ .../integration_tests/invalid_config.test.ts | 22 +++++++---- src/dev/build/tasks/bin/scripts/kibana | 2 +- src/dev/build/tasks/copy_source_task.ts | 3 ++ src/setup_node_env/babel_register/register.js | 24 +++--------- src/setup_node_env/index.js | 2 +- ..._dev_only_entry.js => no_transpilation.js} | 0 x-pack/package.json | 2 +- yarn.lock | 18 +++------ 25 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js rename src/cli/{index.js => dev.js} (91%) create mode 100644 src/cli/dist.js rename src/setup_node_env/{prebuilt_dev_only_entry.js => no_transpilation.js} (100%) diff --git a/package.json b/package.json index 5089e6e1a140d..cebfddbe34e94 100644 --- a/package.json +++ b/package.json @@ -115,8 +115,6 @@ ] }, "dependencies": { - "@babel/core": "^7.11.1", - "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", @@ -128,7 +126,6 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/apm-config-loader": "1.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", @@ -214,6 +211,7 @@ "rxjs": "^6.5.5", "seedrandom": "^3.0.5", "semver": "^5.7.0", + "source-map-support": "^0.5.19", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -227,7 +225,9 @@ "yauzl": "^2.10.0" }, "devDependencies": { + "@babel/core": "^7.11.1", "@babel/parser": "^7.11.2", + "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "23.0.0", @@ -238,6 +238,7 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/ui-ace": "0.2.3", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/es-archiver": "1.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 45afe5d5ebc32..86817ed253e7c 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -49,13 +49,5 @@ module.exports = (_, options = {}) => { ], require('./common_preset'), ], - plugins: [ - [ - require.resolve('babel-plugin-transform-define'), - { - 'global.__BUILT_WITH_BABEL__': 'true', - }, - ], - ], }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index bc4e0ec338f94..79d2fd8687dae 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -14,7 +14,6 @@ "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", - "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" diff --git a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js new file mode 100644 index 0000000000000..44ff579411bd9 --- /dev/null +++ b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const Path = require('path'); + +const { REPO_ROOT } = require('@kbn/dev-utils'); + +// modifies all future calls to require() to automatically +// compile the required source with babel +require('@babel/register')({ + ignore: [/[\/\\](node_modules|target|dist)[\/\\]/], + only: [ + Path.resolve(REPO_ROOT, 'test'), + Path.resolve(REPO_ROOT, 'x-pack/test'), + Path.resolve(REPO_ROOT, 'examples'), + Path.resolve(REPO_ROOT, 'x-pack/examples'), + // TODO: should should probably remove this link back to the source + Path.resolve(REPO_ROOT, 'x-pack/plugins/task_manager/server/config.ts'), + ], + babelrc: false, + presets: [require.resolve('@kbn/babel-preset/node_preset')], + extensions: ['.js', '.ts', '.tsx'], +}); diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js index fb9f8f7a52408..e7ec99467ecfd 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js @@ -17,9 +17,26 @@ * under the License. */ -import { resolve } from 'path'; +import { resolve, relative } from 'path'; import { KIBANA_ROOT, KIBANA_EXEC, KIBANA_EXEC_PATH } from './paths'; +function extendNodeOptions(installDir) { + if (!installDir) { + return {}; + } + + const testOnlyRegisterPath = relative( + installDir, + require.resolve('./babel_register_for_test_plugins') + ); + + return { + NODE_OPTIONS: `--require=${testOnlyRegisterPath}${ + process.env.NODE_OPTIONS ? ` ${process.env.NODE_OPTIONS}` : '' + }`, + }; +} + export async function runKibanaServer({ procs, config, options }) { const { installDir } = options; @@ -29,6 +46,7 @@ export async function runKibanaServer({ procs, config, options }) { env: { FORCE_COLOR: 1, ...process.env, + ...extendNodeOptions(installDir), }, cwd: installDir || KIBANA_ROOT, wait: /http server running/, diff --git a/scripts/build_plugin_list_docs.js b/scripts/build_plugin_list_docs.js index 54821a1b10ee8..6f184ca7b14c6 100644 --- a/scripts/build_plugin_list_docs.js +++ b/scripts/build_plugin_list_docs.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils').runPluginListCli(); diff --git a/scripts/es.js b/scripts/es.js index 2d56496f2fdd2..53b01d8cb4414 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_plugin.js b/scripts/generate_plugin.js index f695eabb30f21..af3d31048ecfc 100644 --- a/scripts/generate_plugin.js +++ b/scripts/generate_plugin.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-generator').runCli(); diff --git a/scripts/kibana.js b/scripts/kibana.js index f5a63e6c07dd6..2767e555f2736 100644 --- a/scripts/kibana.js +++ b/scripts/kibana.js @@ -17,6 +17,4 @@ * under the License. */ -require('../src/apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); -require('../src/setup_node_env'); -require('../src/cli/cli'); +require('../src/cli/dev'); diff --git a/scripts/plugin_helpers.js b/scripts/plugin_helpers.js index a07ba7a9185f8..f28bf8fcfff90 100644 --- a/scripts/plugin_helpers.js +++ b/scripts/plugin_helpers.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-helpers').runCli(); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index af3f54619bcec..50dfeaf46109f 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/scripts/release_notes.js b/scripts/release_notes.js index f46ee5823d70d..ee9275194ae94 100644 --- a/scripts/release_notes.js +++ b/scripts/release_notes.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/release-notes').runReleaseNotesCli(); diff --git a/scripts/telemetry_check.js b/scripts/telemetry_check.js index 06b3ed46bdba6..22a22b401cb15 100644 --- a/scripts/telemetry_check.js +++ b/scripts/telemetry_check.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryCheck(); diff --git a/scripts/telemetry_extract.js b/scripts/telemetry_extract.js index 051bee26537b9..e2fbb64c26719 100644 --- a/scripts/telemetry_extract.js +++ b/scripts/telemetry_extract.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryExtract(); diff --git a/src/cli/cluster/worker.ts b/src/cli/cluster/worker.ts index c8a8a067d30bf..f6205b41ac5a5 100644 --- a/src/cli/cluster/worker.ts +++ b/src/cli/cluster/worker.ts @@ -24,7 +24,7 @@ import { EventEmitter } from 'events'; import { BinderFor } from './binder_for'; import { fromRoot } from '../../core/server/utils'; -const cliPath = fromRoot('src/cli'); +const cliPath = fromRoot('src/cli/dev'); const baseArgs = _.difference(process.argv.slice(2), ['--no-watch']); const baseArgv = [process.execPath, cliPath].concat(baseArgs); diff --git a/src/cli/index.js b/src/cli/dev.js similarity index 91% rename from src/cli/index.js rename to src/cli/dev.js index 45f88eaf82a5b..9d0cb35c3d730 100644 --- a/src/cli/index.js +++ b/src/cli/dev.js @@ -17,6 +17,6 @@ * under the License. */ -require('../apm')(); +require('../apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); require('../setup_node_env'); require('./cli'); diff --git a/src/cli/dist.js b/src/cli/dist.js new file mode 100644 index 0000000000000..e5480d2137624 --- /dev/null +++ b/src/cli/dist.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +require('../apm')(); +require('../setup_node_env/no_transpilation'); +require('../setup_node_env/babel_register/polyfill'); +require('./cli'); diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index fd6fa1bf192fc..a72142faa22fe 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -18,10 +18,10 @@ */ import { spawnSync } from 'child_process'; -import { resolve } from 'path'; -const ROOT_DIR = resolve(__dirname, '../../../../'); -const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml'); +import { REPO_ROOT } from '@kbn/dev-utils'; + +const INVALID_CONFIG_PATH = require.resolve('./__fixtures__/invalid_config.yml'); interface LogEntry { message: string; @@ -35,11 +35,11 @@ describe('cli invalid config support', function () { function () { // Unused keys only throw once LegacyService starts, so disable migrations so that Core // will finish the start lifecycle without a running Elasticsearch instance. - const { error, status, stdout } = spawnSync( + const { error, status, stdout, stderr } = spawnSync( process.execPath, - ['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], + ['scripts/kibana', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], { - cwd: ROOT_DIR, + cwd: REPO_ROOT, } ); @@ -57,13 +57,21 @@ describe('cli invalid config support', function () { })); expect(error).toBe(undefined); - expect(status).toBe(64); + + if (!fatalLogLine) { + throw new Error( + `cli did not log the expected fatal error message:\n\nstdout: \n${stdout}\n\nstderr:\n${stderr}` + ); + } + expect(fatalLogLine.message).toContain( 'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' + '"some.array". Check for spelling errors and ensure that expected plugins are installed.' ); expect(fatalLogLine.tags).toEqual(['fatal', 'root']); expect(fatalLogLine.type).toEqual('log'); + + expect(status).toBe(64); }, 20 * 1000 ); diff --git a/src/dev/build/tasks/bin/scripts/kibana b/src/dev/build/tasks/bin/scripts/kibana index 3283e17008e7c..c606436c7b83f 100755 --- a/src/dev/build/tasks/bin/scripts/kibana +++ b/src/dev/build/tasks/bin/scripts/kibana @@ -26,4 +26,4 @@ if [ -f "${CONFIG_DIR}/node.options" ]; then KBN_NODE_OPTS="$(grep -v ^# < ${CONFIG_DIR}/node.options | xargs)" fi -NODE_OPTIONS="--no-warnings --max-http-header-size=65536 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli" ${@} +NODE_OPTIONS="--no-warnings --max-http-header-size=65536 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli/dist" ${@} diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 948e2357effb0..a5039717760ae 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -35,8 +35,11 @@ export const CopySource: Task = { '!src/fixtures/**', '!src/cli/cluster/**', '!src/cli/repl/**', + '!src/cli/dev.js', '!src/functional_test_runner/**', '!src/dev/**', + '!src/setup_node_env/babel_register/index.js', + '!src/setup_node_env/babel_register/register.js', '!**/public/**', 'typings/**', 'config/kibana.yml', diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js index 6d573d8922244..3c0bd387c8e44 100644 --- a/src/setup_node_env/babel_register/register.js +++ b/src/setup_node_env/babel_register/register.js @@ -46,26 +46,12 @@ var ignore = [ // ignore paths matching `/canvas/canvas_plugin/` /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, -]; -if (global.__BUILT_WITH_BABEL__) { - // when building the Kibana source we replace the statement - // `global.__BUILT_WITH_BABEL__` with the value `true` so that - // when @babel/register is required for the first time by users - // it will exclude kibana's `src` directory. - // - // We still need @babel/register for plugins though, we've been - // building their server code at require-time since version 4.2 - // TODO: the plugin install process could transpile plugin server code... - ignore.push(resolve(__dirname, '../../../src')); -} else { - ignore.push( - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/ - ); -} + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, +]; // modifies all future calls to require() to automatically // compile the required source with babel diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index d84249df7fd8f..60f0982f50d20 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -17,5 +17,5 @@ * under the License. */ -require('./prebuilt_dev_only_entry'); +require('./no_transpilation'); require('./babel_register'); diff --git a/src/setup_node_env/prebuilt_dev_only_entry.js b/src/setup_node_env/no_transpilation.js similarity index 100% rename from src/setup_node_env/prebuilt_dev_only_entry.js rename to src/setup_node_env/no_transpilation.js diff --git a/x-pack/package.json b/x-pack/package.json index 5742200b55d9f..4145d8d72cc63 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -32,6 +32,7 @@ "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/expect": "1.0.0", @@ -280,7 +281,6 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/safer-lodash-set": "0.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 971a94bfe56c3..806424b222ad3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7545,14 +7545,6 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-transform-define@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" - integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== - dependencies: - lodash "^4.17.11" - traverse "0.6.6" - babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -27110,10 +27102,10 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== +source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -28936,7 +28928,7 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -traverse@0.6.6, traverse@^0.6.6, traverse@~0.6.6: +traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= From 9ee9bc7f2000540f266cb45d9c87fc2a3f745899 Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 2 Oct 2020 16:41:05 -0700 Subject: [PATCH 08/10] [core/server/plugins] don't run discovery in dev server parent process (take 2) (#79358) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/plugins/plugins_service.test.ts | 81 +++++++++++++------ src/core/server/plugins/plugins_service.ts | 18 +++-- src/core/server/server.test.ts | 17 ++++ src/core/server/server.ts | 11 ++- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index d36fd2251176a..64a382e539fb0 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -102,35 +102,42 @@ const createPlugin = ( }); }; -describe('PluginsService', () => { - beforeEach(async () => { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; +async function testSetup(options: { isDevClusterMaster?: boolean } = {}) { + mockPackage.raw = { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }; - coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, getEnvOptions()); + coreId = Symbol('core'); + env = Env.createDefault(REPO_ROOT, { + ...getEnvOptions(), + isDevClusterMaster: options.isDevClusterMaster ?? false, + }); - config$ = new BehaviorSubject>({ plugins: { initialize: true } }); - const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); - configService = new ConfigService(rawConfigService, env, logger); - await configService.setSchema(config.path, config.schema); - pluginsService = new PluginsService({ coreId, env, logger, configService }); + config$ = new BehaviorSubject>({ plugins: { initialize: true } }); + const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); + configService = new ConfigService(rawConfigService, env, logger); + await configService.setSchema(config.path, config.schema); + pluginsService = new PluginsService({ coreId, env, logger, configService }); - [mockPluginSystem] = MockPluginsSystem.mock.instances as any; - mockPluginSystem.uiPlugins.mockReturnValue(new Map()); + [mockPluginSystem] = MockPluginsSystem.mock.instances as any; + mockPluginSystem.uiPlugins.mockReturnValue(new Map()); - environmentSetup = environmentServiceMock.createSetupContract(); - }); + environmentSetup = environmentServiceMock.createSetupContract(); +} - afterEach(() => { - jest.clearAllMocks(); +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('PluginsService', () => { + beforeEach(async () => { + await testSetup(); }); describe('#discover()', () => { @@ -613,3 +620,29 @@ describe('PluginsService', () => { }); }); }); + +describe('PluginService when isDevClusterMaster is true', () => { + beforeEach(async () => { + await testSetup({ + isDevClusterMaster: true, + }); + }); + + describe('#discover()', () => { + it('does not try to run discovery', async () => { + await expect(pluginsService.discover({ environment: environmentSetup })).resolves + .toMatchInlineSnapshot(` + Object { + "pluginTree": undefined, + "uiPlugins": Object { + "browserConfigs": Map {}, + "internal": Map {}, + "public": Map {}, + }, + } + `); + + expect(mockDiscover).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index e8fe42ee491ca..a1062bde7765f 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -18,7 +18,7 @@ */ import Path from 'path'; -import { Observable } from 'rxjs'; +import { Observable, EMPTY } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; import { pick } from '@kbn/std'; @@ -86,9 +86,11 @@ export class PluginsService implements CoreService; private readonly pluginConfigDescriptors = new Map(); private readonly uiPluginInternalInfo = new Map(); + private readonly discoveryDisabled: boolean; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); + this.discoveryDisabled = coreContext.env.isDevClusterMaster; this.pluginsSystem = new PluginsSystem(coreContext); this.configService = coreContext.configService; this.config$ = coreContext.configService @@ -97,13 +99,17 @@ export class PluginsService implements CoreService { + const devParentEnv = Env.createDefault(REPO_ROOT, { + ...getEnvOptions(), + isDevClusterMaster: true, + }); + + const server = new Server(rawConfigService, devParentEnv, logger); + await server.setup(); + + expect(mockEnsureValidConfiguration).not.toHaveBeenCalled(); + expect(mockContextService.setup).toHaveBeenCalled(); + expect(mockAuditTrailService.setup).toHaveBeenCalled(); + expect(mockHttpService.setup).toHaveBeenCalled(); + expect(mockElasticsearchService.setup).toHaveBeenCalled(); + expect(mockSavedObjectsService.setup).toHaveBeenCalled(); +}); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index ece10db41962d..600f45e0b50da 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -117,10 +117,13 @@ export class Server { }); const legacyConfigSetup = await this.legacy.setupLegacyConfig(); - // Immediately terminate in case of invalid configuration - // This needs to be done after plugin discovery - await this.configService.validate(); - await ensureValidConfiguration(this.configService, legacyConfigSetup); + // rely on dev server to validate config, don't validate in the parent process + if (!this.env.isDevClusterMaster) { + // Immediately terminate in case of invalid configuration + // This needs to be done after plugin discovery + await this.configService.validate(); + await ensureValidConfiguration(this.configService, legacyConfigSetup); + } const contextServiceSetup = this.context.setup({ // We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins: From 92a629fc1f3ed49c5839ba12df36a1c2e1e8d135 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Fri, 2 Oct 2020 20:11:37 -0400 Subject: [PATCH 09/10] =?UTF-8?q?[Ingest=20Manager]=20Rename=20Fleet=20set?= =?UTF-8?q?up=20and=20requirement,=20Fleet=20=3D>=20Central=E2=80=A6=20(#7?= =?UTF-8?q?9291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_enrollment_flyout/managed_instructions.tsx | 2 +- .../sections/fleet/setup_page/index.tsx | 12 ++++++------ x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx index c840b487a3970..e620424f88f4e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx @@ -81,7 +81,7 @@ export const ManagedInstructions = React.memo(({ agentPolicies }) => { ), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx index fbd74f8b03e72..7f0a23caa5fa2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx @@ -73,7 +73,7 @@ export const SetupPage: React.FunctionComponent<{ ) { return ( - + @@ -95,15 +95,15 @@ export const SetupPage: React.FunctionComponent<{ @@ -127,7 +127,7 @@ export const SetupPage: React.FunctionComponent<{ > diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f144cbd3873f7..420df7618b934 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9246,7 +9246,6 @@ "xpack.ingestManager.setupPage.elasticsearchApiKeyFlagText": "{apiKeyLink}.{apiKeyFlag}を{true}に設定します。", "xpack.ingestManager.setupPage.elasticsearchSecurityFlagText": "{esSecurityLink}.{securityFlag}を{true}に設定します。", "xpack.ingestManager.setupPage.elasticsearchSecurityLink": "Elasticsearchセキュリティ", - "xpack.ingestManager.setupPage.enableFleet": "ユーザーを作成してフリートを有効にます", "xpack.ingestManager.setupPage.enableText": "フリートを使用するには、Elasticユーザーを作成する必要があります。このユーザーは、APIキーを作成して、logs-*およびmetrics-*に書き込むことができます。", "xpack.ingestManager.setupPage.enableTitle": "フリートを有効にする", "xpack.ingestManager.setupPage.encryptionKeyFlagText": "{encryptionKeyLink}.{keyFlag}を32文字以上の英数字に設定します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c4a599fa35e7f..a17be0f992b65 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9252,7 +9252,6 @@ "xpack.ingestManager.setupPage.elasticsearchApiKeyFlagText": "{apiKeyLink}。将 {apiKeyFlag} 设置为 {true}。", "xpack.ingestManager.setupPage.elasticsearchSecurityFlagText": "{esSecurityLink}。将 {securityFlag} 设置为 {true}。", "xpack.ingestManager.setupPage.elasticsearchSecurityLink": "Elasticsearch 安全", - "xpack.ingestManager.setupPage.enableFleet": "创建用户并启用 Fleet", "xpack.ingestManager.setupPage.enableText": "要使用 Fleet,必须创建 Elastic 用户。此用户可以创建 API 密钥并写入到 logs-* and metrics-*。", "xpack.ingestManager.setupPage.enableTitle": "启用 Fleet", "xpack.ingestManager.setupPage.encryptionKeyFlagText": "{encryptionKeyLink}。将 {keyFlag} 设置为至少 32 个字符的字母数字值。", From ddf2d82e4446701024a0db9ba278174289874ff0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 2 Oct 2020 18:36:25 -0700 Subject: [PATCH 10/10] [kbn/optimizer] implement more efficient auto transpilation for node (#79052) Co-authored-by: spalger --- NOTICE.txt | 24 +++ package.json | 7 +- packages/kbn-apm-config-loader/package.json | 2 +- packages/kbn-config/package.json | 2 +- packages/kbn-dev-utils/package.json | 2 +- packages/kbn-dev-utils/src/index.ts | 2 +- packages/kbn-i18n/package.json | 2 +- packages/kbn-interpreter/package.json | 2 +- packages/kbn-optimizer/package.json | 8 + packages/kbn-optimizer/src/index.ts | 1 + packages/kbn-optimizer/src/node/cache.ts | 171 ++++++++++++++++ .../kbn-optimizer/src/node/index.ts | 6 +- .../src/node/node_auto_tranpilation.ts | 187 ++++++++++++++++++ .../kbn-optimizer/src/node/polyfill.ts | 11 +- packages/kbn-pm/package.json | 2 +- packages/kbn-ui-framework/package.json | 4 +- packages/kbn-ui-shared-deps/package.json | 2 +- packages/kbn-utils/src/repo_root.ts | 50 +++-- scripts/precommit_hook.js | 2 +- src/cli/dist.js | 2 +- .../tasks/create_empty_dirs_and_files_task.ts | 3 +- .../service_templates/sysv/etc/default/kibana | 2 - src/dev/ci_setup/setup_env.sh | 9 - src/dev/jest/setup/babel_polyfill.js | 2 +- src/setup_node_env/babel_register/register.js | 63 ------ src/setup_node_env/index.js | 2 +- x-pack/package.json | 5 +- .../apm/scripts/aggregate-latency-metrics.js | 12 +- .../create-functional-tests-archive.js | 9 +- x-pack/plugins/apm/scripts/jest.js | 9 +- .../apm/scripts/setup-kibana-security.js | 9 +- .../apm/scripts/upload-telemetry-data.js | 12 +- yarn.lock | 128 ++++++++++-- 33 files changed, 563 insertions(+), 191 deletions(-) create mode 100644 packages/kbn-optimizer/src/node/cache.ts rename src/setup_node_env/babel_register/index.js => packages/kbn-optimizer/src/node/index.ts (81%) create mode 100644 packages/kbn-optimizer/src/node/node_auto_tranpilation.ts rename src/setup_node_env/babel_register/polyfill.js => packages/kbn-optimizer/src/node/polyfill.ts (61%) delete mode 100644 src/setup_node_env/babel_register/register.js diff --git a/NOTICE.txt b/NOTICE.txt index d689abf4c4e05..0504b7f7d6db2 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -208,6 +208,30 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + --- This product bundles bootstrap@3.3.6 which is available under a "MIT" license. diff --git a/package.json b/package.json index cebfddbe34e94..30d614aa43f7b 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,6 @@ "chokidar": "^3.4.2", "color": "1.0.3", "commander": "^3.0.2", - "core-js": "^3.6.4", "cypress-promise": "^1.1.0", "deep-freeze-strict": "^1.1.1", "del": "^5.1.0", @@ -178,7 +177,7 @@ "inert": "^5.1.0", "inline-style": "^2.0.0", "joi": "^13.5.2", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", "json-stringify-safe": "5.0.1", "lodash": "^4.17.20", @@ -225,7 +224,7 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/parser": "^7.11.2", "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", @@ -262,7 +261,7 @@ "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", "@types/archiver": "^3.1.0", - "@types/babel__core": "^7.1.2", + "@types/babel__core": "^7.1.10", "@types/bluebird": "^3.1.1", "@types/boom": "^7.2.0", "@types/chance": "^1.0.0", diff --git a/packages/kbn-apm-config-loader/package.json b/packages/kbn-apm-config-loader/package.json index c570fdc0218b9..6865e9ec9bf66 100644 --- a/packages/kbn-apm-config-loader/package.json +++ b/packages/kbn-apm-config-loader/package.json @@ -13,7 +13,7 @@ "dependencies": { "@elastic/safer-lodash-set": "0.0.0", "@kbn/utils": "1.0.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "lodash": "^4.17.20" }, "devDependencies": { diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index 062520f47f0f9..6d2d56b929ead 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -16,7 +16,7 @@ "@kbn/logging": "1.0.0", "@kbn/std": "1.0.0", "@kbn/utility-types": "1.0.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "load-json-file": "^6.2.0", "lodash": "^4.17.20", "moment": "^2.24.0", diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a85f5924f0ea2..a51734168cf76 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -10,7 +10,7 @@ "kbn:watch": "yarn build --watch" }, "dependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@kbn/utils": "1.0.0", "axios": "^0.19.2", "chalk": "^4.1.0", diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 8217999b01128..6a845825f0fd4 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { REPO_ROOT } from '@kbn/utils'; +export * from '@kbn/utils'; export { withProcRunner, ProcRunner } from './proc_runner'; export * from './tooling_log'; export * from './serializers'; diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 9e0ec50fb838e..f23faecbeaa67 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/intl-relativeformat": "^2.1.0", diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index 430dac6cb2e00..4d415e96389df 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/plugin-transform-modules-commonjs": "^7.10.4", "@babel/plugin-transform-runtime": "^7.11.0", "@kbn/babel-preset": "1.0.0", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 5871c81f48aea..52f9349aec696 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", @@ -19,21 +20,26 @@ "clean-webpack-plugin": "^3.0.0", "compression-webpack-plugin": "^4.0.0", "cpy": "^8.0.0", + "core-js": "^3.6.5", "css-loader": "^3.4.2", "del": "^5.1.0", "execa": "^4.0.2", "file-loader": "^4.2.0", "istanbul-instrumenter-loader": "^3.0.1", "jest-diff": "^26.4.2", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", + "lmdb-store": "^0.6.10", "loader-utils": "^1.2.3", "node-sass": "^4.13.1", "normalize-path": "^3.0.0", + "pirates": "^4.0.1", "postcss": "^7.0.32", "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "rxjs": "^6.5.5", "sass-loader": "^8.0.2", + "source-map-support": "^0.5.19", "style-loader": "^1.1.3", "terser-webpack-plugin": "^2.1.2", "tinymath": "1.2.1", @@ -44,8 +50,10 @@ "webpack-merge": "^4.2.2" }, "devDependencies": { + "@types/babel__core": "^7.1.10", "@types/compression-webpack-plugin": "^2.0.2", "@types/loader-utils": "^1.1.3", + "@types/source-map-support": "^0.5.3", "@types/watchpack": "^1.1.5", "@types/webpack": "^4.41.3" } diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 39cf2120baf0a..549e4b13a4ac0 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -21,3 +21,4 @@ export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; export * from './report_optimizer_stats'; +export * from './node'; diff --git a/packages/kbn-optimizer/src/node/cache.ts b/packages/kbn-optimizer/src/node/cache.ts new file mode 100644 index 0000000000000..7fbf009e38a7d --- /dev/null +++ b/packages/kbn-optimizer/src/node/cache.ts @@ -0,0 +1,171 @@ +/* + * 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 Path from 'path'; + +// @ts-expect-error no types available +import * as LmdbStore from 'lmdb-store'; +import { REPO_ROOT, UPSTREAM_BRANCH } from '@kbn/dev-utils'; + +const CACHE_DIR = Path.resolve(REPO_ROOT, 'data/node_auto_transpilation_cache', UPSTREAM_BRANCH); +const reportError = () => { + // right now I'm not sure we need to worry about errors, the cache isn't actually + // necessary, and if the cache is broken it should just rebuild on the next restart + // of the process. We don't know how often errors occur though and what types of + // things might fail on different machines so we probably want some way to signal + // to users that something is wrong +}; + +const GLOBAL_ATIME = `${Date.now()}`; +const MINUTE = 1000 * 60; +const HOUR = MINUTE * 60; +const DAY = HOUR * 24; + +interface Lmdb { + get(key: string): T | undefined; + put(key: string, value: T, version?: number, ifVersion?: number): Promise; + remove(key: string, ifVersion?: number): Promise; + openDB(options: { name: string; encoding: 'msgpack' | 'string' | 'json' | 'binary' }): Lmdb; + getRange(options?: { + start?: T; + end?: T; + reverse?: boolean; + limit?: number; + versions?: boolean; + }): Iterable<{ key: string; value: T }>; +} + +export class Cache { + private readonly codes: Lmdb; + private readonly atimes: Lmdb; + private readonly mtimes: Lmdb; + private readonly sourceMaps: Lmdb; + private readonly prefix: string; + + constructor(config: { prefix: string }) { + this.prefix = config.prefix; + + this.codes = LmdbStore.open({ + name: 'codes', + path: CACHE_DIR, + }); + + this.atimes = this.codes.openDB({ + name: 'atimes', + encoding: 'string', + }); + + this.mtimes = this.codes.openDB({ + name: 'mtimes', + encoding: 'string', + }); + + this.sourceMaps = this.codes.openDB({ + name: 'sourceMaps', + encoding: 'msgpack', + }); + + // after the process has been running for 30 minutes prune the + // keys which haven't been used in 30 days. We use `unref()` to + // make sure this timer doesn't hold other processes open + // unexpectedly + setTimeout(() => { + this.pruneOldKeys(); + }, 30 * MINUTE).unref(); + } + + getMtime(path: string) { + return this.mtimes.get(this.getKey(path)); + } + + getCode(path: string) { + const key = this.getKey(path); + + // when we use a file from the cache set the "atime" of that cache entry + // so that we know which cache items we use and which haven't been + // touched in a long time (currently 30 days) + this.atimes.put(key, GLOBAL_ATIME).catch(reportError); + + return this.codes.get(key); + } + + getSourceMap(path: string) { + return this.sourceMaps.get(this.getKey(path)); + } + + update(path: string, file: { mtime: string; code: string; map: any }) { + const key = this.getKey(path); + + Promise.all([ + this.atimes.put(key, GLOBAL_ATIME), + this.mtimes.put(key, file.mtime), + this.codes.put(key, file.code), + this.sourceMaps.put(key, file.map), + ]).catch(reportError); + } + + private getKey(path: string) { + return `${this.prefix}${path}`; + } + + private async pruneOldKeys() { + try { + const ATIME_LIMIT = Date.now() - 30 * DAY; + const BATCH_SIZE = 1000; + + const validKeys: string[] = []; + const invalidKeys: string[] = []; + + for (const { key, value } of this.atimes.getRange()) { + const atime = parseInt(value, 10); + if (atime < ATIME_LIMIT) { + invalidKeys.push(key); + } else { + validKeys.push(key); + } + + if (validKeys.length + invalidKeys.length >= BATCH_SIZE) { + const promises = new Set(); + + if (invalidKeys.length) { + for (const k of invalidKeys) { + // all these promises are the same currently, so Set() will + // optimise this to a single promise, but I wouldn't be shocked + // if a future version starts returning independent promises so + // this is just for some future-proofing + promises.add(this.atimes.remove(k)); + promises.add(this.mtimes.remove(k)); + promises.add(this.codes.remove(k)); + promises.add(this.sourceMaps.remove(k)); + } + } else { + // delay a smidge to allow other things to happen before the next batch of checks + promises.add(new Promise((resolve) => setTimeout(resolve, 1))); + } + + invalidKeys.length = 0; + validKeys.length = 0; + await Promise.all(Array.from(promises)); + } + } + } catch { + // ignore errors, the cache is totally disposable and will rebuild if there is some sort of corruption + } + } +} diff --git a/src/setup_node_env/babel_register/index.js b/packages/kbn-optimizer/src/node/index.ts similarity index 81% rename from src/setup_node_env/babel_register/index.js rename to packages/kbn-optimizer/src/node/index.ts index 1574be8937a29..64dd75ce573e6 100644 --- a/src/setup_node_env/babel_register/index.js +++ b/packages/kbn-optimizer/src/node/index.ts @@ -17,8 +17,4 @@ * under the License. */ -// register and polyfill need to happen in this -// order and in separate files. Checkout each file -// for a much more detailed explanation -require('./register'); -require('./polyfill'); +export * from './node_auto_tranpilation'; diff --git a/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts b/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts new file mode 100644 index 0000000000000..ff6ab1c68da53 --- /dev/null +++ b/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts @@ -0,0 +1,187 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * This module is based on @babel/register @ 9808d25, modified to use + * a more efficient caching implementation which writes to disk as + * the cache is built rather than keeping the whole cache in memory + * and then dumping it to disk when the process exits. + */ + +/** + * @notice + * MIT License + * + * Copyright (c) 2014-present Sebastian McKenzie and other contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import Fs from 'fs'; +import Path from 'path'; +import Crypto from 'crypto'; + +import * as babel from '@babel/core'; +import { addHook } from 'pirates'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import sourceMapSupport from 'source-map-support'; + +import { Cache } from './cache'; + +const cwd = process.cwd(); + +const IGNORE_PATTERNS = [ + /[\/\\]kbn-pm[\/\\]dist[\/\\]/, + + // ignore paths matching `/node_modules/{a}/{b}`, unless `a` + // is `x-pack` and `b` is not `node_modules` + /[\/\\]node_modules[\/\\](?!x-pack[\/\\](?!node_modules)([^\/\\]+))([^\/\\]+[\/\\][^\/\\]+)/, + + // ignore paths matching `/canvas/canvas_plugin/` + /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, + + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, +]; + +function getBabelOptions(path: string) { + return babel.loadOptions({ + cwd, + sourceRoot: Path.dirname(path) + Path.sep, + filename: path, + babelrc: false, + presets: [require.resolve('@kbn/babel-preset/node_preset')], + sourceMaps: 'both', + ast: false, + })!; +} + +/** + * @babel/register uses a JSON encoded copy of the config + babel.version + * as the cache key for files, so we do something similar but we don't need + * a unique cache key for every file as our config isn't different for + * different files (by design). Instead we determine a unique prefix and + * automatically prepend all paths with the prefix to create cache keys + */ +function determineCachePrefix() { + const json = JSON.stringify({ + babelVersion: babel.version, + // get a config for a fake js, ts, and tsx file to make sure we + // capture conditional config portions based on the file extension + js: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.js')), + ts: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.ts')), + tsx: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.tsx')), + }); + + const checksum = Crypto.createHash('sha256').update(json).digest('hex'); + return `${checksum}:`; +} + +function compile(cache: Cache, source: string, path: string) { + try { + const mtime = `${Fs.statSync(path).mtimeMs}`; + if (cache.getMtime(path) === mtime) { + const code = cache.getCode(path); + if (code) { + // code *should* always be defined, but if it isn't for some reason rebuild it + return code; + } + } + + const options = getBabelOptions(path); + const result = babel.transform(source, options); + + if (!result || !result.code || !result.map) { + throw new Error(`babel failed to transpile [${path}]`); + } + + cache.update(path, { + mtime, + map: result.map, + code: result.code, + }); + + return result.code; + } catch (error) { + throw error; + } +} + +let installed = false; + +export function registerNodeAutoTranspilation() { + if (installed) { + return; + } + installed = true; + + const cache = new Cache({ + prefix: determineCachePrefix(), + }); + + sourceMapSupport.install({ + handleUncaughtExceptions: false, + environment: 'node', + // @ts-expect-error bad source-map-support types + retrieveSourceMap(path: string) { + const map = cache.getSourceMap(path); + + if (map) { + return { + url: null, + map, + }; + } else { + return null; + } + }, + }); + + let compiling = false; + + addHook( + (code, path) => { + if (compiling) { + return code; + } + + if (IGNORE_PATTERNS.some((re) => re.test(path))) { + return code; + } + + try { + compiling = true; + return compile(cache, code, path); + } finally { + compiling = false; + } + }, + { + exts: ['.js', '.ts', '.tsx'], + ignoreNodeModules: false, + } + ); + + // require the polyfills after setting up the require hook so that @babel/preset-env + // will spot the import in the polyfill file and replace it with the necessary polyfills + // for the current node.js version + require('./polyfill'); +} diff --git a/src/setup_node_env/babel_register/polyfill.js b/packages/kbn-optimizer/src/node/polyfill.ts similarity index 61% rename from src/setup_node_env/babel_register/polyfill.js rename to packages/kbn-optimizer/src/node/polyfill.ts index b6928f6cb8caf..5d445ef89f71e 100644 --- a/src/setup_node_env/babel_register/polyfill.js +++ b/packages/kbn-optimizer/src/node/polyfill.ts @@ -17,13 +17,4 @@ * under the License. */ -// `@babel/preset-env` looks for and rewrites the following import -// statement into a list of import statements based on the polyfills -// necessary for our target environment (the current version of node) -// but since it does that during compilation, `import 'core-js/stable'` -// must be in a file that is loaded with `require()` AFTER `@babel/register` -// is configured. -// -// This is why we have this single statement in it's own file and require -// it from ./index.js -require('core-js/stable'); +import 'core-js/stable'; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index f4e9ee8249900..8ffd86b84bf76 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -10,7 +10,7 @@ "prettier": "prettier --write './src/**/*.ts'" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-object-rest-spread": "^7.11.0", "@babel/preset-env": "^7.11.0", diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 676985fa24740..639d4e17d0e71 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -30,7 +30,7 @@ "enzyme-adapter-react-16": "^1.9.1" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@elastic/eui": "29.0.0", "@kbn/babel-preset": "1.0.0", "@kbn/optimizer": "1.0.0", @@ -38,7 +38,7 @@ "brace": "0.11.1", "chalk": "^4.1.0", "chokidar": "^3.4.2", - "core-js": "^3.6.4", + "core-js": "^3.6.5", "css-loader": "^3.4.2", "expose-loader": "^0.7.5", "file-loader": "^4.2.0", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index e5f1a06e5bffa..d2a590d29947b 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -17,7 +17,7 @@ "abortcontroller-polyfill": "^1.4.0", "angular": "^1.8.0", "compression-webpack-plugin": "^4.0.0", - "core-js": "^3.6.4", + "core-js": "^3.6.5", "custom-event-polyfill": "^0.3.0", "jquery": "^3.5.0", "lodash": "^4.17.20", diff --git a/packages/kbn-utils/src/repo_root.ts b/packages/kbn-utils/src/repo_root.ts index b33b28d8d6e2f..ed3516a7304f9 100644 --- a/packages/kbn-utils/src/repo_root.ts +++ b/packages/kbn-utils/src/repo_root.ts @@ -22,38 +22,50 @@ import Fs from 'fs'; import loadJsonFile from 'load-json-file'; -const isKibanaDir = (dir: string) => { +const readKibanaPkgJson = (dir: string) => { try { const path = Path.resolve(dir, 'package.json'); const json = loadJsonFile.sync(path); if (json && typeof json === 'object' && 'name' in json && json.name === 'kibana') { - return true; + return json; } } catch (error) { if (error && error.code === 'ENOENT') { - return false; + return; } throw error; } }; -// search for the kibana directory, since this file is moved around it might -// not be where we think but should always be a relatively close parent -// of this directory -const startDir = Fs.realpathSync(__dirname); -const { root: rootDir } = Path.parse(startDir); -let cursor = startDir; -while (true) { - if (isKibanaDir(cursor)) { - break; - } +const findKibanaPackageJson = () => { + // search for the kibana directory, since this file is moved around it might + // not be where we think but should always be a relatively close parent + // of this directory + const startDir = Fs.realpathSync(__dirname); + const { root: rootDir } = Path.parse(startDir); + let cursor = startDir; + while (true) { + const kibanaPkgJson = readKibanaPkgJson(cursor); + if (kibanaPkgJson) { + return { + kibanaDir: cursor, + kibanaPkgJson: kibanaPkgJson as { + name: string; + branch: string; + }, + }; + } - const parent = Path.dirname(cursor); - if (parent === rootDir) { - throw new Error(`unable to find kibana directory from ${startDir}`); + const parent = Path.dirname(cursor); + if (parent === rootDir) { + throw new Error(`unable to find kibana directory from ${startDir}`); + } + cursor = parent; } - cursor = parent; -} +}; + +const { kibanaDir, kibanaPkgJson } = findKibanaPackageJson(); -export const REPO_ROOT = cursor; +export const REPO_ROOT = kibanaDir; +export const UPSTREAM_BRANCH = kibanaPkgJson.branch; diff --git a/scripts/precommit_hook.js b/scripts/precommit_hook.js index 7b9647cb7a911..7749fab6d371f 100644 --- a/scripts/precommit_hook.js +++ b/scripts/precommit_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/babel_register'); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('../src/dev/run_precommit_hook'); diff --git a/src/cli/dist.js b/src/cli/dist.js index e5480d2137624..2e26eaf52e836 100644 --- a/src/cli/dist.js +++ b/src/cli/dist.js @@ -19,5 +19,5 @@ require('../apm')(); require('../setup_node_env/no_transpilation'); -require('../setup_node_env/babel_register/polyfill'); +require('core-js/stable'); require('./cli'); diff --git a/src/dev/build/tasks/create_empty_dirs_and_files_task.ts b/src/dev/build/tasks/create_empty_dirs_and_files_task.ts index a72c6a4598338..48745572def78 100644 --- a/src/dev/build/tasks/create_empty_dirs_and_files_task.ts +++ b/src/dev/build/tasks/create_empty_dirs_and_files_task.ts @@ -17,7 +17,7 @@ * under the License. */ -import { mkdirp, write, Task } from '../lib'; +import { mkdirp, Task } from '../lib'; export const CreateEmptyDirsAndFiles: Task = { description: 'Creating some empty directories and files to prevent file-permission issues', @@ -26,7 +26,6 @@ export const CreateEmptyDirsAndFiles: Task = { await Promise.all([ mkdirp(build.resolvePath('plugins')), mkdirp(build.resolvePath('data/optimize')), - write(build.resolvePath('data/optimize/.babel_register_cache.json'), '{}'), ]); }, }; diff --git a/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana b/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana index ee019d348ed97..9c9f58ded350b 100644 --- a/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana +++ b/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana @@ -10,6 +10,4 @@ nice="" # The default behavior is to simply log a message "program stop failed; still running" KILL_ON_STOP_TIMEOUT=0 -BABEL_CACHE_PATH="/var/lib/kibana/optimize/.babel_register_cache.json" - KBN_PATH_CONF="/etc/kibana" diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index 72ec73ad810e6..8ec80ac295c73 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -24,15 +24,6 @@ export NODE_OPTIONS="$NODE_OPTIONS --max-old-space-size=4096" ### export FORCE_COLOR=1 -### -### The @babel/register cache collects the build output from each file in -### a map, in memory, and then when the process exits it writes that to the -### babel cache file as a JSON encoded object. Stringifying that object -### causes OOMs on CI regularly enough that we need to find another solution, -### and until we do we need to disable the cache -### -export BABEL_DISABLE_CACHE=true - ### ### check that we seem to be in a kibana project ### diff --git a/src/dev/jest/setup/babel_polyfill.js b/src/dev/jest/setup/babel_polyfill.js index 58325c1a67f94..085c8e2a25eef 100644 --- a/src/dev/jest/setup/babel_polyfill.js +++ b/src/dev/jest/setup/babel_polyfill.js @@ -20,4 +20,4 @@ // Note: In theory importing the polyfill should not be needed, as Babel should // include the necessary polyfills when using `@babel/preset-env`, but for some // reason it did not work. See https://github.com/elastic/kibana/issues/14506 -import '../../../setup_node_env/babel_register/polyfill'; +import '@kbn/optimizer/src/node/polyfill'; diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js deleted file mode 100644 index 3c0bd387c8e44..0000000000000 --- a/src/setup_node_env/babel_register/register.js +++ /dev/null @@ -1,63 +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. - */ - -var resolve = require('path').resolve; - -// this must happen before `require('@babel/register')` and can't be changed -// once the module has been loaded -if (!process.env.BABEL_CACHE_PATH) { - process.env.BABEL_CACHE_PATH = resolve( - __dirname, - '../../../data/optimize/.babel_register_cache.json' - ); -} - -// paths that @babel/register should ignore -var ignore = [ - /[\/\\]bower_components[\/\\]/, - /[\/\\]kbn-pm[\/\\]dist[\/\\]/, - - // TODO: remove this and just transpile plugins at build time, but - // has tricky edge cases that will probably require better eslint - // restrictions to make sure that code destined for the server/browser - // follows respects the limitations of each environment. - // - // https://github.com/elastic/kibana/issues/14800#issuecomment-366130268 - - // ignore paths matching `/node_modules/{a}/{b}`, unless `a` - // is `x-pack` and `b` is not `node_modules` - /[\/\\]node_modules[\/\\](?!x-pack[\/\\](?!node_modules)([^\/\\]+))([^\/\\]+[\/\\][^\/\\]+)/, - - // ignore paths matching `/canvas/canvas_plugin/` - /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, - - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, -]; - -// modifies all future calls to require() to automatically -// compile the required source with babel -require('@babel/register')({ - ignore, - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/node_preset')], - extensions: ['.js', '.ts', '.tsx'], -}); diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index 60f0982f50d20..55539c56a364e 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -18,4 +18,4 @@ */ require('./no_transpilation'); -require('./babel_register'); +require('@kbn/optimizer').registerNodeAutoTranspilation(); diff --git a/x-pack/package.json b/x-pack/package.json index 4145d8d72cc63..ffe1a08855888 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -271,8 +271,7 @@ "yargs": "^15.4.1" }, "dependencies": { - "@babel/core": "^7.11.1", - "@babel/register": "^7.10.5", + "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.10.0", @@ -334,7 +333,7 @@ "isbinaryfile": "4.0.2", "joi": "^13.5.2", "jquery": "^3.5.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.20", diff --git a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js b/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js index 287f267343b11..15414c8166520 100644 --- a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js +++ b/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js @@ -4,17 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - ], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); const { aggregateLatencyMetrics, diff --git a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js b/x-pack/plugins/apm/scripts/create-functional-tests-archive.js index 6b3473dc2ac0a..ce265acf39c8b 100644 --- a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js +++ b/x-pack/plugins/apm/scripts/create-functional-tests-archive.js @@ -6,13 +6,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.js', '.ts'], - plugins: ['@babel/plugin-proposal-optional-chaining'], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./create-functional-tests-archive/index.ts'); diff --git a/x-pack/plugins/apm/scripts/jest.js b/x-pack/plugins/apm/scripts/jest.js index 5c29dd9126937..3a73dbf6e3464 100644 --- a/x-pack/plugins/apm/scripts/jest.js +++ b/x-pack/plugins/apm/scripts/jest.js @@ -4,14 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.js'], - plugins: [], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); // eslint-disable-next-line import/no-extraneous-dependencies const { run } = require('jest'); diff --git a/x-pack/plugins/apm/scripts/setup-kibana-security.js b/x-pack/plugins/apm/scripts/setup-kibana-security.js index 414d7208a7864..86b8c30988ca5 100644 --- a/x-pack/plugins/apm/scripts/setup-kibana-security.js +++ b/x-pack/plugins/apm/scripts/setup-kibana-security.js @@ -17,13 +17,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: ['@babel/plugin-proposal-optional-chaining'], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./kibana-security/setup-custom-kibana-user-role.ts'); diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data.js b/x-pack/plugins/apm/scripts/upload-telemetry-data.js index 03cd5095ec02d..76bf9c5aebe64 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data.js +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data.js @@ -6,16 +6,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - ], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./upload-telemetry-data/index.ts'); diff --git a/yarn.lock b/yarn.lock index 806424b222ad3..c4af49d12c8cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,7 +63,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== @@ -85,6 +85,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.10.5", "@babel/generator@^7.11.0", "@babel/generator@^7.9.6": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" @@ -94,6 +116,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -325,6 +356,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9" integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA== +"@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -1105,6 +1141,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" @@ -1114,6 +1165,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -3734,7 +3794,7 @@ resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== -"@types/babel__core@^7.0.0": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.10": version "7.1.10" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== @@ -3745,17 +3805,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" - integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -5053,6 +5102,13 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== +"@types/source-map-support@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.5.3.tgz#acb6b3e499c20692552d16934c16162c84594e16" + integrity sha512-fvjMjVH8Rmokw2dWh1dkj90iX5R8FPjeZzjNH+6eFXReh0QnHFf1YBl3B0CF0RohIAA3SDRJsGeeUWKl6d7HqA== + dependencies: + source-map "^0.6.0" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -10021,7 +10077,7 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3, core-js@^2.6.5, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.1, core-js@^3.0.4, core-js@^3.6.4: +core-js@^3.0.1, core-js@^3.0.4: version "3.6.4" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== @@ -18700,6 +18756,14 @@ js-yaml@3.13.1, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1 argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -19496,6 +19560,17 @@ livereload-js@^2.3.0: resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== +lmdb-store@^0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.6.10.tgz#db8efde6e052aabd17ebc63c8a913e1f31694129" + integrity sha512-ZLvp3qbBQ5VlBmaWa4EUAPyYEZ8qdUHsW69HmxkDi84pFQ37WMxYhFaF/7PQkdtxS/vyiKkZigd9TFgHjek1Nw== + dependencies: + fs-extra "^9.0.1" + msgpackr "^0.5.0" + nan "^2.14.1" + node-gyp-build "^4.2.3" + weak-lru-cache "^0.2.0" + load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" @@ -21151,6 +21226,21 @@ ms@2.1.1, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +msgpackr-extract@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.4.tgz#8ee5e73d1135340e564c498e8c593134365eb060" + integrity sha512-d3+qwTJzgqqsq2L2sQuH0SoO4StvpUhMqMAKy6tMimn7XdBaRtDlquFzRJsp0iMGt2hnU4UOqD8Tz9mb0KglTA== + dependencies: + nan "^2.14.1" + node-gyp-build "^4.2.3" + +msgpackr@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.5.1.tgz#7eecbf342645b7718dd2e3386894368d06732b3f" + integrity sha512-nK2uJl67Q5KU3MWkYBUlYynqKS1UUzJ5M1h6TQejuJtJzD3hW2Suv2T1pf01E9lUEr93xaLokf/xC+jwBShMPQ== + optionalDependencies: + msgpackr-extract "^0.3.4" + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -21489,6 +21579,11 @@ node-forge@^0.10.0, node-forge@^0.7.6: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-gyp-build@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + node-gyp@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" @@ -30798,6 +30893,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +weak-lru-cache@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-0.2.0.tgz#447379ccff6dfda1b7a9566c9ef168260be859d1" + integrity sha512-M1l5CzKvM7maa7tCbtL0NW6sOnp8gqup853+9Aq7GL0XNWKNnFOkeE3v3Z5X2IeMzedPwQyPbi4RlFvD6rxs7A== + web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"