Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
Expand Up @@ -8,6 +8,7 @@ import {
InteractivePopover,
} from '@mongodb-js/compass-components';
import { connect } from 'react-redux';
import { usePreference } from 'compass-preferences-model/provider';

import PipelineStages from './pipeline-stages';
import PipelineActions from './pipeline-actions';
Expand Down Expand Up @@ -112,7 +113,10 @@ export const PipelineHeader: React.FunctionComponent<PipelineHeaderProps> = ({
}) => {
// TODO: remove direct check for storage existing, breaks single source of
// truth rule and exposes services to UI, this breaks the rules for locators
const isSavingAggregationsEnabled = !!usePipelineStorage();
const pipelineStorageAvailable = !!usePipelineStorage();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I'm not mistaken we can now replace this check with the new flag. tagging @gribnoysup to confirm that the check was for compass-web and not some other case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, should replace this with a preference if we have one now

const isMyQueriesEnabled = usePreference('enableMyQueries');
const isSavingAggregationsEnabled =
pipelineStorageAvailable && isMyQueriesEnabled;
return (
<div className={containerStyles} data-testid="pipeline-header">
{isOpenPipelineVisible && isSavingAggregationsEnabled && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { Button, Icon, css, spacing } from '@mongodb-js/compass-components';
import { Button, css, Icon, spacing } from '@mongodb-js/compass-components';
import { exportToLanguage } from '../../../modules/export-to-language';
import { SaveMenu } from './pipeline-menus';
import PipelineName from './pipeline-name';
Expand All @@ -11,6 +11,7 @@ import { confirmNewPipeline } from '../../../modules/is-new-pipeline-confirm';
import { hiddenOnNarrowPipelineToolbarStyles } from '../pipeline-toolbar-container';
import ModifySourceBanner from '../../modify-source-banner';
import { usePipelineStorage } from '@mongodb-js/my-queries-storage/provider';
import { usePreference } from 'compass-preferences-model/provider';

const containerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -49,7 +50,10 @@ export const PipelineSettings: React.FunctionComponent<
}) => {
// TODO: remove direct check for storage existing, breaks single source of
// truth rule and exposes services to UI, this breaks the rules for locators
const enableSavedAggregationsQueries = !!usePipelineStorage();
const pipelineStorageAvailable = !!usePipelineStorage();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

const isMyQueriesEnabled = usePreference('enableMyQueries');
const enableSavedAggregationsQueries =
pipelineStorageAvailable && isMyQueriesEnabled;
const isPipelineNameDisplayed =
!editViewName && !!enableSavedAggregationsQueries;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const editablePreferences: (keyof UserPreferences)[] = [
'enableGenAIFeaturesAtlasOrg',
'enableGenAIFeaturesAtlasProject',
'enableDataModeling',
'enableMyQueries',
];

export class CompassWebPreferencesAccess implements PreferencesAccess {
Expand Down
13 changes: 13 additions & 0 deletions packages/compass-preferences-model/src/preferences-schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags &
enableExplainPlan: boolean;
enableAtlasSearchIndexes: boolean;
enableImportExport: boolean;
enableMyQueries: boolean;
enableAggregationBuilderRunPipeline: boolean;
enableAggregationBuilderExtraOptions: boolean;
enableGenAISampleDocumentPassing: boolean;
Expand Down Expand Up @@ -851,6 +852,18 @@ export const storedUserPreferencesProps: Required<{
type: 'boolean',
},

enableMyQueries: {
ui: true,
cli: true,
global: true,
description: {
short:
'Enable My Queries feature to save and manage favorite queries and aggregations',
},
validator: z.boolean().default(true),
type: 'boolean',
},

enableAggregationBuilderRunPipeline: {
ui: true,
cli: true,
Expand Down
10 changes: 8 additions & 2 deletions packages/compass-query-bar/src/components/query-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
createAIPlaceholderHTMLPlaceholder,
} from '@mongodb-js/compass-generative-ai';
import { connect } from '../stores/context';
import { useIsAIFeatureEnabled } from 'compass-preferences-model/provider';
import {
useIsAIFeatureEnabled,
usePreference,
} from 'compass-preferences-model/provider';
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';

import {
Expand Down Expand Up @@ -202,8 +205,11 @@ export const QueryBar: React.FunctionComponent<QueryBarProps> = ({

const favoriteQueryStorageAvailable = !!useFavoriteQueryStorageAccess();
const recentQueryStorageAvailable = !!useRecentQueryStorageAccess();
const isMyQueriesEnabled = usePreference('enableMyQueries');
const enableSavedAggregationsQueries =
favoriteQueryStorageAvailable && recentQueryStorageAvailable;
favoriteQueryStorageAvailable &&
recentQueryStorageAvailable &&
isMyQueriesEnabled;

return (
<form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ export function Navigation({
const { openMyQueriesWorkspace, openDataModelingWorkspace } =
useOpenWorkspace();
const isDataModelingEnabled = usePreference('enableDataModeling');
const isMyQueriesEnabled = usePreference('enableMyQueries');
return (
<div>
{hasWorkspacePlugin('My Queries') && (
{hasWorkspacePlugin('My Queries') && isMyQueriesEnabled && (
<NavigationItem
onClick={openMyQueriesWorkspace}
glyph="CurlyBraces"
Expand Down
5 changes: 3 additions & 2 deletions packages/compass-web/sandbox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useCallback, useLayoutEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import {
resetGlobalCSS,
css,
Body,
css,
openToast,
resetGlobalCSS,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alphabetical 🙌 big fan.

} from '@mongodb-js/compass-components';
import type { AllPreferences } from 'compass-preferences-model';
import { CompassWeb } from '../src/index';
Expand Down Expand Up @@ -137,6 +137,7 @@ const App = () => {
isAtlas && !!enableGenAIFeaturesAtlasOrg,
optInGenAIFeatures: isAtlas && !!optInGenAIFeatures,
enableDataModeling: true,
enableMyQueries: true,
}}
onTrack={sandboxTelemetry.track}
onDebug={sandboxLogger.debug}
Expand Down
148 changes: 83 additions & 65 deletions packages/compass-web/src/entrypoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import WorkspacesPlugin, {
WorkspacesProvider,
} from '@mongodb-js/compass-workspaces';
import {
DatabasesWorkspaceTab,
CollectionsWorkspaceTab,
CreateNamespacePlugin,
DatabasesWorkspaceTab,
DropNamespacePlugin,
RenameCollectionPlugin,
} from '@mongodb-js/compass-databases-collections';
import { CompassComponentsProvider, css } from '@mongodb-js/compass-components';
import {
WorkspaceTab as CollectionWorkspace,
CollectionTabsProvider,
WorkspaceTab as CollectionWorkspace,
} from '@mongodb-js/compass-collection';
import {
CompassSidebarPlugin,
AtlasClusterConnectionsOnlyProvider,
CompassSidebarPlugin,
} from '@mongodb-js/compass-sidebar';
import CompassQueryBarPlugin from '@mongodb-js/compass-query-bar';
import { CompassDocumentsPlugin } from '@mongodb-js/compass-crud';
Expand All @@ -40,13 +43,8 @@ import { CompassGlobalWritesPlugin } from '@mongodb-js/compass-global-writes';
import { CompassGenerativeAIPlugin } from '@mongodb-js/compass-generative-ai';
import ExplainPlanCollectionTabModal from '@mongodb-js/compass-explain-plan';
import ExportToLanguageCollectionTabModal from '@mongodb-js/compass-export-to-language';
import {
CreateNamespacePlugin,
DropNamespacePlugin,
RenameCollectionPlugin,
} from '@mongodb-js/compass-databases-collections';
import { PreferencesProvider } from 'compass-preferences-model/provider';
import type { AllPreferences } from 'compass-preferences-model/provider';
import { PreferencesProvider } from 'compass-preferences-model/provider';
import FieldStorePlugin from '@mongodb-js/compass-field-store';
import { AtlasServiceProvider } from '@mongodb-js/atlas-service/provider';
import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider';
Expand All @@ -55,13 +53,22 @@ import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider';
import CompassConnections from '@mongodb-js/compass-connections';
import { AtlasCloudConnectionStorageProvider } from './connection-storage';
import { AtlasCloudAuthServiceProvider } from './atlas-auth-service';
import type { LogFunction, DebugFunction } from './logger';
import type { DebugFunction, LogFunction } from './logger';
import { useCompassWebLogger } from './logger';
import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry';
import { WebWorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
import { useCompassWebPreferences } from './preferences';
import { DataModelingWorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling';
import { DataModelStorageServiceProviderInMemory } from '@mongodb-js/compass-data-modeling/web';
import { WorkspaceTab as MyQueriesWorkspace } from '@mongodb-js/compass-saved-aggregations-queries';
import {
compassFavoriteQueryStorageAccess,
compassRecentQueryStorageAccess,
CompassPipelineStorage,
FavoriteQueryStorageProvider,
RecentQueryStorageProvider,
PipelineStorageProvider,
} from '@mongodb-js/my-queries-storage';
import { CompassAssistantProvider } from '@mongodb-js/compass-assistant';
import { CompassAssistantDrawerWithConnections } from './compass-assistant-drawer';

Expand Down Expand Up @@ -179,62 +186,72 @@ function CompassWorkspace({
onActiveWorkspaceTabChange,
onOpenConnectViaModal,
}: CompassWorkspaceProps) {
// Create a simple pipeline storage instance for sandbox
const pipelineStorage = new CompassPipelineStorage();

return (
<WorkspacesProvider
value={[
WelcomeWorkspaceTab,
DatabasesWorkspaceTab,
CollectionsWorkspaceTab,
CollectionWorkspace,
DataModelingWorkspace,
]}
>
<CollectionTabsProvider
queryBar={CompassQueryBarPlugin}
tabs={[
CompassDocumentsPlugin,
CompassAggregationsPlugin,
CompassSchemaPlugin,
CompassIndexesPlugin,
CompassSchemaValidationPlugin,
CompassGlobalWritesPlugin,
]}
modals={[
ExplainPlanCollectionTabModal,
ExportToLanguageCollectionTabModal,
]}
>
<div
data-testid="compass-web-connected"
className={connectedContainerStyles}
>
<WorkspacesPlugin
initialWorkspaceTabs={initialWorkspaceTabs}
openOnEmptyWorkspace={{ type: 'Welcome' }}
onActiveWorkspaceTabChange={onActiveWorkspaceTabChange}
renderSidebar={() => {
return (
<CompassSidebarPlugin
onOpenConnectViaModal={onOpenConnectViaModal}
isCompassWeb={true}
></CompassSidebarPlugin>
);
}}
renderModals={() => {
return (
<>
<CreateViewPlugin></CreateViewPlugin>
<CreateNamespacePlugin></CreateNamespacePlugin>
<DropNamespacePlugin></DropNamespacePlugin>
<RenameCollectionPlugin></RenameCollectionPlugin>
<CompassAssistantDrawerWithConnections />
</>
);
}}
></WorkspacesPlugin>
</div>
</CollectionTabsProvider>
</WorkspacesProvider>
<PipelineStorageProvider value={pipelineStorage}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

placeholder until the implementation PR goes in (COMPASS-9565)

Copy link
Contributor Author

@nvs119 nvs119 Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a TODO or note? Also fine as is as I doubt anyone else will be touching it.

Giving this some thought, we are constructing the file storage items here, it could lead to some unintended errors or something if we change the file storage implementation. Similarly compassFavoriteQueryStorageAccess is only exported for testing. Should we leave these out until we have the web storage? Is there something we want them for now?

Slightly related, a total nit, and not something we follow in many places of Compass really:
https://react.dev/reference/react/useRef#avoiding-recreating-the-ref-contents

We could instead write the creation as:

// Create a simple pipeline storage instance for sandbox.
const pipelineStorageRef = useRef<CompassPipelineStorage | null>(null);
if (pipelineStorageRef.current === null) {
  pipelineStorageRef.current = new CompassPipelineStorage();
}

to avoid calling the constructor of CompassPipelineStorage on each render.

<FavoriteQueryStorageProvider value={compassFavoriteQueryStorageAccess}>
<RecentQueryStorageProvider value={compassRecentQueryStorageAccess}>
<WorkspacesProvider
value={[
WelcomeWorkspaceTab,
DatabasesWorkspaceTab,
CollectionsWorkspaceTab,
CollectionWorkspace,
DataModelingWorkspace,
MyQueriesWorkspace,
]}
>
<CollectionTabsProvider
queryBar={CompassQueryBarPlugin}
tabs={[
CompassDocumentsPlugin,
CompassAggregationsPlugin,
CompassSchemaPlugin,
CompassIndexesPlugin,
CompassSchemaValidationPlugin,
CompassGlobalWritesPlugin,
]}
modals={[
ExplainPlanCollectionTabModal,
ExportToLanguageCollectionTabModal,
]}
>
<div
data-testid="compass-web-connected"
className={connectedContainerStyles}
>
<WorkspacesPlugin
initialWorkspaceTabs={initialWorkspaceTabs}
openOnEmptyWorkspace={{ type: 'Welcome' }}
onActiveWorkspaceTabChange={onActiveWorkspaceTabChange}
renderSidebar={() => {
return (
<CompassSidebarPlugin
onOpenConnectViaModal={onOpenConnectViaModal}
isCompassWeb={true}
></CompassSidebarPlugin>
);
}}
renderModals={() => {
return (
<>
<CreateViewPlugin></CreateViewPlugin>
<CreateNamespacePlugin></CreateNamespacePlugin>
<DropNamespacePlugin></DropNamespacePlugin>
<RenameCollectionPlugin></RenameCollectionPlugin>
<CompassAssistantDrawerWithConnections />
</>
);
}}
></WorkspacesPlugin>
</div>
</CollectionTabsProvider>
</WorkspacesProvider>
</RecentQueryStorageProvider>
</FavoriteQueryStorageProvider>
</PipelineStorageProvider>
);
}

Expand Down Expand Up @@ -286,6 +303,7 @@ const CompassWeb = ({
onDebug,
});
const preferencesAccess = useCompassWebPreferences(initialPreferences);
// TODO (COMPASS-9565): My Queries feature flag will be used to conditionally provide storage providers
const initialWorkspaceRef = useRef(initialWorkspace);
const initialWorkspaceTabsRef = useRef(
initialWorkspaceRef.current ? [initialWorkspaceRef.current] : []
Expand Down
3 changes: 2 additions & 1 deletion packages/compass-web/src/preferences.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useRef, useEffect } from 'react';
import React, { useContext, useEffect, useRef } from 'react';
import type { AllPreferences } from 'compass-preferences-model/provider';
import { CompassWebPreferencesAccess } from 'compass-preferences-model/provider';

Expand Down Expand Up @@ -47,6 +47,7 @@ export function useCompassWebPreferences(
enableGenAISampleDocumentPassingOnAtlasProject: false,
enableGenAIFeaturesAtlasOrg: false,
enablePerformanceAdvisorBanner: true,
enableMyQueries: false,
cloudFeatureRolloutAccess: {
GEN_AI_COMPASS: false,
},
Expand Down
15 changes: 10 additions & 5 deletions packages/my-queries-storage/src/compass-query-storage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { UUID, EJSON } from 'bson';
import { type z } from '@mongodb-js/compass-user-data';
import { type IUserData, FileUserData } from '@mongodb-js/compass-user-data';
import { RecentQuerySchema, FavoriteQuerySchema } from './query-storage-schema';
import { EJSON, UUID } from 'bson';
import {
FileUserData,
type IUserData,
type z,
} from '@mongodb-js/compass-user-data';
import { FavoriteQuerySchema, RecentQuerySchema } from './query-storage-schema';
import type { FavoriteQueryStorage, RecentQueryStorage } from './query-storage';

export type QueryStorageOptions = {
Expand All @@ -10,12 +13,14 @@ export type QueryStorageOptions = {

export abstract class CompassQueryStorage<TSchema extends z.Schema> {
protected readonly userData: IUserData<TSchema>;

constructor(
schemaValidator: TSchema,
protected readonly folder: string,
protected readonly options: QueryStorageOptions
) {
// TODO: logic for whether we're in compass web or compass desktop
// Simple implementation - use FileUserData for now
// TODO(COMPASS-9565): The use-atlas-user-data branch will add proper Atlas integration
this.userData = new FileUserData(schemaValidator, folder, {
basePath: options.basepath,
serialize: (content) => EJSON.stringify(content, undefined, 2),
Expand Down
7 changes: 7 additions & 0 deletions packages/my-queries-storage/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ export {
} from './compass-query-storage';
export type { RecentQuery, FavoriteQuery } from './query-storage-schema';

// Export providers for use in applications
export {
FavoriteQueryStorageProvider,
RecentQueryStorageProvider,
PipelineStorageProvider,
} from './provider';

Copy link
Collaborator

@paula-stacho paula-stacho Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provider being exported separately is actually intentional, this is to have a clear separation between the service and the provider (we even have a rule that restricts direct use of the service).
You can import these from @mongodb-js/my-queries-storage/provider

// These are exported to aid in testing
export const compassFavoriteQueryStorageAccess: FavoriteQueryStorageAccess = {
getStorage() {
Expand Down
Loading