feat: 🎸 Expose extension config to modules (#1279)
* feat: 🎸 Expose extension config to modules Currently, only the preRegistration hook receives the extension's configuration as a parameter. Providing getModuleFn's with the extension's configuration, and all lifecycle/modules with the application's configuration as rootConfig should open the doors to more configurable extensions. Closes: #1268 * CR Update: Pass extension and service config through extension manager preinit/getmodule * CR Update: Remove appConfig from servicesManager * CR Update: Remove appconfig variable
This commit is contained in:
parent
625d5d70a7
commit
4ea239a953
@ -193,7 +193,8 @@ today, create a GitHub issue!
|
|||||||
|
|
||||||
The `ExtensionManager` is a class made available to us via the `@ohif/core`
|
The `ExtensionManager` is a class made available to us via the `@ohif/core`
|
||||||
project (platform/core). Our application instantiates a single instance of it,
|
project (platform/core). Our application instantiates a single instance of it,
|
||||||
and provides a `ServicesManager` and `CommandsManager`.
|
and provides a `ServicesManager` and `CommandsManager` along with the application's
|
||||||
|
configuration through the appConfig key (optional).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const commandsManager = new CommandsManager();
|
const commandsManager = new CommandsManager();
|
||||||
@ -201,6 +202,7 @@ const servicesManager = new ServicesManager();
|
|||||||
const extensionManager = new ExtensionManager({
|
const extensionManager = new ExtensionManager({
|
||||||
commandsManager,
|
commandsManager,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
|
appConfig
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -210,8 +212,8 @@ The `ExtensionManager` only has a few public members:
|
|||||||
- `registerExtensions` - Registers an array of extensions
|
- `registerExtensions` - Registers an array of extensions
|
||||||
- `modules` - An object containing registered extensions by `MODULE_TYPE`
|
- `modules` - An object containing registered extensions by `MODULE_TYPE`
|
||||||
|
|
||||||
During registration, lifecycle hooks and modules have access to
|
During registration, lifecycle hooks and modules have access to the extension's config,
|
||||||
`ExtensionManager`'s `ServicesManager` and `CommandsManager`.
|
the application's config and `ExtensionManager`'s `ServicesManager` and `CommandsManager` instances.
|
||||||
|
|
||||||
Our `@ohif/viewer` uses the `modules` member to access registered extensions at
|
Our `@ohif/viewer` uses the `modules` member to access registered extensions at
|
||||||
appropriate places in our application.
|
appropriate places in our application.
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export default {
|
|||||||
preRegistration({
|
preRegistration({
|
||||||
servicesManager = {},
|
servicesManager = {},
|
||||||
commandsManager = {},
|
commandsManager = {},
|
||||||
|
appConfig = {},
|
||||||
configuration = {},
|
configuration = {},
|
||||||
}) {},
|
}) {},
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,14 @@ import MODULE_TYPES from './MODULE_TYPES.js';
|
|||||||
import log from './../log.js';
|
import log from './../log.js';
|
||||||
|
|
||||||
export default class ExtensionManager {
|
export default class ExtensionManager {
|
||||||
constructor({ commandsManager, servicesManager }) {
|
constructor({ commandsManager, servicesManager, appConfig = {} }) {
|
||||||
this.modules = {};
|
this.modules = {};
|
||||||
this.registeredExtensionIds = [];
|
this.registeredExtensionIds = [];
|
||||||
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
||||||
//
|
//
|
||||||
this._commandsManager = commandsManager;
|
this._commandsManager = commandsManager;
|
||||||
this._servicesManager = servicesManager;
|
this._servicesManager = servicesManager;
|
||||||
|
this._appConfig = appConfig;
|
||||||
|
|
||||||
this.moduleTypeNames.forEach(moduleType => {
|
this.moduleTypeNames.forEach(moduleType => {
|
||||||
this.modules[moduleType] = [];
|
this.modules[moduleType] = [];
|
||||||
@ -70,6 +71,7 @@ export default class ExtensionManager {
|
|||||||
extension.preRegistration({
|
extension.preRegistration({
|
||||||
servicesManager: this._servicesManager,
|
servicesManager: this._servicesManager,
|
||||||
commandsManager: this._commandsManager,
|
commandsManager: this._commandsManager,
|
||||||
|
appConfig: this._appConfig,
|
||||||
configuration,
|
configuration,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -79,7 +81,8 @@ export default class ExtensionManager {
|
|||||||
const extensionModule = this._getExtensionModule(
|
const extensionModule = this._getExtensionModule(
|
||||||
moduleType,
|
moduleType,
|
||||||
extension,
|
extension,
|
||||||
extensionId
|
extensionId,
|
||||||
|
configuration
|
||||||
);
|
);
|
||||||
|
|
||||||
if (extensionModule) {
|
if (extensionModule) {
|
||||||
@ -102,7 +105,7 @@ export default class ExtensionManager {
|
|||||||
* @param {Object} extension
|
* @param {Object} extension
|
||||||
* @param {string} extensionId - Used for logging warnings
|
* @param {string} extensionId - Used for logging warnings
|
||||||
*/
|
*/
|
||||||
_getExtensionModule(moduleType, extension, extensionId) {
|
_getExtensionModule(moduleType, extension, extensionId, configuration) {
|
||||||
const getModuleFnName = 'get' + _capitalizeFirstCharacter(moduleType);
|
const getModuleFnName = 'get' + _capitalizeFirstCharacter(moduleType);
|
||||||
const getModuleFn = extension[getModuleFnName];
|
const getModuleFn = extension[getModuleFnName];
|
||||||
|
|
||||||
@ -114,6 +117,8 @@ export default class ExtensionManager {
|
|||||||
const extensionModule = getModuleFn({
|
const extensionModule = getModuleFn({
|
||||||
servicesManager: this._servicesManager,
|
servicesManager: this._servicesManager,
|
||||||
commandsManager: this._commandsManager,
|
commandsManager: this._commandsManager,
|
||||||
|
appConfig: this._appConfig,
|
||||||
|
configuration,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!extensionModule) {
|
if (!extensionModule) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import log from './../log.js';
|
|||||||
jest.mock('./../log.js');
|
jest.mock('./../log.js');
|
||||||
|
|
||||||
describe('ExtensionManager.js', () => {
|
describe('ExtensionManager.js', () => {
|
||||||
let extensionManager, commandsManager;
|
let extensionManager, commandsManager, servicesManager, appConfig;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
commandsManager = {
|
commandsManager = {
|
||||||
@ -14,7 +14,17 @@ describe('ExtensionManager.js', () => {
|
|||||||
getContext: jest.fn(),
|
getContext: jest.fn(),
|
||||||
registerCommand: jest.fn(),
|
registerCommand: jest.fn(),
|
||||||
};
|
};
|
||||||
extensionManager = new ExtensionManager({ commandsManager });
|
servicesManager = {
|
||||||
|
registerService: jest.fn(),
|
||||||
|
};
|
||||||
|
appConfig = {
|
||||||
|
testing: true,
|
||||||
|
};
|
||||||
|
extensionManager = new ExtensionManager({
|
||||||
|
servicesManager,
|
||||||
|
commandsManager,
|
||||||
|
appConfig,
|
||||||
|
});
|
||||||
log.warn.mockClear();
|
log.warn.mockClear();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
@ -51,7 +61,7 @@ describe('ExtensionManager.js', () => {
|
|||||||
extensionManager.registerExtensions(fakeExtensions);
|
extensionManager.registerExtensions(fakeExtensions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(extensionManager.registerExtension.mock.calls[1]).toContain(
|
expect(extensionManager.registerExtension.mock.calls[1][1]).toEqual(
|
||||||
fakeConfiguration
|
fakeConfiguration
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -67,19 +77,19 @@ describe('ExtensionManager.js', () => {
|
|||||||
expect(fakeExtension.preRegistration.mock.calls.length).toBe(1);
|
expect(fakeExtension.preRegistration.mock.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls preRegistration() passing configuration along with servicesManager and commandsManager instances for extension', () => {
|
it('calls preRegistration() passing dependencies and extension configuration to extension', () => {
|
||||||
const configuration = { config: 'Some configuration' };
|
const extensionConfiguration = { config: 'Some configuration' };
|
||||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
|
||||||
|
|
||||||
// SUT
|
// SUT
|
||||||
const fakeExtension = { one: '1', preRegistration: jest.fn() };
|
const extension = { one: '1', preRegistration: jest.fn() };
|
||||||
extensionManager.registerExtension(fakeExtension, configuration);
|
extensionManager.registerExtension(extension, extensionConfiguration);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(fakeExtension.preRegistration.mock.calls[0][0]).toEqual({
|
expect(extension.preRegistration.mock.calls[0][0]).toEqual({
|
||||||
servicesManager: extensionManager._servicesManager,
|
servicesManager,
|
||||||
commandsManager: extensionManager._commandsManager,
|
commandsManager,
|
||||||
configuration,
|
appConfig,
|
||||||
|
configuration: extensionConfiguration,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -153,8 +163,8 @@ describe('ExtensionManager.js', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successfully passes a servicesManager and commandsManager instances to each module', () => {
|
it('successfully passes dependencies to each module along with extension configuration', () => {
|
||||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
const extensionConfiguration = { testing: true };
|
||||||
|
|
||||||
const extension = {
|
const extension = {
|
||||||
id: 'hello-world',
|
id: 'hello-world',
|
||||||
@ -165,11 +175,17 @@ describe('ExtensionManager.js', () => {
|
|||||||
getCommandsModule: jest.fn(),
|
getCommandsModule: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
extensionManager.registerExtension(extension);
|
extensionManager.registerExtension(extension, extensionConfiguration);
|
||||||
|
|
||||||
expect(extension.getViewportModule.mock.calls[0][0]).toEqual({
|
Object.keys(extension).forEach(module => {
|
||||||
servicesManager: extensionManager._servicesManager,
|
if (typeof extension[module] === 'function') {
|
||||||
commandsManager: extensionManager._commandsManager,
|
expect(extension[module].mock.calls[0][0]).toEqual({
|
||||||
|
servicesManager,
|
||||||
|
commandsManager,
|
||||||
|
appConfig,
|
||||||
|
configuration: extensionConfiguration,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,9 @@ export default class ServicesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (service.create) {
|
if (service.create) {
|
||||||
this.services[service.name] = service.create({ configuration });
|
this.services[service.name] = service.create({
|
||||||
|
configuration,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
log.warn(`Service create factory function not defined. Exiting early.`);
|
log.warn(`Service create factory function not defined. Exiting early.`);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -33,7 +33,7 @@ describe('ServicesManager.js', () => {
|
|||||||
[{ name: 'UIModalTestService', create: jest.fn() }, fakeConfiguration],
|
[{ name: 'UIModalTestService', create: jest.fn() }, fakeConfiguration],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(servicesManager.registerService.mock.calls[1]).toContain(
|
expect(servicesManager.registerService.mock.calls[1][1]).toEqual(
|
||||||
fakeConfiguration
|
fakeConfiguration
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -85,7 +85,7 @@ describe('ServicesManager.js', () => {
|
|||||||
expect(log.warn.mock.calls.length).toBe(1);
|
expect(log.warn.mock.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pass configuration to service create factory function', () => {
|
it('pass dependencies and configuration to service create factory function', () => {
|
||||||
const configuration = { config: 'Some configuration' };
|
const configuration = { config: 'Some configuration' };
|
||||||
|
|
||||||
servicesManager.registerService(fakeService, configuration);
|
servicesManager.registerService(fakeService, configuration);
|
||||||
|
|||||||
@ -64,10 +64,7 @@ const commandsManagerConfig = {
|
|||||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||||
const servicesManager = new ServicesManager();
|
const servicesManager = new ServicesManager();
|
||||||
const extensionManager = new ExtensionManager({
|
let extensionManager;
|
||||||
commandsManager,
|
|
||||||
servicesManager,
|
|
||||||
});
|
|
||||||
/** ~~~~~~~~~~~~~ End Application Setup */
|
/** ~~~~~~~~~~~~~ End Application Setup */
|
||||||
|
|
||||||
// TODO[react] Use a provider when the whole tree is React
|
// TODO[react] Use a provider when the whole tree is React
|
||||||
@ -128,7 +125,8 @@ class App extends Component {
|
|||||||
_initServices([UINotificationService, UIModalService, UIDialogService]);
|
_initServices([UINotificationService, UIModalService, UIDialogService]);
|
||||||
_initExtensions(
|
_initExtensions(
|
||||||
[...defaultExtensions, ...extensions],
|
[...defaultExtensions, ...extensions],
|
||||||
cornerstoneExtensionConfig
|
cornerstoneExtensionConfig,
|
||||||
|
this._appConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -241,7 +239,13 @@ function _initServices(services) {
|
|||||||
/**
|
/**
|
||||||
* @param
|
* @param
|
||||||
*/
|
*/
|
||||||
function _initExtensions(extensions, cornerstoneExtensionConfig) {
|
function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
|
||||||
|
extensionManager = new ExtensionManager({
|
||||||
|
commandsManager,
|
||||||
|
servicesManager,
|
||||||
|
appConfig,
|
||||||
|
});
|
||||||
|
|
||||||
const requiredExtensions = [
|
const requiredExtensions = [
|
||||||
GenericViewerCommands,
|
GenericViewerCommands,
|
||||||
[OHIFCornerstoneExtension, cornerstoneExtensionConfig],
|
[OHIFCornerstoneExtension, cornerstoneExtensionConfig],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user