ohif-viewer/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts

237 lines
7.4 KiB
TypeScript

import { ExtensionManager, Types } from '@ohif/core';
/**
* This file contains the implementations of BaseDataSourceConfigurationAPIItem
* and BaseDataSourceConfigurationAPI for the Google cloud healthcare API. To
* better understand this implementation and/or to implement custom implementations,
* see the platform\core\src\types\DataSourceConfigurationAPI.ts and its JS doc
* comments as a guide.
*/
/**
* The various Google Cloud Healthcare path item types.
*/
enum ItemType {
projects = 0,
locations = 1,
datasets = 2,
dicomStores = 3,
}
interface NamedItem {
name: string;
}
interface Project extends NamedItem {
projectId: string;
}
const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
class GoogleCloudDataSourceConfigurationAPIItem
implements Types.BaseDataSourceConfigurationAPIItem
{
id: string;
name: string;
url: string;
itemType: ItemType;
}
class GoogleCloudDataSourceConfigurationAPI implements Types.BaseDataSourceConfigurationAPI {
private _extensionManager: ExtensionManager;
private _fetchOptions: { method: string; headers: unknown };
private _dataSourceName: string;
constructor(dataSourceName, servicesManager: AppTypes.ServicesManager, extensionManager) {
this._dataSourceName = dataSourceName;
this._extensionManager = extensionManager;
const userAuthenticationService = servicesManager.services.userAuthenticationService;
this._fetchOptions = {
method: 'GET',
headers: userAuthenticationService.getAuthorizationHeader(),
};
}
getItemLabels = () => ['Project', 'Location', 'Data set', 'DICOM store'];
async initialize(): Promise<Types.BaseDataSourceConfigurationAPIItem[]> {
const url = `${initialUrl}/projects`;
const projects = (await GoogleCloudDataSourceConfigurationAPI._doFetch(
url,
ItemType.projects,
this._fetchOptions
)) as Array<Project>;
if (!projects?.length) {
return [];
}
const projectItems = projects.map(project => {
return {
id: project.projectId,
name: project.name,
itemType: ItemType.projects,
url: `${baseHealthcareUrl}/projects/${project.projectId}`,
};
});
return projectItems;
}
async setCurrentItem(
anItem: Types.BaseDataSourceConfigurationAPIItem
): Promise<Types.BaseDataSourceConfigurationAPIItem[]> {
const googleCloudItem = anItem as GoogleCloudDataSourceConfigurationAPIItem;
if (googleCloudItem.itemType === ItemType.dicomStores) {
// Last configurable item, so update the data source configuration.
const url = `${googleCloudItem.url}/dicomWeb`;
const dataSourceDefCopy = JSON.parse(
JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName))
);
dataSourceDefCopy.configuration = {
...dataSourceDefCopy.configuration,
wadoUriRoot: url,
qidoRoot: url,
wadoRoot: url,
};
this._extensionManager.updateDataSourceConfiguration(
dataSourceDefCopy.sourceName,
dataSourceDefCopy.configuration
);
return [];
}
const subItemType = googleCloudItem.itemType + 1;
const subItemField = `${ItemType[subItemType]}`;
const url = `${googleCloudItem.url}/${subItemField}`;
const fetchedSubItems = await GoogleCloudDataSourceConfigurationAPI._doFetch(
url,
subItemType,
this._fetchOptions
);
if (!fetchedSubItems?.length) {
return [];
}
const subItems = fetchedSubItems.map(subItem => {
const nameSplit = subItem.name.split('/');
return {
id: subItem.name,
name: nameSplit[nameSplit.length - 1],
itemType: subItemType,
url: `${baseHealthcareUrl}/${subItem.name}`,
};
});
return subItems;
}
async getConfiguredItems(): Promise<Array<GoogleCloudDataSourceConfigurationAPIItem>> {
const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(
this._dataSourceName
);
const url = dataSourceDefinition.configuration.wadoUriRoot;
const projectsIndex = url.indexOf('projects');
// Split the configured URL into (essentially) pairs (i.e. item type followed by item)
// Explicitly: ['projects','aProject','locations','aLocation','datasets','aDataSet','dicomStores','aDicomStore']
// Note that a partial configuration will have a subset of the above.
const urlSplit = url.substring(projectsIndex).split('/');
const configuredItems = [];
for (
let itemType = 0;
// the number of configured items is either the max (4) or the number extracted from the url split
itemType < 4 && (itemType + 1) * 2 < urlSplit.length;
itemType += 1
) {
if (itemType === ItemType.projects) {
const projectId = urlSplit[1];
const projectUrl = `${initialUrl}/projects/${projectId}`;
const data = await GoogleCloudDataSourceConfigurationAPI._doFetch(
projectUrl,
ItemType.projects,
this._fetchOptions
);
const project = data[0] as Project;
configuredItems.push({
id: project.projectId,
name: project.name,
itemType: itemType,
url: `${baseHealthcareUrl}/projects/${project.projectId}`,
});
} else {
const relativePath = urlSplit.slice(0, itemType * 2 + 2).join('/');
configuredItems.push({
id: relativePath,
name: urlSplit[itemType * 2 + 1],
itemType: itemType,
url: `${baseHealthcareUrl}/${relativePath}`,
});
}
}
return configuredItems;
}
/**
* Fetches an array of items the specified item type.
* @param urlStr the fetch url
* @param fetchItemType the type to fetch
* @param fetchOptions the header options for the fetch (e.g. authorization header)
* @param fetchSearchParams any search query params; currently only used for paging results
* @returns an array of items of the specified type
*/
private static async _doFetch(
urlStr: string,
fetchItemType: ItemType,
fetchOptions = {},
fetchSearchParams: Record<string, string> = {}
): Promise<Array<Project> | Array<NamedItem>> {
try {
const url = new URL(urlStr);
url.search = new URLSearchParams(fetchSearchParams).toString();
const response = await fetch(url, fetchOptions);
const data = await response.json();
if (response.status >= 200 && response.status < 300 && data != null) {
if (data.nextPageToken != null) {
fetchSearchParams.pageToken = data.nextPageToken;
const subPageData = await this._doFetch(
urlStr,
fetchItemType,
fetchOptions,
fetchSearchParams
);
data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
}
if (data[ItemType[fetchItemType]]) {
return data[ItemType[fetchItemType]];
} else if (data.name) {
return [data];
} else {
return [];
}
} else {
const message =
data?.error?.message ||
`Error returned from Google Cloud Healthcare: ${response.status} - ${response.statusText}`;
throw new Error(message);
}
} catch (err) {
const message = err?.message || 'Error occurred during fetch request.';
throw new Error(message);
}
}
}
export { GoogleCloudDataSourceConfigurationAPI };