From d6f3df0b6003bde4b59866fd4aceaa6e80fa6592 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 24 Nov 2023 10:58:54 +0000 Subject: [PATCH] feat: disable the update preview when transactions are not supported COMPASS-7449 (#5152) * disable the update preview when transactions are not supported * lint * fix initial state test --- .../components/bulk-update-dialog.spec.tsx | 33 +- .../src/components/bulk-update-dialog.tsx | 46 +-- .../src/components/document-list.tsx | 3 + .../src/stores/crud-store.spec.ts | 309 +++++++++++++++--- .../compass-crud/src/stores/crud-store.ts | 26 ++ 5 files changed, 356 insertions(+), 61 deletions(-) diff --git a/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx b/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx index d3690065ceb..b309be52583 100644 --- a/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx +++ b/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx @@ -23,6 +23,7 @@ function renderBulkUpdateDialog( }, ], }} + enablePreview={true} closeBulkUpdateDialog={() => {}} updateBulkUpdatePreview={() => {}} runBulkUpdate={() => {}} @@ -156,9 +157,37 @@ describe('BulkUpdateDialog Component', function () { expect(onCloseSpy).to.have.been.calledOnce; }); - it('runs the update when the update button is clicked', function () { + it('runs the update when the update button is clicked (preview supported)', function () { const onUpdateSpy = sinon.spy(); - renderBulkUpdateDialog({ runBulkUpdate: onUpdateSpy, count: 60 }); + renderBulkUpdateDialog({ + enablePreview: true, + runBulkUpdate: onUpdateSpy, + count: 60, + }); + + // has a preview + expect( + screen.getAllByTestId('bulk-update-preview-document') + ).to.have.lengthOf(1); + + userEvent.click( + screen.getByRole('button', { name: 'Update 60 documents' }) + ); + expect(onUpdateSpy).to.have.been.calledOnce; + }); + + it('runs the update when the update button is clicked (preview unsupported)', function () { + const onUpdateSpy = sinon.spy(); + renderBulkUpdateDialog({ + enablePreview: false, + runBulkUpdate: onUpdateSpy, + count: 60, + }); + + // does not render a preview + expect( + screen.queryAllByTestId('bulk-update-preview-document') + ).to.have.lengthOf(0); userEvent.click( screen.getByRole('button', { name: 'Update 60 documents' }) diff --git a/packages/compass-crud/src/components/bulk-update-dialog.tsx b/packages/compass-crud/src/components/bulk-update-dialog.tsx index b55484edd85..691623ee85e 100644 --- a/packages/compass-crud/src/components/bulk-update-dialog.tsx +++ b/packages/compass-crud/src/components/bulk-update-dialog.tsx @@ -234,6 +234,7 @@ export type BulkUpdateDialogProps = { preview: UpdatePreview; syntaxError?: Error & { loc?: { index: number } }; serverError?: Error; + enablePreview?: boolean; closeBulkUpdateDialog: () => void; updateBulkUpdatePreview: (updateText: string) => void; runBulkUpdate: () => void; @@ -249,6 +250,7 @@ export default function BulkUpdateDialog({ preview, syntaxError, serverError, + enablePreview = false, closeBulkUpdateDialog, updateBulkUpdatePreview, runBulkUpdate, @@ -311,13 +313,13 @@ export default function BulkUpdateDialog({ -
+
-
- -
- {previewDocuments.map((doc: HadronDocument, index: number) => { - return ( - - ); - })} + {enablePreview && ( +
+ +
+ {previewDocuments.map((doc: HadronDocument, index: number) => { + return ( + + ); + })} +
-
+ )}
diff --git a/packages/compass-crud/src/components/document-list.tsx b/packages/compass-crud/src/components/document-list.tsx index 1dd6e4fd6c0..5e27b75fca6 100644 --- a/packages/compass-crud/src/components/document-list.tsx +++ b/packages/compass-crud/src/components/document-list.tsx @@ -107,6 +107,7 @@ export type DocumentListProps = { darkMode?: boolean; isCollectionScan?: boolean; isSearchIndexesSupported: boolean; + isUpdatePreviewSupported: boolean; query: QueryState; } & Omit & Omit & @@ -279,6 +280,7 @@ class DocumentList extends React.Component { ns={this.props.ns} filter={this.props.query.filter} count={this.props.count} + enablePreview={this.props.isUpdatePreviewSupported} {...this.props.bulkUpdate} closeBulkUpdateDialog={this.props.closeBulkUpdateDialog} updateBulkUpdatePreview={this.props.updateBulkUpdatePreview} @@ -564,6 +566,7 @@ DocumentList.propTypes = { darkMode: PropTypes.bool, isCollectionScan: PropTypes.bool, isSearchIndexesSupported: PropTypes.bool, + isUpdatePreviewSupported: PropTypes.bool, }; DocumentList.defaultProps = { diff --git a/packages/compass-crud/src/stores/crud-store.spec.ts b/packages/compass-crud/src/stores/crud-store.spec.ts index fee34d8102d..fe31852258c 100644 --- a/packages/compass-crud/src/stores/crud-store.spec.ts +++ b/packages/compass-crud/src/stores/crud-store.spec.ts @@ -183,6 +183,14 @@ describe('store', function () { globalAppRegistry: globalAppRegistry, actions: actions, isSearchIndexesSupported: true, + isReadonly: false, + isTimeSeries: false, + namespace: 'db.col', + noRefreshOnConfigure: true, // so it won't start loading before we can check the initial state + dataProvider: { + dataProvider: dataService, + }, + isUpdatePreviewSupported: true, }); }); @@ -199,7 +207,7 @@ describe('store', function () { }, debouncingLoad: false, loadingCount: false, - collection: '', + collection: 'col', count: 0, docs: [], end: 0, @@ -229,8 +237,9 @@ describe('store', function () { isReadonly: false, isSearchIndexesSupported: true, isTimeSeries: false, + isUpdatePreviewSupported: true, isWritable: false, - ns: '', + ns: 'db.col', outdated: false, page: 0, query: { @@ -270,6 +279,14 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, actions: actions, + isSearchIndexesSupported: true, + isReadonly: false, + isTimeSeries: false, + namespace: 'db.col', + dataProvider: { + dataProvider: dataService, + }, + isUpdatePreviewSupported: true, }); mockCopyToClipboard = sinon.fake.resolves(null); @@ -313,6 +330,14 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, actions: actions, + isSearchIndexesSupported: true, + isReadonly: false, + isTimeSeries: false, + namespace: 'db.col', + dataProvider: { + dataProvider: dataService, + }, + isUpdatePreviewSupported: true, }); await store.openInsertDocumentDialog({ foo: 1 }); }); @@ -349,10 +374,14 @@ describe('store', function () { globalAppRegistry: globalAppRegistry, namespace: 'compass-crud.another', dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, + isSearchIndexesSupported: true, + isReadonly: false, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -388,12 +417,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, namespace: 'compass-crud.another', actions: actions, isReadonly: true, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); store.state.table.path = ['test-path']; store.state.table.types = ['test-types']; @@ -429,11 +461,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -468,12 +504,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -577,11 +617,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); await dataService.insertOne('compass-crud.test', { _id: 'testing', @@ -599,7 +643,7 @@ describe('store', function () { beforeEach(function () { store.state.docs = [hadronDoc]; - hadronDoc.elements.at(1).rename('new name'); + hadronDoc.elements.at(1)?.rename('new name'); }); it('replaces the document in the list', function (done) { @@ -692,7 +736,7 @@ describe('store', function () { const hadronDoc = new HadronDocument(doc); beforeEach(function () { - hadronDoc.elements.at(1).rename('new name'); + hadronDoc.elements.at(1)?.rename('new name'); sinon .stub(dataService, 'findOneAndUpdate') .throws({ message: 'error happened' }); @@ -713,7 +757,7 @@ describe('store', function () { const hadronDoc = new HadronDocument(doc); beforeEach(function () { - hadronDoc.elements.at(1).rename('new name'); + hadronDoc.elements.at(1)?.rename('new name'); sinon.stub(dataService, 'findOneAndUpdate').resolves(null); }); @@ -732,7 +776,7 @@ describe('store', function () { let stub; beforeEach(function () { - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); stub = sinon.stub(dataService, 'findOneAndUpdate').resolves({}); }); @@ -760,7 +804,7 @@ describe('store', function () { beforeEach(function () { store.state.shardKeys = { yes: 1 }; - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); stub = sinon.stub(dataService, 'findOneAndUpdate').resolves({}); }); @@ -813,7 +857,7 @@ describe('store', function () { let isUpdateAllowedStub; beforeEach(function () { - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); findOneAndReplaceStub = sinon .stub(dataService, 'findOneAndReplace') .resolves({}); @@ -854,11 +898,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); await dataService.insertOne('compass-crud.test', { name: 'testing' }); @@ -903,6 +951,7 @@ describe('store', function () { it('closes the bulk dialog keeping previous state', async function () { store.openBulkUpdateDialog(); + store.openBulkUpdateDialog(); await waitForState(store, (state) => { expect(state.bulkUpdate.previewAbortController).to.not.exist; @@ -947,11 +996,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1010,11 +1063,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1063,7 +1120,7 @@ describe('store', function () { let stub; beforeEach(function () { - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); stub = sinon.stub(dataService, 'findOneAndReplace').resolves({}); }); @@ -1086,7 +1143,7 @@ describe('store', function () { beforeEach(function () { store.state.shardKeys = { yes: 1 }; - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); stub = sinon.stub(dataService, 'findOneAndReplace').resolves({}); }); @@ -1120,7 +1177,7 @@ describe('store', function () { let isUpdateAllowedStub; beforeEach(function () { - hadronDoc.get('name').edit('Desert Sand'); + hadronDoc.get('name')?.edit('Desert Sand'); findOneAndReplaceStub = sinon .stub(dataService, 'findOneAndReplace') .resolves({}); @@ -1161,12 +1218,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1327,12 +1388,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1494,11 +1559,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1672,11 +1741,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1708,11 +1781,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1741,11 +1818,15 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -1771,12 +1852,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); await dataService.insertOne('compass-crud.test', { name: 'testing' }); }); @@ -1843,12 +1928,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); await dataService.insertOne('config.collections', { _id: 'compass-crud.test', @@ -1885,12 +1974,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); store.setState({ query: { project: { _id: 0 } } }); @@ -1922,12 +2015,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); store.setState({ isEditable: false }); @@ -1963,12 +2060,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.timeseries', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); store.setState({ isTimeSeries: true }); @@ -1997,7 +2098,7 @@ describe('store', function () { // the count should be the only aggregate we ran expect(spy.callCount).to.equal(1); const opts = spy.args[0][2]; - expect(opts.hint).to.not.exist; + expect(opts?.hint).to.not.exist; }); }); @@ -2011,12 +2112,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -2056,7 +2161,7 @@ describe('store', function () { // the count should be the only aggregate we ran expect(spy.callCount).to.equal(1); const opts = spy.args[0][2]; - expect(opts.hint).to.equal('_id_'); + expect(opts?.hint).to.equal('_id_'); }); }); }); @@ -2072,12 +2177,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.test', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); findSpy = sinon.spy(store.dataService, 'find'); @@ -2174,12 +2283,16 @@ describe('store', function () { localAppRegistry: localAppRegistry, globalAppRegistry: globalAppRegistry, dataProvider: { - error: null, + error: undefined, dataProvider: dataService, }, actions: actions, namespace: 'compass-crud.testview', noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); await dataService.insertMany('compass-crud.test', [ { _id: '001', cat: 'nori' }, @@ -2596,8 +2709,7 @@ describe('store', function () { }); describe('saveUpdateQuery', function () { - const favoriteQueriesStorage: FavoriteQueryStorage = - new FavoriteQueryStorage(); + const favoriteQueriesStorage = new FavoriteQueryStorage(); let saveQueryStub; let actions; @@ -2618,6 +2730,10 @@ describe('store', function () { namespace: 'compass-crud.testview', noRefreshOnConfigure: true, favoriteQueriesStorage: favoriteQueriesStorage, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); @@ -2639,8 +2755,121 @@ describe('store', function () { }); }); + describe('updateBulkUpdatePreview', function () { + context('with isUpdatePreviewSupported=false', function () { + let previewUpdateStub; + let actions; + let store; + + beforeEach(function () { + previewUpdateStub = sinon.stub().resolves({ + changes: [ + { + before: {}, + after: {}, + }, + ], + }); + dataService.previewUpdate = previewUpdateStub; + fakeInstance.topologyDescription.type = 'Single'; + actions = configureActions(); + store = configureStore({ + localAppRegistry: localAppRegistry, + globalAppRegistry: globalAppRegistry, + dataProvider: { + dataProvider: dataService, + }, + actions: actions, + namespace: 'compass-crud.testview', + noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: false, // taken from fakeInstance.topologyDescription.type anyway + }); + }); + + it('never calls dataService.previewUpdate()', async function () { + store.openBulkUpdateDialog(); + await store.onQueryChanged({ filter: { field: 1 } }); + await store.updateBulkUpdatePreview('{ $set: { anotherField: 2 } }'); + + expect(previewUpdateStub.called).to.be.false; + + expect(store.state.bulkUpdate).to.deep.equal({ + isOpen: true, + preview: { + changes: [], + }, + serverError: undefined, + syntaxError: undefined, + updateText: '{ $set: { anotherField: 2 } }', + }); + }); + }); + + context('with isUpdatePreviewSupported=true', function () { + let previewUpdateStub; + let actions; + let store; + + beforeEach(function () { + previewUpdateStub = sinon.stub().resolves({ + changes: [ + { + before: {}, + after: {}, + }, + ], + }); + dataService.previewUpdate = previewUpdateStub; + fakeInstance.topologyDescription.type = 'Unknown'; // anything not 'Single' + actions = configureActions(); + store = configureStore({ + localAppRegistry: localAppRegistry, + globalAppRegistry: globalAppRegistry, + dataProvider: { + dataProvider: dataService, + }, + actions: actions, + namespace: 'compass-crud.testview', + noRefreshOnConfigure: true, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, // taken from fakeInstance.topologyDescription.type anyway + }); + }); + + it('calls dataService.previewUpdate()', async function () { + store.openBulkUpdateDialog(); + await store.onQueryChanged({ filter: { field: 1 } }); + await store.updateBulkUpdatePreview('{ $set: { anotherField: 2 } }'); + + // why two? because it also gets called when the dialog opens + expect(previewUpdateStub.callCount).to.equal(2); + + expect(store.state.bulkUpdate).to.deep.equal({ + isOpen: true, + preview: { + changes: [ + { + before: {}, + after: {}, + }, + ], + }, + previewAbortController: undefined, + serverError: undefined, + syntaxError: undefined, + updateText: '{ $set: { anotherField: 2 } }', + }); + }); + }); + }); + describe('saveRecentQueryQuery', function () { - const recentQueriesStorage: RecentQueryStorage = new RecentQueryStorage(); + const recentQueriesStorage = new RecentQueryStorage(); let saveQueryStub; let actions; @@ -2661,6 +2890,10 @@ describe('store', function () { namespace: 'compass-crud.testview', noRefreshOnConfigure: true, recentQueriesStorage: recentQueriesStorage, + isReadonly: false, + isSearchIndexesSupported: true, + isTimeSeries: false, + isUpdatePreviewSupported: true, }); }); diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index 28148cb06c1..81c37b4aaab 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -336,6 +336,7 @@ type CrudStoreOptions = { dataProvider: { error?: Error; dataProvider?: DataService }; noRefreshOnConfigure?: boolean; isSearchIndexesSupported: boolean; + isUpdatePreviewSupported: boolean; favoriteQueriesStorage?: FavoriteQueryStorage; recentQueriesStorage?: RecentQueryStorage; }; @@ -428,6 +429,7 @@ type CrudState = { fields: string[]; isCollectionScan?: boolean; isSearchIndexesSupported: boolean; + isUpdatePreviewSupported: boolean; bulkDelete: BulkDeleteState; }; @@ -495,6 +497,7 @@ class CrudStoreImpl fields: [], isCollectionScan: false, isSearchIndexesSupported: false, + isUpdatePreviewSupported: false, bulkDelete: { previews: [], status: 'closed', @@ -1129,6 +1132,28 @@ class CrudStoreImpl }, }); + // Don't try and calculate the update preview if we know it won't work. Just + // see if the update will parse. + if (!this.state.isUpdatePreviewSupported) { + try { + parseShellBSON(updateText); + } catch (err: any) { + this.setState({ + bulkUpdate: { + ...this.state.bulkUpdate, + preview: { + changes: [], + }, + serverError: undefined, + syntaxError: err, + previewAbortController: undefined, + }, + }); + } + + return; + } + const abortController = new AbortController(); this.setState({ @@ -2009,6 +2034,7 @@ const configureStore = (options: CrudStoreOptions & GridStoreOptions) => { isWritable: instance.isWritable, instanceDescription: instance.description, version: instance.build.version, + isUpdatePreviewSupported: instance.topologyDescription.type !== 'Single', }; if (instance.dataLake.isDataLake) { instanceState.isDataLake = true;