Skip to content

Commit 7d42eef

Browse files
committed
merge main back into branch
Merge branch 'main' into extend-user-data
2 parents 3b8fc66 + 01668f8 commit 7d42eef

File tree

26 files changed

+1232
-171
lines changed

26 files changed

+1232
-171
lines changed

.evergreen/functions.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ functions:
731731
test-web-sandbox-atlas-cloud:
732732
- command: shell.exec
733733
# It can take a very long time for Atlas cluster to get deployed
734-
timeout_secs: 2400
734+
timeout_secs: 3600 # 1 hour
735735
params:
736736
working_dir: src
737737
shell: bash
@@ -746,6 +746,9 @@ functions:
746746
MCLI_ORG_ID: ${e2e_tests_mcli_org_id}
747747
MCLI_PROJECT_ID: ${e2e_tests_mcli_project_id}
748748
MCLI_OPS_MANAGER_URL: ${e2e_tests_mcli_ops_manager_url}
749+
# CCS connection / op running time is slower than allowed timeouts
750+
COMPASS_E2E_MOCHA_TIMEOUT: '720000' # 12 min
751+
COMPASS_E2E_WEBDRIVER_WAITFOR_TIMEOUT: '360000' # 6 min
749752
script: |
750753
set -e
751754
# Load environment variables

THIRD-PARTY-NOTICES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
The following third-party software is used by and included in **Mongodb Compass**.
2-
This document was automatically generated on Fri Jul 18 2025.
2+
This document was automatically generated on Tue Jul 22 2025.
33

44
## List of dependencies
55

configs/webpack-config-compass/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ const sharedIgnoreWarnings: NonNullable<Configuration['ignoreWarnings']> = [
4545
/the request of a dependency is an expression/,
4646
// Optional, platform-specific dependencies (mostly from driver)
4747
/Module not found.+?(mongo_crypt_v1.(dll|so|dylib)|@mongodb-js\/zstd|aws-crt|gcp-metadata)/,
48+
// Optional, comes from emotion trying to (safely) use react apis that we
49+
// don't have in React 17
50+
/export 'useInsertionEffect'/,
4851
];
4952

5053
const sharedResolveOptions = (

docs/tracking-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
> the tracking plan for the specific Compass version you can use the following
77
> URL: `https://github.com/mongodb-js/compass/blob/<compass version>/docs/tracking-plan.md`
88
9-
Generated on Fri, Jul 18, 2025
9+
Generated on Tue, Jul 22, 2025
1010

1111
## Table of Contents
1212

packages/compass-app-stores/src/stores/instance-store.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,13 @@ export function createInstancesStore(
101101
> = {},
102102
{ connectionId }: { connectionId?: string } = {}
103103
) => {
104-
let isFirstRun: boolean | undefined;
105-
106104
try {
107105
if (!connectionId) {
108106
throw new Error('No connectionId provided');
109107
}
110108
const instance =
111109
instancesManager.getMongoDBInstanceForConnection(connectionId);
112110
const dataService = connections.getDataServiceForConnection(connectionId);
113-
isFirstRun = instance.status === 'initial';
114111
await instance.refresh({
115112
dataService,
116113
...refreshOptions,
@@ -123,7 +120,7 @@ export function createInstancesStore(
123120
{
124121
message: (err as Error).message,
125122
connectionId: connectionId,
126-
isFirstRun,
123+
isFirstRun: refreshOptions.firstRun,
127124
}
128125
);
129126
// The `instance.refresh` method is catching all expected errors: we treat
@@ -137,7 +134,7 @@ export function createInstancesStore(
137134
// place for the user to see the error. This is a very rare case, but we
138135
// don't want to leave the user without any indication that something went
139136
// wrong and so we show an toast with the error message
140-
if (isFirstRun) {
137+
if (refreshOptions.firstRun) {
141138
const { name, message } = err as Error;
142139
openToast('instance-refresh-failed', {
143140
title: 'Failed to retrieve server info',
@@ -359,6 +356,7 @@ export function createInstancesStore(
359356
{
360357
fetchDatabases: true,
361358
fetchDbStats: true,
359+
firstRun: true,
362360
},
363361
{
364362
connectionId: instanceConnectionId,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import type { Relationship } from '../services/data-model-storage';
4+
import { Button, H3 } from '@mongodb-js/compass-components';
5+
import {
6+
createNewRelationship,
7+
deleteRelationship,
8+
getCurrentDiagramFromState,
9+
selectCurrentModel,
10+
selectRelationship,
11+
} from '../store/diagram';
12+
import type { DataModelingState } from '../store/reducer';
13+
14+
type CollectionDrawerContentProps = {
15+
namespace: string;
16+
relationships: Relationship[];
17+
shouldShowRelationshipEditingForm?: boolean;
18+
onCreateNewRelationshipClick: (namespace: string) => void;
19+
onEditRelationshipClick: (rId: string) => void;
20+
onDeleteRelationshipClick: (rId: string) => void;
21+
};
22+
23+
const CollectionDrawerContent: React.FunctionComponent<
24+
CollectionDrawerContentProps
25+
> = ({
26+
namespace,
27+
relationships,
28+
onCreateNewRelationshipClick,
29+
onEditRelationshipClick,
30+
onDeleteRelationshipClick,
31+
}) => {
32+
return (
33+
<>
34+
<H3>{namespace}</H3>
35+
<ul>
36+
{relationships.map((r) => {
37+
return (
38+
<li key={r.id} data-relationship-id={r.id}>
39+
{r.relationship[0].fields?.join('.')}&nbsp;-&gt;&nbsp;
40+
{r.relationship[1].fields?.join('.')}
41+
<Button
42+
onClick={() => {
43+
onEditRelationshipClick(r.id);
44+
}}
45+
>
46+
Edit
47+
</Button>
48+
<Button
49+
onClick={() => {
50+
onDeleteRelationshipClick(r.id);
51+
}}
52+
>
53+
Delete
54+
</Button>
55+
</li>
56+
);
57+
})}
58+
</ul>
59+
<Button
60+
onClick={() => {
61+
onCreateNewRelationshipClick(namespace);
62+
}}
63+
>
64+
Add relationship manually
65+
</Button>
66+
</>
67+
);
68+
};
69+
70+
export default connect(
71+
(state: DataModelingState, ownProps: { namespace: string }) => {
72+
return {
73+
relationships: selectCurrentModel(
74+
getCurrentDiagramFromState(state).edits
75+
).relationships.filter((r) => {
76+
const [local, foreign] = r.relationship;
77+
return (
78+
local.ns === ownProps.namespace || foreign.ns === ownProps.namespace
79+
);
80+
}),
81+
};
82+
},
83+
{
84+
onCreateNewRelationshipClick: createNewRelationship,
85+
onEditRelationshipClick: selectRelationship,
86+
onDeleteRelationshipClick: deleteRelationship,
87+
}
88+
)(CollectionDrawerContent);

packages/compass-data-modeling/src/components/data-modeling.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import NewDiagramFormModal from './new-diagram-form';
66
import type { DataModelingState } from '../store/reducer';
77
import { DiagramProvider } from '@mongodb-js/diagramming';
88
import DiagramEditorSidePanel from './diagram-editor-side-panel';
9-
type DataModelingPluginInitialProps = {
9+
10+
type DataModelingProps = {
1011
showList: boolean;
1112
};
1213

13-
const DataModeling: React.FunctionComponent<DataModelingPluginInitialProps> = ({
14+
const DataModeling: React.FunctionComponent<DataModelingProps> = ({
1415
showList,
1516
}) => {
1617
return (
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import {
4+
createPluginTestHelpers,
5+
screen,
6+
waitFor,
7+
userEvent,
8+
within,
9+
} from '@mongodb-js/testing-library-compass';
10+
import { DataModelingWorkspaceTab } from '../index';
11+
import DiagramEditorSidePanel from './diagram-editor-side-panel';
12+
import {
13+
getCurrentDiagramFromState,
14+
openDiagram,
15+
selectCollection,
16+
selectCurrentModel,
17+
selectRelationship,
18+
} from '../store/diagram';
19+
import dataModel from '../../test/fixtures/data-model-with-relationships.json';
20+
import type { MongoDBDataModelDescription } from '../services/data-model-storage';
21+
22+
async function comboboxSelectItem(
23+
label: string,
24+
value: string,
25+
visibleLabel = value
26+
) {
27+
userEvent.click(screen.getByRole('textbox', { name: label }));
28+
await waitFor(() => {
29+
screen.getByRole('option', { name: visibleLabel });
30+
});
31+
userEvent.click(screen.getByRole('option', { name: visibleLabel }));
32+
await waitFor(() => {
33+
expect(screen.getByRole('textbox', { name: label })).to.have.attribute(
34+
'value',
35+
value
36+
);
37+
});
38+
}
39+
40+
describe('DiagramEditorSidePanel', function () {
41+
function renderDrawer() {
42+
const { renderWithConnections } = createPluginTestHelpers(
43+
DataModelingWorkspaceTab.provider.withMockServices({})
44+
);
45+
const result = renderWithConnections(
46+
<DiagramEditorSidePanel></DiagramEditorSidePanel>
47+
);
48+
result.plugin.store.dispatch(
49+
openDiagram(dataModel as MongoDBDataModelDescription)
50+
);
51+
return result;
52+
}
53+
54+
it('should not render if no items are selected', function () {
55+
renderDrawer();
56+
expect(screen.queryByTestId('data-modeling-drawer')).to.eq(null);
57+
});
58+
59+
it('should render a collection context drawer when collection is clicked', function () {
60+
const result = renderDrawer();
61+
result.plugin.store.dispatch(selectCollection('flights.airlines'));
62+
expect(screen.getByText('flights.airlines')).to.be.visible;
63+
});
64+
65+
it('should render a relationship context drawer when relations is clicked', function () {
66+
const result = renderDrawer();
67+
result.plugin.store.dispatch(
68+
selectRelationship('204b1fc0-601f-4d62-bba3-38fade71e049')
69+
);
70+
expect(screen.getByText('Edit Relationship')).to.be.visible;
71+
expect(
72+
document.querySelector(
73+
'[data-relationship-id="204b1fc0-601f-4d62-bba3-38fade71e049"]'
74+
)
75+
).to.be.visible;
76+
});
77+
78+
it('should change the content of the drawer when selecting different items', function () {
79+
const result = renderDrawer();
80+
81+
result.plugin.store.dispatch(selectCollection('flights.airlines'));
82+
expect(screen.getByText('flights.airlines')).to.be.visible;
83+
84+
result.plugin.store.dispatch(
85+
selectCollection('flights.airports_coordinates_for_schema')
86+
);
87+
expect(screen.getByText('flights.airports_coordinates_for_schema')).to.be
88+
.visible;
89+
90+
result.plugin.store.dispatch(
91+
selectRelationship('204b1fc0-601f-4d62-bba3-38fade71e049')
92+
);
93+
expect(
94+
document.querySelector(
95+
'[data-relationship-id="204b1fc0-601f-4d62-bba3-38fade71e049"]'
96+
)
97+
).to.be.visible;
98+
99+
result.plugin.store.dispatch(
100+
selectRelationship('6f776467-4c98-476b-9b71-1f8a724e6c2c')
101+
);
102+
expect(
103+
document.querySelector(
104+
'[data-relationship-id="6f776467-4c98-476b-9b71-1f8a724e6c2c"]'
105+
)
106+
).to.be.visible;
107+
108+
result.plugin.store.dispatch(selectCollection('flights.planes'));
109+
expect(screen.getByText('flights.planes')).to.be.visible;
110+
});
111+
112+
it('should open and edit relationship starting from collection', async function () {
113+
const result = renderDrawer();
114+
result.plugin.store.dispatch(selectCollection('flights.countries'));
115+
116+
// Open relationshipt editing form
117+
const relationshipCard = document.querySelector<HTMLElement>(
118+
'[data-relationship-id="204b1fc0-601f-4d62-bba3-38fade71e049"]'
119+
);
120+
userEvent.click(
121+
within(relationshipCard!).getByRole('button', { name: 'Edit' })
122+
);
123+
expect(screen.getByText('Edit Relationship')).to.be.visible;
124+
125+
// Select new values
126+
await comboboxSelectItem('Local collection', 'planes');
127+
await comboboxSelectItem('Local field', 'name');
128+
await comboboxSelectItem('Foreign collection', 'countries');
129+
await comboboxSelectItem('Foreign field', 'iso_code');
130+
131+
// We should be testing through rendered UI but as it's really hard to make
132+
// diagram rendering in tests property, we are just validating the final
133+
// model here
134+
const modifiedRelationship = selectCurrentModel(
135+
getCurrentDiagramFromState(result.plugin.store.getState()).edits
136+
).relationships.find((r) => {
137+
return r.id === '204b1fc0-601f-4d62-bba3-38fade71e049';
138+
});
139+
140+
expect(modifiedRelationship)
141+
.to.have.property('relationship')
142+
.deep.eq([
143+
{
144+
ns: 'flights.planes',
145+
fields: ['name'],
146+
cardinality: 1,
147+
},
148+
{
149+
ns: 'flights.countries',
150+
fields: ['iso_code'],
151+
cardinality: 1,
152+
},
153+
]);
154+
});
155+
});

0 commit comments

Comments
 (0)