From 1c690c68af6ea05248ca04b259fb1f01970a044c Mon Sep 17 00:00:00 2001
From: Justin Kambic
Date: Mon, 27 Jul 2020 15:39:52 -0400
Subject: [PATCH 001/102] [Uptime] Increase timeout in attempt to fix skipped
a11y test (#73078)
* Increase timeout in attempt to fix skipped a11y test.
* Temporarily only run uptime tests for faster flaky testing.
* Uncomment other test suites.
* Unskip test and delete comment.
Co-authored-by: Elastic Machine
---
x-pack/test/accessibility/apps/uptime.ts | 3 +--
x-pack/test/functional/services/uptime/navigation.ts | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/x-pack/test/accessibility/apps/uptime.ts b/x-pack/test/accessibility/apps/uptime.ts
index e6ef1cfe8cfe2e..ebd120fa0feea4 100644
--- a/x-pack/test/accessibility/apps/uptime.ts
+++ b/x-pack/test/accessibility/apps/uptime.ts
@@ -17,8 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const es = getService('es');
- // FLAKY: https://github.com/elastic/kibana/issues/72994
- describe.skip('uptime', () => {
+ describe('uptime', () => {
before(async () => {
await esArchiver.load('uptime/blank');
await makeChecks(es, A11Y_TEST_MONITOR_ID, 150, 1, 1000, {
diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts
index f8e0c0cff41f4d..ab511abf130a54 100644
--- a/x-pack/test/functional/services/uptime/navigation.ts
+++ b/x-pack/test/functional/services/uptime/navigation.ts
@@ -41,7 +41,7 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv
goToSettings: async () => {
await goToUptimeRoot();
await testSubjects.click('settings-page-link', 5000);
- await testSubjects.existOrFail('uptimeSettingsPage', { timeout: 2000 });
+ await testSubjects.existOrFail('uptimeSettingsPage', { timeout: 10000 });
},
checkIfOnMonitorPage: async (monitorId: string) => {
From 2ae470e897976abb939c31708bff41fd0d0dcd07 Mon Sep 17 00:00:00 2001
From: Scotty Bollinger
Date: Mon, 27 Jul 2020 16:04:10 -0500
Subject: [PATCH 002/102] Add Kea.js support to Enterprise Search plugin
(#72160)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add Kea packages
- kea and kea-waitfor
* Add Kea declarations and types
Hopefully TypeScript support coming soon from author
* Add Kea to entry point
* Add logic for overview
* Update components to use Kea
* Fix a couple of tests that weren’t getting complete coverage
* Remove kea-waitfor
Turns out we don’t need it
* Remove unused declaration
* Update x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts
Co-authored-by: Constance
* Update x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts
Co-authored-by: Constance
* Update x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts
Co-authored-by: Constance
* Update x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts
Co-authored-by: Constance
* [Opinionated] Remove extra actions defs
- they're already being defined in IOverviewActions, so no need to repeat them
* DRY out a new reusable/generics IKeaLogic/Listeners interface
- Multiple logic files can now do IKeaListeners and not have to declare their own IListenerParams!
+ bonus IKeaSelectors just for consistency
* DRY out Kea reducers definitions to generics interface
* [Refactor] Improve KeaReducers generic to actually type-check/check key names
- Typescript will now throw an error if you put in a key name that isn't declared in your actions/values interface
- default & new states now will be type checked!! :tada:
* [Refactor] Update selectors() and listeners() to also check types and keys
* [Refactor] Move param defs to bottom of file instead of inline
- so that inline definitions mostly focus on type checks, and more boilerplate defs are DRYed out
- I played around with 2.1 obj definitions and got terrible results here :(
* Update tests and remove selectors per code review
* Remove last statsColumns instance
* Remove last instance of hideOnboarding
Co-authored-by: Constance
Co-authored-by: Constance Chen
Co-authored-by: Elastic Machine
---
x-pack/package.json | 3 +-
.../public/applications/kea.d.ts | 13 ++
.../public/applications/shared/types.ts | 56 ++++++
.../components/overview/__mocks__/index.ts | 7 +
.../overview/__mocks__/overview_logic.mock.ts | 47 +++++
.../overview/onboarding_steps.test.tsx | 77 ++++----
.../components/overview/onboarding_steps.tsx | 27 +--
.../overview/organization_stats.test.tsx | 8 +-
.../overview/organization_stats.tsx | 115 ++++++------
.../components/overview/overview.test.tsx | 45 +++--
.../components/overview/overview.tsx | 97 ++--------
.../overview/overview_logic.test.ts | 141 +++++++++++++++
.../components/overview/overview_logic.ts | 168 ++++++++++++++++++
.../overview/recent_activity.test.tsx | 21 ++-
.../components/overview/recent_activity.tsx | 13 +-
.../content_section/content_section.test.tsx | 3 +-
.../view_content_header.test.tsx | 3 +-
.../applications/workplace_search/index.tsx | 11 +-
.../applications/workplace_search/types.ts | 11 ++
yarn.lock | 5 +
20 files changed, 634 insertions(+), 237 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/kea.d.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/index.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts
diff --git a/x-pack/package.json b/x-pack/package.json
index d1f638ccad8d01..dee99d6f0ddac0 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -282,6 +282,7 @@
"json-stable-stringify": "^1.0.1",
"jsonwebtoken": "^8.5.1",
"jsts": "^1.6.2",
+ "kea": "^2.0.1",
"lodash": "^4.17.15",
"lz-string": "^1.4.4",
"mapbox-gl": "^1.10.0",
@@ -384,4 +385,4 @@
"cypress-multi-reporters"
]
}
-}
\ No newline at end of file
+}
diff --git a/x-pack/plugins/enterprise_search/public/applications/kea.d.ts b/x-pack/plugins/enterprise_search/public/applications/kea.d.ts
new file mode 100644
index 00000000000000..961d93ccc12e68
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/kea.d.ts
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ */
+
+declare module 'kea' {
+ export function useValues(logic?: object): object;
+ export function useActions(logic?: object): object;
+ export function getContext(): { store: object };
+ export function resetContext(context: object): object;
+ export function kea(logic: object): object;
+}
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts
index 3f28710d922959..74bb53ef3a9544 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts
@@ -12,3 +12,59 @@ export interface IFlashMessagesProps {
isWrapped?: boolean;
children?: React.ReactNode;
}
+
+export interface IKeaLogic {
+ mount(): void;
+ values: IKeaValues;
+ actions: IKeaActions;
+}
+
+/**
+ * This reusable interface mostly saves us a few characters / allows us to skip
+ * defining params inline. Unfortunately, the return values *do not work* as
+ * expected (hence the voids). While I can tell selectors to use TKeaSelectors,
+ * the return value is *not* properly type checked if it's not declared inline. :/
+ *
+ * Also note that if you switch to Kea 2.1's plain object notation -
+ * `selectors: {}` vs. `selectors: () => ({})`
+ * - type checking also stops working and type errors become significantly less
+ * helpful - showing less specific error messages and highlighting. 👎
+ */
+export interface IKeaParams {
+ selectors?(params: { selectors: IKeaValues }): void;
+ listeners?(params: { actions: IKeaActions; values: IKeaValues }): void;
+}
+
+/**
+ * This reducers() type checks that:
+ *
+ * 1. The value object keys are defined within IKeaValues
+ * 2. The default state (array[0]) matches the type definition within IKeaValues
+ * 3. The action object keys (array[1]) are defined within IKeaActions
+ * 3. The new state returned by the action matches the type definition within IKeaValues
+ */
+export type TKeaReducers = {
+ [Value in keyof IKeaValues]?: [
+ IKeaValues[Value],
+ {
+ [Action in keyof IKeaActions]?: (state: IKeaValues, payload: IKeaValues) => IKeaValues[Value];
+ }
+ ];
+};
+
+/**
+ * This selectors() type checks that:
+ *
+ * 1. The object keys are defined within IKeaValues
+ * 2. The selected values are defined within IKeaValues
+ * 3. The returned value match the type definition within IKeaValues
+ *
+ * The unknown[] and any[] are unfortunately because I have no idea how to
+ * assert for arbitrary type/values as an array
+ */
+export type TKeaSelectors = {
+ [Value in keyof IKeaValues]?: [
+ (selectors: IKeaValues) => unknown[],
+ (...args: any[]) => IKeaValues[Value] // eslint-disable-line @typescript-eslint/no-explicit-any
+ ];
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/index.ts
new file mode 100644
index 00000000000000..e5169a51ce5227
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { setMockValues, mockLogicValues, mockLogicActions } from './overview_logic.mock';
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts
new file mode 100644
index 00000000000000..43cff5de6668da
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts
@@ -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 { IOverviewValues } from '../overview_logic';
+import { IAccount, IOrganization, IUser } from '../../../types';
+
+export const mockLogicValues = {
+ accountsCount: 0,
+ activityFeed: [],
+ canCreateContentSources: false,
+ canCreateInvitations: false,
+ currentUser: {} as IUser,
+ fpAccount: {} as IAccount,
+ hasOrgSources: false,
+ hasUsers: false,
+ isFederatedAuth: true,
+ isOldAccount: false,
+ organization: {} as IOrganization,
+ pendingInvitationsCount: 0,
+ personalSourcesCount: 0,
+ sourcesCount: 0,
+ dataLoading: true,
+ hasErrorConnecting: false,
+ flashMessages: {},
+} as IOverviewValues;
+
+export const mockLogicActions = {
+ initializeOverview: jest.fn(() => ({})),
+};
+
+jest.mock('kea', () => ({
+ ...(jest.requireActual('kea') as object),
+ useActions: jest.fn(() => ({ ...mockLogicActions })),
+ useValues: jest.fn(() => ({ ...mockLogicValues })),
+}));
+
+import { useValues } from 'kea';
+
+export const setMockValues = (values: object) => {
+ (useValues as jest.Mock).mockImplementationOnce(() => ({
+ ...mockLogicValues,
+ ...values,
+ }));
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.test.tsx
index 6174dc1c795eb1..3cf88cf120cc4c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.test.tsx
@@ -5,6 +5,8 @@
*/
import '../../../__mocks__/shallow_usecontext.mock';
+import './__mocks__/overview_logic.mock';
+import { setMockValues } from './__mocks__';
import React from 'react';
import { shallow } from 'enzyme';
@@ -16,7 +18,6 @@ import { sendTelemetry } from '../../../shared/telemetry';
import { OnboardingSteps, OrgNameOnboarding } from './onboarding_steps';
import { OnboardingCard } from './onboarding_card';
-import { defaultServerData } from './overview';
const account = {
id: '1',
@@ -30,7 +31,8 @@ const account = {
describe('OnboardingSteps', () => {
describe('Shared Sources', () => {
it('renders 0 sources state', () => {
- const wrapper = shallow();
+ setMockValues({ canCreateContentSources: true });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard)).toHaveLength(1);
expect(wrapper.find(OnboardingCard).prop('actionPath')).toBe(ORG_SOURCES_PATH);
@@ -40,9 +42,8 @@ describe('OnboardingSteps', () => {
});
it('renders completed sources state', () => {
- const wrapper = shallow(
-
- );
+ setMockValues({ sourcesCount: 2, hasOrgSources: true });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard).prop('description')).toEqual(
'You have added 2 shared sources. Happy searching.'
@@ -50,9 +51,8 @@ describe('OnboardingSteps', () => {
});
it('disables link when the user cannot create sources', () => {
- const wrapper = shallow(
-
- );
+ setMockValues({ canCreateContentSources: false });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard).prop('actionPath')).toBe(undefined);
});
@@ -60,15 +60,14 @@ describe('OnboardingSteps', () => {
describe('Users & Invitations', () => {
it('renders 0 users when not on federated auth', () => {
- const wrapper = shallow(
-
- );
+ setMockValues({
+ canCreateInvitations: true,
+ isFederatedAuth: false,
+ fpAccount: account,
+ accountsCount: 0,
+ hasUsers: false,
+ });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard)).toHaveLength(2);
expect(wrapper.find(OnboardingCard).last().prop('actionPath')).toBe(USERS_PATH);
@@ -78,15 +77,13 @@ describe('OnboardingSteps', () => {
});
it('renders completed users state', () => {
- const wrapper = shallow(
-
- );
+ setMockValues({
+ isFederatedAuth: false,
+ fpAccount: account,
+ accountsCount: 1,
+ hasUsers: true,
+ });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard).last().prop('description')).toEqual(
'Nice, you’ve invited colleagues to search with you.'
@@ -94,21 +91,15 @@ describe('OnboardingSteps', () => {
});
it('disables link when the user cannot create invitations', () => {
- const wrapper = shallow(
-
- );
-
+ setMockValues({ isFederatedAuth: false, canCreateInvitations: false });
+ const wrapper = shallow();
expect(wrapper.find(OnboardingCard).last().prop('actionPath')).toBe(undefined);
});
});
describe('Org Name', () => {
it('renders button to change name', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
const button = wrapper
.find(OrgNameOnboarding)
@@ -120,15 +111,13 @@ describe('OnboardingSteps', () => {
});
it('hides card when name has been changed', () => {
- const wrapper = shallow(
-
- );
+ setMockValues({
+ organization: {
+ name: 'foo',
+ defaultOrgName: 'bar',
+ },
+ });
+ const wrapper = shallow();
expect(wrapper.find(OrgNameOnboarding)).toHaveLength(0);
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.tsx
index 1b003474373382..7fe1eae5023298 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/onboarding_steps.tsx
@@ -7,6 +7,7 @@
import React, { useContext } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { useValues } from 'kea';
import {
EuiSpacer,
@@ -28,7 +29,7 @@ import { ORG_SOURCES_PATH, USERS_PATH, ORG_SETTINGS_PATH } from '../../routes';
import { ContentSection } from '../shared/content_section';
-import { IAppServerData } from './overview';
+import { OverviewLogic, IOverviewValues } from './overview_logic';
import { OnboardingCard } from './onboarding_card';
@@ -57,17 +58,19 @@ const ONBOARDING_USERS_CARD_DESCRIPTION = i18n.translate(
{ defaultMessage: 'Invite your colleagues into this organization to search with you.' }
);
-export const OnboardingSteps: React.FC = ({
- hasUsers,
- hasOrgSources,
- canCreateContentSources,
- canCreateInvitations,
- accountsCount,
- sourcesCount,
- fpAccount: { isCurated },
- organization: { name, defaultOrgName },
- isFederatedAuth,
-}) => {
+export const OnboardingSteps: React.FC = () => {
+ const {
+ hasUsers,
+ hasOrgSources,
+ canCreateContentSources,
+ canCreateInvitations,
+ accountsCount,
+ sourcesCount,
+ fpAccount: { isCurated },
+ organization: { name, defaultOrgName },
+ isFederatedAuth,
+ } = useValues(OverviewLogic) as IOverviewValues;
+
const accountsPath =
!isFederatedAuth && (canCreateInvitations || isCurated) ? USERS_PATH : undefined;
const sourcesPath = canCreateContentSources || isCurated ? ORG_SOURCES_PATH : undefined;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.test.tsx
index 112e9a910667ae..d9b05c5da777df 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.test.tsx
@@ -5,6 +5,8 @@
*/
import '../../../__mocks__/shallow_usecontext.mock';
+import './__mocks__/overview_logic.mock';
+import { setMockValues } from './__mocks__';
import React from 'react';
import { shallow } from 'enzyme';
@@ -12,18 +14,18 @@ import { EuiFlexGrid } from '@elastic/eui';
import { OrganizationStats } from './organization_stats';
import { StatisticCard } from './statistic_card';
-import { defaultServerData } from './overview';
describe('OrganizationStats', () => {
it('renders', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
expect(wrapper.find(StatisticCard)).toHaveLength(2);
expect(wrapper.find(EuiFlexGrid).prop('columns')).toEqual(2);
});
it('renders additional cards for federated auth', () => {
- const wrapper = shallow();
+ setMockValues({ isFederatedAuth: false });
+ const wrapper = shallow();
expect(wrapper.find(StatisticCard)).toHaveLength(4);
expect(wrapper.find(EuiFlexGrid).prop('columns')).toEqual(4);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.tsx
index aa9be81f32baed..4c5efce9baf129 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/organization_stats.tsx
@@ -6,6 +6,7 @@
import React from 'react';
import { EuiFlexGrid } from '@elastic/eui';
+import { useValues } from 'kea';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@@ -13,62 +14,66 @@ import { i18n } from '@kbn/i18n';
import { ContentSection } from '../shared/content_section';
import { ORG_SOURCES_PATH, USERS_PATH } from '../../routes';
-import { IAppServerData } from './overview';
+import { OverviewLogic, IOverviewValues } from './overview_logic';
import { StatisticCard } from './statistic_card';
-export const OrganizationStats: React.FC = ({
- sourcesCount,
- pendingInvitationsCount,
- accountsCount,
- personalSourcesCount,
- isFederatedAuth,
-}) => (
-
- }
- headerSpacer="m"
- >
-
-
- {!isFederatedAuth && (
- <>
-
-
- >
- )}
- {
+ const {
+ sourcesCount,
+ pendingInvitationsCount,
+ accountsCount,
+ personalSourcesCount,
+ isFederatedAuth,
+ } = useValues(OverviewLogic) as IOverviewValues;
+
+ return (
+
+ }
+ headerSpacer="m"
+ >
+
+
+ {!isFederatedAuth && (
+ <>
+
+
+ >
)}
- count={personalSourcesCount}
- />
-
-
-);
+
+
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx
index e5e5235c523686..744fd8aeb19516 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx
@@ -5,11 +5,11 @@
*/
import '../../../__mocks__/react_router_history.mock';
+import './__mocks__/overview_logic.mock';
+import { mockLogicActions, setMockValues } from './__mocks__';
import React from 'react';
-import { shallow } from 'enzyme';
-
-import { mountWithAsyncContext, mockKibanaContext } from '../../../__mocks__';
+import { shallow, mount } from 'enzyme';
import { ErrorState } from '../error_state';
import { Loading } from '../shared/loading';
@@ -18,11 +18,9 @@ import { ViewContentHeader } from '../shared/view_content_header';
import { OnboardingSteps } from './onboarding_steps';
import { OrganizationStats } from './organization_stats';
import { RecentActivity } from './recent_activity';
-import { Overview, defaultServerData } from './overview';
+import { Overview } from './overview';
describe('Overview', () => {
- const mockHttp = mockKibanaContext.http;
-
describe('non-happy-path states', () => {
it('isLoading', () => {
const wrapper = shallow();
@@ -30,24 +28,24 @@ describe('Overview', () => {
expect(wrapper.find(Loading)).toHaveLength(1);
});
- it('hasErrorConnecting', async () => {
- const wrapper = await mountWithAsyncContext(, {
- http: {
- ...mockHttp,
- get: () => Promise.reject({ invalidPayload: true }),
- },
- });
+ it('hasErrorConnecting', () => {
+ setMockValues({ hasErrorConnecting: true });
+ const wrapper = shallow();
expect(wrapper.find(ErrorState)).toHaveLength(1);
});
});
describe('happy-path states', () => {
- it('renders onboarding state', async () => {
- const mockApi = jest.fn(() => defaultServerData);
- const wrapper = await mountWithAsyncContext(, {
- http: { ...mockHttp, get: mockApi },
- });
+ it('calls initialize function', async () => {
+ mount();
+
+ expect(mockLogicActions.initializeOverview).toHaveBeenCalled();
+ });
+
+ it('renders onboarding state', () => {
+ setMockValues({ dataLoading: false });
+ const wrapper = shallow();
expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
expect(wrapper.find(OnboardingSteps)).toHaveLength(1);
@@ -55,9 +53,9 @@ describe('Overview', () => {
expect(wrapper.find(RecentActivity)).toHaveLength(1);
});
- it('renders when onboarding complete', async () => {
- const obCompleteData = {
- ...defaultServerData,
+ it('renders when onboarding complete', () => {
+ setMockValues({
+ dataLoading: false,
hasUsers: true,
hasOrgSources: true,
isOldAccount: true,
@@ -65,11 +63,8 @@ describe('Overview', () => {
name: 'foo',
defaultOrgName: 'bar',
},
- };
- const mockApi = jest.fn(() => obCompleteData);
- const wrapper = await mountWithAsyncContext(, {
- http: { ...mockHttp, get: mockApi },
});
+ const wrapper = shallow();
expect(wrapper.find(OnboardingSteps)).toHaveLength(0);
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx
index bacd65a2be75f4..b75a2841dad9b0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx
@@ -4,15 +4,16 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useContext, useEffect, useState } from 'react';
+import React, { useContext, useEffect } from 'react';
import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import { useActions, useValues } from 'kea';
import { SetWorkplaceSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs';
import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { KibanaContext, IKibanaContext } from '../../../index';
-import { IAccount } from '../../types';
+import { OverviewLogic, IOverviewActions, IOverviewValues } from './overview_logic';
import { ErrorState } from '../error_state';
@@ -22,57 +23,7 @@ import { ViewContentHeader } from '../shared/view_content_header';
import { OnboardingSteps } from './onboarding_steps';
import { OrganizationStats } from './organization_stats';
-import { RecentActivity, IFeedActivity } from './recent_activity';
-
-export interface IAppServerData {
- hasUsers: boolean;
- hasOrgSources: boolean;
- canCreateContentSources: boolean;
- canCreateInvitations: boolean;
- isOldAccount: boolean;
- sourcesCount: number;
- pendingInvitationsCount: number;
- accountsCount: number;
- personalSourcesCount: number;
- activityFeed: IFeedActivity[];
- organization: {
- name: string;
- defaultOrgName: string;
- };
- isFederatedAuth: boolean;
- currentUser: {
- firstName: string;
- email: string;
- name: string;
- color: string;
- };
- fpAccount: IAccount;
-}
-
-export const defaultServerData = {
- accountsCount: 1,
- activityFeed: [],
- canCreateContentSources: true,
- canCreateInvitations: true,
- currentUser: {
- firstName: '',
- email: '',
- name: '',
- color: '',
- },
- fpAccount: {} as IAccount,
- hasOrgSources: false,
- hasUsers: false,
- isFederatedAuth: true,
- isOldAccount: false,
- organization: {
- name: '',
- defaultOrgName: '',
- },
- pendingInvitationsCount: 0,
- personalSourcesCount: 0,
- sourcesCount: 0,
-} as IAppServerData;
+import { RecentActivity } from './recent_activity';
const ONBOARDING_HEADER_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.overviewOnboardingHeader.title',
@@ -96,34 +47,24 @@ const HEADER_DESCRIPTION = i18n.translate(
export const Overview: React.FC = () => {
const { http } = useContext(KibanaContext) as IKibanaContext;
- const [isLoading, setIsLoading] = useState(true);
- const [hasErrorConnecting, setHasErrorConnecting] = useState(false);
- const [appData, setAppData] = useState(defaultServerData);
-
- const getAppData = async () => {
- try {
- const response = await http.get('/api/workplace_search/overview');
- setAppData(response);
- } catch (error) {
- setHasErrorConnecting(true);
- } finally {
- setIsLoading(false);
- }
- };
-
- useEffect(() => {
- getAppData();
- }, []);
-
- if (hasErrorConnecting) return ;
- if (isLoading) return ;
+ const { initializeOverview } = useActions(OverviewLogic) as IOverviewActions;
const {
+ dataLoading,
+ hasErrorConnecting,
hasUsers,
hasOrgSources,
isOldAccount,
organization: { name: orgName, defaultOrgName },
- } = appData as IAppServerData;
+ } = useValues(OverviewLogic) as IOverviewValues;
+
+ useEffect(() => {
+ initializeOverview({ http });
+ }, [initializeOverview]);
+
+ if (hasErrorConnecting) return ;
+ if (dataLoading) return ;
+
const hideOnboarding = hasUsers && hasOrgSources && isOldAccount && orgName !== defaultOrgName;
const headerTitle = hideOnboarding ? HEADER_TITLE : ONBOARDING_HEADER_TITLE;
@@ -140,11 +81,11 @@ export const Overview: React.FC = () => {
description={headerDescription}
action={}
/>
- {!hideOnboarding && }
+ {!hideOnboarding && }
-
+
-
+
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts
new file mode 100644
index 00000000000000..285ec9b9733785
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts
@@ -0,0 +1,141 @@
+/*
+ * 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 { resetContext } from 'kea';
+import { act } from 'react-dom/test-utils';
+
+import { mockKibanaContext } from '../../../__mocks__';
+
+import { mockLogicValues } from './__mocks__';
+import { OverviewLogic } from './overview_logic';
+
+describe('OverviewLogic', () => {
+ let unmount: any;
+
+ beforeEach(() => {
+ resetContext({});
+ unmount = OverviewLogic.mount() as any;
+ jest.clearAllMocks();
+ });
+
+ afterEach(() => {
+ unmount();
+ });
+
+ it('has expected default values', () => {
+ expect(OverviewLogic.values).toEqual(mockLogicValues);
+ });
+
+ describe('setServerData', () => {
+ const feed = [{ foo: 'bar' }] as any;
+ const user = { firstName: 'Joe', email: 'e@e.e', name: 'Joe Jo', color: 'pearl' };
+ const account = {
+ name: 'Jane doe',
+ id: '1243',
+ isAdmin: true,
+ canCreatePersonalSources: true,
+ groups: [],
+ supportEligible: true,
+ };
+ const org = { name: 'ACME', defaultOrgName: 'Org' };
+
+ const data = {
+ accountsCount: 1,
+ activityFeed: feed,
+ canCreateContentSources: true,
+ canCreateInvitations: true,
+ currentUser: user,
+ fpAccount: account,
+ hasOrgSources: true,
+ hasUsers: true,
+ isFederatedAuth: false,
+ isOldAccount: true,
+ organization: org,
+ pendingInvitationsCount: 1,
+ personalSourcesCount: 1,
+ sourcesCount: 1,
+ };
+
+ beforeEach(() => {
+ OverviewLogic.actions.setServerData(data);
+ });
+
+ it('will set `dataLoading` to false', () => {
+ expect(OverviewLogic.values.dataLoading).toEqual(false);
+ });
+
+ it('will set server values', () => {
+ expect(OverviewLogic.values.organization).toEqual(org);
+ expect(OverviewLogic.values.isFederatedAuth).toEqual(false);
+ expect(OverviewLogic.values.currentUser).toEqual(user);
+ expect(OverviewLogic.values.fpAccount).toEqual(account);
+ expect(OverviewLogic.values.canCreateInvitations).toEqual(true);
+ expect(OverviewLogic.values.hasUsers).toEqual(true);
+ expect(OverviewLogic.values.hasOrgSources).toEqual(true);
+ expect(OverviewLogic.values.canCreateContentSources).toEqual(true);
+ expect(OverviewLogic.values.isOldAccount).toEqual(true);
+ expect(OverviewLogic.values.sourcesCount).toEqual(1);
+ expect(OverviewLogic.values.pendingInvitationsCount).toEqual(1);
+ expect(OverviewLogic.values.accountsCount).toEqual(1);
+ expect(OverviewLogic.values.personalSourcesCount).toEqual(1);
+ expect(OverviewLogic.values.activityFeed).toEqual(feed);
+ });
+ });
+
+ describe('setFlashMessages', () => {
+ it('will set `flashMessages`', () => {
+ const flashMessages = { error: ['error'] };
+ OverviewLogic.actions.setFlashMessages(flashMessages);
+
+ expect(OverviewLogic.values.flashMessages).toEqual(flashMessages);
+ });
+ });
+
+ describe('setHasErrorConnecting', () => {
+ it('will set `hasErrorConnecting`', () => {
+ OverviewLogic.actions.setHasErrorConnecting(true);
+
+ expect(OverviewLogic.values.hasErrorConnecting).toEqual(true);
+ expect(OverviewLogic.values.dataLoading).toEqual(false);
+ });
+ });
+
+ describe('initializeOverview', () => {
+ it('calls API and sets values', async () => {
+ const mockHttp = mockKibanaContext.http;
+ const mockApi = jest.fn(() => mockLogicValues as any);
+ const setServerDataSpy = jest.spyOn(OverviewLogic.actions, 'setServerData');
+
+ await act(async () =>
+ OverviewLogic.actions.initializeOverview({
+ http: {
+ ...mockHttp,
+ get: mockApi,
+ },
+ })
+ );
+
+ expect(mockApi).toHaveBeenCalledWith('/api/workplace_search/overview');
+ expect(setServerDataSpy).toHaveBeenCalled();
+ });
+
+ it('handles error state', async () => {
+ const mockHttp = mockKibanaContext.http;
+ const setHasErrorConnectingSpy = jest.spyOn(OverviewLogic.actions, 'setHasErrorConnecting');
+
+ await act(async () =>
+ OverviewLogic.actions.initializeOverview({
+ http: {
+ ...mockHttp,
+ get: () => Promise.reject(),
+ },
+ })
+ );
+
+ expect(setHasErrorConnectingSpy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts
new file mode 100644
index 00000000000000..f1b4f447f74453
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts
@@ -0,0 +1,168 @@
+/*
+ * 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 { HttpSetup } from 'src/core/public';
+
+import { kea } from 'kea';
+
+import { IAccount, IOrganization, IUser } from '../../types';
+import { IFlashMessagesProps, IKeaLogic, TKeaReducers, IKeaParams } from '../../../shared/types';
+
+import { IFeedActivity } from './recent_activity';
+
+export interface IOverviewServerData {
+ hasUsers: boolean;
+ hasOrgSources: boolean;
+ canCreateContentSources: boolean;
+ canCreateInvitations: boolean;
+ isOldAccount: boolean;
+ sourcesCount: number;
+ pendingInvitationsCount: number;
+ accountsCount: number;
+ personalSourcesCount: number;
+ activityFeed: IFeedActivity[];
+ organization: IOrganization;
+ isFederatedAuth: boolean;
+ currentUser: IUser;
+ fpAccount: IAccount;
+}
+
+export interface IOverviewActions {
+ setServerData(serverData: IOverviewServerData): void;
+ setFlashMessages(flashMessages: IFlashMessagesProps): void;
+ setHasErrorConnecting(hasErrorConnecting: boolean): void;
+ initializeOverview({ http }: { http: HttpSetup }): void;
+}
+
+export interface IOverviewValues extends IOverviewServerData {
+ dataLoading: boolean;
+ hasErrorConnecting: boolean;
+ flashMessages: IFlashMessagesProps;
+}
+
+export const OverviewLogic = kea({
+ actions: (): IOverviewActions => ({
+ setServerData: (serverData) => serverData,
+ setFlashMessages: (flashMessages) => ({ flashMessages }),
+ setHasErrorConnecting: (hasErrorConnecting) => ({ hasErrorConnecting }),
+ initializeOverview: ({ http }) => ({ http }),
+ }),
+ reducers: (): TKeaReducers => ({
+ organization: [
+ {} as IOrganization,
+ {
+ setServerData: (_, { organization }) => organization,
+ },
+ ],
+ isFederatedAuth: [
+ true,
+ {
+ setServerData: (_, { isFederatedAuth }) => isFederatedAuth,
+ },
+ ],
+ currentUser: [
+ {} as IUser,
+ {
+ setServerData: (_, { currentUser }) => currentUser,
+ },
+ ],
+ fpAccount: [
+ {} as IAccount,
+ {
+ setServerData: (_, { fpAccount }) => fpAccount,
+ },
+ ],
+ canCreateInvitations: [
+ false,
+ {
+ setServerData: (_, { canCreateInvitations }) => canCreateInvitations,
+ },
+ ],
+ flashMessages: [
+ {},
+ {
+ setFlashMessages: (_, { flashMessages }) => flashMessages,
+ },
+ ],
+ hasUsers: [
+ false,
+ {
+ setServerData: (_, { hasUsers }) => hasUsers,
+ },
+ ],
+ hasOrgSources: [
+ false,
+ {
+ setServerData: (_, { hasOrgSources }) => hasOrgSources,
+ },
+ ],
+ canCreateContentSources: [
+ false,
+ {
+ setServerData: (_, { canCreateContentSources }) => canCreateContentSources,
+ },
+ ],
+ isOldAccount: [
+ false,
+ {
+ setServerData: (_, { isOldAccount }) => isOldAccount,
+ },
+ ],
+ sourcesCount: [
+ 0,
+ {
+ setServerData: (_, { sourcesCount }) => sourcesCount,
+ },
+ ],
+ pendingInvitationsCount: [
+ 0,
+ {
+ setServerData: (_, { pendingInvitationsCount }) => pendingInvitationsCount,
+ },
+ ],
+ accountsCount: [
+ 0,
+ {
+ setServerData: (_, { accountsCount }) => accountsCount,
+ },
+ ],
+ personalSourcesCount: [
+ 0,
+ {
+ setServerData: (_, { personalSourcesCount }) => personalSourcesCount,
+ },
+ ],
+ activityFeed: [
+ [],
+ {
+ setServerData: (_, { activityFeed }) => activityFeed,
+ },
+ ],
+ dataLoading: [
+ true,
+ {
+ setServerData: () => false,
+ setHasErrorConnecting: () => false,
+ },
+ ],
+ hasErrorConnecting: [
+ false,
+ {
+ setHasErrorConnecting: (_, { hasErrorConnecting }) => hasErrorConnecting,
+ },
+ ],
+ }),
+ listeners: ({ actions }): Partial => ({
+ initializeOverview: async ({ http }: { http: HttpSetup }) => {
+ try {
+ const response = await http.get('/api/workplace_search/overview');
+ actions.setServerData(response);
+ } catch (error) {
+ actions.setHasErrorConnecting(true);
+ }
+ },
+ }),
+} as IKeaParams) as IKeaLogic;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.test.tsx
index e9bdedb199dada..22a82af18527d6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.test.tsx
@@ -5,6 +5,8 @@
*/
import '../../../__mocks__/shallow_usecontext.mock';
+import './__mocks__/overview_logic.mock';
+import { setMockValues } from './__mocks__';
import React from 'react';
import { shallow } from 'enzyme';
@@ -12,14 +14,13 @@ import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
import { RecentActivity, RecentActivityItem } from './recent_activity';
-import { defaultServerData } from './overview';
jest.mock('../../../shared/telemetry', () => ({ sendTelemetry: jest.fn() }));
import { sendTelemetry } from '../../../shared/telemetry';
-const org = { name: 'foo', defaultOrgName: 'bar' };
+const organization = { name: 'foo', defaultOrgName: 'bar' };
-const feed = [
+const activityFeed = [
{
id: 'demo',
sourceId: 'd2d2d23d',
@@ -30,17 +31,19 @@ const feed = [
];
describe('RecentActivity', () => {
- it('renders with no feed data', () => {
- const wrapper = shallow();
+ it('renders with no activityFeed data', () => {
+ const wrapper = shallow();
expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
// Branch coverage - renders without error for custom org name
- shallow();
+ setMockValues({ organization });
+ shallow();
});
- it('renders an activity feed with links', () => {
- const wrapper = shallow();
+ it('renders an activityFeed with links', () => {
+ setMockValues({ activityFeed });
+ const wrapper = shallow();
const activity = wrapper.find(RecentActivityItem).dive();
expect(activity).toHaveLength(1);
@@ -51,7 +54,7 @@ describe('RecentActivity', () => {
});
it('renders activity item error state', () => {
- const props = { ...feed[0], status: 'error' };
+ const props = { ...activityFeed[0], status: 'error' };
const wrapper = shallow();
expect(wrapper.find('.activity--error')).toHaveLength(1);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.tsx
index 8d69582c936842..2c0fbe1275cbfb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/recent_activity.tsx
@@ -7,6 +7,7 @@
import React, { useContext } from 'react';
import moment from 'moment';
+import { useValues } from 'kea';
import { EuiEmptyPrompt, EuiLink, EuiPanel, EuiSpacer, EuiLinkProps } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -17,7 +18,7 @@ import { sendTelemetry } from '../../../shared/telemetry';
import { KibanaContext, IKibanaContext } from '../../../index';
import { getSourcePath } from '../../routes';
-import { IAppServerData } from './overview';
+import { OverviewLogic, IOverviewValues } from './overview_logic';
import './recent_activity.scss';
@@ -29,10 +30,12 @@ export interface IFeedActivity {
sourceId: string;
}
-export const RecentActivity: React.FC = ({
- organization: { name, defaultOrgName },
- activityFeed,
-}) => {
+export const RecentActivity: React.FC = () => {
+ const {
+ organization: { name, defaultOrgName },
+ activityFeed,
+ } = useValues(OverviewLogic) as IOverviewValues;
+
return (
,
testSubj: 'contentSection',
- className: 'test',
};
describe('ContentSection', () => {
it('renders', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
expect(wrapper.prop('data-test-subj')).toEqual('contentSection');
expect(wrapper.prop('className')).toEqual('test');
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx
index 4680f15771caab..b0b07c46b4ea81 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx
@@ -26,9 +26,10 @@ describe('ViewContentHeader', () => {
});
it('shows description, when present', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
expect(wrapper.find('p').text()).toEqual('Hello World');
+ expect(wrapper.find(EuiFlexGroup).prop('alignItems')).toEqual('center');
});
it('shows action, when present', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
index 36b1a56ecba262..cfa70ea29eca84 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
@@ -6,6 +6,13 @@
import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { Store } from 'redux';
+import { getContext, resetContext } from 'kea';
+
+resetContext({ createStore: true });
+
+const store = getContext().store as Store;
import { KibanaContext, IKibanaContext } from '../index';
@@ -17,13 +24,13 @@ import { Overview } from './components/overview';
export const WorkplaceSearch: React.FC = () => {
const { enterpriseSearchUrl } = useContext(KibanaContext) as IKibanaContext;
return (
- <>
+
{!enterpriseSearchUrl ? : }
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
index b448c59c52f3e3..77c35adef3300a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
@@ -13,4 +13,15 @@ export interface IAccount {
supportEligible: boolean;
}
+export interface IOrganization {
+ name: string;
+ defaultOrgName: string;
+}
+export interface IUser {
+ firstName: string;
+ email: string;
+ name: string;
+ color: string;
+}
+
export type TSpacerSize = 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
diff --git a/yarn.lock b/yarn.lock
index 0638a019a9402e..899bc45fbe3fbb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19957,6 +19957,11 @@ kdbush@^3.0.0:
resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0"
integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==
+kea@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/kea/-/kea-2.1.1.tgz#6e3c65c4873b67d270a2ec7bf73b0d178937234c"
+ integrity sha512-W9o4lHLOcEDIu3ASHPrWJJJzL1bMkGyxaHn9kuaDgI96ztBShVrf52R0QPGlQ2k9ca3XnkB/dnVHio1UB8kGWA==
+
kew@~0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/kew/-/kew-0.1.7.tgz#0a32a817ff1a9b3b12b8c9bacf4bc4d679af8e72"
From 88aebc9fe17fa0583b7c5b9af17520511c9b18ad Mon Sep 17 00:00:00 2001
From: liza-mae
Date: Mon, 27 Jul 2020 15:10:33 -0600
Subject: [PATCH 003/102] Remove ca cert path for cloud testing (#73317)
---
test/common/services/elasticsearch.ts | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/test/common/services/elasticsearch.ts b/test/common/services/elasticsearch.ts
index 0436dc901292d5..a01f9ff3c8da59 100644
--- a/test/common/services/elasticsearch.ts
+++ b/test/common/services/elasticsearch.ts
@@ -27,11 +27,18 @@ import { FtrProviderContext } from '../ftr_provider_context';
export function ElasticsearchProvider({ getService }: FtrProviderContext) {
const config = getService('config');
- return new Client({
- ssl: {
- ca: fs.readFileSync(CA_CERT_PATH, 'utf-8'),
- },
- nodes: [formatUrl(config.get('servers.elasticsearch'))],
- requestTimeout: config.get('timeouts.esRequestTimeout'),
- });
+ if (process.env.TEST_CLOUD) {
+ return new Client({
+ nodes: [formatUrl(config.get('servers.elasticsearch'))],
+ requestTimeout: config.get('timeouts.esRequestTimeout'),
+ });
+ } else {
+ return new Client({
+ ssl: {
+ ca: fs.readFileSync(CA_CERT_PATH, 'utf-8'),
+ },
+ nodes: [formatUrl(config.get('servers.elasticsearch'))],
+ requestTimeout: config.get('timeouts.esRequestTimeout'),
+ });
+ }
}
From 5a472189715931012096b99b95651ffd5791179c Mon Sep 17 00:00:00 2001
From: Nathan L Smith
Date: Mon, 27 Jul 2020 16:24:45 -0500
Subject: [PATCH 004/102] [APM] Fix focus map link on service map (#73338)
The link was including `serviceName` from the `urlParams` so it was linking to the wrong service. Overwrite the service name so the link is correct.
Fixes #72911.
---
.../app/ServiceMap/Popover/Buttons.test.tsx | 32 +++++++++++++++++++
.../app/ServiceMap/Popover/Buttons.tsx | 7 +++-
2 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.test.tsx
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.test.tsx
new file mode 100644
index 00000000000000..4146266b179167
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.test.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { Buttons } from './Buttons';
+import { render } from '@testing-library/react';
+
+describe('Popover Buttons', () => {
+ it('renders', () => {
+ expect(() =>
+ render()
+ ).not.toThrowError();
+ });
+
+ it('handles focus click', async () => {
+ const onFocusClick = jest.fn();
+ const result = render(
+
+ );
+ const focusButton = await result.findByText('Focus map');
+
+ focusButton.click();
+
+ expect(onFocusClick).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx
index d67447e04ef812..cb33fb32f3b0d3 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx
@@ -22,7 +22,12 @@ export function Buttons({
onFocusClick = () => {},
selectedNodeServiceName,
}: ButtonsProps) {
- const urlParams = useUrlParams().urlParams as APMQueryParams;
+ // The params may contain the service name. We want to use the selected node's
+ // service name in the button URLs, so make a copy and set the
+ // `serviceName` property.
+ const urlParams = { ...useUrlParams().urlParams } as APMQueryParams;
+ urlParams.serviceName = selectedNodeServiceName;
+
const detailsUrl = getAPMHref(
`/services/${selectedNodeServiceName}/transactions`,
'',
From 157fb097a9aeed8a9e167efa91347617a258ca5b Mon Sep 17 00:00:00 2001
From: Spencer
Date: Mon, 27 Jul 2020 14:31:02 -0700
Subject: [PATCH 005/102] [dev/build/docker_generator] convert to typescript
(#73339)
Co-authored-by: spalger
---
...e_dockerfiles.js => bundle_dockerfiles.ts} | 28 ++++++++--------
.../os_packages/docker_generator/index.ts | 1 -
.../docker_generator/{run.js => run.ts} | 19 ++++++++---
.../docker_generator/template_context.ts | 33 +++++++++++++++++++
...emplate.js => build_docker_sh.template.ts} | 4 ++-
...ile.template.js => dockerfile.template.ts} | 4 ++-
.../templates/{index.js => index.ts} | 0
...yml.template.js => kibana_yml.template.ts} | 4 ++-
8 files changed, 71 insertions(+), 22 deletions(-)
rename src/dev/build/tasks/os_packages/docker_generator/{bundle_dockerfiles.js => bundle_dockerfiles.ts} (80%)
rename src/dev/build/tasks/os_packages/docker_generator/{run.js => run.ts} (90%)
create mode 100644 src/dev/build/tasks/os_packages/docker_generator/template_context.ts
rename src/dev/build/tasks/os_packages/docker_generator/templates/{build_docker_sh.template.js => build_docker_sh.template.ts} (94%)
rename src/dev/build/tasks/os_packages/docker_generator/templates/{dockerfile.template.js => dockerfile.template.ts} (98%)
rename src/dev/build/tasks/os_packages/docker_generator/templates/{index.js => index.ts} (100%)
rename src/dev/build/tasks/os_packages/docker_generator/templates/{kibana_yml.template.js => kibana_yml.template.ts} (91%)
diff --git a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts
similarity index 80%
rename from src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js
rename to src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts
index 3f34a840576681..7a8f7316913be9 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js
+++ b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts
@@ -18,10 +18,14 @@
*/
import { resolve } from 'path';
-import { compressTar, copyAll, mkdirp, write } from '../../../lib';
+
+import { ToolingLog } from '@kbn/dev-utils';
+
+import { compressTar, copyAll, mkdirp, write, Config } from '../../../lib';
import { dockerfileTemplate } from './templates';
+import { TemplateContext } from './template_context';
-export async function bundleDockerFiles(config, log, build, scope) {
+export async function bundleDockerFiles(config: Config, log: ToolingLog, scope: TemplateContext) {
log.info(
`Generating kibana${scope.imageFlavor}${scope.ubiImageFlavor} docker build context bundle`
);
@@ -50,17 +54,15 @@ export async function bundleDockerFiles(config, log, build, scope) {
// Compress dockerfiles dir created inside
// docker build dir as output it as a target
// on targets folder
- await compressTar(
- {
- archiverOptions: {
- gzip: true,
- gzipOptions: {
- level: 9,
- },
+ await compressTar({
+ source: dockerFilesBuildDir,
+ destination: dockerFilesOutputDir,
+ archiverOptions: {
+ gzip: true,
+ gzipOptions: {
+ level: 9,
},
- createRootDirectory: false,
},
- dockerFilesBuildDir,
- dockerFilesOutputDir
- );
+ createRootDirectory: false,
+ });
}
diff --git a/src/dev/build/tasks/os_packages/docker_generator/index.ts b/src/dev/build/tasks/os_packages/docker_generator/index.ts
index 78d2b197dc7b2f..dff56585fc7040 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/index.ts
+++ b/src/dev/build/tasks/os_packages/docker_generator/index.ts
@@ -17,5 +17,4 @@
* under the License.
*/
-// @ts-expect-error not ts yet
export { runDockerGenerator, runDockerGeneratorForUBI } from './run';
diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.js b/src/dev/build/tasks/os_packages/docker_generator/run.ts
similarity index 90%
rename from src/dev/build/tasks/os_packages/docker_generator/run.js
rename to src/dev/build/tasks/os_packages/docker_generator/run.ts
index b6dab43887f14b..0a26729f3502de 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/run.js
+++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts
@@ -20,8 +20,12 @@
import { access, link, unlink, chmod } from 'fs';
import { resolve } from 'path';
import { promisify } from 'util';
-import { write, copyAll, mkdirp, exec } from '../../../lib';
+
+import { ToolingLog } from '@kbn/dev-utils';
+
+import { write, copyAll, mkdirp, exec, Config, Build } from '../../../lib';
import * as dockerTemplates from './templates';
+import { TemplateContext } from './template_context';
import { bundleDockerFiles } from './bundle_dockerfiles';
const accessAsync = promisify(access);
@@ -29,7 +33,12 @@ const linkAsync = promisify(link);
const unlinkAsync = promisify(unlink);
const chmodAsync = promisify(chmod);
-export async function runDockerGenerator(config, log, build, ubi = false) {
+export async function runDockerGenerator(
+ config: Config,
+ log: ToolingLog,
+ build: Build,
+ ubi: boolean = false
+) {
// UBI var config
const baseOSImage = ubi ? 'registry.access.redhat.com/ubi7/ubi-minimal:7.7' : 'centos:7';
const ubiVersionTag = 'ubi7';
@@ -52,7 +61,7 @@ export async function runDockerGenerator(config, log, build, ubi = false) {
const dockerOutputDir = config.resolveFromTarget(
`kibana${imageFlavor}${ubiImageFlavor}-${versionTag}-docker.tar.gz`
);
- const scope = {
+ const scope: TemplateContext = {
artifactTarball,
imageFlavor,
versionTag,
@@ -112,10 +121,10 @@ export async function runDockerGenerator(config, log, build, ubi = false) {
});
// Pack Dockerfiles and create a target for them
- await bundleDockerFiles(config, log, build, scope);
+ await bundleDockerFiles(config, log, scope);
}
-export async function runDockerGeneratorForUBI(config, log, build) {
+export async function runDockerGeneratorForUBI(config: Config, log: ToolingLog, build: Build) {
// Only run ubi docker image build for default distribution
if (build.isOss()) {
return;
diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts
new file mode 100644
index 00000000000000..115d4c6927c30b
--- /dev/null
+++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts
@@ -0,0 +1,33 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface TemplateContext {
+ artifactTarball: string;
+ imageFlavor: string;
+ versionTag: string;
+ license: string;
+ artifactsDir: string;
+ imageTag: string;
+ dockerBuildDir: string;
+ dockerOutputDir: string;
+ baseOSImage: string;
+ ubiImageFlavor: string;
+ dockerBuildDate: string;
+ usePublicArtifact?: boolean;
+}
diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts
similarity index 94%
rename from src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js
rename to src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts
index 4e8dfe188b8671..ff6fcf7548d9dd 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js
+++ b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts
@@ -19,6 +19,8 @@
import dedent from 'dedent';
+import { TemplateContext } from '../template_context';
+
function generator({
imageTag,
imageFlavor,
@@ -26,7 +28,7 @@ function generator({
dockerOutputDir,
baseOSImage,
ubiImageFlavor,
-}) {
+}: TemplateContext) {
return dedent(`
#!/usr/bin/env bash
#
diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts
similarity index 98%
rename from src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js
rename to src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts
index 5832d00162b209..ea2f881768c8fe 100755
--- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js
+++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts
@@ -19,6 +19,8 @@
import dedent from 'dedent';
+import { TemplateContext } from '../template_context';
+
function generator({
artifactTarball,
versionTag,
@@ -27,7 +29,7 @@ function generator({
baseOSImage,
ubiImageFlavor,
dockerBuildDate,
-}) {
+}: TemplateContext) {
const copyArtifactTarballInsideDockerOptFolder = () => {
if (usePublicArtifact) {
return `RUN cd /opt && curl --retry 8 -s -L -O https://artifacts.elastic.co/downloads/kibana/${artifactTarball} && cd -`;
diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/index.js b/src/dev/build/tasks/os_packages/docker_generator/templates/index.ts
similarity index 100%
rename from src/dev/build/tasks/os_packages/docker_generator/templates/index.js
rename to src/dev/build/tasks/os_packages/docker_generator/templates/index.ts
diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.js b/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
similarity index 91%
rename from src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.js
rename to src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
index c80f9334cfaeb9..240ec6f4e9326a 100644
--- a/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.js
+++ b/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
@@ -19,7 +19,9 @@
import dedent from 'dedent';
-function generator({ imageFlavor }) {
+import { TemplateContext } from '../template_context';
+
+function generator({ imageFlavor }: TemplateContext) {
return dedent(`
#
# ** THIS IS AN AUTO-GENERATED FILE **
From 57997beed8f7eaf7f67cd17d397eb4abcd6abf36 Mon Sep 17 00:00:00 2001
From: Constance
Date: Mon, 27 Jul 2020 15:06:42 -0700
Subject: [PATCH 006/102] [Enterprise Search] Error state UI tweaks to account
for current Cloud SSO behavior (#73324)
* Do not disable the Launch App Search button on the error page
- so users always have access to App Search
* Add troubleshooting steps that mention user authentication
- more info can be found in setup guide
* Tweak styling/spacing on troubleshooting steps
* Copyedits (thanks Chris!)
---
.../components/empty_states/error_state.tsx | 2 +-
.../engine_overview_header.test.tsx | 8 -------
.../engine_overview_header.tsx | 23 +++++-------------
.../error_state/error_state_prompt.scss | 12 ++++++++++
.../shared/error_state/error_state_prompt.tsx | 24 ++++++++++++++++++-
5 files changed, 42 insertions(+), 27 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.scss
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx
index 7ac02082ee75c1..346e70d32f7b19 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx
@@ -21,7 +21,7 @@ export const ErrorState: React.FC = () => {
-
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx
index 2e49540270ef07..7d2106f2a56f77 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx
@@ -30,12 +30,4 @@ describe('EngineOverviewHeader', () => {
button.simulate('click');
expect(sendTelemetry).toHaveBeenCalled();
});
-
- it('renders a disabled button when isButtonDisabled is true', () => {
- const wrapper = shallow();
- const button = wrapper.find('[data-test-subj="launchButton"]');
-
- expect(button.prop('isDisabled')).toBe(true);
- expect(button.prop('href')).toBeUndefined();
- });
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
index 9aafa8ec0380c7..cc480d241ad500 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
@@ -18,34 +18,23 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { sendTelemetry } from '../../../shared/telemetry';
import { KibanaContext, IKibanaContext } from '../../../index';
-interface IEngineOverviewHeaderProps {
- isButtonDisabled?: boolean;
-}
-
-export const EngineOverviewHeader: React.FC = ({
- isButtonDisabled,
-}) => {
+export const EngineOverviewHeader: React.FC = () => {
const { enterpriseSearchUrl, http } = useContext(KibanaContext) as IKibanaContext;
const buttonProps = {
fill: true,
iconType: 'popout',
'data-test-subj': 'launchButton',
- } as EuiButtonProps & EuiLinkProps;
-
- if (isButtonDisabled) {
- buttonProps.isDisabled = true;
- } else {
- buttonProps.href = `${enterpriseSearchUrl}/as`;
- buttonProps.target = '_blank';
- buttonProps.onClick = () =>
+ href: `${enterpriseSearchUrl}/as`,
+ target: '_blank',
+ onClick: () =>
sendTelemetry({
http,
product: 'app_search',
action: 'clicked',
metric: 'header_launch_button',
- });
- }
+ }),
+ } as EuiButtonProps & EuiLinkProps;
return (
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.scss b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.scss
new file mode 100644
index 00000000000000..0d9926ab147bf4
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.scss
@@ -0,0 +1,12 @@
+.troubleshootingSteps {
+ text-align: left;
+
+ li {
+ margin: $euiSizeS auto;
+ }
+
+ ul,
+ ol {
+ margin-bottom: 0;
+ }
+}
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx
index 81455cea0b497a..ccd5beff66e70c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx
@@ -11,6 +11,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton } from '../react_router_helpers';
import { KibanaContext, IKibanaContext } from '../../index';
+import './error_state_prompt.scss';
+
export const ErrorStatePrompt: React.FC = () => {
const { enterpriseSearchUrl } = useContext(KibanaContext) as IKibanaContext;
@@ -38,7 +40,7 @@ export const ErrorStatePrompt: React.FC = () => {
}}
/>
-
+
-
{
defaultMessage="Confirm that the Enterprise Search server is responsive."
/>
+ -
+
+
+
-
Date: Mon, 27 Jul 2020 18:19:16 -0400
Subject: [PATCH 007/102] [Security Solution][Exceptions] - Update exception
item comments to include id (#73129)
## Summary
This PR is somewhat of an intermediary step. Comments on exception list items are denormalized. We initially decided that we would not add `uuid` to comments, but found that it is in fact necessary. This is intermediary in the sense that what we ideally want to have is a dedicated `comments` CRUD route.
Also just note that I added a callout for when a version conflict occurs (ie: exception item was updated by someone else while a user is editing the same item).
With this PR users are able to:
- Create comments when creating exception list items
- Add new comments on exception item update
Users will currently be blocked from:
- Deleting comments
- Updating comments
- Updating exception item if version conflict is found
---
x-pack/plugins/lists/common/constants.mock.ts | 1 +
.../create_endpoint_list_item_schema.test.ts | 36 +-
.../create_exception_list_item_schema.test.ts | 36 +-
...ate_exception_list_item_validation.test.ts | 43 ++
.../update_exception_list_item_validation.ts | 40 ++
.../{comments.mock.ts => comment.mock.ts} | 7 +-
.../{comments.test.ts => comment.test.ts} | 109 +++--
.../schemas/types/{comments.ts => comment.ts} | 23 +-
...omments.mock.ts => create_comment.mock.ts} | 4 +-
...omments.test.ts => create_comment.test.ts} | 50 +--
.../{create_comments.ts => create_comment.ts} | 11 +-
.../types/default_comments_array.test.ts | 21 +-
.../schemas/types/default_comments_array.ts | 6 +-
.../default_create_comments_array.test.ts | 30 +-
.../types/default_create_comments_array.ts | 6 +-
.../default_update_comments_array.test.ts | 23 +-
.../types/default_update_comments_array.ts | 2 +-
.../lists/common/schemas/types/index.ts | 6 +-
...omments.mock.ts => update_comment.mock.ts} | 15 +-
.../schemas/types/update_comment.test.ts | 150 +++++++
.../{update_comments.ts => update_comment.ts} | 20 +-
.../schemas/types/update_comments.test.ts | 108 -----
x-pack/plugins/lists/common/shared_exports.ts | 5 +-
.../update_exception_list_item_route.ts | 6 +
.../server/saved_objects/exception_list.ts | 3 +
.../updates/simple_update_item.json | 25 +-
.../create_exception_list_item.ts | 5 +-
.../services/exception_lists/utils.test.ts | 390 ++----------------
.../server/services/exception_lists/utils.ts | 115 +-----
.../common/shared_imports.ts | 5 +-
.../exceptions/add_exception_comments.tsx | 4 +-
.../exceptions/add_exception_modal/index.tsx | 4 +-
.../components/exceptions/builder/index.tsx | 2 +-
.../exceptions/edit_exception_modal/index.tsx | 40 +-
.../edit_exception_modal/translations.ts | 15 +
.../components/exceptions/helpers.test.tsx | 55 ++-
.../common/components/exceptions/helpers.tsx | 55 ++-
.../exception_item/exception_details.test.tsx | 2 +-
.../viewer/exception_item/index.stories.tsx | 2 +-
.../viewer/exception_item/index.test.tsx | 2 +-
.../components/exceptions/viewer/index.tsx | 3 +-
41 files changed, 702 insertions(+), 783 deletions(-)
create mode 100644 x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.test.ts
create mode 100644 x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.ts
rename x-pack/plugins/lists/common/schemas/types/{comments.mock.ts => comment.mock.ts} (71%)
rename x-pack/plugins/lists/common/schemas/types/{comments.test.ts => comment.test.ts} (56%)
rename x-pack/plugins/lists/common/schemas/types/{comments.ts => comment.ts} (56%)
rename x-pack/plugins/lists/common/schemas/types/{create_comments.mock.ts => create_comment.mock.ts} (73%)
rename x-pack/plugins/lists/common/schemas/types/{create_comments.test.ts => create_comment.test.ts} (72%)
rename x-pack/plugins/lists/common/schemas/types/{create_comments.ts => create_comment.ts} (64%)
rename x-pack/plugins/lists/common/schemas/types/{update_comments.mock.ts => update_comment.mock.ts} (54%)
create mode 100644 x-pack/plugins/lists/common/schemas/types/update_comment.test.ts
rename x-pack/plugins/lists/common/schemas/types/{update_comments.ts => update_comment.ts} (58%)
delete mode 100644 x-pack/plugins/lists/common/schemas/types/update_comments.test.ts
diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/plugins/lists/common/constants.mock.ts
index 30f219c3ec1010..22706890e20209 100644
--- a/x-pack/plugins/lists/common/constants.mock.ts
+++ b/x-pack/plugins/lists/common/constants.mock.ts
@@ -6,6 +6,7 @@
import { EntriesArray } from './schemas/types';
export const DATE_NOW = '2020-04-20T15:25:31.830Z';
+export const OLD_DATE_RELATIVE_TO_DATE_NOW = '2020-04-19T15:25:31.830Z';
export const USER = 'some user';
export const LIST_INDEX = '.lists';
export const LIST_ITEM_INDEX = '.items';
diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts
index 5de9fbb0d5b50f..75e0410be610aa 100644
--- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts
+++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts
@@ -8,8 +8,8 @@ import { left } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps';
-import { getCreateCommentsArrayMock } from '../types/create_comments.mock';
-import { getCommentsMock } from '../types/comments.mock';
+import { getCreateCommentsArrayMock } from '../types/create_comment.mock';
+import { getCommentsMock } from '../types/comment.mock';
import { CommentsArray } from '../types';
import {
@@ -19,7 +19,7 @@ import {
import { getCreateEndpointListItemSchemaMock } from './create_endpoint_list_item_schema.mock';
describe('create_endpoint_list_item_schema', () => {
- test('it should validate a typical list item request not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied a typical list item request not counting the auto generated uuid', () => {
const payload = getCreateEndpointListItemSchemaMock();
const decoded = createEndpointListItemSchema.decode(payload);
const checked = exactCheck(payload, decoded);
@@ -29,7 +29,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate an undefined for "description"', () => {
+ test('it should fail validation when supplied an undefined for "description"', () => {
const payload = getCreateEndpointListItemSchemaMock();
delete payload.description;
const decoded = createEndpointListItemSchema.decode(payload);
@@ -41,7 +41,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate an undefined for "name"', () => {
+ test('it should fail validation when supplied an undefined for "name"', () => {
const payload = getCreateEndpointListItemSchemaMock();
delete payload.name;
const decoded = createEndpointListItemSchema.decode(payload);
@@ -53,7 +53,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate an undefined for "type"', () => {
+ test('it should fail validation when supplied an undefined for "type"', () => {
const payload = getCreateEndpointListItemSchemaMock();
delete payload.type;
const decoded = createEndpointListItemSchema.decode(payload);
@@ -65,7 +65,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate a "list_id" since it does not required one', () => {
+ test('it should fail validation when supplied a "list_id" since it does not required one', () => {
const inputPayload: CreateEndpointListItemSchema & { list_id: string } = {
...getCreateEndpointListItemSchemaMock(),
list_id: 'list-123',
@@ -77,7 +77,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate a "namespace_type" since it does not required one', () => {
+ test('it should fail validation when supplied a "namespace_type" since it does not required one', () => {
const inputPayload: CreateEndpointListItemSchema & { namespace_type: string } = {
...getCreateEndpointListItemSchemaMock(),
namespace_type: 'single',
@@ -89,7 +89,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should validate an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => {
const payload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete payload.meta;
@@ -102,7 +102,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.comments;
@@ -115,7 +115,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate "comments" array', () => {
+ test('it should pass validation when supplied "comments" array', () => {
const inputPayload = {
...getCreateEndpointListItemSchemaMock(),
comments: getCreateCommentsArrayMock(),
@@ -128,7 +128,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(inputPayload);
});
- test('it should NOT validate "comments" with "created_at" or "created_by" values', () => {
+ test('it should fail validation when supplied "comments" with "created_at", "created_by", or "id" values', () => {
const inputPayload: Omit & {
comments?: CommentsArray;
} = {
@@ -138,11 +138,11 @@ describe('create_endpoint_list_item_schema', () => {
const decoded = createEndpointListItemSchema.decode(inputPayload);
const checked = exactCheck(inputPayload, decoded);
const message = pipe(checked, foldLeftRight);
- expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by"']);
+ expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']);
expect(message.schema).toEqual({});
});
- test('it should NOT validate an undefined for "entries"', () => {
+ test('it should fail validation when supplied an undefined for "entries"', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.entries;
@@ -157,7 +157,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should validate an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.tags;
@@ -170,7 +170,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload._tags;
@@ -183,7 +183,7 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "item_id" and auto generate a uuid', () => {
+ test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.item_id;
const decoded = createEndpointListItemSchema.decode(inputPayload);
@@ -195,7 +195,7 @@ describe('create_endpoint_list_item_schema', () => {
);
});
- test('it should validate an undefined for "item_id" and generate a correct body not counting the uuid', () => {
+ test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.item_id;
const decoded = createEndpointListItemSchema.decode(inputPayload);
diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts
index 08f3966af08d95..cf4c1fea0306f5 100644
--- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts
+++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts
@@ -8,8 +8,8 @@ import { left } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps';
-import { getCreateCommentsArrayMock } from '../types/create_comments.mock';
-import { getCommentsMock } from '../types/comments.mock';
+import { getCreateCommentsArrayMock } from '../types/create_comment.mock';
+import { getCommentsMock } from '../types/comment.mock';
import { CommentsArray } from '../types';
import {
@@ -19,7 +19,7 @@ import {
import { getCreateExceptionListItemSchemaMock } from './create_exception_list_item_schema.mock';
describe('create_exception_list_item_schema', () => {
- test('it should validate a typical exception list item request not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied a typical exception list item request not counting the auto generated uuid', () => {
const payload = getCreateExceptionListItemSchemaMock();
const decoded = createExceptionListItemSchema.decode(payload);
const checked = exactCheck(payload, decoded);
@@ -29,7 +29,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate an undefined for "description"', () => {
+ test('it should fail validation when supplied an undefined for "description"', () => {
const payload = getCreateExceptionListItemSchemaMock();
delete payload.description;
const decoded = createExceptionListItemSchema.decode(payload);
@@ -41,7 +41,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate an undefined for "name"', () => {
+ test('it should fail validation when supplied an undefined for "name"', () => {
const payload = getCreateExceptionListItemSchemaMock();
delete payload.name;
const decoded = createExceptionListItemSchema.decode(payload);
@@ -53,7 +53,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate an undefined for "type"', () => {
+ test('it should fail validation when supplied an undefined for "type"', () => {
const payload = getCreateExceptionListItemSchemaMock();
delete payload.type;
const decoded = createExceptionListItemSchema.decode(payload);
@@ -65,7 +65,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate an undefined for "list_id"', () => {
+ test('it should fail validation when supplied an undefined for "list_id"', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.list_id;
const decoded = createExceptionListItemSchema.decode(inputPayload);
@@ -77,7 +77,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should validate an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => {
const payload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete payload.meta;
@@ -90,7 +90,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.comments;
@@ -103,7 +103,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate "comments" array', () => {
+ test('it should pass validation when supplied "comments" array', () => {
const inputPayload = {
...getCreateExceptionListItemSchemaMock(),
comments: getCreateCommentsArrayMock(),
@@ -116,7 +116,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(inputPayload);
});
- test('it should NOT validate "comments" with "created_at" or "created_by" values', () => {
+ test('it should fail validation when supplied "comments" with "created_at" or "created_by" values', () => {
const inputPayload: Omit & {
comments?: CommentsArray;
} = {
@@ -126,11 +126,11 @@ describe('create_exception_list_item_schema', () => {
const decoded = createExceptionListItemSchema.decode(inputPayload);
const checked = exactCheck(inputPayload, decoded);
const message = pipe(checked, foldLeftRight);
- expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by"']);
+ expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']);
expect(message.schema).toEqual({});
});
- test('it should NOT validate an undefined for "entries"', () => {
+ test('it should fail validation when supplied an undefined for "entries"', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.entries;
@@ -145,7 +145,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual({});
});
- test('it should validate an undefined for "namespace_type" but return enum "single" and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "namespace_type" but return enum "single" and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.namespace_type;
@@ -158,7 +158,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.tags;
@@ -171,7 +171,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
+ test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload._tags;
@@ -184,7 +184,7 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});
- test('it should validate an undefined for "item_id" and auto generate a uuid', () => {
+ test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.item_id;
const decoded = createExceptionListItemSchema.decode(inputPayload);
@@ -196,7 +196,7 @@ describe('create_exception_list_item_schema', () => {
);
});
- test('it should validate an undefined for "item_id" and generate a correct body not counting the uuid', () => {
+ test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.item_id;
const decoded = createExceptionListItemSchema.decode(inputPayload);
diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.test.ts
new file mode 100644
index 00000000000000..3358582786cc73
--- /dev/null
+++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.test.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { getUpdateExceptionListItemSchemaMock } from './update_exception_list_item_schema.mock';
+import { validateComments } from './update_exception_list_item_validation';
+
+describe('update_exception_list_item_validation', () => {
+ describe('#validateComments', () => {
+ test('it returns no errors if comments is undefined', () => {
+ const payload = getUpdateExceptionListItemSchemaMock();
+ delete payload.comments;
+ const output = validateComments(payload);
+
+ expect(output).toEqual([]);
+ });
+
+ test('it returns no errors if new comments are append only', () => {
+ const payload = getUpdateExceptionListItemSchemaMock();
+ payload.comments = [
+ { comment: 'Im an old comment', id: '1' },
+ { comment: 'Im a new comment' },
+ ];
+ const output = validateComments(payload);
+
+ expect(output).toEqual([]);
+ });
+
+ test('it returns error if comments are not append only', () => {
+ const payload = getUpdateExceptionListItemSchemaMock();
+ payload.comments = [
+ { comment: 'Im an old comment', id: '1' },
+ { comment: 'Im a new comment modifying the order of existing comments' },
+ { comment: 'Im an old comment', id: '2' },
+ ];
+ const output = validateComments(payload);
+
+ expect(output).toEqual(['item "comments" are append only']);
+ });
+ });
+});
diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.ts
new file mode 100644
index 00000000000000..5e44c4e9f73e71
--- /dev/null
+++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_validation.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 { UpdateExceptionListItemSchema } from './update_exception_list_item_schema';
+
+export const validateComments = (item: UpdateExceptionListItemSchema): string[] => {
+ if (item.comments == null) {
+ return [];
+ }
+
+ const [appendOnly] = item.comments.reduce(
+ (acc, comment) => {
+ const [, hasNewComments] = acc;
+ if (comment.id == null) {
+ return [true, true];
+ }
+
+ if (hasNewComments && comment.id != null) {
+ return [false, true];
+ }
+
+ return acc;
+ },
+ [true, false]
+ );
+ if (!appendOnly) {
+ return ['item "comments" are append only'];
+ } else {
+ return [];
+ }
+};
+
+export const updateExceptionListItemValidate = (
+ schema: UpdateExceptionListItemSchema
+): string[] => {
+ return [...validateComments(schema)];
+};
diff --git a/x-pack/plugins/lists/common/schemas/types/comments.mock.ts b/x-pack/plugins/lists/common/schemas/types/comment.mock.ts
similarity index 71%
rename from x-pack/plugins/lists/common/schemas/types/comments.mock.ts
rename to x-pack/plugins/lists/common/schemas/types/comment.mock.ts
index 9e56ac292f8b56..213259b3cce294 100644
--- a/x-pack/plugins/lists/common/schemas/types/comments.mock.ts
+++ b/x-pack/plugins/lists/common/schemas/types/comment.mock.ts
@@ -4,14 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { DATE_NOW, USER } from '../../constants.mock';
+import { DATE_NOW, ID, USER } from '../../constants.mock';
-import { Comments, CommentsArray } from './comments';
+import { Comment, CommentsArray } from './comment';
-export const getCommentsMock = (): Comments => ({
+export const getCommentsMock = (): Comment => ({
comment: 'some old comment',
created_at: DATE_NOW,
created_by: USER,
+ id: ID,
});
export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()];
diff --git a/x-pack/plugins/lists/common/schemas/types/comments.test.ts b/x-pack/plugins/lists/common/schemas/types/comment.test.ts
similarity index 56%
rename from x-pack/plugins/lists/common/schemas/types/comments.test.ts
rename to x-pack/plugins/lists/common/schemas/types/comment.test.ts
index 29bfde03abcc8d..c7c945277f7566 100644
--- a/x-pack/plugins/lists/common/schemas/types/comments.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/comment.test.ts
@@ -10,56 +10,79 @@ import { left } from 'fp-ts/lib/Either';
import { DATE_NOW } from '../../constants.mock';
import { foldLeftRight, getPaths } from '../../siem_common_deps';
-import { getCommentsArrayMock, getCommentsMock } from './comments.mock';
+import { getCommentsArrayMock, getCommentsMock } from './comment.mock';
import {
- Comments,
+ Comment,
CommentsArray,
CommentsArrayOrUndefined,
- comments,
+ comment,
commentsArray,
commentsArrayOrUndefined,
-} from './comments';
+} from './comment';
-describe('Comments', () => {
- describe('comments', () => {
- test('it should validate a comments', () => {
+describe('Comment', () => {
+ describe('comment', () => {
+ test('it fails validation when "id" is undefined', () => {
+ const payload = { ...getCommentsMock(), id: undefined };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "id"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it passes validation with a typical comment', () => {
const payload = getCommentsMock();
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
- test('it should validate with "updated_at" and "updated_by"', () => {
+ test('it passes validation with "updated_at" and "updated_by" fields included', () => {
const payload = getCommentsMock();
payload.updated_at = DATE_NOW;
payload.updated_by = 'someone';
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
- test('it should not validate when undefined', () => {
+ test('it fails validation when undefined', () => {
const payload = undefined;
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "undefined" supplied to "({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)"',
- 'Invalid value "undefined" supplied to "({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)"',
+ 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"',
+ 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"',
]);
expect(message.schema).toEqual({});
});
- test('it should not validate when "comment" is not a string', () => {
- const payload: Omit & { comment: string[] } = {
+ test('it fails validation when "comment" is an empty string', () => {
+ const payload: Omit & { comment: string } = {
+ ...getCommentsMock(),
+ comment: '',
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "comment" is not a string', () => {
+ const payload: Omit & { comment: string[] } = {
...getCommentsMock(),
comment: ['some value'],
};
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -68,12 +91,12 @@ describe('Comments', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate when "created_at" is not a string', () => {
- const payload: Omit & { created_at: number } = {
+ test('it fails validation when "created_at" is not a string', () => {
+ const payload: Omit & { created_at: number } = {
...getCommentsMock(),
created_at: 1,
};
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -82,12 +105,12 @@ describe('Comments', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate when "created_by" is not a string', () => {
- const payload: Omit & { created_by: number } = {
+ test('it fails validation when "created_by" is not a string', () => {
+ const payload: Omit & { created_by: number } = {
...getCommentsMock(),
created_by: 1,
};
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -96,12 +119,12 @@ describe('Comments', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate when "updated_at" is not a string', () => {
- const payload: Omit & { updated_at: number } = {
+ test('it fails validation when "updated_at" is not a string', () => {
+ const payload: Omit & { updated_at: number } = {
...getCommentsMock(),
updated_at: 1,
};
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -110,12 +133,12 @@ describe('Comments', () => {
expect(message.schema).toEqual({});
});
- test('it should not validate when "updated_by" is not a string', () => {
- const payload: Omit & { updated_by: number } = {
+ test('it fails validation when "updated_by" is not a string', () => {
+ const payload: Omit & { updated_by: number } = {
...getCommentsMock(),
updated_by: 1,
};
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -125,11 +148,11 @@ describe('Comments', () => {
});
test('it should strip out extra keys', () => {
- const payload: Comments & {
+ const payload: Comment & {
extraKey?: string;
} = getCommentsMock();
payload.extraKey = 'some value';
- const decoded = comments.decode(payload);
+ const decoded = comment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
@@ -138,7 +161,7 @@ describe('Comments', () => {
});
describe('commentsArray', () => {
- test('it should validate an array of comments', () => {
+ test('it passes validation an array of Comment', () => {
const payload = getCommentsArrayMock();
const decoded = commentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -147,7 +170,7 @@ describe('Comments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate when a comments includes "updated_at" and "updated_by"', () => {
+ test('it passes validation when a Comment includes "updated_at" and "updated_by"', () => {
const commentsPayload = getCommentsMock();
commentsPayload.updated_at = DATE_NOW;
commentsPayload.updated_by = 'someone';
@@ -159,32 +182,32 @@ describe('Comments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate when undefined', () => {
+ test('it fails validation when undefined', () => {
const payload = undefined;
const decoded = commentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "undefined" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
]);
expect(message.schema).toEqual({});
});
- test('it should not validate when array includes non comments types', () => {
+ test('it fails validation when array includes non Comment types', () => {
const payload = ([1] as unknown) as CommentsArray;
const decoded = commentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
]);
expect(message.schema).toEqual({});
});
});
describe('commentsArrayOrUndefined', () => {
- test('it should validate an array of comments', () => {
+ test('it passes validation an array of Comment', () => {
const payload = getCommentsArrayMock();
const decoded = commentsArrayOrUndefined.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -193,7 +216,7 @@ describe('Comments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate when undefined', () => {
+ test('it passes validation when undefined', () => {
const payload = undefined;
const decoded = commentsArrayOrUndefined.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -202,14 +225,14 @@ describe('Comments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate when array includes non comments types', () => {
+ test('it fails validation when array includes non Comment types', () => {
const payload = ([1] as unknown) as CommentsArrayOrUndefined;
const decoded = commentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
]);
expect(message.schema).toEqual({});
});
diff --git a/x-pack/plugins/lists/common/schemas/types/comments.ts b/x-pack/plugins/lists/common/schemas/types/comment.ts
similarity index 56%
rename from x-pack/plugins/lists/common/schemas/types/comments.ts
rename to x-pack/plugins/lists/common/schemas/types/comment.ts
index 0ee3b05c8102f1..6b0b0166b9ee14 100644
--- a/x-pack/plugins/lists/common/schemas/types/comments.ts
+++ b/x-pack/plugins/lists/common/schemas/types/comment.ts
@@ -3,26 +3,33 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+
+/* eslint-disable @typescript-eslint/camelcase */
+
import * as t from 'io-ts';
-export const comments = t.intersection([
+import { NonEmptyString } from '../../siem_common_deps';
+import { created_at, created_by, id, updated_at, updated_by } from '../common/schemas';
+
+export const comment = t.intersection([
t.exact(
t.type({
- comment: t.string,
- created_at: t.string, // TODO: Make this into an ISO Date string check,
- created_by: t.string,
+ comment: NonEmptyString,
+ created_at,
+ created_by,
+ id,
})
),
t.exact(
t.partial({
- updated_at: t.string,
- updated_by: t.string,
+ updated_at,
+ updated_by,
})
),
]);
-export const commentsArray = t.array(comments);
+export const commentsArray = t.array(comment);
export type CommentsArray = t.TypeOf;
-export type Comments = t.TypeOf;
+export type Comment = t.TypeOf;
export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]);
export type CommentsArrayOrUndefined = t.TypeOf;
diff --git a/x-pack/plugins/lists/common/schemas/types/create_comments.mock.ts b/x-pack/plugins/lists/common/schemas/types/create_comment.mock.ts
similarity index 73%
rename from x-pack/plugins/lists/common/schemas/types/create_comments.mock.ts
rename to x-pack/plugins/lists/common/schemas/types/create_comment.mock.ts
index 60a59432275ca2..689d4ccdc2c2e7 100644
--- a/x-pack/plugins/lists/common/schemas/types/create_comments.mock.ts
+++ b/x-pack/plugins/lists/common/schemas/types/create_comment.mock.ts
@@ -3,9 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { CreateComments, CreateCommentsArray } from './create_comments';
+import { CreateComment, CreateCommentsArray } from './create_comment';
-export const getCreateCommentsMock = (): CreateComments => ({
+export const getCreateCommentsMock = (): CreateComment => ({
comment: 'some comments',
});
diff --git a/x-pack/plugins/lists/common/schemas/types/create_comments.test.ts b/x-pack/plugins/lists/common/schemas/types/create_comment.test.ts
similarity index 72%
rename from x-pack/plugins/lists/common/schemas/types/create_comments.test.ts
rename to x-pack/plugins/lists/common/schemas/types/create_comment.test.ts
index d2680750e05e4e..366bf84d48bbf0 100644
--- a/x-pack/plugins/lists/common/schemas/types/create_comments.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/create_comment.test.ts
@@ -9,44 +9,44 @@ import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '../../siem_common_deps';
-import { getCreateCommentsArrayMock, getCreateCommentsMock } from './create_comments.mock';
+import { getCreateCommentsArrayMock, getCreateCommentsMock } from './create_comment.mock';
import {
- CreateComments,
+ CreateComment,
CreateCommentsArray,
CreateCommentsArrayOrUndefined,
- createComments,
+ createComment,
createCommentsArray,
createCommentsArrayOrUndefined,
-} from './create_comments';
+} from './create_comment';
-describe('CreateComments', () => {
- describe('createComments', () => {
- test('it should validate a comments', () => {
+describe('CreateComment', () => {
+ describe('createComment', () => {
+ test('it passes validation with a default comment', () => {
const payload = getCreateCommentsMock();
- const decoded = createComments.decode(payload);
+ const decoded = createComment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
- test('it should not validate when undefined', () => {
+ test('it fails validation when undefined', () => {
const payload = undefined;
- const decoded = createComments.decode(payload);
+ const decoded = createComment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "undefined" supplied to "{| comment: string |}"',
+ 'Invalid value "undefined" supplied to "{| comment: NonEmptyString |}"',
]);
expect(message.schema).toEqual({});
});
- test('it should not validate when "comment" is not a string', () => {
- const payload: Omit & { comment: string[] } = {
+ test('it fails validation when "comment" is not a string', () => {
+ const payload: Omit & { comment: string[] } = {
...getCreateCommentsMock(),
comment: ['some value'],
};
- const decoded = createComments.decode(payload);
+ const decoded = createComment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
@@ -56,11 +56,11 @@ describe('CreateComments', () => {
});
test('it should strip out extra keys', () => {
- const payload: CreateComments & {
+ const payload: CreateComment & {
extraKey?: string;
} = getCreateCommentsMock();
payload.extraKey = 'some value';
- const decoded = createComments.decode(payload);
+ const decoded = createComment.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
@@ -69,7 +69,7 @@ describe('CreateComments', () => {
});
describe('createCommentsArray', () => {
- test('it should validate an array of comments', () => {
+ test('it passes validation an array of comments', () => {
const payload = getCreateCommentsArrayMock();
const decoded = createCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -78,31 +78,31 @@ describe('CreateComments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate when undefined', () => {
+ test('it fails validation when undefined', () => {
const payload = undefined;
const decoded = createCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "undefined" supplied to "Array<{| comment: string |}>"',
+ 'Invalid value "undefined" supplied to "Array<{| comment: NonEmptyString |}>"',
]);
expect(message.schema).toEqual({});
});
- test('it should not validate when array includes non comments types', () => {
+ test('it fails validation when array includes non comments types', () => {
const payload = ([1] as unknown) as CreateCommentsArray;
const decoded = createCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<{| comment: string |}>"',
+ 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"',
]);
expect(message.schema).toEqual({});
});
});
describe('createCommentsArrayOrUndefined', () => {
- test('it should validate an array of comments', () => {
+ test('it passes validation an array of comments', () => {
const payload = getCreateCommentsArrayMock();
const decoded = createCommentsArrayOrUndefined.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -111,7 +111,7 @@ describe('CreateComments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate when undefined', () => {
+ test('it passes validation when undefined', () => {
const payload = undefined;
const decoded = createCommentsArrayOrUndefined.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -120,13 +120,13 @@ describe('CreateComments', () => {
expect(message.schema).toEqual(payload);
});
- test('it should not validate when array includes non comments types', () => {
+ test('it fails validation when array includes non comments types', () => {
const payload = ([1] as unknown) as CreateCommentsArrayOrUndefined;
const decoded = createCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<{| comment: string |}>"',
+ 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"',
]);
expect(message.schema).toEqual({});
});
diff --git a/x-pack/plugins/lists/common/schemas/types/create_comments.ts b/x-pack/plugins/lists/common/schemas/types/create_comment.ts
similarity index 64%
rename from x-pack/plugins/lists/common/schemas/types/create_comments.ts
rename to x-pack/plugins/lists/common/schemas/types/create_comment.ts
index c34419298ef935..fd33313430ce6a 100644
--- a/x-pack/plugins/lists/common/schemas/types/create_comments.ts
+++ b/x-pack/plugins/lists/common/schemas/types/create_comment.ts
@@ -5,14 +5,17 @@
*/
import * as t from 'io-ts';
-export const createComments = t.exact(
+import { NonEmptyString } from '../../siem_common_deps';
+
+export const createComment = t.exact(
t.type({
- comment: t.string,
+ comment: NonEmptyString,
})
);
-export const createCommentsArray = t.array(createComments);
+export type CreateComment = t.TypeOf;
+export const createCommentsArray = t.array(createComment);
export type CreateCommentsArray = t.TypeOf;
-export type CreateComments = t.TypeOf;
+export type CreateComments = t.TypeOf;
export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]);
export type CreateCommentsArrayOrUndefined = t.TypeOf;
diff --git a/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts
index 3a4241aaec82d3..541b8ab1c799c0 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts
@@ -10,11 +10,11 @@ import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '../../siem_common_deps';
import { DefaultCommentsArray } from './default_comments_array';
-import { CommentsArray } from './comments';
-import { getCommentsArrayMock } from './comments.mock';
+import { CommentsArray } from './comment';
+import { getCommentsArrayMock } from './comment.mock';
describe('default_comments_array', () => {
- test('it should validate an empty array', () => {
+ test('it should pass validation when supplied an empty array', () => {
const payload: CommentsArray = [];
const decoded = DefaultCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -23,7 +23,7 @@ describe('default_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate an array of comments', () => {
+ test('it should pass validation when supplied an array of comments', () => {
const payload: CommentsArray = getCommentsArrayMock();
const decoded = DefaultCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -32,27 +32,26 @@ describe('default_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should NOT validate an array of numbers', () => {
+ test('it should fail validation when supplied an array of numbers', () => {
const payload = [1];
const decoded = DefaultCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
- // TODO: Known weird error formatting that is on our list to address
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
- 'Invalid value "1" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
]);
expect(message.schema).toEqual({});
});
- test('it should NOT validate an array of strings', () => {
+ test('it should fail validation when supplied an array of strings', () => {
const payload = ['some string'];
const decoded = DefaultCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "some string" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
- 'Invalid value "some string" supplied to "Array<({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
]);
expect(message.schema).toEqual({});
});
diff --git a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts
index 342cf8b0d70912..0d7e28e69cf719 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts
@@ -7,7 +7,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
-import { CommentsArray, comments } from './comments';
+import { CommentsArray, comment } from './comment';
/**
* Types the DefaultCommentsArray as:
@@ -15,8 +15,8 @@ import { CommentsArray, comments } from './comments';
*/
export const DefaultCommentsArray = new t.Type(
'DefaultCommentsArray',
- t.array(comments).is,
+ t.array(comment).is,
(input): Either =>
- input == null ? t.success([]) : t.array(comments).decode(input),
+ input == null ? t.success([]) : t.array(comment).decode(input),
t.identity
);
diff --git a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.test.ts
index f5ef7d0ad96bd0..eb960b54119048 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.test.ts
@@ -10,11 +10,12 @@ import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '../../siem_common_deps';
import { DefaultCreateCommentsArray } from './default_create_comments_array';
-import { CreateCommentsArray } from './create_comments';
-import { getCreateCommentsArrayMock } from './create_comments.mock';
+import { CreateCommentsArray } from './create_comment';
+import { getCreateCommentsArrayMock } from './create_comment.mock';
+import { getCommentsArrayMock } from './comment.mock';
describe('default_create_comments_array', () => {
- test('it should validate an empty array', () => {
+ test('it should pass validation when an empty array', () => {
const payload: CreateCommentsArray = [];
const decoded = DefaultCreateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -23,7 +24,7 @@ describe('default_create_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate an array of comments', () => {
+ test('it should pass validation when an array of comments', () => {
const payload: CreateCommentsArray = getCreateCommentsArrayMock();
const decoded = DefaultCreateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -32,25 +33,38 @@ describe('default_create_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should NOT validate an array of numbers', () => {
+ test('it should strip out "created_at" and "created_by" if they are passed in', () => {
+ const payload = getCommentsArrayMock();
+ const decoded = DefaultCreateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ // TODO: Known weird error formatting that is on our list to address
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([
+ { comment: 'some old comment' },
+ { comment: 'some old comment' },
+ ]);
+ });
+
+ test('it should not pass validation when an array of numbers', () => {
const payload = [1];
const decoded = DefaultCreateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
// TODO: Known weird error formatting that is on our list to address
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<{| comment: string |}>"',
+ 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"',
]);
expect(message.schema).toEqual({});
});
- test('it should NOT validate an array of strings', () => {
+ test('it should not pass validation when an array of strings', () => {
const payload = ['some string'];
const decoded = DefaultCreateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "some string" supplied to "Array<{| comment: string |}>"',
+ 'Invalid value "some string" supplied to "Array<{| comment: NonEmptyString |}>"',
]);
expect(message.schema).toEqual({});
});
diff --git a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts
index 7fd79782836e32..4df888ba728fbb 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts
@@ -7,7 +7,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
-import { CreateCommentsArray, createComments } from './create_comments';
+import { CreateCommentsArray, createComment } from './create_comment';
/**
* Types the DefaultCreateComments as:
@@ -19,8 +19,8 @@ export const DefaultCreateCommentsArray = new t.Type<
unknown
>(
'DefaultCreateComments',
- t.array(createComments).is,
+ t.array(createComment).is,
(input): Either =>
- input == null ? t.success([]) : t.array(createComments).decode(input),
+ input == null ? t.success([]) : t.array(createComment).decode(input),
t.identity
);
diff --git a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts
index b023e73cb9328b..612148dc4ccabc 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts
@@ -10,11 +10,11 @@ import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '../../siem_common_deps';
import { DefaultUpdateCommentsArray } from './default_update_comments_array';
-import { UpdateCommentsArray } from './update_comments';
-import { getUpdateCommentsArrayMock } from './update_comments.mock';
+import { UpdateCommentsArray } from './update_comment';
+import { getUpdateCommentsArrayMock } from './update_comment.mock';
describe('default_update_comments_array', () => {
- test('it should validate an empty array', () => {
+ test('it should pass validation when supplied an empty array', () => {
const payload: UpdateCommentsArray = [];
const decoded = DefaultUpdateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -23,7 +23,7 @@ describe('default_update_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should validate an array of comments', () => {
+ test('it should pass validation when supplied an array of comments', () => {
const payload: UpdateCommentsArray = getUpdateCommentsArrayMock();
const decoded = DefaultUpdateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -32,29 +32,26 @@ describe('default_update_comments_array', () => {
expect(message.schema).toEqual(payload);
});
- test('it should NOT validate an array of numbers', () => {
+ test('it should fail validation when supplied an array of numbers', () => {
const payload = [1];
const decoded = DefaultUpdateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
- // TODO: Known weird error formatting that is on our list to address
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
]);
expect(message.schema).toEqual({});
});
- test('it should NOT validate an array of strings', () => {
+ test('it should fail validation when supplied an array of strings', () => {
const payload = ['some string'];
const decoded = DefaultUpdateCommentsArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "some string" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "some string" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "some string" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
]);
expect(message.schema).toEqual({});
});
diff --git a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts
index 854b7cf7ada7e6..35338dae64387f 100644
--- a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts
+++ b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts
@@ -7,7 +7,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
-import { UpdateCommentsArray, updateCommentsArray } from './update_comments';
+import { UpdateCommentsArray, updateCommentsArray } from './update_comment';
/**
* Types the DefaultCommentsUpdate as:
diff --git a/x-pack/plugins/lists/common/schemas/types/index.ts b/x-pack/plugins/lists/common/schemas/types/index.ts
index 463f7cfe51ce3a..6b7e9fd17a1af6 100644
--- a/x-pack/plugins/lists/common/schemas/types/index.ts
+++ b/x-pack/plugins/lists/common/schemas/types/index.ts
@@ -3,9 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-export * from './comments';
-export * from './create_comments';
-export * from './update_comments';
+export * from './comment';
+export * from './create_comment';
+export * from './update_comment';
export * from './default_comments_array';
export * from './default_create_comments_array';
export * from './default_update_comments_array';
diff --git a/x-pack/plugins/lists/common/schemas/types/update_comments.mock.ts b/x-pack/plugins/lists/common/schemas/types/update_comment.mock.ts
similarity index 54%
rename from x-pack/plugins/lists/common/schemas/types/update_comments.mock.ts
rename to x-pack/plugins/lists/common/schemas/types/update_comment.mock.ts
index 3e963c2607dc53..9b85a24abe40b9 100644
--- a/x-pack/plugins/lists/common/schemas/types/update_comments.mock.ts
+++ b/x-pack/plugins/lists/common/schemas/types/update_comment.mock.ts
@@ -4,11 +4,16 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { getCommentsMock } from './comments.mock';
-import { getCreateCommentsMock } from './create_comments.mock';
-import { UpdateCommentsArray } from './update_comments';
+import { ID } from '../../constants.mock';
+
+import { UpdateComment, UpdateCommentsArray } from './update_comment';
+
+export const getUpdateCommentMock = (): UpdateComment => ({
+ comment: 'some comment',
+ id: ID,
+});
export const getUpdateCommentsArrayMock = (): UpdateCommentsArray => [
- getCommentsMock(),
- getCreateCommentsMock(),
+ getUpdateCommentMock(),
+ getUpdateCommentMock(),
];
diff --git a/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts b/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts
new file mode 100644
index 00000000000000..ac7716af40966d
--- /dev/null
+++ b/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts
@@ -0,0 +1,150 @@
+/*
+ * 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 { pipe } from 'fp-ts/lib/pipeable';
+import { left } from 'fp-ts/lib/Either';
+
+import { foldLeftRight, getPaths } from '../../siem_common_deps';
+
+import { getUpdateCommentMock, getUpdateCommentsArrayMock } from './update_comment.mock';
+import {
+ UpdateComment,
+ UpdateCommentsArray,
+ UpdateCommentsArrayOrUndefined,
+ updateComment,
+ updateCommentsArray,
+ updateCommentsArrayOrUndefined,
+} from './update_comment';
+
+describe('CommentsUpdate', () => {
+ describe('updateComment', () => {
+ test('it should pass validation when supplied typical comment update', () => {
+ const payload = getUpdateCommentMock();
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when supplied an undefined for "comment"', () => {
+ const payload = getUpdateCommentMock();
+ delete payload.comment;
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "comment"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when supplied an empty string for "comment"', () => {
+ const payload = { ...getUpdateCommentMock(), comment: '' };
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should pass validation when supplied an undefined for "id"', () => {
+ const payload = getUpdateCommentMock();
+ delete payload.id;
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when supplied an empty string for "id"', () => {
+ const payload = { ...getUpdateCommentMock(), id: '' };
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "id"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should strip out extra key passed in', () => {
+ const payload: UpdateComment & {
+ extraKey?: string;
+ } = { ...getUpdateCommentMock(), extraKey: 'some new value' };
+ const decoded = updateComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(getUpdateCommentMock());
+ });
+ });
+
+ describe('updateCommentsArray', () => {
+ test('it should pass validation when supplied an array of comments', () => {
+ const payload = getUpdateCommentsArrayMock();
+ const decoded = updateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when undefined', () => {
+ const payload = undefined;
+ const decoded = updateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when array includes non comments types', () => {
+ const payload = ([1] as unknown) as UpdateCommentsArray;
+ const decoded = updateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+
+ describe('updateCommentsArrayOrUndefined', () => {
+ test('it should pass validation when supplied an array of comments', () => {
+ const payload = getUpdateCommentsArrayMock();
+ const decoded = updateCommentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should pass validation when supplied when undefined', () => {
+ const payload = undefined;
+ const decoded = updateCommentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when array includes non comments types', () => {
+ const payload = ([1] as unknown) as UpdateCommentsArrayOrUndefined;
+ const decoded = updateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+});
diff --git a/x-pack/plugins/lists/common/schemas/types/update_comments.ts b/x-pack/plugins/lists/common/schemas/types/update_comment.ts
similarity index 58%
rename from x-pack/plugins/lists/common/schemas/types/update_comments.ts
rename to x-pack/plugins/lists/common/schemas/types/update_comment.ts
index 4a21bfa363d450..b95812cb35bf9a 100644
--- a/x-pack/plugins/lists/common/schemas/types/update_comments.ts
+++ b/x-pack/plugins/lists/common/schemas/types/update_comment.ts
@@ -5,10 +5,24 @@
*/
import * as t from 'io-ts';
-import { comments } from './comments';
-import { createComments } from './create_comments';
+import { NonEmptyString } from '../../siem_common_deps';
+import { id } from '../common/schemas';
-export const updateCommentsArray = t.array(t.union([comments, createComments]));
+export const updateComment = t.intersection([
+ t.exact(
+ t.type({
+ comment: NonEmptyString,
+ })
+ ),
+ t.exact(
+ t.partial({
+ id,
+ })
+ ),
+]);
+
+export type UpdateComment = t.TypeOf;
+export const updateCommentsArray = t.array(updateComment);
export type UpdateCommentsArray = t.TypeOf;
export const updateCommentsArrayOrUndefined = t.union([updateCommentsArray, t.undefined]);
export type UpdateCommentsArrayOrUndefined = t.TypeOf;
diff --git a/x-pack/plugins/lists/common/schemas/types/update_comments.test.ts b/x-pack/plugins/lists/common/schemas/types/update_comments.test.ts
deleted file mode 100644
index 7668504b031b5a..00000000000000
--- a/x-pack/plugins/lists/common/schemas/types/update_comments.test.ts
+++ /dev/null
@@ -1,108 +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 { pipe } from 'fp-ts/lib/pipeable';
-import { left } from 'fp-ts/lib/Either';
-
-import { foldLeftRight, getPaths } from '../../siem_common_deps';
-
-import { getUpdateCommentsArrayMock } from './update_comments.mock';
-import {
- UpdateCommentsArray,
- UpdateCommentsArrayOrUndefined,
- updateCommentsArray,
- updateCommentsArrayOrUndefined,
-} from './update_comments';
-import { getCommentsMock } from './comments.mock';
-import { getCreateCommentsMock } from './create_comments.mock';
-
-describe('CommentsUpdate', () => {
- describe('updateCommentsArray', () => {
- test('it should validate an array of comments', () => {
- const payload = getUpdateCommentsArrayMock();
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should validate an array of existing comments', () => {
- const payload = [getCommentsMock()];
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should validate an array of new comments', () => {
- const payload = [getCreateCommentsMock()];
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should not validate when undefined', () => {
- const payload = undefined;
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "undefined" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- ]);
- expect(message.schema).toEqual({});
- });
-
- test('it should not validate when array includes non comments types', () => {
- const payload = ([1] as unknown) as UpdateCommentsArray;
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- ]);
- expect(message.schema).toEqual({});
- });
- });
-
- describe('updateCommentsArrayOrUndefined', () => {
- test('it should validate an array of comments', () => {
- const payload = getUpdateCommentsArrayMock();
- const decoded = updateCommentsArrayOrUndefined.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should validate when undefined', () => {
- const payload = undefined;
- const decoded = updateCommentsArrayOrUndefined.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should not validate when array includes non comments types', () => {
- const payload = ([1] as unknown) as UpdateCommentsArrayOrUndefined;
- const decoded = updateCommentsArray.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- 'Invalid value "1" supplied to "Array<(({| comment: string, created_at: string, created_by: string |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: string |})>"',
- ]);
- expect(message.schema).toEqual({});
- });
- });
-});
diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts
index dc0a9aa5926ef1..1f6c65919b063a 100644
--- a/x-pack/plugins/lists/common/shared_exports.ts
+++ b/x-pack/plugins/lists/common/shared_exports.ts
@@ -8,8 +8,8 @@ export {
ListSchema,
CommentsArray,
CreateCommentsArray,
- Comments,
- CreateComments,
+ Comment,
+ CreateComment,
ExceptionListSchema,
ExceptionListItemSchema,
CreateExceptionListSchema,
@@ -28,6 +28,7 @@ export {
OperatorType,
OperatorTypeEnum,
ExceptionListTypeEnum,
+ comment,
exceptionListItemSchema,
exceptionListType,
createExceptionListItemSchema,
diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts
index 293435b3f62023..f5e0e7ae757005 100644
--- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts
+++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts
@@ -14,6 +14,7 @@ import {
exceptionListItemSchema,
updateExceptionListItemSchema,
} from '../../common/schemas';
+import { updateExceptionListItemValidate } from '../../common/schemas/request/update_exception_list_item_validation';
import { getExceptionListClient } from '.';
@@ -33,6 +34,11 @@ export const updateExceptionListItemRoute = (router: IRouter): void => {
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
+ const validationErrors = updateExceptionListItemValidate(request.body);
+ if (validationErrors.length) {
+ return siemResponse.error({ body: validationErrors, statusCode: 400 });
+ }
+
try {
const {
description,
diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts
index 3bde3545837cf1..f9e408833e0697 100644
--- a/x-pack/plugins/lists/server/saved_objects/exception_list.ts
+++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts
@@ -83,6 +83,9 @@ export const exceptionListItemMapping: SavedObjectsType['mappings'] = {
created_by: {
type: 'keyword',
},
+ id: {
+ type: 'keyword',
+ },
updated_at: {
type: 'keyword',
},
diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json
index da345fb930c048..81db9092775955 100644
--- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json
+++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json
@@ -1,17 +1,18 @@
{
- "item_id": "simple_list_item",
- "_tags": ["endpoint", "process", "malware", "os:windows"],
- "tags": ["user added string for a tag", "malware"],
- "type": "simple",
- "description": "This is a sample change here this list",
- "name": "Sample Endpoint Exception List update change",
- "comments": [{ "comment": "this is a newly added comment" }],
+ "_tags": ["detection"],
+ "comments": [],
+ "description": "Test comments - exception list item",
"entries": [
{
- "field": "event.category",
- "operator": "included",
- "type": "match_any",
- "value": ["process", "malware"]
+ "field": "host.name",
+ "type": "match",
+ "value": "rock01",
+ "operator": "included"
}
- ]
+ ],
+ "item_id": "993f43f7-325d-4df3-9338-964e77c37053",
+ "name": "Test comments - exception list item",
+ "namespace_type": "single",
+ "tags": [],
+ "type": "simple"
}
diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts
index a90ec61aef4af9..47c21735b45f44 100644
--- a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts
+++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts
@@ -64,7 +64,10 @@ export const createExceptionListItem = async ({
}: CreateExceptionListItemOptions): Promise => {
const savedObjectType = getSavedObjectType({ namespaceType });
const dateNow = new Date().toISOString();
- const transformedComments = transformCreateCommentsToComments({ comments, user });
+ const transformedComments = transformCreateCommentsToComments({
+ incomingComments: comments,
+ user,
+ });
const savedObject = await savedObjectsClient.create(savedObjectType, {
_tags,
comments: transformedComments,
diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.test.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.test.ts
index 6f0c5195f2025d..e3d96a9c3f6d00 100644
--- a/x-pack/plugins/lists/server/services/exception_lists/utils.test.ts
+++ b/x-pack/plugins/lists/server/services/exception_lists/utils.test.ts
@@ -5,15 +5,11 @@
*/
import sinon from 'sinon';
import moment from 'moment';
+import uuid from 'uuid';
-import { USER } from '../../../common/constants.mock';
+import { transformCreateCommentsToComments, transformUpdateCommentsToComments } from './utils';
-import {
- isCommentEqual,
- transformCreateCommentsToComments,
- transformUpdateComments,
- transformUpdateCommentsToComments,
-} from './utils';
+jest.mock('uuid/v4');
describe('utils', () => {
const oldDate = '2020-03-17T20:34:51.337Z';
@@ -22,59 +18,43 @@ describe('utils', () => {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
+ ((uuid.v4 as unknown) as jest.Mock)
+ .mockImplementationOnce(() => '123')
+ .mockImplementationOnce(() => '456');
+
clock = sinon.useFakeTimers(unix);
});
afterEach(() => {
clock.restore();
+ jest.clearAllMocks();
+ jest.restoreAllMocks();
+ jest.resetAllMocks();
});
describe('#transformUpdateCommentsToComments', () => {
- test('it returns empty array if "comments" is undefined and no comments exist', () => {
+ test('it formats new comments', () => {
const comments = transformUpdateCommentsToComments({
- comments: undefined,
+ comments: [{ comment: 'Im a new comment' }],
existingComments: [],
user: 'lily',
});
- expect(comments).toEqual([]);
- });
-
- test('it formats newly added comments', () => {
- const comments = transformUpdateCommentsToComments({
- comments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'bane' },
- { comment: 'Im a new comment' },
- ],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'bane' },
- ],
- user: 'lily',
- });
-
expect(comments).toEqual([
- {
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'bane',
- },
{
comment: 'Im a new comment',
created_at: dateNow,
created_by: 'lily',
+ id: '123',
},
]);
});
- test('it formats multiple newly added comments', () => {
+ test('it formats new comments and preserves existing comments', () => {
const comments = transformUpdateCommentsToComments({
- comments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- { comment: 'Im a new comment' },
- { comment: 'Im another new comment' },
- ],
+ comments: [{ comment: 'Im an old comment', id: '1' }, { comment: 'Im a new comment' }],
existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
+ { comment: 'Im an old comment', created_at: oldDate, created_by: 'bane', id: '1' },
],
user: 'lily',
});
@@ -83,26 +63,23 @@ describe('utils', () => {
{
comment: 'Im an old comment',
created_at: oldDate,
- created_by: 'lily',
+ created_by: 'bane',
+ id: '1',
},
{
comment: 'Im a new comment',
created_at: dateNow,
created_by: 'lily',
- },
- {
- comment: 'Im another new comment',
- created_at: dateNow,
- created_by: 'lily',
+ id: '123',
},
]);
});
- test('it should not throw if comments match existing comments', () => {
+ test('it returns existing comments if empty array passed for "comments"', () => {
const comments = transformUpdateCommentsToComments({
- comments: [{ comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' }],
+ comments: [],
existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
+ { comment: 'Im an old comment', created_at: oldDate, created_by: 'bane', id: '1' },
],
user: 'lily',
});
@@ -111,170 +88,42 @@ describe('utils', () => {
{
comment: 'Im an old comment',
created_at: oldDate,
- created_by: 'lily',
+ created_by: 'bane',
+ id: '1',
},
]);
});
- test('it does not throw if user tries to update one of their own existing comments', () => {
+ test('it acts as append only, only modifying new comments', () => {
const comments = transformUpdateCommentsToComments({
- comments: [
- {
- comment: 'Im an old comment that is trying to be updated',
- created_at: oldDate,
- created_by: 'lily',
- },
- ],
+ comments: [{ comment: 'Im a new comment' }],
existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
+ { comment: 'Im an old comment', created_at: oldDate, created_by: 'bane', id: '1' },
],
user: 'lily',
});
expect(comments).toEqual([
{
- comment: 'Im an old comment that is trying to be updated',
+ comment: 'Im an old comment',
created_at: oldDate,
+ created_by: 'bane',
+ id: '1',
+ },
+ {
+ comment: 'Im a new comment',
+ created_at: dateNow,
created_by: 'lily',
- updated_at: dateNow,
- updated_by: 'lily',
+ id: '123',
},
]);
});
-
- test('it throws an error if user tries to update their comment, without passing in the "created_at" and "created_by" properties', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [
- {
- comment: 'Im an old comment that is trying to be updated',
- },
- ],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(
- `"When trying to update a comment, \\"created_at\\" and \\"created_by\\" must be present"`
- );
- });
-
- test('it throws an error if user tries to delete comments', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Comments cannot be deleted, only new comments may be added"`
- );
- });
-
- test('it throws if user tries to update existing comment timestamp', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [{ comment: 'Im an old comment', created_at: dateNow, created_by: 'lily' }],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'bane',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Not authorized to edit others comments"`);
- });
-
- test('it throws if user tries to update existing comment author', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [{ comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' }],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'me!' },
- ],
- user: 'bane',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Not authorized to edit others comments"`);
- });
-
- test('it throws if user tries to update an existing comment that is not their own', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [
- {
- comment: 'Im an old comment that is trying to be updated',
- created_at: oldDate,
- created_by: 'lily',
- },
- ],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'bane',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Not authorized to edit others comments"`);
- });
-
- test('it throws if user tries to update order of comments', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [
- { comment: 'Im a new comment' },
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(
- `"When trying to update a comment, \\"created_at\\" and \\"created_by\\" must be present"`
- );
- });
-
- test('it throws an error if user tries to add comment formatted as existing comment when none yet exist', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- { comment: 'Im a new comment' },
- ],
- existingComments: [],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Only new comments may be added"`);
- });
-
- test('it throws if empty comment exists', () => {
- expect(() =>
- transformUpdateCommentsToComments({
- comments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- { comment: ' ' },
- ],
- existingComments: [
- { comment: 'Im an old comment', created_at: oldDate, created_by: 'lily' },
- ],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Empty comments not allowed"`);
- });
});
describe('#transformCreateCommentsToComments', () => {
- test('it returns "undefined" if "comments" is "undefined"', () => {
- const comments = transformCreateCommentsToComments({
- comments: undefined,
- user: 'lily',
- });
-
- expect(comments).toBeUndefined();
- });
-
test('it formats newly added comments', () => {
const comments = transformCreateCommentsToComments({
- comments: [{ comment: 'Im a new comment' }, { comment: 'Im another new comment' }],
+ incomingComments: [{ comment: 'Im a new comment' }, { comment: 'Im another new comment' }],
user: 'lily',
});
@@ -283,178 +132,15 @@ describe('utils', () => {
comment: 'Im a new comment',
created_at: dateNow,
created_by: 'lily',
+ id: '123',
},
{
comment: 'Im another new comment',
created_at: dateNow,
created_by: 'lily',
+ id: '456',
},
]);
});
-
- test('it throws an error if user tries to add an empty comment', () => {
- expect(() =>
- transformCreateCommentsToComments({
- comments: [{ comment: ' ' }],
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Empty comments not allowed"`);
- });
- });
-
- describe('#transformUpdateComments', () => {
- test('it updates comment and adds "updated_at" and "updated_by" if content differs', () => {
- const comments = transformUpdateComments({
- comment: {
- comment: 'Im an old comment that is trying to be updated',
- created_at: oldDate,
- created_by: 'lily',
- },
- existingComment: {
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'lily',
- },
- user: 'lily',
- });
-
- expect(comments).toEqual({
- comment: 'Im an old comment that is trying to be updated',
- created_at: oldDate,
- created_by: 'lily',
- updated_at: dateNow,
- updated_by: 'lily',
- });
- });
-
- test('it does not update comment and add "updated_at" and "updated_by" if content is the same', () => {
- const comments = transformUpdateComments({
- comment: {
- comment: 'Im an old comment ',
- created_at: oldDate,
- created_by: 'lily',
- },
- existingComment: {
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'lily',
- },
- user: 'lily',
- });
-
- expect(comments).toEqual({
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'lily',
- });
- });
-
- test('it throws if user tries to update an existing comment that is not their own', () => {
- expect(() =>
- transformUpdateComments({
- comment: {
- comment: 'Im an old comment that is trying to be updated',
- created_at: oldDate,
- created_by: 'lily',
- },
- existingComment: {
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'lily',
- },
- user: 'bane',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Not authorized to edit others comments"`);
- });
-
- test('it throws if user tries to update an existing comments timestamp', () => {
- expect(() =>
- transformUpdateComments({
- comment: {
- comment: 'Im an old comment',
- created_at: dateNow,
- created_by: 'lily',
- },
- existingComment: {
- comment: 'Im an old comment',
- created_at: oldDate,
- created_by: 'lily',
- },
- user: 'lily',
- })
- ).toThrowErrorMatchingInlineSnapshot(`"Unable to update comment"`);
- });
- });
-
- describe('#isCommentEqual', () => {
- test('it returns false if "comment" values differ', () => {
- const result = isCommentEqual(
- {
- comment: 'some old comment',
- created_at: oldDate,
- created_by: USER,
- },
- {
- comment: 'some older comment',
- created_at: oldDate,
- created_by: USER,
- }
- );
-
- expect(result).toBeFalsy();
- });
-
- test('it returns false if "created_at" values differ', () => {
- const result = isCommentEqual(
- {
- comment: 'some old comment',
- created_at: oldDate,
- created_by: USER,
- },
- {
- comment: 'some old comment',
- created_at: dateNow,
- created_by: USER,
- }
- );
-
- expect(result).toBeFalsy();
- });
-
- test('it returns false if "created_by" values differ', () => {
- const result = isCommentEqual(
- {
- comment: 'some old comment',
- created_at: oldDate,
- created_by: USER,
- },
- {
- comment: 'some old comment',
- created_at: oldDate,
- created_by: 'lily',
- }
- );
-
- expect(result).toBeFalsy();
- });
-
- test('it returns true if comment values are equivalent', () => {
- const result = isCommentEqual(
- {
- comment: 'some old comment',
- created_at: oldDate,
- created_by: USER,
- },
- {
- created_at: oldDate,
- created_by: USER,
- // Disabling to assure that order doesn't matter
- // eslint-disable-next-line sort-keys
- comment: 'some old comment',
- }
- );
-
- expect(result).toBeTruthy();
- });
});
});
diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts
index b168fae741822f..836f642899086a 100644
--- a/x-pack/plugins/lists/server/services/exception_lists/utils.ts
+++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts
@@ -3,17 +3,14 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-
+import uuid from 'uuid';
import { SavedObject, SavedObjectsFindResponse, SavedObjectsUpdateResponse } from 'kibana/server';
import { NamespaceTypeArray } from '../../../common/schemas/types/default_namespace_array';
-import { ErrorWithStatusCode } from '../../error_with_status_code';
import {
- Comments,
CommentsArray,
- CommentsArrayOrUndefined,
- CreateComments,
- CreateCommentsArrayOrUndefined,
+ CreateComment,
+ CreateCommentsArray,
ExceptionListItemSchema,
ExceptionListSchema,
ExceptionListSoSchema,
@@ -21,7 +18,6 @@ import {
FoundExceptionListSchema,
NamespaceType,
UpdateCommentsArrayOrUndefined,
- comments as commentsSchema,
exceptionListItemType,
exceptionListType,
} from '../../../common/schemas';
@@ -296,17 +292,6 @@ export const transformSavedObjectsToFoundExceptionList = ({
};
};
-/*
- * Determines whether two comments are equal, this is a very
- * naive implementation, not meant to be used for deep equality of complex objects
- */
-export const isCommentEqual = (commentA: Comments, commentB: Comments): boolean => {
- const a = Object.values(commentA).sort().join();
- const b = Object.values(commentB).sort().join();
-
- return a === b;
-};
-
export const transformUpdateCommentsToComments = ({
comments,
existingComments,
@@ -316,90 +301,28 @@ export const transformUpdateCommentsToComments = ({
existingComments: CommentsArray;
user: string;
}): CommentsArray => {
- const newComments = comments ?? [];
+ const incomingComments = comments ?? [];
+ const newComments = incomingComments.filter((comment) => comment.id == null);
+ const newCommentsFormatted = transformCreateCommentsToComments({
+ incomingComments: newComments,
+ user,
+ });
- if (newComments.length < existingComments.length) {
- throw new ErrorWithStatusCode(
- 'Comments cannot be deleted, only new comments may be added',
- 403
- );
- } else {
- return newComments.flatMap((c, index) => {
- const existingComment = existingComments[index];
-
- if (commentsSchema.is(existingComment) && !commentsSchema.is(c)) {
- throw new ErrorWithStatusCode(
- 'When trying to update a comment, "created_at" and "created_by" must be present',
- 403
- );
- } else if (existingComment == null && commentsSchema.is(c)) {
- throw new ErrorWithStatusCode('Only new comments may be added', 403);
- } else if (
- commentsSchema.is(c) &&
- existingComment != null &&
- isCommentEqual(c, existingComment)
- ) {
- return existingComment;
- } else if (commentsSchema.is(c) && existingComment != null) {
- return transformUpdateComments({ comment: c, existingComment, user });
- } else {
- return transformCreateCommentsToComments({ comments: [c], user }) ?? [];
- }
- });
- }
-};
-
-export const transformUpdateComments = ({
- comment,
- existingComment,
- user,
-}: {
- comment: Comments;
- existingComment: Comments;
- user: string;
-}): Comments => {
- if (comment.created_by !== user) {
- // existing comment is being edited, can only be edited by author
- throw new ErrorWithStatusCode('Not authorized to edit others comments', 401);
- } else if (existingComment.created_at !== comment.created_at) {
- throw new ErrorWithStatusCode('Unable to update comment', 403);
- } else if (comment.comment.trim().length === 0) {
- throw new ErrorWithStatusCode('Empty comments not allowed', 403);
- } else if (comment.comment.trim() !== existingComment.comment) {
- const dateNow = new Date().toISOString();
-
- return {
- ...existingComment,
- comment: comment.comment,
- updated_at: dateNow,
- updated_by: user,
- };
- } else {
- return existingComment;
- }
+ return [...existingComments, ...newCommentsFormatted];
};
export const transformCreateCommentsToComments = ({
- comments,
+ incomingComments,
user,
}: {
- comments: CreateCommentsArrayOrUndefined;
+ incomingComments: CreateCommentsArray;
user: string;
-}): CommentsArrayOrUndefined => {
+}): CommentsArray => {
const dateNow = new Date().toISOString();
- if (comments != null) {
- return comments.map((c: CreateComments) => {
- if (c.comment.trim().length === 0) {
- throw new ErrorWithStatusCode('Empty comments not allowed', 403);
- } else {
- return {
- comment: c.comment,
- created_at: dateNow,
- created_by: user,
- };
- }
- });
- } else {
- return comments;
- }
+ return incomingComments.map((comment: CreateComment) => ({
+ comment: comment.comment,
+ created_at: dateNow,
+ created_by: user,
+ id: uuid.v4(),
+ }));
};
diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts
index 7fb94cea7b6129..e28d1969b39763 100644
--- a/x-pack/plugins/security_solution/common/shared_imports.ts
+++ b/x-pack/plugins/security_solution/common/shared_imports.ts
@@ -8,8 +8,8 @@ export {
ListSchema,
CommentsArray,
CreateCommentsArray,
- Comments,
- CreateComments,
+ Comment,
+ CreateComment,
ExceptionListSchema,
ExceptionListItemSchema,
CreateExceptionListSchema,
@@ -30,6 +30,7 @@ export {
ExceptionListTypeEnum,
exceptionListItemSchema,
exceptionListType,
+ comment,
createExceptionListItemSchema,
listSchema,
entry,
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx
index db2d0540971de7..22d14ec6bedb11 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx
@@ -16,13 +16,13 @@ import {
EuiCommentProps,
EuiText,
} from '@elastic/eui';
-import { Comments } from '../../../lists_plugin_deps';
+import { Comment } from '../../../shared_imports';
import * as i18n from './translations';
import { useCurrentUser } from '../../lib/kibana';
import { getFormattedComments } from './helpers';
interface AddExceptionCommentsProps {
- exceptionItemComments?: Comments[];
+ exceptionItemComments?: Comment[];
newCommentValue: string;
newCommentOnChange: (value: string) => void;
}
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx
index a4fe52eaacf4e1..0f7e5b24ed8f96 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx
@@ -38,7 +38,7 @@ import { useSignalIndex } from '../../../../detections/containers/detection_engi
import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list';
import { AddExceptionComments } from '../add_exception_comments';
import {
- enrichExceptionItemsWithComments,
+ enrichNewExceptionItemsWithComments,
enrichExceptionItemsWithOS,
defaultEndpointExceptionItems,
entryHasListType,
@@ -251,7 +251,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({
let enriched: Array = [];
enriched =
comment !== ''
- ? enrichExceptionItemsWithComments(exceptionItemsToAdd, [{ comment }])
+ ? enrichNewExceptionItemsWithComments(exceptionItemsToAdd, [{ comment }])
: exceptionItemsToAdd;
if (exceptionListType === 'endpoint') {
const osTypes = retrieveAlertOsTypes();
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx
index 1ec49425ce8fd0..734434484fb4cc 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx
@@ -392,7 +392,7 @@ export const ExceptionBuilder = ({
)}
{
- addError(error, { title: i18n.EDIT_EXCEPTION_ERROR });
- onCancel();
+ if (error.message.includes('Conflict')) {
+ setHasVersionConflict(true);
+ } else {
+ addError(error, { title: i18n.EDIT_EXCEPTION_ERROR });
+ onCancel();
+ }
},
[addError, onCancel]
);
@@ -147,8 +153,8 @@ export const EditExceptionModal = memo(function EditExceptionModal({
}, [shouldDisableBulkClose]);
const isSubmitButtonDisabled = useMemo(
- () => exceptionItemsToAdd.every((item) => item.entries.length === 0),
- [exceptionItemsToAdd]
+ () => exceptionItemsToAdd.every((item) => item.entries.length === 0) || hasVersionConflict,
+ [exceptionItemsToAdd, hasVersionConflict]
);
const handleBuilderOnChange = useCallback(
@@ -177,11 +183,15 @@ export const EditExceptionModal = memo(function EditExceptionModal({
);
const enrichExceptionItems = useCallback(() => {
- let enriched: Array = [];
- enriched = enrichExceptionItemsWithComments(exceptionItemsToAdd, [
- ...(exceptionItem.comments ? exceptionItem.comments : []),
- ...(comment !== '' ? [{ comment }] : []),
- ]);
+ const [exceptionItemToEdit] = exceptionItemsToAdd;
+ let enriched: Array = [
+ {
+ ...enrichExistingExceptionItemWithComments(exceptionItemToEdit, [
+ ...exceptionItem.comments,
+ ...(comment.trim() !== '' ? [{ comment }] : []),
+ ]),
+ },
+ ];
if (exceptionListType === 'endpoint') {
const osTypes = exceptionItem._tags ? getOperatingSystems(exceptionItem._tags) : [];
enriched = enrichExceptionItemsWithOS(enriched, osTypes);
@@ -222,7 +232,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({
listId={exceptionItem.list_id}
listNamespaceType={exceptionItem.namespace_type}
ruleName={ruleName}
- isOrDisabled={false}
+ isOrDisabled
isAndDisabled={false}
isNestedDisabled={false}
data-test-subj="edit-exception-modal-builder"
@@ -263,6 +273,14 @@ export const EditExceptionModal = memo(function EditExceptionModal({
>
)}
+ {hasVersionConflict && (
+
+
+
{i18n.VERSION_CONFLICT_ERROR_DESCRIPTION}
+
+
+ )}
+
{i18n.CANCEL}
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts
index 6c5cb733b7a73c..d09f0158b2e1db 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts
@@ -67,3 +67,18 @@ export const EXCEPTION_BUILDER_INFO = i18n.translate(
defaultMessage: "Alerts are generated when the rule's conditions are met, except when:",
}
);
+
+export const VERSION_CONFLICT_ERROR_TITLE = i18n.translate(
+ 'xpack.securitySolution.exceptions.editException.versionConflictTitle',
+ {
+ defaultMessage: 'Sorry, there was an error',
+ }
+);
+
+export const VERSION_CONFLICT_ERROR_DESCRIPTION = i18n.translate(
+ 'xpack.securitySolution.exceptions.editException.versionConflictDescription',
+ {
+ defaultMessage:
+ "It appears this exception was updated since you first selected to edit it. Try clicking 'Cancel' and editing the exception again.",
+ }
+);
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx
index 78936d5d0da6fc..5cb65ee6db8ffc 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx
@@ -18,7 +18,8 @@ import {
formatOperatingSystems,
getEntryValue,
formatExceptionItemForUpdate,
- enrichExceptionItemsWithComments,
+ enrichNewExceptionItemsWithComments,
+ enrichExistingExceptionItemWithComments,
enrichExceptionItemsWithOS,
entryHasListType,
entryHasNonEcsType,
@@ -35,14 +36,14 @@ import {
existsOperator,
doesNotExistOperator,
} from '../autocomplete/operators';
-import { OperatorTypeEnum, OperatorEnum, EntryNested } from '../../../lists_plugin_deps';
+import { OperatorTypeEnum, OperatorEnum, EntryNested } from '../../../shared_imports';
import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
import { getEntryMatchMock } from '../../../../../lists/common/schemas/types/entry_match.mock';
import { getEntryMatchAnyMock } from '../../../../../lists/common/schemas/types/entry_match_any.mock';
import { getEntryExistsMock } from '../../../../../lists/common/schemas/types/entry_exists.mock';
import { getEntryListMock } from '../../../../../lists/common/schemas/types/entry_list.mock';
-import { getCommentsArrayMock } from '../../../../../lists/common/schemas/types/comments.mock';
-import { ENTRIES } from '../../../../../lists/common/constants.mock';
+import { getCommentsArrayMock } from '../../../../../lists/common/schemas/types/comment.mock';
+import { ENTRIES, OLD_DATE_RELATIVE_TO_DATE_NOW } from '../../../../../lists/common/constants.mock';
import {
CreateExceptionListItemSchema,
ExceptionListItemSchema,
@@ -410,12 +411,52 @@ describe('Exception helpers', () => {
expect(result).toEqual(expected);
});
});
+ describe('#enrichExistingExceptionItemWithComments', () => {
+ test('it should return exception item with comments stripped of "created_by", "created_at", "updated_by", "updated_at" fields', () => {
+ const payload = getExceptionListItemSchemaMock();
+ const comments = [
+ {
+ comment: 'Im an existing comment',
+ created_at: OLD_DATE_RELATIVE_TO_DATE_NOW,
+ created_by: 'lily',
+ id: '1',
+ },
+ {
+ comment: 'Im another existing comment',
+ created_at: OLD_DATE_RELATIVE_TO_DATE_NOW,
+ created_by: 'lily',
+ id: '2',
+ },
+ {
+ comment: 'Im a new comment',
+ },
+ ];
+ const result = enrichExistingExceptionItemWithComments(payload, comments);
+ const expected = {
+ ...getExceptionListItemSchemaMock(),
+ comments: [
+ {
+ comment: 'Im an existing comment',
+ id: '1',
+ },
+ {
+ comment: 'Im another existing comment',
+ id: '2',
+ },
+ {
+ comment: 'Im a new comment',
+ },
+ ],
+ };
+ expect(result).toEqual(expected);
+ });
+ });
- describe('#enrichExceptionItemsWithComments', () => {
+ describe('#enrichNewExceptionItemsWithComments', () => {
test('it should add comments to an exception item', () => {
const payload = [getExceptionListItemSchemaMock()];
const comments = getCommentsArrayMock();
- const result = enrichExceptionItemsWithComments(payload, comments);
+ const result = enrichNewExceptionItemsWithComments(payload, comments);
const expected = [
{
...getExceptionListItemSchemaMock(),
@@ -428,7 +469,7 @@ describe('Exception helpers', () => {
test('it should add comments to multiple exception items', () => {
const payload = [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()];
const comments = getCommentsArrayMock();
- const result = enrichExceptionItemsWithComments(payload, comments);
+ const result = enrichNewExceptionItemsWithComments(payload, comments);
const expected = [
{
...getExceptionListItemSchemaMock(),
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx
index a54f20f56d56f7..ee45f9b5de1fa8 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx
@@ -20,13 +20,14 @@ import { EXCEPTION_OPERATORS, isOperator } from '../autocomplete/operators';
import { OperatorOption } from '../autocomplete/types';
import {
CommentsArray,
- Comments,
- CreateComments,
+ Comment,
+ CreateComment,
Entry,
ExceptionListItemSchema,
NamespaceType,
OperatorTypeEnum,
CreateExceptionListItemSchema,
+ comment,
entry,
entriesNested,
createExceptionListItemSchema,
@@ -34,7 +35,7 @@ import {
UpdateExceptionListItemSchema,
ExceptionListType,
EntryNested,
-} from '../../../lists_plugin_deps';
+} from '../../../shared_imports';
import { IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { validate } from '../../../../common/validate';
import { TimelineNonEcsData } from '../../../graphql/types';
@@ -140,16 +141,16 @@ export const getTagsInclude = ({
* @param comments ExceptionItem.comments
*/
export const getFormattedComments = (comments: CommentsArray): EuiCommentProps[] =>
- comments.map((comment) => ({
- username: comment.created_by,
- timestamp: moment(comment.created_at).format('on MMM Do YYYY @ HH:mm:ss'),
+ comments.map((commentItem) => ({
+ username: commentItem.created_by,
+ timestamp: moment(commentItem.created_at).format('on MMM Do YYYY @ HH:mm:ss'),
event: i18n.COMMENT_EVENT,
- timelineIcon: ,
- children: {comment.comment},
+ timelineIcon: ,
+ children: {commentItem.comment},
actions: (
),
@@ -271,11 +272,11 @@ export const prepareExceptionItemsForBulkClose = (
/**
* Adds new and existing comments to all new exceptionItems if not present already
* @param exceptionItems new or existing ExceptionItem[]
- * @param comments new Comments
+ * @param comments new Comment
*/
-export const enrichExceptionItemsWithComments = (
+export const enrichNewExceptionItemsWithComments = (
exceptionItems: Array,
- comments: Array
+ comments: Array
): Array => {
return exceptionItems.map((item: ExceptionListItemSchema | CreateExceptionListItemSchema) => {
return {
@@ -285,6 +286,36 @@ export const enrichExceptionItemsWithComments = (
});
};
+/**
+ * Adds new and existing comments to exceptionItem
+ * @param exceptionItem existing ExceptionItem
+ * @param comments array of comments that can include existing
+ * and new comments
+ */
+export const enrichExistingExceptionItemWithComments = (
+ exceptionItem: ExceptionListItemSchema | CreateExceptionListItemSchema,
+ comments: Array
+): ExceptionListItemSchema | CreateExceptionListItemSchema => {
+ const formattedComments = comments.map((item) => {
+ if (comment.is(item)) {
+ const { id, comment: existingComment } = item;
+ return {
+ id,
+ comment: existingComment,
+ };
+ } else {
+ return {
+ comment: item.comment,
+ };
+ }
+ });
+
+ return {
+ ...exceptionItem,
+ comments: formattedComments,
+ };
+};
+
/**
* Adds provided osTypes to all exceptionItems if not present already
* @param exceptionItems new or existing ExceptionItem[]
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx
index 8df7b51bb9d31b..ab6588b67d5baf 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx
@@ -12,7 +12,7 @@ import moment from 'moment-timezone';
import { ExceptionDetails } from './exception_details';
import { getExceptionListItemSchemaMock } from '../../../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
-import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comments.mock';
+import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comment.mock';
describe('ExceptionDetails', () => {
beforeEach(() => {
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx
index 56b029aaee81eb..fec7354855935f 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx
@@ -11,7 +11,7 @@ import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { ExceptionItem } from './';
import { getExceptionListItemSchemaMock } from '../../../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
-import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comments.mock';
+import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comment.mock';
addDecorator((storyFn) => (
({ eui: euiLightVars, darkMode: false })}>{storyFn()}
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx
index 90752f9450e4cf..c9def092fda474 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx
@@ -11,7 +11,7 @@ import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { ExceptionItem } from './';
import { getExceptionListItemSchemaMock } from '../../../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
-import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comments.mock';
+import { getCommentsArrayMock } from '../../../../../../../lists/common/schemas/types/comment.mock';
jest.mock('../../../../lib/kibana');
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
index 34dc47b9cd4110..16eaef4136983e 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
@@ -190,7 +190,8 @@ const ExceptionsViewerComponent = ({
const handleOnCancelExceptionModal = useCallback((): void => {
setCurrentModal(null);
- }, [setCurrentModal]);
+ handleFetchList();
+ }, [setCurrentModal, handleFetchList]);
const handleOnConfirmExceptionModal = useCallback((): void => {
setCurrentModal(null);
From ddff1c9ab9b0a36824ac0fdac97a957827cb8496 Mon Sep 17 00:00:00 2001
From: Steph Milovic
Date: Mon, 27 Jul 2020 17:50:46 -0600
Subject: [PATCH 008/102] [Security solution] Threat hunting test coverage
improvements (#73276)
---
.../components/markdown_editor/index.test.tsx | 49 ++++++
.../components/markdown_editor/index.tsx | 1 -
.../navigation/breadcrumbs/index.test.ts | 74 +++++++++
.../utils/timeline/use_show_timeline.test.tsx | 33 ++++
.../components/manage_timeline/index.test.tsx | 145 ++++++++++++++++++
.../components/manage_timeline/index.tsx | 12 +-
6 files changed, 308 insertions(+), 6 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/common/components/markdown_editor/index.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx
diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.test.tsx
new file mode 100644
index 00000000000000..b5e5b01189418b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.test.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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 { mount } from 'enzyme';
+import React from 'react';
+
+import { MarkdownEditor } from '.';
+import { TestProviders } from '../../mock';
+
+describe('Markdown Editor', () => {
+ const onChange = jest.fn();
+ const onCursorPositionUpdate = jest.fn();
+ const defaultProps = {
+ content: 'hello world',
+ onChange,
+ onCursorPositionUpdate,
+ };
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ test('it calls onChange with correct value', () => {
+ const wrapper = mount(
+
+
+
+ );
+ const newValue = 'a new string';
+ wrapper
+ .find(`[data-test-subj="textAreaInput"]`)
+ .first()
+ .simulate('change', { target: { value: newValue } });
+ expect(onChange).toBeCalledWith(newValue);
+ });
+ test('it calls onCursorPositionUpdate with correct args', () => {
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.find(`[data-test-subj="textAreaInput"]`).first().simulate('blur');
+ expect(onCursorPositionUpdate).toBeCalledWith({
+ start: 0,
+ end: 0,
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx
index c40b3910ec1523..d4ad4a11b60a32 100644
--- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx
@@ -103,7 +103,6 @@ export const MarkdownEditor = React.memo<{
end: e!.target!.selectionEnd ?? 0,
});
}
- return false;
},
[onCursorPositionUpdate]
);
diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
index 7e508c28c62dfa..89aa77106933e5 100644
--- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
+++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
@@ -36,6 +36,13 @@ const getMockObject = (
): RouteSpyState & TabNavigationProps => ({
detailName,
navTabs: {
+ case: {
+ disabled: false,
+ href: '/app/security/cases',
+ id: 'case',
+ name: 'Cases',
+ urlKey: 'case',
+ },
hosts: {
disabled: false,
href: '/app/security/hosts',
@@ -227,6 +234,73 @@ describe('Navigation Breadcrumbs', () => {
{ text: 'Flows', href: '' },
]);
});
+
+ test('should return Alerts breadcrumbs when supplied detection pathname', () => {
+ const breadcrumbs = getBreadcrumbsForRoute(
+ getMockObject('detections', '/', undefined),
+ getUrlForAppMock
+ );
+ expect(breadcrumbs).toEqual([
+ { text: 'Security', href: '/app/security/overview' },
+ {
+ text: 'Detections',
+ href:
+ "securitySolution:detections?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
+ },
+ ]);
+ });
+ test('should return Cases breadcrumbs when supplied case pathname', () => {
+ const breadcrumbs = getBreadcrumbsForRoute(
+ getMockObject('case', '/', undefined),
+ getUrlForAppMock
+ );
+ expect(breadcrumbs).toEqual([
+ { text: 'Security', href: '/app/security/overview' },
+ {
+ text: 'Cases',
+ href:
+ "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
+ },
+ ]);
+ });
+ test('should return Case details breadcrumbs when supplied case details pathname', () => {
+ const sampleCase = {
+ id: 'my-case-id',
+ name: 'Case name',
+ };
+ const breadcrumbs = getBreadcrumbsForRoute(
+ {
+ ...getMockObject('case', `/${sampleCase.id}`, sampleCase.id),
+ state: { caseTitle: sampleCase.name },
+ },
+ getUrlForAppMock
+ );
+ expect(breadcrumbs).toEqual([
+ { text: 'Security', href: '/app/security/overview' },
+ {
+ text: 'Cases',
+ href:
+ "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
+ },
+ {
+ text: sampleCase.name,
+ href: `securitySolution:case/${sampleCase.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
+ },
+ ]);
+ });
+ test('should return Admin breadcrumbs when supplied admin pathname', () => {
+ const breadcrumbs = getBreadcrumbsForRoute(
+ getMockObject('administration', '/', undefined),
+ getUrlForAppMock
+ );
+ expect(breadcrumbs).toEqual([
+ { text: 'Security', href: '/app/security/overview' },
+ {
+ text: 'Administration',
+ href: 'securitySolution:administration',
+ },
+ ]);
+ });
});
describe('setBreadcrumbs()', () => {
diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx
new file mode 100644
index 00000000000000..db6e2536ce558a
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { renderHook, act } from '@testing-library/react-hooks';
+import { useShowTimeline } from './use_show_timeline';
+import { globalNode } from '../../mock';
+
+describe('use show timeline', () => {
+ it('shows timeline for routes on default', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
+ await waitForNextUpdate();
+ const uninitializedTimeline = result.current;
+ expect(uninitializedTimeline).toEqual([true]);
+ });
+ });
+ it('hides timeline for blacklist routes', async () => {
+ await act(async () => {
+ Object.defineProperty(globalNode.window, 'location', {
+ value: {
+ pathname: `/cases/configure`,
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
+ await waitForNextUpdate();
+ const uninitializedTimeline = result.current;
+ expect(uninitializedTimeline).toEqual([false]);
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx
new file mode 100644
index 00000000000000..b918e5abc652b3
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx
@@ -0,0 +1,145 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+import { getTimelineDefaults, useTimelineManager, UseTimelineManager } from './';
+import { FilterManager } from '../../../../../../../src/plugins/data/public/query/filter_manager';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { TimelineRowAction } from '../timeline/body/actions';
+
+const isStringifiedComparisonEqual = (a: {}, b: {}): boolean =>
+ JSON.stringify(a) === JSON.stringify(b);
+
+describe('useTimelineManager', () => {
+ const setupMock = coreMock.createSetup();
+ const testId = 'coolness';
+ const timelineDefaults = getTimelineDefaults(testId);
+ const timelineRowActions = () => [];
+ const mockFilterManager = new FilterManager(setupMock.uiSettings);
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.restoreAllMocks();
+ });
+ it('initilizes an undefined timeline', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ const uninitializedTimeline = result.current.getManageTimelineById(testId);
+ expect(isStringifiedComparisonEqual(uninitializedTimeline, timelineDefaults)).toBeTruthy();
+ });
+ });
+ it('getIndexToAddById', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ const data = result.current.getIndexToAddById(testId);
+ expect(data).toEqual(timelineDefaults.indexToAdd);
+ });
+ });
+ it('setIndexToAdd', async () => {
+ await act(async () => {
+ const indexToAddArgs = { id: testId, indexToAdd: ['example'] };
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ result.current.initializeTimeline({
+ id: testId,
+ timelineRowActions,
+ });
+ result.current.setIndexToAdd(indexToAddArgs);
+ const data = result.current.getIndexToAddById(testId);
+ expect(data).toEqual(indexToAddArgs.indexToAdd);
+ });
+ });
+ it('setIsTimelineLoading', async () => {
+ await act(async () => {
+ const isLoadingArgs = { id: testId, isLoading: true };
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ result.current.initializeTimeline({
+ id: testId,
+ timelineRowActions,
+ });
+ let timeline = result.current.getManageTimelineById(testId);
+ expect(timeline.isLoading).toBeFalsy();
+ result.current.setIsTimelineLoading(isLoadingArgs);
+ timeline = result.current.getManageTimelineById(testId);
+ expect(timeline.isLoading).toBeTruthy();
+ });
+ });
+ it('setTimelineRowActions', async () => {
+ await act(async () => {
+ const timelineRowActionsEx = () => [
+ { id: 'wow', content: 'hey', displayType: 'icon', onClick: () => {} } as TimelineRowAction,
+ ];
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ result.current.initializeTimeline({
+ id: testId,
+ timelineRowActions,
+ });
+ let timeline = result.current.getManageTimelineById(testId);
+ expect(timeline.timelineRowActions).toEqual(timelineRowActions);
+ result.current.setTimelineRowActions({
+ id: testId,
+ timelineRowActions: timelineRowActionsEx,
+ });
+ timeline = result.current.getManageTimelineById(testId);
+ expect(timeline.timelineRowActions).toEqual(timelineRowActionsEx);
+ });
+ });
+ it('getTimelineFilterManager undefined on uninitialized', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ const data = result.current.getTimelineFilterManager(testId);
+ expect(data).toEqual(undefined);
+ });
+ });
+ it('getTimelineFilterManager defined at initialize', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ result.current.initializeTimeline({
+ id: testId,
+ timelineRowActions,
+ filterManager: mockFilterManager,
+ });
+ const data = result.current.getTimelineFilterManager(testId);
+ expect(data).toEqual(mockFilterManager);
+ });
+ });
+ it('isManagedTimeline returns false when unset and then true when set', async () => {
+ await act(async () => {
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useTimelineManager()
+ );
+ await waitForNextUpdate();
+ let data = result.current.isManagedTimeline(testId);
+ expect(data).toBeFalsy();
+ result.current.initializeTimeline({
+ id: testId,
+ timelineRowActions,
+ filterManager: mockFilterManager,
+ });
+ data = result.current.isManagedTimeline(testId);
+ expect(data).toBeTruthy();
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
index dba8506add0ade..a425f9b49add0d 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
@@ -137,7 +137,7 @@ const reducerManageTimeline = (
}
};
-interface UseTimelineManager {
+export interface UseTimelineManager {
getIndexToAddById: (id: string) => string[] | null;
getManageTimelineById: (id: string) => ManageTimeline;
getTimelineFilterManager: (id: string) => FilterManager | undefined;
@@ -152,7 +152,9 @@ interface UseTimelineManager {
}) => void;
}
-const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseTimelineManager => {
+export const useTimelineManager = (
+ manageTimelineForTesting?: ManageTimelineById
+): UseTimelineManager => {
const [state, dispatch] = useReducer<
(state: ManageTimelineById, action: ActionManageTimeline) => ManageTimelineById
>(reducerManageTimeline, manageTimelineForTesting ?? initManageTimeline);
@@ -241,12 +243,12 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
};
const init = {
- getManageTimelineById: (id: string) => getTimelineDefaults(id),
getIndexToAddById: (id: string) => null,
+ getManageTimelineById: (id: string) => getTimelineDefaults(id),
getTimelineFilterManager: () => undefined,
- setIndexToAdd: () => undefined,
- isManagedTimeline: () => false,
initializeTimeline: () => noop,
+ isManagedTimeline: () => false,
+ setIndexToAdd: () => undefined,
setIsTimelineLoading: () => noop,
setTimelineRowActions: () => noop,
};
From ef83e772ca0357932c53dedfbb3ce68dc2361f55 Mon Sep 17 00:00:00 2001
From: Michael Olorunnisola
Date: Mon, 27 Jul 2020 20:03:23 -0400
Subject: [PATCH 009/102] [Security Solution][Resolver] Show origin node
details in panel on load (#73313)
* show origin node details in panel on load
* added comment
Co-authored-by: Elastic Machine
---
.../public/resolver/view/map.tsx | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/resolver/view/map.tsx b/x-pack/plugins/security_solution/public/resolver/view/map.tsx
index 30aa4b63a138d6..19c403f1257bec 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/map.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/map.tsx
@@ -8,7 +8,7 @@
/* eslint-disable react/display-name */
-import React, { useContext } from 'react';
+import React, { useContext, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useEffectOnce } from 'react-use';
import { EuiLoadingSpinner } from '@elastic/eui';
@@ -68,11 +68,25 @@ export const ResolverMap = React.memo(function ({
const hasError = useSelector(selectors.hasError);
const activeDescendantId = useSelector(selectors.ariaActiveDescendant);
const { colorMap } = useResolverTheme();
- const { cleanUpQueryParams } = useResolverQueryParams();
+ const {
+ cleanUpQueryParams,
+ queryParams: { crumbId },
+ pushToQueryParams,
+ } = useResolverQueryParams();
+
useEffectOnce(() => {
return () => cleanUpQueryParams();
});
+ useEffect(() => {
+ // When you refresh the page after selecting a process in the table view (not the timeline view)
+ // The old crumbId still exists in the query string even though a resolver is no longer visible
+ // This just makes sure the activeDescendant and crumbId are in sync on load for that view as well as the timeline
+ if (activeDescendantId && crumbId !== activeDescendantId) {
+ pushToQueryParams({ crumbId: activeDescendantId, crumbEvent: '' });
+ }
+ }, [crumbId, activeDescendantId, pushToQueryParams]);
+
return (
{isLoading ? (
From 8c52d39b9e757471f472a36eea30cdace30fd3ff Mon Sep 17 00:00:00 2001
From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com>
Date: Mon, 27 Jul 2020 20:34:08 -0400
Subject: [PATCH 010/102] [Security Solution] Show proper icon for termination
status of all processes (#73235)
* Show proper icon for termination status of all processes
* Add basic test for isProcessTerminated selector
---
.../resolver/store/data/selectors.test.ts | 29 +++++++++
.../public/resolver/store/data/selectors.ts | 13 ++++
.../resolver/store/mocks/endpoint_event.ts | 4 +-
.../resolver/store/mocks/resolver_tree.ts | 63 +++++++++++++++++++
.../public/resolver/store/selectors.ts | 8 +++
.../public/resolver/view/panel.tsx | 20 +-----
.../panels/panel_content_process_detail.tsx | 17 +++--
.../panels/panel_content_process_list.tsx | 14 ++---
.../view/panels/process_cube_icon.tsx | 4 +-
9 files changed, 131 insertions(+), 41 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
index 9e1c396723a274..0826391a106881 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
@@ -13,6 +13,7 @@ import {
mockTreeWithNoAncestorsAnd2Children,
mockTreeWith2AncestorsAndNoChildren,
mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents,
+ mockTreeWithAllProcessesTerminated,
} from '../mocks/resolver_tree';
import { uniquePidForProcess } from '../../models/process_event';
import { EndpointEvent } from '../../../../common/endpoint/types';
@@ -299,6 +300,34 @@ describe('data state', () => {
expect(selectors.ariaFlowtoCandidate(state())(secondAncestorID)).toBe(null);
});
});
+ describe('with a tree with all processes terminated', () => {
+ const originID = 'c';
+ const firstAncestorID = 'b';
+ const secondAncestorID = 'a';
+ beforeEach(() => {
+ actions.push({
+ type: 'serverReturnedResolverData',
+ payload: {
+ result: mockTreeWithAllProcessesTerminated({
+ originID,
+ firstAncestorID,
+ secondAncestorID,
+ }),
+ // this value doesn't matter
+ databaseDocumentID: '',
+ },
+ });
+ });
+ it('should have origin as terminated', () => {
+ expect(selectors.isProcessTerminated(state())(originID)).toBe(true);
+ });
+ it('should have first ancestor as termianted', () => {
+ expect(selectors.isProcessTerminated(state())(firstAncestorID)).toBe(true);
+ });
+ it('should have second ancestor as terminated', () => {
+ expect(selectors.isProcessTerminated(state())(secondAncestorID)).toBe(true);
+ });
+ });
describe('with a tree with 2 children and no ancestors', () => {
const originID = 'c';
const firstChildID = 'd';
diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
index 1d65b406306a36..ea0cb8663d11d0 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
@@ -105,6 +105,19 @@ export const terminatedProcesses = createSelector(resolverTreeResponse, function
);
});
+/**
+ * A function that given an entity id returns a boolean indicating if the id is in the set of terminated processes.
+ */
+export const isProcessTerminated = createSelector(terminatedProcesses, function (
+ /* eslint-disable no-shadow */
+ terminatedProcesses
+ /* eslint-enable no-shadow */
+) {
+ return (entityId: string) => {
+ return terminatedProcesses.has(entityId);
+ };
+});
+
/**
* Process events that will be graphed.
*/
diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
index b58ea73e1fdc74..8f2e0ad3a6d858 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
@@ -14,16 +14,18 @@ export function mockEndpointEvent({
name,
parentEntityId,
timestamp,
+ lifecycleType,
}: {
entityID: string;
name: string;
parentEntityId: string | undefined;
timestamp: number;
+ lifecycleType?: string;
}): EndpointEvent {
return {
'@timestamp': timestamp,
event: {
- type: 'start',
+ type: lifecycleType ? lifecycleType : 'start',
category: 'process',
},
process: {
diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
index 2860eec5a6ab6d..ae43955f4c47c7 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
@@ -46,6 +46,69 @@ export function mockTreeWith2AncestorsAndNoChildren({
} as unknown) as ResolverTree;
}
+export function mockTreeWithAllProcessesTerminated({
+ originID,
+ firstAncestorID,
+ secondAncestorID,
+}: {
+ secondAncestorID: string;
+ firstAncestorID: string;
+ originID: string;
+}): ResolverTree {
+ const secondAncestor: ResolverEvent = mockEndpointEvent({
+ entityID: secondAncestorID,
+ name: 'a',
+ parentEntityId: 'none',
+ timestamp: 0,
+ });
+ const firstAncestor: ResolverEvent = mockEndpointEvent({
+ entityID: firstAncestorID,
+ name: 'b',
+ parentEntityId: secondAncestorID,
+ timestamp: 1,
+ });
+ const originEvent: ResolverEvent = mockEndpointEvent({
+ entityID: originID,
+ name: 'c',
+ parentEntityId: firstAncestorID,
+ timestamp: 2,
+ });
+ const secondAncestorTermination: ResolverEvent = mockEndpointEvent({
+ entityID: secondAncestorID,
+ name: 'a',
+ parentEntityId: 'none',
+ timestamp: 0,
+ lifecycleType: 'end',
+ });
+ const firstAncestorTermination: ResolverEvent = mockEndpointEvent({
+ entityID: firstAncestorID,
+ name: 'b',
+ parentEntityId: secondAncestorID,
+ timestamp: 1,
+ lifecycleType: 'end',
+ });
+ const originEventTermination: ResolverEvent = mockEndpointEvent({
+ entityID: originID,
+ name: 'c',
+ parentEntityId: firstAncestorID,
+ timestamp: 2,
+ lifecycleType: 'end',
+ });
+ return ({
+ entityID: originID,
+ children: {
+ childNodes: [],
+ },
+ ancestry: {
+ ancestors: [
+ { lifecycle: [secondAncestor, secondAncestorTermination] },
+ { lifecycle: [firstAncestor, firstAncestorTermination] },
+ ],
+ },
+ lifecycle: [originEvent, originEventTermination],
+ } as unknown) as ResolverTree;
+}
+
export function mockTreeWithNoAncestorsAnd2Children({
originID,
firstChildID,
diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
index 66d7e04d118ede..87ef8d5d095ef0 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
@@ -53,6 +53,14 @@ export const userIsPanning = composeSelectors(cameraStateSelector, cameraSelecto
*/
export const isAnimating = composeSelectors(cameraStateSelector, cameraSelectors.isAnimating);
+/**
+ * Whether or not a given entity id is in the set of termination events.
+ */
+export const isProcessTerminated = composeSelectors(
+ dataStateSelector,
+ dataSelectors.isProcessTerminated
+);
+
/**
* Given a nodeID (aka entity_id) get the indexed process event.
* Legacy functions take process events instead of nodeID, use this to get
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
index cb0acdc29ceb11..83d3930065da61 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
@@ -162,19 +162,10 @@ const PanelContent = memo(function PanelContent() {
return 'processListWithCounts';
}, [uiSelectedEvent, crumbEvent, crumbId, graphableProcessEntityIds]);
- const terminatedProcesses = useSelector(selectors.terminatedProcesses);
- const processEntityId = uiSelectedEvent ? event.entityId(uiSelectedEvent) : undefined;
- const isProcessTerminated = processEntityId ? terminatedProcesses.has(processEntityId) : false;
-
const panelInstance = useMemo(() => {
if (panelToShow === 'processDetails') {
return (
-
+
);
}
@@ -213,13 +204,7 @@ const PanelContent = memo(function PanelContent() {
);
}
// The default 'Event List' / 'List of all processes' view
- return (
-
- );
+ return ;
}, [
uiSelectedEvent,
crumbEvent,
@@ -227,7 +212,6 @@ const PanelContent = memo(function PanelContent() {
pushToQueryParams,
relatedStatsForIdFromParams,
panelToShow,
- isProcessTerminated,
]);
return <>{panelInstance}>;
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx
index 5d90cd11d31af4..29c7676d2167de 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { memo, useMemo } from 'react';
+import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import {
htmlIdGenerator,
@@ -15,6 +16,7 @@ import {
} from '@elastic/eui';
import styled from 'styled-components';
import { FormattedMessage } from 'react-intl';
+import * as selectors from '../../store/selectors';
import * as event from '../../../../common/endpoint/models/event';
import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities';
import {
@@ -41,16 +43,14 @@ const StyledDescriptionList = styled(EuiDescriptionList)`
*/
export const ProcessDetails = memo(function ProcessDetails({
processEvent,
- isProcessTerminated,
- isProcessOrigin,
pushToQueryParams,
}: {
processEvent: ResolverEvent;
- isProcessTerminated: boolean;
- isProcessOrigin: boolean;
pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
}) {
const processName = event.eventName(processEvent);
+ const entityId = event.entityId(processEvent);
+ const isProcessTerminated = useSelector(selectors.isProcessTerminated)(entityId);
const processInfoEntry = useMemo(() => {
const eventTime = event.eventTimestamp(processEvent);
const dateTime = eventTime ? formatDate(eventTime) : '';
@@ -151,8 +151,8 @@ export const ProcessDetails = memo(function ProcessDetails({
if (!processEvent) {
return { descriptionText: '' };
}
- return cubeAssetsForNode(isProcessTerminated, isProcessOrigin);
- }, [processEvent, cubeAssetsForNode, isProcessTerminated, isProcessOrigin]);
+ return cubeAssetsForNode(isProcessTerminated, false);
+ }, [processEvent, cubeAssetsForNode, isProcessTerminated]);
const titleId = useMemo(() => htmlIdGenerator('resolverTable')(), []);
return (
@@ -161,10 +161,7 @@ export const ProcessDetails = memo(function ProcessDetails({
-
+
{processName}
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
index 6f9bfad8c08c23..efb96cde431e53 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
@@ -50,12 +50,8 @@ const StyledLimitWarning = styled(LimitWarning)`
*/
export const ProcessListWithCounts = memo(function ProcessListWithCounts({
pushToQueryParams,
- isProcessTerminated,
- isProcessOrigin,
}: {
pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
- isProcessTerminated: boolean;
- isProcessOrigin: boolean;
}) {
interface ProcessTableView {
name: string;
@@ -65,6 +61,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
const dispatch = useResolverDispatch();
const { timestamp } = useContext(SideEffectContext);
+ const isProcessTerminated = useSelector(selectors.isProcessTerminated);
const handleBringIntoViewClick = useCallback(
(processTableViewItem) => {
dispatch({
@@ -92,6 +89,8 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
sortable: true,
truncateText: true,
render(name: string, item: ProcessTableView) {
+ const entityId = event.entityId(item.event);
+ const isTerminated = isProcessTerminated(entityId);
return name === '' ? (
{i18n.translate(
@@ -108,10 +107,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
pushToQueryParams({ crumbId: event.entityId(item.event), crumbEvent: '' });
}}
>
-
+
{name}
);
@@ -143,7 +139,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
},
},
],
- [pushToQueryParams, handleBringIntoViewClick, isProcessOrigin, isProcessTerminated]
+ [pushToQueryParams, handleBringIntoViewClick, isProcessTerminated]
);
const { processNodePositions } = useSelector(selectors.layout);
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
index 98eea51a011b6f..b073324b27f9bd 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
@@ -13,13 +13,11 @@ import { useResolverTheme } from '../assets';
*/
export const CubeForProcess = memo(function CubeForProcess({
isProcessTerminated,
- isProcessOrigin,
}: {
isProcessTerminated: boolean;
- isProcessOrigin: boolean;
}) {
const { cubeAssetsForNode } = useResolverTheme();
- const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, isProcessOrigin);
+ const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false);
return (
<>
From 765c2d1ad3308a3c3af50f8d67b80579aeb13a9a Mon Sep 17 00:00:00 2001
From: Garrett Spong
Date: Mon, 27 Jul 2020 19:52:28 -0600
Subject: [PATCH 011/102] [Security Solution][ML] Updates siem group name to
security (#73218)
## Summary
Resolves https://github.com/elastic/kibana/issues/69319
Updates `siem` grouping to `security`, and enables cloudtrail module, fixing mis-match between the newly updated modules (https://github.com/elastic/kibana/pull/71696).
Also updates all module icons to be consistent:
Auditbeat (Before/After):
Packetbeat (Before/After):
Winlogbeat (Before/After):
- [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)
- [X] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials
- Working w/ @benskelker on updated ML Jobs & nomenclature
---
.../models/data_recognizer/modules/siem_auditbeat/logo.json | 2 +-
.../data_recognizer/modules/siem_auditbeat_auth/logo.json | 4 ++--
.../data_recognizer/modules/siem_packetbeat/logo.json | 4 ++--
.../data_recognizer/modules/siem_winlogbeat/logo.json | 2 +-
.../data_recognizer/modules/siem_winlogbeat_auth/logo.json | 4 ++--
.../public/common/components/ml_popover/api.tsx | 2 +-
.../common/components/ml_popover/hooks/translations.ts | 2 +-
.../components/ml_popover/hooks/use_siem_jobs_helpers.tsx | 2 +-
.../ml_popover/jobs_table/filters/groups_filter_popover.tsx | 6 +++---
.../public/common/components/ml_popover/ml_modules.tsx | 1 +
.../detections/components/rules/ml_job_select/index.tsx | 2 +-
.../server/usage/detections/detections_helpers.ts | 4 +++-
12 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/logo.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/logo.json
index 40a5c596771472..dfd22f6b1140b7 100644
--- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/logo.json
+++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/logo.json
@@ -1,3 +1,3 @@
{
- "icon": "securityAnalyticsApp"
+ "icon": "logoSecurity"
}
diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat_auth/logo.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat_auth/logo.json
index 6b02648ccf2877..dfd22f6b1140b7 100644
--- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat_auth/logo.json
+++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat_auth/logo.json
@@ -1,3 +1,3 @@
{
- "icon": "securityAnalyticsApp"
-}
\ No newline at end of file
+ "icon": "logoSecurity"
+}
diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_packetbeat/logo.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_packetbeat/logo.json
index 6b02648ccf2877..dfd22f6b1140b7 100644
--- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_packetbeat/logo.json
+++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_packetbeat/logo.json
@@ -1,3 +1,3 @@
{
- "icon": "securityAnalyticsApp"
-}
\ No newline at end of file
+ "icon": "logoSecurity"
+}
diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/logo.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/logo.json
index 40a5c596771472..dfd22f6b1140b7 100644
--- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/logo.json
+++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/logo.json
@@ -1,3 +1,3 @@
{
- "icon": "securityAnalyticsApp"
+ "icon": "logoSecurity"
}
diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat_auth/logo.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat_auth/logo.json
index 6b02648ccf2877..dfd22f6b1140b7 100644
--- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat_auth/logo.json
+++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat_auth/logo.json
@@ -1,3 +1,3 @@
{
- "icon": "securityAnalyticsApp"
-}
\ No newline at end of file
+ "icon": "logoSecurity"
+}
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.tsx
index b4da4fa79e035a..7c72098209a066 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.tsx
@@ -71,7 +71,7 @@ export const setupMlJob = async ({
configTemplate,
indexPatternName = 'auditbeat-*',
jobIdErrorFilter = [],
- groups = ['siem'],
+ groups = ['security'],
prefix = '',
}: MlSetupArgs): Promise => {
const response = await KibanaServices.get().http.fetch(
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/translations.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/translations.ts
index 2b37c437866e0c..7b29bab2e38f36 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/translations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/translations.ts
@@ -9,6 +9,6 @@ import { i18n } from '@kbn/i18n';
export const SIEM_JOB_FETCH_FAILURE = i18n.translate(
'xpack.securitySolution.components.mlPopup.hooks.errors.siemJobFetchFailureTitle',
{
- defaultMessage: 'SIEM job fetch failure',
+ defaultMessage: 'Security job fetch failure',
}
);
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs_helpers.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs_helpers.tsx
index 658d2659282ce4..adbd712ffeb3e0 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs_helpers.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs_helpers.tsx
@@ -104,7 +104,7 @@ export const getInstalledJobs = (
compatibleModuleIds: string[]
): SiemJob[] =>
jobSummaryData
- .filter(({ groups }) => groups.includes('siem'))
+ .filter(({ groups }) => groups.includes('siem') || groups.includes('security'))
.map((jobSummary) => ({
...jobSummary,
...getAugmentedFields(jobSummary.id, moduleJobs, compatibleModuleIds),
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
index 1aa3ad630306e6..d879942b8b1014 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
@@ -25,8 +25,8 @@ interface GroupsFilterPopoverProps {
/**
* Popover for selecting which SiemJob groups to filter on. Component extracts unique groups and
- * their counts from the provided SiemJobs. The 'siem' group is filtered out as all jobs will be
- * siem jobs
+ * their counts from the provided SiemJobs. The 'siem' & 'security' groups are filtered out as all jobs will be
+ * siem/security jobs
*
* @param siemJobs jobs to fetch groups from to display for filtering
* @param onSelectedGroupsChanged change listener to be notified when group selection changes
@@ -41,7 +41,7 @@ export const GroupsFilterPopoverComponent = ({
const groups = siemJobs
.map((j) => j.groups)
.flat()
- .filter((g) => g !== 'siem');
+ .filter((g) => g !== 'siem' && g !== 'security');
const uniqueGroups = Array.from(new Set(groups));
useEffect(() => {
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_modules.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_modules.tsx
index b956cf2c1494c2..4dccba08590a4e 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_modules.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_modules.tsx
@@ -12,6 +12,7 @@
export const mlModules: string[] = [
'siem_auditbeat',
'siem_auditbeat_auth',
+ 'siem_cloudtrail',
'siem_packetbeat',
'siem_winlogbeat',
'siem_winlogbeat_auth',
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx
index cb084d4daa7829..cdfdf4ca6b66bf 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx
@@ -41,7 +41,7 @@ const HelpText: React.FC<{ href: string; showEnableWarning: boolean }> = ({
<>
diff --git a/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts b/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts
index e9d4f3aa426f4a..f9905c373291c4 100644
--- a/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts
+++ b/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts
@@ -176,7 +176,9 @@ export const getMlJobsUsage = async (ml: MlPluginSetup | undefined): Promise module.jobs);
- const jobs = await ml.jobServiceProvider(internalMlClient, fakeRequest).jobsSummary(['siem']);
+ const jobs = await ml
+ .jobServiceProvider(internalMlClient, fakeRequest)
+ .jobsSummary(['siem', 'security']);
jobsUsage = jobs.reduce((usage, job) => {
const isElastic = moduleJobs.some((moduleJob) => moduleJob.id === job.id);
From 5af2c1080a85b247324d7b1fd36428c6d561ac55 Mon Sep 17 00:00:00 2001
From: Jen Huang
Date: Mon, 27 Jul 2020 19:21:14 -0700
Subject: [PATCH 012/102] Exclude `version` from package config attributes that
are copied, add safeguard to package config bulk create (#73128)
Co-authored-by: Elastic Machine
---
.../ingest_manager/server/services/agent_config.ts | 12 +++++-------
.../ingest_manager/server/services/package_config.ts | 5 ++++-
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts
index 0a9adc1f1c5934..3886146e28806e 100644
--- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts
+++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts
@@ -233,16 +233,14 @@ class AgentConfigService {
if (baseAgentConfig.package_configs.length) {
const newPackageConfigs = (baseAgentConfig.package_configs as PackageConfig[]).map(
(packageConfig: PackageConfig) => {
- const { id: packageConfigId, ...newPackageConfig } = packageConfig;
+ const { id: packageConfigId, version, ...newPackageConfig } = packageConfig;
return newPackageConfig;
}
);
- await packageConfigService.bulkCreate(
- soClient,
- newPackageConfigs,
- newAgentConfig.id,
- options
- );
+ await packageConfigService.bulkCreate(soClient, newPackageConfigs, newAgentConfig.id, {
+ ...options,
+ bumpConfigRevision: false,
+ });
}
// Get updated config
diff --git a/x-pack/plugins/ingest_manager/server/services/package_config.ts b/x-pack/plugins/ingest_manager/server/services/package_config.ts
index c2d465cf7c73f8..5d1c5d1717714c 100644
--- a/x-pack/plugins/ingest_manager/server/services/package_config.ts
+++ b/x-pack/plugins/ingest_manager/server/services/package_config.ts
@@ -121,7 +121,7 @@ class PackageConfigService {
options?: { user?: AuthenticatedUser; bumpConfigRevision?: boolean }
): Promise {
const isoDate = new Date().toISOString();
- const { saved_objects: newSos } = await soClient.bulkCreate(
+ const { saved_objects } = await soClient.bulkCreate(
packageConfigs.map((packageConfig) => ({
type: SAVED_OBJECT_TYPE,
attributes: {
@@ -136,6 +136,9 @@ class PackageConfigService {
}))
);
+ // Filter out invalid SOs
+ const newSos = saved_objects.filter((so) => !so.error && so.attributes);
+
// Assign it to the given agent config
await agentConfigService.assignPackageConfigs(
soClient,
From 82d7e7db699bbe961da5eb8b2218de5d2c2e7e18 Mon Sep 17 00:00:00 2001
From: Jen Huang
Date: Mon, 27 Jul 2020 19:21:41 -0700
Subject: [PATCH 013/102] [Ingest Manager] Convert select agent config step to
use combo box (#73172)
* Initial pass at using combo box instead of selectable for agent configs
* Hide agent count messaging if fleet isn't set up
* Fix types
* Fix i18n
* Fix i18n again
* Add comment explaining styling
Co-authored-by: Elastic Machine
---
.../step_select_config.tsx | 227 +++++++++++-------
.../list_page/components/create_config.tsx | 2 +-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
4 files changed, 145 insertions(+), 86 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx
index 91c80b7eee4c87..6f06530100d716 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx
@@ -3,17 +3,19 @@
* 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, { useEffect, useState, Fragment } from 'react';
+import React, { useEffect, useState } from 'react';
+import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFlexGroup,
EuiFlexItem,
- EuiSelectable,
- EuiSpacer,
+ EuiComboBox,
+ EuiComboBoxOptionOption,
EuiTextColor,
EuiPortal,
- EuiButtonEmpty,
+ EuiFormRow,
+ EuiLink,
} from '@elastic/eui';
import { Error } from '../../../components';
import { AgentConfig, PackageInfo, GetAgentConfigsResponseItem } from '../../../types';
@@ -23,9 +25,30 @@ import {
useGetAgentConfigs,
sendGetOneAgentConfig,
useCapabilities,
+ useFleetStatus,
} from '../../../hooks';
import { CreateAgentConfigFlyout } from '../list_page/components';
+const AgentConfigWrapper = styled(EuiFormRow)`
+ .euiFormRow__label {
+ width: 100%;
+ }
+`;
+
+// Custom styling for drop down list items due to:
+// 1) the max-width and overflow properties is added to prevent long config
+// names/descriptions from overflowing the flex items
+// 2) max-width is built from the grow property on the flex items because the value
+// changes based on if Fleet is enabled/setup or not
+const AgentConfigNameColumn = styled(EuiFlexItem)`
+ max-width: ${(props) => `${((props.grow as number) / 9) * 100}%`};
+ overflow: hidden;
+`;
+const AgentConfigDescriptionColumn = styled(EuiFlexItem)`
+ max-width: ${(props) => `${((props.grow as number) / 9) * 100}%`};
+ overflow: hidden;
+`;
+
export const StepSelectConfig: React.FunctionComponent<{
pkgkey: string;
updatePackageInfo: (packageInfo: PackageInfo | undefined) => void;
@@ -33,6 +56,8 @@ export const StepSelectConfig: React.FunctionComponent<{
updateAgentConfig: (config: AgentConfig | undefined) => void;
setIsLoadingSecondStep: (isLoading: boolean) => void;
}> = ({ pkgkey, updatePackageInfo, agentConfig, updateAgentConfig, setIsLoadingSecondStep }) => {
+ const { isReady: isFleetReady } = useFleetStatus();
+
// Selected config state
const [selectedConfigId, setSelectedConfigId] = useState(
agentConfig ? agentConfig.id : undefined
@@ -106,6 +131,40 @@ export const StepSelectConfig: React.FunctionComponent<{
}
}, [selectedConfigId, agentConfig, updateAgentConfig, setIsLoadingSecondStep]);
+ const agentConfigOptions: Array> = packageInfoData
+ ? agentConfigs.map((agentConf) => {
+ const alreadyHasLimitedPackage =
+ (isLimitedPackage &&
+ doesAgentConfigAlreadyIncludePackage(agentConf, packageInfoData.response.name)) ||
+ false;
+ return {
+ label: agentConf.name,
+ value: agentConf.id,
+ disabled: alreadyHasLimitedPackage,
+ 'data-test-subj': 'agentConfigItem',
+ };
+ })
+ : [];
+
+ const selectedConfigOption = agentConfigOptions.find(
+ (option) => option.value === selectedConfigId
+ );
+
+ // Try to select default agent config
+ useEffect(() => {
+ if (!selectedConfigId && agentConfigs.length && agentConfigOptions.length) {
+ const defaultAgentConfig = agentConfigs.find((config) => config.is_default);
+ if (defaultAgentConfig) {
+ const defaultAgentConfigOption = agentConfigOptions.find(
+ (option) => option.value === defaultAgentConfig.id
+ );
+ if (defaultAgentConfigOption && !defaultAgentConfigOption.disabled) {
+ setSelectedConfigId(defaultAgentConfig.id);
+ }
+ }
+ }
+ }, [agentConfigs, agentConfigOptions, selectedConfigId]);
+
// Display package error if there is one
if (packageInfoError) {
return (
@@ -154,77 +213,95 @@ export const StepSelectConfig: React.FunctionComponent<{
) : null}
- {
- const alreadyHasLimitedPackage =
- (isLimitedPackage &&
- packageInfoData &&
- doesAgentConfigAlreadyIncludePackage(agentConf, packageInfoData.response.name)) ||
- false;
- return {
- label: agentConf.name,
- key: agentConf.id,
- checked: selectedConfigId === agentConf.id ? 'on' : undefined,
- disabled: alreadyHasLimitedPackage,
- 'data-test-subj': 'agentConfigItem',
- };
- })}
- renderOption={(option) => (
-
- {option.label}
+
-
- {agentConfigsById[option.key!].description}
-
+
-
-
-
+
+ setIsCreateAgentConfigFlyoutOpen(true)}
+ >
+
+
+
- )}
- listProps={{
- bordered: true,
- }}
- searchProps={{
- placeholder: i18n.translate(
- 'xpack.ingestManager.createPackageConfig.StepSelectConfig.filterAgentConfigsInputPlaceholder',
+ }
+ helpText={
+ isFleetReady && selectedConfigId ? (
+
+ ) : null
+ }
+ >
+ {
- const selectedOption = options.find((option) => option.checked === 'on');
- if (selectedOption) {
- if (selectedOption.key !== selectedConfigId) {
- setSelectedConfigId(selectedOption.key);
+ )}
+ singleSelection={{ asPlainText: true }}
+ isClearable={false}
+ fullWidth={true}
+ isLoading={isAgentConfigsLoading || isPackageInfoLoading}
+ options={agentConfigOptions}
+ renderOption={(option: EuiComboBoxOptionOption) => {
+ return (
+
+
+ {option.label}
+
+
+
+ {agentConfigsById[option.value!].description}
+
+
+ {isFleetReady ? (
+
+
+
+
+
+ ) : null}
+
+ );
+ }}
+ selectedOptions={selectedConfigOption ? [selectedConfigOption] : []}
+ onChange={(options) => {
+ const selectedOption = options[0] || undefined;
+ if (selectedOption) {
+ if (selectedOption.value !== selectedConfigId) {
+ setSelectedConfigId(selectedOption.value);
+ }
+ } else {
+ setSelectedConfigId(undefined);
}
- } else {
- setSelectedConfigId(undefined);
- }
- }}
- >
- {(list, search) => (
-
- {search}
-
- {list}
-
- )}
-
+ }}
+ />
+
{/* Display selected agent config error if there is one */}
{selectedConfigError ? (
@@ -240,22 +317,6 @@ export const StepSelectConfig: React.FunctionComponent<{
/>
) : null}
-
-
- setIsCreateAgentConfigFlyoutOpen(true)}
- flush="left"
- size="s"
- >
-
-
-
-
>
);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
index fc593705a4e1b5..749716b473c858 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
@@ -160,7 +160,7 @@ export const CreateAgentConfigFlyout: React.FunctionComponent = ({
);
return (
-
+ onClose()} size="l" maxWidth={400} {...restOfProps}>
{header}
{body}
{footer}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index cf79f463b35cb9..ee7d1e0298d001 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -8108,7 +8108,6 @@
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingAgentConfigsTitle": "エージェント構成の読み込みエラー",
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingPackageTitle": "パッケージ情報の読み込みエラー",
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingSelectedAgentConfigTitle": "選択したエージェント構成の読み込みエラー",
- "xpack.ingestManager.createPackageConfig.StepSelectConfig.filterAgentConfigsInputPlaceholder": "エージェント構成の検索",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingConfigTitle": "エージェント構成情報の読み込みエラー",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingPackagesTitle": "統合の読み込みエラー",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingSelectedPackageTitle": "選択した統合の読み込みエラー",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index b45fe1baa9e9a9..30c932c362a4f5 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -8113,7 +8113,6 @@
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingAgentConfigsTitle": "加载代理配置时出错",
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingPackageTitle": "加载软件包信息时出错",
"xpack.ingestManager.createPackageConfig.StepSelectConfig.errorLoadingSelectedAgentConfigTitle": "加载选定代理配置时出错",
- "xpack.ingestManager.createPackageConfig.StepSelectConfig.filterAgentConfigsInputPlaceholder": "搜索代理配置",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingConfigTitle": "加载代理配置信息时出错",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingPackagesTitle": "加载集成时出错",
"xpack.ingestManager.createPackageConfig.stepSelectPackage.errorLoadingSelectedPackageTitle": "加载选定集成时出错",
From cc84ee31856c8eb70d5a2d1093b21678d5842f88 Mon Sep 17 00:00:00 2001
From: Phillip Burch
Date: Mon, 27 Jul 2020 21:28:39 -0500
Subject: [PATCH 014/102] [Metrics UI] Saved views bugs (#72518)
* Add test for logs and metrics telemetry
* wait before you go
* Remove kubenetes
* Fix type check
* Add back kubernetes test
* Remove kubernetes
* Don't allow deleting default default view.
* Fix bug with duplicate loads of data.
Because the load data function takes options.source and the source of options can change, we need to remove it from deps
* Remove unused variable
* Reload when loadData function is changed
* Don't send the request immediately
Co-authored-by: Elastic Machine
---
.../public/components/saved_views/manage_views_flyout.tsx | 4 ++++
.../public/components/saved_views/toolbar_control.tsx | 2 +-
.../infra/public/containers/saved_view/saved_view.tsx | 8 +++-----
.../pages/metrics/inventory_view/components/layout.tsx | 3 ++-
.../infra/public/pages/metrics/metrics_explorer/index.tsx | 3 ++-
5 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx
index fa9b45558e4918..698034f8154d1b 100644
--- a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx
+++ b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx
@@ -96,6 +96,10 @@ export function SavedViewManageViewsFlyout({
const renderDeleteAction = useCallback(
(item: SavedView) => {
+ if (item.id === '0') {
+ return <>>;
+ }
+
return (
(props: Props) {
/>
-
+
{
const { data, loading, find, error: errorOnFind, hasView } = useFindSavedObject<
SavedViewSavedObject
>(viewType);
-
+ const [shouldLoadDefault] = useState(props.shouldLoadDefault);
const [currentView, setCurrentView] = useState | null>(null);
const [loadingDefaultView, setLoadingDefaultView] = useState(null);
const { create, error: errorOnCreate, data: createdViewData, createdId } = useCreateSavedObject(
@@ -211,8 +211,6 @@ export const useSavedView = (props: Props) => {
}, [setCurrentView, defaultViewId, defaultViewState]);
useEffect(() => {
- const shouldLoadDefault = props.shouldLoadDefault;
-
if (loadingDefaultView || currentView || !shouldLoadDefault) {
return;
}
@@ -225,7 +223,7 @@ export const useSavedView = (props: Props) => {
}
}, [
loadDefaultView,
- props.shouldLoadDefault,
+ shouldLoadDefault,
setDefault,
loadingDefaultView,
currentView,
@@ -246,7 +244,7 @@ export const useSavedView = (props: Props) => {
errorOnUpdate,
errorOnFind,
errorOnCreate: createError,
- shouldLoadDefault: props.shouldLoadDefault,
+ shouldLoadDefault,
makeDefault,
sourceIsLoading,
deleteView,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
index fddd92128708a4..ad92c054ee459d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
@@ -55,7 +55,8 @@ export const Layout = () => {
sourceId,
currentTime,
accountId,
- region
+ region,
+ false
);
const options = {
diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx
index cd875ae54071cb..20efca79650a1e 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx
@@ -57,7 +57,8 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl
// load metrics explorer data after default view loaded, unless we're not loading a view
loadData();
}
- }, [loadData, currentView, shouldLoadDefault]);
+ /* eslint-disable-next-line react-hooks/exhaustive-deps */
+ }, [loadData, shouldLoadDefault]);
return (
From 281c76767b21c458a237474d77211f18883d8d68 Mon Sep 17 00:00:00 2001
From: MadameSheema
Date: Tue, 28 Jul 2020 09:23:28 +0200
Subject: [PATCH 015/102] updates cypress to v4.11.0 (#73327)
Co-authored-by: Elastic Machine
---
x-pack/package.json | 2 +-
yarn.lock | 170 +++++++++++++++-----------------------------
2 files changed, 60 insertions(+), 112 deletions(-)
diff --git a/x-pack/package.json b/x-pack/package.json
index dee99d6f0ddac0..76655f75cadcce 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -131,7 +131,7 @@
"cheerio": "0.22.0",
"commander": "3.0.2",
"copy-webpack-plugin": "^6.0.2",
- "cypress": "4.5.0",
+ "cypress": "4.11.0",
"cypress-multi-reporters": "^1.2.3",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
diff --git a/yarn.lock b/yarn.lock
index 899bc45fbe3fbb..c1328731db1508 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4717,21 +4717,11 @@
resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.2.5.tgz#582b2476169a6cba460a214d476c744441d873d5"
integrity sha1-WCskdhaabLpGCiFNR2x0REHYc9U=
-"@types/blob-util@1.3.3":
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a"
- integrity sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==
-
"@types/bluebird@*", "@types/bluebird@^3.1.1":
version "3.5.30"
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.30.tgz#ee034a0eeea8b84ed868b1aa60d690b08a6cfbc5"
integrity sha512-8LhzvcjIoqoi1TghEkRMkbbmM+jhHnBokPGkJWjclMK+Ks0MxEBow3/p2/iFTZ+OIbJHQDSfpgdZEb+af3gfVw==
-"@types/bluebird@3.5.29":
- version "3.5.29"
- resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.29.tgz#7cd933c902c4fc83046517a1bef973886d00bdb6"
- integrity sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==
-
"@types/boom@*", "@types/boom@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/boom/-/boom-7.2.0.tgz#19c36cbb5811a7493f0f2e37f31d42b28df1abc1"
@@ -4762,15 +4752,7 @@
resolved "https://registry.yarnpkg.com/@types/catbox/-/catbox-10.0.1.tgz#266679017749041fe9873fee1131dd2aaa04a07e"
integrity sha512-ECuJ+f5gGHiLeiE4RlE/xdqv/0JVDToegPV1aTb10tQStYa0Ycq2OJfQukDv3IFaw3B+CMV46jHc5bXe6QXEQg==
-"@types/chai-jquery@1.1.40":
- version "1.1.40"
- resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.40.tgz#445bedcbbb2ae4e3027f46fa2c1733c43481ffa1"
- integrity sha512-mCNEZ3GKP7T7kftKeIs7QmfZZQM7hslGSpYzKbOlR2a2HCFf9ph4nlMRA9UnuOETeOQYJVhJQK7MwGqNZVyUtQ==
- dependencies:
- "@types/chai" "*"
- "@types/jquery" "*"
-
-"@types/chai@*", "@types/chai@4.2.7", "@types/chai@^4.2.11":
+"@types/chai@^4.2.11":
version "4.2.11"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.11.tgz#d3614d6c5f500142358e6ed24e1bf16657536c50"
integrity sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==
@@ -5260,7 +5242,7 @@
resolved "https://registry.yarnpkg.com/@types/joi/-/joi-13.6.1.tgz#325486a397504f8e22c8c551dc8b0e1d41d5d5ae"
integrity sha512-JxZ0NP8NuB0BJOXi1KvAA6rySLTPmhOy4n2gzSFq/IFM3LNFm0h+2Vn/bPPgEYlWqzS2NPeLgKqfm75baX+Hog==
-"@types/jquery@*", "@types/jquery@3.3.31", "@types/jquery@^3.3.31":
+"@types/jquery@*", "@types/jquery@^3.3.31":
version "3.3.31"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.31.tgz#27c706e4bf488474e1cb54a71d8303f37c93451b"
integrity sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==
@@ -5346,11 +5328,6 @@
"@types/node" "*"
"@types/webpack" "*"
-"@types/lodash@4.14.149", "@types/lodash@^4.14.155":
- version "4.14.156"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.156.tgz#cbe30909c89a1feeb7c60803e785344ea0ec82d1"
- integrity sha512-l2AgHXcKUwx2DsvP19wtRPqZ4NkONjmorOdq4sMcxIjqdIuuV/ULo2ftuv4NUpevwfW7Ju/UKLqo0ZXuEt/8lQ==
-
"@types/lodash@^3.10.1":
version "3.10.3"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-3.10.3.tgz#aaddec6a3c93bf03b402db3acf5d4c77bce8bdff"
@@ -5361,6 +5338,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.150.tgz#649fe44684c3f1fcb6164d943c5a61977e8cf0bd"
integrity sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w==
+"@types/lodash@^4.14.155":
+ version "4.14.156"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.156.tgz#cbe30909c89a1feeb7c60803e785344ea0ec82d1"
+ integrity sha512-l2AgHXcKUwx2DsvP19wtRPqZ4NkONjmorOdq4sMcxIjqdIuuV/ULo2ftuv4NUpevwfW7Ju/UKLqo0ZXuEt/8lQ==
+
"@types/log-symbols@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/log-symbols/-/log-symbols-2.0.0.tgz#7919e2ec3c8d13879bfdcab310dd7a3f7fc9466d"
@@ -5419,7 +5401,7 @@
dependencies:
"@types/mime-db" "*"
-"@types/minimatch@*", "@types/minimatch@3.0.3", "@types/minimatch@^3.0.3":
+"@types/minimatch@*", "@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
@@ -5441,11 +5423,6 @@
dependencies:
"@types/node" "*"
-"@types/mocha@5.2.7":
- version "5.2.7"
- resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
- integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
-
"@types/mocha@^7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
@@ -5859,32 +5836,12 @@
dependencies:
"@types/node" "*"
-"@types/sinon-chai@3.2.3":
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.3.tgz#afe392303dda95cc8069685d1e537ff434fa506e"
- integrity sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==
- dependencies:
- "@types/chai" "*"
- "@types/sinon" "*"
-
-"@types/sinon@*":
- version "9.0.4"
- resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.4.tgz#e934f904606632287a6e7f7ab0ce3f08a0dad4b1"
- integrity sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==
- dependencies:
- "@types/sinonjs__fake-timers" "*"
-
-"@types/sinon@7.5.1":
- version "7.5.1"
- resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.1.tgz#d27b81af0d1cfe1f9b24eebe7a24f74ae40f5b7c"
- integrity sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==
-
"@types/sinon@^7.0.13":
version "7.0.13"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.13.tgz#ca039c23a9e27ebea53e0901ef928ea2a1a6d313"
integrity sha512-d7c/C/+H/knZ3L8/cxhicHUiTDxdgap0b/aNJfsmLwFu/iOP17mdgbQsbHA3SJmrzsjD0l3UEE5SN4xxuz5ung==
-"@types/sinonjs__fake-timers@*":
+"@types/sinonjs__fake-timers@6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e"
integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==
@@ -7378,10 +7335,10 @@ aproba@^1.0.3, aproba@^1.1.1:
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
-arch@2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e"
- integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==
+arch@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
+ integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
archiver-utils@^2.1.0:
version "2.1.0"
@@ -7849,7 +7806,7 @@ async@^2.6.3:
dependencies:
lodash "^4.17.14"
-async@^3.1.0:
+async@^3.1.0, async@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
@@ -10499,10 +10456,10 @@ commander@3.0.2:
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
-commander@4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
- integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
+commander@4.1.1, commander@^4.0.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
+ integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0:
version "2.20.0"
@@ -10524,11 +10481,6 @@ commander@^3.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.0.tgz#0641ea00838c7a964627f04cddc336a2deddd60a"
integrity sha512-pl3QrGOBa9RZaslQiqnnKX2J068wcQw7j9AIaBQ9/JEp5RY6je4jKTImg0Bd+rpoONSe7GUFSgkxLeo17m3Pow==
-commander@^4.0.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
- integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
-
commander@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.0.0.tgz#dbf1909b49e5044f8fdaf0adc809f0c0722bdfd0"
@@ -11489,48 +11441,39 @@ cypress-multi-reporters@^1.2.3:
debug "^4.1.1"
lodash "^4.17.11"
-cypress@4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.5.0.tgz#01940d085f6429cec3c87d290daa47bb976a7c7b"
- integrity sha512-2A4g5FW5d2fHzq8HKUGAMVTnW6P8nlWYQALiCoGN4bqBLvgwhYM/oG9oKc2CS6LnvgHFiKivKzpm9sfk3uU3zQ==
+cypress@4.11.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.11.0.tgz#054b0b85fd3aea793f186249ee1216126d5f0a7e"
+ integrity sha512-6Yd598+KPATM+dU1Ig0g2hbA+R/o1MAKt0xIejw4nZBVLSplCouBzqeKve6XsxGU6n4HMSt/+QYsWfFcoQeSEw==
dependencies:
"@cypress/listr-verbose-renderer" "0.4.1"
"@cypress/request" "2.88.5"
"@cypress/xvfb" "1.2.4"
- "@types/blob-util" "1.3.3"
- "@types/bluebird" "3.5.29"
- "@types/chai" "4.2.7"
- "@types/chai-jquery" "1.1.40"
- "@types/jquery" "3.3.31"
- "@types/lodash" "4.14.149"
- "@types/minimatch" "3.0.3"
- "@types/mocha" "5.2.7"
- "@types/sinon" "7.5.1"
- "@types/sinon-chai" "3.2.3"
+ "@types/sinonjs__fake-timers" "6.0.1"
"@types/sizzle" "2.3.2"
- arch "2.1.1"
+ arch "2.1.2"
bluebird "3.7.2"
cachedir "2.3.0"
chalk "2.4.2"
check-more-types "2.24.0"
cli-table3 "0.5.1"
- commander "4.1.0"
+ commander "4.1.1"
common-tags "1.8.0"
debug "4.1.1"
- eventemitter2 "4.1.2"
+ eventemitter2 "6.4.2"
execa "1.0.0"
executable "4.1.1"
extract-zip "1.7.0"
fs-extra "8.1.0"
- getos "3.1.4"
+ getos "3.2.1"
is-ci "2.0.0"
- is-installed-globally "0.1.0"
+ is-installed-globally "0.3.2"
lazy-ass "1.6.0"
listr "0.14.3"
- lodash "4.17.15"
+ lodash "4.17.19"
log-symbols "3.0.0"
minimist "1.2.5"
- moment "2.24.0"
+ moment "2.26.0"
ospath "1.2.2"
pretty-bytes "5.3.0"
ramda "0.26.1"
@@ -13890,10 +13833,10 @@ event-target-shim@^5.0.0:
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
-eventemitter2@4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15"
- integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=
+eventemitter2@6.4.2:
+ version "6.4.2"
+ resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.2.tgz#f31f8b99d45245f0edbc5b00797830ff3b388970"
+ integrity sha512-r/Pwupa5RIzxIHbEKCkNXqpEQIIT4uQDxmP4G/Lug/NokVUWj0joz/WzWl3OxRpC5kDrH/WdiUJoR+IrwvXJEw==
eventemitter2@~0.4.13:
version "0.4.14"
@@ -15515,12 +15458,12 @@ getopts@^2.2.5:
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b"
integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==
-getos@3.1.4:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf"
- integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==
+getos@3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5"
+ integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==
dependencies:
- async "^3.1.0"
+ async "^3.2.0"
getos@^3.1.0:
version "3.1.0"
@@ -18256,15 +18199,7 @@ is-hexadecimal@^1.0.0:
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz#6e084bbc92061fbb0971ec58b6ce6d404e24da69"
integrity sha1-bghLvJIGH7sJcexYts5tQE4k2mk=
-is-installed-globally@0.1.0, is-installed-globally@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
- integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
- dependencies:
- global-dirs "^0.1.0"
- is-path-inside "^1.0.0"
-
-is-installed-globally@^0.3.1:
+is-installed-globally@0.3.2, is-installed-globally@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
@@ -18272,6 +18207,14 @@ is-installed-globally@^0.3.1:
global-dirs "^2.0.1"
is-path-inside "^3.0.1"
+is-installed-globally@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
+ integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
+ dependencies:
+ global-dirs "^0.1.0"
+ is-path-inside "^1.0.0"
+
is-integer@^1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c"
@@ -20799,16 +20742,16 @@ lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+lodash@4.17.19, lodash@^4.17.16:
+ version "4.17.19"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
+ integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+
lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
-lodash@^4.17.16:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
-
"lodash@npm:@elastic/lodash@3.10.1-kibana4":
version "3.10.1-kibana4"
resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana4.tgz#d491228fd659b4a1b0dfa08ba9c67a4979b9746d"
@@ -21974,7 +21917,12 @@ moment-timezone@^0.5.27:
dependencies:
moment ">= 2.9.0"
-moment@2.24.0, "moment@>= 2.9.0", moment@>=1.6.0, moment@>=2.14.0, moment@^2.10.6, moment@^2.24.0:
+moment@2.26.0:
+ version "2.26.0"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
+ integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
+
+"moment@>= 2.9.0", moment@>=1.6.0, moment@>=2.14.0, moment@^2.10.6, moment@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
From 7b29ecf0b51a835394b0c45fe0623cc978455520 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 28 Jul 2020 10:29:33 +0300
Subject: [PATCH 016/102] [Functional Tests] Fix flakiness on TSVB chart on
switching index patterns test (#73238)
---
test/functional/services/combo_box.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/functional/services/combo_box.ts b/test/functional/services/combo_box.ts
index 60fea7ea86cf9b..ac7a40361d065e 100644
--- a/test/functional/services/combo_box.ts
+++ b/test/functional/services/combo_box.ts
@@ -90,7 +90,7 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont
await this.clickOption(options.clickWithMouse, selectOptions[0]);
} else {
// if it doesn't find the item which text starts with value, it will choose the first option
- const firstOption = await find.byCssSelector('.euiFilterSelectItem');
+ const firstOption = await find.byCssSelector('.euiFilterSelectItem', 5000);
await this.clickOption(options.clickWithMouse, firstOption);
}
} else {
From a696f6c79b3fe20d60712faeb21e31d1f4538de4 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 28 Jul 2020 10:29:47 +0300
Subject: [PATCH 017/102] [Functional Tests] Increase waitTime for timelion to
fetch the results (#73255)
Co-authored-by: Elastic Machine
---
test/functional/page_objects/timelion_page.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/functional/page_objects/timelion_page.ts b/test/functional/page_objects/timelion_page.ts
index f025fc946bef1d..23a9cc514a444a 100644
--- a/test/functional/page_objects/timelion_page.ts
+++ b/test/functional/page_objects/timelion_page.ts
@@ -47,7 +47,7 @@ export function TimelionPageProvider({ getService, getPageObjects }: FtrProvider
public async updateExpression(updates: string) {
const input = await testSubjects.find('timelionExpressionTextArea');
await input.type(updates);
- await PageObjects.common.sleep(500);
+ await PageObjects.common.sleep(1000);
}
public async getExpression() {
@@ -60,7 +60,7 @@ export function TimelionPageProvider({ getService, getPageObjects }: FtrProvider
return await Promise.all(elements.map(async (element) => await element.getVisibleText()));
}
- public async clickSuggestion(suggestionIndex = 0, waitTime = 500) {
+ public async clickSuggestion(suggestionIndex = 0, waitTime = 1000) {
const elements = await testSubjects.findAll('timelionSuggestionListItem');
if (suggestionIndex > elements.length) {
throw new Error(
From 9b570a9bf1262428661695179fee801345017efc Mon Sep 17 00:00:00 2001
From: Anton Dosov
Date: Tue, 28 Jul 2020 09:46:36 +0200
Subject: [PATCH 018/102] fix dashboard index pattern race condition (#72899)
* fix dashboard index pattern race condition
* improve
Co-authored-by: Elastic Machine
---
.../application/dashboard_app_controller.tsx | 63 ++++++++++++-------
1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
index 8138e1c7f4dfdb..2a0e2889575f36 100644
--- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
@@ -25,8 +25,8 @@ import React, { useState, ReactElement } from 'react';
import ReactDOM from 'react-dom';
import angular from 'angular';
-import { Subscription } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { Observable, pipe, Subscription } from 'rxjs';
+import { filter, map, mapTo, startWith, switchMap } from 'rxjs/operators';
import { History } from 'history';
import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
@@ -253,11 +253,7 @@ export class DashboardAppController {
navActions[TopNavIds.VISUALIZE]();
};
- const updateIndexPatterns = (container?: DashboardContainer) => {
- if (!container || isErrorEmbeddable(container)) {
- return;
- }
-
+ function getDashboardIndexPatterns(container: DashboardContainer): IndexPattern[] {
let panelIndexPatterns: IndexPattern[] = [];
Object.values(container.getChildIds()).forEach((id) => {
const embeddableInstance = container.getChild(id);
@@ -267,19 +263,34 @@ export class DashboardAppController {
panelIndexPatterns.push(...embeddableIndexPatterns);
});
panelIndexPatterns = uniqBy(panelIndexPatterns, 'id');
+ return panelIndexPatterns;
+ }
- if (panelIndexPatterns && panelIndexPatterns.length > 0) {
- $scope.$evalAsync(() => {
- $scope.indexPatterns = panelIndexPatterns;
- });
- } else {
- indexPatterns.getDefault().then((defaultIndexPattern) => {
- $scope.$evalAsync(() => {
- $scope.indexPatterns = [defaultIndexPattern as IndexPattern];
- });
+ const updateIndexPatternsOperator = pipe(
+ filter((container: DashboardContainer) => !!container && !isErrorEmbeddable(container)),
+ map(getDashboardIndexPatterns),
+ // using switchMap for previous task cancellation
+ switchMap((panelIndexPatterns: IndexPattern[]) => {
+ return new Observable((observer) => {
+ if (panelIndexPatterns && panelIndexPatterns.length > 0) {
+ $scope.$evalAsync(() => {
+ if (observer.closed) return;
+ $scope.indexPatterns = panelIndexPatterns;
+ observer.complete();
+ });
+ } else {
+ indexPatterns.getDefault().then((defaultIndexPattern) => {
+ if (observer.closed) return;
+ $scope.$evalAsync(() => {
+ if (observer.closed) return;
+ $scope.indexPatterns = [defaultIndexPattern as IndexPattern];
+ observer.complete();
+ });
+ });
+ }
});
- }
- };
+ })
+ );
const getEmptyScreenProps = (
shouldShowEditHelp: boolean,
@@ -384,11 +395,17 @@ export class DashboardAppController {
) : null;
};
- updateIndexPatterns(dashboardContainer);
-
- outputSubscription = dashboardContainer.getOutput$().subscribe(() => {
- updateIndexPatterns(dashboardContainer);
- });
+ outputSubscription = new Subscription();
+ outputSubscription.add(
+ dashboardContainer
+ .getOutput$()
+ .pipe(
+ mapTo(dashboardContainer),
+ startWith(dashboardContainer), // to trigger initial index pattern update
+ updateIndexPatternsOperator
+ )
+ .subscribe()
+ );
inputSubscription = dashboardContainer.getInput$().subscribe(() => {
let dirty = false;
From abfda1f79273111b581a14b1a43fe134c6053e6c Mon Sep 17 00:00:00 2001
From: Anton Dosov
Date: Tue, 28 Jul 2020 09:57:04 +0200
Subject: [PATCH 019/102] Use "Apply_filter_trigger" in dashboard drilldown
(#71468)
* attach dashboard drilldown to apply filter trigger
* fix types
Co-authored-by: Elastic Machine
---
...na-plugin-plugins-data-public.esfilters.md | 1 +
src/plugins/dashboard/public/index.ts | 6 +-
src/plugins/dashboard/public/plugin.tsx | 7 +-
src/plugins/dashboard/public/url_generator.ts | 6 +-
src/plugins/data/public/index.ts | 2 +
src/plugins/data/public/public.api.md | 94 ++++++-------
.../data/public/query/timefilter/index.ts | 2 +-
.../timefilter/lib/extract_time_filter.ts | 15 ++-
x-pack/plugins/dashboard_enhanced/kibana.json | 3 +-
.../flyout_create_drilldown.tsx | 11 +-
.../constants.ts | 7 +
.../drilldown.test.tsx | 54 ++------
.../drilldown.tsx | 125 +++++++-----------
.../dashboard_to_dashboard_drilldown/index.ts | 5 +-
.../dashboard_to_dashboard_drilldown/types.ts | 10 --
.../embeddable_action_storage.test.ts | 41 ++++++
.../embeddables/embeddable_action_storage.ts | 30 ++++-
.../connected_flyout_manage_drilldowns.tsx | 6 +-
18 files changed, 227 insertions(+), 198 deletions(-)
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md
index 37142cf1794c32..bc34d4113f847b 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md
@@ -52,5 +52,6 @@ esFilters: {
convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString;
mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[];
extractTimeFilter: typeof extractTimeFilter;
+ extractTimeRange: typeof extractTimeRange;
}
```
diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts
index 17968dd0281e6c..dcfde67cd9f131 100644
--- a/src/plugins/dashboard/public/index.ts
+++ b/src/plugins/dashboard/public/index.ts
@@ -32,7 +32,11 @@ export {
export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants';
export { DashboardStart, DashboardUrlGenerator } from './plugin';
-export { DASHBOARD_APP_URL_GENERATOR, createDashboardUrlGenerator } from './url_generator';
+export {
+ DASHBOARD_APP_URL_GENERATOR,
+ createDashboardUrlGenerator,
+ DashboardUrlGeneratorState,
+} from './url_generator';
export { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
export { SavedObjectDashboard } from './saved_dashboards';
export { SavedDashboardPanel } from './types';
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 041a02a251e8ab..f0b57fec169fd2 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -65,6 +65,7 @@ import {
ACTION_REPLACE_PANEL,
ClonePanelAction,
ClonePanelActionContext,
+ createDashboardContainerByValueRenderer,
DASHBOARD_CONTAINER_TYPE,
DashboardContainerFactory,
DashboardContainerFactoryDefinition,
@@ -77,17 +78,17 @@ import {
import {
createDashboardUrlGenerator,
DASHBOARD_APP_URL_GENERATOR,
- DashboardAppLinkGeneratorState,
+ DashboardUrlGeneratorState,
} from './url_generator';
import { createSavedDashboardLoader } from './saved_dashboards';
import { DashboardConstants } from './dashboard_constants';
import { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
-import { createDashboardContainerByValueRenderer } from './application';
+import { UrlGeneratorState } from '../../share/public';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
- [DASHBOARD_APP_URL_GENERATOR]: DashboardAppLinkGeneratorState;
+ [DASHBOARD_APP_URL_GENERATOR]: UrlGeneratorState;
}
}
diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts
index 188de7fd857be8..68a50396e00d69 100644
--- a/src/plugins/dashboard/public/url_generator.ts
+++ b/src/plugins/dashboard/public/url_generator.ts
@@ -26,7 +26,7 @@ import {
RefreshInterval,
} from '../../data/public';
import { setStateToKbnUrl } from '../../kibana_utils/public';
-import { UrlGeneratorsDefinition, UrlGeneratorState } from '../../share/public';
+import { UrlGeneratorsDefinition } from '../../share/public';
import { SavedObjectLoader } from '../../saved_objects/public';
import { ViewMode } from '../../embeddable/public';
@@ -35,7 +35,7 @@ export const GLOBAL_STATE_STORAGE_KEY = '_g';
export const DASHBOARD_APP_URL_GENERATOR = 'DASHBOARD_APP_URL_GENERATOR';
-export type DashboardAppLinkGeneratorState = UrlGeneratorState<{
+export interface DashboardUrlGeneratorState {
/**
* If given, the dashboard saved object with this id will be loaded. If not given,
* a new, unsaved dashboard will be loaded up.
@@ -79,7 +79,7 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{
* View mode of the dashboard.
*/
viewMode?: ViewMode;
-}>;
+}
export const createDashboardUrlGenerator = (
getStartServices: () => Promise<{
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 846471420327fa..e95150e8f6f732 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -58,6 +58,7 @@ import {
changeTimeFilter,
mapAndFlattenFilters,
extractTimeFilter,
+ extractTimeRange,
convertRangeFilterToTimeRangeString,
} from './query';
@@ -99,6 +100,7 @@ export const esFilters = {
convertRangeFilterToTimeRangeString,
mapAndFlattenFilters,
extractTimeFilter,
+ extractTimeRange,
};
export {
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index a8868c07061c3b..65670bc1cf83e6 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -499,6 +499,7 @@ export const esFilters: {
convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString;
mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[];
extractTimeFilter: typeof extractTimeFilter;
+ extractTimeRange: typeof extractTimeRange;
};
// Warning: (ae-missing-release-tag) "esKuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -1973,52 +1974,53 @@ export const UI_SETTINGS: {
// src/plugins/data/common/es_query/filters/match_all_filter.ts:28:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:65:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:136:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:176:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:232:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:371:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:372:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:381:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "extractTimeRange" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:373:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:374:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:41:60 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/types.ts:54:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/types.ts:55:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/query/timefilter/index.ts b/src/plugins/data/public/query/timefilter/index.ts
index 19386c10ab59fd..dc9a4ef8c21a67 100644
--- a/src/plugins/data/public/query/timefilter/index.ts
+++ b/src/plugins/data/public/query/timefilter/index.ts
@@ -23,5 +23,5 @@ export * from './types';
export { Timefilter, TimefilterContract } from './timefilter';
export { TimeHistory, TimeHistoryContract } from './time_history';
export { changeTimeFilter, convertRangeFilterToTimeRangeString } from './lib/change_time_filter';
-export { extractTimeFilter } from './lib/extract_time_filter';
+export { extractTimeFilter, extractTimeRange } from './lib/extract_time_filter';
export { validateTimeRange } from './lib/validate_timerange';
diff --git a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts
index 23dd1547baf10b..2f93196e3218b9 100644
--- a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts
+++ b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts
@@ -18,7 +18,8 @@
*/
import { keys, partition } from 'lodash';
-import { Filter, isRangeFilter, RangeFilter } from '../../../../common';
+import { Filter, isRangeFilter, RangeFilter, TimeRange } from '../../../../common';
+import { convertRangeFilterToTimeRangeString } from './change_time_filter';
export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
const [timeRangeFilter, restOfFilters] = partition(filters, (obj: Filter) => {
@@ -36,3 +37,15 @@ export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
timeRangeFilter: timeRangeFilter[0] as RangeFilter | undefined,
};
}
+
+export function extractTimeRange(
+ filters: Filter[],
+ timeFieldName?: string
+): { restOfFilters: Filter[]; timeRange?: TimeRange } {
+ if (!timeFieldName) return { restOfFilters: filters, timeRange: undefined };
+ const { timeRangeFilter, restOfFilters } = extractTimeFilter(timeFieldName, filters);
+ return {
+ restOfFilters,
+ timeRange: timeRangeFilter ? convertRangeFilterToTimeRangeString(timeRangeFilter) : undefined,
+ };
+}
diff --git a/x-pack/plugins/dashboard_enhanced/kibana.json b/x-pack/plugins/dashboard_enhanced/kibana.json
index ba5d8052ca7876..264fa0438ea118 100644
--- a/x-pack/plugins/dashboard_enhanced/kibana.json
+++ b/x-pack/plugins/dashboard_enhanced/kibana.json
@@ -8,6 +8,7 @@
"requiredBundles": [
"kibanaUtils",
"embeddableEnhanced",
- "kibanaReact"
+ "kibanaReact",
+ "uiActions"
]
}
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx
index 4804a700c6cff3..2de862a6708a81 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx
@@ -6,7 +6,12 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { ActionByType } from '../../../../../../../../src/plugins/ui_actions/public';
+import {
+ ActionByType,
+ APPLY_FILTER_TRIGGER,
+ SELECT_RANGE_TRIGGER,
+ VALUE_CLICK_TRIGGER,
+} from '../../../../../../../../src/plugins/ui_actions/public';
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
import { isEnhancedEmbeddable } from '../../../../../../embeddable_enhanced/public';
import { EmbeddableContext } from '../../../../../../../../src/plugins/embeddable/public';
@@ -42,7 +47,9 @@ export class FlyoutCreateDrilldownAction implements ActionByType -1;
+ return supportedTriggers.some((trigger) =>
+ [VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER, APPLY_FILTER_TRIGGER].includes(trigger)
+ );
}
public async isCompatible(context: EmbeddableContext) {
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/constants.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/constants.ts
index e2a530b156da5c..daefcf2d68cc53 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/constants.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/constants.ts
@@ -4,4 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/**
+ * note:
+ * don't change this string without carefull consideration,
+ * because it is stored in saved objects.
+ * Also temporary dashboard drilldown migration code inside embeddable plugin relies on it
+ * x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
+ */
export const DASHBOARD_TO_DASHBOARD_DRILLDOWN = 'DASHBOARD_TO_DASHBOARD_DRILLDOWN';
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx
index 52b232afa9410f..40fa469feb34b8 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx
@@ -5,9 +5,8 @@
*/
import { DashboardToDashboardDrilldown } from './drilldown';
-import { savedObjectsServiceMock, coreMock } from '../../../../../../../src/core/public/mocks';
-import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
-import { ActionContext, Config } from './types';
+import { Config } from './types';
+import { coreMock, savedObjectsServiceMock } from '../../../../../../../src/core/public/mocks';
import {
Filter,
FilterStateStore,
@@ -15,16 +14,13 @@ import {
RangeFilter,
TimeRange,
} from '../../../../../../../src/plugins/data/common';
-import { esFilters } from '../../../../../../../src/plugins/data/public';
-
+import {
+ ApplyGlobalFilterActionContext,
+ esFilters,
+} from '../../../../../../../src/plugins/data/public';
// convenient to use real implementation here.
import { createDashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public/url_generator';
import { UrlGeneratorsService } from '../../../../../../../src/plugins/share/public/url_generators';
-import { VisualizeEmbeddableContract } from '../../../../../../../src/plugins/visualizations/public';
-import {
- RangeSelectContext,
- ValueClickContext,
-} from '../../../../../../../src/plugins/embeddable/public';
import { StartDependencies } from '../../../plugin';
import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public';
import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_utils/public/core';
@@ -82,11 +78,10 @@ describe('.execute() & getHref', () => {
config: Partial,
embeddableInput: { filters?: Filter[]; timeRange?: TimeRange; query?: Query },
filtersFromEvent: Filter[],
- useRangeEvent = false
+ timeFieldName?: string
) {
const navigateToApp = jest.fn();
const getUrlForApp = jest.fn((app, opt) => `${app}/${opt.path}`);
- const dataPluginActions = dataPluginMock.createStartContract().actions;
const savedObjectsClient = savedObjectsServiceMock.createStartContract().client;
const drilldown = new DashboardToDashboardDrilldown({
@@ -102,9 +97,6 @@ describe('.execute() & getHref', () => {
},
plugins: {
uiActionsEnhanced: {},
- data: {
- actions: dataPluginActions,
- },
},
self: {},
})) as unknown) as StartServicesGetter>,
@@ -119,12 +111,6 @@ describe('.execute() & getHref', () => {
)
),
});
- const selectRangeFiltersSpy = jest
- .spyOn(dataPluginActions, 'createFiltersFromRangeSelectAction')
- .mockImplementation(() => Promise.resolve(filtersFromEvent));
- const valueClickFiltersSpy = jest
- .spyOn(dataPluginActions, 'createFiltersFromValueClickAction')
- .mockImplementation(() => Promise.resolve(filtersFromEvent));
const completeConfig: Config = {
dashboardId: 'id',
@@ -134,12 +120,7 @@ describe('.execute() & getHref', () => {
};
const context = ({
- data: {
- ...(useRangeEvent
- ? ({ range: {} } as RangeSelectContext['data'])
- : ({ data: [] } as ValueClickContext['data'])),
- timeFieldName: 'order_date',
- },
+ filters: filtersFromEvent,
embeddable: {
getInput: () => ({
filters: [],
@@ -148,18 +129,11 @@ describe('.execute() & getHref', () => {
...embeddableInput,
}),
},
- } as unknown) as ActionContext;
+ timeFieldName,
+ } as unknown) as ApplyGlobalFilterActionContext;
await drilldown.execute(completeConfig, context);
- if (useRangeEvent) {
- expect(selectRangeFiltersSpy).toBeCalledTimes(1);
- expect(valueClickFiltersSpy).toBeCalledTimes(0);
- } else {
- expect(selectRangeFiltersSpy).toBeCalledTimes(0);
- expect(valueClickFiltersSpy).toBeCalledTimes(1);
- }
-
expect(navigateToApp).toBeCalledTimes(1);
expect(navigateToApp.mock.calls[0][0]).toBe('dashboards');
@@ -180,8 +154,7 @@ describe('.execute() & getHref', () => {
dashboardId: testDashboardId,
},
{},
- [],
- false
+ []
);
expect(href).toEqual(expect.stringContaining(`view/${testDashboardId}`));
@@ -289,8 +262,7 @@ describe('.execute() & getHref', () => {
to: 'now',
},
},
- [],
- false
+ []
);
expect(href).not.toEqual(expect.stringContaining('now-300m'));
@@ -308,7 +280,7 @@ describe('.execute() & getHref', () => {
},
},
[getMockTimeRangeFilter()],
- true
+ getMockTimeRangeFilter().meta.key
);
expect(href).not.toEqual(expect.stringContaining('now-300m'));
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx
index 26a69132cffb17..703acbc8d9d59c 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx
@@ -6,20 +6,24 @@
import React from 'react';
import { reactToUiComponent } from '../../../../../../../src/plugins/kibana_react/public';
-import { DashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public';
-import { ActionContext, Config } from './types';
+import {
+ DashboardUrlGenerator,
+ DashboardUrlGeneratorState,
+} from '../../../../../../../src/plugins/dashboard/public';
import { CollectConfigContainer } from './components';
import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from './constants';
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../../ui_actions_enhanced/public';
import { txtGoToDashboard } from './i18n';
-import { esFilters } from '../../../../../../../src/plugins/data/public';
-import { VisualizeEmbeddableContract } from '../../../../../../../src/plugins/visualizations/public';
import {
- isRangeSelectTriggerContext,
- isValueClickTriggerContext,
-} from '../../../../../../../src/plugins/embeddable/public';
+ ApplyGlobalFilterActionContext,
+ esFilters,
+ isFilters,
+ isQuery,
+ isTimeRange,
+} from '../../../../../../../src/plugins/data/public';
import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_utils/public';
import { StartDependencies } from '../../../plugin';
+import { Config } from './types';
export interface Params {
start: StartServicesGetter>;
@@ -27,7 +31,7 @@ export interface Params {
}
export class DashboardToDashboardDrilldown
- implements Drilldown> {
+ implements Drilldown {
constructor(protected readonly params: Params) {}
public readonly id = DASHBOARD_TO_DASHBOARD_DRILLDOWN;
@@ -57,15 +61,12 @@ export class DashboardToDashboardDrilldown
public readonly getHref = async (
config: Config,
- context: ActionContext
+ context: ApplyGlobalFilterActionContext
): Promise => {
return this.getDestinationUrl(config, context);
};
- public readonly execute = async (
- config: Config,
- context: ActionContext
- ) => {
+ public readonly execute = async (config: Config, context: ApplyGlobalFilterActionContext) => {
const dashboardPath = await this.getDestinationUrl(config, context);
const dashboardHash = dashboardPath.split('#')[1];
@@ -76,73 +77,43 @@ export class DashboardToDashboardDrilldown
private getDestinationUrl = async (
config: Config,
- context: ActionContext
+ context: ApplyGlobalFilterActionContext
): Promise => {
+ const state: DashboardUrlGeneratorState = {
+ dashboardId: config.dashboardId,
+ };
+
+ if (context.embeddable) {
+ const input = context.embeddable.getInput();
+ if (isQuery(input.query) && config.useCurrentFilters) state.query = input.query;
+
+ // if useCurrentDashboardDataRange is enabled, then preserve current time range
+ // if undefined is passed, then destination dashboard will figure out time range itself
+ // for brush event this time range would be overwritten
+ if (isTimeRange(input.timeRange) && config.useCurrentDateRange)
+ state.timeRange = input.timeRange;
+
+ // if useCurrentDashboardFilters enabled, then preserve all the filters (pinned and unpinned)
+ // otherwise preserve only pinned
+ if (isFilters(input.filters))
+ state.filters = config.useCurrentFilters
+ ? input.filters
+ : input.filters?.filter((f) => esFilters.isFilterPinned(f));
+ }
+
const {
- createFiltersFromRangeSelectAction,
- createFiltersFromValueClickAction,
- } = this.params.start().plugins.data.actions;
- const {
- timeRange: currentTimeRange,
- query,
- filters: currentFilters,
- } = context.embeddable!.getInput();
-
- // if useCurrentDashboardFilters enabled, then preserve all the filters (pinned and unpinned)
- // otherwise preserve only pinned
- const existingFilters =
- (config.useCurrentFilters
- ? currentFilters
- : currentFilters?.filter((f) => esFilters.isFilterPinned(f))) ?? [];
-
- // if useCurrentDashboardDataRange is enabled, then preserve current time range
- // if undefined is passed, then destination dashboard will figure out time range itself
- // for brush event this time range would be overwritten
- let timeRange = config.useCurrentDateRange ? currentTimeRange : undefined;
- let filtersFromEvent = await (async () => {
- try {
- if (isRangeSelectTriggerContext(context))
- return await createFiltersFromRangeSelectAction(context.data);
- if (isValueClickTriggerContext(context))
- return await createFiltersFromValueClickAction(context.data);
-
- // eslint-disable-next-line no-console
- console.warn(
- `
- DashboardToDashboard drilldown: can't extract filters from action.
- Is it not supported action?`,
- context
- );
-
- return [];
- } catch (e) {
- // eslint-disable-next-line no-console
- console.warn(
- `
- DashboardToDashboard drilldown: error extracting filters from action.
- Continuing without applying filters from event`,
- e
- );
- return [];
- }
- })();
-
- if (context.data.timeFieldName) {
- const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
- context.data.timeFieldName,
- filtersFromEvent
- );
- filtersFromEvent = restOfFilters;
- if (timeRangeFilter) {
- timeRange = esFilters.convertRangeFilterToTimeRangeString(timeRangeFilter);
- }
+ restOfFilters: filtersFromEvent,
+ timeRange: timeRangeFromEvent,
+ } = esFilters.extractTimeRange(context.filters, context.timeFieldName);
+
+ if (filtersFromEvent) {
+ state.filters = [...(state.filters ?? []), ...filtersFromEvent];
}
- return this.params.getDashboardUrlGenerator().createUrl({
- dashboardId: config.dashboardId,
- query: config.useCurrentFilters ? query : undefined,
- timeRange,
- filters: [...existingFilters, ...filtersFromEvent],
- });
+ if (timeRangeFromEvent) {
+ state.timeRange = timeRangeFromEvent;
+ }
+
+ return this.params.getDashboardUrlGenerator().createUrl(state);
};
}
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/index.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/index.ts
index 914f34980a2722..49065a96b4f7b3 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/index.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/index.ts
@@ -9,7 +9,4 @@ export {
DashboardToDashboardDrilldown,
Params as DashboardToDashboardDrilldownParams,
} from './drilldown';
-export {
- ActionContext as DashboardToDashboardActionContext,
- Config as DashboardToDashboardConfig,
-} from './types';
+export { Config } from './types';
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts
index 6be2e2a77269fc..426e250499de02 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts
@@ -4,16 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- ValueClickContext,
- RangeSelectContext,
- IEmbeddable,
-} from '../../../../../../../src/plugins/embeddable/public';
-
-export type ActionContext =
- | ValueClickContext
- | RangeSelectContext;
-
export interface Config {
dashboardId?: string;
useCurrentFilters: boolean;
diff --git a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
index 5c5d98d75295de..fffb75451f8ac7 100644
--- a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
+++ b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.test.ts
@@ -11,6 +11,9 @@ import {
} from './embeddable_action_storage';
import { UiActionsEnhancedSerializedEvent } from '../../../ui_actions_enhanced/public';
import { of } from '../../../../../src/plugins/kibana_utils/public';
+// use real const to make test fail in case someone accidentally changes it
+import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from '../../../dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown';
+import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public';
class TestEmbeddable extends Embeddable {
public readonly type = 'test';
@@ -539,4 +542,42 @@ describe('EmbeddableActionStorage', () => {
expect(await storage.list()).toEqual([]);
});
});
+
+ describe('migrate', () => {
+ test('DASHBOARD_TO_DASHBOARD_DRILLDOWN triggers migration', async () => {
+ const embeddable = new TestEmbeddable();
+ const OTHER_TRIGGER = 'OTHER_TRIGGER';
+ embeddable.updateInput({
+ enhancements: {
+ dynamicActions: {
+ events: [
+ {
+ eventId: '1',
+ triggers: [OTHER_TRIGGER],
+ action: {
+ factoryId: DASHBOARD_TO_DASHBOARD_DRILLDOWN,
+ name: '',
+ config: {},
+ },
+ },
+ {
+ eventId: '2',
+ triggers: [OTHER_TRIGGER],
+ action: {
+ factoryId: 'SOME_OTHER',
+ name: '',
+ config: {},
+ },
+ },
+ ],
+ },
+ },
+ });
+ const storage = new EmbeddableActionStorage(embeddable);
+
+ const [event1, event2] = await storage.list();
+ expect(event1.triggers).toEqual([APPLY_FILTER_TRIGGER]);
+ expect(event2.triggers).toEqual([OTHER_TRIGGER]);
+ });
+ });
});
diff --git a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
index fdc42585a80ce3..8881b2063c8db3 100644
--- a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
+++ b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
@@ -46,7 +46,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async create(event: SerializedEvent) {
const input = this.embbeddable.getInput();
- const events = input.enhancements?.dynamicActions?.events || [];
+ const events = this.getEventsFromEmbeddable();
const exists = !!events.find(({ eventId }) => eventId === event.eventId);
if (exists) {
@@ -61,7 +61,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async update(event: SerializedEvent) {
const input = this.embbeddable.getInput();
- const events = input.enhancements?.dynamicActions?.events || [];
+ const events = this.getEventsFromEmbeddable();
const index = events.findIndex(({ eventId }) => eventId === event.eventId);
if (index === -1) {
@@ -77,7 +77,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async remove(eventId: string) {
const input = this.embbeddable.getInput();
- const events = input.enhancements?.dynamicActions?.events || [];
+ const events = this.getEventsFromEmbeddable();
const index = events.findIndex((event) => eventId === event.eventId);
if (index === -1) {
@@ -93,7 +93,7 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
public async read(eventId: string): Promise {
const input = this.embbeddable.getInput();
- const events = input.enhancements?.dynamicActions?.events || [];
+ const events = this.getEventsFromEmbeddable();
const event = events.find((ev) => eventId === ev.eventId);
if (!event) {
@@ -107,8 +107,28 @@ export class EmbeddableActionStorage extends AbstractActionStorage {
}
public async list(): Promise {
+ return this.getEventsFromEmbeddable();
+ }
+
+ private getEventsFromEmbeddable() {
const input = this.embbeddable.getInput();
const events = input.enhancements?.dynamicActions?.events || [];
- return events;
+ return this.migrate(events);
+ }
+
+ // TODO: https://github.com/elastic/kibana/issues/71431
+ // Migration implementation should use registry
+ // Action factories implementations should register own migrations
+ private migrate(events: SerializedEvent[]): SerializedEvent[] {
+ return events.map((event) => {
+ // Initially dashboard drilldown relied on VALUE_CLICK & RANGE_SELECT
+ if (event.action.factoryId === 'DASHBOARD_TO_DASHBOARD_DRILLDOWN') {
+ return {
+ ...event,
+ triggers: ['FILTER_TRIGGER'],
+ };
+ }
+ return event;
+ });
}
}
diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx
index 20d15b4f4d2bd3..283464b137ff9b 100644
--- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx
+++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx
@@ -11,9 +11,8 @@ import { DrilldownWizardConfig, FlyoutDrilldownWizard } from '../flyout_drilldow
import { FlyoutListManageDrilldowns } from '../flyout_list_manage_drilldowns';
import { IStorageWrapper } from '../../../../../../../src/plugins/kibana_utils/public';
import {
- VALUE_CLICK_TRIGGER,
- SELECT_RANGE_TRIGGER,
TriggerContextMapping,
+ APPLY_FILTER_TRIGGER,
} from '../../../../../../../src/plugins/ui_actions/public';
import { useContainerState } from '../../../../../../../src/plugins/kibana_utils/public';
import { DrilldownListItem } from '../list_manage_drilldowns';
@@ -67,8 +66,9 @@ export function createFlyoutManageDrilldowns({
return (props: ConnectedFlyoutManageDrilldownsProps) => {
const isCreateOnly = props.viewMode === 'create';
+ // TODO: https://github.com/elastic/kibana/issues/59569
const selectedTriggers: Array = React.useMemo(
- () => [VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER],
+ () => [APPLY_FILTER_TRIGGER],
[]
);
From 5ea28702f6a2aa3e0592a79fa5ea396ff68fd972 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 28 Jul 2020 11:15:58 +0300
Subject: [PATCH 020/102] [Functional Tests] Increase the timeout when locating
the tableview] (#73243)
---
test/functional/page_objects/visual_builder_page.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index 0db8cac0f07581..8488eb8cd27493 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -408,7 +408,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
* @memberof VisualBuilderPage
*/
public async getViewTable(): Promise {
- const tableView = await testSubjects.find('tableView');
+ const tableView = await testSubjects.find('tableView', 20000);
return await tableView.getVisibleText();
}
From c0826a32730ba55c0192e81fc23788be5966fdcd Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Tue, 28 Jul 2020 11:37:37 +0300
Subject: [PATCH 021/102] Fix App status flaky test (#72853)
* wait for link to be updated
* await, please!
Co-authored-by: Elastic Machine
---
.../plugins/core_app_status/public/plugin.tsx | 3 +--
.../core_plugins/application_status.ts | 16 +++++++++-------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/test/plugin_functional/plugins/core_app_status/public/plugin.tsx b/test/plugin_functional/plugins/core_app_status/public/plugin.tsx
index af23bfbe1f8f5f..bdc08c03c19128 100644
--- a/test/plugin_functional/plugins/core_app_status/public/plugin.tsx
+++ b/test/plugin_functional/plugins/core_app_status/public/plugin.tsx
@@ -26,6 +26,7 @@ import {
CoreStart,
AppMountParameters,
} from 'kibana/public';
+import { renderApp } from './application';
import './types';
export class CoreAppStatusPlugin implements Plugin<{}, CoreAppStatusPluginStart> {
@@ -36,7 +37,6 @@ export class CoreAppStatusPlugin implements Plugin<{}, CoreAppStatusPluginStart>
id: 'app_status_start',
title: 'App Status Start Page',
async mount(params: AppMountParameters) {
- const { renderApp } = await import('./application');
return renderApp('app_status_start', params);
},
});
@@ -47,7 +47,6 @@ export class CoreAppStatusPlugin implements Plugin<{}, CoreAppStatusPluginStart>
euiIconType: 'snowflake',
updater$: this.appUpdater,
async mount(params: AppMountParameters) {
- const { renderApp } = await import('./application');
return renderApp('app_status', params);
},
});
diff --git a/test/plugin_functional/test_suites/core_plugins/application_status.ts b/test/plugin_functional/test_suites/core_plugins/application_status.ts
index 31a1c28b508429..a4c2db733b8949 100644
--- a/test/plugin_functional/test_suites/core_plugins/application_status.ts
+++ b/test/plugin_functional/test_suites/core_plugins/application_status.ts
@@ -41,6 +41,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
const PageObjects = getPageObjects(['common']);
const browser = getService('browser');
const appsMenu = getService('appsMenu');
+ const retry = getService('retry');
const testSubjects = getService('testSubjects');
const setAppStatus = async (s: Partial) => {
@@ -50,15 +51,14 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
}, s);
};
- const navigateToApp = async (i: string) => {
+ const navigateToApp = async (id: string) => {
return await browser.executeAsync(async (appId, cb) => {
await window.__coreAppStatus.navigateToApp(appId);
cb();
- }, i);
+ }, id);
};
- // FLAKY: https://github.com/elastic/kibana/issues/65423
- describe.skip('application status management', () => {
+ describe('application status management', () => {
beforeEach(async () => {
await PageObjects.common.navigateToApp('app_status_start');
});
@@ -101,15 +101,17 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
});
it('allows to change the defaultPath of an application', async () => {
- let link = await appsMenu.getLink('App Status');
+ const link = await appsMenu.getLink('App Status');
expect(link!.href).to.eql(getKibanaUrl('/app/app_status'));
await setAppStatus({
defaultPath: '/arbitrary/path',
});
- link = await appsMenu.getLink('App Status');
- expect(link!.href).to.eql(getKibanaUrl('/app/app_status/arbitrary/path'));
+ await retry.waitFor('link url updated with "defaultPath"', async () => {
+ const updatedLink = await appsMenu.getLink('App Status');
+ return updatedLink?.href === getKibanaUrl('/app/app_status/arbitrary/path');
+ });
await navigateToApp('app_status');
expect(await testSubjects.exists('appStatusApp')).to.eql(true);
From 1c791f39dac906f3a46b3703a82ba33e8f263a4b Mon Sep 17 00:00:00 2001
From: MadameSheema
Date: Tue, 28 Jul 2020 10:48:14 +0200
Subject: [PATCH 022/102] [SIEM][Timelines] Updates timeline template callout
text (#73334)
* updates timeline template callout text
* fixes typo in constant
Co-authored-by: Elastic Machine
---
.../timelines/components/timeline/header/index.test.tsx | 2 +-
.../public/timelines/components/timeline/header/index.tsx | 2 +-
.../timelines/components/timeline/header/translations.ts | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx
index e0043f3b232da7..e7b0ce7b7428e4 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx
@@ -177,7 +177,7 @@ describe('Header', () => {
expect(
wrapper.find('[data-test-subj="timelineImmutableCallOut"]').first().prop('title')
).toEqual(
- 'This timeline is immutable, therefore not allowed to save it within the security application, though you may continue to use the timeline to search and filter security events'
+ 'This prebuilt timeline template cannot be modified. To make changes, please duplicate this template and make modifications to the duplicate template.'
);
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx
index 75bfb52f2756b1..e50a6ed1e45fe0 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx
@@ -73,7 +73,7 @@ const TimelineHeaderComponent: React.FC = ({
{status === TimelineStatus.immutable && (
Date: Tue, 28 Jul 2020 13:00:16 +0300
Subject: [PATCH 023/102] [Search] add server logs (#72454)
* improve test stability
* logs and scope search function
* uncomment
* fix ts
* ts
Co-authored-by: Elastic Machine
---
src/plugins/data/server/plugin.ts | 4 ++--
.../es_search/es_search_strategy.test.ts | 11 +++++----
.../search/es_search/es_search_strategy.ts | 6 +++--
.../data/server/search/search_service.test.ts | 5 +++-
.../data/server/search/search_service.ts | 24 +++++++++++++++----
x-pack/plugins/data_enhanced/server/plugin.ts | 12 ++++++++--
.../server/search/es_search_strategy.test.ts | 13 ++++++----
.../server/search/es_search_strategy.ts | 6 ++++-
8 files changed, 60 insertions(+), 21 deletions(-)
diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts
index 8fa32f9bd564f9..61d8e566d2d2b8 100644
--- a/src/plugins/data/server/plugin.ts
+++ b/src/plugins/data/server/plugin.ts
@@ -62,11 +62,11 @@ export class DataServerPlugin implements Plugin) {
- this.searchService = new SearchService(initializerContext);
+ this.logger = initializerContext.logger.get('data');
+ this.searchService = new SearchService(initializerContext, this.logger);
this.scriptsService = new ScriptsService();
this.kqlTelemetryService = new KqlTelemetryService(initializerContext);
this.autocompleteService = new AutocompleteService(initializerContext);
- this.logger = initializerContext.logger.get('data');
}
public setup(
diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
index 1155a5491e8f3c..bc59bdee6a40a0 100644
--- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
+++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
@@ -22,6 +22,9 @@ import { pluginInitializerContextConfigMock } from '../../../../../core/server/m
import { esSearchStrategyProvider } from './es_search_strategy';
describe('ES search strategy', () => {
+ const mockLogger: any = {
+ info: () => {},
+ };
const mockApiCaller = jest.fn().mockResolvedValue({
_shards: {
total: 10,
@@ -40,14 +43,14 @@ describe('ES search strategy', () => {
});
it('returns a strategy with `search`', async () => {
- const esSearch = await esSearchStrategyProvider(mockConfig$);
+ const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger);
expect(typeof esSearch.search).toBe('function');
});
it('calls the API caller with the params with defaults', async () => {
const params = { index: 'logstash-*' };
- const esSearch = await esSearchStrategyProvider(mockConfig$);
+ const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger);
await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params });
@@ -63,7 +66,7 @@ describe('ES search strategy', () => {
it('calls the API caller with overridden defaults', async () => {
const params = { index: 'logstash-*', ignoreUnavailable: false, timeout: '1000ms' };
- const esSearch = await esSearchStrategyProvider(mockConfig$);
+ const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger);
await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params });
@@ -77,7 +80,7 @@ describe('ES search strategy', () => {
it('returns total, loaded, and raw response', async () => {
const params = { index: 'logstash-*' };
- const esSearch = await esSearchStrategyProvider(mockConfig$);
+ const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger);
const response = await esSearch.search((mockContext as unknown) as RequestHandlerContext, {
params,
diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts
index 82f8ef21ebb386..b8010f735c3274 100644
--- a/src/plugins/data/server/search/es_search/es_search_strategy.ts
+++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts
@@ -17,16 +17,18 @@
* under the License.
*/
import { first } from 'rxjs/operators';
-import { SharedGlobalConfig } from 'kibana/server';
+import { SharedGlobalConfig, Logger } from 'kibana/server';
import { SearchResponse } from 'elasticsearch';
import { Observable } from 'rxjs';
import { ISearchStrategy, getDefaultSearchParams, getTotalLoaded } from '..';
export const esSearchStrategyProvider = (
- config$: Observable
+ config$: Observable,
+ logger: Logger
): ISearchStrategy => {
return {
search: async (context, request, options) => {
+ logger.info(`search ${JSON.stringify(request.params)}`);
const config = await config$.pipe(first()).toPromise();
const defaultParams = getDefaultSearchParams(config);
diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts
index 8c2ed96503003e..be00b7409fe4a9 100644
--- a/src/plugins/data/server/search/search_service.test.ts
+++ b/src/plugins/data/server/search/search_service.test.ts
@@ -28,7 +28,10 @@ describe('Search service', () => {
let mockCoreSetup: MockedKeys>;
beforeEach(() => {
- plugin = new SearchService(coreMock.createPluginInitializerContext({}));
+ const mockLogger: any = {
+ info: () => {},
+ };
+ plugin = new SearchService(coreMock.createPluginInitializerContext({}), mockLogger);
mockCoreSetup = coreMock.createSetup();
});
diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts
index 5686023e9a667a..bbd0671754749f 100644
--- a/src/plugins/data/server/search/search_service.ts
+++ b/src/plugins/data/server/search/search_service.ts
@@ -22,6 +22,7 @@ import {
PluginInitializerContext,
CoreSetup,
RequestHandlerContext,
+ Logger,
} from '../../../../core/server';
import { ISearchSetup, ISearchStart, ISearchStrategy } from './types';
import { registerSearchRoute } from './routes';
@@ -41,7 +42,10 @@ interface StrategyMap {
export class SearchService implements Plugin {
private searchStrategies: StrategyMap = {};
- constructor(private initializerContext: PluginInitializerContext) {}
+ constructor(
+ private initializerContext: PluginInitializerContext,
+ private readonly logger: Logger
+ ) {}
public setup(
core: CoreSetup
From 7a10077776a729a1f7dc674c04e73b757a1dd2f4 Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Tue, 28 Jul 2020 12:53:36 +0100
Subject: [PATCH 028/102] [Security Solution] Template unit tests (#72399)
* add unit test for failure cases
* add unit tests
* update wording
* fix error when update template without ttid or ttversion
* fix unit test
* add comment
* review
Co-authored-by: Elastic Machine
---
.../rules/pre_packaged_rules/translations.ts | 2 +-
.../update_callout.test.tsx | 92 +++
.../pre_packaged_rules/update_callout.tsx | 9 +-
.../rules/use_pre_packaged_rules.tsx | 4 +-
.../detection_engine/rules/helpers.test.tsx | 136 +++++
...get_prepackaged_rules_status_route.test.ts | 51 ++
.../routes/import_timelines_route.test.ts | 22 +
.../routes/utils/compare_timelines_status.ts | 40 +-
.../routes/utils/failure_cases.test.ts | 542 ++++++++++++++++++
.../timeline/routes/utils/failure_cases.ts | 17 +-
10 files changed, 888 insertions(+), 27 deletions(-)
create mode 100644 x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.test.ts
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/translations.ts
index 37c1715c05d71d..49da7dbf6d514e 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/translations.ts
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/translations.ts
@@ -24,7 +24,7 @@ export const PRE_BUILT_MSG = i18n.translate(
export const PRE_BUILT_ACTION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton',
{
- defaultMessage: 'Load prebuilt detection rules',
+ defaultMessage: 'Load prebuilt detection rules and timeline templates',
}
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx
index 5033fcd11dc7ca..283bba462792ca 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx
@@ -9,6 +9,7 @@ import { shallow } from 'enzyme';
import { UpdatePrePackagedRulesCallOut } from './update_callout';
import { useKibana } from '../../../../common/lib/kibana';
+
jest.mock('../../../../common/lib/kibana');
describe('UpdatePrePackagedRulesCallOut', () => {
@@ -22,6 +23,7 @@ describe('UpdatePrePackagedRulesCallOut', () => {
},
});
});
+
it('renders correctly', () => {
const wrapper = shallow(
{
expect(wrapper.find('EuiCallOut')).toHaveLength(1);
});
+
+ it('renders callOutMessage correctly: numberOfUpdatedRules > 0 and numberOfUpdatedTimelines = 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout"]').find('p').text()).toEqual(
+ 'You can update 1 Elastic prebuilt ruleRelease notes'
+ );
+ });
+
+ it('renders buttonTitle correctly: numberOfUpdatedRules > 0 and numberOfUpdatedTimelines = 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout-button"]').prop('children')).toEqual(
+ 'Update 1 Elastic prebuilt rule'
+ );
+ });
+
+ it('renders callOutMessage correctly: numberOfUpdatedRules = 0 and numberOfUpdatedTimelines > 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout"]').find('p').text()).toEqual(
+ 'You can update 1 Elastic prebuilt timelineRelease notes'
+ );
+ });
+
+ it('renders buttonTitle correctly: numberOfUpdatedRules = 0 and numberOfUpdatedTimelines > 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout-button"]').prop('children')).toEqual(
+ 'Update 1 Elastic prebuilt timeline'
+ );
+ });
+
+ it('renders callOutMessage correctly: numberOfUpdatedRules > 0 and numberOfUpdatedTimelines > 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout"]').find('p').text()).toEqual(
+ 'You can update 1 Elastic prebuilt rule and 1 Elastic prebuilt timeline. Note that this will reload deleted Elastic prebuilt rules.Release notes'
+ );
+ });
+
+ it('renders buttonTitle correctly: numberOfUpdatedRules > 0 and numberOfUpdatedTimelines > 0', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="update-callout-button"]').prop('children')).toEqual(
+ 'Update 1 Elastic prebuilt rule and 1 Elastic prebuilt timeline'
+ );
+ });
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx
index 4b454a9ed4d4a4..30f8cfa7fb3a5f 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx
@@ -51,7 +51,7 @@ const UpdatePrePackagedRulesCallOutComponent: React.FC
+
{prepackagedRulesOrTimelines?.callOutMessage}
@@ -62,7 +62,12 @@ const UpdatePrePackagedRulesCallOutComponent: React.FC
-
+
{prepackagedRulesOrTimelines?.buttonTitle}
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx
index 08c85695e9313f..d82d97883a3d0e 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx
@@ -169,7 +169,9 @@ export const usePrePackagedRules = ({
if (
isSubscribed &&
((prePackagedRuleStatusResponse.rules_not_installed === 0 &&
- prePackagedRuleStatusResponse.rules_not_updated === 0) ||
+ prePackagedRuleStatusResponse.rules_not_updated === 0 &&
+ prePackagedRuleStatusResponse.timelines_not_installed === 0 &&
+ prePackagedRuleStatusResponse.timelines_not_updated === 0) ||
iterationTryOfFetchingPrePackagedCount > 100)
) {
setLoadingCreatePrePackagedRules(false);
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
index c01317e4f48c52..b40243efcfb46d 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
@@ -13,6 +13,8 @@ import {
getActionsStepsData,
getHumanizedDuration,
getModifiedAboutDetailsData,
+ getPrePackagedRuleStatus,
+ getPrePackagedTimelineStatus,
determineDetailsValue,
userHasNoPermissions,
} from './helpers';
@@ -394,4 +396,138 @@ describe('rule helpers', () => {
expect(result).toEqual(userHasNoPermissionsExpectedResult);
});
});
+
+ describe('getPrePackagedRuleStatus', () => {
+ test('ruleNotInstalled', () => {
+ const rulesInstalled = 0;
+ const rulesNotInstalled = 1;
+ const rulesNotUpdated = 0;
+ const result: string = getPrePackagedRuleStatus(
+ rulesInstalled,
+ rulesNotInstalled,
+ rulesNotUpdated
+ );
+
+ expect(result).toEqual('ruleNotInstalled');
+ });
+
+ test('ruleInstalled', () => {
+ const rulesInstalled = 1;
+ const rulesNotInstalled = 0;
+ const rulesNotUpdated = 0;
+ const result: string = getPrePackagedRuleStatus(
+ rulesInstalled,
+ rulesNotInstalled,
+ rulesNotUpdated
+ );
+
+ expect(result).toEqual('ruleInstalled');
+ });
+
+ test('someRuleUninstall', () => {
+ const rulesInstalled = 1;
+ const rulesNotInstalled = 1;
+ const rulesNotUpdated = 0;
+ const result: string = getPrePackagedRuleStatus(
+ rulesInstalled,
+ rulesNotInstalled,
+ rulesNotUpdated
+ );
+
+ expect(result).toEqual('someRuleUninstall');
+ });
+
+ test('ruleNeedUpdate', () => {
+ const rulesInstalled = 1;
+ const rulesNotInstalled = 0;
+ const rulesNotUpdated = 1;
+ const result: string = getPrePackagedRuleStatus(
+ rulesInstalled,
+ rulesNotInstalled,
+ rulesNotUpdated
+ );
+
+ expect(result).toEqual('ruleNeedUpdate');
+ });
+
+ test('unknown', () => {
+ const rulesInstalled = null;
+ const rulesNotInstalled = null;
+ const rulesNotUpdated = null;
+ const result: string = getPrePackagedRuleStatus(
+ rulesInstalled,
+ rulesNotInstalled,
+ rulesNotUpdated
+ );
+
+ expect(result).toEqual('unknown');
+ });
+ });
+
+ describe('getPrePackagedTimelineStatus', () => {
+ test('timelinesNotInstalled', () => {
+ const timelinesInstalled = 0;
+ const timelinesNotInstalled = 1;
+ const timelinesNotUpdated = 0;
+ const result: string = getPrePackagedTimelineStatus(
+ timelinesInstalled,
+ timelinesNotInstalled,
+ timelinesNotUpdated
+ );
+
+ expect(result).toEqual('timelinesNotInstalled');
+ });
+
+ test('timelinesInstalled', () => {
+ const timelinesInstalled = 1;
+ const timelinesNotInstalled = 0;
+ const timelinesNotUpdated = 0;
+ const result: string = getPrePackagedTimelineStatus(
+ timelinesInstalled,
+ timelinesNotInstalled,
+ timelinesNotUpdated
+ );
+
+ expect(result).toEqual('timelinesInstalled');
+ });
+
+ test('someTimelineUninstall', () => {
+ const timelinesInstalled = 1;
+ const timelinesNotInstalled = 1;
+ const timelinesNotUpdated = 0;
+ const result: string = getPrePackagedTimelineStatus(
+ timelinesInstalled,
+ timelinesNotInstalled,
+ timelinesNotUpdated
+ );
+
+ expect(result).toEqual('someTimelineUninstall');
+ });
+
+ test('timelineNeedUpdate', () => {
+ const timelinesInstalled = 1;
+ const timelinesNotInstalled = 0;
+ const timelinesNotUpdated = 1;
+ const result: string = getPrePackagedTimelineStatus(
+ timelinesInstalled,
+ timelinesNotInstalled,
+ timelinesNotUpdated
+ );
+
+ expect(result).toEqual('timelineNeedUpdate');
+ });
+
+ test('unknown', () => {
+ const timelinesInstalled = null;
+ const timelinesNotInstalled = null;
+ const timelinesNotUpdated = null;
+ const result: string = getPrePackagedTimelineStatus(
+ timelinesInstalled,
+ timelinesNotInstalled,
+ timelinesNotUpdated
+ );
+
+ expect(result).toEqual('unknown');
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts
index f8b6f7e3ddcba7..fa2a575d3f69f0 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts
@@ -14,6 +14,11 @@ import {
} from '../__mocks__/request_responses';
import { requestContextMock, serverMock, createMockConfig } from '../__mocks__';
import { SecurityPluginSetup } from '../../../../../../security/server';
+import { checkTimelinesStatus } from '../../../timeline/routes/utils/check_timelines_status';
+import {
+ mockCheckTimelinesStatusBeforeInstallResult,
+ mockCheckTimelinesStatusAfterInstallResult,
+} from '../../../timeline/routes/__mocks__/import_timelines';
jest.mock('../../rules/get_prepackaged_rules', () => {
return {
@@ -38,6 +43,12 @@ jest.mock('../../rules/get_prepackaged_rules', () => {
};
});
+jest.mock('../../../timeline/routes/utils/check_timelines_status', () => {
+ return {
+ checkTimelinesStatus: jest.fn(),
+ };
+});
+
describe('get_prepackaged_rule_status_route', () => {
const mockGetCurrentUser = {
user: {
@@ -126,5 +137,45 @@ describe('get_prepackaged_rule_status_route', () => {
timelines_not_updated: 0,
});
});
+
+ test('0 timelines installed, 3 timelines not installed, 0 timelines not updated', async () => {
+ clients.alertsClient.find.mockResolvedValue(getEmptyFindResult());
+ (checkTimelinesStatus as jest.Mock).mockResolvedValue(
+ mockCheckTimelinesStatusBeforeInstallResult
+ );
+ const request = getPrepackagedRulesStatusRequest();
+ const response = await server.inject(request, context);
+
+ expect(response.status).toEqual(200);
+ expect(response.body).toEqual({
+ rules_custom_installed: 0,
+ rules_installed: 0,
+ rules_not_installed: 1,
+ rules_not_updated: 0,
+ timelines_installed: 0,
+ timelines_not_installed: 3,
+ timelines_not_updated: 0,
+ });
+ });
+
+ test('3 timelines installed, 0 timelines not installed, 0 timelines not updated', async () => {
+ clients.alertsClient.find.mockResolvedValue(getEmptyFindResult());
+ (checkTimelinesStatus as jest.Mock).mockResolvedValue(
+ mockCheckTimelinesStatusAfterInstallResult
+ );
+ const request = getPrepackagedRulesStatusRequest();
+ const response = await server.inject(request, context);
+
+ expect(response.status).toEqual(200);
+ expect(response.body).toEqual({
+ rules_custom_installed: 0,
+ rules_installed: 0,
+ rules_not_installed: 1,
+ rules_not_updated: 0,
+ timelines_installed: 3,
+ timelines_not_installed: 0,
+ timelines_not_updated: 0,
+ });
+ });
});
});
diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts
index fe5993cb0161de..b817896e901c10 100644
--- a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts
@@ -598,6 +598,28 @@ describe('import timeline templates', () => {
mockNewTemplateTimelineId
);
});
+
+ test('should return 200 if create via import without a templateTimelineId or templateTimelineVersion', async () => {
+ mockGetTupleDuplicateErrorsAndUniqueTimeline.mockReturnValue([
+ mockDuplicateIdErrors,
+ [
+ {
+ ...mockUniqueParsedTemplateTimelineObjects[0],
+ templateTimelineId: null,
+ templateTimelineVersion: null,
+ },
+ ],
+ ]);
+ const mockRequest = getImportTimelinesRequest();
+ const result = await server.inject(mockRequest, context);
+ expect(result.body).toEqual({
+ errors: [],
+ success: true,
+ success_count: 1,
+ timelines_installed: 1,
+ timelines_updated: 0,
+ });
+ });
});
describe('Import a timeline template already exist', () => {
diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/compare_timelines_status.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/compare_timelines_status.ts
index d61d217a4cf492..f9515741d12506 100644
--- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/compare_timelines_status.ts
+++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/compare_timelines_status.ts
@@ -3,7 +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.
*/
-import { isEmpty } from 'lodash/fp';
+import { isEmpty, isInteger } from 'lodash/fp';
import {
TimelineTypeLiteralWithNull,
TimelineType,
@@ -71,13 +71,28 @@ export class CompareTimelinesStatus {
}
public get isCreatable() {
+ const noExistingTimeline = this.timelineObject.isCreatable && !this.isHandlingTemplateTimeline;
+
+ const templateCreatable =
+ this.isHandlingTemplateTimeline && this.templateTimelineObject.isCreatable;
+
+ const noExistingTimelineOrTemplate = templateCreatable && this.timelineObject.isCreatable;
+
+ // From Line 87-91 is the condition for creating a template via import without given a templateTimelineId or templateTimelineVersion,
+ // but keep the existing savedObjectId and version there.
+ // Therefore even the timeline exists, we still allow it to create a new timeline template by assigning a templateTimelineId and templateTimelineVersion.
+ // https://github.com/elastic/kibana/pull/67496#discussion_r454337222
+ // Line 90-91 means that we want to make sure the existing timeline retrieved by savedObjectId is atemplate.
+ // If it is not a template, we show an error this timeline is already exist instead.
+ const retriveTemplateViaSavedObjectId =
+ templateCreatable &&
+ !this.timelineObject.isCreatable &&
+ this.timelineObject.getData?.timelineType === this.timelineType;
+
return (
this.isTitleValid &&
!this.isSavedObjectVersionConflict &&
- ((this.timelineObject.isCreatable && !this.isHandlingTemplateTimeline) ||
- (this.templateTimelineObject.isCreatable &&
- this.timelineObject.isCreatable &&
- this.isHandlingTemplateTimeline))
+ (noExistingTimeline || noExistingTimelineOrTemplate || retriveTemplateViaSavedObjectId)
);
}
@@ -195,24 +210,27 @@ export class CompareTimelinesStatus {
}
private get isTemplateVersionConflict() {
- const version = this.templateTimelineObject?.getVersion;
+ const templateTimelineVersion = this.templateTimelineObject?.getVersion;
const existingTemplateTimelineVersion = this.templateTimelineObject?.data
?.templateTimelineVersion;
if (
- version != null &&
+ templateTimelineVersion != null &&
this.templateTimelineObject.isExists &&
existingTemplateTimelineVersion != null
) {
- return version <= existingTemplateTimelineVersion;
- } else if (this.templateTimelineObject.isExists && version == null) {
+ return templateTimelineVersion <= existingTemplateTimelineVersion;
+ } else if (this.templateTimelineObject.isExists && templateTimelineVersion == null) {
return true;
}
return false;
}
private get isTemplateVersionValid() {
- const version = this.templateTimelineObject?.getVersion;
- return typeof version === 'number' && !this.isTemplateVersionConflict;
+ const templateTimelineVersion = this.templateTimelineObject?.getVersion;
+ return (
+ templateTimelineVersion == null ||
+ (isInteger(templateTimelineVersion) && !this.isTemplateVersionConflict)
+ );
}
private get isUpdatedTimelineStatusValid() {
diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.test.ts
new file mode 100644
index 00000000000000..3c3ad1cf2d7f88
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.test.ts
@@ -0,0 +1,542 @@
+/*
+ * 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 {
+ commonFailureChecker,
+ checkIsCreateFailureCases,
+ checkIsUpdateFailureCases,
+ checkIsCreateViaImportFailureCases,
+ EMPTY_TITLE_ERROR_MESSAGE,
+ UPDATE_STATUS_ERROR_MESSAGE,
+ CREATE_TIMELINE_ERROR_MESSAGE,
+ CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE,
+ CREATE_TEMPLATE_TIMELINE_WITHOUT_VERSION_ERROR_MESSAGE,
+ NO_MATCH_ID_ERROR_MESSAGE,
+ NO_MATCH_VERSION_ERROR_MESSAGE,
+ NOT_ALLOW_UPDATE_TIMELINE_TYPE_ERROR_MESSAGE,
+ UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE,
+ CREATE_WITH_INVALID_STATUS_ERROR_MESSAGE,
+ getImportExistingTimelineError,
+ checkIsUpdateViaImportFailureCases,
+ NOT_ALLOW_UPDATE_STATUS_ERROR_MESSAGE,
+ TEMPLATE_TIMELINE_VERSION_CONFLICT_MESSAGE,
+} from './failure_cases';
+import {
+ TimelineStatus,
+ TimelineType,
+ TimelineSavedObject,
+} from '../../../../../common/types/timeline';
+import { mockGetTimelineValue, mockGetTemplateTimelineValue } from '../__mocks__/import_timelines';
+
+describe('failure cases', () => {
+ describe('commonFailureChecker', () => {
+ test('If timeline type is draft, it should not return error if title is not given', () => {
+ const result = commonFailureChecker(TimelineStatus.draft, null);
+
+ expect(result).toBeNull();
+ });
+
+ test('If timeline type is active, it should return error if title is not given', () => {
+ const result = commonFailureChecker(TimelineStatus.active, null);
+
+ expect(result).toEqual({
+ body: EMPTY_TITLE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('If timeline type is immutable, it should return error if title is not given', () => {
+ const result = commonFailureChecker(TimelineStatus.immutable, null);
+
+ expect(result).toEqual({
+ body: EMPTY_TITLE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('If timeline type is not a draft, it should return no error if title is given', () => {
+ const result = commonFailureChecker(TimelineStatus.active, 'title');
+
+ expect(result).toBeNull();
+ });
+ });
+
+ describe('checkIsCreateFailureCases', () => {
+ test('Should return error if trying to create a timeline that is already exist', () => {
+ const isHandlingTemplateTimeline = false;
+ const version = null;
+ const templateTimelineVersion = null;
+ const templateTimelineId = null;
+ const existTimeline = mockGetTimelineValue as TimelineSavedObject;
+ const existTemplateTimeline = null;
+ const result = checkIsCreateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: CREATE_TIMELINE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('Should return error if trying to create a timeline template that is already exist', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = null;
+ const templateTimelineVersion = 1;
+ const templateTimelineId = 'template-timeline-id-one';
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsCreateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('Should return error if trying to create a timeline template without providing templateTimelineVersion', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = null;
+ const templateTimelineVersion = null;
+ const templateTimelineId = 'template-timeline-id-one';
+ const existTimeline = null;
+ const existTemplateTimeline = null;
+ const result = checkIsCreateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: CREATE_TEMPLATE_TIMELINE_WITHOUT_VERSION_ERROR_MESSAGE,
+ statusCode: 403,
+ });
+ });
+ });
+
+ describe('checkIsUpdateFailureCases', () => {
+ test('Should return error if trying to update status field of an existing immutable timeline', () => {
+ const isHandlingTemplateTimeline = false;
+ const version = mockGetTimelineValue.version;
+ const templateTimelineVersion = null;
+ const templateTimelineId = null;
+ const existTimeline = {
+ ...(mockGetTimelineValue as TimelineSavedObject),
+ status: TimelineStatus.immutable,
+ };
+ const existTemplateTimeline = null;
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: UPDATE_STATUS_ERROR_MESSAGE,
+ statusCode: 403,
+ });
+ });
+
+ test('Should return error if trying to update status field of an existing immutable timeline template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = {
+ ...(mockGetTemplateTimelineValue as TimelineSavedObject),
+ status: TimelineStatus.immutable,
+ };
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: UPDATE_STATUS_ERROR_MESSAGE,
+ statusCode: 403,
+ });
+ });
+
+ test('should return error if trying to update timelineType field of an existing timeline template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NOT_ALLOW_UPDATE_TIMELINE_TYPE_ERROR_MESSAGE,
+ statusCode: 403,
+ });
+ });
+
+ test('should return error if trying to update a timeline template that does not exist', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = null;
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if there is no matched timeline found by given templateTimelineId', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = {
+ ...(mockGetTemplateTimelineValue as TimelineSavedObject),
+ savedObjectId: 'someOtherId',
+ };
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NO_MATCH_ID_ERROR_MESSAGE,
+ statusCode: 409,
+ });
+ });
+
+ test('should return error if given version field is defferent from existing version of timelin template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = 'xxx';
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NO_MATCH_VERSION_ERROR_MESSAGE,
+ statusCode: 409,
+ });
+ });
+ });
+
+ describe('checkIsCreateViaImportFailureCases', () => {
+ test('should return error if trying to create a draft timeline', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsCreateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.draft,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: CREATE_WITH_INVALID_STATUS_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if trying to create a timeline template which is already exist', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsCreateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: getImportExistingTimelineError(mockGetTimelineValue.savedObjectId),
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if importe a timeline which is already exists', () => {
+ const isHandlingTemplateTimeline = false;
+ const version = mockGetTimelineValue.version;
+ const templateTimelineVersion = null;
+ const templateTimelineId = null;
+ const existTimeline = mockGetTimelineValue as TimelineSavedObject;
+ const existTemplateTimeline = null;
+ const result = checkIsCreateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: getImportExistingTimelineError(mockGetTimelineValue.savedObjectId),
+ statusCode: 405,
+ });
+ });
+ });
+
+ describe('checkIsUpdateViaImportFailureCases', () => {
+ test('should return error if trying to update a timeline which does not exist', () => {
+ const isHandlingTemplateTimeline = false;
+ const version = mockGetTimelineValue.version;
+ const templateTimelineVersion = null;
+ const templateTimelineId = null;
+ const existTimeline = mockGetTimelineValue as TimelineSavedObject;
+ const existTemplateTimeline = null;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: getImportExistingTimelineError(mockGetTimelineValue.savedObjectId),
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if trying to update timelineType field of an existing timeline template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NOT_ALLOW_UPDATE_TIMELINE_TYPE_ERROR_MESSAGE,
+ statusCode: 403,
+ });
+ });
+
+ test('should return error if trying to update status field of an existing timeline template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.immutable,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NOT_ALLOW_UPDATE_STATUS_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if trying to update a timeline template that does not exist', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = null;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.default,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE,
+ statusCode: 405,
+ });
+ });
+
+ test('should return error if there is no matched timeline found by given templateTimelineId', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = {
+ ...(mockGetTemplateTimelineValue as TimelineSavedObject),
+ savedObjectId: 'someOtherId',
+ };
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NO_MATCH_ID_ERROR_MESSAGE,
+ statusCode: 409,
+ });
+ });
+
+ test('should return error if given version field is defferent from existing version of timelin template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = 'xxx';
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: NO_MATCH_VERSION_ERROR_MESSAGE,
+ statusCode: 409,
+ });
+ });
+
+ test('should return error if given templateTimelineVersion field is less or equal to existing templateTimelineVersion of timelin template', () => {
+ const isHandlingTemplateTimeline = true;
+ const version = mockGetTemplateTimelineValue.version;
+ const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
+ const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
+ const existTimeline = null;
+ const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
+ const result = checkIsUpdateViaImportFailureCases(
+ isHandlingTemplateTimeline,
+ TimelineStatus.active,
+ TimelineType.template,
+ version,
+ templateTimelineVersion,
+ templateTimelineId,
+ existTimeline,
+ existTemplateTimeline
+ );
+
+ expect(result).toEqual({
+ body: TEMPLATE_TIMELINE_VERSION_CONFLICT_MESSAGE,
+ statusCode: 409,
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.ts
index d41e8fc1909836..b926819d66c92b 100644
--- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.ts
+++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/failure_cases.ts
@@ -78,7 +78,10 @@ const commonUpdateTemplateTimelineCheck = (
existTemplateTimeline: TimelineSavedObject | null
) => {
if (isHandlingTemplateTimeline) {
- if (existTimeline != null && timelineType !== existTimeline.timelineType) {
+ if (
+ (existTimeline != null && timelineType !== existTimeline.timelineType) ||
+ (existTemplateTimeline != null && timelineType !== existTemplateTimeline.timelineType)
+ ) {
return {
body: NOT_ALLOW_UPDATE_TIMELINE_TYPE_ERROR_MESSAGE,
statusCode: 403,
@@ -106,11 +109,7 @@ const commonUpdateTemplateTimelineCheck = (
};
}
- if (
- existTemplateTimeline != null &&
- existTemplateTimeline.templateTimelineVersion == null &&
- existTemplateTimeline.version !== version
- ) {
+ if (existTemplateTimeline != null && existTemplateTimeline.version !== version) {
// throw error 409 conflict timeline
return {
body: NO_MATCH_VERSION_ERROR_MESSAGE,
@@ -231,12 +230,6 @@ export const checkIsUpdateViaImportFailureCases = (
};
}
} else {
- if (existTemplateTimeline != null && timelineType !== existTemplateTimeline?.timelineType) {
- return {
- body: NOT_ALLOW_UPDATE_TIMELINE_TYPE_ERROR_MESSAGE,
- statusCode: 403,
- };
- }
const isStatusValid =
((existTemplateTimeline?.status == null ||
existTemplateTimeline?.status === TimelineStatus.active) &&
From 8c710aae3a7702ecd16e7dab997ed331103ff165 Mon Sep 17 00:00:00 2001
From: Marco Liberati
Date: Tue, 28 Jul 2020 14:21:24 +0200
Subject: [PATCH 029/102] [ Functional test ] Increase the waiting time for the
filter bar request (#73424)
---
.../apps/visualize/input_control_vis/chained_controls.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/functional/apps/visualize/input_control_vis/chained_controls.js b/test/functional/apps/visualize/input_control_vis/chained_controls.js
index 179ffa5125a9a1..89cca7dc7827ec 100644
--- a/test/functional/apps/visualize/input_control_vis/chained_controls.js
+++ b/test/functional/apps/visualize/input_control_vis/chained_controls.js
@@ -34,6 +34,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.loadSavedVisualization('chained input control', {
navigateToVisualize: false,
});
+ await testSubjects.waitForEnabled('addFilter', 10000);
});
it('should disable child control when parent control is not set', async () => {
From 49846834ebae9d2e1d0ac67353649f0c13ed9dd8 Mon Sep 17 00:00:00 2001
From: MadameSheema
Date: Tue, 28 Jul 2020 15:23:05 +0200
Subject: [PATCH 030/102] [SIEM] Unskips and fixes Cypress tests (#73322)
* removes not needed configuration
* fixes events columnts tests
* unksips persisten timeline test
* fixes failing test
* skips events test since need more time for investigation
Co-authored-by: Elastic Machine
---
.../cypress/integration/timeline_local_storage.spec.ts | 3 +--
x-pack/plugins/security_solution/cypress/tasks/common.ts | 8 ++++----
.../security_solution/cypress/tasks/hosts/events.ts | 2 +-
x-pack/test/security_solution_cypress/config.ts | 2 --
4 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
index 7c047459c56cc4..383ebe22205859 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
@@ -13,8 +13,7 @@ import { TABLE_COLUMN_EVENTS_MESSAGE } from '../screens/hosts/external_events';
import { waitsForEventsToBeLoaded, openEventsViewerFieldsBrowser } from '../tasks/hosts/events';
import { removeColumn, resetFields } from '../tasks/timeline';
-// Failing: See https://github.com/elastic/kibana/issues/72339
-describe.skip('persistent timeline', () => {
+describe('persistent timeline', () => {
before(() => {
loginAndWaitForPage(HOSTS_URL);
openEvents();
diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts
index a385ad78f63b7b..e16db545999812 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/common.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts
@@ -23,14 +23,14 @@ export const drag = (subject: JQuery) => {
clientY: subjectLocation.top,
force: true,
})
- .wait(1000)
+ .wait(3000)
.trigger('mousemove', {
button: primaryButton,
clientX: subjectLocation.left + dndSloppyClickDetectionThreshold,
clientY: subjectLocation.top,
force: true,
})
- .wait(1000);
+ .wait(3000);
};
/** Drags the subject being dragged on the specified drop target, but does not drop it */
@@ -44,9 +44,9 @@ export const dragWithoutDrop = (dropTarget: JQuery) => {
export const drop = (dropTarget: JQuery) => {
cy.wrap(dropTarget)
.trigger('mousemove', { button: primaryButton, force: true })
- .wait(1000)
+ .wait(3000)
.trigger('mouseup', { force: true })
- .wait(1000);
+ .wait(3000);
};
export const reload = (afterReload: () => void) => {
diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
index 57c819d9678835..1d2c4aa8d08343 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
@@ -68,7 +68,7 @@ export const dragAndDropColumn = ({
.eq(column)
.then((header) => drag(header));
- cy.wait(3000); // wait for DOM updates before moving
+ cy.wait(5000); // wait for DOM updates before moving
cy.get(DRAGGABLE_HEADER)
.eq(newPosition)
diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts
index 1ad3a36cc57ae5..83290a60a17a66 100644
--- a/x-pack/test/security_solution_cypress/config.ts
+++ b/x-pack/test/security_solution_cypress/config.ts
@@ -46,8 +46,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'--csp.strict=false',
// define custom kibana server args here
`--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`,
- '--xpack.ingestManager.enabled=true',
- '--xpack.ingestManager.fleet.enabled=true',
],
},
};
From 19532fc43911d887bebc3ecabae57706509e25ff Mon Sep 17 00:00:00 2001
From: Dario Gieselaar
Date: Tue, 28 Jul 2020 15:53:23 +0200
Subject: [PATCH 031/102] [APM] Optimize traces overview (#70200)
Co-authored-by: Elastic Machine
---
.../app/TraceOverview/TraceList.tsx | 12 +-
.../app/TransactionOverview/List/index.tsx | 12 +-
.../apm/public/hooks/useTransactionList.ts | 40 +-
.../aggregate-latency-metrics/index.ts | 6 +-
.../apm/scripts/shared/read-kibana-config.ts | 4 +-
...egister_transaction_duration_alert_type.ts | 2 +-
.../metrics/fetch_and_transform_metrics.ts | 37 +-
.../lib/metrics/transform_metrics_chart.ts | 37 +-
.../get_services/get_services_items_stats.ts | 3 +-
.../__snapshots__/fetcher.test.ts.snap | 228 -
.../__snapshots__/queries.test.ts.snap | 557 ++-
.../__snapshots__/transform.test.ts.snap | 2822 ------------
.../lib/transaction_groups/fetcher.test.ts | 64 -
.../server/lib/transaction_groups/fetcher.ts | 185 +-
.../get_transaction_group_stats.ts | 144 +
.../server/lib/transaction_groups/index.ts | 10 +-
.../lib/transaction_groups/queries.test.ts | 8 +-
.../lib/transaction_groups/transform.test.ts | 135 -
.../lib/transaction_groups/transform.ts | 89 -
.../get_local_filter_query.ts | 2 +-
.../lib/ui_filters/local_ui_filters/index.ts | 1 +
.../apm/typings/elasticsearch/aggregations.ts | 20 +-
.../expectation/top_traces.expectation.json | 3970 ++++++++++++-----
.../basic/tests/traces/top_traces.ts | 25 +-
.../expectation/top_transaction_groups.json | 2639 ++++++++---
.../top_transaction_groups.ts | 25 +-
26 files changed, 5606 insertions(+), 5471 deletions(-)
delete mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
delete mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
delete mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
create mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
delete mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
delete mode 100644 x-pack/plugins/apm/server/lib/transaction_groups/transform.ts
diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
index 898e32f5c2c093..f54255ec0cd186 100644
--- a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
+++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
@@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from 'styled-components';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { ITransactionGroup } from '../../../../server/lib/transaction_groups/transform';
+import { TransactionGroup } from '../../../../server/lib/transaction_groups/fetcher';
import { fontSizes, truncate } from '../../../style/variables';
import { asMillisecondDuration } from '../../../utils/formatters';
import { EmptyMessage } from '../../shared/EmptyMessage';
@@ -24,11 +24,11 @@ const StyledTransactionLink = styled(TransactionDetailLink)`
`;
interface Props {
- items: ITransactionGroup[];
+ items: TransactionGroup[];
isLoading: boolean;
}
-const traceListColumns: Array> = [
+const traceListColumns: Array> = [
{
field: 'name',
name: i18n.translate('xpack.apm.tracesTable.nameColumnLabel', {
@@ -36,8 +36,8 @@ const traceListColumns: Array> = [
}),
width: '40%',
sortable: true,
- render: (name: string, { sample }: ITransactionGroup) => (
-
+ render: (_: string, { sample }: TransactionGroup) => (
+
> = [
transactionName={sample.transaction.name}
transactionType={sample.transaction.type}
>
- {name}
+ {sample.transaction.name}
),
diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
index ae1b07bde0c87a..2b1c1b8e8c11c8 100644
--- a/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
@@ -10,7 +10,7 @@ import React, { useMemo } from 'react';
import styled from 'styled-components';
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { ITransactionGroup } from '../../../../../server/lib/transaction_groups/transform';
+import { TransactionGroup } from '../../../../../server/lib/transaction_groups/fetcher';
import { fontFamilyCode, truncate } from '../../../../style/variables';
import { asDecimal, asMillisecondDuration } from '../../../../utils/formatters';
import { ImpactBar } from '../../../shared/ImpactBar';
@@ -25,12 +25,12 @@ const TransactionNameLink = styled(TransactionDetailLink)`
`;
interface Props {
- items: ITransactionGroup[];
+ items: TransactionGroup[];
isLoading: boolean;
}
export function TransactionList({ items, isLoading }: Props) {
- const columns: Array> = useMemo(
+ const columns: Array> = useMemo(
() => [
{
field: 'name',
@@ -39,11 +39,11 @@ export function TransactionList({ items, isLoading }: Props) {
}),
width: '50%',
sortable: true,
- render: (transactionName: string, { sample }: ITransactionGroup) => {
+ render: (_, { sample }: TransactionGroup) => {
return (
- {transactionName || NOT_AVAILABLE_LABEL}
+ {sample.transaction.name || NOT_AVAILABLE_LABEL}
);
diff --git a/x-pack/plugins/apm/public/hooks/useTransactionList.ts b/x-pack/plugins/apm/public/hooks/useTransactionList.ts
index ed6bb9309a557c..0ad221b95b4ffe 100644
--- a/x-pack/plugins/apm/public/hooks/useTransactionList.ts
+++ b/x-pack/plugins/apm/public/hooks/useTransactionList.ts
@@ -4,45 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { useMemo } from 'react';
import { IUrlParams } from '../context/UrlParamsContext/types';
import { useUiFilters } from '../context/UrlParamsContext';
import { useFetcher } from './useFetcher';
import { APIReturnType } from '../services/rest/createCallApmApi';
-const getRelativeImpact = (
- impact: number,
- impactMin: number,
- impactMax: number
-) =>
- Math.max(
- ((impact - impactMin) / Math.max(impactMax - impactMin, 1)) * 100,
- 1
- );
-
type TransactionsAPIResponse = APIReturnType<
'/api/apm/services/{serviceName}/transaction_groups'
>;
-function getWithRelativeImpact(items: TransactionsAPIResponse['items']) {
- const impacts = items
- .map(({ impact }) => impact)
- .filter((impact) => impact !== null) as number[];
-
- const impactMin = Math.min(...impacts);
- const impactMax = Math.max(...impacts);
-
- return items.map((item) => {
- return {
- ...item,
- impactRelative:
- item.impact !== null
- ? getRelativeImpact(item.impact, impactMin, impactMax)
- : null,
- };
- });
-}
-
const DEFAULT_RESPONSE: TransactionsAPIResponse = {
items: [],
isAggregationAccurate: true,
@@ -72,16 +42,8 @@ export function useTransactionList(urlParams: IUrlParams) {
[serviceName, start, end, transactionType, uiFilters]
);
- const memoizedData = useMemo(
- () => ({
- items: getWithRelativeImpact(data.items),
- isAggregationAccurate: data.isAggregationAccurate,
- bucketSize: data.bucketSize,
- }),
- [data]
- );
return {
- data: memoizedData,
+ data,
status,
error,
};
diff --git a/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts b/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts
index 28b095335e93d8..c3cf363cbec057 100644
--- a/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts
+++ b/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts
@@ -10,7 +10,7 @@ import pLimit from 'p-limit';
import pRetry from 'p-retry';
import { parse, format } from 'url';
import { set } from '@elastic/safer-lodash-set';
-import { unique, without, merge, flatten } from 'lodash';
+import { uniq, without, merge, flatten } from 'lodash';
import * as histogram from 'hdr-histogram-js';
import { ESSearchResponse } from '../../typings/elasticsearch';
import {
@@ -114,8 +114,8 @@ export async function aggregateLatencyMetrics() {
.filter(Boolean) as string[];
const fields = only.length
- ? unique(only)
- : without(unique([...include, ...defaultFields]), ...exclude);
+ ? uniq(only)
+ : without(uniq([...include, ...defaultFields]), ...exclude);
const globalFilter = argv.filter ? JSON.parse(String(argv.filter)) : {};
diff --git a/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts b/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts
index bc5f1afc63cacb..fe226c8ab27d23 100644
--- a/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts
+++ b/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts
@@ -6,7 +6,7 @@
import path from 'path';
import fs from 'fs';
import yaml from 'js-yaml';
-import { identity, pick } from 'lodash';
+import { identity, pickBy } from 'lodash';
export type KibanaConfig = ReturnType;
@@ -22,7 +22,7 @@ export const readKibanaConfig = () => {
)
) || {}) as {};
- const cliEsCredentials = pick(
+ const cliEsCredentials = pickBy(
{
'elasticsearch.username': process.env.ELASTICSEARCH_USERNAME,
'elasticsearch.password': process.env.ELASTICSEARCH_PASSWORD,
diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
index 1d14c509274a89..a922457b14cea7 100644
--- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
@@ -157,7 +157,7 @@ export function registerTransactionDurationAlertType({
const { agg } = response.aggregations;
- const value = 'values' in agg ? agg.values[0] : agg?.value;
+ const value = 'values' in agg ? Object.values(agg.values)[0] : agg?.value;
if (value && value > alertParams.threshold * 1000) {
const alertInstance = services.alertInstanceFactory(
diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index 6de2728ee4366b..895920a9b6c7d5 100644
--- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Unionize } from 'utility-types';
+import { Unionize, Overwrite } from 'utility-types';
+import { ESSearchRequest } from '../../../typings/elasticsearch';
import {
Setup,
SetupTimeRange,
@@ -17,14 +18,28 @@ import { getMetricsProjection } from '../../../common/projections/metrics';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
-interface Aggs {
- [key: string]: Unionize<{
- min: AggregationOptionsByType['min'];
- max: AggregationOptionsByType['max'];
- sum: AggregationOptionsByType['sum'];
- avg: AggregationOptionsByType['avg'];
- }>;
-}
+type MetricsAggregationMap = Unionize<{
+ min: AggregationOptionsByType['min'];
+ max: AggregationOptionsByType['max'];
+ sum: AggregationOptionsByType['sum'];
+ avg: AggregationOptionsByType['avg'];
+}>;
+
+type MetricAggs = Record;
+
+export type GenericMetricsRequest = Overwrite<
+ ESSearchRequest,
+ {
+ body: {
+ aggs: {
+ timeseriesData: {
+ date_histogram: AggregationOptionsByType['date_histogram'];
+ aggs: MetricAggs;
+ };
+ } & MetricAggs;
+ };
+ }
+>;
interface Filter {
exists?: {
@@ -35,7 +50,7 @@ interface Filter {
};
}
-export async function fetchAndTransformMetrics({
+export async function fetchAndTransformMetrics({
setup,
serviceName,
serviceNodeName,
@@ -58,7 +73,7 @@ export async function fetchAndTransformMetrics({
serviceNodeName,
});
- const params = mergeProjection(projection, {
+ const params: GenericMetricsRequest = mergeProjection(projection, {
body: {
size: 0,
query: {
diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
index affb7c2a120750..a191d5400e36cf 100644
--- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
@@ -4,40 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
-import { Unionize, Overwrite } from 'utility-types';
import { ChartBase } from './types';
-import {
- ESSearchResponse,
- ESSearchRequest,
-} from '../../../typings/elasticsearch';
-import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
+import { ESSearchResponse } from '../../../typings/elasticsearch';
import { getVizColorForIndex } from '../../../common/viz_colors';
+import { GenericMetricsRequest } from './fetch_and_transform_metrics';
export type GenericMetricsChart = ReturnType<
typeof transformDataToMetricsChart
>;
-interface MetricsAggregationMap {
- min: AggregationOptionsByType['min'];
- max: AggregationOptionsByType['max'];
- sum: AggregationOptionsByType['sum'];
- avg: AggregationOptionsByType['avg'];
-}
-
-type GenericMetricsRequest = Overwrite<
- ESSearchRequest,
- {
- body: {
- aggs: {
- timeseriesData: {
- date_histogram: AggregationOptionsByType['date_histogram'];
- aggs: Record>;
- };
- } & Record>;
- };
- }
->;
-
export function transformDataToMetricsChart(
result: ESSearchResponse,
chartBase: ChartBase
@@ -51,11 +26,7 @@ export function transformDataToMetricsChart(
yUnit: chartBase.yUnit,
noHits: hits.total.value === 0,
series: Object.keys(chartBase.series).map((seriesKey, i) => {
- const overallValue = (aggregations?.[seriesKey] as
- | {
- value: number | null;
- }
- | undefined)?.value;
+ const overallValue = aggregations?.[seriesKey]?.value;
return {
title: chartBase.series[seriesKey].title,
@@ -66,7 +37,7 @@ export function transformDataToMetricsChart(
overallValue,
data:
timeseriesData?.buckets.map((bucket) => {
- const { value } = bucket[seriesKey] as { value: number | null };
+ const { value } = bucket[seriesKey];
const y = value === null || isNaN(value) ? null : value;
return {
x: bucket.key,
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts
index c28bcad841ffd8..de699028f56754 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { arrayUnionToCallable } from '../../../../common/utils/array_union_to_callable';
import {
PROCESSOR_EVENT,
TRANSACTION_DURATION,
@@ -187,7 +186,7 @@ export const getTransactionRates = async ({
const deltaAsMinutes = getDeltaAsMinutes(setup);
- return arrayUnionToCallable(aggregations.services.buckets).map((bucket) => {
+ return aggregations.services.buckets.map((bucket) => {
const transactionsPerMinute = bucket.doc_count / deltaAsMinutes;
return {
serviceName: bucket.key as string,
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
deleted file mode 100644
index b354d3ed1f88db..00000000000000
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
+++ /dev/null
@@ -1,228 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`transactionGroupsFetcher type: top_traces should call client.search with correct query 1`] = `
-Array [
- Array [
- Object {
- "body": Object {
- "aggs": Object {
- "transaction_groups": Object {
- "aggs": Object {
- "avg": Object {
- "avg": Object {
- "field": "transaction.duration.us",
- },
- },
- "p95": Object {
- "percentiles": Object {
- "field": "transaction.duration.us",
- "hdr": Object {
- "number_of_significant_value_digits": 2,
- },
- "percents": Array [
- 95,
- ],
- },
- },
- "sample": Object {
- "top_hits": Object {
- "size": 1,
- "sort": Array [
- Object {
- "_score": "desc",
- },
- Object {
- "@timestamp": Object {
- "order": "desc",
- },
- },
- ],
- },
- },
- "sum": Object {
- "sum": Object {
- "field": "transaction.duration.us",
- },
- },
- },
- "composite": Object {
- "size": 10000,
- "sources": Array [
- Object {
- "service": Object {
- "terms": Object {
- "field": "service.name",
- },
- },
- },
- Object {
- "transaction": Object {
- "terms": Object {
- "field": "transaction.name",
- },
- },
- },
- ],
- },
- },
- },
- "query": Object {
- "bool": Object {
- "filter": Array [
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
- Object {
- "term": Object {
- "processor.event": "transaction",
- },
- },
- Object {
- "term": Object {
- "service.environment": "test",
- },
- },
- ],
- "must_not": Array [
- Object {
- "exists": Object {
- "field": "parent.id",
- },
- },
- ],
- "should": Array [
- Object {
- "term": Object {
- "transaction.sampled": true,
- },
- },
- ],
- },
- },
- "size": 0,
- },
- "index": "myIndex",
- },
- ],
-]
-`;
-
-exports[`transactionGroupsFetcher type: top_transactions should call client.search with correct query 1`] = `
-Array [
- Array [
- Object {
- "body": Object {
- "aggs": Object {
- "transaction_groups": Object {
- "aggs": Object {
- "avg": Object {
- "avg": Object {
- "field": "transaction.duration.us",
- },
- },
- "p95": Object {
- "percentiles": Object {
- "field": "transaction.duration.us",
- "hdr": Object {
- "number_of_significant_value_digits": 2,
- },
- "percents": Array [
- 95,
- ],
- },
- },
- "sample": Object {
- "top_hits": Object {
- "size": 1,
- "sort": Array [
- Object {
- "_score": "desc",
- },
- Object {
- "@timestamp": Object {
- "order": "desc",
- },
- },
- ],
- },
- },
- "sum": Object {
- "sum": Object {
- "field": "transaction.duration.us",
- },
- },
- },
- "composite": Object {
- "size": 101,
- "sources": Array [
- Object {
- "transaction": Object {
- "terms": Object {
- "field": "transaction.name",
- },
- },
- },
- ],
- },
- },
- "transactions": Object {
- "terms": Object {
- "field": "transaction.name",
- },
- },
- },
- "query": Object {
- "bool": Object {
- "filter": Array [
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
- Object {
- "term": Object {
- "processor.event": "transaction",
- },
- },
- Object {
- "term": Object {
- "transaction.type": "request",
- },
- },
- Object {
- "term": Object {
- "service.name": "opbeans-node",
- },
- },
- Object {
- "term": Object {
- "service.environment": "test",
- },
- },
- ],
- "should": Array [
- Object {
- "term": Object {
- "transaction.sampled": true,
- },
- },
- ],
- },
- },
- "size": 0,
- },
- "index": "myIndex",
- },
- ],
-]
-`;
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index 884a7d18cc4d4e..deca46f4ebd0cd 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -1,220 +1,479 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`transaction group queries fetches top traces 1`] = `
-Object {
- "body": Object {
- "aggs": Object {
- "transaction_groups": Object {
- "aggs": Object {
- "avg": Object {
- "avg": Object {
- "field": "transaction.duration.us",
- },
- },
- "p95": Object {
- "percentiles": Object {
- "field": "transaction.duration.us",
- "hdr": Object {
- "number_of_significant_value_digits": 2,
+Array [
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "sample": Object {
+ "top_hits": Object {
+ "size": 1,
},
- "percents": Array [
- 95,
- ],
},
},
- "sample": Object {
- "top_hits": Object {
- "size": 1,
- "sort": Array [
- Object {
- "_score": "desc",
+ "composite": Object {
+ "size": 10000,
+ "sources": Array [
+ Object {
+ "service.name": Object {
+ "terms": Object {
+ "field": "service.name",
+ },
},
- Object {
- "@timestamp": Object {
- "order": "desc",
+ },
+ Object {
+ "transaction.name": Object {
+ "terms": Object {
+ "field": "transaction.name",
},
},
- ],
+ },
+ ],
+ },
+ },
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
+ },
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ ],
+ "must_not": Array [
+ Object {
+ "exists": Object {
+ "field": "parent.id",
+ },
+ },
+ ],
+ "should": Array [
+ Object {
+ "term": Object {
+ "transaction.sampled": true,
+ },
},
+ ],
+ },
+ },
+ "sort": Array [
+ Object {
+ "_score": "desc",
+ },
+ Object {
+ "@timestamp": Object {
+ "order": "desc",
},
- "sum": Object {
- "sum": Object {
- "field": "transaction.duration.us",
+ },
+ ],
+ },
+ "index": "myIndex",
+ "size": 0,
+ },
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "avg": Object {
+ "avg": Object {
+ "field": "transaction.duration.us",
+ },
},
},
+ "composite": Object {
+ "size": 10000,
+ "sources": Array [
+ Object {
+ "service.name": Object {
+ "terms": Object {
+ "field": "service.name",
+ },
+ },
+ },
+ Object {
+ "transaction.name": Object {
+ "terms": Object {
+ "field": "transaction.name",
+ },
+ },
+ },
+ ],
+ },
},
- "composite": Object {
- "size": 10000,
- "sources": Array [
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
Object {
- "service": Object {
- "terms": Object {
- "field": "service.name",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
},
},
},
Object {
- "transaction": Object {
- "terms": Object {
- "field": "transaction.name",
- },
+ "term": Object {
+ "processor.event": "transaction",
+ },
+ },
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ ],
+ "must_not": Array [
+ Object {
+ "exists": Object {
+ "field": "parent.id",
},
},
],
},
},
},
- "query": Object {
- "bool": Object {
- "filter": Array [
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
+ "index": "myIndex",
+ "size": 0,
+ },
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "sum": Object {
+ "sum": Object {
+ "field": "transaction.duration.us",
},
},
},
- Object {
- "term": Object {
- "processor.event": "transaction",
- },
+ "composite": Object {
+ "size": 10000,
+ "sources": Array [
+ Object {
+ "service.name": Object {
+ "terms": Object {
+ "field": "service.name",
+ },
+ },
+ },
+ Object {
+ "transaction.name": Object {
+ "terms": Object {
+ "field": "transaction.name",
+ },
+ },
+ },
+ ],
},
- Object {
- "term": Object {
- "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
},
- },
- ],
- "must_not": Array [
- Object {
- "exists": Object {
- "field": "parent.id",
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
},
- },
- ],
- "should": Array [
- Object {
- "term": Object {
- "transaction.sampled": true,
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
},
- },
- ],
+ ],
+ "must_not": Array [
+ Object {
+ "exists": Object {
+ "field": "parent.id",
+ },
+ },
+ ],
+ },
},
},
+ "index": "myIndex",
"size": 0,
},
- "index": "myIndex",
-}
+]
`;
exports[`transaction group queries fetches top transactions 1`] = `
-Object {
- "body": Object {
- "aggs": Object {
- "transaction_groups": Object {
- "aggs": Object {
- "avg": Object {
- "avg": Object {
- "field": "transaction.duration.us",
- },
- },
- "p95": Object {
- "percentiles": Object {
- "field": "transaction.duration.us",
- "hdr": Object {
- "number_of_significant_value_digits": 2,
+Array [
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "sample": Object {
+ "top_hits": Object {
+ "size": 1,
},
- "percents": Array [
- 95,
- ],
},
},
- "sample": Object {
- "top_hits": Object {
- "size": 1,
- "sort": Array [
- Object {
- "_score": "desc",
- },
- Object {
- "@timestamp": Object {
- "order": "desc",
- },
+ "terms": Object {
+ "field": "transaction.name",
+ "size": 101,
+ },
+ },
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
},
- ],
+ },
},
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
+ },
+ Object {
+ "term": Object {
+ "transaction.type": "bar",
+ },
+ },
+ Object {
+ "term": Object {
+ "service.name": "foo",
+ },
+ },
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ ],
+ "should": Array [
+ Object {
+ "term": Object {
+ "transaction.sampled": true,
+ },
+ },
+ ],
+ },
+ },
+ "sort": Array [
+ Object {
+ "_score": "desc",
+ },
+ Object {
+ "@timestamp": Object {
+ "order": "desc",
},
- "sum": Object {
- "sum": Object {
- "field": "transaction.duration.us",
+ },
+ ],
+ },
+ "index": "myIndex",
+ "size": 0,
+ },
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "avg": Object {
+ "avg": Object {
+ "field": "transaction.duration.us",
+ },
},
},
+ "terms": Object {
+ "field": "transaction.name",
+ "size": 101,
+ },
},
- "composite": Object {
- "size": 101,
- "sources": Array [
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
Object {
- "transaction": Object {
- "terms": Object {
- "field": "transaction.name",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
},
},
},
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
+ },
+ Object {
+ "term": Object {
+ "transaction.type": "bar",
+ },
+ },
+ Object {
+ "term": Object {
+ "service.name": "foo",
+ },
+ },
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
],
},
},
- "transactions": Object {
- "terms": Object {
- "field": "transaction.name",
+ },
+ "index": "myIndex",
+ "size": 0,
+ },
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "sum": Object {
+ "sum": Object {
+ "field": "transaction.duration.us",
+ },
+ },
+ },
+ "terms": Object {
+ "field": "transaction.name",
+ "size": 101,
+ },
+ },
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
+ },
+ Object {
+ "term": Object {
+ "transaction.type": "bar",
+ },
+ },
+ Object {
+ "term": Object {
+ "service.name": "foo",
+ },
+ },
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ ],
},
},
},
- "query": Object {
- "bool": Object {
- "filter": Array [
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
+ "index": "myIndex",
+ "size": 0,
+ },
+ Object {
+ "body": Object {
+ "aggs": Object {
+ "transaction_groups": Object {
+ "aggs": Object {
+ "p95": Object {
+ "percentiles": Object {
+ "field": "transaction.duration.us",
+ "hdr": Object {
+ "number_of_significant_value_digits": 2,
+ },
+ "percents": Array [
+ 95,
+ ],
},
},
},
- Object {
- "term": Object {
- "processor.event": "transaction",
- },
+ "terms": Object {
+ "field": "transaction.name",
+ "size": 101,
},
- Object {
- "term": Object {
- "transaction.type": "bar",
+ },
+ },
+ "query": Object {
+ "bool": Object {
+ "filter": Array [
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
},
- },
- Object {
- "term": Object {
- "service.name": "foo",
+ Object {
+ "term": Object {
+ "processor.event": "transaction",
+ },
},
- },
- Object {
- "term": Object {
- "my.custom.ui.filter": "foo-bar",
+ Object {
+ "term": Object {
+ "transaction.type": "bar",
+ },
},
- },
- ],
- "should": Array [
- Object {
- "term": Object {
- "transaction.sampled": true,
+ Object {
+ "term": Object {
+ "service.name": "foo",
+ },
},
- },
- ],
+ Object {
+ "term": Object {
+ "my.custom.ui.filter": "foo-bar",
+ },
+ },
+ ],
+ },
},
},
+ "index": "myIndex",
"size": 0,
},
- "index": "myIndex",
-}
+]
`;
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
deleted file mode 100644
index 66b805ab2efc15..00000000000000
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
+++ /dev/null
@@ -1,2822 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`transactionGroupsTransformer should match snapshot 1`] = `
-Array [
- Object {
- "averageResponseTime": 48021.972616494,
- "impact": 100,
- "name": "GET /api",
- "p95": 67138.18364917398,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:44.070Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 5176,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-86c68779d8a65b06fb78e770ffc436a5-4aaea53dc1791183-01",
- "host": "opbeans-node:3000",
- "user-agent": "python-requests/2.20.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.6",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/types/3",
- "hostname": "opbeans-node",
- "pathname": "/api/types/3",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/types/3",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-type": "application/json;charset=UTF-8",
- "date": "Sun, 18 Nov 2018 20:53:43 GMT",
- "transfer-encoding": "chunked",
- "x-powered-by": "Express",
- },
- "status_code": 404,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "4aaea53dc1791183",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574424070007,
- },
- "trace": Object {
- "id": "86c68779d8a65b06fb78e770ffc436a5",
- },
- "transaction": Object {
- "duration": Object {
- "us": 8684,
- },
- "id": "a78bca581dcd8ff8",
- "name": "GET /api",
- "result": "HTTP 4xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 691926.3157894736,
- },
- Object {
- "averageResponseTime": 2651.8784461553205,
- "impact": 15.770246496477105,
- "name": "GET static file",
- "p95": 6140.579335038363,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:43.304Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "host": "opbeans-node:3000",
- "user-agent": "curl/7.38.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/",
- "hostname": "opbeans-node",
- "pathname": "/",
- "port": "3000",
- "protocol": "http:",
- "raw": "/",
- },
- },
- "response": Object {
- "headers": Object {
- "accept-ranges": "bytes",
- "cache-control": "public, max-age=0",
- "connection": "keep-alive",
- "content-length": "640",
- "content-type": "text/html; charset=UTF-8",
- "date": "Sun, 18 Nov 2018 20:53:43 GMT",
- "etag": "W/\\"280-1670775e878\\"",
- "last-modified": "Mon, 12 Nov 2018 10:27:07 GMT",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574423304006,
- },
- "trace": Object {
- "id": "b303d2a4a007946b63b9db7fafe639a0",
- },
- "transaction": Object {
- "duration": Object {
- "us": 1801,
- },
- "id": "2869c13633534be5",
- "name": "GET static file",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 1977031.5789473683,
- },
- Object {
- "averageResponseTime": 32554.36257814184,
- "impact": 14.344171563678346,
- "name": "GET /api/stats",
- "p95": 59356.73611111111,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:42.560Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 207,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-63ccc3b0929dafb7f2fbcabdc7f7af25-821a787e73ab1563-01",
- "host": "opbeans-node:3000",
- "if-none-match": "W/\\"77-uxKJrX5GSMJJWTKh3orUFAEVxSs\\"",
- "referer": "http://opbeans-node:3000/dashboard",
- "user-agent": "Chromeless 1.4.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.7",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/stats",
- "hostname": "opbeans-node",
- "pathname": "/api/stats",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/stats",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "date": "Sun, 18 Nov 2018 20:53:42 GMT",
- "etag": "W/\\"77-uxKJrX5GSMJJWTKh3orUFAEVxSs\\"",
- "x-powered-by": "Express",
- },
- "status_code": 304,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "821a787e73ab1563",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574422560002,
- },
- "trace": Object {
- "id": "63ccc3b0929dafb7f2fbcabdc7f7af25",
- },
- "transaction": Object {
- "duration": Object {
- "us": 28753,
- },
- "id": "fb754e7628da2fb5",
- "name": "GET /api/stats",
- "result": "HTTP 3xx",
- "sampled": true,
- "span_count": Object {
- "started": 7,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 146494.73684210525,
- },
- Object {
- "averageResponseTime": 32159.926322043968,
- "impact": 10.27904952170656,
- "name": "GET /api/customers",
- "p95": 59845.85714285714,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:21.180Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 2531,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3710,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-541025da8ecc2f51f21c1a4ad6992b77-ca18d9d4c3879519-01",
- "host": "opbeans-node:3000",
- "user-agent": "python-requests/2.20.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.6",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/customers",
- "hostname": "opbeans-node",
- "pathname": "/api/customers",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/customers",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "186769",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:53:21 GMT",
- "etag": "W/\\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "ca18d9d4c3879519",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574401180002,
- },
- "trace": Object {
- "id": "541025da8ecc2f51f21c1a4ad6992b77",
- },
- "transaction": Object {
- "duration": Object {
- "us": 18077,
- },
- "id": "94852b9dd1075982",
- "name": "GET /api/customers",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 2,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 106294.73684210525,
- },
- Object {
- "averageResponseTime": 33265.03326147213,
- "impact": 10.256357027376065,
- "name": "GET /api/orders",
- "p95": 58827.489999999976,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:40.973Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 408,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/orders",
- "hostname": "opbeans-node",
- "pathname": "/api/orders",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/orders",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "103612",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:53:40 GMT",
- "etag": "W/\\"194bc-cOw6+iRf7XCeqMXHrle3IOig7tY\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574420973006,
- },
- "trace": Object {
- "id": "0afce85f593cbbdd09949936fe964f0f",
- },
- "transaction": Object {
- "duration": Object {
- "us": 23040,
- },
- "id": "89f200353eb50539",
- "name": "GET /api/orders",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 2,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 102536.84210526315,
- },
- Object {
- "averageResponseTime": 27516.89144558744,
- "impact": 9.651458992731666,
- "name": "GET /api/products/top",
- "p95": 56064.679999999986,
- "sample": Object {
- "@timestamp": "2018-11-18T20:52:57.316Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 5113,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3686,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-74f12e705936d66350f4741ebeb55189-fcebe94cd2136215-01",
- "host": "opbeans-node:3000",
- "referer": "http://opbeans-node:3000/dashboard",
- "user-agent": "Chromeless 1.4.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.7",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/products/top",
- "hostname": "opbeans-node",
- "pathname": "/api/products/top",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/products/top",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "282",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:52:57 GMT",
- "etag": "W/\\"11a-lcI9zuMZYYsDRpEZgYqDYr96cKM\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "fcebe94cd2136215",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574377316005,
- },
- "trace": Object {
- "id": "74f12e705936d66350f4741ebeb55189",
- },
- "transaction": Object {
- "duration": Object {
- "us": 48781,
- },
- "id": "be4bd5475d5d9e6f",
- "name": "GET /api/products/top",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 4,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 116652.63157894736,
- },
- Object {
- "averageResponseTime": 12683.190864600327,
- "impact": 4.4239778504968,
- "name": "GET /api/products",
- "p95": 35009.67999999999,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:43.477Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 2857,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/products",
- "hostname": "opbeans-node",
- "pathname": "/api/products",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/products",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "1023",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:53:43 GMT",
- "etag": "W/\\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574423477006,
- },
- "trace": Object {
- "id": "bee00a8efb523ca4b72adad57f7caba3",
- },
- "transaction": Object {
- "duration": Object {
- "us": 6915,
- },
- "id": "d8fc6d3b8707b64c",
- "name": "GET /api/products",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 2,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 116147.36842105263,
- },
- Object {
- "averageResponseTime": 255966.30555555556,
- "impact": 4.3693406535517445,
- "name": "POST /api/orders",
- "p95": 320238.5,
- "sample": Object {
- "@timestamp": "2018-11-18T20:43:32.010Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 4669,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 2413,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "body": "[REDACTED]",
- "headers": Object {
- "accept": "application/json",
- "connection": "close",
- "content-length": "129",
- "content-type": "application/json",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "POST",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/orders",
- "hostname": "opbeans-node",
- "pathname": "/api/orders",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/orders",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "13",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:43:32 GMT",
- "etag": "W/\\"d-g9K2iK4ordyN88lGL4LmPlYNfhc\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542573812010006,
- },
- "trace": Object {
- "id": "2b1252a338249daeecf6afb0c236e31b",
- },
- "transaction": Object {
- "duration": Object {
- "us": 291572,
- },
- "id": "2c9f39e9ec4a0111",
- "name": "POST /api/orders",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 16,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 5684.210526315789,
- },
- Object {
- "averageResponseTime": 17189.329210275926,
- "impact": 3.424381787142002,
- "name": "GET /api/products/:id/customers",
- "p95": 39284.79999999999,
- "sample": Object {
- "@timestamp": "2018-11-18T20:48:24.769Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 1735,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3100,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-28f178c354d17f400dea04bc4a7b3c57-68f5d1607cac7779-01",
- "host": "opbeans-node:3000",
- "user-agent": "python-requests/2.20.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.6",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/products/2/customers",
- "hostname": "opbeans-node",
- "pathname": "/api/products/2/customers",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/products/2/customers",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "186570",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:48:24 GMT",
- "etag": "W/\\"2d8ca-Z9NzuHyGyxwtzpOkcIxBvzm24iw\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "68f5d1607cac7779",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574104769029,
- },
- "trace": Object {
- "id": "28f178c354d17f400dea04bc4a7b3c57",
- },
- "transaction": Object {
- "duration": Object {
- "us": 49338,
- },
- "id": "2a87ae20ad04ee0c",
- "name": "GET /api/products/:id/customers",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 66378.94736842105,
- },
- Object {
- "averageResponseTime": 11257.757916666667,
- "impact": 2.558180605569336,
- "name": "GET /api/types",
- "p95": 35222.944444444445,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:44.978Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 2193,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/types",
- "hostname": "opbeans-node",
- "pathname": "/api/types",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/types",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "112",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:53:44 GMT",
- "etag": "W/\\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574424978005,
- },
- "trace": Object {
- "id": "0d84126973411c19b470f2d9eea958d3",
- },
- "transaction": Object {
- "duration": Object {
- "us": 7891,
- },
- "id": "0f10668e4fb3adc7",
- "name": "GET /api/types",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 2,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 75789.47368421052,
- },
- Object {
- "averageResponseTime": 3504.5108924806746,
- "impact": 2.3600993453143766,
- "name": "GET *",
- "p95": 11431.738095238095,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:42.493Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 6446,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "host": "opbeans-node:3000",
- "if-modified-since": "Mon, 12 Nov 2018 10:27:07 GMT",
- "if-none-match": "W/\\"280-1670775e878\\"",
- "upgrade-insecure-requests": "1",
- "user-agent": "Chromeless 1.4.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.7",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/dashboard",
- "hostname": "opbeans-node",
- "pathname": "/dashboard",
- "port": "3000",
- "protocol": "http:",
- "raw": "/dashboard",
- },
- },
- "response": Object {
- "headers": Object {
- "accept-ranges": "bytes",
- "cache-control": "public, max-age=0",
- "connection": "keep-alive",
- "date": "Sun, 18 Nov 2018 20:53:42 GMT",
- "etag": "W/\\"280-1670775e878\\"",
- "last-modified": "Mon, 12 Nov 2018 10:27:07 GMT",
- "x-powered-by": "Express",
- },
- "status_code": 304,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574422493006,
- },
- "trace": Object {
- "id": "7efb6ade88cdea20cd96ca482681cde7",
- },
- "transaction": Object {
- "duration": Object {
- "us": 1901,
- },
- "id": "f5fc4621949b63fb",
- "name": "GET *",
- "result": "HTTP 3xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 224684.21052631576,
- },
- Object {
- "averageResponseTime": 32387.73641304348,
- "impact": 2.2558112380477584,
- "name": "GET /log-error",
- "p95": 40061.1,
- "sample": Object {
- "@timestamp": "2018-11-18T20:52:51.462Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 4877,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3659,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/log-error",
- "hostname": "opbeans-node",
- "pathname": "/log-error",
- "port": "3000",
- "protocol": "http:",
- "raw": "/log-error",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "24",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:52:51 GMT",
- "etag": "W/\\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\\"",
- "x-powered-by": "Express",
- },
- "status_code": 500,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574371462005,
- },
- "trace": Object {
- "id": "15366d65659b5fc8f67ff127391b3aff",
- },
- "transaction": Object {
- "duration": Object {
- "us": 33367,
- },
- "id": "ec9c465c5042ded8",
- "name": "GET /log-error",
- "result": "HTTP 5xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 23242.105263157893,
- },
- Object {
- "averageResponseTime": 32900.72714285714,
- "impact": 2.1791207411745854,
- "name": "GET /log-message",
- "p95": 40444,
- "sample": Object {
- "@timestamp": "2018-11-18T20:49:09.225Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 321,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3142,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/log-message",
- "hostname": "opbeans-node",
- "pathname": "/log-message",
- "port": "3000",
- "protocol": "http:",
- "raw": "/log-message",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "24",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:49:09 GMT",
- "etag": "W/\\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\\"",
- "x-powered-by": "Express",
- },
- "status_code": 500,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574149225004,
- },
- "trace": Object {
- "id": "ba18b741cdd3ac83eca89a5fede47577",
- },
- "transaction": Object {
- "duration": Object {
- "us": 32381,
- },
- "id": "b9a8f96d7554d09f",
- "name": "GET /log-message",
- "result": "HTTP 5xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 22105.263157894737,
- },
- Object {
- "averageResponseTime": 10548.218597063622,
- "impact": 1.8338763992340905,
- "name": "GET /api/products/:id",
- "p95": 28413.383333333328,
- "sample": Object {
- "@timestamp": "2018-11-18T20:52:57.963Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 7184,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3686,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/products/3",
- "hostname": "opbeans-node",
- "pathname": "/api/products/3",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/products/3",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "231",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:52:57 GMT",
- "etag": "W/\\"e7-kkuzj37GZDzXDh0CWqh5Gan0VO4\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574377963005,
- },
- "trace": Object {
- "id": "ca86ec845e412e4b4506a715d51548ec",
- },
- "transaction": Object {
- "duration": Object {
- "us": 6959,
- },
- "id": "d324897ffb7ebcdc",
- "name": "GET /api/products/:id",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 58073.68421052631,
- },
- Object {
- "averageResponseTime": 9868.217894736843,
- "impact": 1.7722323960215767,
- "name": "GET /api/customers/:id",
- "p95": 27486.5,
- "sample": Object {
- "@timestamp": "2018-11-18T20:52:56.797Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 8225,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3686,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "elastic-apm-traceparent": "00-e6140d30363f18b585f5d3b753f4d025-aa82e2c847265626-01",
- "host": "opbeans-node:3000",
- "user-agent": "python-requests/2.20.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.6",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/customers/700",
- "hostname": "opbeans-node",
- "pathname": "/api/customers/700",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/customers/700",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "193",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:52:56 GMT",
- "etag": "W/\\"c1-LbuhkuLzFyZ0H+7+JQGA5b0kvNs\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "aa82e2c847265626",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574376797031,
- },
- "trace": Object {
- "id": "e6140d30363f18b585f5d3b753f4d025",
- },
- "transaction": Object {
- "duration": Object {
- "us": 9735,
- },
- "id": "60e230d12f3f0960",
- "name": "GET /api/customers/:id",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 59999.99999999999,
- },
- Object {
- "averageResponseTime": 12763.68806073154,
- "impact": 1.7479924334286208,
- "name": "GET /api/types/:id",
- "p95": 30576.749999999996,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:35.967Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 5345,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/types/1",
- "hostname": "opbeans-node",
- "pathname": "/api/types/1",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/types/1",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "217",
- "content-type": "application/json; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:53:35 GMT",
- "etag": "W/\\"d9-cebOOHODBQMZd1wt+ZZBaSPgQLQ\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574415967005,
- },
- "trace": Object {
- "id": "2223b30b5cbaf2e221fcf70ac6d9abbe",
- },
- "transaction": Object {
- "duration": Object {
- "us": 13064,
- },
- "id": "053436abacdec0a4",
- "name": "GET /api/types/:id",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 2,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 45757.8947368421,
- },
- Object {
- "averageResponseTime": 10584.05144193297,
- "impact": 1.280810614916383,
- "name": "GET /api/orders/:id",
- "p95": 26555.399999999998,
- "sample": Object {
- "@timestamp": "2018-11-18T20:51:36.949Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 5999,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3475,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/orders/183",
- "hostname": "opbeans-node",
- "pathname": "/api/orders/183",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/orders/183",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "0",
- "date": "Sun, 18 Nov 2018 20:51:36 GMT",
- "x-powered-by": "Express",
- },
- "status_code": 404,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574296949004,
- },
- "trace": Object {
- "id": "dab6421fa44a6869887e0edf32e1ad6f",
- },
- "transaction": Object {
- "duration": Object {
- "us": 5906,
- },
- "id": "937ef5588454f74a",
- "name": "GET /api/orders/:id",
- "result": "HTTP 4xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 40515.789473684206,
- },
- Object {
- "averageResponseTime": 1422.926672899693,
- "impact": 1.0027124806135428,
- "name": "GET unknown route",
- "p95": 2311.885238095238,
- "sample": Object {
- "@timestamp": "2018-11-18T20:53:42.504Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3756,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "connection": "keep-alive",
- "host": "opbeans-node:3000",
- "referer": "http://opbeans-node:3000/dashboard",
- "user-agent": "Chromeless 1.4.0",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.7",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/rum-config.js",
- "hostname": "opbeans-node",
- "pathname": "/rum-config.js",
- "port": "3000",
- "protocol": "http:",
- "raw": "/rum-config.js",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "172",
- "content-type": "text/javascript",
- "date": "Sun, 18 Nov 2018 20:53:42 GMT",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574422504004,
- },
- "trace": Object {
- "id": "4399e7233e6e7b77e70c2fff111b8f28",
- },
- "transaction": Object {
- "duration": Object {
- "us": 911,
- },
- "id": "107881ae2be1b56d",
- "name": "GET unknown route",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 236431.5789473684,
- },
- Object {
- "averageResponseTime": 21331.714285714286,
- "impact": 0.28817487960409877,
- "name": "POST /api",
- "p95": 30938,
- "sample": Object {
- "@timestamp": "2018-11-18T20:29:42.751Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 2927,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 546,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "body": "[REDACTED]",
- "headers": Object {
- "accept": "application/json",
- "connection": "close",
- "content-length": "129",
- "content-type": "application/json",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "POST",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/api/orders",
- "hostname": "opbeans-node",
- "pathname": "/api/orders",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/orders",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "0",
- "date": "Sun, 18 Nov 2018 20:29:42 GMT",
- "x-powered-by": "Express",
- },
- "status_code": 400,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542572982751005,
- },
- "trace": Object {
- "id": "8ed4d94ec8fc11b1ea1b0aa59c2320ff",
- },
- "transaction": Object {
- "duration": Object {
- "us": 21083,
- },
- "id": "d67c2f7aa897110c",
- "name": "POST /api",
- "result": "HTTP 4xx",
- "sampled": true,
- "span_count": Object {
- "started": 1,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 4642.105263157894,
- },
- Object {
- "averageResponseTime": 4694.005586592179,
- "impact": 0.1498515000753004,
- "name": "GET /is-it-coffee-time",
- "p95": 11022.99999999992,
- "sample": Object {
- "@timestamp": "2018-11-18T20:46:19.317Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 8593,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 2760,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/is-it-coffee-time",
- "hostname": "opbeans-node",
- "pathname": "/is-it-coffee-time",
- "port": "3000",
- "protocol": "http:",
- "raw": "/is-it-coffee-time",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "148",
- "content-security-policy": "default-src 'self'",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:46:19 GMT",
- "x-content-type-options": "nosniff",
- "x-powered-by": "Express",
- },
- "status_code": 500,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542573979317007,
- },
- "trace": Object {
- "id": "821812b416de4c73ced87f8777fa46a6",
- },
- "transaction": Object {
- "duration": Object {
- "us": 4253,
- },
- "id": "319a5c555a1ab207",
- "name": "GET /is-it-coffee-time",
- "result": "HTTP 5xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 11305.263157894737,
- },
- Object {
- "averageResponseTime": 4549.889880952381,
- "impact": 0.13543365054509587,
- "name": "GET /throw-error",
- "p95": 7719.700000000001,
- "sample": Object {
- "@timestamp": "2018-11-18T20:47:10.714Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 7220,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 2895,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "GET",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/throw-error",
- "hostname": "opbeans-node",
- "pathname": "/throw-error",
- "port": "3000",
- "protocol": "http:",
- "raw": "/throw-error",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "148",
- "content-security-policy": "default-src 'self'",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:47:10 GMT",
- "x-content-type-options": "nosniff",
- "x-powered-by": "Express",
- },
- "status_code": 500,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574030714012,
- },
- "trace": Object {
- "id": "6c0ef23e1f963f304ce440a909914d35",
- },
- "transaction": Object {
- "duration": Object {
- "us": 4458,
- },
- "id": "ecd187dc53f09fbd",
- "name": "GET /throw-error",
- "result": "HTTP 5xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 10610.526315789473,
- },
- Object {
- "averageResponseTime": 2742.4615384615386,
- "impact": 0.08501028923348058,
- "name": "OPTIONS unknown route",
- "p95": 4370.000000000002,
- "sample": Object {
- "@timestamp": "2018-11-18T20:49:00.707Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 3775,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 3142,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "headers": Object {
- "connection": "close",
- "content-length": "0",
- "host": "opbeans-node:3000",
- "user-agent": "workload/2.4.3",
- },
- "http_version": "1.1",
- "method": "OPTIONS",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.10",
- },
- "url": Object {
- "full": "http://opbeans-node:3000/",
- "hostname": "opbeans-node",
- "pathname": "/",
- "port": "3000",
- "protocol": "http:",
- "raw": "/",
- },
- },
- "response": Object {
- "headers": Object {
- "allow": "GET,HEAD",
- "connection": "close",
- "content-length": "8",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 20:49:00 GMT",
- "etag": "W/\\"8-ZRAf8oNBS3Bjb/SU2GYZCmbtmXg\\"",
- "x-powered-by": "Express",
- },
- "status_code": 200,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542574140707006,
- },
- "trace": Object {
- "id": "469e3e5f91ffe3195a8e58cdd1cdefa8",
- },
- "transaction": Object {
- "duration": Object {
- "us": 2371,
- },
- "id": "a8c87ebc7ec68bc0",
- "name": "OPTIONS unknown route",
- "result": "HTTP 2xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 11494.736842105262,
- },
- Object {
- "averageResponseTime": 5192.9,
- "impact": 0,
- "name": "POST unknown route",
- "p95": 13230.5,
- "sample": Object {
- "@timestamp": "2018-11-18T18:43:50.994Z",
- "agent": Object {
- "hostname": "b359e3afece8",
- "type": "apm-server",
- "version": "7.0.0-alpha1",
- },
- "context": Object {
- "custom": Object {
- "containerId": 6102,
- },
- "process": Object {
- "argv": Array [
- "/usr/local/bin/node",
- "/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js",
- ],
- "pid": 19196,
- "ppid": 1,
- "title": "node /app/server.js",
- },
- "request": Object {
- "body": "[REDACTED]",
- "headers": Object {
- "accept": "*/*",
- "accept-encoding": "gzip, deflate",
- "content-length": "380",
- "content-type": "multipart/form-data; boundary=2b2e40be188a4cb5a56c05a0c182f6c9",
- "elastic-apm-traceparent": "00-19688959ea6cbccda8013c11566ea329-1fc3665eef2dcdfc-01",
- "host": "172.18.0.9:3000",
- "user-agent": "Python/3.7 aiohttp/3.3.2",
- "x-forwarded-for": "172.18.0.11",
- },
- "http_version": "1.1",
- "method": "POST",
- "socket": Object {
- "encrypted": false,
- "remote_address": "::ffff:172.18.0.9",
- },
- "url": Object {
- "full": "http://172.18.0.9:3000/api/orders/csv",
- "hostname": "172.18.0.9",
- "pathname": "/api/orders/csv",
- "port": "3000",
- "protocol": "http:",
- "raw": "/api/orders/csv",
- },
- },
- "response": Object {
- "headers": Object {
- "connection": "keep-alive",
- "content-length": "154",
- "content-security-policy": "default-src 'self'",
- "content-type": "text/html; charset=utf-8",
- "date": "Sun, 18 Nov 2018 18:43:50 GMT",
- "x-content-type-options": "nosniff",
- "x-powered-by": "Express",
- },
- "status_code": 404,
- },
- "service": Object {
- "agent": Object {
- "name": "nodejs",
- "version": "1.14.2",
- },
- "language": Object {
- "name": "javascript",
- },
- "name": "opbeans-node",
- "runtime": Object {
- "name": "node",
- "version": "8.12.0",
- },
- "version": "1.0.0",
- },
- "system": Object {
- "architecture": "x64",
- "hostname": "98195610c255",
- "ip": "172.18.0.10",
- "platform": "linux",
- },
- "tags": Object {
- "foo": "bar",
- "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
- "multi-line": "foo
-bar
-baz",
- "this-is-a-very-long-tag-name-without-any-spaces": "test",
- },
- "user": Object {
- "email": "kimchy@elastic.co",
- "id": "42",
- "username": "kimchy",
- },
- },
- "host": Object {
- "name": "b359e3afece8",
- },
- "parent": Object {
- "id": "1fc3665eef2dcdfc",
- },
- "processor": Object {
- "event": "transaction",
- "name": "transaction",
- },
- "timestamp": Object {
- "us": 1542566630994005,
- },
- "trace": Object {
- "id": "19688959ea6cbccda8013c11566ea329",
- },
- "transaction": Object {
- "duration": Object {
- "us": 3467,
- },
- "id": "92c3ceea57899061",
- "name": "POST unknown route",
- "result": "HTTP 4xx",
- "sampled": true,
- "span_count": Object {
- "started": 0,
- },
- "type": "request",
- },
- },
- "transactionsPerMinute": 631.578947368421,
- },
-]
-`;
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
deleted file mode 100644
index a26c3d85a3fc47..00000000000000
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
+++ /dev/null
@@ -1,64 +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 { transactionGroupsFetcher } from './fetcher';
-import { APMConfig } from '../..';
-
-function getSetup() {
- return {
- start: 1528113600000,
- end: 1528977600000,
- client: {
- search: jest.fn(),
- } as any,
- internalClient: {
- search: jest.fn(),
- } as any,
- config: {
- 'xpack.apm.ui.transactionGroupBucketSize': 100,
- } as APMConfig,
- uiFiltersES: [{ term: { 'service.environment': 'test' } }],
- indices: {
- 'apm_oss.sourcemapIndices': 'myIndex',
- 'apm_oss.errorIndices': 'myIndex',
- 'apm_oss.onboardingIndices': 'myIndex',
- 'apm_oss.spanIndices': 'myIndex',
- 'apm_oss.transactionIndices': 'myIndex',
- 'apm_oss.metricsIndices': 'myIndex',
- apmAgentConfigurationIndex: 'myIndex',
- apmCustomLinkIndex: 'myIndex',
- },
- dynamicIndexPattern: null as any,
- };
-}
-
-describe('transactionGroupsFetcher', () => {
- describe('type: top_traces', () => {
- it('should call client.search with correct query', async () => {
- const setup = getSetup();
- const bucketSize = 100;
- await transactionGroupsFetcher({ type: 'top_traces' }, setup, bucketSize);
- expect(setup.client.search.mock.calls).toMatchSnapshot();
- });
- });
-
- describe('type: top_transactions', () => {
- it('should call client.search with correct query', async () => {
- const setup = getSetup();
- const bucketSize = 100;
- await transactionGroupsFetcher(
- {
- type: 'top_transactions',
- serviceName: 'opbeans-node',
- transactionType: 'request',
- },
- setup,
- bucketSize
- );
- expect(setup.client.search.mock.calls).toMatchSnapshot();
- });
- });
-});
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index a5cc74b18a7ef5..73bf1d01924e71 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -3,23 +3,31 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-
+import { take, sortBy } from 'lodash';
+import { Unionize } from 'utility-types';
+import moment from 'moment';
+import { joinByKey } from '../../../common/utils/join_by_key';
+import { ESSearchRequest } from '../../../typings/elasticsearch';
import {
SERVICE_NAME,
- TRANSACTION_DURATION,
- TRANSACTION_SAMPLED,
TRANSACTION_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { getTransactionGroupsProjection } from '../../../common/projections/transaction_groups';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { PromiseReturnType } from '../../../../observability/typings/common';
-import { SortOptions } from '../../../typings/elasticsearch/aggregations';
+import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
import { Transaction } from '../../../typings/es_schemas/ui/transaction';
import {
Setup,
SetupTimeRange,
SetupUIFilters,
} from '../helpers/setup_request';
+import {
+ getSamples,
+ getAverages,
+ getSums,
+ getPercentiles,
+} from './get_transaction_group_stats';
interface TopTransactionOptions {
type: 'top_transactions';
@@ -36,68 +44,149 @@ interface TopTraceOptions {
export type Options = TopTransactionOptions | TopTraceOptions;
export type ESResponse = PromiseReturnType;
+
+export type TransactionGroupRequestBase = ESSearchRequest & {
+ body: {
+ aggs: {
+ transaction_groups: Unionize<
+ Pick
+ >;
+ };
+ };
+};
+
+export type TransactionGroupSetup = Setup & SetupTimeRange & SetupUIFilters;
+
+function getItemsWithRelativeImpact(
+ setup: TransactionGroupSetup,
+ items: Array<{
+ sum?: number | null;
+ key: string | Record;
+ avg?: number | null;
+ count?: number | null;
+ p95?: number;
+ sample?: Transaction;
+ }>
+) {
+ const values = items
+ .map(({ sum }) => sum)
+ .filter((value) => value !== null) as number[];
+
+ const max = Math.max(...values);
+ const min = Math.min(...values);
+
+ const duration = moment.duration(setup.end - setup.start);
+ const minutes = duration.asMinutes();
+
+ const itemsWithRelativeImpact: TransactionGroup[] = items
+ .map((item) => {
+ return {
+ key: item.key,
+ averageResponseTime: item.avg,
+ transactionsPerMinute: (item.count ?? 0) / minutes,
+ impact:
+ item.sum !== null && item.sum !== undefined
+ ? ((item.sum - min) / (max - min)) * 100 || 0
+ : 0,
+ p95: item.p95,
+ sample: item.sample!,
+ };
+ })
+ .filter((item) => item.sample);
+
+ return itemsWithRelativeImpact;
+}
+
export async function transactionGroupsFetcher(
options: Options,
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: TransactionGroupSetup,
bucketSize: number
) {
- const { client } = setup;
-
const projection = getTransactionGroupsProjection({
setup,
options,
});
- const sort: SortOptions = [
- { _score: 'desc' as const }, // sort by _score to ensure that buckets with sampled:true ends up on top
- { '@timestamp': { order: 'desc' as const } },
- ];
-
const isTopTraces = options.type === 'top_traces';
- if (isTopTraces) {
- // Delete the projection aggregation when searching for traces, as it should use the combined aggregation instead
- delete projection.body.aggs;
- }
+ delete projection.body.aggs;
+
+ // traces overview is hardcoded to 10000
+ // transactions overview: 1 extra bucket is added to check whether the total number of buckets exceed the specified bucket size.
+ const expectedBucketSize = isTopTraces ? 10000 : bucketSize;
+ const size = isTopTraces ? 10000 : expectedBucketSize + 1;
- const params = mergeProjection(projection, {
+ const request = mergeProjection(projection, {
+ size: 0,
body: {
- size: 0,
- query: {
- bool: {
- // prefer sampled transactions
- should: [{ term: { [TRANSACTION_SAMPLED]: true } }],
- },
- },
aggs: {
transaction_groups: {
- composite: {
- // traces overview is hardcoded to 10000
- // transactions overview: 1 extra bucket is added to check whether the total number of buckets exceed the specified bucket size.
- size: isTopTraces ? 10000 : bucketSize + 1,
- sources: [
- ...(isTopTraces
- ? [{ service: { terms: { field: SERVICE_NAME } } }]
- : []),
- { transaction: { terms: { field: TRANSACTION_NAME } } },
- ],
- },
- aggs: {
- sample: { top_hits: { size: 1, sort } },
- avg: { avg: { field: TRANSACTION_DURATION } },
- p95: {
- percentiles: {
- field: TRANSACTION_DURATION,
- percents: [95],
- hdr: { number_of_significant_value_digits: 2 },
- },
- },
- sum: { sum: { field: TRANSACTION_DURATION } },
- },
+ ...(isTopTraces
+ ? {
+ composite: {
+ sources: [
+ { [SERVICE_NAME]: { terms: { field: SERVICE_NAME } } },
+ {
+ [TRANSACTION_NAME]: {
+ terms: { field: TRANSACTION_NAME },
+ },
+ },
+ ],
+ size,
+ },
+ }
+ : {
+ terms: {
+ field: TRANSACTION_NAME,
+ size,
+ },
+ }),
},
},
},
});
- return client.search(params);
+ const params = {
+ request,
+ setup,
+ };
+
+ const [samples, averages, sums, percentiles] = await Promise.all([
+ getSamples(params),
+ getAverages(params),
+ getSums(params),
+ !isTopTraces ? getPercentiles(params) : Promise.resolve(undefined),
+ ]);
+
+ const stats = [
+ ...samples,
+ ...averages,
+ ...sums,
+ ...(percentiles ? percentiles : []),
+ ];
+
+ const items = joinByKey(stats, 'key');
+
+ const itemsWithRelativeImpact = getItemsWithRelativeImpact(setup, items);
+
+ return {
+ items: take(
+ // sort by impact by default so most impactful services are not cut off
+ sortBy(itemsWithRelativeImpact, 'impact').reverse(),
+ expectedBucketSize
+ ),
+ // The aggregation is considered accurate if the configured bucket size is larger or equal to the number of buckets returned
+ // the actual number of buckets retrieved are `bucketsize + 1` to detect whether it's above the limit
+ isAggregationAccurate: expectedBucketSize >= itemsWithRelativeImpact.length,
+ bucketSize,
+ };
+}
+
+export interface TransactionGroup {
+ key: Record | string;
+ averageResponseTime: number | null | undefined;
+ transactionsPerMinute: number;
+ p95: number | undefined;
+ impact: number;
+ sample: Transaction;
}
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
new file mode 100644
index 00000000000000..59fb370113ec27
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
@@ -0,0 +1,144 @@
+/*
+ * 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 { merge } from 'lodash';
+import { arrayUnionToCallable } from '../../../common/utils/array_union_to_callable';
+import { Transaction } from '../../../typings/es_schemas/ui/transaction';
+import {
+ TRANSACTION_SAMPLED,
+ TRANSACTION_DURATION,
+} from '../../../common/elasticsearch_fieldnames';
+import {
+ AggregationInputMap,
+ SortOptions,
+} from '../../../typings/elasticsearch/aggregations';
+import { TransactionGroupRequestBase, TransactionGroupSetup } from './fetcher';
+
+interface MetricParams {
+ request: TransactionGroupRequestBase;
+ setup: TransactionGroupSetup;
+}
+
+type BucketKey = string | Record;
+
+function mergeRequestWithAggs<
+ TRequestBase extends TransactionGroupRequestBase,
+ TInputMap extends AggregationInputMap
+>(request: TRequestBase, aggs: TInputMap) {
+ return merge({}, request, {
+ body: {
+ aggs: {
+ transaction_groups: {
+ aggs,
+ },
+ },
+ },
+ });
+}
+
+export async function getSamples({ request, setup }: MetricParams) {
+ const params = mergeRequestWithAggs(request, {
+ sample: {
+ top_hits: {
+ size: 1,
+ },
+ },
+ });
+
+ const sort: SortOptions = [
+ { _score: 'desc' as const }, // sort by _score to ensure that buckets with sampled:true ends up on top
+ { '@timestamp': { order: 'desc' as const } },
+ ];
+
+ const response = await setup.client.search({
+ ...params,
+ body: {
+ ...params.body,
+ query: {
+ ...params.body.query,
+ bool: {
+ ...params.body.query.bool,
+ should: [{ term: { [TRANSACTION_SAMPLED]: true } }],
+ },
+ },
+ sort,
+ },
+ });
+
+ return arrayUnionToCallable(
+ response.aggregations?.transaction_groups.buckets ?? []
+ ).map((bucket) => {
+ return {
+ key: bucket.key as BucketKey,
+ count: bucket.doc_count,
+ sample: bucket.sample.hits.hits[0]._source as Transaction,
+ };
+ });
+}
+
+export async function getAverages({ request, setup }: MetricParams) {
+ const params = mergeRequestWithAggs(request, {
+ avg: {
+ avg: {
+ field: TRANSACTION_DURATION,
+ },
+ },
+ });
+
+ const response = await setup.client.search(params);
+
+ return arrayUnionToCallable(
+ response.aggregations?.transaction_groups.buckets ?? []
+ ).map((bucket) => {
+ return {
+ key: bucket.key as BucketKey,
+ avg: bucket.avg.value,
+ };
+ });
+}
+
+export async function getSums({ request, setup }: MetricParams) {
+ const params = mergeRequestWithAggs(request, {
+ sum: {
+ sum: {
+ field: TRANSACTION_DURATION,
+ },
+ },
+ });
+
+ const response = await setup.client.search(params);
+
+ return arrayUnionToCallable(
+ response.aggregations?.transaction_groups.buckets ?? []
+ ).map((bucket) => {
+ return {
+ key: bucket.key as BucketKey,
+ sum: bucket.sum.value,
+ };
+ });
+}
+
+export async function getPercentiles({ request, setup }: MetricParams) {
+ const params = mergeRequestWithAggs(request, {
+ p95: {
+ percentiles: {
+ field: TRANSACTION_DURATION,
+ hdr: { number_of_significant_value_digits: 2 },
+ percents: [95],
+ },
+ },
+ });
+
+ const response = await setup.client.search(params);
+
+ return arrayUnionToCallable(
+ response.aggregations?.transaction_groups.buckets ?? []
+ ).map((bucket) => {
+ return {
+ key: bucket.key as BucketKey,
+ p95: Object.values(bucket.p95.values)[0],
+ };
+ });
+}
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
index 893e586b351a80..6e0d619268d444 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
@@ -10,19 +10,11 @@ import {
SetupUIFilters,
} from '../helpers/setup_request';
import { transactionGroupsFetcher, Options } from './fetcher';
-import { transactionGroupsTransformer } from './transform';
export async function getTransactionGroupList(
options: Options,
setup: Setup & SetupTimeRange & SetupUIFilters
) {
- const { start, end } = setup;
const bucketSize = setup.config['xpack.apm.ui.transactionGroupBucketSize'];
- const response = await transactionGroupsFetcher(options, setup, bucketSize);
- return transactionGroupsTransformer({
- response,
- start,
- end,
- bucketSize,
- });
+ return await transactionGroupsFetcher(options, setup, bucketSize);
}
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
index 2c5aa79bb3483c..0b2ff3a72975ba 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
@@ -31,7 +31,9 @@ describe('transaction group queries', () => {
)
);
- expect(mock.params).toMatchSnapshot();
+ const allParams = mock.spy.mock.calls.map((call) => call[0]);
+
+ expect(allParams).toMatchSnapshot();
});
it('fetches top traces', async () => {
@@ -46,6 +48,8 @@ describe('transaction group queries', () => {
)
);
- expect(mock.params).toMatchSnapshot();
+ const allParams = mock.spy.mock.calls.map((call) => call[0]);
+
+ expect(allParams).toMatchSnapshot();
});
});
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
deleted file mode 100644
index 0bb29e27f0219e..00000000000000
--- a/x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
+++ /dev/null
@@ -1,135 +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 { ESResponse } from './fetcher';
-import { transactionGroupsResponse } from './mock_responses/transaction_groups_response';
-import { transactionGroupsTransformer } from './transform';
-
-describe('transactionGroupsTransformer', () => {
- it('should match snapshot', () => {
- const {
- bucketSize,
- isAggregationAccurate,
- items,
- } = transactionGroupsTransformer({
- response: transactionGroupsResponse,
- start: 100,
- end: 2000,
- bucketSize: 100,
- });
-
- expect(bucketSize).toBe(100);
- expect(isAggregationAccurate).toBe(true);
- expect(items).toMatchSnapshot();
- });
-
- it('should transform response correctly', () => {
- const bucket = {
- key: { transaction: 'POST /api/orders' },
- doc_count: 180,
- avg: { value: 255966.30555555556 },
- p95: { values: { '95.0': 320238.5 } },
- sum: { value: 3000000000 },
- sample: {
- hits: {
- total: 180,
- hits: [{ _source: 'sample source' }],
- },
- },
- };
-
- const response = ({
- aggregations: {
- transaction_groups: {
- buckets: [bucket],
- },
- },
- } as unknown) as ESResponse;
-
- expect(
- transactionGroupsTransformer({
- response,
- start: 100,
- end: 20000,
- bucketSize: 100,
- })
- ).toEqual({
- bucketSize: 100,
- isAggregationAccurate: true,
- items: [
- {
- averageResponseTime: 255966.30555555556,
- impact: 0,
- name: 'POST /api/orders',
- p95: 320238.5,
- sample: 'sample source',
- transactionsPerMinute: 542.713567839196,
- },
- ],
- });
- });
-
- it('`isAggregationAccurate` should be false if number of bucket is higher than `bucketSize`', () => {
- const bucket = {
- key: { transaction: 'POST /api/orders' },
- doc_count: 180,
- avg: { value: 255966.30555555556 },
- p95: { values: { '95.0': 320238.5 } },
- sum: { value: 3000000000 },
- sample: {
- hits: {
- total: 180,
- hits: [{ _source: 'sample source' }],
- },
- },
- };
-
- const response = ({
- aggregations: {
- transaction_groups: {
- buckets: [bucket, bucket, bucket, bucket], // four buckets returned
- },
- },
- } as unknown) as ESResponse;
-
- const { isAggregationAccurate } = transactionGroupsTransformer({
- response,
- start: 100,
- end: 20000,
- bucketSize: 3, // bucket size of three
- });
-
- expect(isAggregationAccurate).toEqual(false);
- });
-
- it('should calculate impact from sum', () => {
- const getBucket = (sum: number) => ({
- key: { transaction: 'POST /api/orders' },
- doc_count: 180,
- avg: { value: 300000 },
- p95: { values: { '95.0': 320000 } },
- sum: { value: sum },
- sample: { hits: { total: 180, hits: [{ _source: 'sample source' }] } },
- });
-
- const response = ({
- aggregations: {
- transaction_groups: {
- buckets: [getBucket(10), getBucket(20), getBucket(50)],
- },
- },
- } as unknown) as ESResponse;
-
- const { items } = transactionGroupsTransformer({
- response,
- start: 100,
- end: 20000,
- bucketSize: 100,
- });
-
- expect(items.map((bucket) => bucket.impact)).toEqual([100, 25, 0]);
- });
-});
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/transform.ts b/x-pack/plugins/apm/server/lib/transaction_groups/transform.ts
deleted file mode 100644
index b04ff6764675df..00000000000000
--- a/x-pack/plugins/apm/server/lib/transaction_groups/transform.ts
+++ /dev/null
@@ -1,89 +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 moment from 'moment';
-import { orderBy } from 'lodash';
-import { ESResponse } from './fetcher';
-
-function calculateRelativeImpacts(items: ITransactionGroup[]) {
- const values = items
- .map(({ impact }) => impact)
- .filter((value) => value !== null) as number[];
-
- const max = Math.max(...values);
- const min = Math.min(...values);
-
- return items.map((bucket) => ({
- ...bucket,
- impact:
- bucket.impact !== null
- ? ((bucket.impact - min) / (max - min)) * 100 || 0
- : 0,
- }));
-}
-
-const getBuckets = (response: ESResponse) => {
- if (response.aggregations) {
- return orderBy(
- response.aggregations.transaction_groups.buckets,
- ['sum.value'],
- ['desc']
- );
- }
- return [];
-};
-
-export type ITransactionGroup = ReturnType;
-function getTransactionGroup(
- bucket: ReturnType[0],
- minutes: number
-) {
- const averageResponseTime = bucket.avg.value;
- const transactionsPerMinute = bucket.doc_count / minutes;
- const impact = bucket.sum.value;
- const sample = bucket.sample.hits.hits[0]._source;
-
- return {
- name: bucket.key.transaction,
- sample,
- p95: bucket.p95.values['95.0'],
- averageResponseTime,
- transactionsPerMinute,
- impact,
- };
-}
-
-export function transactionGroupsTransformer({
- response,
- start,
- end,
- bucketSize,
-}: {
- response: ESResponse;
- start: number;
- end: number;
- bucketSize: number;
-}): {
- items: ITransactionGroup[];
- isAggregationAccurate: boolean;
- bucketSize: number;
-} {
- const buckets = getBuckets(response);
- const duration = moment.duration(end - start);
- const minutes = duration.asMinutes();
- const items = buckets.map((bucket) => getTransactionGroup(bucket, minutes));
-
- const itemsWithRelativeImpact = calculateRelativeImpacts(items);
-
- return {
- items: itemsWithRelativeImpact,
-
- // The aggregation is considered accurate if the configured bucket size is larger or equal to the number of buckets returned
- // the actual number of buckets retrieved are `bucketsize + 1` to detect whether it's above the limit
- isAggregationAccurate: bucketSize >= buckets.length,
- bucketSize,
- };
-}
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
index 1cecf14f2eeb8d..e892284fd87cd8 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
@@ -35,7 +35,7 @@ export const getLocalFilterQuery = ({
},
},
}
- : {};
+ : null;
return mergeProjection(projection, {
body: {
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
index 588d5c7896db99..3833b93c8d1f73 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
@@ -43,6 +43,7 @@ export async function getLocalUIFilters({
const response = await client.search(query);
const filter = localUIFilters[name];
+
const buckets = response?.aggregations?.by_terms?.buckets ?? [];
return {
diff --git a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
index ac7499c23e9268..d25ec8709e3bef 100644
--- a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
+++ b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Unionize } from 'utility-types';
+import { Unionize, UnionToIntersection } from 'utility-types';
type SortOrder = 'asc' | 'desc';
type SortInstruction = Record;
@@ -288,10 +288,13 @@ interface AggregationResponsePart<
}
| undefined;
composite: {
- after_key: Record, number>;
+ after_key: Record<
+ GetCompositeKeys,
+ string | number
+ >;
buckets: Array<
{
- key: Record, number>;
+ key: Record, string | number>;
doc_count: number;
} & BucketSubAggregationResponse<
TAggregationOptionsMap['aggs'],
@@ -337,6 +340,15 @@ interface AggregationResponsePart<
// keyof AggregationResponsePart<{}, unknown>
// >;
+// ensures aggregations work with requests where aggregation options are a union type,
+// e.g. { transaction_groups: { composite: any } | { terms: any } }.
+// Union keys are not included in keyof. The type will fall back to keyof T if
+// UnionToIntersection fails, which happens when there are conflicts between the union
+// types, e.g. { foo: string; bar?: undefined } | { foo?: undefined; bar: string };
+export type ValidAggregationKeysOf<
+ T extends Record
+> = keyof (UnionToIntersection extends never ? T : UnionToIntersection);
+
export type AggregationResponseMap<
TAggregationInputMap extends AggregationInputMap | undefined,
TDocument
@@ -345,6 +357,6 @@ export type AggregationResponseMap<
[TName in keyof TAggregationInputMap]: AggregationResponsePart<
TAggregationInputMap[TName],
TDocument
- >[AggregationType & keyof TAggregationInputMap[TName]];
+ >[AggregationType & ValidAggregationKeysOf];
}
: undefined;
diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json b/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json
index bacb340292f931..4db040e92e7faf 100644
--- a/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json
+++ b/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json
@@ -1,20 +1,35 @@
[
{
- "name": "Process payment",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "Process payment"
+ },
+ "averageResponseTime": 1745009,
+ "transactionsPerMinute": 0.25,
+ "impact": 100,
"sample": {
"@timestamp": "2020-06-29T06:48:29.892Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.379730Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:39.379730Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"observer": {
"ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a",
@@ -34,59 +49,97 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "timestamp": { "us": 1593413309892019 },
- "trace": { "id": "bc393b659bef63291b6fa08e6f1d3f14" },
+ "timestamp": {
+ "us": 1593413309892019
+ },
+ "trace": {
+ "id": "bc393b659bef63291b6fa08e6f1d3f14"
+ },
"transaction": {
- "duration": { "us": 1745009 },
+ "duration": {
+ "us": 1745009
+ },
"id": "a58333df6d851cf1",
"name": "Process payment",
"result": "success",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "Worker"
}
- },
- "p95": 1744896,
- "averageResponseTime": 1745009,
- "transactionsPerMinute": 0.25,
- "impact": 100
+ }
},
{
- "name": "GET /api",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api"
+ },
+ "averageResponseTime": 49816.15625,
+ "transactionsPerMinute": 8,
+ "impact": 91.32732325394932,
"sample": {
- "@timestamp": "2020-06-29T06:48:41.454Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:06.969Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.992834Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:08.306961Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -96,11 +149,18 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:41 GMT"],
- "Transfer-Encoding": ["chunked"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "0"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:06 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -130,61 +190,103 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413321454009 },
- "trace": { "id": "0507830eeff93f7bf1a354e4031097b3" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413286969018
+ },
+ "trace": {
+ "id": "87a828bcedd44d9e872d8f552fb04aa6"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 8334 },
- "id": "878250a8b937445d",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 25229
+ },
+ "id": "b1843afd04271423",
"name": "GET /api",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/6",
- "original": "/api/products/6",
- "path": "/api/products/6",
+ "full": "http://opbeans-node:3000/api/orders/474",
+ "original": "/api/orders/474",
+ "path": "/api/orders/474",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 81888,
- "averageResponseTime": 49816.15625,
- "transactionsPerMinute": 8,
- "impact": 91.32732325394932
+ }
},
{
- "name": "/dashboard",
+ "key": {
+ "service.name": "client",
+ "transaction.name": "/dashboard"
+ },
+ "averageResponseTime": 208000,
+ "transactionsPerMinute": 0.75,
+ "impact": 35.56882613781033,
"sample": {
- "@timestamp": "2020-06-29T06:48:21.621Z",
- "agent": { "name": "rum-js", "version": "5.2.0" },
- "client": { "ip": "172.18.0.8" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:22.625275Z" },
+ "@timestamp": "2020-06-29T06:48:07.275Z",
+ "agent": {
+ "name": "rum-js",
+ "version": "5.2.0"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:08.291261Z"
+ },
"http": {
- "request": { "referrer": "" },
+ "request": {
+ "referrer": ""
+ },
"response": {
"decoded_body_size": 813,
"encoded_body_size": 813,
@@ -199,52 +301,73 @@
"version": "8.0.0",
"version_major": 8
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
- "language": { "name": "javascript" },
+ "language": {
+ "name": "javascript"
+ },
"name": "client",
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.8" },
- "timestamp": { "us": 1593413301621808 },
- "trace": { "id": "ee0ce8b38b8d5945829fc1c9432538bf" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413287275113
+ },
+ "trace": {
+ "id": "ca86ffcac7753ec8733933bd8fd45d11"
+ },
"transaction": {
"custom": {
"userConfig": {
- "featureFlags": ["double-trouble", "4423-hotfix"],
+ "featureFlags": [
+ "double-trouble",
+ "4423-hotfix"
+ ],
"showDashboard": true
}
},
- "duration": { "us": 109000 },
- "id": "c546a6716b681bf2",
+ "duration": {
+ "us": 342000
+ },
+ "id": "c40f735132c8e864",
"marks": {
"agent": {
- "domComplete": 98,
- "domInteractive": 87,
- "timeToFirstByte": 3
+ "domComplete": 335,
+ "domInteractive": 327,
+ "timeToFirstByte": 16
},
"navigationTiming": {
- "connectEnd": 0,
- "connectStart": 0,
- "domComplete": 98,
- "domContentLoadedEventEnd": 87,
- "domContentLoadedEventStart": 87,
- "domInteractive": 87,
- "domLoading": 8,
- "domainLookupEnd": 0,
- "domainLookupStart": 0,
+ "connectEnd": 12,
+ "connectStart": 12,
+ "domComplete": 335,
+ "domContentLoadedEventEnd": 327,
+ "domContentLoadedEventStart": 327,
+ "domInteractive": 327,
+ "domLoading": 21,
+ "domainLookupEnd": 12,
+ "domainLookupStart": 10,
"fetchStart": 0,
- "loadEventEnd": 98,
- "loadEventStart": 98,
- "requestStart": 1,
- "responseEnd": 8,
- "responseStart": 3
+ "loadEventEnd": 335,
+ "loadEventStart": 335,
+ "requestStart": 12,
+ "responseEnd": 17,
+ "responseStart": 16
}
},
"name": "/dashboard",
- "page": { "referer": "", "url": "http://opbeans-node:3000/dashboard" },
+ "page": {
+ "referer": "",
+ "url": "http://opbeans-node:3000/dashboard"
+ },
"sampled": true,
- "span_count": { "started": 8 },
+ "span_count": {
+ "started": 9
+ },
"type": "page-load"
},
"url": {
@@ -261,50 +384,75 @@
"name": "arthurdent"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "HeadlessChrome",
"original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
- "os": { "name": "Linux" },
+ "os": {
+ "name": "Linux"
+ },
"version": "79.0.3945"
}
- },
- "p95": 341504,
- "averageResponseTime": 208000,
- "transactionsPerMinute": 0.75,
- "impact": 35.56882613781033
+ }
},
{
- "name": "DispatcherServlet#doGet",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "DispatcherServlet#doGet"
+ },
+ "averageResponseTime": 36010.53846153846,
+ "transactionsPerMinute": 3.25,
+ "impact": 26.61043592713186,
"sample": {
- "@timestamp": "2020-06-29T06:48:40.104Z",
+ "@timestamp": "2020-06-29T06:48:10.529Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.706956Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.757591Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
@@ -321,77 +469,132 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Servlet API" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Servlet API"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413320104008 },
- "trace": { "id": "90bd7780b32cc51a7f4c200b1e0c170f" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413290529006
+ },
+ "trace": {
+ "id": "66e3db4cf016b138a43d319d15174891"
+ },
"transaction": {
- "duration": { "us": 8896 },
- "id": "40b22b21e92bbb20",
+ "duration": {
+ "us": 34366
+ },
+ "id": "7ea720a0175e7ffa",
"name": "DispatcherServlet#doGet",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 1 },
+ "span_count": {
+ "dropped": 0,
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/orders",
- "path": "/api/orders",
+ "full": "http://172.18.0.6:3000/api/products",
+ "path": "/api/products",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 34528,
- "averageResponseTime": 36010.53846153846,
- "transactionsPerMinute": 3.25,
- "impact": 26.61043592713186
+ }
},
{
- "name": "POST /api/orders",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "POST /api/orders"
+ },
+ "averageResponseTime": 270684,
+ "transactionsPerMinute": 0.25,
+ "impact": 15.261616628971955,
"sample": {
"@timestamp": "2020-06-29T06:48:39.953Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.991549Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:43.991549Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
- "body": { "original": "[REDACTED]" },
+ "body": {
+ "original": "[REDACTED]"
+ },
"headers": {
- "Accept": ["application/json"],
- "Connection": ["close"],
- "Content-Length": ["129"],
- "Content-Type": ["application/json"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "application/json"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "129"
+ ],
+ "Content-Type": [
+ "application/json"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "post",
"socket": {
@@ -401,12 +604,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["13"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:40 GMT"],
- "Etag": ["W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "13"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:40 GMT"
+ ],
+ "Etag": [
+ "W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -436,29 +651,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413319953033 },
- "trace": { "id": "52b8fda5f6df745b990740ba18378620" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413319953033
+ },
+ "trace": {
+ "id": "52b8fda5f6df745b990740ba18378620"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 270684 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 270684
+ },
"id": "a3afc2a112e9c893",
"name": "POST /api/orders",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 16 },
+ "span_count": {
+ "started": 16
+ },
"type": "request"
},
"url": {
@@ -469,50 +707,77 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 270336,
- "averageResponseTime": 270684,
- "transactionsPerMinute": 0.25,
- "impact": 15.261616628971955
+ }
},
{
- "name": "ResourceHttpRequestHandler",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "ResourceHttpRequestHandler"
+ },
+ "averageResponseTime": 14419.42857142857,
+ "transactionsPerMinute": 3.5,
+ "impact": 11.30657439844125,
"sample": {
- "@timestamp": "2020-06-29T06:48:44.376Z",
+ "@timestamp": "2020-06-29T06:48:06.640Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.720380Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.517678Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
@@ -529,63 +794,100 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413324376010 },
- "trace": { "id": "7e70dc471913473e7d3bffda9b27d720" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413286640008
+ },
+ "trace": {
+ "id": "81d8ffb0a39e755eed400f6486e15672"
+ },
"transaction": {
- "duration": { "us": 1420 },
- "id": "5c4e9f4b0846c2f8",
+ "duration": {
+ "us": 2953
+ },
+ "id": "353d42a2f9046e99",
"name": "ResourceHttpRequestHandler",
"result": "HTTP 4xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 0 },
+ "span_count": {
+ "dropped": 0,
+ "started": 0
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/types",
- "path": "/api/types",
+ "full": "http://172.18.0.6:3000/api/types/3",
+ "path": "/api/types/3",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 4120,
- "averageResponseTime": 14419.42857142857,
- "transactionsPerMinute": 3.5,
- "impact": 11.30657439844125
+ }
},
{
- "name": "/orders",
+ "key": {
+ "service.name": "client",
+ "transaction.name": "/orders"
+ },
+ "averageResponseTime": 81500,
+ "transactionsPerMinute": 0.5,
+ "impact": 9.072365225837785,
"sample": {
- "@timestamp": "2020-06-29T06:48:38.358Z",
- "agent": { "name": "rum-js", "version": "5.2.0" },
- "client": { "ip": "172.18.0.8" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.365914Z" },
+ "@timestamp": "2020-06-29T06:48:29.296Z",
+ "agent": {
+ "name": "rum-js",
+ "version": "5.2.0"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.986555Z"
+ },
"http": {
- "request": { "referrer": "" },
- "response": {
- "decoded_body_size": 813,
- "encoded_body_size": 813,
- "transfer_size": 962
+ "request": {
+ "referrer": ""
}
},
"observer": {
@@ -596,53 +898,50 @@
"version": "8.0.0",
"version_major": 8
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
- "language": { "name": "javascript" },
+ "language": {
+ "name": "javascript"
+ },
"name": "client",
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.8" },
- "timestamp": { "us": 1593413318358392 },
- "trace": { "id": "c1dea08a4128e776fd9965ccf8c8da99" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413309296660
+ },
+ "trace": {
+ "id": "978b56807e0b7a27cbc41a0dfb665f47"
+ },
"transaction": {
"custom": {
"userConfig": {
- "featureFlags": ["double-trouble", "4423-hotfix"],
+ "featureFlags": [
+ "double-trouble",
+ "4423-hotfix"
+ ],
"showDashboard": true
}
},
- "duration": { "us": 140000 },
- "id": "4f2ea2796645d6e5",
- "marks": {
- "agent": {
- "domComplete": 126,
- "domInteractive": 116,
- "timeToFirstByte": 3
- },
- "navigationTiming": {
- "connectEnd": 0,
- "connectStart": 0,
- "domComplete": 126,
- "domContentLoadedEventEnd": 116,
- "domContentLoadedEventStart": 116,
- "domInteractive": 116,
- "domLoading": 20,
- "domainLookupEnd": 0,
- "domainLookupStart": 0,
- "fetchStart": 0,
- "loadEventEnd": 126,
- "loadEventStart": 126,
- "requestStart": 1,
- "responseEnd": 3,
- "responseStart": 3
- }
+ "duration": {
+ "us": 23000
},
+ "id": "c3801eadbdef5c7c",
"name": "/orders",
- "page": { "referer": "", "url": "http://opbeans-node:3000/orders" },
+ "page": {
+ "referer": "",
+ "url": "http://opbeans-node:3000/orders"
+ },
"sampled": true,
- "span_count": { "started": 7 },
- "type": "page-load"
+ "span_count": {
+ "started": 1
+ },
+ "type": "route-change"
},
"url": {
"domain": "opbeans-node",
@@ -652,59 +951,94 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "adastra@example.com", "id": "3", "name": "trillian" },
+ "user": {
+ "email": "arthur.dent@example.com",
+ "id": "1",
+ "name": "arthurdent"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "HeadlessChrome",
"original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
- "os": { "name": "Linux" },
+ "os": {
+ "name": "Linux"
+ },
"version": "79.0.3945"
}
- },
- "p95": 140160,
- "averageResponseTime": 81500,
- "transactionsPerMinute": 0.5,
- "impact": 9.072365225837785
+ }
},
{
- "name": "APIRestController#customer",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#customer"
+ },
+ "averageResponseTime": 19370.6,
+ "transactionsPerMinute": 1.25,
+ "impact": 5.270496679320978,
"sample": {
- "@timestamp": "2020-06-29T06:48:38.893Z",
+ "@timestamp": "2020-06-29T06:48:08.631Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.680126Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.536897Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:38 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:08 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -719,59 +1053,101 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413318893006 },
- "trace": { "id": "efcf3446b51d080dbde1339969cf79a0" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413288631008
+ },
+ "trace": {
+ "id": "c00da24c5c793cd679ce3df47cee8f37"
+ },
"transaction": {
- "duration": { "us": 4594 },
- "id": "31ef64d71933e846",
+ "duration": {
+ "us": 76826
+ },
+ "id": "3c8403055ff75866",
"name": "APIRestController#customer",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 2 },
+ "span_count": {
+ "dropped": 0,
+ "started": 2
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/customers/235",
- "path": "/api/customers/235",
+ "full": "http://172.18.0.6:3000/api/customers/56",
+ "path": "/api/customers/56",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 77280,
- "averageResponseTime": 19370.6,
- "transactionsPerMinute": 1.25,
- "impact": 5.270496679320978
+ }
},
{
- "name": "/products",
+ "key": {
+ "service.name": "client",
+ "transaction.name": "/products"
+ },
+ "averageResponseTime": 77000,
+ "transactionsPerMinute": 0.25,
+ "impact": 4.129424578484989,
"sample": {
"@timestamp": "2020-06-29T06:48:48.824Z",
- "agent": { "name": "rum-js", "version": "5.2.0" },
- "client": { "ip": "172.18.0.8" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:49.293664Z" },
+ "agent": {
+ "name": "rum-js",
+ "version": "5.2.0"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:49.293664Z"
+ },
"http": {
- "request": { "referrer": "" },
+ "request": {
+ "referrer": ""
+ },
"response": {
"decoded_body_size": 813,
"encoded_body_size": 813,
@@ -786,23 +1162,39 @@
"version": "8.0.0",
"version_major": 8
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
- "language": { "name": "javascript" },
+ "language": {
+ "name": "javascript"
+ },
"name": "client",
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.8" },
- "timestamp": { "us": 1593413328824656 },
- "trace": { "id": "f6c4a9197bbd080bd45072970f251525" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413328824656
+ },
+ "trace": {
+ "id": "f6c4a9197bbd080bd45072970f251525"
+ },
"transaction": {
"custom": {
"userConfig": {
- "featureFlags": ["double-trouble", "4423-hotfix"],
+ "featureFlags": [
+ "double-trouble",
+ "4423-hotfix"
+ ],
"showDashboard": true
}
},
- "duration": { "us": 77000 },
+ "duration": {
+ "us": 77000
+ },
"id": "a11ede1968973bc5",
"marks": {
"agent": {
@@ -829,9 +1221,14 @@
}
},
"name": "/products",
- "page": { "referer": "", "url": "http://opbeans-node:3000/products" },
+ "page": {
+ "referer": "",
+ "url": "http://opbeans-node:3000/products"
+ },
"sampled": true,
- "span_count": { "started": 5 },
+ "span_count": {
+ "started": 5
+ },
"type": "page-load"
},
"url": {
@@ -842,29 +1239,52 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "z@example.com", "id": "4", "name": "zaphod" },
+ "user": {
+ "email": "z@example.com",
+ "id": "4",
+ "name": "zaphod"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "HeadlessChrome",
"original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
- "os": { "name": "Linux" },
+ "os": {
+ "name": "Linux"
+ },
"version": "79.0.3945"
}
- },
- "p95": 76800,
- "averageResponseTime": 77000,
- "transactionsPerMinute": 0.25,
- "impact": 4.129424578484989
+ }
},
{
- "name": "/customers",
+ "key": {
+ "service.name": "client",
+ "transaction.name": "/customers"
+ },
+ "averageResponseTime": 33500,
+ "transactionsPerMinute": 0.5,
+ "impact": 3.5546640380951287,
"sample": {
- "@timestamp": "2020-06-29T06:48:48.287Z",
- "agent": { "name": "rum-js", "version": "5.2.0" },
- "client": { "ip": "172.18.0.8" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:49.292535Z" },
- "http": { "request": { "referrer": "" } },
+ "@timestamp": "2020-06-29T06:48:35.071Z",
+ "agent": {
+ "name": "rum-js",
+ "version": "5.2.0"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:36.077184Z"
+ },
+ "http": {
+ "request": {
+ "referrer": ""
+ }
+ },
"observer": {
"ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a",
"hostname": "aa0bd613aa4c",
@@ -873,28 +1293,49 @@
"version": "8.0.0",
"version_major": 8
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
- "language": { "name": "javascript" },
+ "language": {
+ "name": "javascript"
+ },
"name": "client",
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.8" },
- "timestamp": { "us": 1593413328287946 },
- "trace": { "id": "48d130530a1fc0b2b99d138d3461bad4" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413315071116
+ },
+ "trace": {
+ "id": "547a92e82a25387321d1b967f2dd0f48"
+ },
"transaction": {
"custom": {
"userConfig": {
- "featureFlags": ["double-trouble", "4423-hotfix"],
+ "featureFlags": [
+ "double-trouble",
+ "4423-hotfix"
+ ],
"showDashboard": true
}
},
- "duration": { "us": 39000 },
- "id": "2f3a2b0fd3016d3e",
+ "duration": {
+ "us": 28000
+ },
+ "id": "d24f9b9dacb83450",
"name": "/customers",
- "page": { "referer": "", "url": "http://opbeans-node:3000/customers" },
+ "page": {
+ "referer": "",
+ "url": "http://opbeans-node:3000/customers"
+ },
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "route-change"
},
"url": {
@@ -905,59 +1346,94 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "adastra@example.com", "id": "3", "name": "trillian" },
+ "user": {
+ "email": "arthur.dent@example.com",
+ "id": "1",
+ "name": "arthurdent"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "HeadlessChrome",
"original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
- "os": { "name": "Linux" },
+ "os": {
+ "name": "Linux"
+ },
"version": "79.0.3945"
}
- },
- "p95": 39040,
- "averageResponseTime": 33500,
- "transactionsPerMinute": 0.5,
- "impact": 3.5546640380951287
+ }
},
{
- "name": "APIRestController#customers",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#customers"
+ },
+ "averageResponseTime": 16690.75,
+ "transactionsPerMinute": 1,
+ "impact": 3.541042213287889,
"sample": {
- "@timestamp": "2020-06-29T06:48:43.765Z",
+ "@timestamp": "2020-06-29T06:48:22.372Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.716850Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:25.888154Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:43 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:21 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 500
@@ -972,29 +1448,56 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413323765008 },
- "trace": { "id": "affce4cea9b60bd5b635dc1bd2e4ce79" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413302372009
+ },
+ "trace": {
+ "id": "21dd795dc3a260b1bf7ebbbac1e86fb8"
+ },
"transaction": {
- "duration": { "us": 10880 },
- "id": "cfe0a84b49b4a340",
+ "duration": {
+ "us": 14795
+ },
+ "id": "0157fc513282138f",
"name": "APIRestController#customers",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 2 },
+ "span_count": {
+ "dropped": 0,
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -1005,40 +1508,61 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 26432,
- "averageResponseTime": 16690.75,
- "transactionsPerMinute": 1,
- "impact": 3.541042213287889
+ }
},
{
- "name": "GET /log-message",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /log-message"
+ },
+ "averageResponseTime": 32667.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 3.458966408120217,
"sample": {
- "@timestamp": "2020-06-29T06:48:28.944Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:25.944Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.370695Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.976822Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1048,12 +1572,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["24"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:28 GMT"],
- "Etag": ["W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "24"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:25 GMT"
+ ],
+ "Etag": [
+ "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -1083,29 +1619,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413308944016 },
- "trace": { "id": "afabe4cb397616f5ec35a2f3da49ba62" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413305944023
+ },
+ "trace": {
+ "id": "cd2ad726ad164d701c5d3103cbab0c81"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 26788 },
- "id": "cc8c4261387507cf",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 38547
+ },
+ "id": "9e41667eb64dea55",
"name": "GET /log-message",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1116,60 +1675,82 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 38528,
- "averageResponseTime": 32667.5,
- "transactionsPerMinute": 0.5,
- "impact": 3.458966408120217
+ }
},
{
- "name": "APIRestController#stats",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#stats"
+ },
+ "averageResponseTime": 15535,
+ "transactionsPerMinute": 1,
+ "impact": 3.275330415465657,
"sample": {
- "@timestamp": "2020-06-29T06:48:42.549Z",
+ "@timestamp": "2020-06-29T06:48:09.912Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.715898Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.543824Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
- "headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:42 GMT"],
- "Transfer-Encoding": ["chunked"]
- },
"headers_sent": true,
- "status_code": 200
+ "status_code": 500
},
"version": "1.1"
},
@@ -1181,29 +1762,56 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413322549008 },
- "trace": { "id": "c3556e143784f94d4b4220dd40687fae" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413289912007
+ },
+ "trace": {
+ "id": "a17ceae4e18d50430ca15ecca5a3e69f"
+ },
"transaction": {
- "duration": { "us": 9166 },
- "id": "ac40e567f63c3eef",
+ "duration": {
+ "us": 10930
+ },
+ "id": "9fb330060bb73271",
"name": "APIRestController#stats",
- "result": "HTTP 2xx",
+ "result": "HTTP 5xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 5 },
+ "span_count": {
+ "dropped": 0,
+ "started": 5
+ },
"type": "request"
},
"url": {
@@ -1214,40 +1822,61 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 32064,
- "averageResponseTime": 15535,
- "transactionsPerMinute": 1,
- "impact": 3.275330415465657
+ }
},
{
- "name": "GET /api/customers",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/customers"
+ },
+ "averageResponseTime": 20092,
+ "transactionsPerMinute": 0.75,
+ "impact": 3.168195050736987,
"sample": {
- "@timestamp": "2020-06-29T06:48:37.952Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:28.444Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.492402Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.982737Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1257,12 +1886,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["186769"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:37 GMT"],
- "Etag": ["W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "186769"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:28 GMT"
+ ],
+ "Etag": [
+ "W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1292,29 +1933,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413317952016 },
- "trace": { "id": "5d99327edae38ed735e8d7a6028cf719" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413308444015
+ },
+ "trace": {
+ "id": "792fb0b00256164e88b277ec40b65e14"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 16824 },
- "id": "071808429ec9d00b",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 26471
+ },
+ "id": "6c1f848752563d2b",
"name": "GET /api/customers",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -1325,42 +1989,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 26368,
- "averageResponseTime": 20092,
- "transactionsPerMinute": 0.75,
- "impact": 3.168195050736987
+ }
},
{
- "name": "GET /api/products/:id",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/products/:id"
+ },
+ "averageResponseTime": 13516.5,
+ "transactionsPerMinute": 1,
+ "impact": 2.8112687551548836,
"sample": {
- "@timestamp": "2020-06-29T06:48:24.977Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:47:57.555Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:27.004717Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:59.085077Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1370,12 +2059,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["231"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:24 GMT"],
- "Etag": ["W/\"e7-kkuzj37GZDzXDh0CWqh5Gan0VO4\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "231"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:57 GMT"
+ ],
+ "Etag": [
+ "W/\"e7-6JlJegaJ+ir0C8I8EmmOjms1dnc\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1401,79 +2102,127 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 87,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413304977014 },
- "trace": { "id": "b9b290bca14c99962fa9a1c509401630" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413277555176
+ },
+ "trace": {
+ "id": "8365e1763f19e4067b88521d4d9247a0"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 4482 },
- "id": "b8d8284ff1fc25d6",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 37709
+ },
+ "id": "be2722a418272f10",
"name": "GET /api/products/:id",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/3",
- "original": "/api/products/3",
- "path": "/api/products/3",
+ "full": "http://opbeans-node:3000/api/products/1",
+ "original": "/api/products/1",
+ "path": "/api/products/1",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 37856,
- "averageResponseTime": 13516.5,
- "transactionsPerMinute": 1,
- "impact": 2.8112687551548836
+ }
},
{
- "name": "GET /api/types",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/types"
+ },
+ "averageResponseTime": 26992.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 2.8066131947777255,
"sample": {
- "@timestamp": "2020-06-29T06:48:26.443Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:47:52.935Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:29.977518Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.471071Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1483,12 +2232,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["112"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:26 GMT"],
- "Etag": ["W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "112"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:52 GMT"
+ ],
+ "Etag": [
+ "W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1514,33 +2275,56 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413306443014 },
- "trace": { "id": "be3f4eb50d253afc032c90eacaa85072" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413272935117
+ },
+ "trace": {
+ "id": "2946c536a33d163d0c984d00d1f3839a"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 8892 },
- "id": "ccce129bb8c6b125",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 45093
+ },
+ "id": "103482fda88b9400",
"name": "GET /api/types",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -1551,42 +2335,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 45248,
- "averageResponseTime": 26992.5,
- "transactionsPerMinute": 0.5,
- "impact": 2.8066131947777255
+ }
},
{
- "name": "GET static file",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET static file"
+ },
+ "averageResponseTime": 3492.9285714285716,
+ "transactionsPerMinute": 3.5,
+ "impact": 2.5144049360435208,
"sample": {
- "@timestamp": "2020-06-29T06:48:40.953Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:47:53.427Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.992454Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.472070Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1596,15 +2405,33 @@
},
"response": {
"headers": {
- "Accept-Ranges": ["bytes"],
- "Cache-Control": ["public, max-age=0"],
- "Connection": ["close"],
- "Content-Length": ["15086"],
- "Content-Type": ["image/x-icon"],
- "Date": ["Mon, 29 Jun 2020 06:48:40 GMT"],
- "Etag": ["W/\"3aee-1725aff14f0\""],
- "Last-Modified": ["Thu, 28 May 2020 11:16:06 GMT"],
- "X-Powered-By": ["Express"]
+ "Accept-Ranges": [
+ "bytes"
+ ],
+ "Cache-Control": [
+ "public, max-age=0"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "15086"
+ ],
+ "Content-Type": [
+ "image/x-icon"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:53 GMT"
+ ],
+ "Etag": [
+ "W/\"3aee-1725aff14f0\""
+ ],
+ "Last-Modified": [
+ "Thu, 28 May 2020 11:16:06 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1624,32 +2451,53 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413320953016 },
- "trace": { "id": "292393440bbe04385f3c2e3715ac35fe" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413273427016
+ },
+ "trace": {
+ "id": "ec8a804fedf28fcf81d5682d69a16970"
+ },
"transaction": {
- "duration": { "us": 1671 },
- "id": "d1d964ca1865dce3",
+ "duration": {
+ "us": 4934
+ },
+ "id": "ab90a62901b770e6",
"name": "GET static file",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1661,55 +2509,84 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 11900,
- "averageResponseTime": 3492.9285714285716,
- "transactionsPerMinute": 3.5,
- "impact": 2.5144049360435208
+ }
},
{
- "name": "APIRestController#customerWhoBought",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#customerWhoBought"
+ },
+ "averageResponseTime": 3742.153846153846,
+ "transactionsPerMinute": 3.25,
+ "impact": 2.4998634943716573,
"sample": {
- "@timestamp": "2020-06-29T06:48:44.982Z",
+ "@timestamp": "2020-06-29T06:48:11.166Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.721114Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.763228Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:44 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:10 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -1724,73 +2601,121 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413324982008 },
- "trace": { "id": "e5ce8ba877f69510e7abc3f0d11c1e4f" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413291166005
+ },
+ "trace": {
+ "id": "fa0d353eb7967b344ed37674f40b2884"
+ },
"transaction": {
- "duration": { "us": 2797 },
- "id": "b8c1bd3b31b197d3",
+ "duration": {
+ "us": 4453
+ },
+ "id": "bce4ce4b09ded6ca",
"name": "APIRestController#customerWhoBought",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 1 },
+ "span_count": {
+ "dropped": 0,
+ "started": 2
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/products/5/customers",
- "path": "/api/products/5/customers",
+ "full": "http://172.18.0.6:3000/api/products/3/customers",
+ "path": "/api/products/3/customers",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 4464,
- "averageResponseTime": 3742.153846153846,
- "transactionsPerMinute": 3.25,
- "impact": 2.4998634943716573
+ }
},
{
- "name": "GET /log-error",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /log-error"
+ },
+ "averageResponseTime": 35846,
+ "transactionsPerMinute": 0.25,
+ "impact": 1.7640550505645587,
"sample": {
"@timestamp": "2020-06-29T06:48:07.467Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:18.533253Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:18.533253Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -1800,12 +2725,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["24"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:07 GMT"],
- "Etag": ["W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "24"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:07 GMT"
+ ],
+ "Etag": [
+ "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -1835,29 +2772,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413287467017 },
- "trace": { "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413287467017
+ },
+ "trace": {
+ "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 35846 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 35846
+ },
"id": "c7a30c1b076907ec",
"name": "GET /log-error",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1868,57 +2828,90 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 35840,
- "averageResponseTime": 35846,
- "transactionsPerMinute": 0.25,
- "impact": 1.7640550505645587
+ }
},
{
- "name": "APIRestController#topProducts",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#topProducts"
+ },
+ "averageResponseTime": 4825,
+ "transactionsPerMinute": 1.75,
+ "impact": 1.6450221426498186,
"sample": {
- "@timestamp": "2020-06-29T06:48:45.587Z",
+ "@timestamp": "2020-06-29T06:48:11.778Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.770758Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.764351Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:44 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:11 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -1933,29 +2926,56 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413325587007 },
- "trace": { "id": "4470d0cc076e22018e2dd258a25c8812" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413291778008
+ },
+ "trace": {
+ "id": "d65e9816f1f6db3961867f7b6d1d4e6a"
+ },
"transaction": {
- "duration": { "us": 4070 },
- "id": "cb860b712121d0d8",
+ "duration": {
+ "us": 4168
+ },
+ "id": "a72f4bb8149ecdc5",
"name": "APIRestController#topProducts",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 1 },
+ "span_count": {
+ "dropped": 0,
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -1966,40 +2986,61 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 7344,
- "averageResponseTime": 4825,
- "transactionsPerMinute": 1.75,
- "impact": 1.6450221426498186
+ }
},
{
- "name": "GET /api/products/top",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/products/top"
+ },
+ "averageResponseTime": 33097,
+ "transactionsPerMinute": 0.25,
+ "impact": 1.6060533780113861,
"sample": {
"@timestamp": "2020-06-29T06:48:01.200Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:02.734903Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:02.734903Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2009,12 +3050,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:01 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:01 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2044,29 +3097,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413281200133 },
- "trace": { "id": "195f32efeb6f91e2f71b6bc8bb74ae3a" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413281200133
+ },
+ "trace": {
+ "id": "195f32efeb6f91e2f71b6bc8bb74ae3a"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 33097 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 33097
+ },
"id": "22e72956dfc8967a",
"name": "GET /api/products/top",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -2077,42 +3153,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 33024,
- "averageResponseTime": 33097,
- "transactionsPerMinute": 0.25,
- "impact": 1.6060533780113861
+ }
},
{
- "name": "GET /api/products",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/products"
+ },
+ "averageResponseTime": 6583,
+ "transactionsPerMinute": 1,
+ "impact": 1.2172278724376455,
"sample": {
- "@timestamp": "2020-06-29T06:48:23.477Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:21.475Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:27.001228Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:26.996210Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2122,12 +3223,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["1023"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:23 GMT"],
- "Etag": ["W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "1023"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:21 GMT"
+ ],
+ "Etag": [
+ "W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2157,29 +3270,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413303477028 },
- "trace": { "id": "9f26158b9a3915577b3cccc17636ea01" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413301475015
+ },
+ "trace": {
+ "id": "389b26b16949c7f783223de4f14b788c"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 7150 },
- "id": "27ff4add22ac2e1b",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 6775
+ },
+ "id": "d2d4088a0b104fb4",
"name": "GET /api/products",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -2190,46 +3326,79 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 8160,
- "averageResponseTime": 6583,
- "transactionsPerMinute": 1,
- "impact": 1.2172278724376455
+ }
},
{
- "name": "POST /api",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "POST /api"
+ },
+ "averageResponseTime": 20011,
+ "transactionsPerMinute": 0.25,
+ "impact": 0.853921734857215,
"sample": {
"@timestamp": "2020-06-29T06:48:25.478Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:27.005671Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:27.005671Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
- "body": { "original": "[REDACTED]" },
+ "body": {
+ "original": "[REDACTED]"
+ },
"headers": {
- "Accept": ["application/json"],
- "Connection": ["close"],
- "Content-Length": ["129"],
- "Content-Type": ["application/json"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "application/json"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "129"
+ ],
+ "Content-Type": [
+ "application/json"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "post",
"socket": {
@@ -2239,12 +3408,24 @@
},
"response": {
"headers": {
- "Allow": ["GET"],
- "Connection": ["close"],
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:25 GMT"],
- "Transfer-Encoding": ["chunked"],
- "X-Powered-By": ["Express"]
+ "Allow": [
+ "GET"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:25 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 405
},
@@ -2274,29 +3455,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413305478010 },
- "trace": { "id": "4bd9027dd1e355ec742970e2d6333124" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413305478010
+ },
+ "trace": {
+ "id": "4bd9027dd1e355ec742970e2d6333124"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 20011 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 20011
+ },
"id": "94104435cf151478",
"name": "POST /api",
"result": "HTTP 4xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -2307,42 +3511,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 19968,
- "averageResponseTime": 20011,
- "transactionsPerMinute": 0.25,
- "impact": 0.853921734857215
+ }
},
{
- "name": "GET /api/types/:id",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/types/:id"
+ },
+ "averageResponseTime": 8181,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.6441916136689552,
"sample": {
- "@timestamp": "2020-06-29T06:48:12.972Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:47:53.928Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:18.543436Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.472718Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2352,12 +3581,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["205"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:12 GMT"],
- "Etag": ["W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "205"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:53 GMT"
+ ],
+ "Etag": [
+ "W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2383,33 +3624,56 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413292972011 },
- "trace": { "id": "aea65cef5f902dda5d8e38f9fb38864d" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413273928016
+ },
+ "trace": {
+ "id": "0becaafb422bfeb69e047bf7153aa469"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 6300 },
- "id": "a5bdbe43ac05fae2",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 10062
+ },
+ "id": "0cee4574091bda3b",
"name": "GET /api/types/:id",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -2420,42 +3684,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 10080,
- "averageResponseTime": 8181,
- "transactionsPerMinute": 0.5,
- "impact": 0.6441916136689552
+ }
},
{
- "name": "GET /api/stats",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/stats"
+ },
+ "averageResponseTime": 5098,
+ "transactionsPerMinute": 0.75,
+ "impact": 0.582807187955318,
"sample": {
- "@timestamp": "2020-06-29T06:48:39.451Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:34.949Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.984824Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:39.479316Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2465,12 +3754,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["92"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:39 GMT"],
- "Etag": ["W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "92"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:34 GMT"
+ ],
+ "Etag": [
+ "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2500,29 +3801,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413319451016 },
- "trace": { "id": "a05787cb03a0af0863fab5e05de942f1" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413314949017
+ },
+ "trace": {
+ "id": "616b3b77abd5534c61d6c0438469aee2"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 5050 },
- "id": "a7e004eba8f021ce",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 5459
+ },
+ "id": "5b4971de59d2099d",
"name": "GET /api/stats",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 4 },
+ "span_count": {
+ "started": 4
+ },
"type": "request"
},
"url": {
@@ -2533,42 +3857,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 5440,
- "averageResponseTime": 5098,
- "transactionsPerMinute": 0.75,
- "impact": 0.582807187955318
+ }
},
{
- "name": "GET /api/orders",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/orders"
+ },
+ "averageResponseTime": 7624.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.5802207655235637,
"sample": {
"@timestamp": "2020-06-29T06:48:35.450Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.483715Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:39.483715Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2578,12 +3927,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:35 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:35 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2613,29 +3974,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413315450014 },
- "trace": { "id": "2da70ccf10599b271f65273d169cde9f" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413315450014
+ },
+ "trace": {
+ "id": "2da70ccf10599b271f65273d169cde9f"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 8784 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 8784
+ },
"id": "a3f4a4f339758440",
"name": "GET /api/orders",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -2646,42 +4030,67 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 8800,
- "averageResponseTime": 7624.5,
- "transactionsPerMinute": 0.5,
- "impact": 0.5802207655235637
+ }
},
{
- "name": "GET /api/orders/:id",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/orders/:id"
+ },
+ "averageResponseTime": 4749.666666666667,
+ "transactionsPerMinute": 0.75,
+ "impact": 0.5227447114845778,
"sample": {
"@timestamp": "2020-06-29T06:48:35.951Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.484133Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:39.484133Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2691,10 +4100,18 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["0"],
- "Date": ["Mon, 29 Jun 2020 06:48:35 GMT"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "0"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:35 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 404
},
@@ -2724,29 +4141,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413315951017 },
- "trace": { "id": "95979caa80e6622cbbb2d308800c3016" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413315951017
+ },
+ "trace": {
+ "id": "95979caa80e6622cbbb2d308800c3016"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 3210 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 3210
+ },
"id": "30344988dace0b43",
"name": "GET /api/orders/:id",
"result": "HTTP 4xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -2757,57 +4197,90 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 7184,
- "averageResponseTime": 4749.666666666667,
- "transactionsPerMinute": 0.75,
- "impact": 0.5227447114845778
+ }
},
{
- "name": "APIRestController#products",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#products"
+ },
+ "averageResponseTime": 6787,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.4839483750082622,
"sample": {
- "@timestamp": "2020-06-29T06:48:27.824Z",
+ "@timestamp": "2020-06-29T06:48:13.595Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:36.087688Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.755614Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:27 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:12 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -2822,29 +4295,56 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413307824008 },
- "trace": { "id": "a6adb17bd5a5d1c0eabb9f36cb626dd5" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413293595007
+ },
+ "trace": {
+ "id": "8519b6c3dbc32a0582228506526e1d74"
+ },
"transaction": {
- "duration": { "us": 5645 },
- "id": "df3e726eaa003d96",
+ "duration": {
+ "us": 7929
+ },
+ "id": "b0354de660cd3698",
"name": "APIRestController#products",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 3 },
+ "span_count": {
+ "dropped": 0,
+ "started": 3
+ },
"type": "request"
},
"url": {
@@ -2855,40 +4355,61 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 7904,
- "averageResponseTime": 6787,
- "transactionsPerMinute": 0.5,
- "impact": 0.4839483750082622
+ }
},
{
- "name": "GET /api/products/:id/customers",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /api/products/:id/customers"
+ },
+ "averageResponseTime": 4757,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.25059559560997896,
"sample": {
- "@timestamp": "2020-06-29T06:48:41.956Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:22.977Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.994692Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:27.000765Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -2898,12 +4419,24 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:41 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:22 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -2933,90 +4466,146 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413321956016 },
- "trace": { "id": "f735ac5fca8f83eebc748f4a2e009e61" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413302977008
+ },
+ "trace": {
+ "id": "da8f22fe652ccb6680b3029ab6efd284"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 3896 },
- "id": "b24ce95c855f83a4",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 5618
+ },
+ "id": "bc51c1523afaf57a",
"name": "GET /api/products/:id/customers",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/5/customers",
- "original": "/api/products/5/customers",
- "path": "/api/products/5/customers",
+ "full": "http://opbeans-node:3000/api/products/3/customers",
+ "original": "/api/products/3/customers",
+ "path": "/api/products/3/customers",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 5616,
- "averageResponseTime": 4757,
- "transactionsPerMinute": 0.5,
- "impact": 0.25059559560997896
+ }
},
{
- "name": "APIRestController#product",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#product"
+ },
+ "averageResponseTime": 4713.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.24559517890858723,
"sample": {
- "@timestamp": "2020-06-29T06:48:41.941Z",
+ "@timestamp": "2020-06-29T06:48:36.383Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.709268Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:46.666467Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:41 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:36 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -3031,81 +4620,131 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413321941007 },
- "trace": { "id": "88a2b9ca970cdd38dfa1b5646d26b897" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413316383008
+ },
+ "trace": {
+ "id": "386b450aef87fc079b20136eda542af1"
+ },
"transaction": {
- "duration": { "us": 4539 },
- "id": "24ee0e4812cfed62",
+ "duration": {
+ "us": 4888
+ },
+ "id": "5a4aa02158b5658c",
"name": "APIRestController#product",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 2 },
+ "span_count": {
+ "dropped": 0,
+ "started": 3
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/products/4",
- "path": "/api/products/4",
+ "full": "http://172.18.0.6:3000/api/products/1",
+ "path": "/api/products/1",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 4864,
- "averageResponseTime": 4713.5,
- "transactionsPerMinute": 0.5,
- "impact": 0.24559517890858723
+ }
},
{
- "name": "APIRestController#order",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#order"
+ },
+ "averageResponseTime": 3392.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.09374344413758617,
"sample": {
- "@timestamp": "2020-06-29T06:48:33.314Z",
+ "@timestamp": "2020-06-29T06:48:07.416Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:36.137777Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:15.534378Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
@@ -3122,88 +4761,144 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413313314007 },
- "trace": { "id": "aaf67f944393124080d1e4de804dc6eb" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413287416007
+ },
+ "trace": {
+ "id": "25c46380df3d44a192ed07279a08b329"
+ },
"transaction": {
- "duration": { "us": 2503 },
- "id": "f7f9f5e0f8a3a0d4",
+ "duration": {
+ "us": 4282
+ },
+ "id": "d4d5b23c685d2ee5",
"name": "APIRestController#order",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 1 },
+ "span_count": {
+ "dropped": 0,
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "172.18.0.6",
- "full": "http://172.18.0.6:3000/api/orders/906",
- "path": "/api/orders/906",
+ "full": "http://172.18.0.6:3000/api/orders/391",
+ "path": "/api/orders/391",
"port": 3000,
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 4272,
- "averageResponseTime": 3392.5,
- "transactionsPerMinute": 0.5,
- "impact": 0.09374344413758617
+ }
},
{
- "name": "APIRestController#orders",
+ "key": {
+ "service.name": "opbeans-java",
+ "transaction.name": "APIRestController#orders"
+ },
+ "averageResponseTime": 3147,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.06552270160444405,
"sample": {
- "@timestamp": "2020-06-29T06:48:39.500Z",
+ "@timestamp": "2020-06-29T06:48:16.028Z",
"agent": {
"ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de",
"name": "java",
"version": "1.17.1-SNAPSHOT"
},
- "client": { "ip": "172.18.0.9" },
+ "client": {
+ "ip": "172.18.0.9"
+ },
"container": {
"id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:46.706280Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:25.800962Z"
+ },
"host": {
"architecture": "amd64",
"hostname": "918ebbd99b4f",
"ip": "172.18.0.6",
"name": "918ebbd99b4f",
- "os": { "platform": "Linux" }
+ "os": {
+ "platform": "Linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Host": ["172.18.0.6:3000"],
- "User-Agent": ["Python/3.7 aiohttp/3.3.2"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Host": [
+ "172.18.0.6:3000"
+ ],
+ "User-Agent": [
+ "Python/3.7 aiohttp/3.3.2"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "172.18.0.9" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "172.18.0.9"
+ }
},
"response": {
"finished": true,
"headers": {
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:38 GMT"],
- "Transfer-Encoding": ["chunked"]
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:15 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ]
},
"headers_sent": true,
"status_code": 200
@@ -3218,29 +4913,56 @@
"version": "8.0.0",
"version_major": 8
},
- "process": { "pid": 6, "ppid": 1, "title": "/opt/java/openjdk/bin/java" },
- "processor": { "event": "transaction", "name": "transaction" },
+ "process": {
+ "pid": 6,
+ "ppid": 1,
+ "title": "/opt/java/openjdk/bin/java"
+ },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "Spring Web MVC", "version": "5.0.6.RELEASE" },
- "language": { "name": "Java", "version": "11.0.7" },
+ "framework": {
+ "name": "Spring Web MVC",
+ "version": "5.0.6.RELEASE"
+ },
+ "language": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"name": "opbeans-java",
"node": {
"name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0"
},
- "runtime": { "name": "Java", "version": "11.0.7" },
+ "runtime": {
+ "name": "Java",
+ "version": "11.0.7"
+ },
"version": "None"
},
- "source": { "ip": "172.18.0.9" },
- "timestamp": { "us": 1593413319500008 },
- "trace": { "id": "f89b02f09a2e7a7f2cc3478f53d4a495" },
+ "source": {
+ "ip": "172.18.0.9"
+ },
+ "timestamp": {
+ "us": 1593413296028008
+ },
+ "trace": {
+ "id": "4110227ecacbccf79894165ae5df932d"
+ },
"transaction": {
- "duration": { "us": 3391 },
- "id": "41c8c4300ee2ccda",
+ "duration": {
+ "us": 2903
+ },
+ "id": "8e3732f0f0da942b",
"name": "APIRestController#orders",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "dropped": 0, "started": 2 },
+ "span_count": {
+ "dropped": 0,
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -3251,40 +4973,61 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "Python/3.7 aiohttp/3.3.2"
}
- },
- "p95": 3376,
- "averageResponseTime": 3147,
- "transactionsPerMinute": 0.5,
- "impact": 0.06552270160444405
+ }
},
{
- "name": "GET /throw-error",
+ "key": {
+ "service.name": "opbeans-node",
+ "transaction.name": "GET /throw-error"
+ },
+ "averageResponseTime": 2577,
+ "transactionsPerMinute": 0.5,
+ "impact": 0,
"sample": {
- "@timestamp": "2020-06-29T06:48:42.954Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
+ "@timestamp": "2020-06-29T06:48:19.975Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
"container": {
"id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.996435Z" },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:21.012520Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
"socket": {
@@ -3294,13 +5037,27 @@
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["148"],
- "Content-Security-Policy": ["default-src 'none'"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:42 GMT"],
- "X-Content-Type-Options": ["nosniff"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "148"
+ ],
+ "Content-Security-Policy": [
+ "default-src 'none'"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:19 GMT"
+ ],
+ "X-Content-Type-Options": [
+ "nosniff"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -3330,29 +5087,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
"node": {
"name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
},
- "runtime": { "name": "node", "version": "12.18.1" },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413322954016 },
- "trace": { "id": "9d5aee7443a43db9820f622a10dfac6e" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413299975019
+ },
+ "trace": {
+ "id": "106f3a55b0b0ea327d1bbe4be66c3bcc"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 1928 },
- "id": "8e6fc8c3f99e8fc9",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 3226
+ },
+ "id": "247b9141552a9e73",
"name": "GET /throw-error",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -3363,16 +5143,18 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 3224,
- "averageResponseTime": 2577,
- "transactionsPerMinute": 0.5,
- "impact": 0
+ }
}
]
diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts
index e96cb20a68fda6..b4a037436adb8a 100644
--- a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts
+++ b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
+import { sortBy, omit } from 'lodash';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
import expectTopTraces from './expectation/top_traces.expectation.json';
@@ -46,8 +47,28 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(response.body.items.length).to.be(33);
});
- it('returns the correct buckets and samples', async () => {
- expect(response.body.items).to.eql(expectTopTraces);
+ it('returns the correct buckets', async () => {
+ const responseWithoutSamples = sortBy(
+ response.body.items.map((item: any) => omit(item, 'sample')),
+ 'impact'
+ );
+
+ const expectedTracesWithoutSamples = sortBy(
+ expectTopTraces.map((item: any) => omit(item, 'sample')),
+ 'impact'
+ );
+
+ expect(responseWithoutSamples).to.eql(expectedTracesWithoutSamples);
+ });
+
+ it('returns a sample', async () => {
+ // sample should provide enough information to deeplink to a transaction detail page
+ response.body.items.forEach((item: any) => {
+ expect(item.sample.trace.id).to.be.an('string');
+ expect(item.sample.transaction.id).to.be.an('string');
+ expect(item.sample.service.name).to.be(item.key['service.name']);
+ expect(item.sample.transaction.name).to.be(item.key['transaction.name']);
+ });
});
});
});
diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json
index 7d314e75e4d1de..29c55d4ef1b5c3 100644
--- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json
+++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json
@@ -1,38 +1,86 @@
{
"items": [
{
- "name": "GET /api",
+ "key": "GET /api",
+ "averageResponseTime": 51175.73170731707,
+ "transactionsPerMinute": 10.25,
+ "impact": 100,
+ "p95": 259040,
"sample": {
- "@timestamp": "2020-06-29T06:48:41.454Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.992834Z" },
+ "@timestamp": "2020-06-29T06:48:06.862Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:08.305742Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Connection": [
+ "keep-alive"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "Referer": [
+ "http://opbeans-node:3000/dashboard"
+ ],
+ "Traceparent": [
+ "00-ca86ffcac7753ec8733933bd8fd45d11-5dcb98c9c9021cfc-01"
+ ],
+ "User-Agent": [
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.8"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:41 GMT"],
- "Transfer-Encoding": ["chunked"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:06 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -52,6 +100,9 @@
"version": "8.0.0",
"version_major": 8
},
+ "parent": {
+ "id": "5dcb98c9c9021cfc"
+ },
"process": {
"args": [
"/usr/local/bin/node",
@@ -62,87 +113,164 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413321454009 },
- "trace": { "id": "0507830eeff93f7bf1a354e4031097b3" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413286862021
+ },
+ "trace": {
+ "id": "ca86ffcac7753ec8733933bd8fd45d11"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 8334 },
- "id": "878250a8b937445d",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 15738
+ },
+ "id": "c95371db21c6f407",
"name": "GET /api",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/6",
- "original": "/api/products/6",
- "path": "/api/products/6",
+ "full": "http://opbeans-node:3000/api/products/top",
+ "original": "/api/products/top",
+ "path": "/api/products/top",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
- "name": "Other",
- "original": "workload/2.4.3"
+ "device": {
+ "name": "Other"
+ },
+ "name": "HeadlessChrome",
+ "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
+ "os": {
+ "name": "Linux"
+ },
+ "version": "79.0.3945"
}
- },
- "p95": 259040,
- "averageResponseTime": 51175.73170731707,
- "transactionsPerMinute": 10.25,
- "impact": 100
+ }
},
{
- "name": "POST /api/orders",
+ "key": "POST /api/orders",
+ "averageResponseTime": 270684,
+ "transactionsPerMinute": 0.25,
+ "impact": 12.686265169840583,
+ "p95": 270336,
"sample": {
"@timestamp": "2020-06-29T06:48:39.953Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.991549Z" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:43.991549Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
- "body": { "original": "[REDACTED]" },
+ "body": {
+ "original": "[REDACTED]"
+ },
"headers": {
- "Accept": ["application/json"],
- "Connection": ["close"],
- "Content-Length": ["129"],
- "Content-Type": ["application/json"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "application/json"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "129"
+ ],
+ "Content-Type": [
+ "application/json"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "post",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["13"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:40 GMT"],
- "Etag": ["W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "13"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:40 GMT"
+ ],
+ "Etag": [
+ "W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -172,27 +300,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413319953033 },
- "trace": { "id": "52b8fda5f6df745b990740ba18378620" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413319953033
+ },
+ "trace": {
+ "id": "52b8fda5f6df745b990740ba18378620"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 270684 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 270684
+ },
"id": "a3afc2a112e9c893",
"name": "POST /api/orders",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 16 },
+ "span_count": {
+ "started": 16
+ },
"type": "request"
},
"url": {
@@ -203,52 +356,92 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 270336,
- "averageResponseTime": 270684,
- "transactionsPerMinute": 0.25,
- "impact": 12.686265169840583
+ }
},
{
- "name": "GET /api/customers",
+ "key": "GET /api/customers",
+ "averageResponseTime": 16896.8,
+ "transactionsPerMinute": 1.25,
+ "impact": 3.790160870423129,
+ "p95": 26432,
"sample": {
- "@timestamp": "2020-06-29T06:48:37.952Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.492402Z" },
+ "@timestamp": "2020-06-29T06:48:28.444Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.982737Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["186769"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:37 GMT"],
- "Etag": ["W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "186769"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:28 GMT"
+ ],
+ "Etag": [
+ "W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -278,27 +471,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413317952016 },
- "trace": { "id": "5d99327edae38ed735e8d7a6028cf719" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413308444015
+ },
+ "trace": {
+ "id": "792fb0b00256164e88b277ec40b65e14"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 16824 },
- "id": "071808429ec9d00b",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 26471
+ },
+ "id": "6c1f848752563d2b",
"name": "GET /api/customers",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -309,52 +527,92 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 26432,
- "averageResponseTime": 16896.8,
- "transactionsPerMinute": 1.25,
- "impact": 3.790160870423129
+ }
},
{
- "name": "GET /log-message",
+ "key": "GET /log-message",
+ "averageResponseTime": 32667.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 2.875276331059301,
+ "p95": 38528,
"sample": {
- "@timestamp": "2020-06-29T06:48:28.944Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.370695Z" },
+ "@timestamp": "2020-06-29T06:48:25.944Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.976822Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["24"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:28 GMT"],
- "Etag": ["W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "24"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:25 GMT"
+ ],
+ "Etag": [
+ "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -384,27 +642,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413308944016 },
- "trace": { "id": "afabe4cb397616f5ec35a2f3da49ba62" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413305944023
+ },
+ "trace": {
+ "id": "cd2ad726ad164d701c5d3103cbab0c81"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 26788 },
- "id": "cc8c4261387507cf",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 38547
+ },
+ "id": "9e41667eb64dea55",
"name": "GET /log-message",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -415,51 +698,89 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 38528,
- "averageResponseTime": 32667.5,
- "transactionsPerMinute": 0.5,
- "impact": 2.875276331059301
+ }
},
{
- "name": "GET /*",
+ "key": "GET /*",
+ "averageResponseTime": 3262.95,
+ "transactionsPerMinute": 5,
+ "impact": 2.8716452680799467,
+ "p95": 4472,
"sample": {
- "@timestamp": "2020-06-29T06:48:42.454Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.995202Z" },
+ "@timestamp": "2020-06-29T06:48:25.064Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:27.005197Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "Wget"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["813"],
- "Content-Type": ["text/html"],
- "Date": ["Mon, 29 Jun 2020 06:48:42 GMT"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "813"
+ ],
+ "Content-Type": [
+ "text/html"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:25 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -479,7 +800,9 @@
"version": "8.0.0",
"version_major": 8
},
- "parent": { "id": "5baa6c3bedc93f9d" },
+ "parent": {
+ "id": "f673ceaf4583f0f2"
+ },
"process": {
"args": [
"/usr/local/bin/node",
@@ -490,27 +813,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413322454015 },
- "trace": { "id": "022b01256b291a4c417199d91ec8755f" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413305064023
+ },
+ "trace": {
+ "id": "30c12f4d8ef77a5be1b4464e5d2235bc"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 1737 },
- "id": "eff3e45e0d9529d9",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 3004
+ },
+ "id": "18a00dfdb919a978",
"name": "GET /*",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -521,59 +869,104 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
- "name": "Other",
- "original": "workload/2.4.3"
+ "device": {
+ "name": "Other"
+ },
+ "name": "Wget",
+ "original": "Wget"
}
- },
- "p95": 4472,
- "averageResponseTime": 3262.95,
- "transactionsPerMinute": 5,
- "impact": 2.8716452680799467
+ }
},
{
- "name": "GET /api/orders",
+ "key": "GET /api/orders",
+ "averageResponseTime": 7615.625,
+ "transactionsPerMinute": 2,
+ "impact": 2.6645791239678345,
+ "p95": 11616,
"sample": {
- "@timestamp": "2020-06-29T06:48:40.106Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.6" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.988104Z" },
+ "@timestamp": "2020-06-29T06:48:28.782Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:29.983252Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
"Accept": [
- "text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*"
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Connection": [
+ "keep-alive"
],
- "Connection": ["keep-alive"],
- "Elastic-Apm-Traceparent": [
- "00-90bd7780b32cc51a7f4c200b1e0c170f-5ff346d602ce27b0-01"
+ "Host": [
+ "opbeans-node:3000"
],
- "Host": ["opbeans-node:3000"],
- "Traceparent": ["00-90bd7780b32cc51a7f4c200b1e0c170f-5ff346d602ce27b0-01"],
- "User-Agent": ["Java/11.0.7"]
+ "Referer": [
+ "http://opbeans-node:3000/orders"
+ ],
+ "Traceparent": [
+ "00-978b56807e0b7a27cbc41a0dfb665f47-3358a24e09e23561-01"
+ ],
+ "User-Agent": [
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.6" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.8"
+ }
},
"response": {
"headers": {
- "Connection": ["keep-alive"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:40 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "keep-alive"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:28 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -593,7 +986,9 @@
"version": "8.0.0",
"version_major": 8
},
- "parent": { "id": "5ff346d602ce27b0" },
+ "parent": {
+ "id": "3358a24e09e23561"
+ },
"process": {
"args": [
"/usr/local/bin/node",
@@ -604,27 +999,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.6" },
- "timestamp": { "us": 1593413320106015 },
- "trace": { "id": "90bd7780b32cc51a7f4c200b1e0c170f" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413308782015
+ },
+ "trace": {
+ "id": "978b56807e0b7a27cbc41a0dfb665f47"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 7424 },
- "id": "f3dd00d12c594cba",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 7134
+ },
+ "id": "a6d8f3c5c98903e1",
"name": "GET /api/orders",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -635,60 +1055,96 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Spider" },
- "name": "Java",
- "original": "Java/11.0.7",
- "version": "0.7."
+ "device": {
+ "name": "Other"
+ },
+ "name": "HeadlessChrome",
+ "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
+ "os": {
+ "name": "Linux"
+ },
+ "version": "79.0.3945"
}
- },
- "p95": 11616,
- "averageResponseTime": 7615.625,
- "transactionsPerMinute": 2,
- "impact": 2.6645791239678345
+ }
},
{
- "name": "GET /api/products",
+ "key": "GET /api/products",
+ "averageResponseTime": 8585,
+ "transactionsPerMinute": 1.75,
+ "impact": 2.624924094061731,
+ "p95": 22112,
"sample": {
- "@timestamp": "2020-06-29T06:48:27.452Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.6" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:29.978463Z" },
+ "@timestamp": "2020-06-29T06:48:21.475Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:26.996210Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": [
- "text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*"
+ "Connection": [
+ "close"
],
- "Connection": ["keep-alive"],
- "Elastic-Apm-Traceparent": [
- "00-27b168a328e0fd442377de8eaa0bf582-2c86873dedb66c2c-01"
+ "Host": [
+ "opbeans-node:3000"
],
- "Host": ["opbeans-node:3000"],
- "Traceparent": ["00-27b168a328e0fd442377de8eaa0bf582-2c86873dedb66c2c-01"],
- "User-Agent": ["Java/11.0.7"]
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.6" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["keep-alive"],
- "Content-Length": ["1023"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:27 GMT"],
- "Etag": ["W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "1023"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:21 GMT"
+ ],
+ "Etag": [
+ "W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -708,7 +1164,6 @@
"version": "8.0.0",
"version_major": 8
},
- "parent": { "id": "2c86873dedb66c2c" },
"process": {
"args": [
"/usr/local/bin/node",
@@ -719,27 +1174,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.6" },
- "timestamp": { "us": 1593413307452013 },
- "trace": { "id": "27b168a328e0fd442377de8eaa0bf582" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413301475015
+ },
+ "trace": {
+ "id": "389b26b16949c7f783223de4f14b788c"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 4292 },
- "id": "141ecc2dfd55eeea",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 6775
+ },
+ "id": "d2d4088a0b104fb4",
"name": "GET /api/products",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -750,53 +1230,92 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Spider" },
- "name": "Java",
- "original": "Java/11.0.7",
- "version": "0.7."
+ "device": {
+ "name": "Other"
+ },
+ "name": "Other",
+ "original": "workload/2.4.3"
}
- },
- "p95": 22112,
- "averageResponseTime": 8585,
- "transactionsPerMinute": 1.75,
- "impact": 2.624924094061731
+ }
},
{
- "name": "GET /api/products/:id",
+ "key": "GET /api/products/:id",
+ "averageResponseTime": 13516.5,
+ "transactionsPerMinute": 1,
+ "impact": 2.3368756900811305,
+ "p95": 37856,
"sample": {
- "@timestamp": "2020-06-29T06:48:24.977Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:27.004717Z" },
+ "@timestamp": "2020-06-29T06:47:57.555Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:59.085077Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["231"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:24 GMT"],
- "Etag": ["W/\"e7-kkuzj37GZDzXDh0CWqh5Gan0VO4\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "231"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:57 GMT"
+ ],
+ "Etag": [
+ "W/\"e7-6JlJegaJ+ir0C8I8EmmOjms1dnc\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -822,87 +1341,152 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 87,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413304977014 },
- "trace": { "id": "b9b290bca14c99962fa9a1c509401630" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413277555176
+ },
+ "trace": {
+ "id": "8365e1763f19e4067b88521d4d9247a0"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 4482 },
- "id": "b8d8284ff1fc25d6",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 37709
+ },
+ "id": "be2722a418272f10",
"name": "GET /api/products/:id",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/3",
- "original": "/api/products/3",
- "path": "/api/products/3",
+ "full": "http://opbeans-node:3000/api/products/1",
+ "original": "/api/products/1",
+ "path": "/api/products/1",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 37856,
- "averageResponseTime": 13516.5,
- "transactionsPerMinute": 1,
- "impact": 2.3368756900811305
+ }
},
{
- "name": "GET /api/types",
+ "key": "GET /api/types",
+ "averageResponseTime": 26992.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 2.3330057413794503,
+ "p95": 45248,
"sample": {
- "@timestamp": "2020-06-29T06:48:26.443Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:29.977518Z" },
+ "@timestamp": "2020-06-29T06:47:52.935Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.471071Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["112"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:26 GMT"],
- "Etag": ["W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "112"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:52 GMT"
+ ],
+ "Etag": [
+ "W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -928,31 +1512,56 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413306443014 },
- "trace": { "id": "be3f4eb50d253afc032c90eacaa85072" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413272935117
+ },
+ "trace": {
+ "id": "2946c536a33d163d0c984d00d1f3839a"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 8892 },
- "id": "ccce129bb8c6b125",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 45093
+ },
+ "id": "103482fda88b9400",
"name": "GET /api/types",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -963,55 +1572,101 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 45248,
- "averageResponseTime": 26992.5,
- "transactionsPerMinute": 0.5,
- "impact": 2.3330057413794503
+ }
},
{
- "name": "GET static file",
+ "key": "GET static file",
+ "averageResponseTime": 3492.9285714285716,
+ "transactionsPerMinute": 3.5,
+ "impact": 2.0901067389184496,
+ "p95": 11900,
"sample": {
- "@timestamp": "2020-06-29T06:48:40.953Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.992454Z" },
+ "@timestamp": "2020-06-29T06:47:53.427Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.472070Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Accept-Ranges": ["bytes"],
- "Cache-Control": ["public, max-age=0"],
- "Connection": ["close"],
- "Content-Length": ["15086"],
- "Content-Type": ["image/x-icon"],
- "Date": ["Mon, 29 Jun 2020 06:48:40 GMT"],
- "Etag": ["W/\"3aee-1725aff14f0\""],
- "Last-Modified": ["Thu, 28 May 2020 11:16:06 GMT"],
- "X-Powered-By": ["Express"]
+ "Accept-Ranges": [
+ "bytes"
+ ],
+ "Cache-Control": [
+ "public, max-age=0"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "15086"
+ ],
+ "Content-Type": [
+ "image/x-icon"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:53 GMT"
+ ],
+ "Etag": [
+ "W/\"3aee-1725aff14f0\""
+ ],
+ "Last-Modified": [
+ "Thu, 28 May 2020 11:16:06 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1031,30 +1686,53 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413320953016 },
- "trace": { "id": "292393440bbe04385f3c2e3715ac35fe" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413273427016
+ },
+ "trace": {
+ "id": "ec8a804fedf28fcf81d5682d69a16970"
+ },
"transaction": {
- "duration": { "us": 1671 },
- "id": "d1d964ca1865dce3",
+ "duration": {
+ "us": 4934
+ },
+ "id": "ab90a62901b770e6",
"name": "GET static file",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1066,56 +1744,86 @@
"scheme": "http"
},
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 11900,
- "averageResponseTime": 3492.9285714285716,
- "transactionsPerMinute": 3.5,
- "impact": 2.0901067389184496
+ }
},
{
- "name": "GET /api/products/top",
+ "key": "GET /api/products/top",
+ "averageResponseTime": 22958.5,
+ "transactionsPerMinute": 0.5,
+ "impact": 1.9475397398343375,
+ "p95": 33216,
"sample": {
- "@timestamp": "2020-06-29T06:48:18.211Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.8" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:21.007197Z" },
+ "@timestamp": "2020-06-29T06:48:01.200Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:02.734903Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Accept": ["*/*"],
- "Accept-Encoding": ["gzip, deflate"],
- "Connection": ["keep-alive"],
- "Host": ["opbeans-node:3000"],
- "Referer": ["http://opbeans-node:3000/dashboard"],
- "Traceparent": ["00-4879105b2de793ca54ce7299aff0f5ce-0d67fab9c9dec84d-01"],
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
"User-Agent": [
- "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36"
+ "workload/2.4.3"
]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.8" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["keep-alive"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:18 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:01 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1135,38 +1843,62 @@
"version": "8.0.0",
"version_major": 8
},
- "parent": { "id": "0d67fab9c9dec84d" },
"process": {
"args": [
"/usr/local/bin/node",
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 115,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.8" },
- "timestamp": { "us": 1593413298211013 },
- "trace": { "id": "4879105b2de793ca54ce7299aff0f5ce" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413281200133
+ },
+ "trace": {
+ "id": "195f32efeb6f91e2f71b6bc8bb74ae3a"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 12820 },
- "id": "b15b12c837ab8b89",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 33097
+ },
+ "id": "22e72956dfc8967a",
"name": "GET /api/products/top",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -1177,56 +1909,103 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
- "name": "HeadlessChrome",
- "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
- "os": { "name": "Linux" },
- "version": "79.0.3945"
+ "device": {
+ "name": "Other"
+ },
+ "name": "Other",
+ "original": "workload/2.4.3"
}
- },
- "p95": 33216,
- "averageResponseTime": 22958.5,
- "transactionsPerMinute": 0.5,
- "impact": 1.9475397398343375
+ }
},
{
- "name": "GET /api/stats",
+ "key": "GET /api/stats",
+ "averageResponseTime": 7105.333333333333,
+ "transactionsPerMinute": 1.5,
+ "impact": 1.7905918202662048,
+ "p95": 15136,
"sample": {
- "@timestamp": "2020-06-29T06:48:39.451Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.984824Z" },
+ "@timestamp": "2020-06-29T06:48:21.150Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.8"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:26.993832Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "*/*"
+ ],
+ "Accept-Encoding": [
+ "gzip, deflate"
+ ],
+ "Connection": [
+ "keep-alive"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "If-None-Match": [
+ "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\""
+ ],
+ "Referer": [
+ "http://opbeans-node:3000/dashboard"
+ ],
+ "Traceparent": [
+ "00-ee0ce8b38b8d5945829fc1c9432538bf-39d52cd5f528d363-01"
+ ],
+ "User-Agent": [
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.8"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["92"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:39 GMT"],
- "Etag": ["W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "keep-alive"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:21 GMT"
+ ],
+ "Etag": [
+ "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
- "status_code": 200
+ "status_code": 304
},
"version": "1.1"
},
@@ -1244,6 +2023,9 @@
"version": "8.0.0",
"version_major": 8
},
+ "parent": {
+ "id": "39d52cd5f528d363"
+ },
"process": {
"args": [
"/usr/local/bin/node",
@@ -1254,27 +2036,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413319451016 },
- "trace": { "id": "a05787cb03a0af0863fab5e05de942f1" },
+ "source": {
+ "ip": "172.18.0.8"
+ },
+ "timestamp": {
+ "us": 1593413301150014
+ },
+ "trace": {
+ "id": "ee0ce8b38b8d5945829fc1c9432538bf"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 5050 },
- "id": "a7e004eba8f021ce",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 7273
+ },
+ "id": "05d5b62182c59a54",
"name": "GET /api/stats",
- "result": "HTTP 2xx",
+ "result": "HTTP 3xx",
"sampled": true,
- "span_count": { "started": 4 },
+ "span_count": {
+ "started": 4
+ },
"type": "request"
},
"url": {
@@ -1285,52 +2092,96 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
- "name": "Other",
- "original": "workload/2.4.3"
+ "device": {
+ "name": "Other"
+ },
+ "name": "HeadlessChrome",
+ "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36",
+ "os": {
+ "name": "Linux"
+ },
+ "version": "79.0.3945"
}
- },
- "p95": 15136,
- "averageResponseTime": 7105.333333333333,
- "transactionsPerMinute": 1.5,
- "impact": 1.7905918202662048
+ }
},
{
- "name": "GET /log-error",
+ "key": "GET /log-error",
+ "averageResponseTime": 35846,
+ "transactionsPerMinute": 0.25,
+ "impact": 1.466376117925459,
+ "p95": 35840,
"sample": {
"@timestamp": "2020-06-29T06:48:07.467Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:18.533253Z" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:18.533253Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["24"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:07 GMT"],
- "Etag": ["W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "24"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:07 GMT"
+ ],
+ "Etag": [
+ "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -1360,27 +2211,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413287467017 },
- "trace": { "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413287467017
+ },
+ "trace": {
+ "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 35846 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 35846
+ },
"id": "c7a30c1b076907ec",
"name": "GET /log-error",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1391,56 +2267,104 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 35840,
- "averageResponseTime": 35846,
- "transactionsPerMinute": 0.25,
- "impact": 1.466376117925459
+ }
},
{
- "name": "POST /api",
+ "key": "POST /api",
+ "averageResponseTime": 20011,
+ "transactionsPerMinute": 0.25,
+ "impact": 0.7098250353192541,
+ "p95": 19968,
"sample": {
"@timestamp": "2020-06-29T06:48:25.478Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:27.005671Z" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:27.005671Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
- "body": { "original": "[REDACTED]" },
+ "body": {
+ "original": "[REDACTED]"
+ },
"headers": {
- "Accept": ["application/json"],
- "Connection": ["close"],
- "Content-Length": ["129"],
- "Content-Type": ["application/json"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Accept": [
+ "application/json"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "129"
+ ],
+ "Content-Type": [
+ "application/json"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "post",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Allow": ["GET"],
- "Connection": ["close"],
- "Content-Type": ["application/json;charset=UTF-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:25 GMT"],
- "Transfer-Encoding": ["chunked"],
- "X-Powered-By": ["Express"]
+ "Allow": [
+ "GET"
+ ],
+ "Connection": [
+ "close"
+ ],
+ "Content-Type": [
+ "application/json;charset=UTF-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:25 GMT"
+ ],
+ "Transfer-Encoding": [
+ "chunked"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 405
},
@@ -1470,27 +2394,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413305478010 },
- "trace": { "id": "4bd9027dd1e355ec742970e2d6333124" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413305478010
+ },
+ "trace": {
+ "id": "4bd9027dd1e355ec742970e2d6333124"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 20011 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 20011
+ },
"id": "94104435cf151478",
"name": "POST /api",
"result": "HTTP 4xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -1501,52 +2450,92 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 19968,
- "averageResponseTime": 20011,
- "transactionsPerMinute": 0.25,
- "impact": 0.7098250353192541
+ }
},
{
- "name": "GET /api/types/:id",
+ "key": "GET /api/types/:id",
+ "averageResponseTime": 8181,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.5354862351657939,
+ "p95": 10080,
"sample": {
- "@timestamp": "2020-06-29T06:48:12.972Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:18.543436Z" },
+ "@timestamp": "2020-06-29T06:47:53.928Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:47:55.472718Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["205"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:12 GMT"],
- "Etag": ["W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "205"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:47:53 GMT"
+ ],
+ "Etag": [
+ "W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1572,31 +2561,56 @@
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
"ecosystem-workload.config.js"
],
- "pid": 137,
+ "pid": 63,
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413292972011 },
- "trace": { "id": "aea65cef5f902dda5d8e38f9fb38864d" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413273928016
+ },
+ "trace": {
+ "id": "0becaafb422bfeb69e047bf7153aa469"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 6300 },
- "id": "a5bdbe43ac05fae2",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 10062
+ },
+ "id": "0cee4574091bda3b",
"name": "GET /api/types/:id",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 2 },
+ "span_count": {
+ "started": 2
+ },
"type": "request"
},
"url": {
@@ -1607,50 +2621,86 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 10080,
- "averageResponseTime": 8181,
- "transactionsPerMinute": 0.5,
- "impact": 0.5354862351657939
+ }
},
{
- "name": "GET /api/orders/:id",
+ "key": "GET /api/orders/:id",
+ "averageResponseTime": 4749.666666666667,
+ "transactionsPerMinute": 0.75,
+ "impact": 0.43453312891085794,
+ "p95": 7184,
"sample": {
"@timestamp": "2020-06-29T06:48:35.951Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:39.484133Z" },
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:39.484133Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["0"],
- "Date": ["Mon, 29 Jun 2020 06:48:35 GMT"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "0"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:35 GMT"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 404
},
@@ -1680,27 +2730,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413315951017 },
- "trace": { "id": "95979caa80e6622cbbb2d308800c3016" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413315951017
+ },
+ "trace": {
+ "id": "95979caa80e6622cbbb2d308800c3016"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 3210 },
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 3210
+ },
"id": "30344988dace0b43",
"name": "GET /api/orders/:id",
"result": "HTTP 4xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
@@ -1711,52 +2786,92 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 7184,
- "averageResponseTime": 4749.666666666667,
- "transactionsPerMinute": 0.75,
- "impact": 0.43453312891085794
+ }
},
{
- "name": "GET /api/products/:id/customers",
+ "key": "GET /api/products/:id/customers",
+ "averageResponseTime": 4757,
+ "transactionsPerMinute": 0.5,
+ "impact": 0.20830834986820673,
+ "p95": 5616,
"sample": {
- "@timestamp": "2020-06-29T06:48:41.956Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.994692Z" },
+ "@timestamp": "2020-06-29T06:48:22.977Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:27.000765Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["2"],
- "Content-Type": ["application/json; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:41 GMT"],
- "Etag": ["W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "2"
+ ],
+ "Content-Type": [
+ "application/json; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:22 GMT"
+ ],
+ "Etag": [
+ "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\""
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 200
},
@@ -1786,84 +2901,151 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413321956016 },
- "trace": { "id": "f735ac5fca8f83eebc748f4a2e009e61" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413302977008
+ },
+ "trace": {
+ "id": "da8f22fe652ccb6680b3029ab6efd284"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 3896 },
- "id": "b24ce95c855f83a4",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 5618
+ },
+ "id": "bc51c1523afaf57a",
"name": "GET /api/products/:id/customers",
"result": "HTTP 2xx",
"sampled": true,
- "span_count": { "started": 1 },
+ "span_count": {
+ "started": 1
+ },
"type": "request"
},
"url": {
"domain": "opbeans-node",
- "full": "http://opbeans-node:3000/api/products/5/customers",
- "original": "/api/products/5/customers",
- "path": "/api/products/5/customers",
+ "full": "http://opbeans-node:3000/api/products/3/customers",
+ "original": "/api/products/3/customers",
+ "path": "/api/products/3/customers",
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 5616,
- "averageResponseTime": 4757,
- "transactionsPerMinute": 0.5,
- "impact": 0.20830834986820673
+ }
},
{
- "name": "GET /throw-error",
+ "key": "GET /throw-error",
+ "averageResponseTime": 2577,
+ "transactionsPerMinute": 0.5,
+ "impact": 0,
+ "p95": 3224,
"sample": {
- "@timestamp": "2020-06-29T06:48:42.954Z",
- "agent": { "name": "nodejs", "version": "3.6.1" },
- "client": { "ip": "172.18.0.7" },
- "container": { "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "ecs": { "version": "1.5.0" },
- "event": { "ingested": "2020-06-29T06:48:43.996435Z" },
+ "@timestamp": "2020-06-29T06:48:19.975Z",
+ "agent": {
+ "name": "nodejs",
+ "version": "3.6.1"
+ },
+ "client": {
+ "ip": "172.18.0.7"
+ },
+ "container": {
+ "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "event": {
+ "ingested": "2020-06-29T06:48:21.012520Z"
+ },
"host": {
"architecture": "x64",
"hostname": "41712ded148f",
"ip": "172.18.0.7",
"name": "41712ded148f",
- "os": { "platform": "linux" }
+ "os": {
+ "platform": "linux"
+ }
},
"http": {
"request": {
"headers": {
- "Connection": ["close"],
- "Host": ["opbeans-node:3000"],
- "User-Agent": ["workload/2.4.3"]
+ "Connection": [
+ "close"
+ ],
+ "Host": [
+ "opbeans-node:3000"
+ ],
+ "User-Agent": [
+ "workload/2.4.3"
+ ]
},
"method": "get",
- "socket": { "encrypted": false, "remote_address": "::ffff:172.18.0.7" }
+ "socket": {
+ "encrypted": false,
+ "remote_address": "::ffff:172.18.0.7"
+ }
},
"response": {
"headers": {
- "Connection": ["close"],
- "Content-Length": ["148"],
- "Content-Security-Policy": ["default-src 'none'"],
- "Content-Type": ["text/html; charset=utf-8"],
- "Date": ["Mon, 29 Jun 2020 06:48:42 GMT"],
- "X-Content-Type-Options": ["nosniff"],
- "X-Powered-By": ["Express"]
+ "Connection": [
+ "close"
+ ],
+ "Content-Length": [
+ "148"
+ ],
+ "Content-Security-Policy": [
+ "default-src 'none'"
+ ],
+ "Content-Type": [
+ "text/html; charset=utf-8"
+ ],
+ "Date": [
+ "Mon, 29 Jun 2020 06:48:19 GMT"
+ ],
+ "X-Content-Type-Options": [
+ "nosniff"
+ ],
+ "X-Powered-By": [
+ "Express"
+ ]
},
"status_code": 500
},
@@ -1893,27 +3075,52 @@
"ppid": 1,
"title": "node /app/server.js"
},
- "processor": { "event": "transaction", "name": "transaction" },
+ "processor": {
+ "event": "transaction",
+ "name": "transaction"
+ },
"service": {
"environment": "production",
- "framework": { "name": "express", "version": "4.17.1" },
- "language": { "name": "javascript" },
+ "framework": {
+ "name": "express",
+ "version": "4.17.1"
+ },
+ "language": {
+ "name": "javascript"
+ },
"name": "opbeans-node",
- "node": { "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" },
- "runtime": { "name": "node", "version": "12.18.1" },
+ "node": {
+ "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "12.18.1"
+ },
"version": "1.0.0"
},
- "source": { "ip": "172.18.0.7" },
- "timestamp": { "us": 1593413322954016 },
- "trace": { "id": "9d5aee7443a43db9820f622a10dfac6e" },
+ "source": {
+ "ip": "172.18.0.7"
+ },
+ "timestamp": {
+ "us": 1593413299975019
+ },
+ "trace": {
+ "id": "106f3a55b0b0ea327d1bbe4be66c3bcc"
+ },
"transaction": {
- "custom": { "shoppingBasketCount": 42 },
- "duration": { "us": 1928 },
- "id": "8e6fc8c3f99e8fc9",
+ "custom": {
+ "shoppingBasketCount": 42
+ },
+ "duration": {
+ "us": 3226
+ },
+ "id": "247b9141552a9e73",
"name": "GET /throw-error",
"result": "HTTP 5xx",
"sampled": true,
- "span_count": { "started": 0 },
+ "span_count": {
+ "started": 0
+ },
"type": "request"
},
"url": {
@@ -1924,19 +3131,21 @@
"port": 3000,
"scheme": "http"
},
- "user": { "email": "kimchy@elastic.co", "id": "42", "name": "kimchy" },
+ "user": {
+ "email": "kimchy@elastic.co",
+ "id": "42",
+ "name": "kimchy"
+ },
"user_agent": {
- "device": { "name": "Other" },
+ "device": {
+ "name": "Other"
+ },
"name": "Other",
"original": "workload/2.4.3"
}
- },
- "p95": 3224,
- "averageResponseTime": 2577,
- "transactionsPerMinute": 0.5,
- "impact": 0
+ }
}
],
"isAggregationAccurate": true,
- "bucketSize": 100
-}
+ "bucketSize": 1000
+}
\ No newline at end of file
diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts
index 43b2ad5414c7a6..94559a3e4aa541 100644
--- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts
+++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts
@@ -4,9 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
+import { sortBy } from 'lodash';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
import expectedTransactionGroups from './expectation/top_transaction_groups.json';
+function sortTransactionGroups(items: any[]) {
+ return sortBy(items, 'impact');
+}
+
+function omitSampleFromTransactionGroups(items: any[]) {
+ return sortTransactionGroups(items).map(({ sample, ...item }) => ({ ...item }));
+}
+
export default function ApiTest({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
@@ -48,15 +57,19 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
it('returns the correct buckets (when ignoring samples)', async () => {
- function omitSample(items: any[]) {
- return items.map(({ sample, ...item }) => ({ ...item }));
- }
-
- expect(omitSample(response.body.items)).to.eql(omitSample(expectedTransactionGroups.items));
+ expect(omitSampleFromTransactionGroups(response.body.items)).to.eql(
+ omitSampleFromTransactionGroups(expectedTransactionGroups.items)
+ );
});
it('returns the correct buckets and samples', async () => {
- expect(response.body.items).to.eql(expectedTransactionGroups.items);
+ // sample should provide enough information to deeplink to a transaction detail page
+ response.body.items.forEach((item: any) => {
+ expect(item.sample.trace.id).to.be.an('string');
+ expect(item.sample.transaction.id).to.be.an('string');
+ expect(item.sample.service.name).to.be('opbeans-node');
+ expect(item.sample.transaction.name).to.be(item.key);
+ });
});
});
});
From fb4ee91f0ca40d2158d0756e1aac18b01ba69761 Mon Sep 17 00:00:00 2001
From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com>
Date: Tue, 28 Jul 2020 09:55:57 -0400
Subject: [PATCH 032/102] [Security Solution][Resolver] Fix resolver isStart
event bug (#73357)
* Check if category is array
* Adding more tests and renaming to isStart
* Handling the case where start is not at the front
---
.../common/endpoint/generate_data.test.ts | 21 ++--
.../common/endpoint/generate_data.ts | 25 +++--
.../common/endpoint/models/event.test.ts | 96 ++++++++++++++-----
.../common/endpoint/models/event.ts | 7 +-
.../resolver/utils/children_helper.test.ts | 4 +-
.../routes/resolver/utils/children_helper.ts | 4 +-
6 files changed, 111 insertions(+), 46 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
index fcea86be4ae9e1..debe4a3da6a6fc 100644
--- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
@@ -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.
*/
+import _ from 'lodash';
import {
EndpointDocGenerator,
Event,
@@ -79,9 +80,9 @@ describe('data generator', () => {
const timestamp = new Date().getTime();
const processEvent = generator.generateEvent({ timestamp });
expect(processEvent['@timestamp']).toEqual(timestamp);
- expect(processEvent.event.category).toEqual('process');
+ expect(processEvent.event.category).toEqual(['process']);
expect(processEvent.event.kind).toEqual('event');
- expect(processEvent.event.type).toEqual('start');
+ expect(processEvent.event.type).toEqual(['start']);
expect(processEvent.agent).not.toBeNull();
expect(processEvent.host).not.toBeNull();
expect(processEvent.process.entity_id).not.toBeNull();
@@ -94,7 +95,7 @@ describe('data generator', () => {
expect(processEvent['@timestamp']).toEqual(timestamp);
expect(processEvent.event.category).toEqual('dns');
expect(processEvent.event.kind).toEqual('event');
- expect(processEvent.event.type).toEqual('start');
+ expect(processEvent.event.type).toEqual(['start']);
expect(processEvent.agent).not.toBeNull();
expect(processEvent.host).not.toBeNull();
expect(processEvent.process.entity_id).not.toBeNull();
@@ -332,6 +333,12 @@ describe('data generator', () => {
describe('creates alert ancestor tree', () => {
let events: Event[];
+ const isCategoryProcess = (event: Event) => {
+ return (
+ _.isEqual(event.event.category, ['process']) || _.isEqual(event.event.category, 'process')
+ );
+ };
+
beforeEach(() => {
events = generator.createAlertEventAncestry({
ancestors: 3,
@@ -343,11 +350,7 @@ describe('data generator', () => {
it('with n-1 process events', () => {
for (let i = events.length - 2; i > 0; ) {
const parentEntityIdOfChild = events[i].process.parent?.entity_id;
- for (
- ;
- --i >= -1 && (events[i].event.kind !== 'event' || events[i].event.category !== 'process');
-
- ) {
+ for (; --i >= -1 && (events[i].event.kind !== 'event' || !isCategoryProcess(events[i])); ) {
// related event - skip it
}
expect(i).toBeGreaterThanOrEqual(0);
@@ -361,7 +364,7 @@ describe('data generator', () => {
;
previousProcessEventIndex >= -1 &&
(events[previousProcessEventIndex].event.kind !== 'event' ||
- events[previousProcessEventIndex].event.category !== 'process');
+ !isCategoryProcess(events[previousProcessEventIndex]));
previousProcessEventIndex--
) {
// related event - skip it
diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
index 66e786cb02e637..97ac5c9030a3db 100644
--- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
@@ -35,7 +35,7 @@ interface EventOptions {
timestamp?: number;
entityID?: string;
parentEntityID?: string;
- eventType?: string;
+ eventType?: string | string[];
eventCategory?: string | string[];
processName?: string;
ancestry?: string[];
@@ -572,9 +572,9 @@ export class EndpointDocGenerator {
},
...detailRecordForEventType,
event: {
- category: options.eventCategory ? options.eventCategory : 'process',
+ category: options.eventCategory ? options.eventCategory : ['process'],
kind: 'event',
- type: options.eventType ? options.eventType : 'start',
+ type: options.eventType ? options.eventType : ['start'],
id: this.seededUUIDv4(),
},
host: this.commonInfo.host,
@@ -633,7 +633,12 @@ export class EndpointDocGenerator {
// place the event in the right array depending on its category
if (event.event.kind === 'event') {
- if (event.event.category === 'process') {
+ if (
+ (Array.isArray(event.event.category) &&
+ event.event.category.length === 1 &&
+ event.event.category[0] === 'process') ||
+ event.event.category === 'process'
+ ) {
node.lifecycle.push(event);
} else {
node.relatedEvents.push(event);
@@ -812,8 +817,8 @@ export class EndpointDocGenerator {
timestamp: timestamp + termProcessDuration * 1000,
entityID: root.process.entity_id,
parentEntityID: root.process.parent?.entity_id,
- eventCategory: 'process',
- eventType: 'end',
+ eventCategory: ['process'],
+ eventType: ['end'],
})
);
}
@@ -838,8 +843,8 @@ export class EndpointDocGenerator {
timestamp: timestamp + termProcessDuration * 1000,
entityID: ancestor.process.entity_id,
parentEntityID: ancestor.process.parent?.entity_id,
- eventCategory: 'process',
- eventType: 'end',
+ eventCategory: ['process'],
+ eventType: ['end'],
ancestry: ancestor.process.Ext?.ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
})
@@ -936,8 +941,8 @@ export class EndpointDocGenerator {
timestamp: timestamp + processDuration * 1000,
entityID: child.process.entity_id,
parentEntityID: child.process.parent?.entity_id,
- eventCategory: 'process',
- eventType: 'end',
+ eventCategory: ['process'],
+ eventType: ['end'],
ancestry: child.process.Ext?.ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
});
diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts
index a0bf00f0274e6c..62f923aa6d793f 100644
--- a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts
@@ -4,38 +4,90 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EndpointDocGenerator } from '../generate_data';
-import { descriptiveName } from './event';
+import { descriptiveName, isStart } from './event';
+import { ResolverEvent } from '../types';
-describe('Event descriptive names', () => {
+describe('Generated documents', () => {
let generator: EndpointDocGenerator;
beforeEach(() => {
generator = new EndpointDocGenerator('seed');
});
- it('returns the right name for a registry event', () => {
- const extensions = { registry: { key: `HKLM/Windows/Software/abc` } };
- const event = generator.generateEvent({ eventCategory: 'registry', extensions });
- expect(descriptiveName(event)).toEqual({ subject: `HKLM/Windows/Software/abc` });
- });
+ describe('Event descriptive names', () => {
+ it('returns the right name for a registry event', () => {
+ const extensions = { registry: { key: `HKLM/Windows/Software/abc` } };
+ const event = generator.generateEvent({ eventCategory: 'registry', extensions });
+ expect(descriptiveName(event)).toEqual({ subject: `HKLM/Windows/Software/abc` });
+ });
- it('returns the right name for a network event', () => {
- const randomIP = `${generator.randomIP()}`;
- const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } };
- const event = generator.generateEvent({ eventCategory: 'network', extensions });
- expect(descriptiveName(event)).toEqual({ subject: `${randomIP}`, descriptor: 'outbound' });
- });
+ it('returns the right name for a network event', () => {
+ const randomIP = `${generator.randomIP()}`;
+ const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } };
+ const event = generator.generateEvent({ eventCategory: 'network', extensions });
+ expect(descriptiveName(event)).toEqual({ subject: `${randomIP}`, descriptor: 'outbound' });
+ });
- it('returns the right name for a file event', () => {
- const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } };
- const event = generator.generateEvent({ eventCategory: 'file', extensions });
- expect(descriptiveName(event)).toEqual({
- subject: 'C:\\My Documents\\business\\January\\processName',
+ it('returns the right name for a file event', () => {
+ const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } };
+ const event = generator.generateEvent({ eventCategory: 'file', extensions });
+ expect(descriptiveName(event)).toEqual({
+ subject: 'C:\\My Documents\\business\\January\\processName',
+ });
+ });
+
+ it('returns the right name for a dns event', () => {
+ const extensions = { dns: { question: { name: `${generator.randomIP()}` } } };
+ const event = generator.generateEvent({ eventCategory: 'dns', extensions });
+ expect(descriptiveName(event)).toEqual({ subject: extensions.dns.question.name });
});
});
- it('returns the right name for a dns event', () => {
- const extensions = { dns: { question: { name: `${generator.randomIP()}` } } };
- const event = generator.generateEvent({ eventCategory: 'dns', extensions });
- expect(descriptiveName(event)).toEqual({ subject: extensions.dns.question.name });
+ describe('Start events', () => {
+ it('is a start event when event.type is a string', () => {
+ const event: ResolverEvent = generator.generateEvent({
+ eventType: 'start',
+ });
+ expect(isStart(event)).toBeTruthy();
+ });
+
+ it('is a start event when event.type is an array of strings', () => {
+ const event: ResolverEvent = generator.generateEvent({
+ eventType: ['start'],
+ });
+ expect(isStart(event)).toBeTruthy();
+ });
+
+ it('is a start event when event.type is an array of strings and contains start', () => {
+ let event: ResolverEvent = generator.generateEvent({
+ eventType: ['bogus', 'start', 'creation'],
+ });
+ expect(isStart(event)).toBeTruthy();
+
+ event = generator.generateEvent({
+ eventType: ['start', 'bogus'],
+ });
+ expect(isStart(event)).toBeTruthy();
+ });
+
+ it('is not a start event when event.type is not start', () => {
+ const event: ResolverEvent = generator.generateEvent({
+ eventType: ['end'],
+ });
+ expect(isStart(event)).toBeFalsy();
+ });
+
+ it('is not a start event when event.type is empty', () => {
+ const event: ResolverEvent = generator.generateEvent({
+ eventType: [],
+ });
+ expect(isStart(event)).toBeFalsy();
+ });
+
+ it('is not a start event when event.type is bogus', () => {
+ const event: ResolverEvent = generator.generateEvent({
+ eventType: ['bogus'],
+ });
+ expect(isStart(event)).toBeFalsy();
+ });
});
});
diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts
index f53da8fb1f0969..216b5cc0285889 100644
--- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts
@@ -9,10 +9,15 @@ export function isLegacyEvent(event: ResolverEvent): event is LegacyEndpointEven
return (event as LegacyEndpointEvent).endgame !== undefined;
}
-export function isProcessStart(event: ResolverEvent): boolean {
+export function isStart(event: ResolverEvent): boolean {
if (isLegacyEvent(event)) {
return event.event?.type === 'process_start' || event.event?.action === 'fork_event';
}
+
+ if (Array.isArray(event.event.type)) {
+ return event.event.type.includes('start');
+ }
+
return event.event.type === 'start';
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts
index ca5b5aef0f6518..01dd59b2611d96 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts
@@ -10,12 +10,12 @@ import {
TreeNode,
} from '../../../../../common/endpoint/generate_data';
import { ChildrenNodesHelper } from './children_helper';
-import { eventId, isProcessStart } from '../../../../../common/endpoint/models/event';
+import { eventId, isStart } from '../../../../../common/endpoint/models/event';
function getStartEvents(events: Event[]): Event[] {
const startEvents: Event[] = [];
for (const event of events) {
- if (isProcessStart(event)) {
+ if (isStart(event)) {
startEvents.push(event);
}
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts
index 01e356682ac478..d3ca7a54c16d2d 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts
@@ -7,7 +7,7 @@
import {
entityId,
parentEntityId,
- isProcessStart,
+ isStart,
getAncestryAsArray,
} from '../../../../../common/endpoint/models/event';
import {
@@ -99,7 +99,7 @@ export class ChildrenNodesHelper {
for (const event of startEvents) {
const parentID = parentEntityId(event);
const entityID = entityId(event);
- if (parentID && entityID && isProcessStart(event)) {
+ if (parentID && entityID && isStart(event)) {
// don't actually add the start event to the node, because that'll be done in
// a different call
const childNode = this.getOrCreateChildNode(entityID);
From 5e8e01fd0f3ea5516a2f68bef9e7a9877b9e549a Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris
Date: Tue, 28 Jul 2020 15:00:41 +0100
Subject: [PATCH 033/102] removed ESO migration from alerting (#73420)
This PR removes the use of ESO migration from alerting as we do not actually need this until the RBAC work lands, which should be 7.10.
This allows us to concentrate the challenges of introducing RBAC into one single release which hopefully will help us better mitigate potential regressions.
---
.../alerts/server/saved_objects/index.ts | 2 -
.../server/saved_objects/migrations.test.ts | 121 ------------------
.../alerts/server/saved_objects/migrations.ts | 53 --------
.../spaces_only/tests/alerting/index.ts | 3 -
.../spaces_only/tests/alerting/migrations.ts | 43 -------
5 files changed, 222 deletions(-)
delete mode 100644 x-pack/plugins/alerts/server/saved_objects/migrations.test.ts
delete mode 100644 x-pack/plugins/alerts/server/saved_objects/migrations.ts
delete mode 100644 x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts
diff --git a/x-pack/plugins/alerts/server/saved_objects/index.ts b/x-pack/plugins/alerts/server/saved_objects/index.ts
index 06ce8d673e6b71..c98d9bcbd9ae55 100644
--- a/x-pack/plugins/alerts/server/saved_objects/index.ts
+++ b/x-pack/plugins/alerts/server/saved_objects/index.ts
@@ -6,7 +6,6 @@
import { SavedObjectsServiceSetup } from 'kibana/server';
import mappings from './mappings.json';
-import { getMigrations } from './migrations';
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
export function setupSavedObjects(
@@ -17,7 +16,6 @@ export function setupSavedObjects(
name: 'alert',
hidden: true,
namespaceType: 'single',
- migrations: getMigrations(encryptedSavedObjects),
mappings: mappings.alert,
});
diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts
deleted file mode 100644
index 19f4e918b78624..00000000000000
--- a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts
+++ /dev/null
@@ -1,121 +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 uuid from 'uuid';
-import { getMigrations } from './migrations';
-import { RawAlert } from '../types';
-import { SavedObjectUnsanitizedDoc } from 'kibana/server';
-import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
-import { migrationMocks } from 'src/core/server/mocks';
-
-const { log } = migrationMocks.createContext();
-const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
-
-describe('7.9.0', () => {
- beforeEach(() => {
- jest.resetAllMocks();
- encryptedSavedObjectsSetup.createMigration.mockImplementation(
- (shouldMigrateWhenPredicate, migration) => migration
- );
- });
-
- test('changes nothing on alerts by other plugins', () => {
- const migration790 = getMigrations(encryptedSavedObjectsSetup)['7.9.0'];
- const alert = getMockData({});
- expect(migration790(alert, { log })).toMatchObject(alert);
-
- expect(encryptedSavedObjectsSetup.createMigration).toHaveBeenCalledWith(
- expect.any(Function),
- expect.any(Function)
- );
- });
-
- test('migrates the consumer for alerting', () => {
- const migration790 = getMigrations(encryptedSavedObjectsSetup)['7.9.0'];
- const alert = getMockData({
- consumer: 'alerting',
- });
- expect(migration790(alert, { log })).toMatchObject({
- ...alert,
- attributes: {
- ...alert.attributes,
- consumer: 'alerts',
- },
- });
- });
-});
-
-describe('7.10.0', () => {
- beforeEach(() => {
- jest.resetAllMocks();
- encryptedSavedObjectsSetup.createMigration.mockImplementation(
- (shouldMigrateWhenPredicate, migration) => migration
- );
- });
-
- test('changes nothing on alerts by other plugins', () => {
- const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
- const alert = getMockData({});
- expect(migration710(alert, { log })).toMatchObject(alert);
-
- expect(encryptedSavedObjectsSetup.createMigration).toHaveBeenCalledWith(
- expect.any(Function),
- expect.any(Function)
- );
- });
-
- test('migrates the consumer for metrics', () => {
- const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
- const alert = getMockData({
- consumer: 'metrics',
- });
- expect(migration710(alert, { log })).toMatchObject({
- ...alert,
- attributes: {
- ...alert.attributes,
- consumer: 'infrastructure',
- },
- });
- });
-});
-
-function getMockData(
- overwrites: Record = {}
-): SavedObjectUnsanitizedDoc {
- return {
- attributes: {
- enabled: true,
- name: 'abc',
- tags: ['foo'],
- alertTypeId: '123',
- consumer: 'bar',
- apiKey: '',
- apiKeyOwner: '',
- schedule: { interval: '10s' },
- throttle: null,
- params: {
- bar: true,
- },
- muteAll: false,
- mutedInstanceIds: [],
- createdBy: new Date().toISOString(),
- updatedBy: new Date().toISOString(),
- createdAt: new Date().toISOString(),
- actions: [
- {
- group: 'default',
- actionRef: '1',
- actionTypeId: '1',
- params: {
- foo: true,
- },
- },
- ],
- ...overwrites,
- },
- id: uuid.v4(),
- type: 'alert',
- };
-}
diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts
deleted file mode 100644
index 57a40058870931..00000000000000
--- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts
+++ /dev/null
@@ -1,53 +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 {
- SavedObjectMigrationMap,
- SavedObjectUnsanitizedDoc,
- SavedObjectMigrationFn,
-} from '../../../../../src/core/server';
-import { RawAlert } from '../types';
-import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
-
-export function getMigrations(
- encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
-): SavedObjectMigrationMap {
- return {
- /**
- * In v7.9.0 we changed the Alerting plugin so it uses the `consumer` value of `alerts`
- * prior to that we were using `alerting` and we need to keep these in sync
- */
- '7.9.0': changeAlertingConsumer(encryptedSavedObjects, 'alerting', 'alerts'),
- /**
- * In v7.10.0 we changed the Matrics plugin so it uses the `consumer` value of `infrastructure`
- * prior to that we were using `metrics` and we need to keep these in sync
- */
- '7.10.0': changeAlertingConsumer(encryptedSavedObjects, 'metrics', 'infrastructure'),
- };
-}
-
-function changeAlertingConsumer(
- encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
- from: string,
- to: string
-): SavedObjectMigrationFn {
- return encryptedSavedObjects.createMigration(
- function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc {
- return doc.attributes.consumer === from;
- },
- (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => {
- const {
- attributes: { consumer },
- } = doc;
- return {
- ...doc,
- attributes: {
- ...doc.attributes,
- consumer: consumer === from ? to : consumer,
- },
- };
- }
- );
-}
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts
index 0970738b630c49..a23f0fa8353133 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts
@@ -27,8 +27,5 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./alerts_space1'));
loadTestFile(require.resolve('./alerts_default_space'));
loadTestFile(require.resolve('./builtin_alert_types'));
-
- // note that this test will destroy existing spaces
- loadTestFile(require.resolve('./migrations'));
});
}
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts
deleted file mode 100644
index d0e1be12e762f1..00000000000000
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts
+++ /dev/null
@@ -1,43 +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 expect from '@kbn/expect';
-import { getUrlPrefix } from '../../../common/lib';
-import { FtrProviderContext } from '../../../common/ftr_provider_context';
-
-// eslint-disable-next-line import/no-default-export
-export default function createGetTests({ getService }: FtrProviderContext) {
- const supertest = getService('supertest');
- const esArchiver = getService('esArchiver');
-
- describe('migrations', () => {
- before(async () => {
- await esArchiver.load('alerts');
- });
-
- after(async () => {
- await esArchiver.unload('alerts');
- });
-
- it('7.9.0 migrates the `alerting` consumer to be the `alerts`', async () => {
- const response = await supertest.get(
- `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e`
- );
-
- expect(response.status).to.eql(200);
- expect(response.body.consumer).to.equal('alerts');
- });
-
- it('7.10.0 migrates the `metrics` consumer to be the `infrastructure`', async () => {
- const response = await supertest.get(
- `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-fdf248d5f2a4`
- );
-
- expect(response.status).to.eql(200);
- expect(response.body.consumer).to.equal('infrastructure');
- });
- });
-}
From dca4a2359724e9121df0b919f57ef2fb14bada4c Mon Sep 17 00:00:00 2001
From: Andrew Goldstein
Date: Tue, 28 Jul 2020 08:09:35 -0600
Subject: [PATCH 034/102] [Security Solution] Full screen fixes for Timeline
based views (#73421)
## Full screen fixes for Timeline based views
- Fixes an issue where sometimes, Global navigation is hidden until the page is scrolled when exiting full screen mode
- Improves performance by adding an intent delay before showing the draggable wrapper hover menu
- Removes an unnecessary CSS transition
### Sometimes, Global navigation is hidden until the page is scrolled when exiting full screen mode
Sometimes, after exiting `Full screen` mode in a page, for example, the `Detections` page, the global navigation, e.g. `Overview Detections Hosts...` is hidden until the page is scrolled.
To reproduce:
1) Navigate to the `Detections` page
2) Click the `Full screen` button in the table
3) Without scrolling the full screen view, click the `Exit full screen` button
**Expected result**
- [x] The global navigation e.g. `Overview Detections Hosts...` is visible above the search bar, per the screenshot below:
![correct-global-navigation](https://user-images.githubusercontent.com/4459398/87717870-571bef80-c76e-11ea-8b7b-1850094326b3.png)
4) Once again, click the `Full screen` button in the table
5) This time, expand an event, which will scroll the view
6) Once again, click the `Exit full screen` button
**Expected result**
- [x] The global navigation e.g. `Overview Detections Hosts...` is visible above the search bar
**Actual result**
- [ ] Sometimes, the global navigation e.g. `Overview Detections Hosts...` is **not** visible until the page is scrolled
---
.../security_solution/common/constants.ts | 1 +
.../index.test.tsx | 3 +
.../drag_and_drop/draggable_wrapper.test.tsx | 6 +
.../drag_and_drop/provider_container.tsx | 7 --
.../filters_global/filters_global.test.tsx | 104 +++++++++++++++++-
.../filters_global/filters_global.tsx | 29 +++--
.../public/common/components/page/index.tsx | 13 ++-
.../components/with_hover_actions/index.tsx | 30 +++--
.../containers/use_full_screen/index.tsx | 12 +-
.../detection_engine/detection_engine.tsx | 5 +-
.../detection_engine/rules/details/index.tsx | 5 +-
.../public/hosts/pages/details/index.tsx | 5 +-
.../public/hosts/pages/hosts.tsx | 5 +-
.../public/network/pages/ip_details/index.tsx | 2 +-
.../public/network/pages/network.tsx | 5 +-
.../public/overview/pages/overview.tsx | 2 +-
.../fields_browser/field_name.test.tsx | 6 +
17 files changed, 206 insertions(+), 34 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index f934d90c740a53..c74cf888a2db66 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -32,6 +32,7 @@ export const DEFAULT_INTERVAL_PAUSE = true;
export const DEFAULT_INTERVAL_TYPE = 'manual';
export const DEFAULT_INTERVAL_VALUE = 300000; // ms
export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges';
+export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled';
export const FILTERS_GLOBAL_HEIGHT = 109; // px
export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled';
export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51';
diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx
index 2af6569394e8f4..eced73e9c3d679 100644
--- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx
@@ -45,6 +45,7 @@ describe('AddFilterToGlobalSearchBar Component', () => {
);
beforeEach(() => {
+ jest.useFakeTimers();
store = createStore(
state,
SUB_PLUGINS_REDUCER,
@@ -159,6 +160,8 @@ describe('AddFilterToGlobalSearchBar Component', () => {
wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter');
wrapper.update();
+ jest.runAllTimers();
+ wrapper.update();
wrapper
.find('[data-test-subj="hover-actions-container"] [data-euiicon-type]')
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx
index e17fc7b9ef9bde..ebfa9ac22bdc71 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx
@@ -22,6 +22,10 @@ describe('DraggableWrapper', () => {
const message = 'draggable wrapper content';
const mount = useMountAppended();
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
describe('rendering', () => {
test('it renders against the snapshot', () => {
const wrapper = shallow(
@@ -78,6 +82,8 @@ describe('DraggableWrapper', () => {
wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter');
wrapper.update();
+ jest.runAllTimers();
+ wrapper.update();
expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true);
});
});
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/provider_container.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/provider_container.tsx
index 06cb8ee2e1a467..8db6d073f96874 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/provider_container.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/provider_container.tsx
@@ -13,13 +13,6 @@ interface ProviderContainerProps {
}
const ProviderContainerComponent = styled.div`
- &,
- &::before,
- &::after {
- transition: background ${({ theme }) => theme.eui.euiAnimSpeedFast} ease,
- color ${({ theme }) => theme.eui.euiAnimSpeedFast} ease;
- }
-
${({ isDragging }) =>
!isDragging &&
css`
diff --git a/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.test.tsx b/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.test.tsx
index ffac0496e9f1a4..9fda60b1f289d6 100644
--- a/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.test.tsx
@@ -4,20 +4,120 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { shallow } from 'enzyme';
+import { mount, ReactWrapper, shallow } from 'enzyme';
import React from 'react';
+import { StickyContainer } from 'react-sticky';
import '../../mock/match_media';
import { FiltersGlobal } from './filters_global';
+import { TestProviders } from '../../mock/test_providers';
describe('rendering', () => {
test('renders correctly', () => {
const wrapper = shallow(
-
+
{'Additional filters here.'}
);
expect(wrapper).toMatchSnapshot();
});
+
+ describe('full screen mode', () => {
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+ {'Filter content'}
+
+
+
+ );
+ });
+
+ test('it does NOT render the sticky container', () => {
+ expect(wrapper.find('[data-test-subj="sticky-filters-global-container"]').exists()).toBe(
+ false
+ );
+ });
+
+ test('it renders the non-sticky container', () => {
+ expect(wrapper.find('[data-test-subj="non-sticky-global-container"]').exists()).toBe(true);
+ });
+
+ test('it does NOT render the container with a `display: none` style when `show` is true (the default)', () => {
+ expect(
+ wrapper.find('[data-test-subj="non-sticky-global-container"]').first()
+ ).not.toHaveStyleRule('display', 'none');
+ });
+ });
+
+ describe('non-full screen mode', () => {
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+ {'Filter content'}
+
+
+
+ );
+ });
+
+ test('it renders the sticky container', () => {
+ expect(wrapper.find('[data-test-subj="sticky-filters-global-container"]').exists()).toBe(
+ true
+ );
+ });
+
+ test('it does NOT render the non-sticky container', () => {
+ expect(wrapper.find('[data-test-subj="non-sticky-global-container"]').exists()).toBe(false);
+ });
+
+ test('it does NOT render the container with a `display: none` style when `show` is true (the default)', () => {
+ expect(
+ wrapper.find('[data-test-subj="sticky-filters-global-container"]').first()
+ ).not.toHaveStyleRule('display', 'none');
+ });
+ });
+
+ describe('when show is false', () => {
+ test('in full screen mode it renders the container with a `display: none` style', () => {
+ const wrapper = mount(
+
+
+
+ {'Filter content'}
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="non-sticky-global-container"]').first()
+ ).toHaveStyleRule('display', 'none');
+ });
+
+ test('in non-full screen mode it renders the container with a `display: none` style', () => {
+ const wrapper = mount(
+
+
+
+ {'Filter content'}
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="sticky-filters-global-container"]').first()
+ ).toHaveStyleRule('display', 'none');
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.tsx b/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.tsx
index b52438486406ec..80e7209492fa5c 100644
--- a/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/filters_global/filters_global.tsx
@@ -47,20 +47,33 @@ const FiltersGlobalContainer = styled.header<{ show: boolean }>`
FiltersGlobalContainer.displayName = 'FiltersGlobalContainer';
+const NO_STYLE: React.CSSProperties = {};
+
export interface FiltersGlobalProps {
children: React.ReactNode;
+ globalFullScreen: boolean;
show?: boolean;
}
-export const FiltersGlobal = React.memo(({ children, show = true }) => (
-
- {({ style, isSticky }) => (
-
-
+export const FiltersGlobal = React.memo(
+ ({ children, globalFullScreen, show = true }) =>
+ globalFullScreen ? (
+
+
{children}
- )}
-
-));
+ ) : (
+
+ {({ style, isSticky }) => (
+
+
+ {children}
+
+
+ )}
+
+ )
+);
+
FiltersGlobal.displayName = 'FiltersGlobal';
diff --git a/x-pack/plugins/security_solution/public/common/components/page/index.tsx b/x-pack/plugins/security_solution/public/common/components/page/index.tsx
index 8737fa95c94a26..8bf0690bfd0adf 100644
--- a/x-pack/plugins/security_solution/public/common/components/page/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/page/index.tsx
@@ -7,7 +7,10 @@
import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiIcon, EuiPage } from '@elastic/eui';
import styled, { createGlobalStyle } from 'styled-components';
-import { FULL_SCREEN_TOGGLED_CLASS_NAME } from '../../../../common/constants';
+import {
+ FULL_SCREEN_TOGGLED_CLASS_NAME,
+ SCROLLING_DISABLED_CLASS_NAME,
+} from '../../../../common/constants';
/*
SIDE EFFECT: the following `createGlobalStyle` overrides default styling in angular code that was not theme-friendly
@@ -63,6 +66,14 @@ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimar
.${FULL_SCREEN_TOGGLED_CLASS_NAME} {
${({ theme }) => `background-color: ${theme.eui.euiColorPrimary} !important`};
}
+
+ .${SCROLLING_DISABLED_CLASS_NAME} body {
+ overflow-y: hidden;
+ }
+
+ .${SCROLLING_DISABLED_CLASS_NAME} #kibana-body {
+ overflow-y: hidden;
+ }
`;
export const DescriptionListStyled = styled(EuiDescriptionList)`
diff --git a/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx b/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx
index 9e28345ffbbcfb..b4abdd4b918050 100644
--- a/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx
@@ -10,6 +10,11 @@ import styled from 'styled-components';
import { IS_DRAGGING_CLASS_NAME } from '../drag_and_drop/helpers';
+/**
+ * To avoid expensive changes to the DOM, delay showing the popover menu
+ */
+const HOVER_INTENT_DELAY = 100; // ms
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const WithHoverActionsPopover = (styled(EuiPopover as any)`
.euiPopover__anchor {
@@ -51,18 +56,27 @@ export const WithHoverActions = React.memo(
({ alwaysShow = false, closePopOverTrigger, hoverContent, render }) => {
const [isOpen, setIsOpen] = useState(hoverContent != null && alwaysShow);
const [showHoverContent, setShowHoverContent] = useState(false);
+ const [hoverTimeout, setHoverTimeout] = useState(undefined);
+
const onMouseEnter = useCallback(() => {
- // NOTE: the following read from the DOM is expensive, but not as
- // expensive as the default behavior, which adds a div to the body,
- // which-in turn performs a more expensive change to the layout
- if (!document.body.classList.contains(IS_DRAGGING_CLASS_NAME)) {
- setShowHoverContent(true);
- }
- }, []);
+ setHoverTimeout(
+ Number(
+ setTimeout(() => {
+ // NOTE: the following read from the DOM is expensive, but not as
+ // expensive as the default behavior, which adds a div to the body,
+ // which-in turn performs a more expensive change to the layout
+ if (!document.body.classList.contains(IS_DRAGGING_CLASS_NAME)) {
+ setShowHoverContent(true);
+ }
+ }, HOVER_INTENT_DELAY)
+ )
+ );
+ }, [setHoverTimeout, setShowHoverContent]);
const onMouseLeave = useCallback(() => {
+ clearTimeout(hoverTimeout);
setShowHoverContent(false);
- }, []);
+ }, [hoverTimeout, setShowHoverContent]);
const content = useMemo(
() => (
diff --git a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx
index b8050034d34a6c..aa0d90a2160350 100644
--- a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx
@@ -6,6 +6,7 @@
import { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import { SCROLLING_DISABLED_CLASS_NAME } from '../../../../common/constants';
import { inputsSelectors } from '../../store';
import { inputsActions } from '../../store/actions';
@@ -16,7 +17,16 @@ export const useFullScreen = () => {
const timelineFullScreen = useSelector(inputsSelectors.timelineFullScreenSelector) ?? false;
const setGlobalFullScreen = useCallback(
- (fullScreen: boolean) => dispatch(inputsActions.setFullScreen({ id: 'global', fullScreen })),
+ (fullScreen: boolean) => {
+ if (fullScreen) {
+ document.body.classList.add(SCROLLING_DISABLED_CLASS_NAME);
+ } else {
+ document.body.classList.remove(SCROLLING_DISABLED_CLASS_NAME);
+ setTimeout(() => window.scrollTo(0, 0), 0);
+ }
+
+ dispatch(inputsActions.setFullScreen({ id: 'global', fullScreen }));
+ },
[dispatch]
);
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx
index 090fdc49809330..8385fcc71682d6 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx
@@ -156,7 +156,10 @@ export const DetectionEnginePageComponent: React.FC = ({
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
index 9c130a7d351fa8..90424e1fb9dd08 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
@@ -366,7 +366,10 @@ export const RuleDetailsPageComponent: FC = ({
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx
index 781aa711ff0d9c..b6c1727ee6afac 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx
@@ -104,7 +104,10 @@ const HostDetailsComponent = React.memo(
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
index 1219effa5ff6d3..1d0b73f80a69a1 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
@@ -98,7 +98,10 @@ export const HostsComponent = React.memo(
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx
index e06f5489a3fc2f..42469a4bf29daf 100644
--- a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx
+++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx
@@ -90,7 +90,7 @@ export const IPDetailsComponent: React.FC
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx
index ca8da4eb711e54..f516f2a2de346d 100644
--- a/x-pack/plugins/security_solution/public/network/pages/network.tsx
+++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx
@@ -106,7 +106,10 @@ const NetworkComponent = React.memo(
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx
index 6563f3c2b824da..423aa597a0129b 100644
--- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx
+++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx
@@ -71,7 +71,7 @@ const OverviewComponent: React.FC = ({
<>
{indicesExist ? (
-
+
diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx
index ddd5c6f07e8b55..2e48215a894735 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx
@@ -28,6 +28,10 @@ const defaultProps = {
};
describe('FieldName', () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
test('it renders the field name', () => {
const wrapper = mount(
@@ -48,6 +52,8 @@ describe('FieldName', () => {
);
wrapper.find('[data-test-subj="withHoverActionsButton"]').at(0).simulate('mouseenter');
wrapper.update();
+ jest.runAllTimers();
+ wrapper.update();
expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true);
});
From f4104743e388c9cec6f91d153bfc4145f541cabb Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris
Date: Tue, 28 Jul 2020 15:20:24 +0100
Subject: [PATCH 035/102] [Alerting] Control Alerts Management via feature
controls & privileges (#72029)
This PR removes the alerting and actions ui privileges (alerting:show, actions:show, etc...) and instead relies on the standard Kibana feature control model to decide whether management displays the Alerts Management section under management.
---
examples/alerting_example/server/plugin.ts | 13 ++++-
x-pack/plugins/actions/server/feature.ts | 9 ++++
.../alerting_builtins/server/feature.ts | 13 ++++-
x-pack/plugins/alerts/server/plugin.ts | 10 ++++
x-pack/plugins/apm/server/feature.ts | 9 ++++
x-pack/plugins/infra/server/features.ts | 13 ++++-
.../security_solution/server/plugin.ts | 13 ++++-
.../public/application/app.tsx | 18 +++----
.../public/application/home.tsx | 40 ++++++--------
.../public/application/lib/capabilities.ts | 13 -----
.../components/alert_details.test.tsx | 3 --
.../sections/alert_form/alert_form.tsx | 22 ++++----
.../components/alerts_list.test.tsx | 28 ++--------
.../alerts_list/components/alerts_list.tsx | 53 ++++++++++---------
.../triggers_actions_ui/public/plugin.ts | 26 +++------
x-pack/plugins/uptime/server/kibana.index.ts | 13 ++++-
16 files changed, 157 insertions(+), 139 deletions(-)
diff --git a/examples/alerting_example/server/plugin.ts b/examples/alerting_example/server/plugin.ts
index 49352cc285693b..e74cad28f77f4a 100644
--- a/examples/alerting_example/server/plugin.ts
+++ b/examples/alerting_example/server/plugin.ts
@@ -44,6 +44,9 @@ export class AlertingExamplePlugin implements Plugin {
+ return {
+ management: {
+ insightsAndAlerting: {
+ triggersActions: true,
+ },
+ },
+ };
+ });
+
this.isESOUsingEphemeralEncryptionKey =
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts
index 38e75f75ad04b9..971bc962343764 100644
--- a/x-pack/plugins/apm/server/feature.ts
+++ b/x-pack/plugins/apm/server/feature.ts
@@ -17,6 +17,9 @@ export const APM_FEATURE = {
navLinkId: 'apm',
app: ['apm', 'kibana'],
catalogue: ['apm'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
alerting: Object.values(AlertType),
// see x-pack/plugins/features/common/feature_kibana_privileges.ts
privileges: {
@@ -31,6 +34,9 @@ export const APM_FEATURE = {
alerting: {
all: Object.values(AlertType),
},
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
ui: ['show', 'save', 'alerting:show', 'alerting:save'],
},
read: {
@@ -44,6 +50,9 @@ export const APM_FEATURE = {
alerting: {
all: Object.values(AlertType),
},
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
ui: ['show', 'alerting:show', 'alerting:save'],
},
},
diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts
index fdbd1ec8940225..3e32cebf19ac22 100644
--- a/x-pack/plugins/infra/server/features.ts
+++ b/x-pack/plugins/infra/server/features.ts
@@ -19,6 +19,9 @@ export const METRICS_FEATURE = {
navLinkId: 'metrics',
app: ['infra', 'metrics', 'kibana'],
catalogue: ['infraops'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
alerting: [METRIC_THRESHOLD_ALERT_TYPE_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID],
privileges: {
all: {
@@ -32,7 +35,10 @@ export const METRICS_FEATURE = {
alerting: {
all: [METRIC_THRESHOLD_ALERT_TYPE_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID],
},
- ui: ['show', 'configureSource', 'save', 'alerting:show'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
+ ui: ['show', 'configureSource', 'save'],
},
read: {
app: ['infra', 'metrics', 'kibana'],
@@ -45,7 +51,10 @@ export const METRICS_FEATURE = {
alerting: {
all: [METRIC_THRESHOLD_ALERT_TYPE_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID],
},
- ui: ['show', 'alerting:show'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
+ ui: ['show'],
},
},
};
diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts
index 06cd3138ca5647..8fc413236dd2c7 100644
--- a/x-pack/plugins/security_solution/server/plugin.ts
+++ b/x-pack/plugins/security_solution/server/plugin.ts
@@ -174,6 +174,9 @@ export class Plugin implements IPlugin {
};
export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) => {
- const { capabilities } = useAppDependencies();
- const canShowAlerts = hasShowAlertsCapability(capabilities);
- const DEFAULT_SECTION: Section = canShowAlerts ? 'alerts' : 'connectors';
return (
- {canShowAlerts && (
-
- )}
-
+
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
index eeb8a77717333a..15099242b6e17d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
@@ -25,7 +25,7 @@ import { Section, routeToConnectors, routeToAlerts } from './constants';
import { getCurrentBreadcrumb } from './lib/breadcrumb';
import { getCurrentDocTitle } from './lib/doc_title';
import { useAppDependencies } from './app_context';
-import { hasShowActionsCapability, hasShowAlertsCapability } from './lib/capabilities';
+import { hasShowActionsCapability } from './lib/capabilities';
import { ActionsConnectorsList } from './sections/actions_connectors_list/components/actions_connectors_list';
import { AlertsList } from './sections/alerts_list/components/alerts_list';
@@ -45,23 +45,17 @@ export const TriggersActionsUIHome: React.FunctionComponent = [];
- if (canShowAlerts) {
- tabs.push({
- id: 'alerts',
- name: (
-
- ),
- });
- }
+ tabs.push({
+ id: 'alerts',
+ name: (
+
+ ),
+ });
if (canShowActions) {
tabs.push({
@@ -151,17 +145,15 @@ export const TriggersActionsUIHome: React.FunctionComponent
)}
- {canShowAlerts && (
- (
-
-
-
- )}
- />
- )}
+ (
+
+
+
+ )}
+ />
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
index 065a782ee96a21..9e89a38377a4d0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { BUILT_IN_ALERTS_FEATURE_ID } from '../../../../alerting_builtins/common';
import { Alert, AlertType } from '../../types';
/**
@@ -15,18 +14,6 @@ import { Alert, AlertType } from '../../types';
type Capabilities = Record;
-const apps = ['apm', 'siem', 'uptime', 'infrastructure', 'actions', BUILT_IN_ALERTS_FEATURE_ID];
-
-function hasCapability(capabilities: Capabilities, capability: string) {
- return apps.some((app) => capabilities[app]?.[capability]);
-}
-
-function createCapabilityCheck(capability: string) {
- return (capabilities: Capabilities) => hasCapability(capabilities, capability);
-}
-
-export const hasShowAlertsCapability = createCapabilityCheck('alerting:show');
-
export const hasShowActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.show;
export const hasSaveActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.save;
export const hasExecuteActionsCapability = (capabilities: Capabilities) =>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
index a620a0db45408c..16d1a5c7c9c652 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
@@ -29,9 +29,6 @@ jest.mock('../../../app_context', () => ({
http: jest.fn(),
capabilities: {
get: jest.fn(() => ({})),
- securitySolution: {
- 'alerting:show': true,
- },
},
actionTypeRegistry: jest.fn(),
alertTypeRegistry: {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
index 9d54baf359af5b..c0674e6c4a5f72 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
@@ -244,15 +244,7 @@ export const AlertForm = ({
) : null}
{AlertParamsExpressionComponent ? (
-
-
-
-
-
- }
- >
+
- ) : (
+ ) : alertTypesIndex ? (
+ ) : (
+
)}
);
};
+const CenterJustifiedSpinner = () => (
+
+
+
+
+
+);
+
const NoAuthorizedAlertTypes = ({ operation }: { operation: string }) => (
{
http: mockes.http,
uiSettings: mockes.uiSettings,
navigateToApp,
- capabilities: {
- ...capabilities,
- securitySolution: {
- 'alerting:show': true,
- },
- },
+ capabilities,
history: scopedHistoryMock.create(),
setBreadcrumbs: jest.fn(),
actionTypeRegistry: actionTypeRegistry as any,
@@ -223,12 +218,7 @@ describe('alerts_list component with items', () => {
http: mockes.http,
uiSettings: mockes.uiSettings,
navigateToApp,
- capabilities: {
- ...capabilities,
- securitySolution: {
- 'alerting:show': true,
- },
- },
+ capabilities,
history: scopedHistoryMock.create(),
setBreadcrumbs: jest.fn(),
actionTypeRegistry: actionTypeRegistry as any,
@@ -303,12 +293,7 @@ describe('alerts_list component empty with show only capability', () => {
http: mockes.http,
uiSettings: mockes.uiSettings,
navigateToApp,
- capabilities: {
- ...capabilities,
- securitySolution: {
- 'alerting:show': true,
- },
- },
+ capabilities,
history: scopedHistoryMock.create(),
setBreadcrumbs: jest.fn(),
actionTypeRegistry: {
@@ -417,12 +402,7 @@ describe('alerts_list with show only capability', () => {
http: mockes.http,
uiSettings: mockes.uiSettings,
navigateToApp,
- capabilities: {
- ...capabilities,
- securitySolution: {
- 'alerting:show': true,
- },
- },
+ capabilities,
history: scopedHistoryMock.create(),
setBreadcrumbs: jest.fn(),
actionTypeRegistry: actionTypeRegistry as any,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index 8cb7afbda0e70a..2b2897a2181b11 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -93,7 +93,7 @@ export const AlertsList: React.FunctionComponent = () => {
useEffect(() => {
loadAlertsData();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [page, searchText, typesFilter, actionTypesFilter]);
+ }, [alertTypesState, page, searchText, typesFilter, actionTypesFilter]);
useEffect(() => {
(async () => {
@@ -136,30 +136,33 @@ export const AlertsList: React.FunctionComponent = () => {
}, []);
async function loadAlertsData() {
- setAlertsState({ ...alertsState, isLoading: true });
- try {
- const alertsResponse = await loadAlerts({
- http,
- page,
- searchText,
- typesFilter,
- actionTypesFilter,
- });
- setAlertsState({
- isLoading: false,
- data: alertsResponse.data,
- totalItemCount: alertsResponse.total,
- });
- } catch (e) {
- toastNotifications.addDanger({
- title: i18n.translate(
- 'xpack.triggersActionsUI.sections.alertsList.unableToLoadAlertsMessage',
- {
- defaultMessage: 'Unable to load alerts',
- }
- ),
- });
- setAlertsState({ ...alertsState, isLoading: false });
+ const hasAnyAuthorizedAlertType = alertTypesState.data.size > 0;
+ if (hasAnyAuthorizedAlertType) {
+ setAlertsState({ ...alertsState, isLoading: true });
+ try {
+ const alertsResponse = await loadAlerts({
+ http,
+ page,
+ searchText,
+ typesFilter,
+ actionTypesFilter,
+ });
+ setAlertsState({
+ isLoading: false,
+ data: alertsResponse.data,
+ totalItemCount: alertsResponse.total,
+ });
+ } catch (e) {
+ toastNotifications.addDanger({
+ title: i18n.translate(
+ 'xpack.triggersActionsUI.sections.alertsList.unableToLoadAlertsMessage',
+ {
+ defaultMessage: 'Unable to load alerts',
+ }
+ ),
+ });
+ setAlertsState({ ...alertsState, isLoading: false });
+ }
}
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
index af4d2784cfa672..25a917c7a1a156 100644
--- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
@@ -14,13 +14,11 @@ import {
import { i18n } from '@kbn/i18n';
import { registerBuiltInActionTypes } from './application/components/builtin_action_types';
import { registerBuiltInAlertTypes } from './application/components/builtin_alert_types';
-import { hasShowActionsCapability, hasShowAlertsCapability } from './application/lib/capabilities';
import { ActionTypeModel, AlertTypeModel } from './types';
import { TypeRegistry } from './application/type_registry';
import {
ManagementSetup,
ManagementAppMountParams,
- ManagementApp,
} from '../../../../src/plugins/management/public';
import { boot } from './application/boot';
import { ChartsPluginStart } from '../../../../src/plugins/charts/public';
@@ -50,10 +48,14 @@ interface PluginsStart {
export class Plugin
implements
- CorePlugin {
+ CorePlugin<
+ TriggersAndActionsUIPublicPluginSetup,
+ TriggersAndActionsUIPublicPluginStart,
+ PluginsSetup,
+ PluginsStart
+ > {
private actionTypeRegistry: TypeRegistry;
private alertTypeRegistry: TypeRegistry;
- private managementApp?: ManagementApp;
constructor(initializerContext: PluginInitializerContext) {
const actionTypeRegistry = new TypeRegistry();
@@ -67,7 +69,7 @@ export class Plugin
const actionTypeRegistry = this.actionTypeRegistry;
const alertTypeRegistry = this.alertTypeRegistry;
- this.managementApp = plugins.management.sections.section.insightsAndAlerting.registerApp({
+ plugins.management.sections.section.insightsAndAlerting.registerApp({
id: 'triggersActions',
title: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', {
defaultMessage: 'Alerts and Actions',
@@ -116,19 +118,7 @@ export class Plugin
};
}
- public start(core: CoreStart): TriggersAndActionsUIPublicPluginStart {
- const { capabilities } = core.application;
-
- const canShowActions = hasShowActionsCapability(capabilities);
- const canShowAlerts = hasShowAlertsCapability(capabilities);
- const managementApp = this.managementApp as ManagementApp;
-
- // Don't register routes when user doesn't have access to the application
- if (canShowActions || canShowAlerts) {
- managementApp.enable();
- } else {
- managementApp.disable();
- }
+ public start(): TriggersAndActionsUIPublicPluginStart {
return {
actionTypeRegistry: this.actionTypeRegistry,
alertTypeRegistry: this.alertTypeRegistry,
diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts
index a2d5f58bbec140..2bf0d84a49de1a 100644
--- a/x-pack/plugins/uptime/server/kibana.index.ts
+++ b/x-pack/plugins/uptime/server/kibana.index.ts
@@ -35,6 +35,9 @@ export const initServerWithKibana = (server: UptimeCoreSetup, plugins: UptimeCor
icon: 'uptimeApp',
app: ['uptime', 'kibana'],
catalogue: ['uptime'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
alerting: ['xpack.uptime.alerts.tls', 'xpack.uptime.alerts.monitorStatus'],
privileges: {
all: {
@@ -48,7 +51,10 @@ export const initServerWithKibana = (server: UptimeCoreSetup, plugins: UptimeCor
alerting: {
all: ['xpack.uptime.alerts.tls', 'xpack.uptime.alerts.monitorStatus'],
},
- ui: ['save', 'configureSettings', 'show', 'alerting:show'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
+ ui: ['save', 'configureSettings', 'show'],
},
read: {
app: ['uptime', 'kibana'],
@@ -61,7 +67,10 @@ export const initServerWithKibana = (server: UptimeCoreSetup, plugins: UptimeCor
alerting: {
all: ['xpack.uptime.alerts.tls', 'xpack.uptime.alerts.monitorStatus'],
},
- ui: ['show', 'alerting:show'],
+ management: {
+ insightsAndAlerting: ['triggersActions'],
+ },
+ ui: ['show'],
},
},
});
From 330c966f4f7837d337a46268469ed3528d0062dd Mon Sep 17 00:00:00 2001
From: Shahzad
Date: Tue, 28 Jul 2020 16:30:58 +0200
Subject: [PATCH 036/102] [Uptime] Reduce miscellaneous uptime bundle size
(#70632)
Co-authored-by: Elastic Machine
---
.../triggers_actions_ui/public/index.ts | 1 +
.../plugins/uptime/common/constants/index.ts | 2 -
x-pack/plugins/uptime/public/app.ts | 7 --
x-pack/plugins/uptime/public/apps/index.ts | 7 --
x-pack/plugins/uptime/public/apps/plugin.ts | 39 ++++----
.../render_app.tsx} | 44 ++++-----
.../plugins/uptime/public/apps/template.html | 1 -
.../uptime/public/{ => apps}/uptime_app.tsx | 23 +++--
.../public/apps/uptime_overview_fetcher.ts | 15 ++-
.../components/monitor/ml/manage_ml_job.tsx | 2 +-
.../monitor/ml/ml_flyout_container.tsx | 3 +-
.../embeddables/__tests__/map_config.test.ts | 2 +-
.../location_map/embeddables/map_config.ts | 2 +-
.../alerts/alerts_containers/alert_tls.tsx | 3 -
.../alerts/anomaly_alert/anomaly_alert.tsx | 3 +-
.../alerts/toggle_alert_flyout_button.tsx | 2 +-
.../overview/kuery_bar/kuery_bar.tsx | 7 +-
.../actions_popover/actions_popover.tsx | 4 +-
.../actions_popover/integration_group.tsx | 9 +-
.../monitor_status_list.tsx | 5 +-
.../monitor_list_status_column.tsx | 5 +-
.../contexts/uptime_settings_context.tsx | 2 +-
.../public/contexts/uptime_theme_context.tsx | 2 +-
x-pack/plugins/uptime/public/index.ts | 2 +-
.../__tests__/monitor_status.test.ts | 21 ++---
.../lib/alert_types/duration_anomaly.tsx | 18 +---
.../lazy_wrapper/duration_anomaly.tsx | 32 +++++++
.../lazy_wrapper/monitor_status.tsx | 32 +++++++
.../alert_types/lazy_wrapper/tls_alert.tsx | 32 +++++++
.../lazy_wrapper/validate_monitor_status.ts | 56 +++++++++++
.../public/lib/alert_types/monitor_status.tsx | 94 +++++--------------
.../lib/alert_types/monitor_status_title.tsx | 17 ----
.../uptime/public/lib/alert_types/tls.tsx | 17 +---
.../observability_integration/build_href.ts | 9 +-
.../observability_integration/get_apm_href.ts | 3 +-
.../get_infra_href.ts | 12 ++-
.../get_logging_href.ts | 12 ++-
x-pack/plugins/uptime/public/lib/lib.ts | 13 ---
.../uptime/public/pages/certificates.tsx | 3 +-
.../plugins/uptime/public/pages/settings.tsx | 12 ++-
.../plugins/uptime/public/state/api/utils.ts | 6 +-
.../public/state/effects/index_status.ts | 2 +-
x-pack/plugins/uptime/server/kibana.index.ts | 2 +-
.../server/lib/alerts/duration_anomaly.ts | 2 +-
.../uptime/server/lib/alerts/status_check.ts | 2 +-
.../plugins/uptime/server/lib/alerts/tls.ts | 3 +-
46 files changed, 327 insertions(+), 265 deletions(-)
delete mode 100644 x-pack/plugins/uptime/public/app.ts
delete mode 100644 x-pack/plugins/uptime/public/apps/index.ts
rename x-pack/plugins/uptime/public/{lib/adapters/framework/new_platform_adapter.tsx => apps/render_app.tsx} (72%)
delete mode 100644 x-pack/plugins/uptime/public/apps/template.html
rename x-pack/plugins/uptime/public/{ => apps}/uptime_app.tsx (84%)
create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx
create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/monitor_status.tsx
create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/tls_alert.tsx
create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts
delete mode 100644 x-pack/plugins/uptime/public/lib/alert_types/monitor_status_title.tsx
diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts
index 1048e15eb11847..7808e2a7f608d1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/index.ts
@@ -19,6 +19,7 @@ export {
ActionType,
ActionTypeRegistryContract,
AlertTypeParamsExpressionProps,
+ ValidationResult,
ActionVariable,
} from './types';
export {
diff --git a/x-pack/plugins/uptime/common/constants/index.ts b/x-pack/plugins/uptime/common/constants/index.ts
index 0ddb9953012665..29ae9e47dfb8a0 100644
--- a/x-pack/plugins/uptime/common/constants/index.ts
+++ b/x-pack/plugins/uptime/common/constants/index.ts
@@ -4,13 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export * from './alerts';
export { CHART_FORMAT_LIMITS } from './chart_format_limits';
export { CLIENT_DEFAULTS } from './client_defaults';
export { CONTEXT_DEFAULTS } from './context_defaults';
export * from './capabilities';
export * from './settings_defaults';
-export { PLUGIN } from './plugin';
export { QUERY } from './query';
export * from './ui';
export * from './rest_api';
diff --git a/x-pack/plugins/uptime/public/app.ts b/x-pack/plugins/uptime/public/app.ts
deleted file mode 100644
index b068f8a9becda5..00000000000000
--- a/x-pack/plugins/uptime/public/app.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './apps/index';
diff --git a/x-pack/plugins/uptime/public/apps/index.ts b/x-pack/plugins/uptime/public/apps/index.ts
deleted file mode 100644
index 65b80d08d4f20f..00000000000000
--- a/x-pack/plugins/uptime/public/apps/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { UptimePlugin } from './plugin';
diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts
index 9af4dea9dbb444..cf750434ab3243 100644
--- a/x-pack/plugins/uptime/public/apps/plugin.ts
+++ b/x-pack/plugins/uptime/public/apps/plugin.ts
@@ -12,10 +12,11 @@ import {
AppMountParameters,
} from 'kibana/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../../src/core/public';
-import { UMFrontendLibs } from '../lib/lib';
-import { PLUGIN } from '../../common/constants';
-import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public';
-import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public';
+
+import {
+ FeatureCatalogueCategory,
+ HomePublicPluginSetup,
+} from '../../../../../src/plugins/home/public';
import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public';
import {
TriggersAndActionsUIPublicPluginSetup,
@@ -26,10 +27,8 @@ import {
DataPublicPluginStart,
} from '../../../../../src/plugins/data/public';
import { alertTypeInitializers } from '../lib/alert_types';
-import { kibanaService } from '../state/kibana_service';
-import { fetchIndexStatus } from '../state/api';
-import { ObservabilityPluginSetup } from '../../../observability/public';
-import { fetchUptimeOverviewData } from './uptime_overview_fetcher';
+import { FetchDataParams, ObservabilityPluginSetup } from '../../../observability/public';
+import { PLUGIN } from '../../common/constants/plugin';
export interface ClientPluginsSetup {
data: DataPublicPluginSetup;
@@ -66,14 +65,23 @@ export class UptimePlugin
category: FeatureCatalogueCategory.DATA,
});
}
+ const getUptimeDataHelper = async () => {
+ const [coreStart] = await core.getStartServices();
+ const { UptimeDataHelper } = await import('./uptime_overview_fetcher');
+ return UptimeDataHelper(coreStart);
+ };
plugins.observability.dashboard.register({
appName: 'uptime',
hasData: async () => {
- const status = await fetchIndexStatus();
+ const dataHelper = await getUptimeDataHelper();
+ const status = await dataHelper.indexStatus();
return status.docCount > 0;
},
- fetchData: fetchUptimeOverviewData,
+ fetchData: async (params: FetchDataParams) => {
+ const dataHelper = await getUptimeDataHelper();
+ return await dataHelper.overviewData(params);
+ },
});
core.application.register({
@@ -85,22 +93,15 @@ export class UptimePlugin
category: DEFAULT_APP_CATEGORIES.observability,
mount: async (params: AppMountParameters) => {
const [coreStart, corePlugins] = await core.getStartServices();
- const { getKibanaFrameworkAdapter } = await import(
- '../lib/adapters/framework/new_platform_adapter'
- );
- const { element } = params;
+ const { renderApp } = await import('./render_app');
- const libs: UMFrontendLibs = {
- framework: getKibanaFrameworkAdapter(coreStart, plugins, corePlugins),
- };
- return libs.framework.render(element);
+ return renderApp(coreStart, plugins, corePlugins, params);
},
});
}
public start(start: CoreStart, plugins: ClientPluginsStart): void {
- kibanaService.core = start;
alertTypeInitializers.forEach((init) => {
const alertInitializer = init({
core: start,
diff --git a/x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx b/x-pack/plugins/uptime/public/apps/render_app.tsx
similarity index 72%
rename from x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx
rename to x-pack/plugins/uptime/public/apps/render_app.tsx
index d6185f2c2589a3..f834f8b5cdd3c3 100644
--- a/x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx
+++ b/x-pack/plugins/uptime/public/apps/render_app.tsx
@@ -4,26 +4,26 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { CoreStart } from 'src/core/public';
import React from 'react';
import ReactDOM from 'react-dom';
import { i18n as i18nFormatter } from '@kbn/i18n';
-import { UptimeApp, UptimeAppProps } from '../../../uptime_app';
-import { getIntegratedAppAvailability } from './capabilities_adapter';
+import { AppMountParameters, CoreStart } from 'kibana/public';
+import { getIntegratedAppAvailability } from '../lib/adapters/framework/capabilities_adapter';
import {
- INTEGRATED_SOLUTIONS,
- PLUGIN,
DEFAULT_DARK_MODE,
DEFAULT_TIMEPICKER_QUICK_RANGES,
-} from '../../../../common/constants';
-import { UMFrameworkAdapter } from '../../lib';
-import { ClientPluginsStart, ClientPluginsSetup } from '../../../apps/plugin';
+ INTEGRATED_SOLUTIONS,
+} from '../../common/constants';
+import { UptimeApp, UptimeAppProps } from './uptime_app';
+import { ClientPluginsSetup, ClientPluginsStart } from './plugin';
+import { PLUGIN } from '../../common/constants/plugin';
-export const getKibanaFrameworkAdapter = (
+export function renderApp(
core: CoreStart,
plugins: ClientPluginsSetup,
- startPlugins: ClientPluginsStart
-): UMFrameworkAdapter => {
+ startPlugins: ClientPluginsStart,
+ { element }: AppMountParameters
+) {
const {
application: { capabilities },
chrome: { setBadge, setHelpExtension },
@@ -40,17 +40,17 @@ export const getKibanaFrameworkAdapter = (
const canSave = (capabilities.uptime.save ?? false) as boolean;
const props: UptimeAppProps = {
- basePath: basePath.get(),
+ plugins,
canSave,
core,
+ i18n,
+ startPlugins,
+ basePath: basePath.get(),
darkMode: core.uiSettings.get(DEFAULT_DARK_MODE),
commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES),
- i18n,
isApmAvailable: apm,
isInfraAvailable: infrastructure,
isLogsAvailable: logs,
- plugins,
- startPlugins,
renderGlobalHelpControls: () =>
setHelpExtension({
appName: i18nFormatter.translate('xpack.uptime.header.appName', {
@@ -72,15 +72,9 @@ export const getKibanaFrameworkAdapter = (
setBreadcrumbs: core.chrome.setBreadcrumbs,
};
- return {
- render: async (element: any) => {
- if (element) {
- ReactDOM.render(, element);
- }
+ ReactDOM.render(, element);
- return () => {
- ReactDOM.unmountComponentAtNode(element);
- };
- },
+ return () => {
+ ReactDOM.unmountComponentAtNode(element);
};
-};
+}
diff --git a/x-pack/plugins/uptime/public/apps/template.html b/x-pack/plugins/uptime/public/apps/template.html
deleted file mode 100644
index a6fb47048a9b1d..00000000000000
--- a/x-pack/plugins/uptime/public/apps/template.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/x-pack/plugins/uptime/public/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx
similarity index 84%
rename from x-pack/plugins/uptime/public/uptime_app.tsx
rename to x-pack/plugins/uptime/public/apps/uptime_app.tsx
index 4208d79e761ed4..41370f9fff4928 100644
--- a/x-pack/plugins/uptime/public/uptime_app.tsx
+++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx
@@ -9,24 +9,25 @@ import { i18n } from '@kbn/i18n';
import React, { useEffect } from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
-import { I18nStart, ChromeBreadcrumb, CoreStart } from 'src/core/public';
-import { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public';
-import { ClientPluginsSetup, ClientPluginsStart } from './apps/plugin';
-import { UMUpdateBadge } from './lib/lib';
+import { I18nStart, ChromeBreadcrumb, CoreStart } from 'kibana/public';
+import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
+import { ClientPluginsSetup, ClientPluginsStart } from './plugin';
+import { UMUpdateBadge } from '../lib/lib';
import {
UptimeRefreshContextProvider,
UptimeSettingsContextProvider,
UptimeThemeContextProvider,
UptimeStartupPluginsContextProvider,
-} from './contexts';
-import { CommonlyUsedRange } from './components/common/uptime_date_picker';
-import { setBasePath } from './state/actions';
-import { PageRouter } from './routes';
+} from '../contexts';
+import { CommonlyUsedRange } from '../components/common/uptime_date_picker';
+import { setBasePath } from '../state/actions';
+import { PageRouter } from '../routes';
import {
UptimeAlertsContextProvider,
UptimeAlertsFlyoutWrapper,
-} from './components/overview/alerts';
-import { store } from './state';
+} from '../components/overview/alerts';
+import { store } from '../state';
+import { kibanaService } from '../state/kibana_service';
export interface UptimeAppColors {
danger: string;
@@ -86,6 +87,8 @@ const Application = (props: UptimeAppProps) => {
);
}, [canSave, renderGlobalHelpControls, setBadge]);
+ kibanaService.core = core;
+
store.dispatch(setBasePath(basePath));
return (
diff --git a/x-pack/plugins/uptime/public/apps/uptime_overview_fetcher.ts b/x-pack/plugins/uptime/public/apps/uptime_overview_fetcher.ts
index d1e394dd4da6b5..7e5c18f13b29e3 100644
--- a/x-pack/plugins/uptime/public/apps/uptime_overview_fetcher.ts
+++ b/x-pack/plugins/uptime/public/apps/uptime_overview_fetcher.ts
@@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { fetchPingHistogram, fetchSnapshotCount } from '../state/api';
+import { CoreStart } from 'kibana/public';
import { UptimeFetchDataResponse, FetchDataParams } from '../../../observability/public';
+import { fetchIndexStatus, fetchPingHistogram, fetchSnapshotCount } from '../state/api';
+import { kibanaService } from '../state/kibana_service';
-export async function fetchUptimeOverviewData({
+async function fetchUptimeOverviewData({
absoluteTime,
relativeTime,
bucketSize,
@@ -52,3 +54,12 @@ export async function fetchUptimeOverviewData({
};
return response;
}
+
+export function UptimeDataHelper(coreStart: CoreStart | null) {
+ kibanaService.core = coreStart!;
+
+ return {
+ indexStatus: fetchIndexStatus,
+ overviewData: fetchUptimeOverviewData,
+ };
+}
diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx
index 87496e91c906cf..7a2899558891dd 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx
@@ -8,7 +8,7 @@ import React, { useContext, useState } from 'react';
import { EuiButton, EuiContextMenu, EuiIcon, EuiPopover } from '@elastic/eui';
import { useSelector, useDispatch } from 'react-redux';
-import { CLIENT_ALERT_TYPES } from '../../../../common/constants';
+import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts';
import {
canDeleteMLJobSelector,
hasMLJobSelector,
diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx
index 84634f328621fb..e4fe1901729d3e 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx
@@ -22,13 +22,14 @@ import {
import { MLJobLink } from './ml_job_link';
import * as labels from './translations';
import { MLFlyoutView } from './ml_flyout';
-import { CLIENT_ALERT_TYPES, ML_JOB_ID } from '../../../../common/constants';
+import { ML_JOB_ID } from '../../../../common/constants';
import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts';
import { useGetUrlParams } from '../../../hooks';
import { getDynamicSettings } from '../../../state/actions/dynamic_settings';
import { useMonitorId } from '../../../hooks';
import { kibanaService } from '../../../state/kibana_service';
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';
+import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts';
interface Props {
onClose: () => void;
diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts
index 09a41bd9eb4b9f..18b43434da24b1 100644
--- a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts
@@ -7,7 +7,7 @@
import { getLayerList } from '../map_config';
import { mockLayerList } from './__mocks__/mock';
import { LocationPoint } from '../embedded_map';
-import { UptimeAppColors } from '../../../../../../uptime_app';
+import { UptimeAppColors } from '../../../../../../apps/uptime_app';
jest.mock('uuid', () => {
return {
diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts
index e766641102a249..6f9b7e4d39c166 100644
--- a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts
+++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts
@@ -6,7 +6,7 @@
import lowPolyLayerFeatures from './low_poly_layer.json';
import { LocationPoint } from './embedded_map';
-import { UptimeAppColors } from '../../../../../uptime_app';
+import { UptimeAppColors } from '../../../../../apps/uptime_app';
/**
* Returns `Source/Destination Point-to-point` Map LayerList configuration, with a source,
diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx
index c7657c34220fc1..70adcdb563bce0 100644
--- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx
@@ -24,6 +24,3 @@ export const AlertTls: React.FC<{}> = () => {
/>
);
};
-
-// eslint-disable-next-line import/no-default-export
-export { AlertTls as default };
diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx
index 4b84012575ae90..1428a7f526fc2b 100644
--- a/x-pack/plugins/uptime/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx
@@ -25,8 +25,7 @@ interface Props {
setAlertParams: (key: string, value: any) => void;
}
-// eslint-disable-next-line import/no-default-export
-export default function AnomalyAlertComponent({ setAlertParams, alertParams }: Props) {
+export function AnomalyAlertComponent({ setAlertParams, alertParams }: Props) {
const [severity, setSeverity] = useState(DEFAULT_SEVERITY);
const monitorIdStore = useSelector(monitorIdSelector);
diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
index 18514bd92d7a08..067972a452f278 100644
--- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
@@ -15,7 +15,7 @@ import {
import React, { useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
-import { CLIENT_ALERT_TYPES } from '../../../../common/constants';
+import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts';
import { ToggleFlyoutTranslations } from './translations';
import { ToggleAlertFlyoutButtonProps } from './alerts_containers';
diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx
index 9c6a4e5d418a71..9e373949aea12a 100644
--- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx
@@ -5,8 +5,7 @@
*/
import React, { useState, useEffect } from 'react';
-import { uniqueId, startsWith } from 'lodash';
-import { EuiCallOut } from '@elastic/eui';
+import { EuiCallOut, htmlIdGenerator } from '@elastic/eui';
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import { Typeahead } from './typeahead';
@@ -94,7 +93,7 @@ export function KueryBar({
setState({ ...state, suggestions: [] });
setSuggestionLimit(15);
- const currentRequest = uniqueId();
+ const currentRequest = htmlIdGenerator()();
currentRequestCheck = currentRequest;
try {
@@ -116,7 +115,7 @@ export function KueryBar({
},
],
})) || []
- ).filter((suggestion: QuerySuggestion) => !startsWith(suggestion.text, 'span.'));
+ ).filter((suggestion: QuerySuggestion) => !suggestion.text.startsWith('span.'));
if (currentRequest !== currentRequestCheck) {
return;
}
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx
index 2070a374e75d0a..9e96f0ca765359 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { EuiPopover, EuiButton } from '@elastic/eui';
@@ -25,7 +24,8 @@ export const ActionsPopoverComponent = ({
}: ActionsPopoverProps) => {
const popoverId = `${summary.monitor_id}_popover`;
- const monitorUrl: string | undefined = get(summary, 'state.url.full', undefined);
+ const monitorUrl: string | undefined = summary?.state?.url?.full;
+
const isPopoverOpen: boolean =
!!popoverState && popoverState.open && popoverState.id === popoverId;
return (
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx
index 38aa9287b0c475..ff3b5d67375fe8 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx
@@ -27,9 +27,12 @@ interface IntegrationGroupProps {
export const extractSummaryValues = (summary: Pick) => {
const domain = summary.state.url?.domain ?? '';
- const podUid = summary.state.summaryPings?.[0]?.kubernetes?.pod?.uid ?? undefined;
- const containerId = summary.state.summaryPings?.[0]?.container?.id ?? undefined;
- const ip = summary.state.summaryPings?.[0]?.monitor.ip ?? undefined;
+
+ const firstCheck = summary.state.summaryPings?.[0];
+
+ const podUid = firstCheck?.kubernetes?.pod?.uid ?? undefined;
+ const containerId = firstCheck?.container?.id ?? undefined;
+ const ip = firstCheck?.monitor.ip ?? undefined;
return {
domain,
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx
index 334de6e3760741..96536a357a4502 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx
@@ -5,7 +5,6 @@
*/
import React from 'react';
-import { upperFirst } from 'lodash';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { LocationLink } from '../../../common/location_link';
@@ -29,9 +28,9 @@ export const MonitorStatusList = ({ summaryPings }: MonitorStatusListProps) => {
const location = ping.observer?.geo?.name ?? UNNAMED_LOCATION;
if (ping.monitor.status === STATUS.UP) {
- upChecks.add(upperFirst(location));
+ upChecks.add(location);
} else if (ping.monitor.status === STATUS.DOWN) {
- downChecks.add(upperFirst(location));
+ downChecks.add(location);
}
});
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx
index 68ddf512e4d3c5..7140211d188071 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx
@@ -7,7 +7,6 @@
import React from 'react';
import moment from 'moment';
import { i18n } from '@kbn/i18n';
-import { upperFirst } from 'lodash';
import styled from 'styled-components';
import { EuiHealth, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui';
import { parseTimestamp } from './parse_timestamp';
@@ -83,9 +82,9 @@ export const getLocationStatus = (summaryPings: Ping[], status: string) => {
const location = summaryPing?.observer?.geo?.name ?? UNNAMED_LOCATION;
if (summaryPing.monitor.status === STATUS.UP) {
- upPings.add(upperFirst(location));
+ upPings.add(location);
} else if (summaryPing.monitor.status === STATUS.DOWN) {
- downPings.add(upperFirst(location));
+ downPings.add(location);
}
});
diff --git a/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx
index 142c6e17c5fd90..4c08e76a11aae1 100644
--- a/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx
+++ b/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx
@@ -5,7 +5,7 @@
*/
import React, { createContext, useMemo } from 'react';
-import { UptimeAppProps } from '../uptime_app';
+import { UptimeAppProps } from '../apps/uptime_app';
import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../common/constants';
import { CommonlyUsedRange } from '../components/common/uptime_date_picker';
import { useGetUrlParams } from '../hooks';
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 ca2fb50cdbc677..51e8bcaed986ff 100644
--- a/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx
+++ b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx
@@ -9,7 +9,7 @@ import React, { createContext, useMemo } from 'react';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme';
import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts';
-import { UptimeAppColors } from '../uptime_app';
+import { UptimeAppColors } from '../apps/uptime_app';
export interface UptimeThemeContextValues {
colors: UptimeAppColors;
diff --git a/x-pack/plugins/uptime/public/index.ts b/x-pack/plugins/uptime/public/index.ts
index 48cf2c90ad07b1..cd6efa9016830a 100644
--- a/x-pack/plugins/uptime/public/index.ts
+++ b/x-pack/plugins/uptime/public/index.ts
@@ -5,7 +5,7 @@
*/
import { PluginInitializerContext } from 'kibana/public';
-import { UptimePlugin } from './apps';
+import { UptimePlugin } from './apps/plugin';
export const plugin = (initializerContext: PluginInitializerContext) =>
new UptimePlugin(initializerContext);
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts b/x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts
index cfcb414f4815df..e999768d4e55d9 100644
--- a/x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts
+++ b/x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts
@@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { validate, initMonitorStatusAlertType } from '../monitor_status';
+import { initMonitorStatusAlertType } from '../monitor_status';
+import { validateMonitorStatusParams as validate } from '../lazy_wrapper/validate_monitor_status';
describe('monitor status alert type', () => {
describe('validate', () => {
@@ -206,19 +207,11 @@ describe('monitor status alert type', () => {
",
"iconClass": "uptimeApp",
"id": "xpack.uptime.alerts.monitorStatus",
- "name":
-
- ,
+ "name": ,
"requiresAppContext": false,
"validate": [Function],
}
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx b/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx
index f0eb3054615826..c1f802c2d0c915 100644
--- a/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx
+++ b/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx
@@ -5,30 +5,22 @@
*/
import React from 'react';
-import { Provider as ReduxProvider } from 'react-redux';
import { AlertTypeModel } from '../../../../triggers_actions_ui/public';
-import { CLIENT_ALERT_TYPES } from '../../../common/constants';
+import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts';
import { DurationAnomalyTranslations } from './translations';
import { AlertTypeInitializer } from '.';
-import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
-import { store } from '../../state';
const { name, defaultActionMessage } = DurationAnomalyTranslations;
-const AnomalyAlertExpression = React.lazy(() =>
- import('../../components/overview/alerts/anomaly_alert/anomaly_alert')
-);
+const DurationAnomalyAlert = React.lazy(() => import('./lazy_wrapper/duration_anomaly'));
+
export const initDurationAnomalyAlertType: AlertTypeInitializer = ({
core,
plugins,
}): AlertTypeModel => ({
id: CLIENT_ALERT_TYPES.DURATION_ANOMALY,
iconClass: 'uptimeApp',
- alertParamsExpression: (params: any) => (
-
-
-
-
-
+ alertParamsExpression: (params: unknown) => (
+
),
name,
validate: () => ({ errors: {} }),
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx
new file mode 100644
index 00000000000000..60f2d2e803b7b2
--- /dev/null
+++ b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { Provider as ReduxProvider } from 'react-redux';
+import { CoreStart } from 'kibana/public';
+import { store } from '../../../state';
+import { AnomalyAlertComponent } from '../../../components/overview/alerts/anomaly_alert/anomaly_alert';
+import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
+import { ClientPluginsStart } from '../../../apps/plugin';
+import { kibanaService } from '../../../state/kibana_service';
+
+interface Props {
+ core: CoreStart;
+ plugins: ClientPluginsStart;
+ params: any;
+}
+
+// eslint-disable-next-line import/no-default-export
+export default function DurationAnomalyAlert({ core, plugins, params }: Props) {
+ kibanaService.core = core;
+ return (
+
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/monitor_status.tsx
new file mode 100644
index 00000000000000..f6b10d0fbf968a
--- /dev/null
+++ b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/monitor_status.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { Provider as ReduxProvider } from 'react-redux';
+import { CoreStart } from 'kibana/public';
+import { store } from '../../../state';
+import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
+import { ClientPluginsStart } from '../../../apps/plugin';
+import { AlertMonitorStatus } from '../../../components/overview/alerts/alerts_containers';
+import { kibanaService } from '../../../state/kibana_service';
+
+interface Props {
+ core: CoreStart;
+ plugins: ClientPluginsStart;
+ params: any;
+}
+
+// eslint-disable-next-line import/no-default-export
+export default function MonitorStatusAlert({ core, plugins, params }: Props) {
+ kibanaService.core = core;
+ return (
+
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/tls_alert.tsx b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/tls_alert.tsx
new file mode 100644
index 00000000000000..413734b63ced5a
--- /dev/null
+++ b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/tls_alert.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { Provider as ReduxProvider } from 'react-redux';
+import { CoreStart } from 'kibana/public';
+import { store } from '../../../state';
+import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
+import { ClientPluginsStart } from '../../../apps/plugin';
+import { AlertTls } from '../../../components/overview/alerts/alerts_containers/alert_tls';
+import { kibanaService } from '../../../state/kibana_service';
+
+interface Props {
+ core: CoreStart;
+ plugins: ClientPluginsStart;
+ params: any;
+}
+
+// eslint-disable-next-line import/no-default-export
+export default function TLSAlert({ core, plugins, params: _params }: Props) {
+ kibanaService.core = core;
+ return (
+
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts
new file mode 100644
index 00000000000000..709669c24ed0ac
--- /dev/null
+++ b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { PathReporter } from 'io-ts/lib/PathReporter';
+import { isRight } from 'fp-ts/lib/Either';
+import {
+ AtomicStatusCheckParamsType,
+ MonitorAvailabilityType,
+ StatusCheckParamsType,
+} from '../../../../common/runtime_types/alerts';
+import { ValidationResult } from '../../../../../triggers_actions_ui/public';
+
+export function validateMonitorStatusParams(alertParams: any): ValidationResult {
+ const errors: Record = {};
+ const decoded = AtomicStatusCheckParamsType.decode(alertParams);
+ const oldDecoded = StatusCheckParamsType.decode(alertParams);
+ const availabilityDecoded = MonitorAvailabilityType.decode(alertParams);
+
+ if (!isRight(decoded) && !isRight(oldDecoded) && !isRight(availabilityDecoded)) {
+ return {
+ errors: {
+ typeCheckFailure: 'Provided parameters do not conform to the expected type.',
+ typeCheckParsingMessage: PathReporter.report(decoded),
+ },
+ };
+ }
+
+ if (
+ !(alertParams.shouldCheckAvailability ?? false) &&
+ !(alertParams.shouldCheckStatus ?? false)
+ ) {
+ return {
+ errors: {
+ noAlertSelected: 'Alert must check for monitor status or monitor availability.',
+ },
+ };
+ }
+
+ if (isRight(decoded) && decoded.right.shouldCheckStatus) {
+ const { numTimes, timerangeCount } = decoded.right;
+ if (numTimes < 1) {
+ errors.invalidNumTimes = 'Number of alert check down times must be an integer greater than 0';
+ }
+ if (isNaN(timerangeCount)) {
+ errors.timeRangeStartValueNaN = 'Specified time range value must be a number';
+ }
+ if (timerangeCount <= 0) {
+ errors.invalidTimeRangeValue = 'Time range value must be greater than 0';
+ }
+ }
+
+ return { errors };
+}
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx
index cb24df2357d010..e4da3eb9ef7ae7 100644
--- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx
+++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx
@@ -4,70 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Provider as ReduxProvider } from 'react-redux';
import React from 'react';
-import { isRight } from 'fp-ts/lib/Either';
-import { PathReporter } from 'io-ts/lib/PathReporter';
-import { AlertTypeModel } from '../../../../triggers_actions_ui/public';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public';
import { AlertTypeInitializer } from '.';
-import {
- AtomicStatusCheckParamsType,
- StatusCheckParamsType,
- MonitorAvailabilityType,
-} from '../../../common/runtime_types';
-import { MonitorStatusTitle } from './monitor_status_title';
-import { CLIENT_ALERT_TYPES } from '../../../common/constants';
-import { MonitorStatusTranslations } from './translations';
-import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
-import { store } from '../../state';
-
-export const validate = (alertParams: any) => {
- const errors: Record = {};
- const decoded = AtomicStatusCheckParamsType.decode(alertParams);
- const oldDecoded = StatusCheckParamsType.decode(alertParams);
- const availabilityDecoded = MonitorAvailabilityType.decode(alertParams);
-
- if (!isRight(decoded) && !isRight(oldDecoded) && !isRight(availabilityDecoded)) {
- return {
- errors: {
- typeCheckFailure: 'Provided parameters do not conform to the expected type.',
- typeCheckParsingMessage: PathReporter.report(decoded),
- },
- };
- }
-
- if (
- !(alertParams.shouldCheckAvailability ?? false) &&
- !(alertParams.shouldCheckStatus ?? false)
- ) {
- return {
- errors: {
- noAlertSelected: 'Alert must check for monitor status or monitor availability.',
- },
- };
- }
-
- if (isRight(decoded) && decoded.right.shouldCheckStatus) {
- const { numTimes, timerangeCount } = decoded.right;
- if (numTimes < 1) {
- errors.invalidNumTimes = 'Number of alert check down times must be an integer greater than 0';
- }
- if (isNaN(timerangeCount)) {
- errors.timeRangeStartValueNaN = 'Specified time range value must be a number';
- }
- if (timerangeCount <= 0) {
- errors.invalidTimeRangeValue = 'Time range value must be greater than 0';
- }
- }
- return { errors };
-};
+import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts';
+import { MonitorStatusTranslations } from './translations';
const { defaultActionMessage } = MonitorStatusTranslations;
-const AlertMonitorStatus = React.lazy(() =>
- import('../../components/overview/alerts/alerts_containers/alert_monitor_status')
-);
+const MonitorStatusAlert = React.lazy(() => import('./lazy_wrapper/monitor_status'));
+
+let validateFunc: (alertParams: any) => ValidationResult;
export const initMonitorStatusAlertType: AlertTypeInitializer = ({
core,
@@ -75,21 +24,26 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({
}): AlertTypeModel => ({
id: CLIENT_ALERT_TYPES.MONITOR_STATUS,
name: (
-
-
-
+
),
iconClass: 'uptimeApp',
- alertParamsExpression: (params: any) => {
- return (
-
-
-
-
-
- );
+ alertParamsExpression: (params: any) => (
+
+ ),
+ validate: (alertParams: any) => {
+ if (!validateFunc) {
+ (async function loadValidate() {
+ const { validateMonitorStatusParams } = await import(
+ './lazy_wrapper/validate_monitor_status'
+ );
+ validateFunc = validateMonitorStatusParams;
+ })();
+ }
+ return validateFunc && validateFunc(alertParams);
},
- validate,
defaultActionMessage,
requiresAppContext: false,
});
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status_title.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status_title.tsx
deleted file mode 100644
index 1e2751a4ac3887..00000000000000
--- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status_title.tsx
+++ /dev/null
@@ -1,17 +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 React from 'react';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-export const MonitorStatusTitle = () => {
- return (
-
- );
-};
diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx
index c541ea4ae13313..9019fc216192c9 100644
--- a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx
+++ b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx
@@ -5,27 +5,18 @@
*/
import React from 'react';
-import { Provider as ReduxProvider } from 'react-redux';
import { AlertTypeModel } from '../../../../triggers_actions_ui/public';
-import { CLIENT_ALERT_TYPES } from '../../../common/constants';
+import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts';
import { TlsTranslations } from './translations';
import { AlertTypeInitializer } from '.';
-import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
-import { store } from '../../state';
const { name, defaultActionMessage } = TlsTranslations;
-const TlsAlertExpression = React.lazy(() =>
- import('../../components/overview/alerts/alerts_containers/alert_tls')
-);
+const TLSAlert = React.lazy(() => import('./lazy_wrapper/tls_alert'));
export const initTlsAlertType: AlertTypeInitializer = ({ core, plugins }): AlertTypeModel => ({
id: CLIENT_ALERT_TYPES.TLS,
iconClass: 'uptimeApp',
- alertParamsExpression: (_params: any) => (
-
-
-
-
-
+ alertParamsExpression: (params: any) => (
+
),
name,
validate: () => ({ errors: {} }),
diff --git a/x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts
index 94383262b0acd5..8c96a469da492e 100644
--- a/x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts
@@ -4,24 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { get } from 'lodash';
import { Ping } from '../../../../common/runtime_types';
/**
* Builds URLs to the designated features by extracting values from the provided
* monitor object on a given path. Then returns the result of a provided function
* to place the value in its rightful place on the URI string.
- * @param checks array of summary checks containing the data to extract
- * @param path the location on the object of the desired data
+ * @param summaryPings array of summary checks containing the data to extract
+ * @param getData the location on the object of the desired data
* @param getHref a function that returns the full URL
*/
export const buildHref = (
summaryPings: Ping[],
- path: string,
+ getData: (ping: Ping) => string | undefined,
getHref: (value: string | string[] | undefined) => string | undefined
): string | undefined => {
const queryValue = summaryPings
- .map((ping) => get(ping, path, undefined))
+ .map((ping) => getData(ping))
.filter((value: string | undefined) => value !== undefined);
if (queryValue.length === 0) {
return getHref(undefined);
diff --git a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts
index 0ff5a8acb33674..a1d69950cb61ab 100644
--- a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { get } from 'lodash';
import { addBasePath } from './add_base_path';
import { MonitorSummary } from '../../../../common/runtime_types';
@@ -17,6 +16,6 @@ export const getApmHref = (
addBasePath(
basePath,
`/app/apm#/services?kuery=${encodeURI(
- `url.domain: "${get(summary, 'state.url.domain')}"`
+ `url.domain: "${summary?.state?.url?.domain}"`
)}&rangeFrom=${dateRangeStart}&rangeTo=${dateRangeEnd}`
);
diff --git a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
index 33d24a0f081b4d..c225382350eac5 100644
--- a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { MonitorSummary } from '../../../../common/runtime_types';
+import { MonitorSummary, Ping } from '../../../../common/runtime_types';
import { addBasePath } from './add_base_path';
import { buildHref } from './build_href';
@@ -22,7 +22,7 @@ export const getInfraContainerHref = (
`/app/metrics/link-to/container-detail/${encodeURIComponent(ret)}`
);
};
- return buildHref(summary.state.summaryPings || [], 'container.id', getHref);
+ return buildHref(summary.state.summaryPings || [], (ping: Ping) => ping?.container?.id, getHref);
};
export const getInfraKubernetesHref = (
@@ -37,7 +37,11 @@ export const getInfraKubernetesHref = (
return addBasePath(basePath, `/app/metrics/link-to/pod-detail/${encodeURIComponent(ret)}`);
};
- return buildHref(summary.state.summaryPings || [], 'kubernetes.pod.uid', getHref);
+ return buildHref(
+ summary.state.summaryPings || [],
+ (ping: Ping) => ping?.kubernetes?.pod?.uid,
+ getHref
+ );
};
export const getInfraIpHref = (summary: MonitorSummary, basePath: string) => {
@@ -63,5 +67,5 @@ export const getInfraIpHref = (summary: MonitorSummary, basePath: string) => {
`/app/metrics/inventory?waffleFilter=(expression:'${encodeURIComponent(ips)}',kind:kuery)`
);
};
- return buildHref(summary.state.summaryPings || [], 'monitor.ip', getHref);
+ return buildHref(summary.state.summaryPings || [], (ping: Ping) => ping?.monitor?.ip, getHref);
};
diff --git a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
index c4fee330e9763a..32709882d1d21a 100644
--- a/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { MonitorSummary } from '../../../../common/runtime_types';
+import { MonitorSummary, Ping } from '../../../../common/runtime_types';
import { addBasePath } from './add_base_path';
import { buildHref } from './build_href';
@@ -22,7 +22,7 @@ export const getLoggingContainerHref = (
`/app/logs?logFilter=${encodeURI(`(expression:'container.id : ${ret}',kind:kuery)`)}`
);
};
- return buildHref(summary.state.summaryPings || [], 'container.id', getHref);
+ return buildHref(summary.state.summaryPings || [], (ping: Ping) => ping?.container?.id, getHref);
};
export const getLoggingKubernetesHref = (summary: MonitorSummary, basePath: string) => {
@@ -36,7 +36,11 @@ export const getLoggingKubernetesHref = (summary: MonitorSummary, basePath: stri
`/app/logs?logFilter=${encodeURI(`(expression:'pod.uid : ${ret}',kind:kuery)`)}`
);
};
- return buildHref(summary.state.summaryPings || [], 'kubernetes.pod.uid', getHref);
+ return buildHref(
+ summary.state.summaryPings || [],
+ (ping: Ping) => ping?.kubernetes?.pod?.uid,
+ getHref
+ );
};
export const getLoggingIpHref = (summary: MonitorSummary, basePath: string) => {
@@ -50,5 +54,5 @@ export const getLoggingIpHref = (summary: MonitorSummary, basePath: string) => {
`/app/logs?logFilter=(expression:'${encodeURIComponent(`host.ip : ${ret}`)}',kind:kuery)`
);
};
- return buildHref(summary.state.summaryPings || [], 'monitor.ip', getHref);
+ return buildHref(summary.state.summaryPings || [], (ping: Ping) => ping?.monitor?.ip, getHref);
};
diff --git a/x-pack/plugins/uptime/public/lib/lib.ts b/x-pack/plugins/uptime/public/lib/lib.ts
index 187dcee7adb1a6..ac95f018a80a24 100644
--- a/x-pack/plugins/uptime/public/lib/lib.ts
+++ b/x-pack/plugins/uptime/public/lib/lib.ts
@@ -4,19 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { ReactElement } from 'react';
-import { AppUnmount } from 'kibana/public';
import { UMBadge } from '../badge';
-import { UptimeAppProps } from '../uptime_app';
-
-export interface UMFrontendLibs {
- framework: UMFrameworkAdapter;
-}
export type UMUpdateBadge = (badge: UMBadge) => void;
-
-export type BootstrapUptimeApp = (props: UptimeAppProps) => ReactElement;
-
-export interface UMFrameworkAdapter {
- render(element: any): Promise;
-}
diff --git a/x-pack/plugins/uptime/public/pages/certificates.tsx b/x-pack/plugins/uptime/public/pages/certificates.tsx
index 58a56a55553230..e46d228c6d21f6 100644
--- a/x-pack/plugins/uptime/public/pages/certificates.tsx
+++ b/x-pack/plugins/uptime/public/pages/certificates.tsx
@@ -21,13 +21,14 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { useTrackPageview } from '../../../observability/public';
import { PageHeader } from './page_header';
import { useBreadcrumbs } from '../hooks/use_breadcrumbs';
-import { OVERVIEW_ROUTE, SETTINGS_ROUTE, CLIENT_ALERT_TYPES } from '../../common/constants';
+import { OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../../common/constants';
import { getDynamicSettings } from '../state/actions/dynamic_settings';
import { UptimeRefreshContext } from '../contexts';
import * as labels from './translations';
import { certificatesSelector, getCertificatesAction } from '../state/certificates/certificates';
import { CertificateList, CertificateSearch, CertSort } from '../components/certificates';
import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers';
+import { CLIENT_ALERT_TYPES } from '../../common/constants/alerts';
const DEFAULT_PAGE_SIZE = 10;
const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize';
diff --git a/x-pack/plugins/uptime/public/pages/settings.tsx b/x-pack/plugins/uptime/public/pages/settings.tsx
index 602911cd41aab2..89c12d0efdac11 100644
--- a/x-pack/plugins/uptime/public/pages/settings.tsx
+++ b/x-pack/plugins/uptime/public/pages/settings.tsx
@@ -17,7 +17,6 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { useDispatch, useSelector } from 'react-redux';
-import { isEqual } from 'lodash';
import { useHistory } from 'react-router-dom';
import { selectDynamicSettings } from '../state/selectors';
import { getDynamicSettings, setDynamicSettings } from '../state/actions/dynamic_settings';
@@ -80,6 +79,14 @@ const getFieldErrors = (formFields: DynamicSettings | null): SettingsPageFieldEr
return null;
};
+const isDirtyForm = (formFields: DynamicSettings | null, settings?: DynamicSettings) => {
+ return (
+ settings?.certAgeThreshold !== formFields?.certAgeThreshold ||
+ settings?.certExpirationThreshold !== formFields?.certExpirationThreshold ||
+ settings?.heartbeatIndices !== formFields?.heartbeatIndices
+ );
+};
+
export const SettingsPage: React.FC = () => {
const dss = useSelector(selectDynamicSettings);
@@ -121,7 +128,8 @@ export const SettingsPage: React.FC = () => {
const resetForm = () => setFormFields(dss.settings ? { ...dss.settings } : null);
- const isFormDirty = !isEqual(dss.settings, formFields);
+ const isFormDirty = isDirtyForm(formFields, dss.settings);
+
const canEdit: boolean =
!!useKibana().services?.application?.capabilities.uptime.configureSettings || false;
const isFormDisabled = dss.loading || !canEdit;
diff --git a/x-pack/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts
index 4f3765275c49ab..e0cec56dd52cd4 100644
--- a/x-pack/plugins/uptime/public/state/api/utils.ts
+++ b/x-pack/plugins/uptime/public/state/api/utils.ts
@@ -8,7 +8,11 @@ import { PathReporter } from 'io-ts/lib/PathReporter';
import { isRight } from 'fp-ts/lib/Either';
import { HttpFetchQuery, HttpSetup } from 'src/core/public';
import * as t from 'io-ts';
-import { isObject } from 'lodash';
+
+function isObject(value: unknown) {
+ const type = typeof value;
+ return value != null && (type === 'object' || type === 'function');
+}
// TODO: Copied from https://github.com/elastic/kibana/blob/master/x-pack/plugins/security_solution/common/format_errors.ts
// We should figure out a better way to share this
diff --git a/x-pack/plugins/uptime/public/state/effects/index_status.ts b/x-pack/plugins/uptime/public/state/effects/index_status.ts
index a4b85312849a2d..3917159381eb5c 100644
--- a/x-pack/plugins/uptime/public/state/effects/index_status.ts
+++ b/x-pack/plugins/uptime/public/state/effects/index_status.ts
@@ -6,8 +6,8 @@
import { takeLeading } from 'redux-saga/effects';
import { indexStatusAction } from '../actions';
-import { fetchIndexStatus } from '../api';
import { fetchEffectFactory } from './fetch_effect';
+import { fetchIndexStatus } from '../api/index_status';
export function* fetchIndexStatusEffect() {
yield takeLeading(
diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts
index 2bf0d84a49de1a..ab8d7a068b19dd 100644
--- a/x-pack/plugins/uptime/server/kibana.index.ts
+++ b/x-pack/plugins/uptime/server/kibana.index.ts
@@ -5,7 +5,7 @@
*/
import { Request, Server } from 'hapi';
-import { PLUGIN } from '../common/constants';
+import { PLUGIN } from '../common/constants/plugin';
import { compose } from './lib/compose/kibana';
import { initUptimeServer } from './uptime_server';
import { UptimeCorePlugins, UptimeCoreSetup } from './lib/adapters/framework';
diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
index 7dd357e99b83df..a71913d0eea9af 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
@@ -8,7 +8,7 @@ import moment from 'moment';
import { schema } from '@kbn/config-schema';
import { ILegacyScopedClusterClient } from 'kibana/server';
import { updateState } from './common';
-import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants';
+import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants/alerts';
import { commonStateTranslations, durationAnomalyTranslations } from './translations';
import { AnomaliesTableRecord } from '../../../../ml/common/types/anomalies';
import { getSeverityType } from '../../../../ml/common/util/anomaly_utils';
diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts
index 2117ac4b7ed4ef..a34d7eb292eeff 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts
@@ -21,7 +21,7 @@ import {
MonitorAvailabilityType,
DynamicSettings,
} from '../../../common/runtime_types';
-import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants';
+import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants/alerts';
import { savedObjectsAdapter } from '../saved_objects';
import { updateState } from './common';
import { commonStateTranslations } from './translations';
diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts
index 61e738b088d500..d4853ad7a9cb03 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/tls.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts
@@ -9,7 +9,8 @@ import { schema } from '@kbn/config-schema';
import { UptimeAlertTypeFactory } from './types';
import { savedObjectsAdapter } from '../saved_objects';
import { updateState } from './common';
-import { ACTION_GROUP_DEFINITIONS, DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
+import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants/alerts';
+import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
import { Cert, CertResult } from '../../../common/runtime_types';
import { commonStateTranslations, tlsTranslations } from './translations';
import { DEFAULT_FROM, DEFAULT_TO } from '../../rest_api/certs/certs';
From 0dbfde4f4d945a92adab66a8895fc0a129af2bd9 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 28 Jul 2020 17:34:40 +0300
Subject: [PATCH 037/102] [Functional Tests] Increase the timeout on getting
the legend value on timeseries (#73279)
---
test/functional/page_objects/visual_builder_page.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index 8488eb8cd27493..2771982fecdea3 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -315,9 +315,9 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
public async getRhythmChartLegendValue(nth = 0) {
await PageObjects.visChart.waitForVisualizationRenderingStabilized();
- const metricValue = (await find.allByCssSelector(`.echLegendItem .echLegendItem__extra`))[
- nth
- ];
+ const metricValue = (
+ await find.allByCssSelector(`.echLegendItem .echLegendItem__extra`, 20000)
+ )[nth];
await metricValue.moveMouseTo();
return await metricValue.getVisibleText();
}
From f87d97b629d2497fd6d63dfe5ce5fd6d3a90537e Mon Sep 17 00:00:00 2001
From: Toby Sutor <55087308+toby-sutor@users.noreply.github.com>
Date: Tue, 28 Jul 2020 16:35:00 +0200
Subject: [PATCH 038/102] 32 characters requirement for
xpack.reporting.encryptionKey (#72593)
Similar to https://www.elastic.co/guide/en/kibana/current/alert-action-settings-kb.html#general-alert-action-settings is a 32 character minimum length required for xpack.reporting.encryptionKey
---
docs/settings/reporting-settings.asciidoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc
index c83cd068eff5b3..b31ae76d280522 100644
--- a/docs/settings/reporting-settings.asciidoc
+++ b/docs/settings/reporting-settings.asciidoc
@@ -21,7 +21,7 @@ You can configure `xpack.reporting` settings in your `kibana.yml` to:
| Set to `false` to disable the {report-features}.
| `xpack.reporting.encryptionKey`
- | Set to any text string. By default, {kib} will generate a random key when it
+ | Set to an alphanumeric, at least 32 characters long text string. By default, {kib} will generate a random key when it
starts, which will cause pending reports to fail after restart. Configure this
setting to preserve the same key across multiple restarts and multiple instances of {kib}.
From 56609049cb51d5a6edddc9929d0cb3bd5bc4cdee Mon Sep 17 00:00:00 2001
From: Toby Sutor <55087308+toby-sutor@users.noreply.github.com>
Date: Tue, 28 Jul 2020 16:35:08 +0200
Subject: [PATCH 039/102] 32 characters requirement for
xpack.reporting.encryptionKey (#72594)
Similar to https://github.com/elastic/kibana/pull/72593 document that the string needs to be at least 32 characters long.
---
docs/user/reporting/configuring-reporting.asciidoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/user/reporting/configuring-reporting.asciidoc b/docs/user/reporting/configuring-reporting.asciidoc
index ca2d79bb2dec0b..6a0c44cf4c2a47 100644
--- a/docs/user/reporting/configuring-reporting.asciidoc
+++ b/docs/user/reporting/configuring-reporting.asciidoc
@@ -23,7 +23,7 @@ reporting job metadata.
To set a static encryption key for reporting, set the
`xpack.reporting.encryptionKey` property in the `kibana.yml`
-configuration file. You can use any text string as the encryption key.
+configuration file. You can use any alphanumeric, at least 32 characters long text string as the encryption key.
[source,yaml]
--------------------------------------------------------------------------------
From cdb1c0d9a4c86cb1342005767ca697263cfafe39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20St=C3=BCrmer?=
Date: Tue, 28 Jul 2020 16:44:27 +0200
Subject: [PATCH 040/102] [Logs UI] Check for presence of data instead of
presence of indices in overview page fetchers (#73101)
This causes the "has data" check for the observability overview page to not only check for the presence of log indices but also of log entries.
---
.../log_sources/get_log_source_status.ts | 10 +++-
.../public/pages/logs/stream/page_content.tsx | 2 +-
.../pages/logs/stream/page_providers.tsx | 2 +-
.../public/utils/logs_overview_fetchers.ts | 2 +-
.../utils/logs_overview_fetches.test.ts | 48 ++++++++++++-------
.../server/graphql/source_status/resolvers.ts | 2 +-
.../lib/adapters/framework/adapter_types.ts | 1 +
.../elasticsearch_source_status_adapter.ts | 22 +++++++--
.../plugins/infra/server/lib/source_status.ts | 19 +++++---
.../infra/server/routes/log_sources/status.ts | 11 +++--
.../infra/server/routes/source/index.ts | 8 ++--
11 files changed, 84 insertions(+), 43 deletions(-)
diff --git a/x-pack/plugins/infra/common/http_api/log_sources/get_log_source_status.ts b/x-pack/plugins/infra/common/http_api/log_sources/get_log_source_status.ts
index b522d869872835..dc4e4f1ea62178 100644
--- a/x-pack/plugins/infra/common/http_api/log_sources/get_log_source_status.ts
+++ b/x-pack/plugins/infra/common/http_api/log_sources/get_log_source_status.ts
@@ -40,9 +40,17 @@ const logIndexFieldRT = rt.strict({
export type LogIndexField = rt.TypeOf;
+const logIndexStatusRT = rt.keyof({
+ missing: null,
+ empty: null,
+ available: null,
+});
+
+export type LogIndexStatus = rt.TypeOf;
+
const logSourceStatusRT = rt.strict({
logIndexFields: rt.array(logIndexFieldRT),
- logIndicesExist: rt.boolean,
+ logIndexStatus: logIndexStatusRT,
});
export type LogSourceStatus = rt.TypeOf;
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx
index b2a4ce65ab2b62..fe362dcf8da8c4 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx
@@ -25,7 +25,7 @@ export const StreamPageContent: React.FunctionComponent = () => {
return ;
} else if (hasFailedLoadingSource) {
return ;
- } else if (sourceStatus?.logIndicesExist) {
+ } else if (sourceStatus?.logIndexStatus !== 'missing') {
return ;
} else {
return ;
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx
index 82c21f663bc968..1a1cc135765563 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx
@@ -107,7 +107,7 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
const { sourceStatus } = useLogSourceContext();
// The providers assume the source is loaded, so short-circuit them otherwise
- if (!sourceStatus?.logIndicesExist) {
+ if (sourceStatus?.logIndexStatus === 'missing') {
return <>{children}>;
}
diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts
index 53f7e00a3354c2..3716e618068a39 100644
--- a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts
+++ b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts
@@ -43,7 +43,7 @@ export function getLogsHasDataFetcher(
return async () => {
const [core] = await getStartServices();
const sourceStatus = await callFetchLogSourceStatusAPI(DEFAULT_SOURCE_ID, core.http.fetch);
- return sourceStatus.data.logIndicesExist;
+ return sourceStatus.data.logIndexStatus === 'available';
};
}
diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts
index 6f9e41fbd08f3a..a2b4e162756e31 100644
--- a/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts
+++ b/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts
@@ -3,17 +3,18 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+
+import { CoreStart } from 'kibana/public';
import { coreMock } from 'src/core/public/mocks';
import { dataPluginMock } from 'src/plugins/data/public/mocks';
-import { CoreStart } from 'kibana/public';
-import { getLogsHasDataFetcher } from './logs_overview_fetchers';
-import { InfraClientStartDeps, InfraClientStartExports } from '../types';
import { callFetchLogSourceStatusAPI } from '../containers/logs/log_source/api/fetch_log_source_status';
+import { InfraClientStartDeps, InfraClientStartExports } from '../types';
+import { getLogsHasDataFetcher } from './logs_overview_fetchers';
-// Note
-// Calls to `.mock*` functions will fail the typecheck because how jest does the mocking.
-// The calls will be preluded with a `@ts-expect-error`
jest.mock('../containers/logs/log_source/api/fetch_log_source_status');
+const mockedCallFetchLogSourceStatusAPI = callFetchLogSourceStatusAPI as jest.MockedFunction<
+ typeof callFetchLogSourceStatusAPI
+>;
function setup() {
const core = coreMock.createStart();
@@ -33,36 +34,47 @@ function setup() {
describe('Logs UI Observability Homepage Functions', () => {
describe('getLogsHasDataFetcher()', () => {
beforeEach(() => {
- // @ts-expect-error
- callFetchLogSourceStatusAPI.mockReset();
+ mockedCallFetchLogSourceStatusAPI.mockReset();
});
- it('should return true when some index is present', async () => {
+ it('should return true when non-empty indices exist', async () => {
const { mockedGetStartServices } = setup();
- // @ts-expect-error
- callFetchLogSourceStatusAPI.mockResolvedValue({
- data: { logIndexFields: [], logIndicesExist: true },
+ mockedCallFetchLogSourceStatusAPI.mockResolvedValue({
+ data: { logIndexFields: [], logIndexStatus: 'available' },
});
const hasData = getLogsHasDataFetcher(mockedGetStartServices);
const response = await hasData();
- expect(callFetchLogSourceStatusAPI).toHaveBeenCalledTimes(1);
+ expect(mockedCallFetchLogSourceStatusAPI).toHaveBeenCalledTimes(1);
expect(response).toBe(true);
});
- it('should return false when no index is present', async () => {
+ it('should return false when only empty indices exist', async () => {
+ const { mockedGetStartServices } = setup();
+
+ mockedCallFetchLogSourceStatusAPI.mockResolvedValue({
+ data: { logIndexFields: [], logIndexStatus: 'empty' },
+ });
+
+ const hasData = getLogsHasDataFetcher(mockedGetStartServices);
+ const response = await hasData();
+
+ expect(mockedCallFetchLogSourceStatusAPI).toHaveBeenCalledTimes(1);
+ expect(response).toBe(false);
+ });
+
+ it('should return false when no index exists', async () => {
const { mockedGetStartServices } = setup();
- // @ts-expect-error
- callFetchLogSourceStatusAPI.mockResolvedValue({
- data: { logIndexFields: [], logIndicesExist: false },
+ mockedCallFetchLogSourceStatusAPI.mockResolvedValue({
+ data: { logIndexFields: [], logIndexStatus: 'missing' },
});
const hasData = getLogsHasDataFetcher(mockedGetStartServices);
const response = await hasData();
- expect(callFetchLogSourceStatusAPI).toHaveBeenCalledTimes(1);
+ expect(mockedCallFetchLogSourceStatusAPI).toHaveBeenCalledTimes(1);
expect(response).toBe(false);
});
});
diff --git a/x-pack/plugins/infra/server/graphql/source_status/resolvers.ts b/x-pack/plugins/infra/server/graphql/source_status/resolvers.ts
index 848d66058e64c8..bd92dd0f7da8be 100644
--- a/x-pack/plugins/infra/server/graphql/source_status/resolvers.ts
+++ b/x-pack/plugins/infra/server/graphql/source_status/resolvers.ts
@@ -73,7 +73,7 @@ export const createSourceStatusResolvers = (libs: {
return await libs.sourceStatus.hasLogAlias(req, source.id);
},
async logIndicesExist(source, args, { req }) {
- return await libs.sourceStatus.hasLogIndices(req, source.id);
+ return (await libs.sourceStatus.getLogIndexStatus(req, source.id)) !== 'missing';
},
async logIndices(source, args, { req }) {
return await libs.sourceStatus.getLogIndexNames(req, source.id);
diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
index 018e5098a42911..117749ae87bbed 100644
--- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
@@ -38,6 +38,7 @@ export interface CallWithRequestParams extends GenericParams {
fields?: string | string[];
path?: string;
query?: string | object;
+ track_total_hits?: boolean | number;
}
export type InfraResponse = Lifecycle.ReturnValue;
diff --git a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
index 9bc58604f12a5f..2a61e64c94fcde 100644
--- a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
@@ -5,7 +5,7 @@
*/
import { RequestHandlerContext } from 'src/core/server';
-import { InfraSourceStatusAdapter } from '../../source_status';
+import { InfraSourceStatusAdapter, SourceIndexStatus } from '../../source_status';
import { InfraDatabaseGetIndicesResponse } from '../framework';
import { KibanaFramework } from '../framework/kibana_framework_adapter';
@@ -40,7 +40,10 @@ export class InfraElasticsearchSourceStatusAdapter implements InfraSourceStatusA
});
}
- public async hasIndices(requestContext: RequestHandlerContext, indexNames: string) {
+ public async getIndexStatus(
+ requestContext: RequestHandlerContext,
+ indexNames: string
+ ): Promise {
return await this.framework
.callWithRequest(requestContext, 'search', {
ignore_unavailable: true,
@@ -48,12 +51,23 @@ export class InfraElasticsearchSourceStatusAdapter implements InfraSourceStatusA
index: indexNames,
size: 0,
terminate_after: 1,
+ track_total_hits: 1,
})
.then(
- (response) => response._shards.total > 0,
+ (response) => {
+ if (response._shards.total <= 0) {
+ return 'missing';
+ }
+
+ if (response.hits.total.value > 0) {
+ return 'available';
+ }
+
+ return 'empty';
+ },
(err) => {
if (err.status === 404) {
- return false;
+ return 'missing';
}
throw err;
}
diff --git a/x-pack/plugins/infra/server/lib/source_status.ts b/x-pack/plugins/infra/server/lib/source_status.ts
index 9bb953845e5a17..c383d019335625 100644
--- a/x-pack/plugins/infra/server/lib/source_status.ts
+++ b/x-pack/plugins/infra/server/lib/source_status.ts
@@ -69,19 +69,19 @@ export class InfraSourceStatus {
);
return hasAlias;
}
- public async hasLogIndices(
+ public async getLogIndexStatus(
requestContext: RequestHandlerContext,
sourceId: string
- ): Promise {
+ ): Promise {
const sourceConfiguration = await this.libs.sources.getSourceConfiguration(
requestContext.core.savedObjects.client,
sourceId
);
- const hasIndices = await this.adapter.hasIndices(
+ const indexStatus = await this.adapter.getIndexStatus(
requestContext,
sourceConfiguration.configuration.logAlias
);
- return hasIndices;
+ return indexStatus;
}
public async hasMetricIndices(
requestContext: RequestHandlerContext,
@@ -91,16 +91,21 @@ export class InfraSourceStatus {
requestContext.core.savedObjects.client,
sourceId
);
- const hasIndices = await this.adapter.hasIndices(
+ const indexStatus = await this.adapter.getIndexStatus(
requestContext,
sourceConfiguration.configuration.metricAlias
);
- return hasIndices;
+ return indexStatus !== 'missing';
}
}
+export type SourceIndexStatus = 'missing' | 'empty' | 'available';
+
export interface InfraSourceStatusAdapter {
getIndexNames(requestContext: RequestHandlerContext, aliasName: string): Promise;
hasAlias(requestContext: RequestHandlerContext, aliasName: string): Promise;
- hasIndices(requestContext: RequestHandlerContext, indexNames: string): Promise;
+ getIndexStatus(
+ requestContext: RequestHandlerContext,
+ indexNames: string
+ ): Promise;
}
diff --git a/x-pack/plugins/infra/server/routes/log_sources/status.ts b/x-pack/plugins/infra/server/routes/log_sources/status.ts
index 4cd85ecfe23c18..193c3541d740bc 100644
--- a/x-pack/plugins/infra/server/routes/log_sources/status.ts
+++ b/x-pack/plugins/infra/server/routes/log_sources/status.ts
@@ -31,16 +31,17 @@ export const initLogSourceStatusRoutes = ({
const { sourceId } = request.params;
try {
- const logIndicesExist = await sourceStatus.hasLogIndices(requestContext, sourceId);
- const logIndexFields = logIndicesExist
- ? await fields.getFields(requestContext, sourceId, InfraIndexType.LOGS)
- : [];
+ const logIndexStatus = await sourceStatus.getLogIndexStatus(requestContext, sourceId);
+ const logIndexFields =
+ logIndexStatus !== 'missing'
+ ? await fields.getFields(requestContext, sourceId, InfraIndexType.LOGS)
+ : [];
return response.ok({
body: getLogSourceStatusSuccessResponsePayloadRT.encode({
data: {
- logIndicesExist,
logIndexFields,
+ logIndexStatus,
},
}),
});
diff --git a/x-pack/plugins/infra/server/routes/source/index.ts b/x-pack/plugins/infra/server/routes/source/index.ts
index 2843897071e191..6069d3a35e54ce 100644
--- a/x-pack/plugins/infra/server/routes/source/index.ts
+++ b/x-pack/plugins/infra/server/routes/source/index.ts
@@ -37,9 +37,9 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
try {
const { type, sourceId } = request.params;
- const [source, logIndicesExist, metricIndicesExist, indexFields] = await Promise.all([
+ const [source, logIndexStatus, metricIndicesExist, indexFields] = await Promise.all([
libs.sources.getSourceConfiguration(requestContext.core.savedObjects.client, sourceId),
- libs.sourceStatus.hasLogIndices(requestContext, sourceId),
+ libs.sourceStatus.getLogIndexStatus(requestContext, sourceId),
libs.sourceStatus.hasMetricIndices(requestContext, sourceId),
libs.fields.getFields(requestContext, sourceId, typeToInfraIndexType(type)),
]);
@@ -49,7 +49,7 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
}
const status = {
- logIndicesExist,
+ logIndicesExist: logIndexStatus !== 'missing',
metricIndicesExist,
indexFields,
};
@@ -83,7 +83,7 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
const hasData =
type === 'metrics'
? await libs.sourceStatus.hasMetricIndices(requestContext, sourceId)
- : await libs.sourceStatus.hasLogIndices(requestContext, sourceId);
+ : (await libs.sourceStatus.getLogIndexStatus(requestContext, sourceId)) !== 'missing';
return response.ok({
body: { hasData },
From 4ede075681fae19701b3d0b004a74b1193493f7a Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Tue, 28 Jul 2020 17:51:23 +0300
Subject: [PATCH 041/102] [KP] fix doc generation for platform code (#73407)
* fix doc generation for platform code
* terminate process if type build failed
* update types
---
...lugin-core-server.countresponse._shards.md | 11 ++
...-plugin-core-server.countresponse.count.md | 11 ++
...kibana-plugin-core-server.countresponse.md | 20 +++
...-core-server.deletedocumentresponse._id.md | 11 ++
...re-server.deletedocumentresponse._index.md | 11 ++
...e-server.deletedocumentresponse._shards.md | 11 ++
...ore-server.deletedocumentresponse._type.md | 11 ++
...-server.deletedocumentresponse._version.md | 11 ++
...ore-server.deletedocumentresponse.error.md | 13 ++
...ore-server.deletedocumentresponse.found.md | 11 ++
...ugin-core-server.deletedocumentresponse.md | 26 ++++
...re-server.deletedocumentresponse.result.md | 11 ++
...-plugin-core-server.elasticsearchclient.md | 17 +++
...gin-core-server.explanation.description.md | 11 ++
...-plugin-core-server.explanation.details.md | 11 ++
.../kibana-plugin-core-server.explanation.md | 21 +++
...na-plugin-core-server.explanation.value.md | 11 ++
...bana-plugin-core-server.getresponse._id.md | 11 ++
...a-plugin-core-server.getresponse._index.md | 11 ++
...n-core-server.getresponse._primary_term.md | 11 ++
...plugin-core-server.getresponse._routing.md | 11 ++
...-plugin-core-server.getresponse._seq_no.md | 11 ++
...-plugin-core-server.getresponse._source.md | 11 ++
...na-plugin-core-server.getresponse._type.md | 11 ++
...plugin-core-server.getresponse._version.md | 11 ++
...na-plugin-core-server.getresponse.found.md | 11 ++
.../kibana-plugin-core-server.getresponse.md | 27 ++++
.../core/server/kibana-plugin-core-server.md | 8 +
...er.savedobjectsdeletebynamespaceoptions.md | 2 +-
...objectsdeletebynamespaceoptions.refresh.md | 4 +-
...n-core-server.searchresponse._scroll_id.md | 11 ++
...ugin-core-server.searchresponse._shards.md | 11 ++
...core-server.searchresponse.aggregations.md | 11 ++
...-plugin-core-server.searchresponse.hits.md | 28 ++++
...ibana-plugin-core-server.searchresponse.md | 24 +++
...in-core-server.searchresponse.timed_out.md | 11 ++
...-plugin-core-server.searchresponse.took.md | 11 ++
...na-plugin-core-server.shardsinfo.failed.md | 11 ++
.../kibana-plugin-core-server.shardsinfo.md | 22 +++
...a-plugin-core-server.shardsinfo.skipped.md | 11 ++
...lugin-core-server.shardsinfo.successful.md | 11 ++
...ana-plugin-core-server.shardsinfo.total.md | 11 ++
...lugin-core-server.shardsresponse.failed.md | 11 ++
...ibana-plugin-core-server.shardsresponse.md | 22 +++
...ugin-core-server.shardsresponse.skipped.md | 11 ++
...n-core-server.shardsresponse.successful.md | 11 ++
...plugin-core-server.shardsresponse.total.md | 11 ++
...plugin-plugins-data-server.isearchsetup.md | 2 +-
src/core/public/public.api.md | 122 +--------------
src/core/server/elasticsearch/client/types.ts | 32 +++-
src/core/server/elasticsearch/index.ts | 6 +-
src/core/server/index.ts | 7 +
src/core/server/server.api.md | 142 +++++++++++++++++-
src/dev/run_check_published_api_changes.ts | 47 +++---
src/plugins/data/public/public.api.md | 120 +--------------
src/plugins/data/server/server.api.md | 5 +
56 files changed, 811 insertions(+), 280 deletions(-)
create mode 100644 docs/development/core/server/kibana-plugin-core-server.countresponse._shards.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.countresponse.count.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.countresponse.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._id.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._index.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._shards.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._type.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._version.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.error.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.found.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.result.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.explanation.description.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.explanation.details.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.explanation.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.explanation.value.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._id.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._index.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._primary_term.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._routing.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._seq_no.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._source.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._type.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse._version.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse.found.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.getresponse.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse._scroll_id.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse._shards.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse.aggregations.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse.hits.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse.timed_out.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.searchresponse.took.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsinfo.failed.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsinfo.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsinfo.skipped.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsinfo.successful.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsinfo.total.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsresponse.failed.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsresponse.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsresponse.skipped.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsresponse.successful.md
create mode 100644 docs/development/core/server/kibana-plugin-core-server.shardsresponse.total.md
diff --git a/docs/development/core/server/kibana-plugin-core-server.countresponse._shards.md b/docs/development/core/server/kibana-plugin-core-server.countresponse._shards.md
new file mode 100644
index 00000000000000..0f31a554e22088
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.countresponse._shards.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CountResponse](./kibana-plugin-core-server.countresponse.md) > [\_shards](./kibana-plugin-core-server.countresponse._shards.md)
+
+## CountResponse.\_shards property
+
+Signature:
+
+```typescript
+_shards: ShardsInfo;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.countresponse.count.md b/docs/development/core/server/kibana-plugin-core-server.countresponse.count.md
new file mode 100644
index 00000000000000..3cd1a6aaf66444
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.countresponse.count.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CountResponse](./kibana-plugin-core-server.countresponse.md) > [count](./kibana-plugin-core-server.countresponse.count.md)
+
+## CountResponse.count property
+
+Signature:
+
+```typescript
+count: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.countresponse.md b/docs/development/core/server/kibana-plugin-core-server.countresponse.md
new file mode 100644
index 00000000000000..f8664f4878f46a
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.countresponse.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CountResponse](./kibana-plugin-core-server.countresponse.md)
+
+## CountResponse interface
+
+
+Signature:
+
+```typescript
+export interface CountResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [\_shards](./kibana-plugin-core-server.countresponse._shards.md) | ShardsInfo
| |
+| [count](./kibana-plugin-core-server.countresponse.count.md) | number
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._id.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._id.md
new file mode 100644
index 00000000000000..ccc6a76361f260
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._id.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [\_id](./kibana-plugin-core-server.deletedocumentresponse._id.md)
+
+## DeleteDocumentResponse.\_id property
+
+Signature:
+
+```typescript
+_id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._index.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._index.md
new file mode 100644
index 00000000000000..a9a04bb2b2ed73
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._index.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [\_index](./kibana-plugin-core-server.deletedocumentresponse._index.md)
+
+## DeleteDocumentResponse.\_index property
+
+Signature:
+
+```typescript
+_index: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._shards.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._shards.md
new file mode 100644
index 00000000000000..e3d5e9208db0a4
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._shards.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [\_shards](./kibana-plugin-core-server.deletedocumentresponse._shards.md)
+
+## DeleteDocumentResponse.\_shards property
+
+Signature:
+
+```typescript
+_shards: ShardsResponse;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._type.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._type.md
new file mode 100644
index 00000000000000..690852e20a76e0
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [\_type](./kibana-plugin-core-server.deletedocumentresponse._type.md)
+
+## DeleteDocumentResponse.\_type property
+
+Signature:
+
+```typescript
+_type: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._version.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._version.md
new file mode 100644
index 00000000000000..acfe8ef55ae717
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse._version.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [\_version](./kibana-plugin-core-server.deletedocumentresponse._version.md)
+
+## DeleteDocumentResponse.\_version property
+
+Signature:
+
+```typescript
+_version: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.error.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.error.md
new file mode 100644
index 00000000000000..aafe8501889980
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.error.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [error](./kibana-plugin-core-server.deletedocumentresponse.error.md)
+
+## DeleteDocumentResponse.error property
+
+Signature:
+
+```typescript
+error?: {
+ type: string;
+ };
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.found.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.found.md
new file mode 100644
index 00000000000000..00bc89bda66ed3
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.found.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [found](./kibana-plugin-core-server.deletedocumentresponse.found.md)
+
+## DeleteDocumentResponse.found property
+
+Signature:
+
+```typescript
+found: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.md
new file mode 100644
index 00000000000000..e8ac7d2fd8ec1c
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.md
@@ -0,0 +1,26 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md)
+
+## DeleteDocumentResponse interface
+
+
+Signature:
+
+```typescript
+export interface DeleteDocumentResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [\_id](./kibana-plugin-core-server.deletedocumentresponse._id.md) | string
| |
+| [\_index](./kibana-plugin-core-server.deletedocumentresponse._index.md) | string
| |
+| [\_shards](./kibana-plugin-core-server.deletedocumentresponse._shards.md) | ShardsResponse
| |
+| [\_type](./kibana-plugin-core-server.deletedocumentresponse._type.md) | string
| |
+| [\_version](./kibana-plugin-core-server.deletedocumentresponse._version.md) | number
| |
+| [error](./kibana-plugin-core-server.deletedocumentresponse.error.md) | {
type: string;
}
| |
+| [found](./kibana-plugin-core-server.deletedocumentresponse.found.md) | boolean
| |
+| [result](./kibana-plugin-core-server.deletedocumentresponse.result.md) | string
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.result.md b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.result.md
new file mode 100644
index 00000000000000..88f7568d3d9bc7
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.deletedocumentresponse.result.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) > [result](./kibana-plugin-core-server.deletedocumentresponse.result.md)
+
+## DeleteDocumentResponse.result property
+
+Signature:
+
+```typescript
+result: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md
new file mode 100644
index 00000000000000..279262aa6a5ec2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md)
+
+## ElasticsearchClient type
+
+Client used to query the elasticsearch cluster.
+
+Signature:
+
+```typescript
+export declare type ElasticsearchClient = Omit & {
+ transport: {
+ request(params: TransportRequestParams, options?: TransportRequestOptions): TransportRequestPromise;
+ };
+};
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.explanation.description.md b/docs/development/core/server/kibana-plugin-core-server.explanation.description.md
new file mode 100644
index 00000000000000..37fc90f5ac5d87
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.explanation.description.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Explanation](./kibana-plugin-core-server.explanation.md) > [description](./kibana-plugin-core-server.explanation.description.md)
+
+## Explanation.description property
+
+Signature:
+
+```typescript
+description: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.explanation.details.md b/docs/development/core/server/kibana-plugin-core-server.explanation.details.md
new file mode 100644
index 00000000000000..afba9175d86cfa
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.explanation.details.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Explanation](./kibana-plugin-core-server.explanation.md) > [details](./kibana-plugin-core-server.explanation.details.md)
+
+## Explanation.details property
+
+Signature:
+
+```typescript
+details: Explanation[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.explanation.md b/docs/development/core/server/kibana-plugin-core-server.explanation.md
new file mode 100644
index 00000000000000..eb18910c4795b1
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.explanation.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Explanation](./kibana-plugin-core-server.explanation.md)
+
+## Explanation interface
+
+
+Signature:
+
+```typescript
+export interface Explanation
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [description](./kibana-plugin-core-server.explanation.description.md) | string
| |
+| [details](./kibana-plugin-core-server.explanation.details.md) | Explanation[]
| |
+| [value](./kibana-plugin-core-server.explanation.value.md) | number
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.explanation.value.md b/docs/development/core/server/kibana-plugin-core-server.explanation.value.md
new file mode 100644
index 00000000000000..b10f60176b0c80
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.explanation.value.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Explanation](./kibana-plugin-core-server.explanation.md) > [value](./kibana-plugin-core-server.explanation.value.md)
+
+## Explanation.value property
+
+Signature:
+
+```typescript
+value: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._id.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._id.md
new file mode 100644
index 00000000000000..d31b61f3962c80
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._id.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_id](./kibana-plugin-core-server.getresponse._id.md)
+
+## GetResponse.\_id property
+
+Signature:
+
+```typescript
+_id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._index.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._index.md
new file mode 100644
index 00000000000000..0353ec1a17b2c5
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._index.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_index](./kibana-plugin-core-server.getresponse._index.md)
+
+## GetResponse.\_index property
+
+Signature:
+
+```typescript
+_index: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._primary_term.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._primary_term.md
new file mode 100644
index 00000000000000..8412302ab727d2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._primary_term.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_primary\_term](./kibana-plugin-core-server.getresponse._primary_term.md)
+
+## GetResponse.\_primary\_term property
+
+Signature:
+
+```typescript
+_primary_term: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._routing.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._routing.md
new file mode 100644
index 00000000000000..1af3ed31ee1120
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._routing.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_routing](./kibana-plugin-core-server.getresponse._routing.md)
+
+## GetResponse.\_routing property
+
+Signature:
+
+```typescript
+_routing?: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._seq_no.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._seq_no.md
new file mode 100644
index 00000000000000..e8d72563f81496
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._seq_no.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_seq\_no](./kibana-plugin-core-server.getresponse._seq_no.md)
+
+## GetResponse.\_seq\_no property
+
+Signature:
+
+```typescript
+_seq_no: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._source.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._source.md
new file mode 100644
index 00000000000000..97aacb42992a31
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._source.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_source](./kibana-plugin-core-server.getresponse._source.md)
+
+## GetResponse.\_source property
+
+Signature:
+
+```typescript
+_source: T;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._type.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._type.md
new file mode 100644
index 00000000000000..b3205e2fe91d77
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_type](./kibana-plugin-core-server.getresponse._type.md)
+
+## GetResponse.\_type property
+
+Signature:
+
+```typescript
+_type: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse._version.md b/docs/development/core/server/kibana-plugin-core-server.getresponse._version.md
new file mode 100644
index 00000000000000..23d3a8c91f4a20
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse._version.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [\_version](./kibana-plugin-core-server.getresponse._version.md)
+
+## GetResponse.\_version property
+
+Signature:
+
+```typescript
+_version: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse.found.md b/docs/development/core/server/kibana-plugin-core-server.getresponse.found.md
new file mode 100644
index 00000000000000..8d34a3e743ccab
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse.found.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md) > [found](./kibana-plugin-core-server.getresponse.found.md)
+
+## GetResponse.found property
+
+Signature:
+
+```typescript
+found: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.getresponse.md b/docs/development/core/server/kibana-plugin-core-server.getresponse.md
new file mode 100644
index 00000000000000..bab3092c6b1fa5
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.getresponse.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [GetResponse](./kibana-plugin-core-server.getresponse.md)
+
+## GetResponse interface
+
+
+Signature:
+
+```typescript
+export interface GetResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [\_id](./kibana-plugin-core-server.getresponse._id.md) | string
| |
+| [\_index](./kibana-plugin-core-server.getresponse._index.md) | string
| |
+| [\_primary\_term](./kibana-plugin-core-server.getresponse._primary_term.md) | number
| |
+| [\_routing](./kibana-plugin-core-server.getresponse._routing.md) | string
| |
+| [\_seq\_no](./kibana-plugin-core-server.getresponse._seq_no.md) | number
| |
+| [\_source](./kibana-plugin-core-server.getresponse._source.md) | T
| |
+| [\_type](./kibana-plugin-core-server.getresponse._type.md) | string
| |
+| [\_version](./kibana-plugin-core-server.getresponse._version.md) | number
| |
+| [found](./kibana-plugin-core-server.getresponse.found.md) | boolean
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md
index 61ffc532f0de5b..95b7627398b45e 100644
--- a/docs/development/core/server/kibana-plugin-core-server.md
+++ b/docs/development/core/server/kibana-plugin-core-server.md
@@ -74,7 +74,9 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the plugins setup
method. |
| [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins start
method. |
| [CoreStatus](./kibana-plugin-core-server.corestatus.md) | Status of core services. |
+| [CountResponse](./kibana-plugin-core-server.countresponse.md) | |
| [CustomHttpResponseOptions](./kibana-plugin-core-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. |
+| [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) | |
| [DeprecationAPIClientParams](./kibana-plugin-core-server.deprecationapiclientparams.md) | |
| [DeprecationAPIResponse](./kibana-plugin-core-server.deprecationapiresponse.md) | |
| [DeprecationInfo](./kibana-plugin-core-server.deprecationinfo.md) | |
@@ -85,7 +87,9 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | |
| [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | |
| [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters |
+| [Explanation](./kibana-plugin-core-server.explanation.md) | |
| [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. |
+| [GetResponse](./kibana-plugin-core-server.getresponse.md) | |
| [HttpAuth](./kibana-plugin-core-server.httpauth.md) | |
| [HttpResources](./kibana-plugin-core-server.httpresources.md) | HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file. |
| [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) | Allows to configure HTTP response parameters |
@@ -189,11 +193,14 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectsTypeMappingDefinition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) | Describe a saved object type mapping. |
| [SavedObjectsUpdateOptions](./kibana-plugin-core-server.savedobjectsupdateoptions.md) | |
| [SavedObjectsUpdateResponse](./kibana-plugin-core-server.savedobjectsupdateresponse.md) | |
+| [SearchResponse](./kibana-plugin-core-server.searchresponse.md) | |
| [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) | The current status of a service at a point in time. |
| [SessionCookieValidationResult](./kibana-plugin-core-server.sessioncookievalidationresult.md) | Return type from a function to validate cookie contents. |
| [SessionStorage](./kibana-plugin-core-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. |
| [SessionStorageCookieOptions](./kibana-plugin-core-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. |
| [SessionStorageFactory](./kibana-plugin-core-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request |
+| [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md) | |
+| [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md) | |
| [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. |
| [StringValidationRegex](./kibana-plugin-core-server.stringvalidationregex.md) | StringValidation with regex object |
| [StringValidationRegexString](./kibana-plugin-core-server.stringvalidationregexstring.md) | StringValidation as regex string |
@@ -228,6 +235,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) | A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md).See [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) for more usage examples. |
| [ConfigPath](./kibana-plugin-core-server.configpath.md) | |
| [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. |
+| [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md) | Client used to query the elasticsearch cluster. |
| [Freezable](./kibana-plugin-core-server.freezable.md) | |
| [GetAuthHeaders](./kibana-plugin-core-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. |
| [GetAuthState](./kibana-plugin-core-server.getauthstate.md) | Gets authentication state for a request. Returned by auth
interceptor. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md
index 3fac0d889c6ce2..ba81a3e8c32d0b 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md
@@ -15,5 +15,5 @@ export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOp
| Property | Type | Description |
| --- | --- | --- |
-| [refresh](./kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md) | MutatingOperationRefreshSetting
| The Elasticsearch Refresh setting for this operation |
+| [refresh](./kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md) | boolean
| The Elasticsearch supports only boolean flag for this operation |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md
index c67866a5553a07..52b562e8e22b7c 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.refresh.md
@@ -4,10 +4,10 @@
## SavedObjectsDeleteByNamespaceOptions.refresh property
-The Elasticsearch Refresh setting for this operation
+The Elasticsearch supports only boolean flag for this operation
Signature:
```typescript
-refresh?: MutatingOperationRefreshSetting;
+refresh?: boolean;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse._scroll_id.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse._scroll_id.md
new file mode 100644
index 00000000000000..a9dd0e76475fd9
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse._scroll_id.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [\_scroll\_id](./kibana-plugin-core-server.searchresponse._scroll_id.md)
+
+## SearchResponse.\_scroll\_id property
+
+Signature:
+
+```typescript
+_scroll_id?: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse._shards.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse._shards.md
new file mode 100644
index 00000000000000..e090ad20e8bc83
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse._shards.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [\_shards](./kibana-plugin-core-server.searchresponse._shards.md)
+
+## SearchResponse.\_shards property
+
+Signature:
+
+```typescript
+_shards: ShardsResponse;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse.aggregations.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse.aggregations.md
new file mode 100644
index 00000000000000..686e6f2aa05e93
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse.aggregations.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [aggregations](./kibana-plugin-core-server.searchresponse.aggregations.md)
+
+## SearchResponse.aggregations property
+
+Signature:
+
+```typescript
+aggregations?: any;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse.hits.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse.hits.md
new file mode 100644
index 00000000000000..1629e774255252
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse.hits.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [hits](./kibana-plugin-core-server.searchresponse.hits.md)
+
+## SearchResponse.hits property
+
+Signature:
+
+```typescript
+hits: {
+ total: number;
+ max_score: number;
+ hits: Array<{
+ _index: string;
+ _type: string;
+ _id: string;
+ _score: number;
+ _source: T;
+ _version?: number;
+ _explanation?: Explanation;
+ fields?: any;
+ highlight?: any;
+ inner_hits?: any;
+ matched_queries?: string[];
+ sort?: string[];
+ }>;
+ };
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse.md
new file mode 100644
index 00000000000000..b53cbf0d87f240
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md)
+
+## SearchResponse interface
+
+
+Signature:
+
+```typescript
+export interface SearchResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [\_scroll\_id](./kibana-plugin-core-server.searchresponse._scroll_id.md) | string
| |
+| [\_shards](./kibana-plugin-core-server.searchresponse._shards.md) | ShardsResponse
| |
+| [aggregations](./kibana-plugin-core-server.searchresponse.aggregations.md) | any
| |
+| [hits](./kibana-plugin-core-server.searchresponse.hits.md) | {
total: number;
max_score: number;
hits: Array<{
_index: string;
_type: string;
_id: string;
_score: number;
_source: T;
_version?: number;
_explanation?: Explanation;
fields?: any;
highlight?: any;
inner_hits?: any;
matched_queries?: string[];
sort?: string[];
}>;
}
| |
+| [timed\_out](./kibana-plugin-core-server.searchresponse.timed_out.md) | boolean
| |
+| [took](./kibana-plugin-core-server.searchresponse.took.md) | number
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse.timed_out.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse.timed_out.md
new file mode 100644
index 00000000000000..a3488117cd8748
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse.timed_out.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [timed\_out](./kibana-plugin-core-server.searchresponse.timed_out.md)
+
+## SearchResponse.timed\_out property
+
+Signature:
+
+```typescript
+timed_out: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.searchresponse.took.md b/docs/development/core/server/kibana-plugin-core-server.searchresponse.took.md
new file mode 100644
index 00000000000000..8c9c0b0f7c4201
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.searchresponse.took.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SearchResponse](./kibana-plugin-core-server.searchresponse.md) > [took](./kibana-plugin-core-server.searchresponse.took.md)
+
+## SearchResponse.took property
+
+Signature:
+
+```typescript
+took: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsinfo.failed.md b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.failed.md
new file mode 100644
index 00000000000000..a47fc1263be416
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.failed.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md) > [failed](./kibana-plugin-core-server.shardsinfo.failed.md)
+
+## ShardsInfo.failed property
+
+Signature:
+
+```typescript
+failed: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsinfo.md b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.md
new file mode 100644
index 00000000000000..9eafe3792c14ac
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md)
+
+## ShardsInfo interface
+
+
+Signature:
+
+```typescript
+export interface ShardsInfo
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [failed](./kibana-plugin-core-server.shardsinfo.failed.md) | number
| |
+| [skipped](./kibana-plugin-core-server.shardsinfo.skipped.md) | number
| |
+| [successful](./kibana-plugin-core-server.shardsinfo.successful.md) | number
| |
+| [total](./kibana-plugin-core-server.shardsinfo.total.md) | number
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsinfo.skipped.md b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.skipped.md
new file mode 100644
index 00000000000000..0c87831edd6ca2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.skipped.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md) > [skipped](./kibana-plugin-core-server.shardsinfo.skipped.md)
+
+## ShardsInfo.skipped property
+
+Signature:
+
+```typescript
+skipped: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsinfo.successful.md b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.successful.md
new file mode 100644
index 00000000000000..c927adb39932a4
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.successful.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md) > [successful](./kibana-plugin-core-server.shardsinfo.successful.md)
+
+## ShardsInfo.successful property
+
+Signature:
+
+```typescript
+successful: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsinfo.total.md b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.total.md
new file mode 100644
index 00000000000000..820c8a70fd222f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsinfo.total.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsInfo](./kibana-plugin-core-server.shardsinfo.md) > [total](./kibana-plugin-core-server.shardsinfo.total.md)
+
+## ShardsInfo.total property
+
+Signature:
+
+```typescript
+total: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsresponse.failed.md b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.failed.md
new file mode 100644
index 00000000000000..7f7a173af2e589
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.failed.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md) > [failed](./kibana-plugin-core-server.shardsresponse.failed.md)
+
+## ShardsResponse.failed property
+
+Signature:
+
+```typescript
+failed: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsresponse.md b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.md
new file mode 100644
index 00000000000000..722ffd8efdb571
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md)
+
+## ShardsResponse interface
+
+
+Signature:
+
+```typescript
+export interface ShardsResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [failed](./kibana-plugin-core-server.shardsresponse.failed.md) | number
| |
+| [skipped](./kibana-plugin-core-server.shardsresponse.skipped.md) | number
| |
+| [successful](./kibana-plugin-core-server.shardsresponse.successful.md) | number
| |
+| [total](./kibana-plugin-core-server.shardsresponse.total.md) | number
| |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsresponse.skipped.md b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.skipped.md
new file mode 100644
index 00000000000000..b01c3501fe0224
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.skipped.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md) > [skipped](./kibana-plugin-core-server.shardsresponse.skipped.md)
+
+## ShardsResponse.skipped property
+
+Signature:
+
+```typescript
+skipped: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsresponse.successful.md b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.successful.md
new file mode 100644
index 00000000000000..23c6ff0519ed78
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.successful.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md) > [successful](./kibana-plugin-core-server.shardsresponse.successful.md)
+
+## ShardsResponse.successful property
+
+Signature:
+
+```typescript
+successful: number;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.shardsresponse.total.md b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.total.md
new file mode 100644
index 00000000000000..e669f6216a10fc
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.shardsresponse.total.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ShardsResponse](./kibana-plugin-core-server.shardsresponse.md) > [total](./kibana-plugin-core-server.shardsresponse.total.md)
+
+## ShardsResponse.total property
+
+Signature:
+
+```typescript
+total: number;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
index 3afba80064f084..d9749bc44f45a8 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
@@ -14,6 +14,6 @@ export interface ISearchSetup
| Property | Type | Description |
| --- | --- | --- |
-| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | TRegisterSearchStrategy
| Extension point exposed for other plugins to register their own search strategies. |
+| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | (name: string, strategy: ISearchStrategy) => void
| Extension point exposed for other plugins to register their own search strategies. |
| [usage](./kibana-plugin-plugins-data-server.isearchsetup.usage.md) | SearchUsage
| Used internally for telemetry |
diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md
index c811209dfa80fd..9b421e0172df0c 100644
--- a/src/core/public/public.api.md
+++ b/src/core/public/public.api.md
@@ -5,147 +5,35 @@
```ts
import { Action } from 'history';
+import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
import Boom from 'boom';
-import { BulkIndexDocumentsParams } from 'elasticsearch';
-import { CatAliasesParams } from 'elasticsearch';
-import { CatAllocationParams } from 'elasticsearch';
-import { CatCommonParams } from 'elasticsearch';
-import { CatFielddataParams } from 'elasticsearch';
-import { CatHealthParams } from 'elasticsearch';
-import { CatHelpParams } from 'elasticsearch';
-import { CatIndicesParams } from 'elasticsearch';
-import { CatRecoveryParams } from 'elasticsearch';
-import { CatSegmentsParams } from 'elasticsearch';
-import { CatShardsParams } from 'elasticsearch';
-import { CatSnapshotsParams } from 'elasticsearch';
-import { CatTasksParams } from 'elasticsearch';
-import { CatThreadPoolParams } from 'elasticsearch';
-import { ClearScrollParams } from 'elasticsearch';
-import { Client } from 'elasticsearch';
-import { ClusterAllocationExplainParams } from 'elasticsearch';
-import { ClusterGetSettingsParams } from 'elasticsearch';
-import { ClusterHealthParams } from 'elasticsearch';
-import { ClusterPendingTasksParams } from 'elasticsearch';
-import { ClusterPutSettingsParams } from 'elasticsearch';
-import { ClusterRerouteParams } from 'elasticsearch';
-import { ClusterStateParams } from 'elasticsearch';
-import { ClusterStatsParams } from 'elasticsearch';
-import { CountParams } from 'elasticsearch';
-import { CreateDocumentParams } from 'elasticsearch';
-import { DeleteDocumentByQueryParams } from 'elasticsearch';
-import { DeleteDocumentParams } from 'elasticsearch';
-import { DeleteScriptParams } from 'elasticsearch';
-import { DeleteTemplateParams } from 'elasticsearch';
import { EuiBreadcrumb } from '@elastic/eui';
import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { ExclusiveUnion } from '@elastic/eui';
-import { ExistsParams } from 'elasticsearch';
-import { ExplainParams } from 'elasticsearch';
-import { FieldStatsParams } from 'elasticsearch';
-import { GenericParams } from 'elasticsearch';
-import { GetParams } from 'elasticsearch';
-import { GetResponse } from 'elasticsearch';
-import { GetScriptParams } from 'elasticsearch';
-import { GetSourceParams } from 'elasticsearch';
-import { GetTemplateParams } from 'elasticsearch';
import { History } from 'history';
import { Href } from 'history';
import { IconType } from '@elastic/eui';
-import { IndexDocumentParams } from 'elasticsearch';
-import { IndicesAnalyzeParams } from 'elasticsearch';
-import { IndicesClearCacheParams } from 'elasticsearch';
-import { IndicesCloseParams } from 'elasticsearch';
-import { IndicesCreateParams } from 'elasticsearch';
-import { IndicesDeleteAliasParams } from 'elasticsearch';
-import { IndicesDeleteParams } from 'elasticsearch';
-import { IndicesDeleteTemplateParams } from 'elasticsearch';
-import { IndicesExistsAliasParams } from 'elasticsearch';
-import { IndicesExistsParams } from 'elasticsearch';
-import { IndicesExistsTemplateParams } from 'elasticsearch';
-import { IndicesExistsTypeParams } from 'elasticsearch';
-import { IndicesFlushParams } from 'elasticsearch';
-import { IndicesFlushSyncedParams } from 'elasticsearch';
-import { IndicesForcemergeParams } from 'elasticsearch';
-import { IndicesGetAliasParams } from 'elasticsearch';
-import { IndicesGetFieldMappingParams } from 'elasticsearch';
-import { IndicesGetMappingParams } from 'elasticsearch';
-import { IndicesGetParams } from 'elasticsearch';
-import { IndicesGetSettingsParams } from 'elasticsearch';
-import { IndicesGetTemplateParams } from 'elasticsearch';
-import { IndicesGetUpgradeParams } from 'elasticsearch';
-import { IndicesOpenParams } from 'elasticsearch';
-import { IndicesPutAliasParams } from 'elasticsearch';
-import { IndicesPutMappingParams } from 'elasticsearch';
-import { IndicesPutSettingsParams } from 'elasticsearch';
-import { IndicesPutTemplateParams } from 'elasticsearch';
-import { IndicesRecoveryParams } from 'elasticsearch';
-import { IndicesRefreshParams } from 'elasticsearch';
-import { IndicesRolloverParams } from 'elasticsearch';
-import { IndicesSegmentsParams } from 'elasticsearch';
-import { IndicesShardStoresParams } from 'elasticsearch';
-import { IndicesShrinkParams } from 'elasticsearch';
-import { IndicesStatsParams } from 'elasticsearch';
-import { IndicesUpdateAliasesParams } from 'elasticsearch';
-import { IndicesUpgradeParams } from 'elasticsearch';
-import { IndicesValidateQueryParams } from 'elasticsearch';
-import { InfoParams } from 'elasticsearch';
-import { IngestDeletePipelineParams } from 'elasticsearch';
-import { IngestGetPipelineParams } from 'elasticsearch';
-import { IngestPutPipelineParams } from 'elasticsearch';
-import { IngestSimulateParams } from 'elasticsearch';
+import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { KibanaConfigType } from 'src/core/server/kibana_config';
import { Location } from 'history';
import { LocationDescriptorObject } from 'history';
import { MaybePromise } from '@kbn/utility-types';
-import { MGetParams } from 'elasticsearch';
-import { MGetResponse } from 'elasticsearch';
-import { MSearchParams } from 'elasticsearch';
-import { MSearchResponse } from 'elasticsearch';
-import { MSearchTemplateParams } from 'elasticsearch';
-import { MTermVectorsParams } from 'elasticsearch';
-import { NodesHotThreadsParams } from 'elasticsearch';
-import { NodesInfoParams } from 'elasticsearch';
-import { NodesStatsParams } from 'elasticsearch';
import { Observable } from 'rxjs';
import { ParsedQuery } from 'query-string';
import { Path } from 'history';
-import { PingParams } from 'elasticsearch';
import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types';
-import { PutScriptParams } from 'elasticsearch';
-import { PutTemplateParams } from 'elasticsearch';
import React from 'react';
import { RecursiveReadonly } from '@kbn/utility-types';
-import { ReindexParams } from 'elasticsearch';
-import { ReindexRethrottleParams } from 'elasticsearch';
-import { RenderSearchTemplateParams } from 'elasticsearch';
import * as Rx from 'rxjs';
-import { ScrollParams } from 'elasticsearch';
-import { SearchParams } from 'elasticsearch';
-import { SearchResponse } from 'elasticsearch';
-import { SearchShardsParams } from 'elasticsearch';
-import { SearchTemplateParams } from 'elasticsearch';
import { ShallowPromise } from '@kbn/utility-types';
-import { SnapshotCreateParams } from 'elasticsearch';
-import { SnapshotCreateRepositoryParams } from 'elasticsearch';
-import { SnapshotDeleteParams } from 'elasticsearch';
-import { SnapshotDeleteRepositoryParams } from 'elasticsearch';
-import { SnapshotGetParams } from 'elasticsearch';
-import { SnapshotGetRepositoryParams } from 'elasticsearch';
-import { SnapshotRestoreParams } from 'elasticsearch';
-import { SnapshotStatusParams } from 'elasticsearch';
-import { SnapshotVerifyRepositoryParams } from 'elasticsearch';
-import { SuggestParams } from 'elasticsearch';
-import { TasksCancelParams } from 'elasticsearch';
-import { TasksGetParams } from 'elasticsearch';
-import { TasksListParams } from 'elasticsearch';
-import { TermvectorsParams } from 'elasticsearch';
+import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { Type } from '@kbn/config-schema';
import { TypeOf } from '@kbn/config-schema';
import { UnregisterCallback } from 'history';
-import { UpdateDocumentByQueryParams } from 'elasticsearch';
-import { UpdateDocumentParams } from 'elasticsearch';
import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types';
// @internal (undocumented)
diff --git a/src/core/server/elasticsearch/client/types.ts b/src/core/server/elasticsearch/client/types.ts
index 285f52e89a591e..827b185672c7c4 100644
--- a/src/core/server/elasticsearch/client/types.ts
+++ b/src/core/server/elasticsearch/client/types.ts
@@ -42,34 +42,50 @@ export type ElasticsearchClient = Omit<
};
};
-interface ShardsResponse {
+/**
+ * All response typings are maintained until elasticsearch-js provides them out of the box
+ * https://github.com/elastic/elasticsearch-js/pull/970
+ */
+
+/**
+ * @public
+ */
+export interface ShardsResponse {
total: number;
successful: number;
failed: number;
skipped: number;
}
-interface Explanation {
+/**
+ * @public
+ */
+export interface Explanation {
value: number;
description: string;
details: Explanation[];
}
-interface ShardsInfo {
+/**
+ * @public
+ */
+export interface ShardsInfo {
total: number;
successful: number;
skipped: number;
failed: number;
}
+/**
+ * @public
+ */
export interface CountResponse {
_shards: ShardsInfo;
count: number;
}
/**
- * Maintained until elasticsearch provides response typings out of the box
- * https://github.com/elastic/elasticsearch-js/pull/970
+ * @public
*/
export interface SearchResponse {
took: number;
@@ -97,6 +113,9 @@ export interface SearchResponse {
aggregations?: any;
}
+/**
+ * @public
+ */
export interface GetResponse {
_index: string;
_type: string;
@@ -109,6 +128,9 @@ export interface GetResponse {
_primary_term: number;
}
+/**
+ * @public
+ */
export interface DeleteDocumentResponse {
_shards: ShardsResponse;
found: boolean;
diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts
index 32be6e6bf34dd2..9359b88434396e 100644
--- a/src/core/server/elasticsearch/index.ts
+++ b/src/core/server/elasticsearch/index.ts
@@ -36,8 +36,12 @@ export {
ElasticsearchClientConfig,
ElasticsearchClient,
IScopedClusterClient,
+ // responses
SearchResponse,
+ CountResponse,
+ ShardsInfo,
+ ShardsResponse,
+ Explanation,
GetResponse,
DeleteDocumentResponse,
- CountResponse,
} from './client';
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index c846e81573acba..f46b41d6b87938 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -110,6 +110,13 @@ export {
FakeRequest,
ScopeableRequest,
ElasticsearchClient,
+ SearchResponse,
+ CountResponse,
+ ShardsInfo,
+ ShardsResponse,
+ Explanation,
+ GetResponse,
+ DeleteDocumentResponse,
} from './elasticsearch';
export * from './elasticsearch/legacy/api_types';
export {
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 4b6bcbc8ad7a0d..bb4f2f30ac18f1 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -45,7 +45,7 @@ import { ExplainParams } from 'elasticsearch';
import { FieldStatsParams } from 'elasticsearch';
import { GenericParams } from 'elasticsearch';
import { GetParams } from 'elasticsearch';
-import { GetResponse } from 'elasticsearch';
+import { GetResponse as GetResponse_2 } from 'elasticsearch';
import { GetScriptParams } from 'elasticsearch';
import { GetSourceParams } from 'elasticsearch';
import { GetTemplateParams } from 'elasticsearch';
@@ -121,7 +121,7 @@ import { ResponseToolkit } from 'hapi';
import { SchemaTypeError } from '@kbn/config-schema';
import { ScrollParams } from 'elasticsearch';
import { SearchParams } from 'elasticsearch';
-import { SearchResponse } from 'elasticsearch';
+import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
import { SearchShardsParams } from 'elasticsearch';
import { SearchTemplateParams } from 'elasticsearch';
import { Server } from 'hapi';
@@ -532,6 +532,14 @@ export interface CoreStatus {
savedObjects: ServiceStatus;
}
+// @public (undocumented)
+export interface CountResponse {
+ // (undocumented)
+ count: number;
+ // (undocumented)
+ _shards: ShardsInfo;
+}
+
// @public
export class CspConfig implements ICspConfig {
// @internal
@@ -592,6 +600,28 @@ export const DEFAULT_APP_CATEGORIES: Readonly<{
};
}>;
+// @public (undocumented)
+export interface DeleteDocumentResponse {
+ // (undocumented)
+ error?: {
+ type: string;
+ };
+ // (undocumented)
+ found: boolean;
+ // (undocumented)
+ _id: string;
+ // (undocumented)
+ _index: string;
+ // (undocumented)
+ result: string;
+ // (undocumented)
+ _shards: ShardsResponse;
+ // (undocumented)
+ _type: string;
+ // (undocumented)
+ _version: number;
+}
+
// @public (undocumented)
export interface DeprecationAPIClientParams extends GenericParams {
// (undocumented)
@@ -642,6 +672,13 @@ export interface DiscoveredPlugin {
readonly requiredPlugins: readonly PluginName[];
}
+// @public
+export type ElasticsearchClient = Omit & {
+ transport: {
+ request(params: TransportRequestParams, options?: TransportRequestOptions): TransportRequestPromise;
+ };
+};
+
// @public
export class ElasticsearchConfig {
constructor(rawConfig: ElasticsearchConfigType);
@@ -709,6 +746,16 @@ export interface ErrorHttpResponseOptions {
headers?: ResponseHeaders;
}
+// @public (undocumented)
+export interface Explanation {
+ // (undocumented)
+ description: string;
+ // (undocumented)
+ details: Explanation[];
+ // (undocumented)
+ value: number;
+}
+
// @public
export function exportSavedObjectsToStream({ types, objects, search, savedObjectsClient, exportSizeLimit, includeReferencesDeep, excludeExportDetails, namespace, }: SavedObjectsExportOptions): Promise;
@@ -736,6 +783,28 @@ export function getFlattenedObject(rootValue: Record): {
[key: string]: any;
};
+// @public (undocumented)
+export interface GetResponse {
+ // (undocumented)
+ found: boolean;
+ // (undocumented)
+ _id: string;
+ // (undocumented)
+ _index: string;
+ // (undocumented)
+ _primary_term: number;
+ // (undocumented)
+ _routing?: string;
+ // (undocumented)
+ _seq_no: number;
+ // (undocumented)
+ _source: T;
+ // (undocumented)
+ _type: string;
+ // (undocumented)
+ _version: number;
+}
+
// @public
export type HandlerContextType> = T extends HandlerFunction ? U : never;
@@ -1042,7 +1111,7 @@ export interface LegacyAPICaller {
// (undocumented)
(endpoint: 'fieldStats', params: FieldStatsParams, options?: LegacyCallAPIOptions): ReturnType;
// (undocumented)
- (endpoint: 'get', params: GetParams, options?: LegacyCallAPIOptions): Promise>;
+ (endpoint: 'get', params: GetParams, options?: LegacyCallAPIOptions): Promise>;
// (undocumented)
(endpoint: 'getScript', params: GetScriptParams, options?: LegacyCallAPIOptions): ReturnType;
// (undocumented)
@@ -1074,9 +1143,9 @@ export interface LegacyAPICaller {
// (undocumented)
(endpoint: 'renderSearchTemplate', params: RenderSearchTemplateParams, options?: LegacyCallAPIOptions): ReturnType;
// (undocumented)
- (endpoint: 'scroll', params: ScrollParams, options?: LegacyCallAPIOptions): Promise>;
+ (endpoint: 'scroll', params: ScrollParams, options?: LegacyCallAPIOptions): Promise>;
// (undocumented)
- (endpoint: 'search', params: SearchParams, options?: LegacyCallAPIOptions): Promise>;
+ (endpoint: 'search', params: SearchParams, options?: LegacyCallAPIOptions): Promise>;
// (undocumented)
(endpoint: 'searchShards', params: SearchShardsParams, options?: LegacyCallAPIOptions): ReturnType;
// (undocumented)
@@ -2082,7 +2151,7 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions {
// @public (undocumented)
export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOptions {
- refresh?: MutatingOperationRefreshSetting;
+ refresh?: boolean;
}
// @public (undocumented)
@@ -2396,7 +2465,7 @@ export class SavedObjectsRepository {
// Warning: (ae-forgotten-export) The symbol "KibanaMigrator" needs to be exported by the entry point index.d.ts
//
// @internal
- static createRepository(migrator: KibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, callCluster: LegacyAPICaller, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
+ static createRepository(migrator: KibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise;
deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<{}>;
@@ -2412,7 +2481,7 @@ export class SavedObjectsRepository {
attributes: any;
}>;
update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>;
- }
+}
// @public
export interface SavedObjectsRepositoryFactory {
@@ -2552,6 +2621,39 @@ export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial
// @public
export type ScopeableRequest = KibanaRequest | LegacyRequest | FakeRequest;
+// @public (undocumented)
+export interface SearchResponse {
+ // (undocumented)
+ aggregations?: any;
+ // (undocumented)
+ hits: {
+ total: number;
+ max_score: number;
+ hits: Array<{
+ _index: string;
+ _type: string;
+ _id: string;
+ _score: number;
+ _source: T;
+ _version?: number;
+ _explanation?: Explanation;
+ fields?: any;
+ highlight?: any;
+ inner_hits?: any;
+ matched_queries?: string[];
+ sort?: string[];
+ }>;
+ };
+ // (undocumented)
+ _scroll_id?: string;
+ // (undocumented)
+ _shards: ShardsResponse;
+ // (undocumented)
+ timed_out: boolean;
+ // (undocumented)
+ took: number;
+}
+
// @public
export interface ServiceStatus | unknown = unknown> {
detail?: string;
@@ -2612,6 +2714,30 @@ export interface SessionStorageFactory {
asScoped: (request: KibanaRequest) => SessionStorage;
}
+// @public (undocumented)
+export interface ShardsInfo {
+ // (undocumented)
+ failed: number;
+ // (undocumented)
+ skipped: number;
+ // (undocumented)
+ successful: number;
+ // (undocumented)
+ total: number;
+}
+
+// @public (undocumented)
+export interface ShardsResponse {
+ // (undocumented)
+ failed: number;
+ // (undocumented)
+ skipped: number;
+ // (undocumented)
+ successful: number;
+ // (undocumented)
+ total: number;
+}
+
// @public (undocumented)
export type SharedGlobalConfig = RecursiveReadonly<{
kibana: Pick;
diff --git a/src/dev/run_check_published_api_changes.ts b/src/dev/run_check_published_api_changes.ts
index 0aa450c8b002a5..28e85708129157 100644
--- a/src/dev/run_check_published_api_changes.ts
+++ b/src/dev/run_check_published_api_changes.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-/* eslint-disable no-console */
-
import { ToolingLog } from '@kbn/dev-utils';
import {
Extractor,
@@ -35,6 +33,11 @@ import fs from 'fs';
import path from 'path';
import getopts from 'getopts';
+const log = new ToolingLog({
+ level: 'info',
+ writeTo: process.stdout,
+});
+
/*
* Step 1: execute build:types
* This users tsconfig.types.json to generate types in `target/types`
@@ -92,13 +95,13 @@ const apiExtractorConfig = (folder: string): ExtractorConfig => {
},
},
};
- const con = ExtractorConfig.prepare({
+ const cfg = ExtractorConfig.prepare({
configObject: config,
configObjectFullPath: undefined,
packageJsonFullPath: path.resolve('package.json'),
});
- return con;
+ return cfg;
};
const runBuildTypes = async () => {
@@ -108,7 +111,7 @@ const runBuildTypes = async () => {
const runApiDocumenter = async (folder: string) => {
const sourceFolder = `./build/${folder}`;
const targetFolder = `./docs/development/${folder}`;
- console.log(`Generating docs from ${sourceFolder} into ${targetFolder}...`);
+ log.info(`Generating docs from ${sourceFolder} into ${targetFolder}...`);
await execa('api-documenter', ['generate', '-i', sourceFolder, '-o', targetFolder], {
preferLocal: true,
});
@@ -117,7 +120,7 @@ const runApiDocumenter = async (folder: string) => {
const renameExtractedApiPackageName = async (folder: string) => {
const fname = getReportFileName(folder);
const jsonApiFile = `build/${folder}/${fname}.api.json`;
- console.log(`Updating ${jsonApiFile}...`);
+ log.info(`Updating ${jsonApiFile}...`);
const json = JSON.parse(fs.readFileSync(jsonApiFile).toString());
json.name = json.canonicalReference = `kibana-plugin-${folder.replace(/\//g, '-')}`;
fs.writeFileSync(jsonApiFile, JSON.stringify(json, null, 2));
@@ -127,11 +130,7 @@ const renameExtractedApiPackageName = async (folder: string) => {
* Runs api-extractor with a custom logger in order to extract results from the process
*
*/
-const runApiExtractor = (
- log: ToolingLog,
- folder: string,
- acceptChanges: boolean = false
-): ExtractorResult => {
+const runApiExtractor = (folder: string, acceptChanges: boolean = false): ExtractorResult => {
const config = apiExtractorConfig(folder);
const options = {
// Indicates that API Extractor is running as part of a local build,
@@ -177,13 +176,10 @@ interface Options {
filter: string;
}
-async function run(
- folder: string,
- { log, opts }: { log: ToolingLog; opts: Options }
-): Promise {
+async function run(folder: string, { opts }: { opts: Options }): Promise {
log.info(`${folder} API: checking for changes in API signature...`);
- const { apiReportChanged, succeeded } = runApiExtractor(log, folder, opts.accept);
+ const { apiReportChanged, succeeded } = runApiExtractor(folder, opts.accept);
// If we're not accepting changes and there's a failure, exit.
if (!opts.accept && !succeeded) {
@@ -209,11 +205,6 @@ async function run(
}
(async () => {
- const log = new ToolingLog({
- level: 'info',
- writeTo: process.stdout,
- });
-
const extraFlags: string[] = [];
const opts = (getopts(process.argv.slice(2), {
boolean: ['accept', 'docs', 'help'],
@@ -276,26 +267,22 @@ async function run(
return !(extraFlags.length > 0);
}
- try {
- log.info(`Building types for api extractor...`);
- await runBuildTypes();
- } catch (e) {
- log.error(e);
- return false;
- }
+ log.info('Building types for api extractor...');
+ await runBuildTypes();
+ log.info('Types for api extractor has been built');
const filteredFolders = folders.filter((folder) =>
opts.filter.length ? folder.match(opts.filter) : true
);
const results = [];
for (const folder of filteredFolders) {
- results.push(await run(folder, { log, opts }));
+ results.push(await run(folder, { opts }));
}
if (results.includes(false)) {
process.exitCode = 1;
}
})().catch((e) => {
- console.log(e);
+ log.error(e);
process.exitCode = 1;
});
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 65670bc1cf83e6..2b904ed9536e04 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -7,44 +7,15 @@
import { $Values } from '@kbn/utility-types';
import _ from 'lodash';
import { Action } from 'history';
+import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
import { ApplicationStart } from 'kibana/public';
import { Assign } from '@kbn/utility-types';
import { BehaviorSubject } from 'rxjs';
import Boom from 'boom';
-import { BulkIndexDocumentsParams } from 'elasticsearch';
-import { CatAliasesParams } from 'elasticsearch';
-import { CatAllocationParams } from 'elasticsearch';
-import { CatCommonParams } from 'elasticsearch';
-import { CatFielddataParams } from 'elasticsearch';
-import { CatHealthParams } from 'elasticsearch';
-import { CatHelpParams } from 'elasticsearch';
-import { CatIndicesParams } from 'elasticsearch';
-import { CatRecoveryParams } from 'elasticsearch';
-import { CatSegmentsParams } from 'elasticsearch';
-import { CatShardsParams } from 'elasticsearch';
-import { CatSnapshotsParams } from 'elasticsearch';
-import { CatTasksParams } from 'elasticsearch';
-import { CatThreadPoolParams } from 'elasticsearch';
-import { ClearScrollParams } from 'elasticsearch';
-import { Client } from 'elasticsearch';
-import { ClusterAllocationExplainParams } from 'elasticsearch';
-import { ClusterGetSettingsParams } from 'elasticsearch';
-import { ClusterHealthParams } from 'elasticsearch';
-import { ClusterPendingTasksParams } from 'elasticsearch';
-import { ClusterPutSettingsParams } from 'elasticsearch';
-import { ClusterRerouteParams } from 'elasticsearch';
-import { ClusterStateParams } from 'elasticsearch';
-import { ClusterStatsParams } from 'elasticsearch';
import { Component } from 'react';
import { CoreSetup } from 'src/core/public';
import { CoreStart } from 'kibana/public';
import { CoreStart as CoreStart_2 } from 'src/core/public';
-import { CountParams } from 'elasticsearch';
-import { CreateDocumentParams } from 'elasticsearch';
-import { DeleteDocumentByQueryParams } from 'elasticsearch';
-import { DeleteDocumentParams } from 'elasticsearch';
-import { DeleteScriptParams } from 'elasticsearch';
-import { DeleteTemplateParams } from 'elasticsearch';
import { Ensure } from '@kbn/utility-types';
import { ErrorToastOptions } from 'src/core/public/notifications';
import { EuiBreadcrumb } from '@elastic/eui';
@@ -53,98 +24,33 @@ import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { ExclusiveUnion } from '@elastic/eui';
-import { ExistsParams } from 'elasticsearch';
-import { ExplainParams } from 'elasticsearch';
import { ExpressionAstFunction } from 'src/plugins/expressions/common';
import { ExpressionsSetup } from 'src/plugins/expressions/public';
-import { FieldStatsParams } from 'elasticsearch';
-import { GenericParams } from 'elasticsearch';
-import { GetParams } from 'elasticsearch';
-import { GetResponse } from 'elasticsearch';
-import { GetScriptParams } from 'elasticsearch';
-import { GetSourceParams } from 'elasticsearch';
-import { GetTemplateParams } from 'elasticsearch';
import { History } from 'history';
import { Href } from 'history';
import { IconType } from '@elastic/eui';
-import { IndexDocumentParams } from 'elasticsearch';
-import { IndicesAnalyzeParams } from 'elasticsearch';
-import { IndicesClearCacheParams } from 'elasticsearch';
-import { IndicesCloseParams } from 'elasticsearch';
-import { IndicesCreateParams } from 'elasticsearch';
-import { IndicesDeleteAliasParams } from 'elasticsearch';
-import { IndicesDeleteParams } from 'elasticsearch';
-import { IndicesDeleteTemplateParams } from 'elasticsearch';
-import { IndicesExistsAliasParams } from 'elasticsearch';
-import { IndicesExistsParams } from 'elasticsearch';
-import { IndicesExistsTemplateParams } from 'elasticsearch';
-import { IndicesExistsTypeParams } from 'elasticsearch';
-import { IndicesFlushParams } from 'elasticsearch';
-import { IndicesFlushSyncedParams } from 'elasticsearch';
-import { IndicesForcemergeParams } from 'elasticsearch';
-import { IndicesGetAliasParams } from 'elasticsearch';
-import { IndicesGetFieldMappingParams } from 'elasticsearch';
-import { IndicesGetMappingParams } from 'elasticsearch';
-import { IndicesGetParams } from 'elasticsearch';
-import { IndicesGetSettingsParams } from 'elasticsearch';
-import { IndicesGetTemplateParams } from 'elasticsearch';
-import { IndicesGetUpgradeParams } from 'elasticsearch';
-import { IndicesOpenParams } from 'elasticsearch';
-import { IndicesPutAliasParams } from 'elasticsearch';
-import { IndicesPutMappingParams } from 'elasticsearch';
-import { IndicesPutSettingsParams } from 'elasticsearch';
-import { IndicesPutTemplateParams } from 'elasticsearch';
-import { IndicesRecoveryParams } from 'elasticsearch';
-import { IndicesRefreshParams } from 'elasticsearch';
-import { IndicesRolloverParams } from 'elasticsearch';
-import { IndicesSegmentsParams } from 'elasticsearch';
-import { IndicesShardStoresParams } from 'elasticsearch';
-import { IndicesShrinkParams } from 'elasticsearch';
-import { IndicesStatsParams } from 'elasticsearch';
-import { IndicesUpdateAliasesParams } from 'elasticsearch';
-import { IndicesUpgradeParams } from 'elasticsearch';
-import { IndicesValidateQueryParams } from 'elasticsearch';
-import { InfoParams } from 'elasticsearch';
-import { IngestDeletePipelineParams } from 'elasticsearch';
-import { IngestGetPipelineParams } from 'elasticsearch';
-import { IngestPutPipelineParams } from 'elasticsearch';
-import { IngestSimulateParams } from 'elasticsearch';
import { InjectedIntl } from '@kbn/i18n/react';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/public';
+import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { KibanaConfigType } from 'src/core/server/kibana_config';
import { Location } from 'history';
import { LocationDescriptorObject } from 'history';
import { MaybePromise } from '@kbn/utility-types';
import { METRIC_TYPE } from '@kbn/analytics';
-import { MGetParams } from 'elasticsearch';
-import { MGetResponse } from 'elasticsearch';
import { Moment } from 'moment';
import moment from 'moment';
-import { MSearchParams } from 'elasticsearch';
-import { MSearchResponse } from 'elasticsearch';
-import { MSearchTemplateParams } from 'elasticsearch';
-import { MTermVectorsParams } from 'elasticsearch';
import { NameList } from 'elasticsearch';
-import { NodesHotThreadsParams } from 'elasticsearch';
-import { NodesInfoParams } from 'elasticsearch';
-import { NodesStatsParams } from 'elasticsearch';
import { Observable } from 'rxjs';
import { Path } from 'history';
-import { PingParams } from 'elasticsearch';
import { Plugin as Plugin_2 } from 'src/core/public';
import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public';
import { PopoverAnchorPosition } from '@elastic/eui';
import { PublicUiSettingsParams } from 'src/core/server/types';
-import { PutScriptParams } from 'elasticsearch';
-import { PutTemplateParams } from 'elasticsearch';
import React from 'react';
import * as React_2 from 'react';
import { RecursiveReadonly } from '@kbn/utility-types';
-import { ReindexParams } from 'elasticsearch';
-import { ReindexRethrottleParams } from 'elasticsearch';
-import { RenderSearchTemplateParams } from 'elasticsearch';
import { Reporter } from '@kbn/analytics';
import { RequestAdapter } from 'src/plugins/inspector/common';
import { RequestStatistics } from 'src/plugins/inspector/common';
@@ -153,38 +59,22 @@ import * as Rx from 'rxjs';
import { SavedObject } from 'src/core/server';
import { SavedObject as SavedObject_3 } from 'src/core/public';
import { SavedObjectsClientContract } from 'src/core/public';
-import { ScrollParams } from 'elasticsearch';
import { SearchParams } from 'elasticsearch';
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
-import { SearchShardsParams } from 'elasticsearch';
-import { SearchTemplateParams } from 'elasticsearch';
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
-import { SnapshotCreateParams } from 'elasticsearch';
-import { SnapshotCreateRepositoryParams } from 'elasticsearch';
-import { SnapshotDeleteParams } from 'elasticsearch';
-import { SnapshotDeleteRepositoryParams } from 'elasticsearch';
-import { SnapshotGetParams } from 'elasticsearch';
-import { SnapshotGetRepositoryParams } from 'elasticsearch';
-import { SnapshotRestoreParams } from 'elasticsearch';
-import { SnapshotStatusParams } from 'elasticsearch';
-import { SnapshotVerifyRepositoryParams } from 'elasticsearch';
import { Subscription } from 'rxjs';
-import { SuggestParams } from 'elasticsearch';
-import { TasksCancelParams } from 'elasticsearch';
-import { TasksGetParams } from 'elasticsearch';
-import { TasksListParams } from 'elasticsearch';
-import { TermvectorsParams } from 'elasticsearch';
import { Toast } from 'kibana/public';
import { ToastInputFields } from 'src/core/public/notifications';
import { ToastsStart } from 'kibana/public';
+import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { TypeOf } from '@kbn/config-schema';
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
import { UiActionsStart } from 'src/plugins/ui_actions/public';
import { Unit } from '@elastic/datemath';
import { UnregisterCallback } from 'history';
import { UnwrapPromiseOrReturn } from '@kbn/utility-types';
-import { UpdateDocumentByQueryParams } from 'elasticsearch';
-import { UpdateDocumentParams } from 'elasticsearch';
import { UserProvidedValues } from 'src/core/server/types';
// Warning: (ae-forgotten-export) The symbol "AggConfigSerialized" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 99a77ff9aeb107..7ad2f9edd33254 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -4,6 +4,7 @@
```ts
+import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
import Boom from 'boom';
import { BulkIndexDocumentsParams } from 'elasticsearch';
import { CatAliasesParams } from 'elasticsearch';
@@ -91,6 +92,7 @@ import { IngestDeletePipelineParams } from 'elasticsearch';
import { IngestGetPipelineParams } from 'elasticsearch';
import { IngestPutPipelineParams } from 'elasticsearch';
import { IngestSimulateParams } from 'elasticsearch';
+import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config';
import { KibanaRequest as KibanaRequest_2 } from 'kibana/server';
import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server';
@@ -143,6 +145,9 @@ import { TasksGetParams } from 'elasticsearch';
import { TasksListParams } from 'elasticsearch';
import { TermvectorsParams } from 'elasticsearch';
import { ToastInputFields } from 'src/core/public/notifications';
+import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport';
+import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { Type } from '@kbn/config-schema';
import { TypeOf } from '@kbn/config-schema';
import { Unit } from '@elastic/datemath';
From 8648063b1073cc04d308dd83ca2b81d8fe1e2120 Mon Sep 17 00:00:00 2001
From: Brian Seeders
Date: Tue, 28 Jul 2020 10:52:28 -0400
Subject: [PATCH 042/102] [CI] Harden Slack notifications (#73361)
---
.../src/test/KibanaBasePipelineTest.groovy | 4 ++
.../src/test/slackNotifications.groovy | 63 +++++++++++++++++++
vars/slackNotifications.groovy | 50 +++++++++++++--
3 files changed, 112 insertions(+), 5 deletions(-)
diff --git a/.ci/pipeline-library/src/test/KibanaBasePipelineTest.groovy b/.ci/pipeline-library/src/test/KibanaBasePipelineTest.groovy
index 086484f2385b0e..4112cb3c0e14bd 100644
--- a/.ci/pipeline-library/src/test/KibanaBasePipelineTest.groovy
+++ b/.ci/pipeline-library/src/test/KibanaBasePipelineTest.groovy
@@ -78,6 +78,10 @@ class KibanaBasePipelineTest extends BasePipelineTest {
return helper.callStack.find { it.methodName == name }
}
+ def fnMocks(String name) {
+ helper.callStack.findAll { it.methodName == name }
+ }
+
void mockFailureBuild() {
props([
buildUtils: [
diff --git a/.ci/pipeline-library/src/test/slackNotifications.groovy b/.ci/pipeline-library/src/test/slackNotifications.groovy
index 467e4a0e5f520f..f7e39f5fad903c 100644
--- a/.ci/pipeline-library/src/test/slackNotifications.groovy
+++ b/.ci/pipeline-library/src/test/slackNotifications.groovy
@@ -59,4 +59,67 @@ class SlackNotificationsTest extends KibanaBasePipelineTest {
args.blocks[2].text.text.toString()
)
}
+
+ @Test
+ void 'sendFailedBuild() should call slackSend() with a backup message when first attempt fails'() {
+ mockFailureBuild()
+ def counter = 0
+ helper.registerAllowedMethod('slackSend', [Map.class], { ++counter > 1 })
+ slackNotifications.sendFailedBuild()
+
+ def args = fnMocks('slackSend')[1].args[0]
+
+ def expected = [
+ channel: '#kibana-operations-alerts',
+ username: 'Kibana Operations',
+ iconEmoji: ':jenkins:',
+ color: 'danger',
+ message: ':broken_heart: elastic / kibana # master #1',
+ ]
+
+ expected.each {
+ assertEquals(it.value.toString(), args[it.key].toString())
+ }
+
+ assertEquals(
+ ":broken_heart: **" +
+ "\n\nFirst attempt at sending this notification failed. Please check the build.",
+ args.blocks[0].text.text.toString()
+ )
+ }
+
+ @Test
+ void 'getTestFailures() should truncate list of failures to 10'() {
+ prop('testUtils', [
+ getFailures: {
+ return (1..12).collect {
+ return [
+ url: Mocks.TEST_FAILURE_URL,
+ fullDisplayName: "Failure #${it}",
+ ]
+ }
+ },
+ ])
+
+ def message = (String) slackNotifications.getTestFailures()
+
+ assertTrue("Message ends with truncated indicator", message.endsWith("...and 2 more"))
+ assertTrue("Message contains Failure #10", message.contains("Failure #10"))
+ assertTrue("Message does not contain Failure #11", !message.contains("Failure #11"))
+ }
+
+ @Test
+ void 'shortenMessage() should truncate a long message, but leave parts that fit'() {
+ assertEquals('Hello\nHello\n[...truncated...]', slackNotifications.shortenMessage('Hello\nHello\nthis is a long string', 29))
+ }
+
+ @Test
+ void 'shortenMessage() should not modify a short message'() {
+ assertEquals('Hello world', slackNotifications.shortenMessage('Hello world', 11))
+ }
+
+ @Test
+ void 'shortenMessage() should truncate an entire message with only one part'() {
+ assertEquals('[...truncated...]', slackNotifications.shortenMessage('Hello world this is a really long message', 40))
+ }
}
diff --git a/vars/slackNotifications.groovy b/vars/slackNotifications.groovy
index 2ffb420ecf3f4f..30f86e6d6f0ad3 100644
--- a/vars/slackNotifications.groovy
+++ b/vars/slackNotifications.groovy
@@ -13,12 +13,35 @@ def dividerBlock() {
return [ type: "divider" ]
}
+// If a message is longer than the limit, split it up by '\n' into parts, and return as many parts as will fit within the limit
+def shortenMessage(message, sizeLimit = 3000) {
+ if (message.size() <= sizeLimit) {
+ return message
+ }
+
+ def truncatedMessage = "[...truncated...]"
+
+ def parts = message.split("\n")
+ message = ""
+
+ for(def part in parts) {
+ if ((message.size() + part.size() + truncatedMessage.size() + 1) > sizeLimit) {
+ break;
+ }
+ message += part+"\n"
+ }
+
+ message += truncatedMessage
+
+ return message.size() <= sizeLimit ? message : truncatedMessage
+}
+
def markdownBlock(message) {
return [
type: "section",
text: [
type: "mrkdwn",
- text: message,
+ text: shortenMessage(message, 3000), // 3000 is max text length for `section`s only
],
]
}
@@ -29,7 +52,7 @@ def contextBlock(message) {
elements: [
[
type: 'mrkdwn',
- text: message,
+ text: message, // Not sure what the size limit is here, I tried 10000s of characters and it still worked
]
]
]
@@ -62,7 +85,7 @@ def getTestFailures() {
def messages = []
messages << "*Test Failures*"
- def list = failures.collect {
+ def list = failures.take(10).collect {
def name = it
.fullDisplayName
.split(/\./, 2)[-1]
@@ -73,7 +96,9 @@ def getTestFailures() {
return "• <${it.url}|${name}>"
}.join("\n")
- return "*Test Failures*\n${list}"
+
+ def moreText = failures.size() > 10 ? "\n• ...and ${failures.size()-10} more" : ""
+ return "*Test Failures*\n${list}${moreText}"
}
def getDefaultDisplayName() {
@@ -98,6 +123,10 @@ def getStatusIcon() {
return ':broken_heart:'
}
+def getBackupMessage(config) {
+ return "${getStatusIcon()} ${config.title}\n\nFirst attempt at sending this notification failed. Please check the build."
+}
+
def sendFailedBuild(Map params = [:]) {
def config = [
channel: '#kibana-operations-alerts',
@@ -117,7 +146,7 @@ def sendFailedBuild(Map params = [:]) {
blocks << dividerBlock()
blocks << config.context
- slackSend(
+ def resp = slackSend(
channel: config.channel,
username: config.username,
iconEmoji: config.icon,
@@ -125,6 +154,17 @@ def sendFailedBuild(Map params = [:]) {
message: message,
blocks: blocks
)
+
+ if (!resp) {
+ slackSend(
+ channel: config.channel,
+ username: config.username,
+ iconEmoji: config.icon,
+ color: config.color,
+ message: message,
+ blocks: [markdownBlock(getBackupMessage(config))]
+ )
+ }
}
def onFailure(Map options = [:]) {
From 0149c65221aac101043eaa03b63b4e223801ec1d Mon Sep 17 00:00:00 2001
From: Joe Portner <5295965+jportner@users.noreply.github.com>
Date: Tue, 28 Jul 2020 10:56:29 -0400
Subject: [PATCH 043/102] lodash `4.17.15` -> `4.17.19` (#73122)
---
yarn.lock | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/yarn.lock b/yarn.lock
index c1328731db1508..ee4188440e0cae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -20737,7 +20737,12 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
+lodash@4.17.11, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
+ version "4.17.19"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
+ integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+
+lodash@4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
From 3d5d4de63ce456e734fa1c5d6fdf28e779c46a24 Mon Sep 17 00:00:00 2001
From: Spencer
Date: Tue, 28 Jul 2020 08:01:34 -0700
Subject: [PATCH 044/102] [kbn/optimizer] log info about the metrics being
reported even when reporter is disabled (#73389)
Co-authored-by: spalger
---
.../ci_stats_reporter/ci_stats_reporter.ts | 4 +++-
packages/kbn-optimizer/src/cli.ts | 2 +-
.../kbn-optimizer/src/log_optimizer_state.ts | 6 ++---
.../src/report_optimizer_stats.ts | 22 ++++++++++++++++---
.../tasks/build_kibana_platform_plugins.ts | 2 +-
5 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
index b38a27fdc1b485..b0378ab6c5cd5a 100644
--- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
+++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
@@ -29,6 +29,8 @@ interface Config {
buildId: string;
}
+export type CiStatsMetrics = Array<{ group: string; id: string; value: number }>;
+
function parseConfig(log: ToolingLog) {
const configJson = process.env.KIBANA_CI_STATS_CONFIG;
if (!configJson) {
@@ -84,7 +86,7 @@ export class CiStatsReporter {
return !!this.config;
}
- async metrics(metrics: Array<{ group: string; id: string; value: number }>) {
+ async metrics(metrics: CiStatsMetrics) {
if (!this.config) {
return;
}
diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts
index 9d3f4b88a258f9..542dc7255f22f2 100644
--- a/packages/kbn-optimizer/src/cli.ts
+++ b/packages/kbn-optimizer/src/cli.ts
@@ -116,7 +116,7 @@ run(
log.warning('Unable to initialize CiStatsReporter from env');
}
- update$ = update$.pipe(reportOptimizerStats(reporter, config));
+ update$ = update$.pipe(reportOptimizerStats(reporter, config, log));
}
await update$.pipe(logOptimizerState(log, config)).toPromise();
diff --git a/packages/kbn-optimizer/src/log_optimizer_state.ts b/packages/kbn-optimizer/src/log_optimizer_state.ts
index 20d98f74dbe860..e8bc6debf971eb 100644
--- a/packages/kbn-optimizer/src/log_optimizer_state.ts
+++ b/packages/kbn-optimizer/src/log_optimizer_state.ts
@@ -104,7 +104,7 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
}
if (state.phase === 'running' || state.phase === 'initializing') {
- return true;
+ return;
}
if (state.phase === 'issue') {
@@ -119,7 +119,7 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
}
}
log.indent(-4);
- return true;
+ return;
}
if (state.phase === 'success') {
@@ -135,7 +135,7 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
);
}
- return true;
+ return;
}
throw new Error(`unhandled optimizer message: ${inspect(update)}`);
diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts
index 5057c717efcc35..eff2bce0b827e4 100644
--- a/packages/kbn-optimizer/src/report_optimizer_stats.ts
+++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts
@@ -21,7 +21,7 @@ import Fs from 'fs';
import Path from 'path';
import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
-import { CiStatsReporter } from '@kbn/dev-utils';
+import { CiStatsReporter, CiStatsMetrics, ToolingLog } from '@kbn/dev-utils';
import { OptimizerUpdate$ } from './run_optimizer';
import { OptimizerState, OptimizerConfig } from './optimizer';
@@ -67,7 +67,11 @@ const getFiles = (dir: string, parent?: string) =>
return true;
});
-export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
+export function reportOptimizerStats(
+ reporter: CiStatsReporter,
+ config: OptimizerConfig,
+ log: ToolingLog
+) {
return pipeClosure((update$: OptimizerUpdate$) => {
let lastState: OptimizerState | undefined;
return update$.pipe(
@@ -98,10 +102,18 @@ export function reportOptimizerStats(reporter: CiStatsReporter, config: Optimize
const miscFiles = outputFiles.filter(
(f) => f !== entry && !asyncChunks.includes(f)
);
+
+ if (asyncChunks.length) {
+ log.verbose(bundle.id, 'async chunks', asyncChunks);
+ }
+ if (miscFiles.length) {
+ log.verbose(bundle.id, 'misc files', asyncChunks);
+ }
+
const sumSize = (files: Entry[]) =>
files.reduce((acc: number, f) => acc + f.stats!.size, 0);
- return [
+ const metrics: CiStatsMetrics = [
{
group: `@kbn/optimizer bundle module count`,
id: bundle.id,
@@ -123,6 +135,10 @@ export function reportOptimizerStats(reporter: CiStatsReporter, config: Optimize
value: sumSize(miscFiles),
},
];
+
+ log.info(bundle.id, 'metrics', metrics);
+
+ return metrics;
})
)
);
diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.ts b/src/dev/build/tasks/build_kibana_platform_plugins.ts
index 08637677fcfbe5..beb5ad40229df7 100644
--- a/src/dev/build/tasks/build_kibana_platform_plugins.ts
+++ b/src/dev/build/tasks/build_kibana_platform_plugins.ts
@@ -44,7 +44,7 @@ export const BuildKibanaPlatformPlugins: Task = {
await runOptimizer(optimizerConfig)
.pipe(
- reportOptimizerStats(reporter, optimizerConfig),
+ reportOptimizerStats(reporter, optimizerConfig, log),
logOptimizerState(log, optimizerConfig)
)
.toPromise();
From f6a53f680552172dced497eb34a20b0f87d0b795 Mon Sep 17 00:00:00 2001
From: Larry Gregory
Date: Tue, 28 Jul 2020 11:45:18 -0400
Subject: [PATCH 045/102] Upgrade jimp to v0.14.0 (#73429)
---
package.json | 2 +-
yarn.lock | 416 +++++++++++++++++++++++++++------------------------
2 files changed, 223 insertions(+), 195 deletions(-)
diff --git a/package.json b/package.json
index 0c49ec26be194a..51a41cbbab9ffc 100644
--- a/package.json
+++ b/package.json
@@ -460,7 +460,7 @@
"jest-cli": "^25.5.4",
"jest-environment-jsdom-thirteen": "^1.0.1",
"jest-raw-loader": "^1.0.1",
- "jimp": "^0.9.6",
+ "jimp": "^0.14.0",
"json5": "^1.0.1",
"license-checker": "^16.0.0",
"listr": "^0.14.1",
diff --git a/yarn.lock b/yarn.lock
index ee4188440e0cae..c31f58c3320e2b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2767,26 +2767,24 @@
"@types/yargs" "^15.0.0"
chalk "^3.0.0"
-"@jimp/bmp@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.9.6.tgz#379e261615d7c1f3b52af4d5a0f324666de53d7d"
- integrity sha512-T2Fh/k/eN6cDyOx0KQ4y56FMLo8+mKNhBh7GXMQXLK2NNZ0ckpFo3VHDBZ3HnaFeVTZXF/atLiR9CfnXH+rLxA==
+"@jimp/bmp@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.14.0.tgz#6df246026554f276f7b354047c6fff9f5b2b5182"
+ integrity sha512-5RkX6tSS7K3K3xNEb2ygPuvyL9whjanhoaB/WmmXlJS6ub4DjTqrapu8j4qnIWmO4YYtFeTbDTXV6v9P1yMA5A==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
+ "@jimp/utils" "^0.14.0"
bmp-js "^0.1.0"
- core-js "^3.4.1"
-"@jimp/core@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.9.6.tgz#a553f801bd436526d36e8982b99e58e8afc3d17a"
- integrity sha512-sQO04S+HZNid68a9ehb4BC2lmW6iZ5JgU9tC+thC2Lhix+N/XKDJcBJ6HevbLgeTzuIAw24C5EKuUeO3C+rE5w==
+"@jimp/core@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.14.0.tgz#870c9ca25b40be353ebda1d2abb48723d9010055"
+ integrity sha512-S62FcKdtLtj3yWsGfJRdFXSutjvHg7aQNiFogMbwq19RP4XJWqS2nOphu7ScB8KrSlyy5nPF2hkWNhLRLyD82w==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
+ "@jimp/utils" "^0.14.0"
any-base "^1.1.0"
buffer "^5.2.0"
- core-js "^3.4.1"
exif-parser "^0.1.12"
file-type "^9.0.0"
load-bmfont "^1.3.1"
@@ -2795,256 +2793,269 @@
pixelmatch "^4.0.2"
tinycolor2 "^1.4.1"
-"@jimp/custom@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.9.6.tgz#3d8a19d6ed717f0f1aa3f1b8f42fa374f43bc715"
- integrity sha512-ZYKgrBZVoQwvIGlQSO7MFmn7Jn8a9X5g1g+KOTDO9Q0s4vnxdPTtr/qUjG9QYX6zW/6AK4LaIsDinDrrKDnOag==
+"@jimp/custom@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.14.0.tgz#1dbbf0094df7403f4e03bc984ed92e7458842f74"
+ integrity sha512-kQJMeH87+kWJdVw8F9GQhtsageqqxrvzg7yyOw3Tx/s7v5RToe8RnKyMM+kVtBJtNAG+Xyv/z01uYQ2jiZ3GwA==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/core" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/core" "^0.14.0"
-"@jimp/gif@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.9.6.tgz#0a7b1e521daca635b02259f941bdb3600569d8e6"
- integrity sha512-Z2muC2On8KHEVrWKCCM0L2eua9kw4bQETzT7gmVsizc8MXAKdS8AyVV9T3ZrImiI0o5UkAN/u0cPi1U2pSiD8Q==
+"@jimp/gif@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.14.0.tgz#db159f57c3cfd1566bbe8b124958791998614960"
+ integrity sha512-DHjoOSfCaCz72+oGGEh8qH0zE6pUBaBxPxxmpYJjkNyDZP7RkbBkZJScIYeQ7BmJxmGN4/dZn+MxamoQlr+UYg==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
+ gifwrap "^0.9.2"
omggif "^1.0.9"
-"@jimp/jpeg@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.9.6.tgz#fb90bdc0111966987c5ba59cdca7040be86ead41"
- integrity sha512-igSe0pIX3le/CKdvqW4vLXMxoFjTLjEaW6ZHt/h63OegaEa61TzJ2OM7j7DxrEHcMCMlkhUc9Bapk57MAefCTQ==
+"@jimp/jpeg@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.14.0.tgz#8a687a6a653bbbae38c522edef8f84bb418d9461"
+ integrity sha512-561neGbr+87S/YVQYnZSTyjWTHBm9F6F1obYHiyU3wVmF+1CLbxY3FQzt4YolwyQHIBv36Bo0PY2KkkU8BEeeQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
- jpeg-js "^0.3.4"
+ "@jimp/utils" "^0.14.0"
+ jpeg-js "^0.4.0"
-"@jimp/plugin-blit@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.9.6.tgz#7937e02e3514b95dbe4c70d444054847f6e9ce3c"
- integrity sha512-zp7X6uDU1lCu44RaSY88aAvsSKbgqUrfDyWRX1wsamJvvZpRnp1WekWlGyydRtnlUBAGIpiHCHmyh/TJ2I4RWA==
+"@jimp/plugin-blit@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.14.0.tgz#5eb374be1201313b2113899fb842232d8fcfd345"
+ integrity sha512-YoYOrnVHeX3InfgbJawAU601iTZMwEBZkyqcP1V/S33Qnz9uzH1Uj1NtC6fNgWzvX6I4XbCWwtr4RrGFb5CFrw==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-blur@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.9.6.tgz#3d74b18c27e9eae11b956ffe26290ece6d250813"
- integrity sha512-xEi63hvzewUp7kzw+PI3f9CIrgZbphLI4TDDHWNYuS70RvhTuplbR6RMHD/zFhosrANCkJGr5OZJlrJnsCg6ug==
+"@jimp/plugin-blur@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.14.0.tgz#fe07e4932d5a2f5d8c9831e245561553224bfc60"
+ integrity sha512-9WhZcofLrT0hgI7t0chf7iBQZib//0gJh9WcQMUt5+Q1Bk04dWs8vTgLNj61GBqZXgHSPzE4OpCrrLDBG8zlhQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-color@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.9.6.tgz#d0fca0ed4c2c48fd6f929ef4a03cebf9c1342e14"
- integrity sha512-o1HSoqBVUUAsWbqSXnpiHU0atKWy/Q1GUbZ3F5GWt/0OSDyl9RWM82V9axT2vePZHInKjIaimhnx1gGj8bfxkQ==
+"@jimp/plugin-circle@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-circle/-/plugin-circle-0.14.0.tgz#82c0e904a34e90fa672fb9c286bc892e92088ddf"
+ integrity sha512-o5L+wf6QA44tvTum5HeLyLSc5eVfIUd5ZDVi5iRfO4o6GT/zux9AxuTSkKwnjhsG8bn1dDmywAOQGAx7BjrQVA==
+ dependencies:
+ "@babel/runtime" "^7.7.2"
+ "@jimp/utils" "^0.14.0"
+
+"@jimp/plugin-color@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.14.0.tgz#772bd2d80a88bc66ea1331d010207870f169a74b"
+ integrity sha512-JJz512SAILYV0M5LzBb9sbOm/XEj2fGElMiHAxb7aLI6jx+n0agxtHpfpV/AePTLm1vzzDxx6AJxXbKv355hBQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
tinycolor2 "^1.4.1"
-"@jimp/plugin-contain@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.9.6.tgz#7d7bbd5e9c2fa4391a3d63620e13a28f51e1e7e8"
- integrity sha512-Xz467EN1I104yranET4ff1ViVKMtwKLg1uRe8j3b5VOrjtiXpDbjirNZjP3HTlv8IEUreWNz4BK7ZtfHSptufA==
+"@jimp/plugin-contain@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.14.0.tgz#c68115420d182e696f81bbe76fb5e704909b2b6a"
+ integrity sha512-RX2q233lGyaxiMY6kAgnm9ScmEkNSof0hdlaJAVDS1OgXphGAYAeSIAwzESZN4x3ORaWvkFefeVH9O9/698Evg==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-cover@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.9.6.tgz#2853de7f8302f655ae8e95f51ab25a0ed77e3756"
- integrity sha512-Ocr27AvtvH4ZT/9EWZgT3+HQV9fG5njwh2CYMHbdpx09O62Asj6pZ4QI0kKzOcux1oLgv59l7a93pEfMOfkfwQ==
+"@jimp/plugin-cover@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.14.0.tgz#4755322589c5885e44e14e31b86b542e907297ce"
+ integrity sha512-0P/5XhzWES4uMdvbi3beUgfvhn4YuQ/ny8ijs5kkYIw6K8mHcl820HahuGpwWMx56DJLHRl1hFhJwo9CeTRJtQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-crop@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.9.6.tgz#82e539af2a2417783abbd143124a57672ff4cc31"
- integrity sha512-d9rNdmz3+eYLbSKcTyyp+b8Nmhf6HySnimDXlTej4UP6LDtkq2VAyVaJ12fz9x6dfd8qcXOBXMozSfNCcgpXYA==
+"@jimp/plugin-crop@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.14.0.tgz#4cbd856ca84ffc37230fad2534906f2f75aa3057"
+ integrity sha512-Ojtih+XIe6/XSGtpWtbAXBozhCdsDMmy+THUJAGu2x7ZgKrMS0JotN+vN2YC3nwDpYkM+yOJImQeptSfZb2Sug==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-displace@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.9.6.tgz#67564d081dc6b19007248ca222d025fd6f90c03b"
- integrity sha512-SWpbrxiHmUYBVWtDDMjaG3eRDBASrTPaad7l07t73/+kmU6owAKWQW6KtVs05MYSJgXz7Ggdr0fhEn9AYLH1Rg==
+"@jimp/plugin-displace@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.14.0.tgz#b0e6a57d00cb1f893f541413fe9d737d23c3b70c"
+ integrity sha512-c75uQUzMgrHa8vegkgUvgRL/PRvD7paFbFJvzW0Ugs8Wl+CDMGIPYQ3j7IVaQkIS+cAxv+NJ3TIRBQyBrfVEOg==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-dither@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.9.6.tgz#dc48669cf51f3933761aa9137e99ebfa000b8cce"
- integrity sha512-abm1GjfYK7ru/PoxH9fAUmhl+meHhGEClbVvjjMMe5g2S0BSTvMJl3SrkQD/FMkRLniaS/Qci6aQhIi+8rZmSw==
+"@jimp/plugin-dither@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.14.0.tgz#9185ec4c38e02edc9e5831f5d709f6ba891e1b93"
+ integrity sha512-g8SJqFLyYexXQQsoh4dc1VP87TwyOgeTElBcxSXX2LaaMZezypmxQfLTzOFzZoK8m39NuaoH21Ou1Ftsq7LzVQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-flip@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.9.6.tgz#f81f9b886da8cd56e23dd04d5aa359f2b94f939e"
- integrity sha512-KFZTzAzQQ5bct3ii7gysOhWrTKVdUOghkkoSzLi+14nO3uS/dxiu8fPeH1m683ligbdnuM/b22OuLwEwrboTHA==
+"@jimp/plugin-fisheye@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-fisheye/-/plugin-fisheye-0.14.0.tgz#9f26346cf2fbc660cc2008cd7fd30a83b5029e78"
+ integrity sha512-BFfUZ64EikCaABhCA6mR3bsltWhPpS321jpeIQfJyrILdpFsZ/OccNwCgpW1XlbldDHIoNtXTDGn3E+vCE7vDg==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-gaussian@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.6.tgz#6c93897ee0ff979466184d7d0ec0fbc95c679be4"
- integrity sha512-WXKLtJKWchXfWHT5HIOq1HkPKpbH7xBLWPgVRxw00NV/6I8v4xT63A7/Nag78m00JgjwwiE7eK2tLGDbbrPYig==
+"@jimp/plugin-flip@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.14.0.tgz#7966d6aa3b5fe1aa4d2d561ff12b8ef5ccb9b071"
+ integrity sha512-WtL1hj6ryqHhApih+9qZQYA6Ye8a4HAmdTzLbYdTMrrrSUgIzFdiZsD0WeDHpgS/+QMsWwF+NFmTZmxNWqKfXw==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-invert@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.9.6.tgz#4b3fa7b81ea976b09b82b3db59ee00ac3093d2fd"
- integrity sha512-Pab/cupZrYxeRp07N4L5a4C/3ksTN9k6Knm/o2G5C789OF0rYsGGLcnBR/6h69nPizRZHBYdXCEyXYgujlIFiw==
+"@jimp/plugin-gaussian@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.14.0.tgz#452bc1971a4467ad9b984aa67f4c200bf941bb65"
+ integrity sha512-uaLwQ0XAQoydDlF9tlfc7iD9drYPriFe+jgYnWm8fbw5cN+eOIcnneEX9XCOOzwgLPkNCxGox6Kxjn8zY6GxtQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-mask@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.9.6.tgz#d70be0030ab3294b191f5b487fb655d776820b19"
- integrity sha512-ikypRoDJkbxXlo6gW+EZOcTiLDIt0DrPwOFMt1bvL8UV2QPgX+GJ685IYwhIfEhBf/GSNFgB/NYsVvuSufTGeg==
+"@jimp/plugin-invert@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.14.0.tgz#cd31a555860e9f821394936d15af161c09c42921"
+ integrity sha512-UaQW9X9vx8orQXYSjT5VcITkJPwDaHwrBbxxPoDG+F/Zgv4oV9fP+udDD6qmkgI9taU+44Fy+zm/J/gGcMWrdg==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-normalize@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.9.6.tgz#c9128412a53485d91236a1da241f3166e572be4a"
- integrity sha512-V3GeuAJ1NeL7qsLoDjnypJq24RWDCwbXpKhtxB+Yg9zzgOCkmb041p7ysxbcpkuJsRpKLNABZeNCCqd83bRawA==
+"@jimp/plugin-mask@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.14.0.tgz#52619643ac6222f85e6b27dee33c771ca3a6a4c9"
+ integrity sha512-tdiGM69OBaKtSPfYSQeflzFhEpoRZ+BvKfDEoivyTjauynbjpRiwB1CaiS8En1INTDwzLXTT0Be9SpI3LkJoEA==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-print@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.9.6.tgz#fea31ffeafee18ae7b5cfd6fa131bb205abfee51"
- integrity sha512-gKkqZZPQtMSufHOL0mtJm5d/KI2O6+0kUpOBVSYdGedtPXA61kmVnsOd3wwajIMlXA3E0bDxLXLdAguWqjjGgw==
+"@jimp/plugin-normalize@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.14.0.tgz#bf39e356b6d473f582ce95633ad49c9cdb82492b"
+ integrity sha512-AfY8sqlsbbdVwFGcyIPy5JH/7fnBzlmuweb+Qtx2vn29okq6+HelLjw2b+VT2btgGUmWWHGEHd86oRGSoWGyEQ==
+ dependencies:
+ "@babel/runtime" "^7.7.2"
+ "@jimp/utils" "^0.14.0"
+
+"@jimp/plugin-print@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.14.0.tgz#1c43c2a92a7adc05b464863882cb89ce486d63e6"
+ integrity sha512-MwP3sH+VS5AhhSTXk7pui+tEJFsxnTKFY3TraFJb8WFbA2Vo2qsRCZseEGwpTLhENB7p/JSsLvWoSSbpmxhFAQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
load-bmfont "^1.4.0"
-"@jimp/plugin-resize@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.9.6.tgz#7fb939c8a42e2a3639d661cc7ab24057598693bd"
- integrity sha512-r5wJcVII7ZWMuY2l6WSbHPG6gKMFemtCHmJRXGUu+/ZhPGBz3IFluycBpHkWW3OB+jfvuyv1EGQWHU50N1l8Og==
+"@jimp/plugin-resize@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.14.0.tgz#ef7fc6c2e45f8bcab62456baf8fd3bc415b02b64"
+ integrity sha512-qFeMOyXE/Bk6QXN0GQo89+CB2dQcXqoxUcDb2Ah8wdYlKqpi53skABkgVy5pW3EpiprDnzNDboMltdvDslNgLQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-rotate@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.9.6.tgz#06d725155e5cdb615133f57a52f5a860a9d03f3e"
- integrity sha512-B2nm/eO2nbvn1DgmnzMd79yt3V6kffhRNrKoo2VKcKFiVze1vGP3MD3fVyw5U1PeqwAFu7oTICFnCf9wKDWSqg==
+"@jimp/plugin-rotate@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.14.0.tgz#3632bc159bf1c3b9ec9f459d9c05d02a11781ee7"
+ integrity sha512-aGaicts44bvpTcq5Dtf93/8TZFu5pMo/61lWWnYmwJJU1RqtQlxbCLEQpMyRhKDNSfPbuP8nyGmaqXlM/82J0Q==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugin-scale@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.9.6.tgz#3fa939c1a4f44383e12afeb7c434eb41f20e4a1c"
- integrity sha512-DLsLB5S3mh9+TZY5ycwfLgOJvUcoS7bP0Mi3I8vE1J91qmA+TXoWFFgrIVgnEPw5jSKzNTt8WhykQ0x2lKXncw==
+"@jimp/plugin-scale@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.14.0.tgz#d30f0cd1365b8e68f43fa423300ae7f124e9bf10"
+ integrity sha512-ZcJk0hxY5ZKZDDwflqQNHEGRblgaR+piePZm7dPwPUOSeYEH31P0AwZ1ziceR74zd8N80M0TMft+e3Td6KGBHw==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
-"@jimp/plugins@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.9.6.tgz#a1cfdf9f3e1adf5b124686486343888a16c8fd27"
- integrity sha512-eQI29e+K+3L/fb5GbPgsBdoftvaYetSOO2RL5z+Gjk6R4EF4QFRo63YcFl+f72Kc1b0JTOoDxClvn/s5GMV0tg==
+"@jimp/plugin-shadow@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-shadow/-/plugin-shadow-0.14.0.tgz#471fdb9f109ff2d9e20d533d45e1e18e0b48c749"
+ integrity sha512-p2igcEr/iGrLiTu0YePNHyby0WYAXM14c5cECZIVnq/UTOOIQ7xIcWZJ1lRbAEPxVVXPN1UibhZAbr3HAb5BjQ==
+ dependencies:
+ "@babel/runtime" "^7.7.2"
+ "@jimp/utils" "^0.14.0"
+
+"@jimp/plugin-threshold@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugin-threshold/-/plugin-threshold-0.14.0.tgz#ebd72721c7d1d518c5bb6e494e55d97ac3351d3b"
+ integrity sha512-N4BlDgm/FoOMV/DQM2rSpzsgqAzkP0DXkWZoqaQrlRxQBo4zizQLzhEL00T/YCCMKnddzgEhnByaocgaaa0fKw==
+ dependencies:
+ "@babel/runtime" "^7.7.2"
+ "@jimp/utils" "^0.14.0"
+
+"@jimp/plugins@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.14.0.tgz#41dba85f15ab8dadb4162100eb54e5f27b93ee2c"
+ integrity sha512-vDO3XT/YQlFlFLq5TqNjQkISqjBHT8VMhpWhAfJVwuXIpilxz5Glu4IDLK6jp4IjPR6Yg2WO8TmRY/HI8vLrOw==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/plugin-blit" "^0.9.6"
- "@jimp/plugin-blur" "^0.9.6"
- "@jimp/plugin-color" "^0.9.6"
- "@jimp/plugin-contain" "^0.9.6"
- "@jimp/plugin-cover" "^0.9.6"
- "@jimp/plugin-crop" "^0.9.6"
- "@jimp/plugin-displace" "^0.9.6"
- "@jimp/plugin-dither" "^0.9.6"
- "@jimp/plugin-flip" "^0.9.6"
- "@jimp/plugin-gaussian" "^0.9.6"
- "@jimp/plugin-invert" "^0.9.6"
- "@jimp/plugin-mask" "^0.9.6"
- "@jimp/plugin-normalize" "^0.9.6"
- "@jimp/plugin-print" "^0.9.6"
- "@jimp/plugin-resize" "^0.9.6"
- "@jimp/plugin-rotate" "^0.9.6"
- "@jimp/plugin-scale" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/plugin-blit" "^0.14.0"
+ "@jimp/plugin-blur" "^0.14.0"
+ "@jimp/plugin-circle" "^0.14.0"
+ "@jimp/plugin-color" "^0.14.0"
+ "@jimp/plugin-contain" "^0.14.0"
+ "@jimp/plugin-cover" "^0.14.0"
+ "@jimp/plugin-crop" "^0.14.0"
+ "@jimp/plugin-displace" "^0.14.0"
+ "@jimp/plugin-dither" "^0.14.0"
+ "@jimp/plugin-fisheye" "^0.14.0"
+ "@jimp/plugin-flip" "^0.14.0"
+ "@jimp/plugin-gaussian" "^0.14.0"
+ "@jimp/plugin-invert" "^0.14.0"
+ "@jimp/plugin-mask" "^0.14.0"
+ "@jimp/plugin-normalize" "^0.14.0"
+ "@jimp/plugin-print" "^0.14.0"
+ "@jimp/plugin-resize" "^0.14.0"
+ "@jimp/plugin-rotate" "^0.14.0"
+ "@jimp/plugin-scale" "^0.14.0"
+ "@jimp/plugin-shadow" "^0.14.0"
+ "@jimp/plugin-threshold" "^0.14.0"
timm "^1.6.1"
-"@jimp/png@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.9.6.tgz#00ed7e6fb783b94f2f1a9fadf9a42bd75f70cc7f"
- integrity sha512-9vhOG2xylcDqPbBf4lzpa2Sa1WNJrEZNGvPvWcM+XVhqYa8+DJBLYkoBlpI/qWIYA+eVWDnLF3ygtGj8CElICw==
+"@jimp/png@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.14.0.tgz#0f2dddb5125c0795ca7e67c771204c5437fcda4b"
+ integrity sha512-0RV/mEIDOrPCcNfXSPmPBqqSZYwGADNRVUTyMt47RuZh7sugbYdv/uvKmQSiqRdR0L1sfbCBMWUEa5G/8MSbdA==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/utils" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/utils" "^0.14.0"
pngjs "^3.3.3"
-"@jimp/tiff@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.9.6.tgz#9ff12122e727ee15f27f40a710516102a636f66b"
- integrity sha512-pKKEMqPzX9ak8mek2iVVoW34+h/TSWUyI4NjbYWJMQ2WExfuvEJvLocy9Q9xi6HqRuJmUxgNIiC5iZM1PDEEfg==
+"@jimp/tiff@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.14.0.tgz#a5b25bbe7c43fc3b07bad4e2ab90e0e164c1967f"
+ integrity sha512-zBYDTlutc7j88G/7FBCn3kmQwWr0rmm1e0FKB4C3uJ5oYfT8645lftUsvosKVUEfkdmOaMAnhrf4ekaHcb5gQw==
dependencies:
"@babel/runtime" "^7.7.2"
- core-js "^3.4.1"
utif "^2.0.1"
-"@jimp/types@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.9.6.tgz#7be7f415ad93be733387c03b8a228c587a868a18"
- integrity sha512-PSjdbLZ8d50En+Wf1XkWFfrXaf/GqyrxxgIwFWPbL+wrW4pmbYovfxSLCY61s8INsOFOft9dzzllhLBtg1aQ6A==
+"@jimp/types@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.14.0.tgz#ef681ff702883c5f105b5e4e30d49abf39ee9e34"
+ integrity sha512-hx3cXAW1KZm+b+XCrY3LXtdWy2U+hNtq0rPyJ7NuXCjU7lZR3vIkpz1DLJ3yDdS70hTi5QDXY3Cd9kd6DtloHQ==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/bmp" "^0.9.6"
- "@jimp/gif" "^0.9.6"
- "@jimp/jpeg" "^0.9.6"
- "@jimp/png" "^0.9.6"
- "@jimp/tiff" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/bmp" "^0.14.0"
+ "@jimp/gif" "^0.14.0"
+ "@jimp/jpeg" "^0.14.0"
+ "@jimp/png" "^0.14.0"
+ "@jimp/tiff" "^0.14.0"
timm "^1.6.1"
-"@jimp/utils@^0.9.6":
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.9.6.tgz#a3e6c29e835e2b9ea9f3899c9d3d230dc63bd518"
- integrity sha512-kzxcp0i4ecSdMXFEmtH+NYdBQysINEUTsrjm7v0zH8t/uwaEMOG46I16wo/iPBXJkUeNdL2rbXoGoxxoeSfrrA==
+"@jimp/utils@^0.14.0":
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.14.0.tgz#296254e63118554c62c31c19ac6b8c4bfe6490e5"
+ integrity sha512-MY5KFYUru0y74IsgM/9asDwb3ERxWxXEu3CRCZEvE7DtT86y1bR1XgtlSliMrptjz4qbivNGMQSvUBpEFJDp1A==
dependencies:
"@babel/runtime" "^7.7.2"
- core-js "^3.4.1"
+ regenerator-runtime "^0.13.3"
"@mapbox/extent@0.4.0":
version "0.4.0"
@@ -10924,7 +10935,7 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3, core-js@^2.6.5,
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.4.1, core-js@^3.6.4:
+core-js@^3.0.1, core-js@^3.0.4, core-js@^3.6.4:
version "3.6.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
@@ -15487,6 +15498,14 @@ gh-got@^5.0.0:
got "^6.2.0"
is-plain-obj "^1.1.0"
+gifwrap@^0.9.2:
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/gifwrap/-/gifwrap-0.9.2.tgz#348e286e67d7cf57942172e1e6f05a71cee78489"
+ integrity sha512-fcIswrPaiCDAyO8xnWvHSZdWChjKXUanKKpAiWWJ/UTkEi/aYKn5+90e7DE820zbEaVR9CE2y4z9bzhQijZ0BA==
+ dependencies:
+ image-q "^1.1.1"
+ omggif "^1.0.10"
+
git-clone@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/git-clone/-/git-clone-0.1.0.tgz#0d76163778093aef7f1c30238f2a9ef3f07a2eb9"
@@ -17368,6 +17387,11 @@ ignore@^5.1.1, ignore@^5.1.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+image-q@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/image-q/-/image-q-1.1.1.tgz#fc84099664460b90ca862d9300b6bfbbbfbf8056"
+ integrity sha1-/IQJlmRGC5DKhi2TALa/u7+/gFY=
+
image-size@^0.8.2:
version "0.8.3"
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.8.3.tgz#f0b568857e034f29baffd37013587f2c0cad8b46"
@@ -19398,16 +19422,15 @@ jest@^25.5.4:
import-local "^3.0.2"
jest-cli "^25.5.4"
-jimp@^0.9.6:
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.9.6.tgz#abf381daf193a4fa335cb4ee0e22948049251eb9"
- integrity sha512-DBDHYeNVqVpoPkcvo0PKTNGvD+i7NYvkKTsl0I3k7ql36uN8wGTptRg0HtgQyYE/bhGSLI6Lq5qLwewaOPXNfg==
+jimp@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.14.0.tgz#fde55f69bdb918c1b01ac633d89a25853af85625"
+ integrity sha512-8BXU+J8+SPmwwyq9ELihpSV4dWPTiOKBWCEgtkbnxxAVMjXdf3yGmyaLSshBfXc8sP/JQ9OZj5R8nZzz2wPXgA==
dependencies:
"@babel/runtime" "^7.7.2"
- "@jimp/custom" "^0.9.6"
- "@jimp/plugins" "^0.9.6"
- "@jimp/types" "^0.9.6"
- core-js "^3.4.1"
+ "@jimp/custom" "^0.14.0"
+ "@jimp/plugins" "^0.14.0"
+ "@jimp/types" "^0.14.0"
regenerator-runtime "^0.13.3"
jit-grunt@0.10.0:
@@ -19429,10 +19452,10 @@ joi@13.x.x, joi@^13.5.2:
isemail "3.x.x"
topo "3.x.x"
-jpeg-js@^0.3.4:
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.4.tgz#dc2ba501ee3d58b7bb893c5d1fab47294917e7e7"
- integrity sha512-6IzjQxvnlT8UlklNmDXIJMWxijULjqGrzgqc0OG7YadZdvm7KPQ1j0ehmQQHckgEWOfgpptzcnWgESovxudpTA==
+jpeg-js@^0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.1.tgz#937a3ae911eb6427f151760f8123f04c8bfe6ef7"
+ integrity sha512-jA55yJiB5tCXEddos8JBbvW+IMrqY0y1tjjx9KNVtA+QPmu7ND5j0zkKopClpUTsaETL135uOM2XfcYG4XRjmw==
jquery@^3.5.0:
version "3.5.0"
@@ -23066,6 +23089,11 @@ octokit-pagination-methods@^1.1.0:
resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4"
integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==
+omggif@^1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19"
+ integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==
+
omggif@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.9.tgz#dcb7024dacd50c52b4d303f04802c91c057c765f"
From c8bb0782eae988cf427c47838c867df1ebbd8741 Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Tue, 28 Jul 2020 11:57:51 -0400
Subject: [PATCH 046/102] [Ingest Manager] Disable unenroll from listing for
inactive agent (#73348)
---
.../fleet/agent_details_page/components/actions_menu.tsx | 1 +
.../ingest_manager/sections/fleet/agent_list_page/index.tsx | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
index 7afc57b30cef48..0f48a230bbf5c8 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
@@ -53,6 +53,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
onClick={() => {
setIsReassignFlyoutOpen(true);
}}
+ disabled={!agent.active}
key="reassignConfig"
>
void; refre
onClick={() => {
onReassignClick();
}}
+ disabled={!agent.active}
key="reassignConfig"
>
void; refre
defaultMessage="Assign new agent config"
/>
,
-
{(unenrollAgentsPrompt) => (
{
unenrollAgentsPrompt([agent.id], 1, () => {
From 1dbea34d2df3644195391106632acef3c8e7c88c Mon Sep 17 00:00:00 2001
From: Sonja Krause-Harder
Date: Tue, 28 Jul 2020 18:26:48 +0200
Subject: [PATCH 047/102] [Ingest Manager] Don't send kibana version to
registry on master. (#73415)
* Don't send kibana version to registry on master.
* Adjust test.
* Create correct app context in mocks.
---
x-pack/plugins/ingest_manager/server/mocks.ts | 1 +
x-pack/plugins/ingest_manager/server/plugin.ts | 5 +++++
.../ingest_manager/server/services/app_context.ts | 9 +++++++++
.../server/services/epm/registry/index.ts | 14 ++++++++++----
.../apis/epm/list.ts | 2 +-
5 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/server/mocks.ts b/x-pack/plugins/ingest_manager/server/mocks.ts
index f305d9dd0c1a77..52cd2947830875 100644
--- a/x-pack/plugins/ingest_manager/server/mocks.ts
+++ b/x-pack/plugins/ingest_manager/server/mocks.ts
@@ -18,6 +18,7 @@ export const createAppContextStartContractMock = (): IngestManagerAppContext =>
logger: loggingSystemMock.create().get(),
isProductionMode: true,
kibanaVersion: '8.0.0',
+ kibanaBranch: 'master',
};
};
diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts
index 69af475886bb92..5664a875010166 100644
--- a/x-pack/plugins/ingest_manager/server/plugin.ts
+++ b/x-pack/plugins/ingest_manager/server/plugin.ts
@@ -15,6 +15,7 @@ import {
HttpServiceSetup,
} from 'kibana/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+import packageJSON from '../../../../package.json';
import { LicensingPluginSetup, ILicense } from '../../licensing/server';
import {
EncryptedSavedObjectsPluginStart,
@@ -85,6 +86,7 @@ export interface IngestManagerAppContext {
savedObjects: SavedObjectsServiceStart;
isProductionMode: boolean;
kibanaVersion: string;
+ kibanaBranch: string;
cloud?: CloudSetup;
logger?: Logger;
httpSetup?: HttpServiceSetup;
@@ -145,6 +147,7 @@ export class IngestManagerPlugin
private isProductionMode: boolean;
private kibanaVersion: string;
+ private kibanaBranch: string;
private httpSetup: HttpServiceSetup | undefined;
private encryptedSavedObjectsSetup: EncryptedSavedObjectsPluginSetup | undefined;
@@ -152,6 +155,7 @@ export class IngestManagerPlugin
this.config$ = this.initializerContext.config.create();
this.isProductionMode = this.initializerContext.env.mode.prod;
this.kibanaVersion = this.initializerContext.env.packageInfo.version;
+ this.kibanaBranch = packageJSON.branch;
this.logger = this.initializerContext.logger.get();
}
@@ -257,6 +261,7 @@ export class IngestManagerPlugin
savedObjects: core.savedObjects,
isProductionMode: this.isProductionMode,
kibanaVersion: this.kibanaVersion,
+ kibanaBranch: this.kibanaBranch,
httpSetup: this.httpSetup,
cloud: this.cloud,
logger: this.logger,
diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts
index 4d109b73d12d90..bdc7a443ba6dd0 100644
--- a/x-pack/plugins/ingest_manager/server/services/app_context.ts
+++ b/x-pack/plugins/ingest_manager/server/services/app_context.ts
@@ -24,6 +24,7 @@ class AppContextService {
private savedObjects: SavedObjectsServiceStart | undefined;
private isProductionMode: boolean = false;
private kibanaVersion: string | undefined;
+ private kibanaBranch: string | undefined;
private cloud?: CloudSetup;
private logger: Logger | undefined;
private httpSetup?: HttpServiceSetup;
@@ -38,6 +39,7 @@ class AppContextService {
this.cloud = appContext.cloud;
this.logger = appContext.logger;
this.kibanaVersion = appContext.kibanaVersion;
+ this.kibanaBranch = appContext.kibanaBranch;
this.httpSetup = appContext.httpSetup;
if (appContext.config$) {
@@ -125,6 +127,13 @@ class AppContextService {
return this.kibanaVersion;
}
+ public getKibanaBranch() {
+ if (!this.kibanaBranch) {
+ throw new Error('Kibana branch is not set.');
+ }
+ return this.kibanaBranch;
+ }
+
public addExternalCallback(type: ExternalCallback[0], callback: ExternalCallback[1]) {
if (!this.externalCallbacks.has(type)) {
this.externalCallbacks.set(type, new Set());
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
index 7fb13e5e671d05..c7f2df38fe41a4 100644
--- a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
+++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
@@ -40,6 +40,8 @@ export const pkgToPkgKey = ({ name, version }: { name: string; version: string }
export async function fetchList(params?: SearchParams): Promise {
const registryUrl = getRegistryUrl();
const url = new URL(`${registryUrl}/search`);
+ const kibanaVersion = appContextService.getKibanaVersion().split('-')[0]; // may be x.y.z-SNAPSHOT
+ const kibanaBranch = appContextService.getKibanaBranch();
if (params) {
if (params.category) {
url.searchParams.set('category', params.category);
@@ -48,8 +50,9 @@ export async function fetchList(params?: SearchParams): Promise {
const registryUrl = getRegistryUrl();
+ const kibanaVersion = appContextService.getKibanaVersion().split('-')[0]; // may be x.y.z-SNAPSHOT
+ const kibanaBranch = appContextService.getKibanaBranch();
const url = new URL(
`${registryUrl}/search?package=${packageName}&internal=true&experimental=true`
);
- const kibanaVersion = appContextService.getKibanaVersion().split('-')[0]; // may be 8.0.0-SNAPSHOT
- if (kibanaVersion) {
+
+ // on master, request all packages regardless of version
+ if (kibanaVersion && kibanaBranch !== 'master') {
url.searchParams.set('kibana.version', kibanaVersion);
}
const res = await fetchUrl(url.toString());
diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts
index 2fbda8f2d3c81a..98b26c1c04ebb7 100644
--- a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts
+++ b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts
@@ -29,7 +29,7 @@ export default function ({ getService }: FtrProviderContext) {
return response.body;
};
const listResponse = await fetchPackageList();
- expect(listResponse.response.length).to.be(6);
+ expect(listResponse.response.length).to.be(13);
} else {
warnAndSkipTest(this, log);
}
From 12e7d995f9b73c71d287341e801fe5c72a05097d Mon Sep 17 00:00:00 2001
From: "Devin W. Hurley"
Date: Tue, 28 Jul 2020 12:46:20 -0400
Subject: [PATCH 048/102] [SIEM] [Detections] Reject on value list + other
exception entries in single exception item (#73158)
Add validation to reject when value list and other exception type are entries in the same exception item. Also adds tests for this situation on the schema validation
---
.../lists/common/schemas/types/entries.mock.ts | 14 +++++++++++++-
.../types/non_empty_entries_array.test.ts | 18 +++++++++++++++---
.../schemas/types/non_empty_entries_array.ts | 9 +++++++++
.../exceptions/viewer/helpers.test.tsx | 6 ------
4 files changed, 37 insertions(+), 10 deletions(-)
diff --git a/x-pack/plugins/lists/common/schemas/types/entries.mock.ts b/x-pack/plugins/lists/common/schemas/types/entries.mock.ts
index 3ed3f4e7ff88fa..16794415138b2a 100644
--- a/x-pack/plugins/lists/common/schemas/types/entries.mock.ts
+++ b/x-pack/plugins/lists/common/schemas/types/entries.mock.ts
@@ -11,10 +11,22 @@ import { getEntryListMock } from './entry_list.mock';
import { getEntryExistsMock } from './entry_exists.mock';
import { getEntryNestedMock } from './entry_nested.mock';
-export const getEntriesArrayMock = (): EntriesArray => [
+export const getListAndNonListEntriesArrayMock = (): EntriesArray => [
{ ...getEntryMatchMock() },
{ ...getEntryMatchAnyMock() },
{ ...getEntryListMock() },
{ ...getEntryExistsMock() },
{ ...getEntryNestedMock() },
];
+
+export const getListEntriesArrayMock = (): EntriesArray => [
+ { ...getEntryListMock() },
+ { ...getEntryListMock() },
+];
+
+export const getEntriesArrayMock = (): EntriesArray => [
+ { ...getEntryMatchMock() },
+ { ...getEntryMatchAnyMock() },
+ { ...getEntryExistsMock() },
+ { ...getEntryNestedMock() },
+];
diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts
index ab7002982cf28c..a2697286aa038d 100644
--- a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts
+++ b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts
@@ -11,10 +11,13 @@ import { foldLeftRight, getPaths } from '../../siem_common_deps';
import { getEntryMatchMock } from './entry_match.mock';
import { getEntryMatchAnyMock } from './entry_match_any.mock';
-import { getEntryListMock } from './entry_list.mock';
import { getEntryExistsMock } from './entry_exists.mock';
import { getEntryNestedMock } from './entry_nested.mock';
-import { getEntriesArrayMock } from './entries.mock';
+import {
+ getEntriesArrayMock,
+ getListAndNonListEntriesArrayMock,
+ getListEntriesArrayMock,
+} from './entries.mock';
import { nonEmptyEntriesArray } from './non_empty_entries_array';
import { EntriesArray } from './entries';
@@ -80,7 +83,7 @@ describe('non_empty_entries_array', () => {
});
test('it should validate an array of "list" entries', () => {
- const payload: EntriesArray = [{ ...getEntryListMock() }, { ...getEntryListMock() }];
+ const payload: EntriesArray = [...getListEntriesArrayMock()];
const decoded = nonEmptyEntriesArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
@@ -106,6 +109,15 @@ describe('non_empty_entries_array', () => {
expect(message.schema).toEqual(payload);
});
+ test('it should NOT validate an array of entries of value list and non-value list entries', () => {
+ const payload: EntriesArray = [...getListAndNonListEntriesArrayMock()];
+ const decoded = nonEmptyEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Cannot have entry of type list and other']);
+ expect(message.schema).toEqual({});
+ });
+
test('it should NOT validate an array of non entries', () => {
const payload = [1];
const decoded = nonEmptyEntriesArray.decode(payload);
diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts
index 1370fe022c2586..3683ca97dedb8b 100644
--- a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts
+++ b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts
@@ -8,6 +8,7 @@ import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
import { EntriesArray, entriesArray } from './entries';
+import { entriesList } from './entry_list';
/**
* Types the nonEmptyEntriesArray as:
@@ -21,6 +22,14 @@ export const nonEmptyEntriesArray = new t.Type entriesList.is(entry)) &&
+ input.some((entry) => !entriesList.is(entry))
+ ) {
+ // fail when an exception item contains both a value list entry and a non-value list entry
+ return t.failure(input, context, 'Cannot have entry of type list and other');
+ }
return entriesArray.validate(input, context);
}
},
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx
index fe00e3530fa839..5d4340db9a4488 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx
@@ -93,12 +93,6 @@ describe('Exception viewer helpers', () => {
operator: 'is one of',
value: ['some host name'],
},
- {
- fieldName: 'host.name',
- isNested: false,
- operator: 'is in list',
- value: 'some-list-id',
- },
{
fieldName: 'host.name',
isNested: false,
From 79c475a2158e208e0a6f6c3979c2eaa28e38cc03 Mon Sep 17 00:00:00 2001
From: Corey Robertson
Date: Tue, 28 Jul 2020 12:59:41 -0400
Subject: [PATCH 049/102] Fixes incorrect platform service usage (#73453)
---
x-pack/plugins/canvas/public/state/reducers/workpad.js | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/x-pack/plugins/canvas/public/state/reducers/workpad.js b/x-pack/plugins/canvas/public/state/reducers/workpad.js
index fffcb69c451ed7..1c210577e10145 100644
--- a/x-pack/plugins/canvas/public/state/reducers/workpad.js
+++ b/x-pack/plugins/canvas/public/state/reducers/workpad.js
@@ -40,11 +40,7 @@ export const workpadReducer = handleActions(
[setName]: (workpadState, { payload }) => {
platformService
.getService()
- .coreStart.chrome.recentlyAccessed.add(
- `${APP_ROUTE_WORKPAD}/${workpadState.id}`,
- payload,
- workpadState.id
- );
+ .setRecentlyAccessed(`${APP_ROUTE_WORKPAD}/${workpadState.id}`, payload, workpadState.id);
return { ...workpadState, name: payload };
},
From 01b70dd9dce9f576a5524426d864a8b9f94a7d4c Mon Sep 17 00:00:00 2001
From: Ryland Herrick
Date: Tue, 28 Jul 2020 12:04:42 -0500
Subject: [PATCH 050/102] [Security Solution] Fix Lists route permissions
(#73368)
* Do not display threshold field for an ML Rule
* Give 'read' privileges to 'all' users
We have several lists routes that require lists-read access. If the user
was given the 'all' privilege for securitySolution, they would
previously be locked out of those routes.
---
.../pages/detection_engine/rules/create/helpers.ts | 7 ++++---
x-pack/plugins/security_solution/server/plugin.ts | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
index a972afbd8c0c5f..705013beb750f9 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
@@ -12,6 +12,7 @@ import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../common/const
import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions';
import { RuleType } from '../../../../../../common/detection_engine/types';
import { isMlRule } from '../../../../../../common/machine_learning/helpers';
+import { isThresholdRule } from '../../../../../../common/detection_engine/utils';
import { List } from '../../../../../../common/detection_engine/schemas/types';
import { ENDPOINT_LIST_ID } from '../../../../../shared_imports';
import { NewRule, Rule } from '../../../../containers/detection_engine/rules';
@@ -57,7 +58,7 @@ export interface RuleFields {
}
type QueryRuleFields = Omit;
type ThresholdRuleFields = Omit;
-type MlRuleFields = Omit;
+type MlRuleFields = Omit;
const isMlFields = (
fields: QueryRuleFields | MlRuleFields | ThresholdRuleFields
@@ -69,9 +70,9 @@ const isThresholdFields = (
export const filterRuleFieldsForType = (fields: T, type: RuleType) => {
if (isMlRule(type)) {
- const { index, queryBar, ...mlRuleFields } = fields;
+ const { index, queryBar, threshold, ...mlRuleFields } = fields;
return mlRuleFields;
- } else if (type === 'threshold') {
+ } else if (isThresholdRule(type)) {
const { anomalyThreshold, machineLearningJobId, ...thresholdRuleFields } = fields;
return thresholdRuleFields;
} else {
diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts
index 8fc413236dd2c7..f2fad16d80414c 100644
--- a/x-pack/plugins/security_solution/server/plugin.ts
+++ b/x-pack/plugins/security_solution/server/plugin.ts
@@ -182,7 +182,7 @@ export class Plugin implements IPlugin
Date: Tue, 28 Jul 2020 13:22:44 -0400
Subject: [PATCH 051/102] Fix adding new visualization from dashboard when
session storage is enabled.
---
.../application/dashboard_app_controller.tsx | 29 ++++++++++++-------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
index 2a0e2889575f36..afccf8deaa2179 100644
--- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
@@ -172,6 +172,10 @@ export class DashboardAppController {
chrome.docTitle.change(dash.title);
}
+ const incomingEmbeddable = embeddable
+ .getStateTransfer(scopedHistory())
+ .getIncomingEmbeddablePackage();
+
const dashboardStateManager = new DashboardStateManager({
savedDashboard: dash,
hideWriteControls: dashboardConfig.getHideWriteControls(),
@@ -444,21 +448,24 @@ export class DashboardAppController {
refreshDashboardContainer();
});
- const incomingState = embeddable
- .getStateTransfer(scopedHistory())
- .getIncomingEmbeddablePackage();
- if (incomingState) {
- if ('id' in incomingState) {
- container.addOrUpdateEmbeddable(incomingState.type, {
- savedObjectId: incomingState.id,
- });
- } else if ('input' in incomingState) {
- const input = incomingState.input;
+ if (incomingEmbeddable) {
+ if ('id' in incomingEmbeddable) {
+ container.addOrUpdateEmbeddable(
+ incomingEmbeddable.type,
+ {
+ savedObjectId: incomingEmbeddable.id,
+ }
+ );
+ } else if ('input' in incomingEmbeddable) {
+ const input = incomingEmbeddable.input;
delete input.id;
const explicitInput = {
savedVis: input,
};
- container.addOrUpdateEmbeddable(incomingState.type, explicitInput);
+ container.addOrUpdateEmbeddable(
+ incomingEmbeddable.type,
+ explicitInput
+ );
}
}
}
From 2ea2f10c4652a848d2ff3c14cd1d327dc32ca1ee Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Tue, 28 Jul 2020 18:26:59 +0100
Subject: [PATCH 052/102] [Security Solution] Super select (#73271)
* fix icon
* fix items
* Cleanup
* match styling
Co-authored-by: Elastic Machine
Co-authored-by: Patryk Kopycinski
---
.../timeline/search_super_select/index.tsx | 63 ++++++++++---------
.../timeline/selectable_timeline/index.tsx | 2 +-
2 files changed, 34 insertions(+), 31 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx
index 825d4fe3b29b11..3019a8add4e361 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx
@@ -4,19 +4,41 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiInputPopover, EuiSelectableOption, EuiSuperSelect } from '@elastic/eui';
+import { EuiInputPopover, EuiSelectableOption, EuiFieldText } from '@elastic/eui';
import React, { memo, useCallback, useMemo, useState } from 'react';
-import { createGlobalStyle } from 'styled-components';
+import styled from 'styled-components';
import { OpenTimelineResult } from '../../open_timeline/types';
import { SelectableTimeline } from '../selectable_timeline';
import * as i18n from '../translations';
import { TimelineType, TimelineTypeLiteral } from '../../../../../common/types/timeline';
-const SearchTimelineSuperSelectGlobalStyle = createGlobalStyle`
- .euiPopover__panel.euiPopover__panel-isOpen.timeline-search-super-select-popover__popoverPanel {
- visibility: hidden;
- z-index: 0;
+const StyledEuiFieldText = styled(EuiFieldText)`
+ padding-left: 12px;
+ padding-right: 40px;
+
+ &[readonly] {
+ cursor: pointer;
+ background-size: 0 100%;
+ background-repeat: no-repeat;
+
+ // To match EuiFieldText focus state
+ &:focus {
+ background-color: #fff;
+ background-image: linear-gradient(
+ to top,
+ #006bb4,
+ #006bb4 2px,
+ transparent 2px,
+ transparent 100%
+ );
+ background-size: 100% 100%;
+ }
+ }
+
+ & + .euiFormControlLayoutIcons {
+ left: unset;
+ right: 12px;
}
`;
@@ -29,13 +51,6 @@ interface SearchTimelineSuperSelectProps {
onTimelineChange: (timelineTitle: string, timelineId: string | null) => void;
}
-const basicSuperSelectOptions = [
- {
- value: '-1',
- inputDisplay: i18n.DEFAULT_TIMELINE_TITLE,
- },
-];
-
const getBasicSelectableOptions = (timelineId: string) => [
{
description: i18n.DEFAULT_TIMELINE_DESCRIPTION,
@@ -67,26 +82,15 @@ const SearchTimelineSuperSelectComponent: React.FC (
-
),
- [handleOpenPopover, isDisabled, timelineId, timelineTitle]
+ [handleOpenPopover, isDisabled, timelineTitle]
);
const handleGetSelectableOptions = useCallback(
@@ -126,7 +130,6 @@ const SearchTimelineSuperSelectComponent: React.FC
-
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx
index ae8bf530907893..7ecbc9a53cb213 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx
@@ -165,7 +165,7 @@ const SelectableTimelineComponent: React.FC = ({
responsive={false}
>
-
+
From f61df057727c4adf0aa4bd1e72f108ec23945567 Mon Sep 17 00:00:00 2001
From: Jen Huang
Date: Tue, 28 Jul 2020 10:57:38 -0700
Subject: [PATCH 053/102] Fix long combo box items breaking out of flex item
width (#73351)
---
.../components/package_config_input_config.tsx | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/components/package_config_input_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/components/package_config_input_config.tsx
index 98f04dbd926598..fd3a64bc760a05 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/components/package_config_input_config.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/components/package_config_input_config.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, Fragment, memo, useMemo } from 'react';
+import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFlexGrid,
@@ -21,6 +22,10 @@ import {
} from '../services';
import { PackageConfigInputVarField } from './package_config_input_var_field';
+const FlexItemWithMaxWidth = styled(EuiFlexItem)`
+ max-width: calc(50% - ${(props) => props.theme.eui.euiSizeL});
+`;
+
export const PackageConfigInputConfig: React.FunctionComponent<{
packageInputVars?: RegistryVarsEntry[];
packageConfigInput: PackageConfigInput;
@@ -88,7 +93,7 @@ export const PackageConfigInputConfig: React.FunctionComponent<{
-
+
{requiredVars.map((varDef) => {
const { name: varName, type: varType } = varDef;
@@ -176,7 +181,7 @@ export const PackageConfigInputConfig: React.FunctionComponent<{
) : null}
-
+
);
}
From 86b60bbc637471ca5211b31042b7dfd89bbd2f5e Mon Sep 17 00:00:00 2001
From: Justin Kambic
Date: Tue, 28 Jul 2020 14:52:12 -0400
Subject: [PATCH 054/102] Mock prototype in unit test to prevent relative date
breaking snapshots. (#73531)
---
.../common/charts/__tests__/ping_histogram.test.tsx | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx
index 57a38f2a949e7f..73c6ee43ccd07c 100644
--- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx
+++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx
@@ -7,8 +7,13 @@
import React from 'react';
import { PingHistogramComponent, PingHistogramComponentProps } from '../ping_histogram';
import { renderWithRouter, shallowWithRouter, MountWithReduxProvider } from '../../../../lib';
+import moment from 'moment';
describe('PingHistogram component', () => {
+ beforeAll(() => {
+ moment.prototype.fromNow = jest.fn(() => 'a year ago');
+ });
+
const props: PingHistogramComponentProps = {
absoluteStartDate: 1548697920000,
absoluteEndDate: 1548700920000,
From 2d8a41d3679ff77a60c6c417cdc10f88a4ed3203 Mon Sep 17 00:00:00 2001
From: Chris Cowan
Date: Tue, 28 Jul 2020 12:39:36 -0700
Subject: [PATCH 055/102] [Metrics UI] Make composite size configurable to
avoid max buckets (#72955)
Co-authored-by: Elastic Machine
---
x-pack/plugins/infra/common/http_api/snapshot_api.ts | 1 +
x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts | 1 +
x-pack/plugins/infra/public/metrics_overview_fetchers.ts | 1 +
x-pack/plugins/infra/server/lib/snapshot/snapshot.ts | 4 ++--
x-pack/plugins/infra/server/routes/snapshot/index.ts | 2 ++
5 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/infra/common/http_api/snapshot_api.ts b/x-pack/plugins/infra/common/http_api/snapshot_api.ts
index 9ddbcb17089f3c..11cb57238f917c 100644
--- a/x-pack/plugins/infra/common/http_api/snapshot_api.ts
+++ b/x-pack/plugins/infra/common/http_api/snapshot_api.ts
@@ -107,6 +107,7 @@ export const SnapshotRequestRT = rt.intersection([
region: rt.string,
filterQuery: rt.union([rt.string, rt.null]),
includeTimeseries: rt.boolean,
+ overrideCompositeSize: rt.number,
}),
]);
diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts
index 88bc426e9a0f76..135b4ea9a5335f 100644
--- a/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts
+++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts
@@ -75,6 +75,7 @@ describe('Metrics UI Observability Homepage Functions', () => {
groupBy: [],
nodeType: 'host',
includeTimeseries: true,
+ overrideCompositeSize: 5,
timerange: {
from: startTime.valueOf(),
to: endTime.valueOf(),
diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts
index 4eaf903e17608a..79e0850635138f 100644
--- a/x-pack/plugins/infra/public/metrics_overview_fetchers.ts
+++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts
@@ -87,6 +87,7 @@ export const createMetricsFetchData = (
groupBy: [],
nodeType: 'host',
includeTimeseries: true,
+ overrideCompositeSize: 5,
timerange: {
from: start,
to: end,
diff --git a/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts b/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts
index 9ca10d5e39da7f..5f359b0523d9ff 100644
--- a/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts
+++ b/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts
@@ -86,7 +86,7 @@ const requestGroupedNodes = async (
aggregations: {
nodes: {
composite: {
- size: SNAPSHOT_COMPOSITE_REQUEST_SIZE,
+ size: options.overrideCompositeSize || SNAPSHOT_COMPOSITE_REQUEST_SIZE,
sources: getGroupedNodesSources(options),
},
aggs: {
@@ -142,7 +142,7 @@ const requestNodeMetrics = async (
aggregations: {
nodes: {
composite: {
- size: SNAPSHOT_COMPOSITE_REQUEST_SIZE,
+ size: options.overrideCompositeSize || SNAPSHOT_COMPOSITE_REQUEST_SIZE,
sources: getMetricsSources(options),
},
aggregations: {
diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts
index 7c81c6eef675ff..e99103080463e9 100644
--- a/x-pack/plugins/infra/server/routes/snapshot/index.ts
+++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts
@@ -40,6 +40,7 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => {
accountId,
region,
includeTimeseries,
+ overrideCompositeSize,
} = pipe(
SnapshotRequestRT.decode(request.body),
fold(throwErrors(Boom.badRequest), identity)
@@ -59,6 +60,7 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => {
metrics,
timerange,
includeTimeseries,
+ overrideCompositeSize,
};
const searchES = (
From 5e624502f82085802333c225882e0d91665355e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?=
Date: Tue, 28 Jul 2020 22:13:02 +0200
Subject: [PATCH 056/102] [Security Solution] Fix query fetchPolicy and
deduplication (#73199)
---
.../components/events_viewer/events_viewer.tsx | 1 +
.../common/components/events_viewer/index.tsx | 5 ++++-
.../common/components/events_viewer/mock.ts | 1 +
.../public/common/containers/source/index.tsx | 17 +++++++++++++----
.../components/alerts_table/index.tsx | 7 ++++---
.../components/rules/step_about_rule/index.tsx | 3 ++-
.../components/rules/step_define_rule/index.tsx | 2 +-
.../rules/fetch_index_patterns.tsx | 2 +-
.../timelines/components/timeline/timeline.tsx | 1 +
.../public/timelines/containers/index.tsx | 9 ++++++++-
10 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
index bc036b38524bad..e836e2e20432aa 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
@@ -222,6 +222,7 @@ const EventsViewerComponent: React.FC = ({
sourceId="default"
startDate={start}
endDate={end}
+ queryDeduplication="events_viewer"
>
{({
events,
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
index 80831b4022ace8..c402116ee27146 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
@@ -69,7 +69,10 @@ const StatefulEventsViewerComponent: React.FC = ({
}) => {
const [
{ docValueFields, browserFields, indexPatterns, isLoading: isLoadingIndexPattern },
- ] = useFetchIndexPatterns(defaultIndices ?? useUiSetting(DEFAULT_INDEX_KEY));
+ ] = useFetchIndexPatterns(
+ defaultIndices ?? useUiSetting(DEFAULT_INDEX_KEY),
+ 'events_viewer'
+ );
useEffect(() => {
if (createTimeline != null) {
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
index ea2e60ebc82b87..6266e840519011 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
@@ -40,6 +40,7 @@ export const mockEventViewerResponse = [
{ field: 'event.end', format: 'date_time' },
],
inspect: false,
+ queryDeduplication: 'events_viewer',
},
},
result: {
diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx
index 54d49d7279d68e..ffbecf9e3d4334 100644
--- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx
@@ -122,7 +122,12 @@ interface UseWithSourceState {
export const useWithSource = (
sourceId = 'default',
indexToAdd?: string[] | null,
- onlyCheckIndexToAdd?: boolean
+ onlyCheckIndexToAdd?: boolean,
+ // Fun fact: When using this hook multiple times within a component (e.g. add_exception_modal & edit_exception_modal),
+ // the apolloClient will perform queryDeduplication and prevent the first query from executing. A deep compare is not
+ // performed on `indices`, so another field must be passed to circumvent this.
+ // For details, see https://github.com/apollographql/react-apollo/issues/2202
+ queryDeduplication = 'default'
) => {
const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY);
const defaultIndex = useMemo