Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
14 changes: 14 additions & 0 deletions packages/compass-global-writes/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import { ShardingStatuses } from '../store/reducer';
import UnshardedState from './states/unsharded';
import ShardingState from './states/sharding';
import ShardKeyCorrect from './states/shard-key-correct';
import ShardKeyInvalid from './states/shard-key-invalid';
import ShardKeyMismatch from './states/shard-key-mismatch';

const containerStyles = css({
paddingLeft: spacing[400],
paddingRight: spacing[400],
display: 'flex',
width: '100%',
height: '100%',
maxWidth: '700px',
});

const workspaceContentStyles = css({
Expand Down Expand Up @@ -70,6 +73,17 @@ function ShardingStateView({
return <ShardKeyCorrect />;
}

if (shardingStatus === ShardingStatuses.SHARD_KEY_INVALID) {
return <ShardKeyInvalid />;
}

if (
shardingStatus === ShardingStatuses.SHARD_KEY_MISMATCH ||
shardingStatus === ShardingStatuses.UNMANAGING_NAMESPACE_MISMATCH
) {
return <ShardKeyMismatch />;
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Body, Code, css, spacing } from '@mongodb-js/compass-components';
import React from 'react';
import type { ShardKey } from '../store/reducer';

const codeBlockContainerStyles = css({
display: 'flex',
flexDirection: 'column',
gap: spacing[100],
});

interface ShardKeyMarkupProps {
shardKey: ShardKey;
namespace: string;
showMetaData?: boolean;
type?: 'requested' | 'existing';
}

export function ShardKeyMarkup({
namespace,
shardKey,
showMetaData,
type = 'existing',
}: ShardKeyMarkupProps) {
let markup = shardKey.fields
.map(
(field) =>
`"${field.name}"` +
(showMetaData ? ` (${field.type.toLowerCase()})` : '')
)
.join(', ');
if (showMetaData) {
markup += ` - unique: ${String(shardKey.isUnique)}`;
}
return (
<div className={codeBlockContainerStyles}>
<Body data-testid={`${type}-shardkey-description-title`}>
{type === 'existing' ? (
<>
<strong>{namespace}</strong> is configured with the following shard
key:
</>
) : (
<>You requested to use the shard key:</>
)}
</Body>
<Code language="js" data-testid={`${type}-shardkey-description-content`}>
{markup}
</Code>
</div>
);
}

export default ShardKeyMarkup;
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ describe('Compass GlobalWrites Plugin', function () {
namespace: 'db1.coll1',
shardKey: {
fields: [
{ type: 'HASHED', name: 'location' },
{ type: 'RANGE', name: 'secondary' },
{ type: 'RANGE', name: 'location' },
{ type: 'HASHED', name: 'secondary' },
],
isUnique: false,
},
Expand Down Expand Up @@ -66,7 +66,7 @@ describe('Compass GlobalWrites Plugin', function () {
await renderWithProps({ onUnmanageNamespace, isUnmanagingNamespace: true });

const btn = await screen.findByTestId<HTMLButtonElement>(
'shard-collection-button'
'unmanage-collection-button'
);
expect(btn).to.be.visible;
expect(btn.getAttribute('aria-disabled')).to.equal('true');
Expand Down Expand Up @@ -103,12 +103,16 @@ describe('Compass GlobalWrites Plugin', function () {
it('Describes the shardKey', async function () {
await renderWithProps();

const title = await screen.findByTestId('shardkey-description-title');
const title = await screen.findByTestId(
'existing-shardkey-description-title'
);
expect(title).to.be.visible;
expect(title.textContent).to.equal(
`${baseProps.namespace} is configured with the following shard key:`
);
const list = await screen.findByTestId('shardkey-description-content');
const list = await screen.findByTestId(
'existing-shardkey-description-content'
);
expect(list).to.be.visible;
expect(list.textContent).to.contain(`"location", "secondary"`);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Subtitle,
Label,
Button,
ButtonVariant,
} from '@mongodb-js/compass-components';
import { connect } from 'react-redux';
import {
Expand All @@ -22,6 +23,7 @@ import {
import toNS from 'mongodb-ns';
import { ShardZonesTable } from '../shard-zones-table';
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
import ShardKeyMarkup from '../shard-key-markup';

const nbsp = '\u00a0';

Expand Down Expand Up @@ -92,17 +94,7 @@ export function ShardKeyCorrect({
</strong>
{nbsp}We have included a table for reference below.
</Banner>

<div className={codeBlockContainerStyles}>
<Body data-testid="shardkey-description-title">
<strong>{namespace}</strong> is configured with the following shard
key:
</Body>
<Code language="js" data-testid="shardkey-description-content">
{shardKey.fields.map((field) => `"${field.name}"`).join(', ')}
</Code>
</div>

<ShardKeyMarkup namespace={namespace} shardKey={shardKey} />
<Subtitle>Example commands</Subtitle>
<div className={paragraphStyles}>
<Body>
Expand Down Expand Up @@ -184,9 +176,9 @@ export function ShardKeyCorrect({
</Body>
<div>
<Button
data-testid="shard-collection-button"
data-testid="unmanage-collection-button"
onClick={onUnmanageNamespace}
variant="primary"
variant={ButtonVariant.Primary}
isLoading={isUnmanagingNamespace}
>
Unmanage collection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import { expect } from 'chai';
import { screen } from '@mongodb-js/testing-library-compass';
import {
ShardKeyInvalid,
type ShardKeyInvalidProps,
} from './shard-key-invalid';
import { renderWithStore } from '../../../tests/create-store';

describe('Compass GlobalWrites Plugin', function () {
const baseProps: ShardKeyInvalidProps = {
namespace: 'db1.coll1',
shardKey: {
fields: [
{ type: 'HASHED', name: 'not-location' },
{ type: 'RANGE', name: 'secondary' },
],
isUnique: false,
},
};

function renderWithProps(
props?: Partial<ShardKeyInvalidProps>,
options?: Parameters<typeof renderWithStore>[1]
) {
return renderWithStore(
<ShardKeyInvalid {...baseProps} {...props} />,
options
);
}

it('Describes next steps', async function () {
await renderWithProps();

expect(screen.findByText(/Please migrate the data in this collection/)).to
.exist;
});

it('Describes the shardKey (with metadata)', async function () {
await renderWithProps();

const title = await screen.findByTestId(
'existing-shardkey-description-title'
);
expect(title).to.be.visible;
expect(title.textContent).to.equal(
`${baseProps.namespace} is configured with the following shard key:`
);
const list = await screen.findByTestId(
'existing-shardkey-description-content'
);
expect(list).to.be.visible;
expect(list.textContent).to.contain(
`"not-location" (hashed), "secondary" (range)`
);
expect(list.textContent).to.contain(`unique: false`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
Banner,
BannerVariant,
spacing,
css,
} from '@mongodb-js/compass-components';
import React from 'react';
import ShardKeyMarkup from '../shard-key-markup';
import type { RootState, ShardKey } from '../../store/reducer';
import { connect } from 'react-redux';

const containerStyles = css({
display: 'flex',
flexDirection: 'column',
gap: spacing[400],
marginBottom: spacing[400],
});

const paragraphStyles = css({
display: 'flex',
flexDirection: 'column',
gap: spacing[100],
});

export interface ShardKeyInvalidProps {
shardKey?: ShardKey;
namespace: string;
}

export function ShardKeyInvalid({ shardKey, namespace }: ShardKeyInvalidProps) {
if (!shardKey) {
throw new Error('Shard key not found in ShardKeyInvalid');
}
return (
<div className={containerStyles}>
<Banner variant={BannerVariant.Danger}>
<strong>
To configure Global Writes, the first shard key of this collection
must be &quot;location&quot; with ranged sharding and you must also
specify a second shard key.
</strong>{' '}
Please migrate the data in this collection to a new collection and
reshard it using a valid compound shard key.
</Banner>
<ShardKeyMarkup
namespace={namespace}
shardKey={shardKey}
showMetaData={true}
/>
<div className={paragraphStyles}>
Documents in this collection will be distributed across your shards
without being mapped to specific zones.
</div>
</div>
);
}

export default connect((state: RootState) => ({
namespace: state.namespace,
shardKey: state.shardKey,
}))(ShardKeyInvalid);
Loading
Loading