Skip to content

Commit

Permalink
chore(hadron-app-registry): make serviceLocator stricter to limit the…
Browse files Browse the repository at this point in the history
… potential misuse (#5535)

* chore(hadron-app-registry): make serviceLocator stricter to limit the potential misuse

* chore(app-registry): reset the inProgress flag in finally to make sure that runtime check state is correct when it's done running even if it throws
  • Loading branch information
gribnoysup authored Mar 5, 2024
1 parent 0581c27 commit 5444b83
Show file tree
Hide file tree
Showing 24 changed files with 310 additions and 129 deletions.
22 changes: 17 additions & 5 deletions package-lock.json

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

31 changes: 25 additions & 6 deletions packages/atlas-service/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,36 @@ import type { AtlasAuthService } from './atlas-auth-service';
import { AtlasService, type AtlasServiceOptions } from './atlas-service';
import { preferencesLocator } from 'compass-preferences-model/provider';
import { useLoggerAndTelemetry } from '@mongodb-js/compass-logging/provider';
import {
createServiceLocator,
createServiceProvider,
} from 'hadron-app-registry';

const AtlasAuthServiceContext = createContext<AtlasAuthService | null>(null);

export const AtlasAuthServiceProvider = AtlasAuthServiceContext.Provider;
function useAtlasAuthServiceLocator(): AtlasAuthService {

function useAtlasAuthServiceContext(): AtlasAuthService {
const service = useContext(AtlasAuthServiceContext);
if (!service) {
throw new Error('No AtlasAuthService available in this context');
}
return service;
}
export const atlasAuthServiceLocator = useAtlasAuthServiceLocator;

export const atlasAuthServiceLocator = createServiceLocator(
useAtlasAuthServiceContext,
'atlasAuthServiceLocator'
);

const AtlasServiceContext = createContext<AtlasService | null>(null);

export const AtlasServiceProvider: React.FC<{
options?: AtlasServiceOptions;
}> = ({ options, children }) => {
}> = createServiceProvider(function AtlasServiceProvider({
options,
children,
}) {
const logger = useLoggerAndTelemetry('ATLAS-SERVICE');
const preferences = preferencesLocator();
const authService = atlasAuthServiceLocator();
Expand All @@ -32,15 +46,20 @@ export const AtlasServiceProvider: React.FC<{
{children}
</AtlasServiceContext.Provider>
);
};
function useAtlasServiceLocator(): AtlasService {
});

function useAtlasServiceContext(): AtlasService {
const service = useContext(AtlasServiceContext);
if (!service) {
throw new Error('No AtlasService available in this context');
}
return service;
}
export const atlasServiceLocator = useAtlasServiceLocator;

export const atlasServiceLocator = createServiceLocator(
useAtlasServiceContext,
'atlasServiceLocator'
);

export { AtlasAuthService } from './atlas-auth-service';
export type { AtlasService } from './atlas-service';
15 changes: 9 additions & 6 deletions packages/compass-app-stores/src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { createServiceLocator } from 'hadron-app-registry';
import type { MongoDBInstance } from 'mongodb-instance-model';
import { createContext, useContext } from 'react';

export const InstanceContext = createContext<MongoDBInstance | null>(null);

export const MongoDBInstanceProvider = InstanceContext.Provider;

export const mongoDBInstanceLocator = (): MongoDBInstance => {
const instance = useContext(InstanceContext);
if (!instance) {
throw new Error('No MongoDBInstance available in this context');
export const mongoDBInstanceLocator = createServiceLocator(
function mongoDBInstanceLocator(): MongoDBInstance {
const instance = useContext(InstanceContext);
if (!instance) {
throw new Error('No MongoDBInstance available in this context');
}
return instance;
}
return instance;
};
);

export type { MongoDBInstance };
8 changes: 5 additions & 3 deletions packages/compass-connections/src/components/connections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { useLoggerAndTelemetry } from '@mongodb-js/compass-logging/provider';
import ConnectionForm from '@mongodb-js/connection-form';
import { type ConnectionInfo } from '@mongodb-js/connection-storage/renderer';
import { connectionStorageLocator } from '@mongodb-js/connection-storage/provider';
import { useConnectionStorageContext } from '@mongodb-js/connection-storage/provider';
import type AppRegistry from 'hadron-app-registry';
import type { DataService } from 'mongodb-data-service';
import { connect } from 'mongodb-data-service';
Expand Down Expand Up @@ -102,8 +102,10 @@ function Connections({
connectFn?: ConnectFn;
}): React.ReactElement {
const { log, mongoLogId } = useLoggerAndTelemetry('COMPASS-CONNECTIONS');
// @TODO: Extract to a prop COMPASS-7397
const connectionStorage = connectionStorageLocator();
// TODO(COMPASS-7397): services should not be used directly in render method,
// when this code is refactored to use the hadron plugin interface, storage
// should be handled through the plugin activation lifecycle
const connectionStorage = useConnectionStorageContext();

const {
state,
Expand Down
7 changes: 5 additions & 2 deletions packages/compass-connections/src/stores/connections-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type ConnectionRepository,
type PartialConnectionInfo,
} from '@mongodb-js/connection-storage/main';
import { connectionRepositoryLocator } from '@mongodb-js/connection-storage/provider';
import { useConnectionRepositoryContext } from '@mongodb-js/connection-storage/provider';
import { cloneDeep, merge } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import type { ConnectionAttempt } from 'mongodb-data-service';
Expand Down Expand Up @@ -285,7 +285,10 @@ export function useConnections({
removeConnection: (connectionInfo: ConnectionInfo) => void;
reloadConnections: () => void;
} {
const connectionRepository = connectionRepositoryLocator();
// TODO(COMPASS-7397): services should not be used directly in render method,
// when this code is refactored to use the hadron plugin interface, storage
// should be handled through the plugin activation lifecycle
const connectionRepository = useConnectionRepositoryContext();

const { openToast } = useToast('compass-connections');
const persistOIDCTokens = usePreference('persistOIDCTokens');
Expand Down
14 changes: 8 additions & 6 deletions packages/compass-generative-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,21 @@
"reformat": "npm run eslint . -- --fix && npm run prettier -- --write ."
},
"peerDependencies": {
"@mongodb-js/compass-components": "^1.22.1",
"compass-preferences-model": "^2.18.1",
"@mongodb-js/compass-logging": "^1.2.14",
"@mongodb-js/atlas-service": "^0.15.1",
"@mongodb-js/compass-components": "^1.22.1",
"@mongodb-js/compass-intercom": "^0.2.1",
"react": "*"
"@mongodb-js/compass-logging": "^1.2.14",
"compass-preferences-model": "^2.18.1",
"hadron-app-registry": "^9.1.8",
"react": "^17.0.2"
},
"dependencies": {
"@mongodb-js/atlas-service": "^0.15.1",
"@mongodb-js/compass-components": "^1.22.1",
"@mongodb-js/compass-intercom": "^0.2.1",
"@mongodb-js/compass-logging": "^1.2.14",
"@mongodb-js/atlas-service": "^0.15.1",
"compass-preferences-model": "^2.18.1",
"@mongodb-js/compass-intercom": "^0.2.1"
"hadron-app-registry": "^9.1.8"
},
"devDependencies": {
"@mongodb-js/eslint-config-compass": "^1.0.17",
Expand Down
51 changes: 30 additions & 21 deletions packages/compass-generative-ai/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,47 @@ import {
atlasAuthServiceLocator,
atlasServiceLocator,
} from '@mongodb-js/atlas-service/provider';
import {
createServiceLocator,
createServiceProvider,
} from 'hadron-app-registry';

const AtlasAiServiceContext = createContext<AtlasAiService | null>(null);

export const AtlasAiServiceProvider: React.FC = ({ children }) => {
const logger = useLoggerAndTelemetry('ATLAS-AI-SERVICE');
const preferences = preferencesLocator();
const atlasAuthService = atlasAuthServiceLocator();
const atlasService = atlasServiceLocator();
export const AtlasAiServiceProvider: React.FC = createServiceProvider(
function AtlasAiServiceProvider({ children }) {
const logger = useLoggerAndTelemetry('ATLAS-AI-SERVICE');
const preferences = preferencesLocator();
const atlasAuthService = atlasAuthServiceLocator();
const atlasService = atlasServiceLocator();

const aiService = useMemo(() => {
return new AtlasAiService(
atlasService,
atlasAuthService,
preferences,
logger
);
}, [atlasAuthService, preferences, logger, atlasService]);
const aiService = useMemo(() => {
return new AtlasAiService(
atlasService,
atlasAuthService,
preferences,
logger
);
}, [atlasAuthService, preferences, logger, atlasService]);

return (
<AtlasAiServiceContext.Provider value={aiService}>
{children}
</AtlasAiServiceContext.Provider>
);
};
return (
<AtlasAiServiceContext.Provider value={aiService}>
{children}
</AtlasAiServiceContext.Provider>
);
}
);

function useAtlasServiceLocator(): AtlasAiService {
function useAtlasAiServiceContext(): AtlasAiService {
const service = useContext(AtlasAiServiceContext);
if (!service) {
throw new Error('No AtlasAiService available in this context');
}
return service;
}

export const atlasAiServiceLocator = useAtlasServiceLocator;
export const atlasAiServiceLocator = createServiceLocator(
useAtlasAiServiceContext,
'atlasAiServiceLocator'
);
export { AtlasAiService } from './atlas-ai-service';
Loading

0 comments on commit 5444b83

Please sign in to comment.