Skip to content

Commit

Permalink
feat(search-indexes): drop search index COMPASS-7167 (#4844)
Browse files Browse the repository at this point in the history
  • Loading branch information
mabaasit authored Sep 14, 2023
1 parent c32a02c commit c84ede7
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 69 deletions.
2 changes: 2 additions & 0 deletions packages/compass-components/src/hooks/use-confirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ConfirmationProperties = {
description: React.ReactNode;
buttonText?: string;
variant?: ConfirmationModalVariant;
requiredInputText?: string;
};
type ConfirmationCallback = (value: boolean) => void;

Expand Down Expand Up @@ -134,6 +135,7 @@ export const ConfirmationModalArea: React.FC = ({ children }) => {
title={confirmationProps.title ?? 'Are you sure?'}
variant={confirmationProps.variant ?? ConfirmationModalVariant.Default}
buttonText={confirmationProps.buttonText ?? 'Confirm'}
requiredInputText={confirmationProps.requiredInputText ?? undefined}
onConfirm={handleConfirm}
onCancel={handleCancel}
>
Expand Down
21 changes: 2 additions & 19 deletions packages/compass-indexes/src/components/indexes/indexes.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { expect } from 'chai';
import sinon from 'sinon';
import preferencesAccess from 'compass-preferences-model';
import type { RegularIndex } from '../../modules/regular-indexes';
import type { SearchIndex } from 'mongodb-data-service';
import type Store from '../../stores';
import type { IndexesDataService } from '../../stores/store';
import Indexes from './indexes';
import { setupStore } from '../../../test/setup-store';
import { searchIndexes } from '../../../test/fixtures/search-indexes';

const renderIndexes = (props: Partial<typeof Store> = {}) => {
const store = setupStore();
Expand Down Expand Up @@ -279,25 +279,8 @@ describe('Indexes Component', function () {
it('renders the search indexes table if the current view changes to search indexes', async function () {
const store = renderIndexes();

const indexes: SearchIndex[] = [
{
id: '1',
name: 'default',
status: 'READY',
queryable: true,
latestDefinition: {},
},
{
id: '2',
name: 'another',
status: 'READY',
queryable: true,
latestDefinition: {},
},
];

store.getState()!.dataService!.getSearchIndexes = function () {
return Promise.resolve(indexes);
return Promise.resolve(searchIndexes);
};

// switch to the Search Indexes tab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type { State as SearchIndexesState } from '../../modules/search-indexes';
import { SearchIndexesStatuses } from '../../modules/search-indexes';
import type { SearchIndexesStatus } from '../../modules/search-indexes';
import type { RootState } from '../../modules';
import CreateSearchIndexModal from '../create-search-index-modal';
import { CreateSearchIndexModal } from '../search-indexes-modals';

// This constant is used as a trigger to show an insight whenever number of
// indexes in a collection is more than what is specified here.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { DEFAULT_INDEX_DEFINITION } from '.';
import CreateSearchIndexModal from '.';
import { DEFAULT_INDEX_DEFINITION } from './create-search-index-modal';
import CreateSearchIndexModal from './create-search-index-modal';
import sinon from 'sinon';
import { Provider } from 'react-redux';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as CreateSearchIndexModal } from './create-search-index-modal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { cleanup, render, screen } from '@testing-library/react';
import { expect } from 'chai';
import { spy } from 'sinon';
import type { SinonSpy } from 'sinon';
import userEvent from '@testing-library/user-event';
import type { SearchIndex } from 'mongodb-data-service';

import SearchIndexActions from './search-index-actions';

describe('SearchIndexActions Component', function () {
let onDropSpy: SinonSpy;

before(cleanup);
afterEach(cleanup);
beforeEach(function () {
onDropSpy = spy();
render(
<SearchIndexActions
index={{ name: 'artist_id_index' } as SearchIndex}
onDropIndex={onDropSpy}
/>
);
});

it('renders drop button', function () {
const button = screen.getByTestId('search-index-actions-drop-action');
expect(button).to.exist;
expect(button.getAttribute('aria-label')).to.equal(
'Drop Index artist_id_index'
);
expect(onDropSpy.callCount).to.equal(0);
userEvent.click(button);
expect(onDropSpy.callCount).to.equal(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useCallback, useMemo } from 'react';
import type { GroupedItemAction } from '@mongodb-js/compass-components';
import { ItemActionGroup } from '@mongodb-js/compass-components';
import type { SearchIndex } from 'mongodb-data-service';

type IndexActionsProps = {
index: SearchIndex;
onDropIndex: (name: string) => void;
};

type SearchIndexAction = 'drop';

const IndexActions: React.FunctionComponent<IndexActionsProps> = ({
index,
onDropIndex,
}) => {
const indexActions: GroupedItemAction<SearchIndexAction>[] = useMemo(() => {
const actions: GroupedItemAction<SearchIndexAction>[] = [
{
action: 'drop',
label: `Drop Index ${index.name}`,
icon: 'Trash',
},
];

return actions;
}, [index]);

const onAction = useCallback(
(action: SearchIndexAction) => {
if (action === 'drop') {
void onDropIndex(index.name);
}
},
[onDropIndex, index]
);

return (
<ItemActionGroup<SearchIndexAction>
data-testid="search-index-actions"
actions={indexActions}
onAction={onAction}
></ItemActionGroup>
);
};

export default IndexActions;
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
import React from 'react';
import {
cleanup,
fireEvent,
render,
screen,
fireEvent,
within,
} from '@testing-library/react';
import { expect } from 'chai';
import sinon from 'sinon';
import userEvent from '@testing-library/user-event';

import { SearchIndexesTable } from './search-indexes-table';
import type { SearchIndex } from 'mongodb-data-service';
import { SearchIndexesStatuses } from '../../modules/search-indexes';

const indexes: SearchIndex[] = [
{
id: '1',
name: 'default',
status: 'READY',
queryable: true,
latestDefinition: {},
},
{
id: '2',
name: 'another',
status: 'READY',
queryable: true,
latestDefinition: {},
},
];
import { searchIndexes as indexes } from './../../../test/fixtures/search-indexes';

const renderIndexList = (
props: Partial<React.ComponentProps<typeof SearchIndexesTable>> = {}
Expand All @@ -44,6 +27,7 @@ const renderIndexList = (
isWritable={true}
readOnly={false}
onSortTable={onSortTableSpy}
onDropIndex={() => {}}
openCreateModal={openCreateSpy}
{...props}
/>
Expand Down Expand Up @@ -89,10 +73,7 @@ describe('SearchIndexesTable Component', function () {
});
}

for (const status of [
SearchIndexesStatuses.PENDING,
SearchIndexesStatuses.ERROR,
]) {
for (const status of [SearchIndexesStatuses.PENDING]) {
it(`does not render the list if the status is ${status}`, function () {
renderIndexList({
status,
Expand Down Expand Up @@ -145,4 +126,19 @@ describe('SearchIndexesTable Component', function () {
expect(onSortTableSpy.getCalls()[1].args).to.deep.equal([column, 'asc']);
});
}

context('renders list with action', function () {
it('renders drop action and shows modal when clicked', function () {
const onDropIndexSpy = sinon.spy();

renderIndexList({ onDropIndex: onDropIndexSpy });
const dropIndexActions = screen.getAllByTestId(
'search-index-actions-drop-action'
);

expect(dropIndexActions.length).to.equal(indexes.length);
dropIndexActions[0].click();
expect(onDropIndexSpy.callCount).to.equal(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ import {
import type { SearchSortColumn } from '../../modules/search-indexes';
import {
SearchIndexesStatuses,
dropSearchIndex,
openModalForCreation,
} from '../../modules/search-indexes';
import type { SearchIndexesStatus } from '../../modules/search-indexes';
import { sortSearchIndexes } from '../../modules/search-indexes';
import type { SortDirection, RootState } from '../../modules';

import { IndexesTable } from '../indexes-table';
import IndexActions from './search-index-actions';
import { ZeroGraphic } from './zero-graphic';

type SearchIndexesTableProps = {
indexes: SearchIndex[];
isWritable?: boolean;
readOnly?: boolean;
onSortTable: (column: SearchSortColumn, direction: SortDirection) => void;
onDropIndex: (name: string) => void;
openCreateModal: () => void;
status: SearchIndexesStatus;
};
Expand Down Expand Up @@ -102,6 +105,7 @@ export const SearchIndexesTable: React.FunctionComponent<
onSortTable,
openCreateModal,
status,
onDropIndex,
}) => {
if (!isReadyStatus(status)) {
// If there's an error or the search indexes are still pending or search
Expand Down Expand Up @@ -137,7 +141,7 @@ export const SearchIndexesTable: React.FunctionComponent<
),
},
],

actions: <IndexActions index={index} onDropIndex={onDropIndex} />,
// TODO(COMPASS-7206): details for the nested row
};
});
Expand All @@ -162,6 +166,7 @@ const mapState = ({ searchIndexes, isWritable }: RootState) => ({

const mapDispatch = {
onSortTable: sortSearchIndexes,
onDropIndex: dropSearchIndex,
openCreateModal: openModalForCreation,
};

Expand Down
59 changes: 40 additions & 19 deletions packages/compass-indexes/src/modules/search-indexes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,16 @@ import {
saveIndex,
fetchSearchIndexes,
sortSearchIndexes,
dropSearchIndex,
} from './search-indexes';
import { setupStore } from '../../test/setup-store';
import { searchIndexes } from '../../test/fixtures/search-indexes';
import sinon from 'sinon';
import type { IndexesDataService } from '../stores/store';
import type { SearchIndex } from 'mongodb-data-service';
import { readonlyViewChanged } from './is-readonly-view';

const searchIndexes: SearchIndex[] = [
{
id: '1',
name: 'default',
status: 'READY',
queryable: true,
latestDefinition: {},
},
{
id: '2',
name: 'another',
status: 'FAILED',
queryable: true,
latestDefinition: {},
},
];
// Importing this to stub showConfirmation
import * as searchIndexesSlice from './search-indexes';

describe('search-indexes module', function () {
let store: ReturnType<typeof setupStore>;
Expand Down Expand Up @@ -127,7 +114,7 @@ describe('search-indexes module', function () {
id: '2',
name: 'another',
status: 'FAILED',
queryable: true,
queryable: false,
latestDefinition: {},
},
{
Expand Down Expand Up @@ -186,7 +173,7 @@ describe('search-indexes module', function () {
id: '2',
name: 'another',
status: 'FAILED',
queryable: true,
queryable: false,
latestDefinition: {},
},
]);
Expand All @@ -209,4 +196,38 @@ describe('search-indexes module', function () {
expect(store.getState().searchIndexes.createIndex.isModalOpen).to.be.false;
expect(dataProvider.createSearchIndex).to.have.been.calledOnce;
});

context('drop search index', function () {
let dropSearchIndexStub: sinon.SinonStub;
let showConfirmationStub: sinon.SinonStub;
beforeEach(function () {
dropSearchIndexStub = sinon.stub(
store.getState().dataService as IndexesDataService,
'dropSearchIndex'
);
showConfirmationStub = sinon.stub(searchIndexesSlice, 'showConfirmation');
});

afterEach(function () {
showConfirmationStub.restore();
dropSearchIndexStub.restore();
});

it('does not drop index when user does not confirm', async function () {
showConfirmationStub.resolves(false);
await store.dispatch(dropSearchIndex('index_name'));
expect(dropSearchIndexStub.callCount).to.equal(0);
});

it('drops index successfully', async function () {
showConfirmationStub.resolves(true);
dropSearchIndexStub.resolves(true);
await store.dispatch(dropSearchIndex('index_name'));
expect(dropSearchIndexStub.firstCall.args).to.deep.equal([
'citibike.trips',
'index_name',
]);
expect(store.getState().searchIndexes.error).to.be.undefined;
});
});
});
Loading

0 comments on commit c84ede7

Please sign in to comment.