Skip to content

Commit

Permalink
[Feature] Acceleration Actions Implementation (#1540) (#1563)
Browse files Browse the repository at this point in the history
* Refactor the icon clicks and introduce vacuum to flyout inside icon



* Add vacuum icon for acceleration table



* Add DELETE ui flow for acceleration table only



* Add VACUUM flow to acceleration table only



* Handle skip index naming in overlay



* Refactor the overlay logic a bit in acceleration table class



* Use util function for creating displayIndexName in overlay



* Add delete flow for acceleration table 0



* Bond refresh logic when delete was successful



* Add vacuum flow for acceleration table



* Rename the class into acceleration operations



* Switch the acc flyout icon into broom



* add vacuum on acc flyout 0



* Change to closing overlay immediately after clicking confirm



* Add flyout reset and fix the confirm behavior



* add vacuum on acc flyout final



* Add close flyout after the succeed status check



* Add teh refresh in acc flyout and trigger after delete/vacuum



* remove comment



* Mini refactor on load status in operation class



* Fix the mv flow



* Remove the sql definition tab



* Correct the visualization of show refresh interval in flyout



* Update the show time logic with refresh + time zone localization



* Define the behavior of refresh icon in both table and flyout



* Update teh acc table test to fix the build



* Update utils to consume the sync action



* Add sync flow to table behavior but with fail status



* Add sync flow to detail flyout but with fail status



* Add the restriction for only sync active acceleration



* Fix the navigate to datasource link



* Implement the single toast control for each status



* Fix keep pulling after switch rendering



* Fix types



* Remove the sql definition class



* Add basic tests for acceleration overlay



* Add basic tests for acceleration operation



* Remove console log



* Remove refresh icon in utils



* Remove the final status check for sync action



* remove unnecessary check



* remove stable datasource from dependencies array



* Resolve conflicts



* Fix lint



* Fix type in types



* Finalize the name for skipping index



* Refactor testing constants



* Upadate the class prop to remove unused index name



---------


(cherry picked from commit 376fde4)

Signed-off-by: Ryan Liang <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 36bd75e commit 668a968
Show file tree
Hide file tree
Showing 17 changed files with 626 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { mount, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { EuiOverlayMask, EuiConfirmModal, EuiFieldText } from '@elastic/eui';
import {
AccelerationActionOverlay,
AccelerationActionOverlayProps,
} from '../manage/accelerations/acceleration_action_overlay';
import { skippingIndexAcceleration } from '../../../../../test/datasources';
import { act } from 'react-dom/test-utils';

configure({ adapter: new Adapter() });

describe('AccelerationActionOverlay Component Tests', () => {
let props: AccelerationActionOverlayProps;

beforeEach(() => {
props = {
isVisible: true,
actionType: 'delete',
acceleration: skippingIndexAcceleration,
dataSourceName: 'test-datasource',
onCancel: jest.fn(),
onConfirm: jest.fn(),
};
});

it('renders correctly', () => {
const wrapper = mount(<AccelerationActionOverlay {...props} />);
expect(wrapper.find(EuiOverlayMask).exists()).toBe(true);
expect(wrapper.find(EuiConfirmModal).exists()).toBe(true);
expect(wrapper.text()).toContain('Delete acceleration');
});

it('calls onConfirm when confirm button is clicked and confirm is enabled', async () => {
const wrapper = mount(<AccelerationActionOverlay {...props} />);

if (props.actionType === 'vacuum') {
await act(async () => {
const onChange = wrapper.find(EuiFieldText).first().prop('onChange');
if (typeof onChange === 'function') {
onChange({
target: { value: props.acceleration!.indexName },
} as any);
}
});
wrapper.update();
}
wrapper
.find('button')
.filterWhere((button) => button.text().includes('Delete'))
.simulate('click');
expect(props.onConfirm).toHaveBeenCalled();
});

it('calls onCancel when cancel button is clicked', () => {
const wrapper = mount(<AccelerationActionOverlay {...props} />);

wrapper
.find('button')
.filterWhere((button) => button.text() === 'Cancel')
.simulate('click');

expect(props.onCancel).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { renderHook, act } from '@testing-library/react-hooks';
import { useAccelerationOperation } from '../manage/accelerations/acceleration_operation';
import * as useDirectQueryModule from '../../../../framework/datasources/direct_query_hook';
import * as useToastModule from '../../../common/toast';
import { DirectQueryLoadingStatus } from '../../../../../common/types/explorer';
import { skippingIndexAcceleration } from '../../../../../test/datasources';

jest.mock('../../../../framework/datasources/direct_query_hook', () => ({
useDirectQuery: jest.fn(),
}));

jest.mock('../../../common/toast', () => ({
useToast: jest.fn(),
}));

describe('useAccelerationOperation', () => {
beforeEach(() => {
jest.clearAllMocks();

(useDirectQueryModule.useDirectQuery as jest.Mock).mockReturnValue({
startLoading: jest.fn(),
stopLoading: jest.fn(),
loadStatus: DirectQueryLoadingStatus.INITIAL,
});

(useToastModule.useToast as jest.Mock).mockReturnValue({
setToast: jest.fn(),
});
});

it('performs acceleration operation and handles success', async () => {
(useDirectQueryModule.useDirectQuery as jest.Mock).mockReturnValue({
startLoading: jest.fn(),
stopLoading: jest.fn(),
loadStatus: DirectQueryLoadingStatus.SUCCESS,
});

const { result } = renderHook(() => useAccelerationOperation('test-datasource'));

act(() => {
result.current.performOperation(skippingIndexAcceleration, 'delete');
});

expect((useDirectQueryModule.useDirectQuery as jest.Mock).mock.calls.length).toBeGreaterThan(0);
expect((useToastModule.useToast as jest.Mock).mock.calls.length).toBeGreaterThan(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ describe('AccelerationTable Component', () => {
});
wrapper!.update();

expect(wrapper!.text()).toContain(accelerationCache.lastUpdated);
const expectedLocalizedTime = accelerationCache.lastUpdated
? new Date(accelerationCache.lastUpdated).toLocaleString()
: '';

expect(wrapper!.text()).toContain(expectedLocalizedTime);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('AssociatedObjectsDetailsFlyout Integration Tests', () => {
wrapper.update();
});

const accName = getAccelerationName(mockTableDetail.accelerations[0], 'flint_s3');
const accName = getAccelerationName(mockTableDetail.accelerations[0]);
const accLink = wrapper
.find('EuiLink')
.findWhere((node) => node.text() === accName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState } from 'react';
import { EuiOverlayMask, EuiConfirmModal, EuiFormRow, EuiFieldText } from '@elastic/eui';
import { CachedAcceleration } from '../../../../../../common/types/data_connections';
import {
ACC_DELETE_MSG,
ACC_VACUUM_MSG,
ACC_SYNC_MSG,
AccelerationActionType,
getAccelerationName,
getAccelerationFullPath,
} from './utils/acceleration_utils';

export interface AccelerationActionOverlayProps {
isVisible: boolean;
actionType: AccelerationActionType;
acceleration: CachedAcceleration | null;
dataSourceName: string;
onCancel: () => void;
onConfirm: () => void;
}

export const AccelerationActionOverlay: React.FC<AccelerationActionOverlayProps> = ({
isVisible,
actionType,
acceleration,
dataSourceName,
onCancel,
onConfirm,
}) => {
const [confirmationInput, setConfirmationInput] = useState('');

if (!isVisible || !acceleration) {
return null;
}

const displayIndexName = getAccelerationName(acceleration);
const displayFullPath = getAccelerationFullPath(acceleration, dataSourceName);

let title = '';
let description = '';
let confirmButtonText = 'Confirm';
let confirmEnabled = true;

switch (actionType) {
case 'vacuum':
title = `Vacuum acceleration ${displayIndexName} on ${displayFullPath}?`;
description = ACC_VACUUM_MSG;
confirmButtonText = 'Vacuum';
confirmEnabled = confirmationInput === displayIndexName;
break;
case 'delete':
title = `Delete acceleration ${displayIndexName} on ${displayFullPath}?`;
description = ACC_DELETE_MSG;
confirmButtonText = 'Delete';
break;
case 'sync':
title = 'Manual sync data?';
description = ACC_SYNC_MSG;
confirmButtonText = 'Sync';
break;
}

return (
<EuiOverlayMask>
<EuiConfirmModal
title={title}
onCancel={onCancel}
onConfirm={() => onConfirm()}
cancelButtonText="Cancel"
confirmButtonText={confirmButtonText}
buttonColor="danger"
defaultFocusedButton="confirm"
confirmButtonDisabled={!confirmEnabled}
>
<p>{description}</p>
{actionType === 'vacuum' && (
<EuiFormRow label={`To confirm, type ${displayIndexName}`}>
<EuiFieldText
name="confirmationInput"
value={confirmationInput}
onChange={(e) => setConfirmationInput(e.target.value)}
/>
</EuiFormRow>
)}
</EuiConfirmModal>
</EuiOverlayMask>
);
};
Loading

0 comments on commit 668a968

Please sign in to comment.