Skip to content

Commit

Permalink
chore(compass-collection): separate collection tab content from colle…
Browse files Browse the repository at this point in the history
…ction tabs management logic (#4858)

* chore(compass-collection): separate collection tab content from collection tabs management logic

* chore(compass-collection): subscribe to the collection model directly; pick stats props from modal before storing in state

* chore(compass-collection): simplify feature flag check

Co-authored-by: Anna Henningsen <[email protected]>

* chore(compass-collection): add tests for tabs store

* chore(compass-collection): add tests for collection tab store

* chore(compass-collection): re-enable workspace component tests

* chore(saved-queries, app-stores): do not emit open-namespace directly from my-queries plugin; add descriptions to collection metadata

* chore(compass-collection): use instance.env instead of atlas (prepare for #4875 to be merged)

---------

Co-authored-by: Anna Henningsen <[email protected]>
  • Loading branch information
gribnoysup and addaleax authored Sep 20, 2023
1 parent 51bf6ce commit a31253f
Show file tree
Hide file tree
Showing 73 changed files with 2,195 additions and 3,876 deletions.
6 changes: 4 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 50 additions & 14 deletions packages/collection-model/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
import type toNS from 'mongodb-ns';
import type { DataService } from 'mongodb-data-service';

type Namespace = ReturnType<typeof toNS>;
type CollectionMetadata = {
/**
* Collection namespace (<database>.<collection>)
*/
namespace: string;
/**
* Indicates that colleciton is read only
*/
isReadonly: boolean;
/**
* Indicates that colleciton is a time series collection
*/
isTimeSeries: boolean;
/**
* Indicates that collection is clustered / has a clustered index
*/
isClustered: boolean;
/**
* Indicates that collection has encrypted fields in it
*/
isFLE: boolean;
/**
* Indicates that MongoDB server supports search indexes (property is exposed
* on collection because the check is relevant for collection tab and requires
* collection namespace to perform the check)
*/
isSearchIndexesSupported: boolean;
/**
* View source namespace (<database>.<collection>)
*/
sourceName?: string;
/**
* Indicates if a source collection is read only
*/
sourceReadonly?: boolean;
/**
* View source view namespace (this is the same as metadata namespace if
* present)
*/
sourceViewon?: string;
/**
* Aggregation pipeline view definition
*/
sourcePipeline?: unknown[];
};

interface Collection {
_id: string;
Expand Down Expand Up @@ -40,18 +84,10 @@ interface Collection {
fetchInfo?: boolean;
force?: boolean;
}): Promise<void>;
fetchMetadata(opts: { dataService: DataService }): Promise<{
namespace: string;
isReadonly: boolean;
isTimeSeries: boolean;
isClustered: boolean;
isFLE: boolean;
isSearchIndexesSupported: boolean;
sourceName?: string;
sourceReadonly?: boolean;
sourceViewon?: string;
sourcePipeline?: unknown[];
}>;
fetchMetadata(opts: {
dataService: DataService;
}): Promise<CollectionMetadata>;
on(evt: string, fn: (...args: any) => void);
toJSON(opts?: { derived: boolean }): this;
}

Expand All @@ -63,4 +99,4 @@ interface CollectionCollection extends Array<Collection> {
}

export default Collection;
export { CollectionCollection };
export { CollectionCollection, CollectionMetadata };
33 changes: 21 additions & 12 deletions packages/compass-aggregations/src/plugin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ import Aggregations from './components/aggregations';
import { Provider } from 'react-redux';
import configureStore from './stores/store';
import { ConfirmationModalArea } from '@mongodb-js/compass-components';
import { withPreferences } from 'compass-preferences-model';

class Plugin extends Component {
static displayName = 'AggregationsPlugin';

class AggregationsPlugin extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
showExportButton: PropTypes.bool,
showRunButton: PropTypes.bool,
showExplainButton: PropTypes.bool,
enableImportExport: PropTypes.bool,
enableAggregationBuilderRunPipeline: PropTypes.bool,
enableExplainPlan: PropTypes.bool,
};

static defaultProps = {
showExportButton: false,
showRunButton: false,
showExplainButton: false,
enableImportExport: false,
enableAggregationBuilderRunPipeline: false,
enableExplainPlan: false,
};

/**
Expand All @@ -29,15 +28,25 @@ class Plugin extends Component {
<ConfirmationModalArea>
<Provider store={this.props.store}>
<Aggregations
showExportButton={this.props.showExportButton}
showRunButton={this.props.showRunButton}
showExplainButton={this.props.showExplainButton}
showExportButton={this.props.enableImportExport}
showRunButton={this.props.enableAggregationBuilderRunPipeline}
showExplainButton={this.props.enableExplainPlan}
/>
</Provider>
</ConfirmationModalArea>
);
}
}

const Plugin = withPreferences(
AggregationsPlugin,
[
'enableImportExport',
'enableAggregationBuilderRunPipeline',
'enableExplainPlan',
],
React
);

export default Plugin;
export { Plugin, configureStore };
4 changes: 1 addition & 3 deletions packages/compass-aggregations/src/plugin.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import Aggregations from './plugin';

const renderPlugin = () => {
const store = configureStore();
render(
<Aggregations showExportButton={true} showRunButton={true} store={store} />
);
render(<Aggregations store={store} />);
};

describe('Aggregations [Plugin]', function () {
Expand Down
105 changes: 79 additions & 26 deletions packages/compass-app-stores/src/stores/instance-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ function getTopologyDescription(topologyDescription) {

const store = createStore(reducer);

store.getInstance = () => {
return store.getState().instance;
};

store.refreshInstance = async (globalAppRegistry, refreshOptions) => {
const { instance, dataService } = store.getState();

Expand Down Expand Up @@ -247,6 +251,20 @@ store.onActivated = (appRegistry) => {
store.refreshInstance(appRegistry);
});

appRegistry.on('database-dropped', async () => {
const { instance, dataService } = store.getState();
await instance.fetchDatabases({ dataService, force: true });
});

appRegistry.on('collection-dropped', async (namespace) => {
const { instance, dataService } = store.getState();
const { database } = toNS(namespace);
await instance.fetchDatabases({ dataService, force: true });
const db = instance.databases.get(database);
// If it was last collection, there will be no db returned
await db?.fetchCollections({ dataService, force: true });
});

// Event emitted when the Databases grid needs to be refreshed
// We additionally refresh the list of collections as well
// since there is the side navigation which could be in expanded mode
Expand All @@ -268,7 +286,6 @@ store.onActivated = (appRegistry) => {
if (!instance.databases.get(database)) {
await instance.fetchDatabases({ dataService, force: true });
}

const db = instance.databases.get(database);
if (db) {
await db.fetchCollectionsDetails({ dataService, force: true });
Expand Down Expand Up @@ -297,46 +314,75 @@ store.onActivated = (appRegistry) => {
appRegistry.emit('select-namespace', metadata);
});

appRegistry.on('active-collection-dropped', async (ns) => {
const { instance, dataService } = store.getState();
const { database } = toNS(ns);
await store.fetchDatabaseDetails(database);
const db = instance.databases.get(database);
await db.fetchCollections({ dataService, force: true });

if (db.collectionsLength) {
appRegistry.emit('select-database', database);
} else {
appRegistry.emit('open-instance-workspace', 'Databases');
}
appRegistry.on('active-collection-dropped', (ns) => {
// This callback will fire after drop collection happened, we force it into
// a microtask to allow drop collections event handler to force start
// databases and collections list update before we run our check here
queueMicrotask(async () => {
const { instance, dataService } = store.getState();
const { database } = toNS(ns);
await instance.fetchDatabases({ dataService });
const db = instance.databases.get(database);
await db?.fetchCollections({ dataService });
if (db?.collectionsLength) {
appRegistry.emit('select-database', database);
} else {
appRegistry.emit('open-instance-workspace', 'Databases');
}
});
});

appRegistry.on('active-database-dropped', async () => {
appRegistry.emit('open-instance-workspace', 'Databases');
});

appRegistry.on('collections-list-select-collection', async ({ ns }) => {
/**
* Opens collection in the current active tab. No-op if currently open tab has
* the same namespace. Additional `query` and `agrregation` props can be
* passed with the namespace to open tab with initial query or aggregation
* pipeline
*/
const openCollectionInSameTab = async ({ ns, ...extraMetadata }) => {
const metadata = await store.fetchCollectionMetadata(ns);
appRegistry.emit('select-namespace', metadata);
});

appRegistry.on('sidebar-select-collection', async ({ ns }) => {
const metadata = await store.fetchCollectionMetadata(ns);
appRegistry.emit('select-namespace', metadata);
});
appRegistry.emit('select-namespace', {
...metadata,
...extraMetadata,
});
};

const openCollectionInNewTab = async ({ ns }) => {
appRegistry.on('collections-list-select-collection', openCollectionInSameTab);
appRegistry.on('sidebar-select-collection', openCollectionInSameTab);
appRegistry.on(
'collection-workspace-select-namespace',
openCollectionInSameTab
);
appRegistry.on('collection-tab-select-collection', openCollectionInSameTab);

/**
* Opens collection in a new tab. Additional `query` and `agrregation` props
* can be passed with the namespace to open tab with initial query or
* aggregation pipeline
*/
const openCollectionInNewTab = async ({ ns, ...extraMetadata }) => {
const metadata = await store.fetchCollectionMetadata(ns);
appRegistry.emit('open-namespace-in-new-tab', metadata);
appRegistry.emit('open-namespace-in-new-tab', {
...metadata,
...extraMetadata,
});
};

appRegistry.on('sidebar-open-collection-in-new-tab', openCollectionInNewTab);
appRegistry.on(
'import-export-open-collection-in-new-tab',
openCollectionInNewTab
);
appRegistry.on(
'collection-workspace-open-collection-in-new-tab',
openCollectionInNewTab
);
appRegistry.on('my-queries-open-saved-item', openCollectionInNewTab);

appRegistry.on('sidebar-modify-view', async ({ ns }) => {
const openModifyView = async ({ ns, sameTab }) => {
const coll = await store.fetchCollectionDetails(ns);
if (coll.sourceId && coll.pipeline) {
// `modify-view` is currently implemented in a way where we are basically
Expand All @@ -349,13 +395,21 @@ store.onActivated = (appRegistry) => {
const metadata = await store.fetchCollectionMetadata(coll.sourceId);
metadata.sourcePipeline = coll.pipeline;
metadata.editViewName = coll.ns;
appRegistry.emit('open-namespace-in-new-tab', metadata);
appRegistry.emit(
sameTab ? 'select-namespace' : 'open-namespace-in-new-tab',
metadata
);
} else {
debug(
'Tried to modify the view on a collection with required metadata missing',
coll.toJSON()
);
}
};

appRegistry.on('sidebar-modify-view', openModifyView);
appRegistry.on('collection-tab-modify-view', ({ ns }) => {
openModifyView({ ns, sameTab: true });
});

appRegistry.on('sidebar-duplicate-view', async ({ ns }) => {
Expand All @@ -379,7 +433,6 @@ store.onActivated = (appRegistry) => {
async function (namespace) {
// Force-refresh specified namespace to update collections list and get
// collection info / stats (in case of opening result collection we're

// always assuming the namespace wasn't yet updated)
await store.refreshNamespace(toNS(namespace));
// Now we can get the metadata from already fetched collection and open a
Expand Down
2 changes: 1 addition & 1 deletion packages/compass-collection/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@
"chai": "^4.3.6",
"depcheck": "^1.4.1",
"eslint": "^7.25.0",
"lodash": "^4.17.21",
"mocha": "^10.2.0",
"mongodb": "^6.0.0",
"mongodb-collection-model": "^5.11.0",
"mongodb-data-service": "^22.11.0",
"mongodb-instance-model": "^12.11.0",
"mongodb-ns": "^2.4.0",
"numeral": "^2.0.6",
"nyc": "^15.1.0",
Expand Down
Loading

0 comments on commit a31253f

Please sign in to comment.