Skip to content

feat: Using the AtlasUserData class to demo save user data project COMPASS-9565 #7152

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

Draft
wants to merge 3 commits into
base: extend-user-data
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions packages/atlas-service/src/atlas-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export class AtlasService {
// https://github.com/10gen/mms/blob/9f858bb987aac6aa80acfb86492dd74c89cbb862/client/packages/project/common/ajaxPrefilter.ts#L34-L49
return this.cloudEndpoint(path);
}
tempEndpoint(path?: string): string {
return `${normalizePath(path)}`;
}
driverProxyEndpoint(path?: string): string {
return `${this.config.ccsBaseUrl}${normalizePath(path)}`;
}
Expand Down Expand Up @@ -122,6 +125,7 @@ export class AtlasService {
url: RequestInfo | URL,
init?: RequestInit
): Promise<Response> {
debugger;
const authHeaders = await this.authService.getAuthHeaders();
return this.fetch(url, {
...init,
Expand Down
2 changes: 1 addition & 1 deletion packages/atlas-service/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const AtlasServiceProvider: React.FC<{
);
});

function useAtlasServiceContext(): AtlasService {
export function useAtlasServiceContext(): AtlasService {
const service = useContext(AtlasServiceContext);
if (!service) {
throw new Error('No AtlasService available in this context');
Expand Down
4 changes: 4 additions & 0 deletions packages/compass-aggregations/src/modules/saved-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const savedPipelineAdd = (

export const getSavedPipelines =
(): PipelineBuilderThunkAction<void> => (dispatch, getState) => {
console.log('Get saved pipelines');
// debugger;
if (!getState().savedPipeline.isLoaded) {
dispatch(updatePipelineList());
}
Expand All @@ -93,6 +95,8 @@ export const updatePipelineList =
{ pipelineStorage, logger: { debug }, globalAppRegistry }
) => {
const state = getState();
console.log('Update pipeline list');
// debugger;
pipelineStorage
?.loadAll()
.then((pipelines: SavedPipeline[]) => {
Expand Down
43 changes: 24 additions & 19 deletions packages/compass-user-data/src/user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { log, mongoLogId } = createLogger('COMPASS-USER-STORAGE');

type SerializeContent<I> = (content: I) => string;
type DeserializeContent = (content: string) => unknown;
type GetResourceUrl = (path?: string) => Promise<string>;
type GetResourceUrl = (path?: string) => string;
type AuthenticatedFetch = (
url: RequestInfo | URL,
options?: RequestInit
Expand Down Expand Up @@ -61,6 +61,10 @@ export abstract class IUserData<T extends z.Schema> {
abstract write(id: string, content: z.input<T>): Promise<boolean>;
abstract delete(id: string): Promise<boolean>;
abstract readAll(options?: ReadOptions): Promise<ReadAllResult<T>>;
abstract readOne(
id: string,
options?: ReadOptions
): Promise<z.output<T> | undefined>;
Copy link
Contributor

Choose a reason for hiding this comment

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

When does this return undefined? I kinda would've expected this to return a version of ReadAllResult that either has a single item in the arrays or the properties are one item instead of an array

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Identified that readOne in AtlasUserData should not be returning null when an error is found. readOne in FileUserData originally had a return type of Promise<z.output<T> | undefined>, so the abstract method should also have type undefined.

abstract updateAttributes(
id: string,
data: Partial<z.input<T>>
Expand Down Expand Up @@ -292,8 +296,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
this.validator.parse(content);

const response = await this.authenticatedFetch(
await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}`
this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}`
),
{
method: 'POST',
Expand Down Expand Up @@ -322,8 +326,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
'Atlas Backend',
'Error writing data',
{
url: await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}`
url: this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}`
),
error: (error as Error).message,
}
Expand All @@ -335,8 +339,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
async delete(id: string): Promise<boolean> {
try {
const response = await this.authenticatedFetch(
await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
{
method: 'DELETE',
Expand All @@ -354,8 +358,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
'Atlas Backend',
'Error deleting data',
{
url: await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
url: this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
error: (error as Error).message,
}
Expand All @@ -369,10 +373,11 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
data: [],
errors: [],
};
// debugger;
try {
const response = await this.authenticatedFetch(
await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}`
this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}`
),
{
method: 'GET',
Expand Down Expand Up @@ -411,8 +416,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
};

const response = await this.authenticatedFetch(
await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
{
method: 'PUT',
Expand All @@ -434,8 +439,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
'Atlas Backend',
'Error updating data',
{
url: await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
url: this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
error: (error as Error).message,
}
Expand All @@ -448,8 +453,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
async readOne(id: string): Promise<z.output<T>> {
try {
const getResponse = await this.authenticatedFetch(
await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
{
method: 'GET',
Expand All @@ -469,8 +474,8 @@ export class AtlasUserData<T extends z.Schema> extends IUserData<T> {
'Atlas Backend',
'Error reading data',
{
url: await this.getResourceUrl(
`${this.dataType}/${this.orgId}/${this.projectId}/${id}`
url: this.getResourceUrl(
`userData/${this.dataType}/${this.orgId}/${this.projectId}/${id}`
),
error: (error as Error).message,
}
Expand Down
182 changes: 126 additions & 56 deletions packages/compass-web/src/entrypoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ import {
import { PreferencesProvider } from 'compass-preferences-model/provider';
import type { AllPreferences } from 'compass-preferences-model/provider';
import FieldStorePlugin from '@mongodb-js/compass-field-store';
import { AtlasServiceProvider } from '@mongodb-js/atlas-service/provider';
import {
AtlasServiceProvider,
useAtlasServiceContext,
} from '@mongodb-js/atlas-service/provider';
import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider';
import { LoggerProvider } from '@mongodb-js/compass-logging/provider';
import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider';
Expand All @@ -55,9 +58,22 @@ import type { LogFunction, DebugFunction } from './logger';
import { useCompassWebLogger } from './logger';
import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry';
import { WebWorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
import { WorkspaceTab as MyQueriesWorkspace } from '@mongodb-js/compass-saved-aggregations-queries';
import { useCompassWebPreferences } from './preferences';
import { DataModelingWorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling';
import { DataModelStorageServiceProviderInMemory } from '@mongodb-js/compass-data-modeling/web';
import {
CompassFavoriteQueryStorage,
CompassPipelineStorage,
CompassRecentQueryStorage,
} from '@mongodb-js/my-queries-storage';
import {
PipelineStorageProvider,
FavoriteQueryStorageProvider,
RecentQueryStorageProvider,
type FavoriteQueryStorageAccess,
type RecentQueryStorageAccess,
} from '@mongodb-js/my-queries-storage/provider';

export type TrackFunction = (
event: string,
Expand All @@ -78,6 +94,57 @@ const WithAtlasProviders: React.FC = ({ children }) => {
);
};

const WithStorageProviders: React.FC<{ orgId: string; projectId: string }> = ({
children,
orgId,
projectId,
}) => {
const atlasService = useAtlasServiceContext();
console.log('atlasService', atlasService);
const authenticatedFetch = atlasService.authenticatedFetch.bind(atlasService);
// TODO: use non-hardcoded endpoint
const getResourceUrl = atlasService.tempEndpoint.bind(atlasService);
const pipelineStorage = useRef(
new CompassPipelineStorage({
orgId,
projectId,
getResourceUrl,
authenticatedFetch,
})
);
const favoriteQueryStorage = useRef<FavoriteQueryStorageAccess>({
getStorage(options) {
return new CompassFavoriteQueryStorage({
...options,
orgId,
projectId,
getResourceUrl,
authenticatedFetch,
});
},
});
const recentQueryStorage = useRef<RecentQueryStorageAccess>({
getStorage(options) {
return new CompassRecentQueryStorage({
...options,
orgId,
projectId,
getResourceUrl,
authenticatedFetch,
});
},
});
return (
<PipelineStorageProvider value={pipelineStorage.current}>
<FavoriteQueryStorageProvider value={favoriteQueryStorage.current}>
<RecentQueryStorageProvider value={recentQueryStorage.current}>
{children}
</RecentQueryStorageProvider>
</FavoriteQueryStorageProvider>
</PipelineStorageProvider>
);
};

type CompassWorkspaceProps = Pick<
React.ComponentProps<typeof WorkspacesPlugin>,
'initialWorkspaceTabs' | 'onActiveWorkspaceTabChange'
Expand Down Expand Up @@ -178,6 +245,7 @@ function CompassWorkspace({
CollectionsWorkspaceTab,
CollectionWorkspace,
DataModelingWorkspace,
MyQueriesWorkspace,
]}
>
<CollectionTabsProvider
Expand Down Expand Up @@ -358,63 +426,65 @@ const CompassWeb = ({
<LoggerProvider value={logger}>
<TelemetryProvider options={telemetryOptions.current}>
<WithAtlasProviders>
<DataModelStorageServiceProviderInMemory>
<AtlasCloudConnectionStorageProvider
orgId={orgId}
projectId={projectId}
>
<CompassConnections
appName={appName ?? 'Compass Web'}
onFailToLoadConnections={onFailToLoadConnections}
onExtraConnectionDataRequest={() => {
return Promise.resolve([{}, null] as [
Record<string, unknown>,
null
]);
}}
onAutoconnectInfoRequest={(connectionStore) => {
if (autoconnectId) {
return connectionStore.loadAll().then(
(connections) => {
return connections.find(
(connectionInfo) =>
connectionInfo.id === autoconnectId
);
},
(err) => {
const { log, mongoLogId } = logger;
log.warn(
mongoLogId(1_001_000_329),
'Compass Web',
'Could not load connections when trying to autoconnect',
{ err: err.message }
);
return undefined;
}
);
}
return Promise.resolve(undefined);
}}
<WithStorageProviders orgId={orgId} projectId={projectId}>
<DataModelStorageServiceProviderInMemory>
<AtlasCloudConnectionStorageProvider
orgId={orgId}
projectId={projectId}
>
<CompassInstanceStorePlugin>
<FieldStorePlugin>
<WithConnectionsStore>
<CompassWorkspace
initialWorkspaceTabs={
initialWorkspaceTabsRef.current
}
onActiveWorkspaceTabChange={
onActiveWorkspaceTabChange
<CompassConnections
appName={appName ?? 'Compass Web'}
onFailToLoadConnections={onFailToLoadConnections}
onExtraConnectionDataRequest={() => {
return Promise.resolve([{}, null] as [
Record<string, unknown>,
null
]);
}}
onAutoconnectInfoRequest={(connectionStore) => {
if (autoconnectId) {
return connectionStore.loadAll().then(
(connections) => {
return connections.find(
(connectionInfo) =>
connectionInfo.id === autoconnectId
);
},
(err) => {
const { log, mongoLogId } = logger;
log.warn(
mongoLogId(1_001_000_329),
'Compass Web',
'Could not load connections when trying to autoconnect',
{ err: err.message }
);
return undefined;
}
onOpenConnectViaModal={onOpenConnectViaModal}
></CompassWorkspace>
</WithConnectionsStore>
</FieldStorePlugin>
<CompassGenerativeAIPlugin projectId={projectId} />
</CompassInstanceStorePlugin>
</CompassConnections>
</AtlasCloudConnectionStorageProvider>
</DataModelStorageServiceProviderInMemory>
);
}
return Promise.resolve(undefined);
}}
>
<CompassInstanceStorePlugin>
<FieldStorePlugin>
<WithConnectionsStore>
<CompassWorkspace
initialWorkspaceTabs={
initialWorkspaceTabsRef.current
}
onActiveWorkspaceTabChange={
onActiveWorkspaceTabChange
}
onOpenConnectViaModal={onOpenConnectViaModal}
></CompassWorkspace>
</WithConnectionsStore>
</FieldStorePlugin>
<CompassGenerativeAIPlugin projectId={projectId} />
</CompassInstanceStorePlugin>
</CompassConnections>
</AtlasCloudConnectionStorageProvider>
</DataModelStorageServiceProviderInMemory>
</WithStorageProviders>
</WithAtlasProviders>
</TelemetryProvider>
</LoggerProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('CompassPipelineStorage', function () {

beforeEach(async function () {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'saved-pipelines-tests'));
pipelineStorage = new CompassPipelineStorage(tmpDir);
pipelineStorage = new CompassPipelineStorage({ basePath: tmpDir });
});

afterEach(async function () {
Expand Down
Loading
Loading