Skip to content

Commit

Permalink
Merge branch 'main' into value_lists
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Apr 15, 2024
2 parents 9b030df + 23dd7cd commit a74fe9c
Show file tree
Hide file tree
Showing 559 changed files with 7,718 additions and 18,192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ steps:
queue: n2-2-spot
depends_on: build
key: tests
timeout_in_minutes: 60
timeout_in_minutes: 90
retry:
automatic:
- exit_status: '-1'
limit: 3
limit: 1
- exit_status: '*'
limit: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { CancelSyncJobModal } from './sync_job_cancel_modal';
import '@testing-library/jest-dom/extend-expect';
import { I18nProvider } from '@kbn/i18n-react';

describe('CancelSyncJobModal', () => {
const mockSyncJobId = '123';
const mockOnConfirmCb = jest.fn();
const mockOnCancel = jest.fn();

beforeEach(() => {
render(
<I18nProvider>
<CancelSyncJobModal
syncJobId={mockSyncJobId}
onConfirmCb={mockOnConfirmCb}
onCancel={mockOnCancel}
isLoading={false}
errorMessages={[]}
/>
</I18nProvider>
);
});

test('renders the sync job ID', () => {
const syncJobIdElement = screen.getByTestId('confirmModalBodyText');
expect(syncJobIdElement).toHaveTextContent(`Sync job ID: ${mockSyncJobId}`);
});

test('calls onConfirmCb when confirm button is clicked', () => {
const confirmButton = screen.getByText('Confirm');
fireEvent.click(confirmButton);
expect(mockOnConfirmCb).toHaveBeenCalledWith(mockSyncJobId);
});

test('calls onCancel when cancel button is clicked', () => {
const cancelButton = screen.getByTestId('confirmModalCancelButton');
fireEvent.click(cancelButton);
expect(mockOnCancel).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EuiConfirmModal, EuiText, EuiCode, EuiSpacer, EuiConfirmModalProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';

export type CancelSyncModalProps = Omit<EuiConfirmModalProps, 'onConfirm'> & {
onConfirmCb: (syncJobId: string) => void;
syncJobId: string;
errorMessages?: string[];
};

export const CancelSyncJobModal: React.FC<CancelSyncModalProps> = ({
syncJobId,
onCancel,
onConfirmCb,
isLoading,
}) => {
return (
<EuiConfirmModal
title={i18n.translate('searchConnectors.syncJobs.cancelSyncModal.title', {
defaultMessage: 'Cancel sync job',
})}
onCancel={onCancel}
onConfirm={() => onConfirmCb(syncJobId)}
cancelButtonText={i18n.translate('searchConnectors.syncJobs.cancelSyncModal.cancelButton', {
defaultMessage: 'Cancel',
})}
confirmButtonText={i18n.translate('searchConnectors.syncJobs.cancelSyncModal.confirmButton', {
defaultMessage: 'Confirm',
})}
buttonColor="danger"
confirmButtonDisabled={isLoading}
isLoading={isLoading}
>
<EuiText size="s">
<FormattedMessage
id="searchConnectors.syncJobs.cancelSyncModal.description"
defaultMessage="Are you sure you want to cancel this sync job?"
/>
<EuiSpacer size="m" />
<FormattedMessage
id="searchConnectors.syncJobs.cancelSyncModal.syncJobId"
defaultMessage="Sync job ID:"
/>
&nbsp;
<EuiCode>{syncJobId}</EuiCode>
</EuiText>
</EuiConfirmModal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,29 @@ import {
EuiBadge,
EuiBasicTable,
EuiBasicTableColumn,
EuiButtonIcon,
Pagination,
} from '@elastic/eui';

import { i18n } from '@kbn/i18n';
import { ConnectorSyncJob, SyncJobType, SyncStatus } from '../..';
import { ConnectorSyncJob, isSyncCancellable, SyncJobType, SyncStatus } from '../..';

import { syncJobTypeToText, syncStatusToColor, syncStatusToText } from '../..';
import { durationToText, getSyncJobDuration } from '../../utils/duration_to_text';
import { FormattedDateTime } from '../../utils/formatted_date_time';
import { SyncJobFlyout } from './sync_job_flyout';
import { CancelSyncJobModal, CancelSyncModalProps } from './sync_job_cancel_modal';

interface SyncJobHistoryTableProps {
isLoading?: boolean;
onPaginate: (criteria: CriteriaWithPagination<ConnectorSyncJob>) => void;
pagination: Pagination;
syncJobs: ConnectorSyncJob[];
type: 'content' | 'access_control';
cancelConfirmModalProps?: Pick<CancelSyncModalProps, 'isLoading' | 'onConfirmCb'> & {
syncJobIdToCancel?: ConnectorSyncJob['id'];
setSyncJobIdToCancel: (syncJobId: ConnectorSyncJob['id'] | undefined) => void;
};
}

export const SyncJobsTable: React.FC<SyncJobHistoryTableProps> = ({
Expand All @@ -38,6 +44,12 @@ export const SyncJobsTable: React.FC<SyncJobHistoryTableProps> = ({
pagination,
syncJobs,
type,
cancelConfirmModalProps = {
onConfirmCb: () => {},
isLoading: false,
setSyncJobIdToCancel: () => {},
syncJobIdToCancel: undefined,
},
}) => {
const [selectedSyncJob, setSelectedSyncJob] = useState<ConnectorSyncJob | undefined>(undefined);
const columns: Array<EuiBasicTableColumn<ConnectorSyncJob>> = [
Expand Down Expand Up @@ -127,6 +139,33 @@ export const SyncJobsTable: React.FC<SyncJobHistoryTableProps> = ({
onClick: (job) => setSelectedSyncJob(job),
type: 'icon',
},
...(cancelConfirmModalProps
? [
{
render: (job: ConnectorSyncJob) => {
return isSyncCancellable(job.status) ? (
<EuiButtonIcon
iconType="cross"
color="danger"
onClick={() => cancelConfirmModalProps.setSyncJobIdToCancel(job.id)}
aria-label={i18n.translate(
'searchConnectors.index.syncJobs.actions.cancelSyncJob.caption',
{
defaultMessage: 'Cancel this sync job',
}
)}
>
{i18n.translate('searchConnectors.index.syncJobs.actions.deleteJob.caption', {
defaultMessage: 'Delete',
})}
</EuiButtonIcon>
) : (
<></>
);
},
},
]
: []),
],
},
];
Expand All @@ -136,6 +175,13 @@ export const SyncJobsTable: React.FC<SyncJobHistoryTableProps> = ({
{Boolean(selectedSyncJob) && (
<SyncJobFlyout onClose={() => setSelectedSyncJob(undefined)} syncJob={selectedSyncJob} />
)}
{Boolean(cancelConfirmModalProps) && cancelConfirmModalProps?.syncJobIdToCancel && (
<CancelSyncJobModal
{...cancelConfirmModalProps}
syncJobId={cancelConfirmModalProps.syncJobIdToCancel}
onCancel={() => cancelConfirmModalProps.setSyncJobIdToCancel(undefined)}
/>
)}
<EuiBasicTable
data-test-subj={`entSearchContent-index-${type}-syncJobs-table`}
items={syncJobs}
Expand Down
32 changes: 32 additions & 0 deletions packages/kbn-search-connectors/lib/cancel_sync.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { cancelSync } from './cancel_sync';

describe('cancelSync lib function', () => {
const mockClient = {
transport: {
request: jest.fn(),
},
};

it('should cancel a sync', async () => {
mockClient.transport.request.mockImplementation(() => ({
success: true,
}));

await expect(cancelSync(mockClient as unknown as ElasticsearchClient, '1234')).resolves.toEqual(
{ success: true }
);
expect(mockClient.transport.request).toHaveBeenCalledWith({
method: 'PUT',
path: '/_connector/_sync_job/1234/_cancel',
});
});
});
18 changes: 18 additions & 0 deletions packages/kbn-search-connectors/lib/cancel_sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { ConnectorAPICancelSyncResponse } from '../types';

export const cancelSync = async (client: ElasticsearchClient, syncJobId: string) => {
const result = await client.transport.request<ConnectorAPICancelSyncResponse>({
method: 'PUT',
path: `/_connector/_sync_job/${syncJobId}/_cancel`,
});
return result;
};
15 changes: 15 additions & 0 deletions packages/kbn-search-connectors/lib/collect_connector_stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
let idle = 0;
let running = 0;
let duration = 0;
const errors = new Map<string, number>();
let topErrors: string[] = [];

for (const syncJob of syncJobs) {
completed += syncJob.status === SyncStatus.COMPLETED ? 1 : 0;
Expand All @@ -386,6 +388,18 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
duration += Math.floor((completedAt.getTime() - startedAt.getTime()) / 1000);
}
}
if (syncJob.status === SyncStatus.ERROR && syncJob.error) {
errors.set(syncJob.error, (errors.get(syncJob.error) ?? 0) + 1);
}
}

if (errors.size <= 5) {
topErrors = [...errors.keys()];
} else {
topErrors = [...errors.entries()]
.sort((a, b) => b[1] - a[1])
.map((a) => a[0])
.slice(0, 5);
}

return {
Expand All @@ -399,5 +413,6 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
idle,
running,
totalDurationSeconds: duration,
topErrors,
} as SyncJobStatsByState;
}
Loading

0 comments on commit a74fe9c

Please sign in to comment.