Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d16ee17
feat(compass-indexes): add index build progress COMPASS-9495
nbbeeken Aug 15, 2025
3408a51
fix: tests
nbbeeken Aug 15, 2025
ab39f35
fix: switch back to 10s intervals and round the number at the component
nbbeeken Aug 15, 2025
7d4209f
chore: dont need math
nbbeeken Aug 15, 2025
df84f03
chore: copilot
nbbeeken Aug 15, 2025
891d3d0
fix: move index progress to data-service
nbbeeken Aug 18, 2025
9106a0a
fix hook deps
nbbeeken Aug 18, 2025
e10b79d
fix: styles and types
nbbeeken Aug 18, 2025
764f474
test: buildProgress display
nbbeeken Aug 19, 2025
3f5faa5
fix: styles
nbbeeken Aug 19, 2025
0853225
fix: rename indexActionsContainerStyles
nbbeeken Aug 22, 2025
dbfa09f
fix: use …
nbbeeken Aug 22, 2025
b43a671
fix: spacing
nbbeeken Aug 22, 2025
d2a93a2
docs: comments on filter
nbbeeken Aug 22, 2025
a902b7f
feat: make the agg run the division
nbbeeken Aug 22, 2025
e2e309f
fix: remove progress from "in-progress"
nbbeeken Aug 22, 2025
990a128
fix: assert thrown error correctly
nbbeeken Aug 26, 2025
4cced3e
fix: use trunc
nbbeeken Aug 26, 2025
c92237b
remove double pass
nbbeeken Aug 26, 2025
163e40c
fix: return index filtering to what it was
nbbeeken Aug 27, 2025
c179d47
chore: diff
nbbeeken Aug 27, 2025
10b2237
test: fix using ... vs …
nbbeeken Sep 2, 2025
8ab3c95
chore: try without setting "allUsers"
nbbeeken Sep 2, 2025
9cc7114
chore: set badge correctly
nbbeeken Sep 5, 2025
3ad263b
chore: remove mergeIndexes logic for buildprogress
nbbeeken Sep 5, 2025
fbbdd21
fix: building -> inprogress
nbbeeken Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ describe('Indexes Component', function () {
},
],
usageCount: 20,
buildProgress: 0,
},
],
inProgressIndexes: [
Expand All @@ -203,6 +204,7 @@ describe('Indexes Component', function () {
},
],
status: 'inprogress',
buildProgress: 0,
},
],
error: undefined,
Expand Down Expand Up @@ -245,6 +247,7 @@ describe('Indexes Component', function () {
},
],
usageCount: 20,
buildProgress: 0,
},
],
inProgressIndexes: [
Expand All @@ -259,6 +262,7 @@ describe('Indexes Component', function () {
],
status: 'failed',
error: 'Error message',
buildProgress: 0,
},
],
error: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('IndexActions Component', function () {
index={{
name: 'artist_id_index',
status: 'inprogress',
buildProgress: 0,
}}
onDeleteFailedIndexClick={onDeleteSpy}
/>
Expand All @@ -41,6 +42,7 @@ describe('IndexActions Component', function () {
index={{
name: 'artist_id_index',
status: 'failed',
buildProgress: 0,
}}
onDeleteFailedIndexClick={onDeleteSpy}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import React, { useCallback, useMemo } from 'react';
import type { GroupedItemAction } from '@mongodb-js/compass-components';
import { ItemActionGroup } from '@mongodb-js/compass-components';
import { ItemActionGroup, css, spacing } from '@mongodb-js/compass-components';
import type { InProgressIndex } from '../../modules/regular-indexes';

type Index = {
name: string;
status: InProgressIndex['status'];
buildProgress: number;
};

const indexActionsContainerStyles = css({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
gap: spacing[200],
});

type IndexActionsProps = {
index: Index;
onDeleteFailedIndexClick: (name: string) => void;
Expand Down Expand Up @@ -44,11 +52,13 @@ const IndexActions: React.FunctionComponent<IndexActionsProps> = ({
);

return (
<ItemActionGroup<IndexAction>
data-testid="index-actions"
actions={indexActions}
onAction={onAction}
></ItemActionGroup>
<div className={indexActionsContainerStyles}>
<ItemActionGroup<IndexAction>
data-testid="index-actions"
actions={indexActions}
onAction={onAction}
/>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ import { spy } from 'sinon';
import type { SinonSpy } from 'sinon';

import RegularIndexActions from './regular-index-actions';
import type { RegularIndex } from '../../modules/regular-indexes';

const commonIndexProperties: RegularIndex = {
name: 'artist_id_index',
type: 'regular',
cardinality: 'compound',
properties: [],
fields: [],
extra: {},
size: 0,
relativeSize: 0,
usageCount: 0,
buildProgress: 0,
};

describe('IndexActions Component', function () {
let onDeleteSpy: SinonSpy;
Expand All @@ -24,10 +38,104 @@ describe('IndexActions Component', function () {
onUnhideIndexSpy = spy();
});

describe('build progress display', function () {
it('does not display progress percentage when buildProgress is 0', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'test_index',
buildProgress: 0,
}}
serverVersion={'4.4.0'}
onDeleteIndexClick={onDeleteSpy}
onHideIndexClick={onHideIndexSpy}
onUnhideIndexClick={onUnhideIndexSpy}
/>
);

// Should not show building spinner or percentage
expect(() => screen.getByTestId('index-building-spinner')).to.throw(
/Unable to find/
);
expect(() => screen.getByText(/Building… \d+%/)).to.throw(
/Unable to find/
);
});

it('displays progress percentage when buildProgress is 50% (0.5)', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'test_index',
buildProgress: 0.5,
}}
serverVersion={'4.4.0'}
onDeleteIndexClick={onDeleteSpy}
onHideIndexClick={onHideIndexSpy}
onUnhideIndexClick={onUnhideIndexSpy}
/>
);

// Should show building spinner and percentage
const buildingSpinner = screen.getByTestId('index-building-spinner');
expect(buildingSpinner).to.exist;

const progressText = screen.getByText('Building... 50%');
expect(progressText).to.exist;
});

it('does not display progress percentage when buildProgress is 100% (1.0)', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'test_index',
buildProgress: 1.0,
}}
serverVersion={'4.4.0'}
onDeleteIndexClick={onDeleteSpy}
onHideIndexClick={onHideIndexSpy}
onUnhideIndexClick={onUnhideIndexSpy}
/>
);

// Should not show building spinner or percentage when complete
expect(() => screen.getByTestId('index-building-spinner')).to.throw;
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw;
});

it('displays cancel button when index is building', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'building_index',
buildProgress: 0.3,
}}
serverVersion={'4.4.0'}
onDeleteIndexClick={onDeleteSpy}
onHideIndexClick={onHideIndexSpy}
onUnhideIndexClick={onUnhideIndexSpy}
/>
);

const cancelButton = screen.getByLabelText('Cancel Index building_index');
expect(cancelButton).to.exist;
expect(onDeleteSpy.callCount).to.equal(0);
userEvent.click(cancelButton);
expect(onDeleteSpy.callCount).to.equal(1);
});
});

it('renders delete button for a regular index', function () {
render(
<RegularIndexActions
index={{ name: 'artist_id_index' }}
index={{
...commonIndexProperties,
name: 'artist_id_index',
}}
serverVersion={'4.4.0'}
onDeleteIndexClick={onDeleteSpy}
onHideIndexClick={onHideIndexSpy}
Expand All @@ -52,6 +160,7 @@ describe('IndexActions Component', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'artist_id_index',
}}
serverVersion={'4.4.0'}
Expand All @@ -75,6 +184,7 @@ describe('IndexActions Component', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'artist_id_index',
extra: { hidden: true },
}}
Expand Down Expand Up @@ -103,6 +213,7 @@ describe('IndexActions Component', function () {
render(
<RegularIndexActions
index={{
...commonIndexProperties,
name: 'artist_id_index',
extra: { hidden: true },
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import semver from 'semver';
import React, { useCallback, useMemo } from 'react';
import type { GroupedItemAction } from '@mongodb-js/compass-components';
import { css, ItemActionGroup } from '@mongodb-js/compass-components';
import {
css,
ItemActionGroup,
SpinLoader,
Body,
spacing,
} from '@mongodb-js/compass-components';
import type { RegularIndex } from '../../modules/regular-indexes';

const styles = css({
// Align actions with the end of the table
justifyContent: 'flex-end',
});

type Index = {
name: string;
extra?: {
hidden?: boolean;
};
};
const buildProgressStyles = css({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
gap: spacing[200],
});

type IndexActionsProps = {
index: Index;
index: RegularIndex;
serverVersion: string;
onDeleteIndexClick: (name: string) => void;
onHideIndexClick: (name: string) => void;
Expand Down Expand Up @@ -44,33 +51,46 @@ const IndexActions: React.FunctionComponent<IndexActionsProps> = ({
}) => {
const indexActions: GroupedItemAction<IndexAction>[] = useMemo(() => {
const actions: GroupedItemAction<IndexAction>[] = [];
const buildProgress = index.buildProgress;
const isBuilding = buildProgress > 0 && buildProgress < 1;

if (serverSupportsHideIndex(serverVersion)) {
actions.push(
index.extra?.hidden
? {
action: 'unhide',
label: `Unhide Index ${index.name}`,
tooltip: `Unhide Index`,
icon: 'Visibility',
}
: {
action: 'hide',
label: `Hide Index ${index.name}`,
tooltip: `Hide Index`,
icon: 'VisibilityOff',
}
);
}
if (isBuilding) {
// partially built
actions.push({
action: 'delete',
label: `Cancel Index ${index.name}`,
icon: 'XWithCircle',
variant: 'destructive',
});
} else {
// completed
if (serverSupportsHideIndex(serverVersion)) {
actions.push(
index.extra?.hidden
? {
action: 'unhide',
label: `Unhide Index ${index.name}`,
tooltip: `Unhide Index`,
icon: 'Visibility',
}
: {
action: 'hide',
label: `Hide Index ${index.name}`,
tooltip: `Hide Index`,
icon: 'VisibilityOff',
}
);
}

actions.push({
action: 'delete',
label: `Drop Index ${index.name}`,
icon: 'Trash',
});
actions.push({
action: 'delete',
label: `Drop Index ${index.name}`,
icon: 'Trash',
});
}

return actions;
}, [index, serverVersion]);
}, [index.name, index.extra?.hidden, index.buildProgress, serverVersion]);

const onAction = useCallback(
(action: IndexAction) => {
Expand All @@ -85,6 +105,21 @@ const IndexActions: React.FunctionComponent<IndexActionsProps> = ({
[onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index]
);

const buildProgress = index.buildProgress;
if (buildProgress > 0 && buildProgress < 1) {
return (
<div className={buildProgressStyles} data-testid="index-building-spinner">
<Body>Building... {Math.trunc(buildProgress * 100)}%</Body>
<SpinLoader size={16} title="Index build in progress" />
<ItemActionGroup<IndexAction>
data-testid="index-actions"
actions={indexActions}
onAction={onAction}
/>
</div>
);
}

return (
<ItemActionGroup<IndexAction>
data-testid="index-actions"
Expand Down
Loading
Loading