feat(DataSource): Allow for dynamic data source configuration and creation (#3543)

This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2023-07-20 11:43:05 -04:00
1 parent 6d88978710
commit 7bbc213f58
36 files changed
+640 -298

No files matched your search

@@ -22,6 +22,7 @@ function create({
getImageIdsForDisplaySet,
getImageIdsForInstance,
getConfig,
getStudyInstanceUIDs,
}) {
const defaultQuery = {
studies: {
@@ -73,6 +74,7 @@ function create({
getImageIdsForDisplaySet,
getImageIdsForInstance,
getConfig: getConfig || defaultGetConfig,
getStudyInstanceUIDs: getStudyInstanceUIDs,
};
}
+120 -26
View File
@@ -1,8 +1,9 @@
import MODULE_TYPES from './MODULE_TYPES';
import log from '../log';
import { AppConfig } from '../types/AppConfig';
import { ServicesManager } from '../services';
import { PubSubService, ServicesManager } from '../services';
import { HotkeysManager, CommandsManager } from '../classes';
import { DataSourceDefinition } from '../types';
/**
* This is the arguments given to create the extension.
@@ -57,7 +58,11 @@ export type CommandsModule = {
defaultContext?: string;
};
export default class ExtensionManager {
export default class ExtensionManager extends PubSubService {
public static readonly EVENTS = {
ACTIVE_DATA_SOURCE_CHANGED: 'event::activedatasourcechanged',
};
private _commandsManager: CommandsManager;
private _servicesManager: ServicesManager;
private _hotkeysManager: HotkeysManager;
@@ -68,6 +73,7 @@ export default class ExtensionManager {
hotkeysManager,
appConfig = {},
}: ExtensionConstructor) {
super(ExtensionManager.EVENTS);
this.modules = {};
this.registeredExtensionIds = [];
this.moduleTypeNames = Object.values(MODULE_TYPES);
@@ -85,11 +91,20 @@ export default class ExtensionManager {
this.dataSourceMap = {};
this.dataSourceDefs = {};
this.defaultDataSourceName = appConfig.defaultDataSourceName;
this.activeDataSource = undefined;
this.activeDataSource = appConfig.defaultDataSourceName;
}
public setActiveDataSource(dataSourceName: string): void {
this.activeDataSource = dataSourceName;
public setActiveDataSource(dataSource: string): void {
if (this.activeDataSource === dataSource) {
return;
}
this.activeDataSource = dataSource;
this._broadcastEvent(
ExtensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED,
this.dataSourceDefs[this.activeDataSource]
);
}
/**
@@ -330,8 +345,20 @@ export default class ExtensionManager {
return this.dataSourceMap[this.activeDataSource];
};
getDataSource = () => {
return this.dataSourceMap[this.activeDataSource];
/**
* Gets the data source definition for the given data source name.
* If no data source name is provided, the active data source definition is
* returned.
* @param dataSourceName the data source name
* @returns the data source definition
*/
getDataSourceDef = dataSourceName => {
if (dataSourceName === undefined) {
// Default to the activeDataSource
dataSourceName = this.activeDataSource;
}
return this.dataSourceDefs[dataSourceName];
};
/**
@@ -383,10 +410,92 @@ export default class ExtensionManager {
});
};
_initDataSourcesModule(extensionModule, extensionId, dataSources = []) {
/**
* Adds the given data source and optionally sets it as the active data source.
* The method does this by first creating the data source.
* @param dataSourceDef the data source definition to be added
* @param activate flag to indicate if the added data source should be set to the active data source
*/
addDataSource(
dataSourceDef: DataSourceDefinition,
options = { activate: false }
) {
const existingDataSource = this.getDataSources(dataSourceDef.sourceName);
if (existingDataSource?.[0]) {
// The data source already exists and cannot be added.
return;
}
this._createDataSourceInstance(dataSourceDef);
if (options.activate) {
this.setActiveDataSource(dataSourceDef.sourceName);
}
}
/**
* Updates the configuration of the given data source name. It first creates a new data source with
* the existing definition and the new configuration passed in.
* @param dataSourceName the name of the data source to update
* @param dataSourceConfiguration the new configuration to update the data source with
*/
updateDataSourceConfiguration(
dataSourceName: string,
dataSourceConfiguration: any
) {
const existingDataSource = this.getDataSources(dataSourceName);
if (!existingDataSource?.[0]) {
// Cannot update a non existent data source.
return;
}
const dataSourceDef = this.dataSourceDefs[dataSourceName];
// Update the configuration.
dataSourceDef.configuration = dataSourceConfiguration;
this._createDataSourceInstance(dataSourceDef);
if (this.activeDataSource === dataSourceName) {
// When the active data source is changed/set, fire an event to indicate that its configuration has changed.
this._broadcastEvent(
ExtensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED,
dataSourceDef
);
}
}
/**
* Creates a data source instance from the given definition. The definition is
* added to dataSourceDefs and the created instance is added to dataSourceMap.
* @param dataSourceDef
* @returns
*/
_createDataSourceInstance(dataSourceDef: DataSourceDefinition) {
const module = this.getModuleEntry(dataSourceDef.namespace);
if (!module) {
return;
}
this.dataSourceDefs[dataSourceDef.sourceName] = dataSourceDef;
const { userAuthenticationService } = this._servicesManager.services;
dataSources.forEach(dataSource => {
this.dataSourceDefs[dataSource.sourceName] = dataSource;
const dataSourceInstance = module.createDataSource(
dataSourceDef.configuration,
userAuthenticationService
);
this.dataSourceMap[dataSourceDef.sourceName] = [dataSourceInstance];
}
_initDataSourcesModule(
extensionModule,
extensionId,
dataSources: Array<DataSourceDefinition> = []
): void {
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`
] = element;
});
extensionModule.forEach(element => {
@@ -394,25 +503,10 @@ export default class ExtensionManager {
dataSources.forEach(dataSource => {
if (dataSource.namespace === namespace) {
const dataSourceInstance = element.createDataSource(
dataSource.configuration,
userAuthenticationService
);
if (this.dataSourceMap[dataSource.sourceName]) {
this.dataSourceMap[dataSource.sourceName].push(dataSourceInstance);
} else {
this.dataSourceMap[dataSource.sourceName] = [dataSourceInstance];
}
this.addDataSource(dataSource);
}
});
});
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`
] = element;
});
}
/**
+7
View File
@@ -0,0 +1,7 @@
export type DataSourceDefinition = {
// TODO friendlyName to move to configuration; here now for legacy purposes
friendlyName: string;
namespace: string;
sourceName: string;
configuration: any;
};
+9 -1
View File
@@ -3,6 +3,7 @@ import * as HangingProtocol from './HangingProtocol';
import Services from './Services';
import Hotkey from '../classes/Hotkey';
import { DisplaySet } from '../services/DisplaySetService/DisplaySetService';
import { DataSourceDefinition } from './DataSource';
export * from '../services/CustomizationService/types';
// Separate out some generic types
@@ -18,4 +19,11 @@ export * from './Color';
* Export the types used within the various services and managers, but
* not the services/managers themselves, which are exported at the top level.
*/
export { Extensions, HangingProtocol, Services, Hotkey, DisplaySet };
export {
Extensions,
HangingProtocol,
Services,
Hotkey,
DisplaySet,
DataSourceDefinition,
};