diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml index 4966a0b506317..f4e62648a9741 100644 --- a/.github/workflows/project-assigner.yml +++ b/.github/workflows/project-assigner.yml @@ -8,8 +8,16 @@ jobs: name: Assign issue or PR to project based on label steps: - name: Assign to project - uses: elastic/github-actions/project-assigner@v2.0.0 + uses: elastic/github-actions/project-assigner@v2.1.0 id: project_assigner with: - issue-mappings: '[{"label": "Feature:Lens", "projectNumber": 32, "columnName": "Long-term goals"}, {"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"}, {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Input Controls", "projectNumber": 72, "columnName": "Inbox"}]' + issue-mappings: | + [ + {"label": "Feature:Lens", "projectNumber": 32, "columnName": "Long-term goals"}, + {"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"}, + {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, + {"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"}, + {"label": "Feature:Input Controls", "projectNumber": 72, "columnName": "Inbox"}, + {"label": "Team:Security", "projectNumber": 320, "columnName": "Awaiting triage", "projectScope": "org"} + ] ghToken: ${{ secrets.PROJECT_ASSIGNER_TOKEN }} diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.test.tsx index 610399c31928b..be1516843184d 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.test.tsx @@ -13,7 +13,14 @@ import routeData from 'react-router'; import { getFormMock, useFormMock, useFormDataMock } from '../__mock__/form'; import { useUpdateComment } from '../../containers/use_update_comment'; -import { basicCase, basicPush, getUserAction } from '../../containers/mock'; +import { + basicCase, + basicPush, + getUserAction, + getHostIsolationUserAction, + hostIsolationComment, + hostReleaseComment, +} from '../../containers/mock'; import { UserActionTree } from '.'; import { TestProviders } from '../../common/mock'; import { Ecs } from '../../../common'; @@ -368,4 +375,82 @@ describe(`UserActionTree`, () => { ).toEqual(true); }); }); + describe('Host isolation action', () => { + it('renders in the cases details view', async () => { + const isolateAction = [getHostIsolationUserAction()]; + const props = { + ...defaultProps, + caseUserActions: isolateAction, + data: { ...defaultProps.data, comments: [...basicCase.comments, hostIsolationComment()] }, + }; + + const wrapper = mount( + + + + ); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj="endpoint-action"]`).exists()).toBe(true); + }); + }); + + it('shows the correct username', async () => { + const isolateAction = [getHostIsolationUserAction()]; + const props = { + ...defaultProps, + caseUserActions: isolateAction, + data: { ...defaultProps.data, comments: [hostIsolationComment()] }, + }; + + const wrapper = mount( + + + + ); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj="user-action-avatar"]`).first().prop('name')).toEqual( + defaultProps.data.createdBy.fullName + ); + }); + }); + + it('shows a lock icon if the action is isolate', async () => { + const isolateAction = [getHostIsolationUserAction()]; + const props = { + ...defaultProps, + caseUserActions: isolateAction, + data: { ...defaultProps.data, comments: [hostIsolationComment()] }, + }; + + const wrapper = mount( + + + + ); + await waitFor(() => { + expect( + wrapper.find(`[data-test-subj="endpoint-action"]`).first().prop('timelineIcon') + ).toBe('lock'); + }); + }); + it('shows a lockOpen icon if the action is unisolate/release', async () => { + const isolateAction = [getHostIsolationUserAction()]; + const props = { + ...defaultProps, + caseUserActions: isolateAction, + data: { ...defaultProps.data, comments: [hostReleaseComment()] }, + }; + + const wrapper = mount( + + + + ); + await waitFor(() => { + expect( + wrapper.find(`[data-test-subj="endpoint-action"]`).first().prop('timelineIcon') + ).toBe('lockOpen'); + }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.test.tsx new file mode 100644 index 0000000000000..636cd7e40aac1 --- /dev/null +++ b/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { HostIsolationCommentEvent } from './user_action_host_isolation_comment_event'; + +const defaultProps = () => { + return { + type: 'isolate', + endpoints: [{ endpointId: 'e1', hostname: 'host1' }], + href: jest.fn(), + onClick: jest.fn(), + }; +}; + +describe('UserActionHostIsolationCommentEvent', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with the correct action and hostname', async () => { + const wrapper = mount(); + expect(wrapper.find(`[data-test-subj="actions-link-e1"]`).first().exists()).toBeTruthy(); + expect(wrapper.text()).toBe('isolated host host1'); + }); + + it('navigates to app on link click', async () => { + const onActionsLinkClick = jest.fn(); + + const wrapper = mount( + + ); + + wrapper.find(`[data-test-subj="actions-link-e1"]`).first().simulate('click'); + expect(onActionsLinkClick).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.tsx b/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.tsx index d363e874a4e0d..2381d31b3ada8 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/user_action_host_isolation_comment_event.tsx @@ -44,7 +44,7 @@ const HostIsolationCommentEventComponent: React.FC = ({ {endpoints[0].hostname} diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index a900010235c9f..c955bb34240e2 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -76,6 +76,58 @@ export const alertComment: Comment = { version: 'WzQ3LDFc', }; +export const hostIsolationComment: () => Comment = () => { + return { + type: CommentType.actions, + comment: 'I just isolated the host!', + id: 'isolate-comment-id', + actions: { + targets: [ + { + hostname: 'host1', + endpointId: '001', + }, + ], + type: 'isolate', + }, + associationType: AssociationType.case, + createdAt: basicCreatedAt, + createdBy: elasticUser, + owner: SECURITY_SOLUTION_OWNER, + pushedAt: null, + pushedBy: null, + updatedAt: null, + updatedBy: null, + version: 'WzQ3LDFc', + }; +}; + +export const hostReleaseComment: () => Comment = () => { + return { + type: CommentType.actions, + comment: 'I just released the host!', + id: 'isolate-comment-id', + actions: { + targets: [ + { + hostname: 'host1', + endpointId: '001', + }, + ], + type: 'unisolate', + }, + associationType: AssociationType.case, + createdAt: basicCreatedAt, + createdBy: elasticUser, + owner: SECURITY_SOLUTION_OWNER, + pushedAt: null, + pushedBy: null, + updatedAt: null, + updatedBy: null, + version: 'WzQ3LDFc', + }; +}; + export const basicCase: Case = { type: CaseType.individual, owner: SECURITY_SOLUTION_OWNER, @@ -374,6 +426,15 @@ export const getAlertUserAction = () => ({ newValue: '{"type":"alert","alertId":"alert-id-1","index":"index-id-1"}', }); +export const getHostIsolationUserAction = () => ({ + ...basicAction, + actionId: 'isolate-action-id', + actionField: ['comment'] as UserActionField, + action: 'create' as UserAction, + commentId: 'isolate-comment-id', + newValue: 'some value', +}); + export const caseUserActions: CaseUserActions[] = [ getUserAction(['description'], 'create'), getUserAction(['comment'], 'create'), diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 23db57c6d3097..313d6cd12a6db 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -52,6 +52,106 @@ export const comment: CommentResponse = { version: 'WzEsMV0=', }; +export const isolateCommentActions: CommentResponse = { + associationType: AssociationType.case, + id: 'mock-action-comment-1', + comment: 'Isolating this for investigation', + type: CommentType.actions as const, + created_at: '2019-11-25T21:55:00.177Z', + actions: { + targets: [ + { + endpointId: '123', + hostname: 'windows-host-1', + }, + ], + type: 'isolate', + }, + created_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + owner: SECURITY_SOLUTION_OWNER, + pushed_at: null, + pushed_by: null, + updated_at: '2019-11-25T21:55:00.177Z', + updated_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + version: 'WzEsMV0=', +}; + +export const releaseCommentActions: CommentResponse = { + associationType: AssociationType.case, + id: 'mock-action-comment-1', + comment: 'Releasing this for investigation', + type: CommentType.actions as const, + created_at: '2019-11-25T21:55:00.177Z', + actions: { + targets: [ + { + endpointId: '123', + hostname: 'windows-host-1', + }, + ], + type: 'unisolate', + }, + created_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + owner: SECURITY_SOLUTION_OWNER, + pushed_at: null, + pushed_by: null, + updated_at: '2019-11-25T21:55:00.177Z', + updated_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + version: 'WzEsMV0=', +}; + +export const isolateCommentActionsMultipleTargets: CommentResponse = { + associationType: AssociationType.case, + id: 'mock-action-comment-1', + comment: 'Isolating this for investigation', + type: CommentType.actions as const, + created_at: '2019-11-25T21:55:00.177Z', + actions: { + targets: [ + { + endpointId: '123', + hostname: 'windows-host-1', + }, + { + endpointId: '456', + hostname: 'windows-host-2', + }, + ], + type: 'isolate', + }, + created_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + owner: SECURITY_SOLUTION_OWNER, + pushed_at: null, + pushed_by: null, + updated_at: '2019-11-25T21:55:00.177Z', + updated_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + version: 'WzEsMV0=', +}; + export const commentAlert: CommentResponse = { associationType: AssociationType.case, id: 'mock-comment-1', diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index bfd5d1279420b..dc8af8785056d 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -18,6 +18,9 @@ import { commentAlert, commentAlertMultipleIds, commentGeneratedAlert, + isolateCommentActions, + releaseCommentActions, + isolateCommentActionsMultipleTargets, } from './mock'; import { @@ -37,6 +40,52 @@ const formatComment = { comment: 'Wow, good luck catching that bad meanie!', }; +const formatIsolateActionComment = { + commentId: isolateCommentActions.id, + comment: 'Isolating this for investigation', + actions: { + targets: [ + { + hostname: 'windows-host-1', + endpointId: '123', + }, + ], + type: 'isolate', + }, +}; + +const formatReleaseActionComment = { + commentId: releaseCommentActions.id, + comment: 'Releasing this for investigation', + actions: { + targets: [ + { + hostname: 'windows-host-1', + endpointId: '123', + }, + ], + type: 'unisolate', + }, +}; + +const formatIsolateCommentActionsMultipleTargets = { + commentId: isolateCommentActionsMultipleTargets.id, + comment: 'Isolating this for investigation', + actions: { + targets: [ + { + hostname: 'windows-host-1', + endpointId: '123', + }, + { + hostname: 'windows-host-2', + endpointId: '456', + }, + ], + type: 'isolate', + }, +}; + const params = { ...basicParams }; describe('utils', () => { @@ -289,6 +338,42 @@ describe('utils', () => { }, ]); }); + + test('transform isolate action comment', () => { + const comments = [isolateCommentActions]; + const res = transformComments(comments, ['informationCreated']); + const actionText = `Isolated host ${formatIsolateActionComment.actions.targets[0].hostname} with comment: ${formatIsolateActionComment.comment}`; + expect(res).toEqual([ + { + commentId: formatIsolateActionComment.commentId, + comment: `${actionText} (created at ${comments[0].created_at} by ${comments[0].created_by.full_name})`, + }, + ]); + }); + + test('transform release action comment', () => { + const comments = [releaseCommentActions]; + const res = transformComments(comments, ['informationCreated']); + const actionText = `Released host ${formatReleaseActionComment.actions.targets[0].hostname} with comment: ${formatReleaseActionComment.comment}`; + expect(res).toEqual([ + { + commentId: formatReleaseActionComment.commentId, + comment: `${actionText} (created at ${comments[0].created_at} by ${comments[0].created_by.full_name})`, + }, + ]); + }); + + test('transform isolate action comment with multiple hosts', () => { + const comments = [isolateCommentActionsMultipleTargets]; + const res = transformComments(comments, ['informationCreated']); + const actionText = `Isolated host ${formatIsolateCommentActionsMultipleTargets.actions.targets[0].hostname} and 1 more with comment: ${formatIsolateCommentActionsMultipleTargets.comment}`; + expect(res).toEqual([ + { + commentId: formatIsolateCommentActionsMultipleTargets.commentId, + comment: `${actionText} (created at ${comments[0].created_at} by ${comments[0].created_by.full_name})`, + }, + ]); + }); }); describe('transformers', () => { diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index f5a10d705e095..f44adbea2c8b2 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -19,6 +19,7 @@ import { CommentAttributes, CommentRequestUserType, CommentRequestAlertType, + CommentRequestActionsType, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -76,6 +77,17 @@ const getCommentContent = (comment: CommentResponse): string => { } else if (comment.type === CommentType.alert || comment.type === CommentType.generatedAlert) { const ids = getAlertIds(comment); return `Alert with ids ${ids.join(', ')} added to case`; + } else if ( + comment.type === CommentType.actions && + (comment.actions.type === 'isolate' || comment.actions.type === 'unisolate') + ) { + const firstHostname = + comment.actions.targets?.length > 0 ? comment.actions.targets[0].hostname : 'unknown'; + const totalHosts = comment.actions.targets.length; + const actionText = comment.actions.type === 'isolate' ? 'Isolated' : 'Released'; + const additionalHostsText = totalHosts - 1 > 0 ? `and ${totalHosts - 1} more ` : ``; + + return `${actionText} host ${firstHostname} ${additionalHostsText}with comment: ${comment.comment}`; } return ''; @@ -161,7 +173,8 @@ export const createIncident = async ({ const commentsToBeUpdated = caseComments?.filter( (comment) => // We push only user's comments - comment.type === CommentType.user && commentsIdsToBeUpdated.has(comment.id) + (comment.type === CommentType.user || comment.type === CommentType.actions) && + commentsIdsToBeUpdated.has(comment.id) ); const totalAlerts = countAlerts(caseComments); @@ -322,7 +335,7 @@ export const isCommentAlertType = ( export const getCommentContextFromAttributes = ( attributes: CommentAttributes -): CommentRequestUserType | CommentRequestAlertType => { +): CommentRequestUserType | CommentRequestAlertType | CommentRequestActionsType => { const owner = attributes.owner; switch (attributes.type) { case CommentType.user: @@ -340,6 +353,16 @@ export const getCommentContextFromAttributes = ( rule: attributes.rule, owner, }; + case CommentType.actions: + return { + type: attributes.type, + comment: attributes.comment, + actions: { + targets: attributes.actions.targets, + type: attributes.actions.type, + }, + owner, + }; default: return { type: CommentType.user, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index 748dc6a7cbcf8..c599a13cc3119 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -303,6 +303,7 @@ export const exampleResult = { titleField: 'otherTitle', subtitleField: 'otherSubtitle', urlField: 'myLink', + urlFieldIsLinkable: true, color: '#e3e3e3', descriptionField: 'about', typeField: 'otherType', @@ -314,14 +315,18 @@ export const exampleResult = { { fieldName: 'dogs', label: 'Canines' }, ], }, - titleFieldHover: false, - urlFieldHover: false, exampleDocuments: [ { myLink: 'http://foo', otherTitle: 'foo', + content_source_id: '60e85e7ea2564c265a88a4f0', + external_id: 'doc-60e85eb7a2564c937a88a4f3', + last_updated: '2021-07-09T14:35:35+00:00', + updated_at: '2021-07-09T14:35:35+00:00', + source: 'custom', }, ], + schemaFields: {}, }; export const mostRecentIndexJob = { 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 edc772b369558..e50b12f781947 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 @@ -96,7 +96,7 @@ export interface ContentSource { export interface SourceContentItem { id: string; last_updated: string; - [key: string]: string; + [key: string]: string | CustomAPIFieldValue; } export interface ContentSourceDetails extends ContentSource { @@ -186,8 +186,25 @@ export interface CustomSource { id: string; } +// https://www.elastic.co/guide/en/workplace-search/current/workplace-search-custom-sources-api.html#_schema_data_types +type CustomAPIString = string | string[]; +type CustomAPINumber = number | number[]; +type CustomAPIDate = string | string[]; +type CustomAPIGeolocation = string | string[] | number[] | number[][]; + +export type CustomAPIFieldValue = + | CustomAPIString + | CustomAPINumber + | CustomAPIDate + | CustomAPIGeolocation; + export interface Result { - [key: string]: string | string[]; + content_source_id: string; + last_updated: string; + external_id: string; + updated_at: string; + source: string; + [key: string]: CustomAPIFieldValue; } export interface OptionValue { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.test.ts index 6475df7f4c399..36df182b99b85 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.test.ts @@ -14,6 +14,12 @@ describe('getAsLocalDateTimeString', () => { expect(getAsLocalDateTimeString(date)).toEqual(new Date(Date.parse(date)).toLocaleString()); }); + it('returns null if passed value is not a string', () => { + const date = ['1', '2']; + + expect(getAsLocalDateTimeString(date)).toEqual(null); + }); + it('returns null if string cannot be parsed as date', () => { const date = 'foo'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.ts index d5ceb50d4c9af..6350c4e4a4099 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/get_as_local_datetime_string.ts @@ -5,7 +5,11 @@ * 2.0. */ -export const getAsLocalDateTimeString = (str: string) => { - const dateValue = Date.parse(str); +import { CustomAPIFieldValue } from '../types'; + +export const getAsLocalDateTimeString = (maybeDate: CustomAPIFieldValue) => { + if (typeof maybeDate !== 'string') return null; + + const dateValue = Date.parse(maybeDate); return dateValue ? new Date(dateValue).toLocaleString() : null; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/mime_types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/mime_types.ts index f7664c90d461c..7a5020be5986e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/mime_types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/mime_types.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { CustomAPIFieldValue } from '../types'; + const mimeTypes = { 'application/iwork-keynote-sffkey': 'Keynote', 'application/x-iwork-keynote-sffkey': 'Keynote', @@ -51,4 +53,5 @@ const mimeTypes = { 'video/quicktime': 'MOV', } as { [key: string]: string }; -export const mimeType = (type: string) => mimeTypes[type.toLowerCase()] || type; +export const mimeType = (type: CustomAPIFieldValue) => + mimeTypes[type.toString().toLowerCase()] || type; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx index b30511f0a6d80..af94707aa3612 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx @@ -58,6 +58,13 @@ describe('AddSourceList', () => { expect(wrapper.find(AvailableSourcesList)).toHaveLength(1); }); + it('does not render header when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.prop('pageHeader')).toBe(undefined); + }); + describe('layout', () => { it('renders the default workplace search layout when on an organization view', () => { setMockValues({ ...mockValues, isOrganization: true }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx index a7a64194cb42f..165586dcc3903 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx @@ -104,7 +104,9 @@ export const AddSourceList: React.FC = () => { {!isOrganization && ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx index eef508b2e618f..8b0a72ac23e39 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx @@ -62,7 +62,7 @@ export const ExampleResultDetailCard: React.FC = () => {
{detailFields.length > 0 ? ( detailFields.map(({ fieldName, label }, index) => { - const value = result[fieldName] as string; + const value = result[fieldName]; const dateValue = getAsLocalDateTimeString(value); return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx index 549faf1676a54..3ca5b619c0366 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx @@ -117,7 +117,7 @@ export const ExampleSearchResultGroup: React.FC = () => { data-test-subj="MediaTypeField" > - {mimeType(result[mediaTypeField] as string)} + {mimeType(result[mediaTypeField])}
)} @@ -135,8 +135,7 @@ export const ExampleSearchResultGroup: React.FC = () => { by {result[updatedByField]}  )} - {getAsLocalDateTimeString(result.last_updated as string) || - result.last_updated} + {getAsLocalDateTimeString(result.last_updated) || result.last_updated} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx index 46b8de6789467..b3ba4c6d50973 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx @@ -109,7 +109,7 @@ export const ExampleStandoutResult: React.FC = () => { data-test-subj="MediaTypeField" > - {mimeType(result[mediaTypeField] as string)} + {mimeType(result[mediaTypeField])} )} @@ -127,7 +127,7 @@ export const ExampleStandoutResult: React.FC = () => { by {result[updatedByField]}  )} - {getAsLocalDateTimeString(result.last_updated as string) || result.last_updated} + {getAsLocalDateTimeString(result.last_updated) || result.last_updated} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.test.tsx index 76c28ae3d4060..7506c499dff31 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.test.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + import React from 'react'; import { shallow } from 'enzyme'; @@ -12,7 +14,11 @@ import { shallow } from 'enzyme'; import { SubtitleField } from './subtitle_field'; describe('SubtitleField', () => { - const result = { foo: 'bar' }; + const result = { + ...exampleResult.exampleDocuments[0], + foo: 'bar', + }; + it('renders', () => { const props = { result, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx index 2ed4aa0b0fad1..e5681bc7e8619 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + import React from 'react'; import { shallow } from 'enzyme'; @@ -12,7 +14,10 @@ import { shallow } from 'enzyme'; import { TitleField } from './title_field'; describe('TitleField', () => { - const result = { foo: 'bar' }; + const result = { + ...exampleResult.exampleDocuments[0], + foo: 'bar', + }; it('renders', () => { const props = { result, @@ -26,7 +31,10 @@ describe('TitleField', () => { it('handles title when array', () => { const props = { - result: { foo: ['baz', 'bar'] }, + result: { + ...exampleResult.exampleDocuments[0], + foo: ['baz', 'bar'], + }, titleField: 'foo', titleFieldHover: false, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx index a0e3c28f20eb0..a97cc85cb822a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx @@ -137,7 +137,7 @@ export const SourceContent: React.FC = () => { )} {urlFieldIsLinkable && ( - + )} diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/insufficient_license_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/insufficient_license_page.tsx index 6d0c3639d939c..7fcac0ad6992a 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/insufficient_license_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/insufficient_license_page.tsx @@ -30,7 +30,7 @@ export const InsufficientLicensePage: FC = ({ basePath }) => (

} @@ -38,13 +38,13 @@ export const InsufficientLicensePage: FC = ({ basePath }) => (

), diff --git a/x-pack/plugins/security_solution/common/cti/constants.ts b/x-pack/plugins/security_solution/common/cti/constants.ts index 631a13df1ecb1..7b22e9036f566 100644 --- a/x-pack/plugins/security_solution/common/cti/constants.ts +++ b/x-pack/plugins/security_solution/common/cti/constants.ts @@ -65,9 +65,9 @@ export const CTI_DEFAULT_SOURCES = [ 'Abuse Malware', 'AlienVault OTX', 'Anomali', - 'Anomali ThreatStream', 'Malware Bazaar', 'MISP', + 'Recorded Future', ]; export const DEFAULT_CTI_SOURCE_INDEX = ['filebeat-*']; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx index b34f6e657d39a..51ce06762ddf9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx @@ -159,10 +159,10 @@ export const ThreatIntelPanelView: React.FC = ({ alignItems="center" justifyContent="flexEnd" > - + {count} - + {path ? ( {linkCopy} diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx index b7f919dc97013..8839aff7dc33d 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx @@ -74,8 +74,8 @@ export const useCtiDashboardLinks = ( }) ) ); - const items = DashboardsSO.savedObjects?.reduce( - (acc: CtiListItem[], dashboardSO, i) => { + const items = DashboardsSO.savedObjects + ?.reduce((acc: CtiListItem[], dashboardSO, i) => { const item = createLinkFromDashboardSO( dashboardSO, eventCountsByDataset, @@ -87,9 +87,8 @@ export const useCtiDashboardLinks = ( acc.push(item); } return acc; - }, - [] - ); + }, []) + .sort((a, b) => (a.title > b.title ? 1 : -1)); setListItems(items); } else { handleDisabledPlugin();