Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compass-global-writes): incomplete sharding setup COMPASS-8372 #6399

Merged
merged 10 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { renderWithStore } from '../../tests/create-store';
import { expect } from 'chai';
import { screen } from '@mongodb-js/testing-library-compass';
import ExampleCommandsMarkup, {
type ExampleCommandsMarkupProps,
} from './example-commands-markup';
import { type ShardKey } from '../store/reducer';

describe('ExampleCommandsMarkup', function () {
const db = 'db1';
const coll = 'coll1';
const namespace = `${db}.${coll}`;
const shardKey: ShardKey = {
fields: [
{ type: 'RANGE', name: 'location' },
{ type: 'HASHED', name: 'secondary' },
],
isUnique: false,
};

function renderWithProps(props?: Partial<ExampleCommandsMarkupProps>) {
return renderWithStore(
<ExampleCommandsMarkup
namespace={namespace}
shardKey={shardKey}
{...props}
/>
);
}

it('Contains sample codes', async function () {
await renderWithProps();

const findingDocumentsSample = await screen.findByTestId(
'sample-finding-documents'
);
expect(findingDocumentsSample).to.be.visible;
expect(findingDocumentsSample.textContent).to.contain(
`use db1db["coll1"].find({"location": "US-NY", "secondary": "<id_value>"})`
);

const insertingDocumentsSample = await screen.findByTestId(
'sample-inserting-documents'
);
expect(insertingDocumentsSample).to.be.visible;
expect(insertingDocumentsSample.textContent).to.contain(
`use db1db["coll1"].insertOne({"location": "US-NY", "secondary": "<id_value>",...<other fields>})`
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
Body,
Code,
css,
Label,
Link,
spacing,
Subtitle,
} from '@mongodb-js/compass-components';
import React, { useMemo } from 'react';
import type { ShardKey } from '../store/reducer';
import toNS from 'mongodb-ns';

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

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

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

export function ExampleCommandsMarkup({
namespace,
shardKey,
}: ExampleCommandsMarkupProps) {
const customShardKeyField = useMemo(() => {
return shardKey.fields[1].name;
}, [shardKey]);

const sampleCodes = useMemo(() => {
const { collection, database } = toNS(namespace);
return {
findingDocuments: `use ${database}\ndb[${JSON.stringify(
collection
)}].find({"location": "US-NY", "${customShardKeyField}": "<id_value>"})`,
insertingDocuments: `use ${database}\ndb[${JSON.stringify(
collection
)}].insertOne({"location": "US-NY", "${customShardKeyField}": "<id_value>",...<other fields>})`,
};
}, [namespace, customShardKeyField]);

return (
<>
<Subtitle>Example commands</Subtitle>
<div className={paragraphStyles}>
<Body>
Start querying your database with some of the most{' '}
<Link
href="https://www.mongodb.com/docs/atlas/global-clusters"
hideExternalIcon
>
common commands
</Link>{' '}
for Global Writes.
</Body>
<Body>
Replace the text to perform operations on different documents. US-NY
is an ISO 3166 location code referring to New York, United States. You
can look up other ISO 3166 location codes below.
</Body>
</div>

<div className={codeBlockContainerStyles}>
<Label htmlFor="finding-documents">Finding documents</Label>
<Code
language="js"
data-testid="sample-finding-documents"
id="finding-documents"
>
{sampleCodes.findingDocuments}
</Code>
</div>

<div className={codeBlockContainerStyles}>
<Label htmlFor="inserting-documents">Inserting documents</Label>
<Code
language="js"
data-testid="sample-inserting-documents"
id="inserting-documents"
>
{sampleCodes.insertingDocuments}
</Code>
</div>
</>
);
}

export default ExampleCommandsMarkup;
8 changes: 8 additions & 0 deletions packages/compass-global-writes/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ShardKeyCorrect from './states/shard-key-correct';
import ShardKeyInvalid from './states/shard-key-invalid';
import ShardKeyMismatch from './states/shard-key-mismatch';
import ShardingError from './states/sharding-error';
import IncompleteShardingSetup from './states/incomplete-sharding-setup';

const containerStyles = css({
paddingLeft: spacing[400],
Expand Down Expand Up @@ -93,6 +94,13 @@ function ShardingStateView({
return <ShardKeyMismatch />;
}

if (
shardingStatus === ShardingStatuses.INCOMPLETE_SHARDING_SETUP ||
shardingStatus === ShardingStatuses.SUBMITTING_FOR_SHARDING_INCOMPLETE
) {
return <IncompleteShardingSetup />;
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Body,
css,
Link,
spacing,
Subtitle,
} from '@mongodb-js/compass-components';
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
import React from 'react';

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

export function ShardZonesDescription() {
const { atlasMetadata } = useConnectionInfo();
return (
<>
<Subtitle>Location Codes</Subtitle>
<div className={paragraphStyles}>
<Body>
Each document’s first field should include an ISO 3166-1 Alpha-2 code
for the location it belongs to.
</Body>
<Body>
We also support ISO 3166-2 subdivision codes for countries containing
a cloud provider data center (both ISO 3166-1 and ISO 3166-2 codes may
be used for these countries). All valid country codes and the zones to
which they map are listed in the table below. Additionally, you can
view a list of all location codes{' '}
<Link href="/static/atlas/country_iso_codes.txt">here</Link>.
</Body>
<Body>
{atlasMetadata?.projectId && atlasMetadata?.clusterName && (
<>
Locations’ zone mapping can be changed by navigating to this
clusters{' '}
<Link
href={`/v2/${atlasMetadata?.projectId}#/clusters/edit/${atlasMetadata?.clusterName}`}
>
Edit Configuration
</Link>{' '}
page and clicking the Configure Location Mappings’ link above the
map.
</>
)}
</Body>
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { expect } from 'chai';
import { screen } from '@mongodb-js/testing-library-compass';
import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';
import { renderWithStore } from '../../tests/create-store';
import { ShardZonesDescription } from './shard-zones-description';

describe('ShardZonesDescription', () => {
it('Provides link to Edit Configuration', async function () {
const connectionInfo = {
id: 'testConnection',
connectionOptions: {
connectionString: 'mongodb://test',
},
atlasMetadata: {
projectId: 'project1',
clusterName: 'myCluster',
} as ConnectionInfo['atlasMetadata'],
};
await renderWithStore(<ShardZonesDescription />, {
connectionInfo,
});

const link = await screen.findByRole('link', {
name: /Edit Configuration/,
});
const expectedHref = `/v2/${connectionInfo.atlasMetadata?.projectId}#/clusters/edit/${connectionInfo.atlasMetadata?.clusterName}`;

expect(link).to.be.visible;
expect(link).to.have.attribute('href', expectedHref);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
type LgTableRowType,
} from '@mongodb-js/compass-components';
import type { ShardZoneData } from '../store/reducer';
import { ShardZonesDescription } from './shard-zones-description';

const containerStyles = css({
height: '400px',
Expand Down Expand Up @@ -131,6 +132,7 @@ export function ShardZonesTable({

return (
<>
<ShardZonesDescription />
<SearchInput
value={searchText}
onChange={handleSearchTextChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import { expect } from 'chai';
import { screen, userEvent } from '@mongodb-js/testing-library-compass';
import {
IncompleteShardingSetup,
type IncompleteShardingSetupProps,
} from './incomplete-sharding-setup';
import Sinon from 'sinon';
import { renderWithStore } from '../../../tests/create-store';
import { type ConnectionInfo } from '@mongodb-js/compass-connections/provider';
import { type ShardZoneData } from '../../store/reducer';

describe('IncompleteShardingSetup', function () {
const shardZones: ShardZoneData[] = [
{
zoneId: '45893084',
country: 'Germany',
readableName: 'Germany',
isoCode: 'DE',
typeOneIsoCode: 'DE',
zoneName: 'EMEA',
zoneLocations: ['Frankfurt'],
},
];
const baseProps: IncompleteShardingSetupProps = {
namespace: 'db1.coll1',
shardZones,
shardKey: {
fields: [
{ type: 'RANGE', name: 'location' },
{ type: 'HASHED', name: 'secondary' },
],
isUnique: false,
},
isSubmittingForSharding: false,
onResume: () => {},
};

const connectionInfo = {
id: 'testConnection',
connectionOptions: {
connectionString: 'mongodb://test',
},
atlasMetadata: {
projectId: 'project1',
clusterName: 'myCluster',
} as ConnectionInfo['atlasMetadata'],
};

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

it('Shows description', async function () {
await renderWithProps();

expect(screen.findByText(/your configuration is incomplete/)).to.be.exist;
expect(screen.findByText(/Please enable Global Writes/)).to.be.exist;
});

it('Provides button to resume managed namespace', async function () {
const onResume = Sinon.spy();
await renderWithProps({ onResume });

const btn = await screen.findByRole<HTMLButtonElement>('button', {
name: /Enable Global Writes/,
});
expect(btn).to.be.visible;

userEvent.click(btn);

expect(onResume).to.have.been.calledOnce;
});

it('Manage btn is disabled when the action is in progress', async function () {
const onResume = Sinon.spy();
await renderWithProps({ onResume, isSubmittingForSharding: true });

const btn = await screen.findByTestId<HTMLButtonElement>(
'manage-collection-button'
);
expect(btn).to.be.visible;
expect(btn.getAttribute('aria-disabled')).to.equal('true');

userEvent.click(btn);

expect(onResume).not.to.have.been.called;
});

it('Describes the shardKey', 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(`"location", "secondary"`);
});

it('Includes code examples', async function () {
await renderWithProps();

const example = await screen.findByText(/Example commands/);
expect(example).to.be.visible;
});
});
Loading
Loading