diff --git a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.test.tsx b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.test.tsx index bd5ed9a8f7b5b..7383cfc78ba7c 100644 --- a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.test.tsx +++ b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.test.tsx @@ -11,13 +11,13 @@ import React from 'react'; import { EuiSuperDatePicker } from '@elastic/eui'; -import { useUrlState } from '@kbn/ml-url-state'; import type { UI_SETTINGS } from '@kbn/data-plugin/common'; import { useDatePickerContext } from '../hooks/use_date_picker_context'; import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service'; import { DatePickerWrapper } from './date_picker_wrapper'; +import { useRefreshIntervalUpdates } from '../..'; jest.mock('@elastic/eui', () => { const EuiButtonMock = jest.fn(() => { @@ -51,7 +51,12 @@ jest.mock('@kbn/ml-url-state', () => { }); jest.mock('../hooks/use_timefilter', () => ({ - useRefreshIntervalUpdates: jest.fn(), + useRefreshIntervalUpdates: jest.fn(() => { + return { + pause: false, + }; + }), + useTimefilter: () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { of } = require('rxjs'); @@ -152,7 +157,7 @@ describe('', () => { test('should set interval to default of 5s when pause is disabled and refresh interval is 0', () => { // arrange - (useUrlState as jest.Mock).mockReturnValue([{ refreshInterval: { pause: false, value: 0 } }]); + (useRefreshIntervalUpdates as jest.Mock).mockReturnValue({ pause: false, value: 0 }); const displayWarningSpy = jest.fn(() => {}); @@ -171,7 +176,7 @@ describe('', () => { test('should show a warning when configured interval is too short', () => { // arrange - (useUrlState as jest.Mock).mockReturnValue([{ refreshInterval: { pause: false, value: 10 } }]); + (useRefreshIntervalUpdates as jest.Mock).mockReturnValue({ pause: false, value: 10 }); const displayWarningSpy = jest.fn(() => {}); diff --git a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx index 081faed04a869..562b49f42991e 100644 --- a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx +++ b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx @@ -121,7 +121,7 @@ export const DatePickerWrapper: FC = (props) => { const time = useTimeRangeUpdates(); useEffect( - function syncTimRangeFromUrlState() { + function syncTimeRangeFromUrlState() { if (globalState?.time !== undefined) { timefilter.setTime({ from: globalState.time.from, @@ -162,11 +162,7 @@ export const DatePickerWrapper: FC = (props) => { timefilter.isTimeRangeSelectorEnabled() ); - const refreshInterval = useMemo( - (): RefreshInterval => globalState?.refreshInterval ?? timeFilterRefreshInterval, - // eslint-disable-next-line react-hooks/exhaustive-deps - [JSON.stringify(globalState?.refreshInterval), timeFilterRefreshInterval] - ); + const refreshInterval = timeFilterRefreshInterval; useEffect( function warnAboutShortRefreshInterval() { diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx index 2a1cb2cf26d89..15aca07a1fb3b 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { type FC, useMemo, useState } from 'react'; +import React, { type FC, useMemo, useState, useEffect, useRef } from 'react'; import { EuiBadge, EuiDescriptionList, @@ -21,7 +21,9 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SelectedChangePoint } from './change_point_detection_context'; +import { useTimefilter } from '@kbn/ml-date-picker'; +import { type RefreshInterval } from '@kbn/data-plugin/common'; +import { type SelectedChangePoint } from './change_point_detection_context'; import { ChartComponent } from './chart_component'; const CHARTS_PER_PAGE = 6; @@ -31,6 +33,28 @@ interface ChartsGridProps { } export const ChartsGrid: FC = ({ changePoints: changePointsDict }) => { + const timefilter = useTimefilter(); + + const initialRefreshSetting = useRef(); + + useEffect( + function pauseRefreshOnMount() { + initialRefreshSetting.current = timefilter.getRefreshInterval(); + + timefilter.setRefreshInterval({ + ...initialRefreshSetting.current, + pause: true, + }); + return () => { + if (initialRefreshSetting.current) { + // reset initial settings + timefilter.setRefreshInterval(initialRefreshSetting.current); + } + }; + }, + [timefilter] + ); + const changePoints = useMemo(() => { return Object.values(changePointsDict).flat(); }, [changePointsDict]); diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts b/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts index 94a7d505b4556..c1c3094eb501f 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts +++ b/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { FilterStateStore } from '@kbn/es-query'; +import moment from 'moment'; +import { FilterStateStore, type TimeRange } from '@kbn/es-query'; import { type TypedLensByValueInput } from '@kbn/lens-plugin/public'; import { useTimeRangeUpdates } from '@kbn/ml-date-picker'; import { useMemo } from 'react'; @@ -33,6 +34,17 @@ export const useCommonChartProps = ({ const { dataView } = useDataSource(); const { bucketInterval, resultQuery, resultFilters } = useChangePointDetectionContext(); + /** + * In order to correctly render annotations for change points at the edges, + * we need to adjust time bound based on the change point timestamp. + */ + const chartTimeRange = useMemo(() => { + return { + from: moment.min(moment(timeRange.from), moment(annotation.timestamp)).toISOString(), + to: moment.max(moment(timeRange.to), moment(annotation.timestamp)).toISOString(), + }; + }, [timeRange, annotation.timestamp]); + const filters = useMemo(() => { return [ ...resultFilters, @@ -214,7 +226,7 @@ export const useCommonChartProps = ({ ]); return { - timeRange, + timeRange: chartTimeRange, filters, query: resultQuery, attributes, diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx index cdd188af84f8b..2080b9ff39d43 100644 --- a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx @@ -53,4 +53,12 @@ describe('useFindMaintenanceWindows', () => { expect(mockAddDanger).toBeCalledWith('Unable to load maintenance windows.') ); }); + + it('should not try to find maintenance windows if not enabled', async () => { + renderHook(() => useFindMaintenanceWindows({ enabled: false }), { + wrapper: appMockRenderer.AppWrapper, + }); + + await waitFor(() => expect(findMaintenanceWindows).toHaveBeenCalledTimes(0)); + }); }); diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts index 10b7f3402aca1..ab36720e893a5 100644 --- a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts @@ -10,7 +10,13 @@ import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../utils/kibana_react'; import { findMaintenanceWindows } from '../services/maintenance_windows_api/find'; -export const useFindMaintenanceWindows = () => { +interface UseFindMaintenanceWindowsProps { + enabled?: boolean; +} + +export const useFindMaintenanceWindows = (props?: UseFindMaintenanceWindowsProps) => { + const { enabled = true } = props || {}; + const { http, notifications: { toasts }, @@ -32,6 +38,7 @@ export const useFindMaintenanceWindows = () => { const { isLoading, + isFetching, data = [], refetch, } = useQuery({ @@ -41,11 +48,12 @@ export const useFindMaintenanceWindows = () => { refetchOnWindowFocus: false, retry: false, cacheTime: 0, + enabled, }); return { maintenanceWindows: data, - isLoading, + isLoading: enabled && (isLoading || isFetching), refetch, }; }; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx index 4d1b0588fda4a..c7c6a41cf7e93 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx @@ -44,7 +44,6 @@ export const LicensePrompt = React.memo(() => { > {i18n.UPGRADE_SUBSCRIPTION} - , { > {i18n.START_TRIAL} - , diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx index 5eae620c5a3e5..052fe5d271ec5 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx @@ -37,10 +37,13 @@ export const MaintenanceWindowsPage = React.memo(() => { docLinks, } = useKibana().services; const { isAtLeastPlatinum } = useLicense(); + const hasLicense = isAtLeastPlatinum(); const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation(); - const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows(); + const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows({ + enabled: hasLicense, + }); useBreadcrumbs(AlertingDeepLinkId.maintenanceWindows); @@ -56,7 +59,6 @@ export const MaintenanceWindowsPage = React.memo(() => { maintenanceWindows.length === 0 && showWindowMaintenance && writeWindowMaintenance; - const hasLicense = isAtLeastPlatinum(); const readOnly = showWindowMaintenance && !writeWindowMaintenance; diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 17319a71732e1..fb77d5f4a95d2 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -9,7 +9,7 @@ import * as rt from 'io-ts'; import { NumberFromString } from '../saved_object'; import { UserRt } from '../user'; -import { CommentResponseRt } from './comment'; +import { CommentRt } from './comment'; import { CasesStatusResponseRt, CaseStatusRt } from './status'; import { CaseConnectorRt } from '../connectors/connector'; import { CaseAssigneesRt } from './assignee'; @@ -258,7 +258,7 @@ export const CaseRt = rt.intersection([ version: rt.string, }), rt.partial({ - comments: rt.array(CommentResponseRt), + comments: rt.array(CommentRt), }), ]); diff --git a/x-pack/plugins/cases/common/api/cases/comment/index.ts b/x-pack/plugins/cases/common/api/cases/comment/index.ts index 7729673410a71..b6b35e7cc73b5 100644 --- a/x-pack/plugins/cases/common/api/cases/comment/index.ts +++ b/x-pack/plugins/cases/common/api/cases/comment/index.ts @@ -185,7 +185,7 @@ export const CommentRequestRt = rt.union([ PersistableStateAttachmentRt, ]); -export const CommentResponseRt = rt.intersection([ +export const CommentRt = rt.intersection([ CommentAttributesRt, rt.type({ id: rt.string, @@ -233,8 +233,6 @@ export const CommentResponseTypePersistableStateRt = rt.intersection([ }), ]); -export const AllCommentsResponseRT = rt.array(CommentResponseRt); - export const CommentPatchRequestRt = rt.intersection([ /** * Partial updates are not allowed. @@ -266,14 +264,14 @@ export const CommentPatchAttributesRt = rt.intersection([ rt.partial(CommentAttributesBasicRt.props), ]); -export const CommentsResponseRt = rt.type({ - comments: rt.array(CommentResponseRt), +export const CommentsFindResponseRt = rt.type({ + comments: rt.array(CommentRt), page: rt.number, per_page: rt.number, total: rt.number, }); -export const AllCommentsResponseRt = rt.array(CommentResponseRt); +export const CommentsRt = rt.array(CommentRt); export const FindQueryParamsRt = rt.partial({ ...SavedObjectFindOptionsRt.props, @@ -286,7 +284,7 @@ export const BulkGetAttachmentsRequestRt = rt.type({ }); export const BulkGetAttachmentsResponseRt = rt.type({ - attachments: AllCommentsResponseRt, + attachments: CommentsRt, errors: rt.array( rt.type({ error: rt.string, @@ -314,7 +312,7 @@ export type CommentAttributesNoSO = rt.TypeOf; export type CommentAttributesWithoutRefs = rt.TypeOf; export type CommentRequest = rt.TypeOf; export type BulkCreateCommentRequest = rt.TypeOf; -export type CommentResponse = rt.TypeOf; +export type Comment = rt.TypeOf; export type CommentResponseUserType = rt.TypeOf; export type CommentResponseAlertsType = rt.TypeOf; export type CommentResponseTypePersistableState = rt.TypeOf< @@ -324,8 +322,8 @@ export type CommentResponseExternalReferenceType = rt.TypeOf< typeof CommentResponseTypeExternalReferenceRt >; export type CommentResponseActionsType = rt.TypeOf; -export type AllCommentsResponse = rt.TypeOf; -export type CommentsResponse = rt.TypeOf; +export type Comments = rt.TypeOf; +export type CommentsFindResponse = rt.TypeOf; export type CommentPatchRequest = rt.TypeOf; export type CommentPatchAttributes = rt.TypeOf; export type CommentRequestUserType = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index c8a1e2172651f..343c95956a913 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -19,7 +19,7 @@ import type { ActionConnector, UserAction, SingleCaseMetricsResponse, - CommentResponse, + Comment, Case as CaseSnakeCase, UserActionFindResponse, FindTypeField as UserActionFindTypeField, @@ -83,7 +83,7 @@ export type CaseViewRefreshPropInterface = null | { refreshCase: () => Promise; }; -export type Comment = SnakeToCamelCase; +export type CommentUI = SnakeToCamelCase; export type AlertComment = SnakeToCamelCase; export type ExternalReferenceComment = SnakeToCamelCase; export type PersistableComment = SnakeToCamelCase; @@ -92,7 +92,7 @@ export type FindCaseUserActions = Omit, userActions: UserActionUI[]; }; export type CaseUserActionsStats = SnakeToCamelCase; -export type CaseUI = Omit, 'comments'> & { comments: Comment[] }; +export type CaseUI = Omit, 'comments'> & { comments: CommentUI[] }; export type CasesUI = Omit, 'cases'> & { cases: CaseUI[] }; export type CasesStatus = SnakeToCamelCase; export type CasesMetrics = SnakeToCamelCase; diff --git a/x-pack/plugins/cases/public/api/utils.ts b/x-pack/plugins/cases/public/api/utils.ts index 4f2dde86dfec1..904205ff4308c 100644 --- a/x-pack/plugins/cases/public/api/utils.ts +++ b/x-pack/plugins/cases/public/api/utils.ts @@ -16,12 +16,12 @@ import type { Case, UserActions, CommentRequest, - CommentResponse, + Comment, CaseResolveResponse, Cases, } from '../../common/api'; import { isCommentUserAction } from '../../common/utils/user_actions'; -import type { CasesUI, CaseUI, Comment, ResolvedCase } from '../containers/types'; +import type { CasesUI, CaseUI, CommentUI, ResolvedCase } from '../containers/types'; export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { @@ -65,11 +65,11 @@ export const convertCaseResolveToCamelCase = (res: CaseResolveResponse): Resolve }; }; -export const convertAttachmentsToCamelCase = (attachments: CommentResponse[]): Comment[] => { +export const convertAttachmentsToCamelCase = (attachments: Comment[]): CommentUI[] => { return attachments.map((attachment) => convertAttachmentToCamelCase(attachment)); }; -export const convertAttachmentToCamelCase = (attachment: CommentRequest): Comment => { +export const convertAttachmentToCamelCase = (attachment: CommentRequest): CommentUI => { if (isCommentRequestTypeExternalReference(attachment)) { return convertAttachmentToCamelExceptProperty(attachment, 'externalReferenceMetadata'); } @@ -78,7 +78,7 @@ export const convertAttachmentToCamelCase = (attachment: CommentRequest): Commen return convertAttachmentToCamelExceptProperty(attachment, 'persistableStateAttachmentState'); } - return convertToCamelCase(attachment); + return convertToCamelCase(attachment); }; export const convertUserActionsToCamelCase = (userActions: UserActions) => { @@ -102,7 +102,7 @@ export const convertUserActionsToCamelCase = (userActions: UserActions) => { const convertAttachmentToCamelExceptProperty = ( attachment: CommentRequest, key: string -): Comment => { +): CommentUI => { const intactValue = get(attachment, key); const attachmentWithoutIntactValue = omit(attachment, key); const camelCaseAttachmentWithoutIntactValue = convertToCamelCase(attachmentWithoutIntactValue); @@ -110,7 +110,7 @@ const convertAttachmentToCamelExceptProperty = ( return { ...camelCaseAttachmentWithoutIntactValue, [key]: intactValue, - } as Comment; + } as CommentUI; }; export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): CasesUI => ({ diff --git a/x-pack/plugins/cases/public/components/case_view/components/helpers.ts b/x-pack/plugins/cases/public/components/case_view/components/helpers.ts index 0fb247dda5282..b3ddc403cc661 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/helpers.ts +++ b/x-pack/plugins/cases/public/components/case_view/components/helpers.ts @@ -6,10 +6,10 @@ */ import { CommentType } from '../../../../common/api'; -import type { Comment } from '../../../containers/types'; +import type { CommentUI } from '../../../containers/types'; -export const getManualAlertIds = (comments: Comment[]): string[] => { - const dedupeAlerts = comments.reduce((alertIds, comment: Comment) => { +export const getManualAlertIds = (comments: CommentUI[]): string[] => { + const dedupeAlerts = comments.reduce((alertIds, comment: CommentUI) => { if (comment.type === CommentType.alert) { const ids = Array.isArray(comment.alertId) ? comment.alertId : [comment.alertId]; ids.forEach((id) => alertIds.add(id)); @@ -20,8 +20,8 @@ export const getManualAlertIds = (comments: Comment[]): string[] => { return Array.from(dedupeAlerts); }; -export const getRegistrationContextFromAlerts = (comments: Comment[]): string[] => { - const dedupeRegistrationContext = comments.reduce((registrationContexts, comment: Comment) => { +export const getRegistrationContextFromAlerts = (comments: CommentUI[]): string[] => { + const dedupeRegistrationContext = comments.reduce((registrationContexts, comment: CommentUI) => { if (comment.type === CommentType.alert) { const indices = Array.isArray(comment.index) ? comment.index : [comment.index]; indices.forEach((index) => { diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx index 434a95cc69b1c..b4fbc12ea48e4 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx @@ -12,7 +12,7 @@ import type { CommentUserAction } from '../../../../common/api'; import { Actions, CommentType } from '../../../../common/api'; import type { UserActionBuilder, UserActionBuilderArgs, UserActionResponse } from '../types'; import { createCommonUpdateUserActionBuilder } from '../common'; -import type { Comment } from '../../../containers/types'; +import type { CommentUI } from '../../../containers/types'; import * as i18n from './translations'; import { createUserAttachmentUserActionBuilder } from './user'; import { createAlertAttachmentUserActionBuilder } from './alert'; @@ -164,7 +164,7 @@ const getCreateCommentUserAction = ({ actionsNavigation, }: { userAction: UserActionResponse; - comment: Comment; + comment: CommentUI; } & Omit< UserActionBuilderArgs, 'comments' | 'index' | 'handleOutlineComment' | 'currentUserProfile' diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/registered_attachments.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/registered_attachments.tsx index 71457cb49e089..2aa419d8ece5c 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/registered_attachments.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/registered_attachments.tsx @@ -24,7 +24,7 @@ import type { import { AttachmentActionType } from '../../../client/attachment_framework/types'; import { UserActionTimestamp } from '../timestamp'; import type { AttachmentTypeRegistry } from '../../../../common/registry'; -import type { CommentResponse } from '../../../../common/api'; +import type { Comment } from '../../../../common/api'; import type { UserActionBuilder, UserActionBuilderArgs } from '../types'; import type { SnakeToCamelCase } from '../../../../common/types'; import { @@ -69,7 +69,7 @@ const getAttachmentRenderer = memoize(() => { }); export const createRegisteredAttachmentUserActionBuilder = < - C extends CommentResponse, + C extends Comment, // eslint-disable-next-line @typescript-eslint/no-explicit-any R extends AttachmentTypeRegistry> >({ diff --git a/x-pack/plugins/cases/public/components/user_actions/helpers.test.ts b/x-pack/plugins/cases/public/components/user_actions/helpers.test.ts index bd1873a591bee..0e84b9463694b 100644 --- a/x-pack/plugins/cases/public/components/user_actions/helpers.test.ts +++ b/x-pack/plugins/cases/public/components/user_actions/helpers.test.ts @@ -7,10 +7,10 @@ import { CommentType } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; -import type { Comment } from '../../containers/types'; +import type { CommentUI } from '../../containers/types'; import { isUserActionTypeSupported, getManualAlertIdsWithNoRuleId } from './helpers'; -const comments: Comment[] = [ +const comments: CommentUI[] = [ { type: CommentType.alert, alertId: 'alert-id-1', diff --git a/x-pack/plugins/cases/public/components/user_actions/helpers.ts b/x-pack/plugins/cases/public/components/user_actions/helpers.ts index fe9e5b2bb5cff..a3bd67bd6e655 100644 --- a/x-pack/plugins/cases/public/components/user_actions/helpers.ts +++ b/x-pack/plugins/cases/public/components/user_actions/helpers.ts @@ -8,15 +8,15 @@ import { isEmpty } from 'lodash'; import { CommentType } from '../../../common/api'; -import type { Comment } from '../../containers/types'; +import type { CommentUI } from '../../containers/types'; import { SUPPORTED_ACTION_TYPES } from './constants'; import type { SupportedUserActionTypes } from './types'; export const isUserActionTypeSupported = (type: string): type is SupportedUserActionTypes => SUPPORTED_ACTION_TYPES.includes(type as SupportedUserActionTypes); -export const getManualAlertIdsWithNoRuleId = (comments: Comment[]): string[] => { - const dedupeAlerts = comments.reduce((alertIds, comment: Comment) => { +export const getManualAlertIdsWithNoRuleId = (comments: CommentUI[]): string[] => { + const dedupeAlerts = comments.reduce((alertIds, comment: CommentUI) => { if (comment.type === CommentType.alert && isEmpty(comment.rule.id)) { const ids = Array.isArray(comment.alertId) ? comment.alertId : [comment.alertId]; ids.forEach((id) => alertIds.add(id)); diff --git a/x-pack/plugins/cases/public/components/user_actions/types.ts b/x-pack/plugins/cases/public/components/user_actions/types.ts index 4e2660734052f..1e36ea22e2033 100644 --- a/x-pack/plugins/cases/public/components/user_actions/types.ts +++ b/x-pack/plugins/cases/public/components/user_actions/types.ts @@ -13,7 +13,7 @@ import type { CaseUI, CaseConnectors, UserActionUI, - Comment, + CommentUI, UseFetchAlertData, CaseUserActionsStats, } from '../../containers/types'; @@ -55,7 +55,7 @@ export interface UserActionBuilderArgs { persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; caseConnectors: CaseConnectors; userAction: UserActionUI; - comments: Comment[]; + comments: CommentUI[]; index: number; commentRefs: React.MutableRefObject< Record diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index d35d001d41bc4..ba8724637038a 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -6,7 +6,7 @@ */ import type { FileJSON } from '@kbn/shared-ux-file-types'; -import type { ActionLicense, CasesUI, CaseUI, CasesStatus, UserActionUI, Comment } from './types'; +import type { ActionLicense, CasesUI, CaseUI, CasesStatus, UserActionUI, CommentUI } from './types'; import type { ResolvedCase, @@ -28,7 +28,7 @@ import type { CasesStatusResponse, UserAction, UserActions, - CommentResponse, + Comment, ActionCategory, UserActionTypes, UserActionWithResponse, @@ -73,7 +73,7 @@ export const elasticUser = { export const tags: string[] = ['coke', 'pepsi']; -export const basicComment: Comment = { +export const basicComment: CommentUI = { comment: 'Solve this fast!', type: CommentType.user, id: basicCommentId, @@ -125,7 +125,7 @@ export const alertCommentWithIndices: AlertComment = { version: 'WzQ3LDFc', }; -export const hostIsolationComment = (overrides?: Record): Comment => { +export const hostIsolationComment = (overrides?: Record): CommentUI => { return { type: CommentType.actions, comment: 'I just isolated the host!', @@ -151,7 +151,7 @@ export const hostIsolationComment = (overrides?: Record): Comme }; }; -export const hostReleaseComment: () => Comment = () => { +export const hostReleaseComment: () => CommentUI = () => { return { type: CommentType.actions, comment: 'I just released the host!', @@ -363,7 +363,7 @@ export const basicCasePost: CaseUI = { updatedBy: null, }; -export const basicCommentPatch: Comment = { +export const basicCommentPatch: CommentUI = { ...basicComment, updatedAt: basicUpdatedAt, updatedBy: { @@ -469,7 +469,7 @@ export const elasticUserSnake = { email: 'leslie.knope@elastic.co', }; -export const basicCommentSnake: CommentResponse = { +export const basicCommentSnake: Comment = { comment: 'Solve this fast!', type: CommentType.user, id: basicCommentId, @@ -483,7 +483,7 @@ export const basicCommentSnake: CommentResponse = { version: 'WzQ3LDFc', }; -export const externalReferenceAttachmentSnake: CommentResponse = { +export const externalReferenceAttachmentSnake: Comment = { type: CommentType.externalReference, id: 'external-reference-comment-id', externalReferenceId: 'my-id', @@ -500,7 +500,7 @@ export const externalReferenceAttachmentSnake: CommentResponse = { version: 'WzQ3LDFc', }; -export const persistableStateAttachmentSnake: CommentResponse = { +export const persistableStateAttachmentSnake: Comment = { type: CommentType.persistableState, id: 'persistable-state-comment-id', persistableStateAttachmentState: { test_foo: 'foo' }, diff --git a/x-pack/plugins/cases/server/client/attachments/client.ts b/x-pack/plugins/cases/server/client/attachments/client.ts index e4244b32d1e7d..987e3c17575e3 100644 --- a/x-pack/plugins/cases/server/client/attachments/client.ts +++ b/x-pack/plugins/cases/server/client/attachments/client.ts @@ -7,11 +7,11 @@ import type { AlertResponse, - AllCommentsResponse, + Comments, BulkGetAttachmentsResponse, Case, - CommentResponse, - CommentsResponse, + Comment, + CommentsFindResponse, } from '../../../common/api'; import type { CasesClient } from '../client'; @@ -60,7 +60,7 @@ export interface AttachmentsSubClient { /** * Retrieves all comments matching the search criteria. */ - find(findArgs: FindArgs): Promise; + find(findArgs: FindArgs): Promise; /** * Retrieves all alerts attach to a case given a single case ID */ @@ -68,11 +68,11 @@ export interface AttachmentsSubClient { /** * Gets all attachments for a single case. */ - getAll(getAllArgs: GetAllArgs): Promise; + getAll(getAllArgs: GetAllArgs): Promise; /** * Retrieves a single attachment for a case. */ - get(getArgs: GetArgs): Promise; + get(getArgs: GetArgs): Promise; /** * Updates a specific attachment. * diff --git a/x-pack/plugins/cases/server/client/attachments/get.ts b/x-pack/plugins/cases/server/client/attachments/get.ts index 91160e857c5cf..79832e98c8016 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.ts @@ -8,12 +8,12 @@ import type { SavedObject } from '@kbn/core/server'; import type { AlertResponse, - AllCommentsResponse, + Comments, AttributesTypeAlerts, - CommentResponse, - CommentsResponse, + Comment, + CommentsFindResponse, } from '../../../common/api'; -import { AllCommentsResponseRt, CommentResponseRt, CommentsResponseRt } from '../../../common/api'; +import { CommentsRt, CommentRt, CommentsFindResponseRt } from '../../../common/api'; import { defaultSortField, transformComments, @@ -100,7 +100,7 @@ export const getAllAlertsAttachToCase = async ( export async function find( { caseID, queryParams }: FindArgs, clientArgs: CasesClientArgs -): Promise { +): Promise { const { unsecuredSavedObjectsClient, services: { caseService }, @@ -159,7 +159,7 @@ export async function find( })) ); - return CommentsResponseRt.encode(transformComments(theComments)); + return CommentsFindResponseRt.encode(transformComments(theComments)); } catch (error) { throw createCaseError({ message: `Failed to find comments case id: ${caseID}: ${error}`, @@ -175,7 +175,7 @@ export async function find( export async function get( { attachmentID, caseID }: GetArgs, clientArgs: CasesClientArgs -): Promise { +): Promise { const { services: { attachmentService }, logger, @@ -192,7 +192,7 @@ export async function get( operation: Operations.getComment, }); - return CommentResponseRt.encode(flattenCommentSavedObject(comment)); + return CommentRt.encode(flattenCommentSavedObject(comment)); } catch (error) { throw createCaseError({ message: `Failed to get comment case id: ${caseID} attachment id: ${attachmentID}: ${error}`, @@ -208,7 +208,7 @@ export async function get( export async function getAll( { caseID }: GetAllArgs, clientArgs: CasesClientArgs -): Promise { +): Promise { const { services: { caseService }, logger, @@ -232,7 +232,7 @@ export async function getAll( comments.saved_objects.map((comment) => ({ id: comment.id, owner: comment.attributes.owner })) ); - return AllCommentsResponseRt.encode(flattenCommentSavedObjects(comments.saved_objects)); + return CommentsRt.encode(flattenCommentSavedObjects(comments.saved_objects)); } catch (error) { throw createCaseError({ message: `Failed to get all comments case id: ${caseID}: ${error}`, diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 48ece730f3ead..527c517e4a79e 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -6,7 +6,7 @@ */ import type { - CommentResponse, + Comment, CommentResponseAlertsType, ConnectorMappingsAttributes, CaseUserActionsDeprecatedResponse, @@ -31,7 +31,7 @@ const entity = { updatedBy: null, }; -export const comment: CommentResponse = { +export const comment: Comment = { id: 'comment-user-1', comment: 'Wow, good luck catching that bad meanie!', type: CommentType.user as const, @@ -53,7 +53,7 @@ export const comment: CommentResponse = { version: 'WzEsMV0=', }; -export const isolateCommentActions: CommentResponse = { +export const isolateCommentActions: Comment = { id: 'mock-action-comment-1', comment: 'Isolating this for investigation', type: CommentType.actions as const, @@ -84,7 +84,7 @@ export const isolateCommentActions: CommentResponse = { version: 'WzEsMV0=', }; -export const releaseCommentActions: CommentResponse = { +export const releaseCommentActions: Comment = { id: 'mock-action-comment-2', comment: 'Releasing this for investigation', type: CommentType.actions as const, @@ -115,7 +115,7 @@ export const releaseCommentActions: CommentResponse = { version: 'WzEsMV0=', }; -export const isolateCommentActionsMultipleTargets: CommentResponse = { +export const isolateCommentActionsMultipleTargets: Comment = { id: 'mock-action-comment-3', comment: 'Isolating this for investigation', type: CommentType.actions as const, @@ -150,7 +150,7 @@ export const isolateCommentActionsMultipleTargets: CommentResponse = { version: 'WzEsMV0=', }; -export const commentAlert: CommentResponse = { +export const commentAlert: Comment = { id: 'comment-alert-1', alertId: 'alert-id-1', index: 'alert-index-1', @@ -186,7 +186,7 @@ export const commentAlertMultipleIds: CommentResponseAlertsType = { owner: SECURITY_SOLUTION_OWNER, }; -export const commentExternalReference: CommentResponse = { +export const commentExternalReference: Comment = { id: 'comment-external-reference-1', type: CommentType.externalReference as const, externalReferenceId: 'my-id', @@ -213,7 +213,7 @@ export const commentExternalReference: CommentResponse = { version: 'WzEsMV0=', }; -export const commentPersistableState: CommentResponse = { +export const commentPersistableState: Comment = { id: 'comment-persistable-state-1', type: CommentType.persistableState, persistableStateAttachmentTypeId: '.test', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index ecf5aaf441447..5b43b4ae68766 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -16,7 +16,7 @@ import type { ActionConnector, CaseFullExternalService, Case, - CommentResponse, + Comment, User, CaseAttributes, CaseAssignees, @@ -77,7 +77,7 @@ export const getLatestPushInfo = ( return null; }; -const getCommentContent = (comment: CommentResponse): string => { +const getCommentContent = (comment: Comment): string => { if (comment.type === CommentType.user) { return comment.comment; } else if (comment.type === CommentType.alert) { diff --git a/x-pack/plugins/cases/server/client/typedoc_interfaces.ts b/x-pack/plugins/cases/server/client/typedoc_interfaces.ts index f0fe44d4d44da..76ff974f2c583 100644 --- a/x-pack/plugins/cases/server/client/typedoc_interfaces.ts +++ b/x-pack/plugins/cases/server/client/typedoc_interfaces.ts @@ -14,7 +14,7 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import type { - AllCommentsResponse, + Comments, CasePostRequest, CaseResolveResponse, Case, @@ -26,7 +26,7 @@ import type { CasesPatchRequest, Cases, UserActions, - CommentsResponse, + CommentsFindResponse, CasesBulkGetResponse, } from '../../common/api'; @@ -48,7 +48,7 @@ export interface ICasesConfigureResponse extends CasesConfigureResponse {} export interface ICasesConfigureRequest extends CasesConfigureRequest {} export interface ICasesConfigurePatch extends CasesConfigurePatch {} -export interface ICommentsResponse extends CommentsResponse {} -export interface IAllCommentsResponse extends AllCommentsResponse {} +export interface ICommentsResponse extends CommentsFindResponse {} +export interface IAllCommentsResponse extends Comments {} export interface ICaseUserActionsResponse extends UserActions {} diff --git a/x-pack/plugins/cases/server/common/types/attachments.ts b/x-pack/plugins/cases/server/common/types/attachments.ts index 55b9990c5fb12..c48f250999a97 100644 --- a/x-pack/plugins/cases/server/common/types/attachments.ts +++ b/x-pack/plugins/cases/server/common/types/attachments.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { SavedObject } from '@kbn/core/server'; import type { JsonValue } from '@kbn/utility-types'; +import type { CommentAttributes } from '../../../common/api'; import type { User } from './user'; interface AttachmentCommonPersistedAttributes { @@ -46,3 +48,6 @@ export interface AttachmentRequestAttributes { export type AttachmentPersistedAttributes = AttachmentRequestAttributes & AttachmentCommonPersistedAttributes; + +export type AttachmentTransformedAttributes = CommentAttributes; +export type AttachmentSavedObjectTransformed = SavedObject; diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index b04c9fd596503..75ca980af639b 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -35,8 +35,8 @@ import type { CommentRequestActionsType, CommentRequestAlertType, CommentRequestUserType, - CommentResponse, - CommentsResponse, + Comment, + CommentsFindResponse, User, } from '../../common/api'; import { @@ -133,7 +133,7 @@ export const flattenCaseSavedObject = ({ export const transformComments = ( comments: SavedObjectsFindResponse -): CommentsResponse => ({ +): CommentsFindResponse => ({ page: comments.page, per_page: comments.per_page, total: comments.total, @@ -142,14 +142,14 @@ export const transformComments = ( export const flattenCommentSavedObjects = ( savedObjects: Array> -): CommentResponse[] => - savedObjects.reduce((acc: CommentResponse[], savedObject: SavedObject) => { +): Comment[] => + savedObjects.reduce((acc: Comment[], savedObject: SavedObject) => { return [...acc, flattenCommentSavedObject(savedObject)]; }, []); export const flattenCommentSavedObject = ( savedObject: SavedObject -): CommentResponse => ({ +): Comment => ({ id: savedObject.id, version: savedObject.version ?? '0', ...savedObject.attributes, diff --git a/x-pack/plugins/cases/server/services/attachments/index.ts b/x-pack/plugins/cases/server/services/attachments/index.ts index e76b93ce0e5b9..7fead24a65162 100644 --- a/x-pack/plugins/cases/server/services/attachments/index.ts +++ b/x-pack/plugins/cases/server/services/attachments/index.ts @@ -6,7 +6,6 @@ */ import type { - SavedObject, SavedObjectsBulkResponse, SavedObjectsBulkUpdateResponse, SavedObjectsFindResponse, @@ -14,7 +13,6 @@ import type { } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { CommentAttributes as AttachmentAttributes } from '../../../common/api'; import { CommentType } from '../../../common/api'; import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT } from '../../../common/constants'; import { buildFilter, combineFilters } from '../../client/utils'; @@ -38,7 +36,11 @@ import type { UpdateAttachmentArgs, } from './types'; import { AttachmentGetter } from './operations/get'; -import type { AttachmentPersistedAttributes } from '../../common/types/attachments'; +import type { + AttachmentPersistedAttributes, + AttachmentTransformedAttributes, + AttachmentSavedObjectTransformed, +} from '../../common/types/attachments'; export class AttachmentService { private readonly _getter: AttachmentGetter; @@ -155,7 +157,7 @@ export class AttachmentService { references, id, refresh, - }: CreateAttachmentArgs): Promise> { + }: CreateAttachmentArgs): Promise { try { this.context.log.debug(`Attempting to POST a new comment`); @@ -190,7 +192,7 @@ export class AttachmentService { public async bulkCreate({ attachments, refresh, - }: BulkCreateAttachments): Promise> { + }: BulkCreateAttachments): Promise> { try { this.context.log.debug(`Attempting to bulk create attachments`); const res = @@ -231,7 +233,7 @@ export class AttachmentService { attachmentId, updatedAttributes, options, - }: UpdateAttachmentArgs): Promise> { + }: UpdateAttachmentArgs): Promise> { try { this.context.log.debug(`Attempting to UPDATE comment ${attachmentId}`); @@ -278,7 +280,9 @@ export class AttachmentService { public async bulkUpdate({ comments, refresh, - }: BulkUpdateAttachmentArgs): Promise> { + }: BulkUpdateAttachmentArgs): Promise< + SavedObjectsBulkUpdateResponse + > { try { this.context.log.debug( `Attempting to UPDATE comments ${comments.map((c) => c.attachmentId).join(', ')}` @@ -336,7 +340,7 @@ export class AttachmentService { options, }: { options?: SavedObjectFindOptionsKueryNode; - }): Promise> { + }): Promise> { try { this.context.log.debug(`Attempting to find comments`); const res = diff --git a/x-pack/plugins/cases/server/services/attachments/operations/get.ts b/x-pack/plugins/cases/server/services/attachments/operations/get.ts index 4aa5c65ebdcc1..c5ba13633ab43 100644 --- a/x-pack/plugins/cases/server/services/attachments/operations/get.ts +++ b/x-pack/plugins/cases/server/services/attachments/operations/get.ts @@ -8,7 +8,11 @@ import type { SavedObject } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { FILE_SO_TYPE } from '@kbn/files-plugin/common'; -import type { AttachmentPersistedAttributes } from '../../../common/types/attachments'; +import type { + AttachmentPersistedAttributes, + AttachmentTransformedAttributes, + AttachmentSavedObjectTransformed, +} from '../../../common/types/attachments'; import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, @@ -16,11 +20,7 @@ import { MAX_DOCS_PER_PAGE, } from '../../../../common/constants'; import { buildFilter, combineFilters } from '../../../client/utils'; -import type { - AttachmentTotals, - AttributesTypeAlerts, - CommentAttributes as AttachmentAttributes, -} from '../../../../common/api'; +import type { AttachmentTotals, AttributesTypeAlerts } from '../../../../common/api'; import { CommentType } from '../../../../common/api'; import type { AlertIdsAggsResult, @@ -42,7 +42,7 @@ export class AttachmentGetter { public async bulkGet( attachmentIds: string[] - ): Promise> { + ): Promise> { try { this.context.log.debug( `Attempting to retrieve attachments with ids: ${attachmentIds.join()}` @@ -190,9 +190,7 @@ export class AttachmentGetter { } } - public async get({ - attachmentId, - }: GetAttachmentArgs): Promise> { + public async get({ attachmentId }: GetAttachmentArgs): Promise { try { this.context.log.debug(`Attempting to GET attachment ${attachmentId}`); const res = await this.context.unsecuredSavedObjectsClient.get( @@ -302,7 +300,7 @@ export class AttachmentGetter { }: { caseId: string; fileIds: string[]; - }): Promise>> { + }): Promise { try { this.context.log.debug('Attempting to find file attachments'); @@ -331,7 +329,7 @@ export class AttachmentGetter { } ); - const foundAttachments: Array> = []; + const foundAttachments: AttachmentSavedObjectTransformed[] = []; for await (const attachmentSavedObjects of finder.find()) { foundAttachments.push( diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts index 93c6d610d5539..42fd0f03a25e2 100644 --- a/x-pack/plugins/cases/server/services/cases/index.ts +++ b/x-pack/plugins/cases/server/services/cases/index.ts @@ -27,7 +27,7 @@ import { CASE_SAVED_OBJECT, MAX_DOCS_PER_PAGE, } from '../../../common/constants'; -import type { Case, CommentAttributes, User, CaseStatuses } from '../../../common/api'; +import type { Case, User, CaseStatuses } from '../../../common/api'; import { caseStatuses } from '../../../common/api'; import type { SavedObjectFindOptionsKueryNode } from '../../common/types'; import { defaultSortField, flattenCaseSavedObject } from '../../common/utils'; @@ -66,6 +66,7 @@ import type { PatchCaseArgs, PatchCasesArgs, } from './types'; +import type { AttachmentTransformedAttributes } from '../../common/types/attachments'; export class CasesService { private readonly log: Logger; @@ -343,7 +344,7 @@ export class CasesService { private async getAllComments({ id, options, - }: FindCommentsArgs): Promise> { + }: FindCommentsArgs): Promise> { try { this.log.debug(`Attempting to GET all comments internal for id ${JSON.stringify(id)}`); if (options?.page !== undefined || options?.perPage !== undefined) { @@ -378,7 +379,7 @@ export class CasesService { public async getAllCaseComments({ id, options, - }: FindCaseCommentsArgs): Promise> { + }: FindCaseCommentsArgs): Promise> { try { const refs = this.asArray(id).map((caseID) => ({ type: CASE_SAVED_OBJECT, id: caseID })); if (refs.length <= 0) { diff --git a/x-pack/plugins/cases/server/services/so_references.ts b/x-pack/plugins/cases/server/services/so_references.ts index 8a3cc747c752a..24896b7601a5c 100644 --- a/x-pack/plugins/cases/server/services/so_references.ts +++ b/x-pack/plugins/cases/server/services/so_references.ts @@ -12,8 +12,8 @@ import type { } from '@kbn/core/server'; import { isEqual, uniqWith } from 'lodash'; import type { - CommentAttributesNoSO, CommentAttributes, + CommentAttributesNoSO, CommentPatchAttributes, } from '../../common/api'; import type { PersistableStateAttachmentTypeRegistry } from '../attachment_framework/persistable_state_registry'; @@ -25,6 +25,8 @@ import { EXTERNAL_REFERENCE_REF_NAME } from '../common/constants'; import type { AttachmentPersistedAttributes, AttachmentRequestAttributes, + AttachmentTransformedAttributes, + AttachmentSavedObjectTransformed, } from '../common/types/attachments'; import { isCommentRequestTypeExternalReferenceSO } from './type_guards'; import type { PartialField } from '../types'; @@ -56,11 +58,11 @@ type OptionalAttributes = PartialField, 'attributes'>; export const injectAttachmentAttributesAndHandleErrors = ( savedObject: OptionalAttributes, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): OptionalAttributes => { +): OptionalAttributes => { if (!hasAttributes(savedObject)) { // we don't actually have an attributes field here so the type doesn't matter, this cast is to get the types to stop // complaining though - return savedObject as OptionalAttributes; + return savedObject as OptionalAttributes; } return injectAttachmentSOAttributesFromRefs(savedObject, persistableStateAttachmentTypeRegistry); @@ -73,9 +75,9 @@ const hasAttributes = (savedObject: OptionalAttributes): savedObject is Sa export const injectAttachmentSOAttributesFromRefs = ( savedObject: SavedObject, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): SavedObject => { +): AttachmentSavedObjectTransformed => { const soExtractor = getAttachmentSOExtractor(savedObject.attributes); - const so = soExtractor.populateFieldsFromReferences(savedObject); + const so = soExtractor.populateFieldsFromReferences(savedObject); const injectedAttributes = injectPersistableReferencesToSO(so.attributes, so.references, { persistableStateAttachmentTypeRegistry, }); @@ -87,9 +89,9 @@ export const injectAttachmentSOAttributesFromRefsForPatch = ( updatedAttributes: CommentPatchAttributes, savedObject: SavedObjectsUpdateResponse, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): SavedObjectsUpdateResponse => { +): SavedObjectsUpdateResponse => { const soExtractor = getAttachmentSOExtractor(savedObject.attributes); - const so = soExtractor.populateFieldsFromReferencesForPatch({ + const so = soExtractor.populateFieldsFromReferencesForPatch({ dataBeforeRequest: updatedAttributes, dataReturnedFromRequest: savedObject, }); @@ -105,7 +107,7 @@ export const injectAttachmentSOAttributesFromRefsForPatch = ( return { ...so, attributes: { ...so.attributes, ...injectedAttributes }, - } as SavedObjectsUpdateResponse; + } as SavedObjectsUpdateResponse; }; interface ExtractionResults { diff --git a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts index 953c49b493b37..38e445c50bef6 100644 --- a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts @@ -37,6 +37,26 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ }), serviceType: 'mysql', }, + { + iconPath: 'mssql.svg', + isBeta: true, + isNative: true, + keywords: ['mssql', 'microsoft', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.microsoftSQL.name', { + defaultMessage: 'Microsoft SQL', + }), + serviceType: 'mssql', + }, + { + iconPath: 'postgresql.svg', + isBeta: true, + isNative: true, + keywords: ['postgresql', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.postgresql.name', { + defaultMessage: 'Postgresql', + }), + serviceType: 'postgresql', + }, { iconPath: 'azure_blob_storage.svg', isBeta: true, @@ -57,16 +77,6 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ }), serviceType: 'google_cloud_storage', }, - { - iconPath: 'mssql.svg', - isBeta: true, - isNative: false, - keywords: ['mssql', 'microsoft', 'sql', 'database', 'connector'], - name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.microsoftSQL.name', { - defaultMessage: 'Microsoft SQL', - }), - serviceType: 'mssql', - }, { iconPath: 'network_drive.svg', isBeta: true, @@ -87,16 +97,6 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ }), serviceType: 'oracle', }, - { - iconPath: 'postgresql.svg', - isBeta: true, - isNative: false, - keywords: ['postgresql', 'sql', 'database', 'connector'], - name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.postgresql.name', { - defaultMessage: 'Postgresql', - }), - serviceType: 'postgresql', - }, { iconPath: 's3.svg', isBeta: true, diff --git a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts index a130d1260d5c4..cbdc221eb6294 100644 --- a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts +++ b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts @@ -146,6 +146,260 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record; started_at: string | null; status: SyncStatus; + total_document_count: number | null; trigger_method: TriggerMethod; worker_hostname: string | null; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts index 0f864125659fa..b0b5d4aa555af 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts @@ -39,7 +39,6 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ default_value: '', depends_on: [], display: DisplayType.TEXTBOX, - key: 'foo', label: 'bar', options: [], order: 1, @@ -48,6 +47,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'barbar', }, }, @@ -151,7 +151,6 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ default_value: '', depends_on: [], display: DisplayType.TEXTBOX, - key: 'foo', label: 'bar', options: [], order: 1, @@ -160,6 +159,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'barbar', }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/sync_job.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/sync_job.mock.ts index 04a3908f5d5b6..e09a221df89bf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/sync_job.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/sync_job.mock.ts @@ -33,6 +33,7 @@ export const syncJob: ConnectorSyncJob = { metadata: {}, started_at: '2022-09-05T14:59:39.816+00:00', status: SyncStatus.COMPLETED, + total_document_count: null, trigger_method: TriggerMethod.ON_DEMAND, worker_hostname: 'hostname_fake', }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts index 4e2a0ceda0fbc..bc4d669f699a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts @@ -49,7 +49,6 @@ export const connectorIndex: ConnectorViewIndex = { default_value: '', depends_on: [], display: DisplayType.TEXTBOX, - key: 'foo', label: 'bar', options: [], order: 1, @@ -58,6 +57,7 @@ export const connectorIndex: ConnectorViewIndex = { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'barbar', }, }, @@ -165,7 +165,6 @@ export const crawlerIndex: CrawlerViewIndex = { default_value: '', depends_on: [], display: DisplayType.TEXTBOX, - key: 'foo', label: 'bar', options: [], order: 1, @@ -174,6 +173,7 @@ export const crawlerIndex: CrawlerViewIndex = { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'barbar', }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts index 2211f64645c35..4092050b3b516 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts @@ -63,6 +63,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'oldBar', }, }, @@ -83,6 +84,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'oldBar', }, }, @@ -101,6 +103,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'oldBar', }, @@ -121,6 +124,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, }); @@ -139,6 +143,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, }, @@ -157,6 +162,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'fourthBar', }, @@ -178,6 +184,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'foofoo', }, password: { @@ -192,6 +199,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, restricted: { @@ -206,6 +214,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: ['advanced'], + validations: [], value: 'I am restricted', }, shownDependent1: { @@ -220,6 +229,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (one dependency)', }, shownDependent2: { @@ -237,6 +247,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (multiple dependencies)', }, hiddenDependent1: { @@ -251,6 +262,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (one dependency)', }, hiddenDependent2: { @@ -268,6 +280,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (multiple dependencies)', }, }); @@ -284,6 +297,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'foofoo', }, password: { @@ -298,6 +312,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, restricted: { @@ -312,6 +327,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: ['advanced'], + validations: [], value: 'I am restricted', }, shownDependent1: { @@ -326,6 +342,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (one dependency)', }, shownDependent2: { @@ -343,6 +360,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (multiple dependencies)', }, hiddenDependent1: { @@ -357,6 +375,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (one dependency)', }, hiddenDependent2: { @@ -374,6 +393,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (multiple dependencies)', }, }); @@ -409,6 +429,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'foofoo', }, password: { @@ -423,6 +444,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, restricted: { @@ -437,6 +459,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: ['advanced'], + validations: [], value: 'I am restricted', }, shownDependent1: { @@ -451,6 +474,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (one dependency)', }, shownDependent2: { @@ -468,6 +492,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (multiple dependencies)', }, hiddenDependent1: { @@ -482,6 +507,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (one dependency)', }, hiddenDependent2: { @@ -499,6 +525,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (multiple dependencies)', }, }, @@ -517,6 +544,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'foofoo', }, @@ -534,6 +562,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'fourthBar', }, @@ -551,6 +580,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'I should appear (one dependency)', }, @@ -571,6 +601,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'I should appear (multiple dependencies)', }, @@ -588,6 +619,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fafa', }, password: { @@ -602,6 +634,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'fourthBar', }, restricted: { @@ -616,6 +649,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: ['advanced'], + validations: [], value: 'I am restricted', }, shownDependent1: { @@ -630,6 +664,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (one dependency)', }, shownDependent2: { @@ -647,6 +682,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should appear (multiple dependencies)', }, hiddenDependent1: { @@ -661,6 +697,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (one dependency)', }, hiddenDependent2: { @@ -678,6 +715,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], value: 'I should hide (multiple dependencies)', }, }, @@ -696,6 +734,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'fafa', }, @@ -713,6 +752,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'fourthBar', }, @@ -730,6 +770,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'I should hide (one dependency)', }, @@ -750,6 +791,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'I should hide (multiple dependencies)', }, @@ -778,6 +820,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'barbar', }, @@ -818,6 +861,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'barbar', }, @@ -843,6 +887,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'barbar', }, @@ -869,6 +914,7 @@ describe('ConnectorConfigurationLogic', () => { tooltip: '', type: FieldType.STRING, ui_restrictions: [], + validations: [], validation_errors: [], value: 'Barbara', }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts index 065fe42d5bb0b..1aec514c0cd0e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts @@ -73,6 +73,7 @@ export interface ConfigEntry { type: FieldType; ui_restrictions: string[]; validation_errors: string[]; + validations?: string[]; value: string | number | boolean | null; } @@ -155,7 +156,7 @@ function validIntInput(value: string | number | boolean | null): boolean { // reject non integers (including x.0 floats), but don't validate if empty return (value !== null || value !== '') && (isNaN(Number(value)) || - !Number.isSafeInteger(value) || + !Number.isSafeInteger(Number(value)) || ensureStringType(value).indexOf('.') >= 0) ? false : true; @@ -328,6 +329,7 @@ export const ConnectorConfigurationLogic = kea< type, // eslint-disable-next-line @typescript-eslint/naming-convention ui_restrictions, + validations, value, } ) => ({ @@ -344,6 +346,7 @@ export const ConnectorConfigurationLogic = kea< tooltip, type, ui_restrictions, + validations: validations ?? [], value: ensureCorrectTyping(type, value), }, }), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_rules.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_rules.tsx index 1ef84d54a13dd..c28b65bffb70d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_rules.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_rules.tsx @@ -23,6 +23,8 @@ import { import { i18n } from '@kbn/i18n'; +import { BetaBadge } from '../../../../../shared/beta/beta_badge'; + import { docLinks } from '../../../../../shared/doc_links'; import { FilteringRulesTable } from '../../../shared/filtering_rules_table/filtering_rules_table'; @@ -67,13 +69,20 @@ export const ConnectorSyncRules: React.FC = () => { - -

- {i18n.translate('xpack.enterpriseSearch.index.connector.syncRules.title', { - defaultMessage: 'Sync rules ', - })} -

-
+ + + +

+ {i18n.translate('xpack.enterpriseSearch.index.connector.syncRules.title', { + defaultMessage: 'Sync rules ', + })} +

+
+
+ + + +

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/edit_sync_rules_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/edit_sync_rules_flyout.tsx index 47278a569a184..e675b7a149d3b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/edit_sync_rules_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/edit_sync_rules_flyout.tsx @@ -24,6 +24,7 @@ import { import { i18n } from '@kbn/i18n'; import { FilteringValidation } from '../../../../../../../common/types/connectors'; +import { BetaCallOut } from '../../../../../shared/beta/beta_callout'; import { AdvancedSyncRules } from './advanced_sync_rules'; import { EditSyncRulesTab } from './edit_sync_rules_tab'; @@ -104,6 +105,16 @@ export const EditSyncRulesFlyout: React.FC = ({ + + {i18n.translate( 'xpack.enterpriseSearch.content.index.connector.syncRules.flyout.description', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx index b3257fa6455e8..0893b6e99d934 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx @@ -75,7 +75,7 @@ export const SyncJobFlyout: React.FC = ({ onClose, syncJob } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts index 1b965632194b8..41ac136437ef5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts @@ -80,6 +80,7 @@ describe('SyncJobsViewLogic', () => { metadata: {}, started_at: '2022-09-05T14:59:39.816+00:00', status: SyncStatus.COMPLETED, + total_document_count: null, trigger_method: TriggerMethod.ON_DEMAND, worker_hostname: 'hostname_fake', }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_badge.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_badge.tsx new file mode 100644 index 0000000000000..2b3b4a8a24d38 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_badge.tsx @@ -0,0 +1,16 @@ +/* + * 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 { EuiBadge } from '@elastic/eui'; + +import { BETA_LABEL } from '../constants'; + +export const BetaBadge: React.FC = () => { + return {BETA_LABEL}; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_callout.tsx new file mode 100644 index 0000000000000..ffda9fad5475f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/beta/beta_callout.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface BetaCallOutProps { + description: string; +} + +export const BetaCallOut: React.FC = ({ description }) => { + return ( + + {description} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index f9dedfe069170..43c7a81ac527e 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -81,6 +81,7 @@ describe('startSync lib function', () => { metadata: {}, started_at: null, status: SyncStatus.PENDING, + total_document_count: null, trigger_method: TriggerMethod.ON_DEMAND, worker_hostname: null, }, @@ -143,6 +144,7 @@ describe('startSync lib function', () => { metadata: {}, started_at: null, status: SyncStatus.PENDING, + total_document_count: null, trigger_method: TriggerMethod.ON_DEMAND, worker_hostname: null, }, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index 0028b7185e867..c2c6e0a9995f3 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -76,6 +76,7 @@ export const startConnectorSync = async ( metadata: {}, started_at: null, status: SyncStatus.PENDING, + total_document_count: null, trigger_method: TriggerMethod.ON_DEMAND, worker_hostname: null, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.test.tsx new file mode 100644 index 0000000000000..4b19e86703f1b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.test.tsx @@ -0,0 +1,46 @@ +/* + * 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 { createFleetTestRendererMock } from '../../../../../../mock'; + +import { AgentStatusBar } from './status_bar'; + +describe('AgentStatusBar', () => { + it('should render the status bar if there is some agent displayed', () => { + const renderer = createFleetTestRendererMock(); + const res = renderer.render( + + ); + expect(res.queryByTestId('agentStatusBar')).not.toBeNull(); + }); + + it('should not render the status bar if there is no agent displayed', () => { + const renderer = createFleetTestRendererMock(); + const res = renderer.render( + + ); + expect(res.queryByTestId('agentStatusBar')).toBeNull(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx index 728134768b799..f4fc01204267b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx @@ -6,7 +6,7 @@ */ import styled from 'styled-components'; -import { EuiColorPaletteDisplay } from '@elastic/eui'; +import { EuiColorPaletteDisplay, EuiSpacer } from '@elastic/eui'; import React, { useMemo } from 'react'; import { AGENT_STATUSES, getColorForAgentStatus } from '../../services/agent_status'; @@ -35,13 +35,19 @@ export const AgentStatusBar: React.FC<{ return acc; }, [] as Array<{ stop: number; color: string }>); }, [agentStatus]); + + const hasNoAgent = palette[palette.length - 1].stop === 0; + + if (hasNoAgent) { + return ; + } + return ( - <> - - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx index 5da07f8add864..5600d231df19a 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx @@ -95,7 +95,11 @@ const getStoreWithCustomState = (newState: typeof state = state) => { return createStore(newState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); }; -const TestComponent: FC> = (props) => ( +const TestComponent: FC< + ComponentProps & { + filterGroupProps?: Partial>; + } +> = (props) => ( > = (props) => ( chainingSystem="HIERARCHICAL" onFilterChange={onFilterChangeMock} onInit={onInitMock} + {...props.filterGroupProps} /> ); @@ -522,6 +527,36 @@ describe(' Filter Group Component ', () => { expect(screen.queryByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeVisible(); }); }); + it('should update controlGroup with new filters and queries when valid query is supplied', async () => { + const validQuery = { query: { language: 'kuery', query: '' } }; + // pass an invalid query + render(); + + await waitFor(() => { + expect(controlGroupMock.updateInput).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + filters: undefined, + query: validQuery.query, + }) + ); + }); + }); + + it('should not update controlGroup with new filters and queries when invalid query is supplied', async () => { + const invalidQuery = { query: { language: 'kuery', query: '\\' } }; + // pass an invalid query + render(); + + await waitFor(() => { + expect(controlGroupMock.updateInput).toHaveBeenCalledWith( + expect.objectContaining({ + filters: [], + query: undefined, + }) + ); + }); + }); }); describe('Filter Changed Banner', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx index a4c61fb9c98a6..b821ffc6ca227 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx @@ -46,6 +46,7 @@ import { FilterGroupContext } from './filter_group_context'; import { NUM_OF_CONTROLS } from './config'; import { TEST_IDS } from './constants'; import { URL_PARAM_ARRAY_EXCEPTION_MSG } from './translations'; +import { convertToBuildEsQuery } from '../../lib/kuery'; const FilterWrapper = styled.div.attrs((props) => ({ className: props.className, @@ -149,14 +150,41 @@ const FilterGroupComponent = (props: PropsWithChildren) => { return cleanup; }, []); + const { filters: validatedFilters, query: validatedQuery } = useMemo(() => { + const [_, kqlError] = convertToBuildEsQuery({ + config: {}, + queries: query ? [query] : [], + filters: filters ?? [], + indexPattern: { fields: [], title: '' }, + }); + + // we only need to handle kqlError because control group can handle Lucene error + if (kqlError) { + /* + * Based on the behaviour from other components, + * ignore all filters and queries if there is some error + * in the input filters and queries + * + * */ + return { + filters: [], + query: undefined, + }; + } + return { + filters, + query, + }; + }, [filters, query]); + useEffect(() => { controlGroup?.updateInput({ + filters: validatedFilters, + query: validatedQuery, timeRange, - filters, - query, chainingSystem, }); - }, [timeRange, filters, query, chainingSystem, controlGroup]); + }, [timeRange, chainingSystem, controlGroup, validatedQuery, validatedFilters]); const handleInputUpdates = useCallback( (newInput: ControlGroupInput) => { @@ -171,7 +199,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { [setControlGroupInputUpdates, getStoredControlInput, isViewMode, setHasPendingChanges] ); - const handleFilterUpdates = useCallback( + const handleOutputFilterUpdates = useCallback( ({ filters: newFilters }: ControlGroupOutput) => { if (isEqual(currentFiltersRef.current, newFilters)) return; if (onFilterChange) onFilterChange(newFilters ?? []); @@ -181,8 +209,8 @@ const FilterGroupComponent = (props: PropsWithChildren) => { ); const debouncedFilterUpdates = useMemo( - () => debounce(handleFilterUpdates, 500), - [handleFilterUpdates] + () => debounce(handleOutputFilterUpdates, 500), + [handleOutputFilterUpdates] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.test.tsx b/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.test.tsx index c173aa836af55..1677e78622c56 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.test.tsx @@ -7,17 +7,17 @@ import { render } from '@testing-library/react'; import React from 'react'; import { DashboardViewPromptState } from '../hooks/use_dashboard_view_prompt_state'; -import { StatusPropmpt } from './status_prompt'; +import { StatusPrompt } from './status_prompt'; -describe('StatusPropmpt', () => { +describe('StatusPrompt', () => { it('hides by default', () => { - const { queryByTestId } = render(); + const { queryByTestId } = render(); expect(queryByTestId(`dashboardViewEmptyDefault`)).not.toBeInTheDocument(); }); it('shows when No Read Permission', () => { const { queryByTestId } = render( - + ); expect( diff --git a/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.tsx b/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.tsx index 7f0584ec0e882..6e4bb16374268 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/status_prompt.tsx @@ -9,7 +9,7 @@ import { EuiPageTemplate } from '@elastic/eui'; import type { DashboardViewPromptState } from '../hooks/use_dashboard_view_prompt_state'; import { useDashboardViewPromptState } from '../hooks/use_dashboard_view_prompt_state'; -const StatusPropmptComponent = ({ +const StatusPromptComponent = ({ currentState, }: { currentState: DashboardViewPromptState | null; @@ -21,5 +21,5 @@ const StatusPropmptComponent = ({ ) : null; }; -StatusPropmptComponent.displayName = 'StatusPropmptComponent'; -export const StatusPropmpt = React.memo(StatusPropmptComponent); +StatusPromptComponent.displayName = 'StatusPromptComponent'; +export const StatusPrompt = React.memo(StatusPromptComponent); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.test.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.test.tsx new file mode 100644 index 0000000000000..9eed2b4a7f5c2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.test.tsx @@ -0,0 +1,107 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { Router } from 'react-router-dom'; +import { DashboardView } from '.'; +import { useCapabilities } from '../../../common/lib/kibana'; +import { TestProviders } from '../../../common/mock'; + +jest.mock('react-router-dom', () => { + const actual = jest.requireActual('react-router-dom'); + return { + ...actual, + useParams: jest.fn().mockReturnValue({ detailName: 'mockSavedObjectId' }), + }; +}); + +jest.mock('../../../common/lib/kibana', () => { + const actual = jest.requireActual('../../../common/lib/kibana'); + return { + ...actual, + useCapabilities: jest.fn().mockReturnValue({ show: true, showWriteControls: true }), + }; +}); + +jest.mock('../../../common/components/dashboards/dashboard_renderer', () => ({ + DashboardRenderer: jest + .fn() + .mockImplementation((props) => ( +

+ )), +})); + +type Action = 'PUSH' | 'POP' | 'REPLACE'; +const pop: Action = 'POP'; +const location = { + pathname: '/network', + search: '', + state: '', + hash: '', +}; +const mockHistory = { + length: 2, + location, + action: pop, + push: jest.fn(), + replace: jest.fn(), + go: jest.fn(), + goBack: jest.fn(), + goForward: jest.fn(), + block: jest.fn(), + createHref: jest.fn(), + listen: jest.fn(), +}; + +describe('DashboardView', () => { + beforeEach(() => { + (useCapabilities as unknown as jest.Mock).mockReturnValue({ + show: true, + showWriteControls: true, + }); + }); + test('render when no error state', () => { + const { queryByTestId } = render( + + + , + { wrapper: TestProviders } + ); + + expect(queryByTestId(`dashboard-view-mockSavedObjectId`)).toBeInTheDocument(); + }); + + test('render a prompt when error state exists', () => { + (useCapabilities as unknown as jest.Mock).mockReturnValue({ + show: false, + showWriteControls: true, + }); + const { queryByTestId } = render( + + + , + { wrapper: TestProviders } + ); + + expect(queryByTestId(`dashboard-view-mockSavedObjectId`)).not.toBeInTheDocument(); + expect(queryByTestId(`dashboard-view-error-prompt-wrapper`)).toBeInTheDocument(); + }); + + test('render dashboard view with height', () => { + const { queryByTestId } = render( + + + , + { wrapper: TestProviders } + ); + + expect(queryByTestId(`dashboard-view-wrapper`)).toHaveStyle({ + 'min-height': `calc(100vh - 140px)`, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx index 410906b29ae73..7fa8671004552 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx @@ -13,13 +13,13 @@ import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { useParams } from 'react-router-dom'; import { pick } from 'lodash/fp'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { SecurityPageName } from '../../../../common/constants'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { useCapabilities } from '../../../common/lib/kibana'; import { DashboardViewPromptState } from '../../hooks/use_dashboard_view_prompt_state'; import { DashboardRenderer } from '../../../common/components/dashboards/dashboard_renderer'; -import { StatusPropmpt } from '../../components/status_prompt'; +import { StatusPrompt } from '../../components/status_prompt'; import { SiemSearchBar } from '../../../common/components/search_bar'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { FiltersGlobal } from '../../../common/components/filters_global'; @@ -33,6 +33,8 @@ import { EditDashboardButton } from '../../components/edit_dashboard_button'; type DashboardDetails = Record; +const dashboardViewFlexGroupStyle = { minHeight: `calc(100vh - 140px)` }; + const DashboardViewComponent: React.FC = () => { const { fromStr, toStr, from, to } = useDeepEqualSelector((state) => pick(['fromStr', 'toStr', 'from', 'to'], inputsSelectors.globalTimeRangeSelector(state)) @@ -76,34 +78,47 @@ const DashboardViewComponent: React.FC = () => { )} - }> - {showWriteControls && dashboardExists && ( - + + + }> + {showWriteControls && dashboardExists && ( + + )} + + + {!errorState && ( + + + )} - - - {!errorState && ( - + + + )} + - )} - - - + ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx index cf3e774d2d717..df1f2b79c5eb0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx @@ -47,4 +47,21 @@ describe('toggleColumn', () => { ] `); }); + + it('adds a column even if no column is currently shown', async () => { + expect( + toggleColumn({ + column: { id: '_id', schema: 'string' }, + columns: [], + defaultColumns: [], + }) + ).toMatchInlineSnapshot(` + Array [ + Object { + "id": "_id", + "schema": "string", + }, + ] + `); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts index 8769fc956365e..dd783991be409 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts @@ -33,6 +33,10 @@ const insert = ({ return [...columns.slice(0, defaultIndex), column, ...columns.slice(defaultIndex)]; } + if (columns.length === 0) { + return [column]; + } + // if the column isn't shown and it's not part of the default config // push it into the second position. Behaviour copied by t_grid, security // does this to insert right after the timestamp column diff --git a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts index 944c2415ec38e..15e3901dcf970 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts @@ -8,14 +8,14 @@ import type SuperTest from 'supertest'; import { CASES_INTERNAL_URL, CASES_URL } from '@kbn/cases-plugin/common/constants'; import { - AllCommentsResponse, + Comments, BulkCreateCommentRequest, BulkGetAttachmentsResponse, Case, CommentPatchRequest, CommentRequest, - CommentResponse, - CommentsResponse, + Comment, + CommentsFindResponse, CommentType, getCaseFindAttachmentsUrl, getCasesDeleteFileAttachmentsUrl, @@ -203,7 +203,7 @@ export const getAllComments = async ({ caseId: string; auth?: { user: User; space: string | null }; expectedHttpCode?: number; -}): Promise => { +}): Promise => { const { body: comments } = await supertest .get(`${getSpaceUrlPrefix(auth.space)}${CASES_URL}/${caseId}/comments`) .auth(auth.user.username, auth.user.password) @@ -224,7 +224,7 @@ export const getComment = async ({ commentId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; -}): Promise => { +}): Promise => { const { body: comment } = await supertest .get(`${getSpaceUrlPrefix(auth.space)}${CASES_URL}/${caseId}/comments/${commentId}`) .auth(auth.user.username, auth.user.password) @@ -295,7 +295,7 @@ export const findAttachments = async ({ query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; -}): Promise => { +}): Promise => { const { body } = await supertest .get(`${getSpaceUrlPrefix(auth.space)}${getCaseFindAttachmentsUrl(caseId)}`) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/cases_api_integration/common/lib/api/omit.ts b/x-pack/test/cases_api_integration/common/lib/api/omit.ts index 4f0b3402b847d..ea974f8357453 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/omit.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/omit.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Case, CommentResponse } from '@kbn/cases-plugin/common/api'; +import { Case, Comment } from '@kbn/cases-plugin/common/api'; import { omit } from 'lodash'; interface CommonSavedObjectAttributes { @@ -41,9 +41,9 @@ export const removeServerGeneratedPropertiesFromCase = (theCase: Case): Partial< }; export const removeServerGeneratedPropertiesFromComments = ( - comments: CommentResponse[] | undefined -): Array> | undefined => { + comments: Comment[] | undefined +): Array> | undefined => { return comments?.map((comment) => { - return removeServerGeneratedPropertiesFromSavedObject(comment, []); + return removeServerGeneratedPropertiesFromSavedObject(comment, []); }); }; diff --git a/x-pack/test/cases_api_integration/common/lib/mock.ts b/x-pack/test/cases_api_integration/common/lib/mock.ts index 8b8562e145395..16355aca51c76 100644 --- a/x-pack/test/cases_api_integration/common/lib/mock.ts +++ b/x-pack/test/cases_api_integration/common/lib/mock.ts @@ -9,7 +9,7 @@ import { CasePostRequest, Case, CasesFindResponse, - CommentResponse, + Comment, ConnectorTypes, CommentRequestUserType, CommentRequestAlertType, @@ -185,7 +185,7 @@ export const commentsResp = ({ comments, }: { comments: CommentRequestWithID[]; -}): Array> => { +}): Array> => { return comments.map(({ comment, id }) => { const baseFields = { id, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts index b030b0ea5db4d..8d656de080556 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -10,14 +10,12 @@ import { join } from 'path'; import { SavedObject } from '@kbn/core/server'; import supertest from 'supertest'; import { - CASES_URL, CASE_SAVED_OBJECT, CASE_USER_ACTION_SAVED_OBJECT, CASE_COMMENT_SAVED_OBJECT, } from '@kbn/cases-plugin/common/constants'; import { AttributesTypeUser, - CommentsResponse, CaseAttributes, CasePostRequest, PushedUserAction, @@ -39,6 +37,7 @@ import { createComment, findCases, getCaseUserActions, + findAttachments, } from '../../../../common/lib/api'; import { getPostCaseRequest, postCommentUserReq } from '../../../../common/lib/mock'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -102,10 +101,10 @@ export default ({ getService }: FtrProviderContext): void => { expect(findResponse.cases[0].title).to.eql('A case to export'); expect(findResponse.cases[0].description).to.eql('a description'); - const { body: commentsResponse }: { body: CommentsResponse } = await supertestService - .get(`${CASES_URL}/${findResponse.cases[0].id}/comments/_find`) - .send() - .expect(200); + const commentsResponse = await findAttachments({ + supertest: supertestService, + caseId: findResponse.cases[0].id, + }); const comment = commentsResponse.comments[0] as unknown as AttributesTypeUser; expect(comment.comment).to.eql('A comment for my case'); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts index 7132972fb5b96..e7dac5f4c3fae 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { CASES_URL } from '@kbn/cases-plugin/common/constants'; -import { CommentsResponse } from '@kbn/cases-plugin/common/api'; +import { CommentsFindResponse } from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getPostCaseRequest, @@ -26,6 +26,7 @@ import { getSpaceUrlPrefix, createCase, superUserSpace1Auth, + findAttachments, } from '../../../../common/lib/api'; import { @@ -221,10 +222,11 @@ export default ({ getService }: FtrProviderContext): void => { caseID: obsCase.id, }, ]) { - const { body: caseComments }: { body: CommentsResponse } = await supertestWithoutAuth - .get(`${getSpaceUrlPrefix(space1)}${CASES_URL}/${scenario.caseID}/comments/_find`) - .auth(scenario.user.username, scenario.user.password) - .expect(200); + const caseComments = await findAttachments({ + supertest: supertestWithoutAuth, + caseId: scenario.caseID, + auth: { user: scenario.user, space: space1 }, + }); ensureSavedObjectIsAuthorized( caseComments.comments, @@ -279,7 +281,7 @@ export default ({ getService }: FtrProviderContext): void => { caseId: obsCase.id, }); - const { body: res }: { body: CommentsResponse } = await supertestWithoutAuth + const { body: res }: { body: CommentsFindResponse } = await supertestWithoutAuth .get( `${getSpaceUrlPrefix('space1')}${CASES_URL}/${ obsCase.id diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts index 046315d9174c2..e04a90fe23e11 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts @@ -178,7 +178,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('infrastructure landing page with data', () => { + // FLAKY: https://github.com/elastic/kibana/issues/156437 + describe.skip('infrastructure landing page with data', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); });