diff --git a/healthcare/dicom/README.md b/healthcare/dicom/README.md new file mode 100644 index 0000000000..cadd6f61eb --- /dev/null +++ b/healthcare/dicom/README.md @@ -0,0 +1,68 @@ +Google Cloud Platform logo + +# Cloud Healthcare API Node.js DICOM store and DICOMweb example + +This sample app demonstrates DICOM store management and the DICOMweb implementation for the Cloud Healthcare API. + +# Setup + +Run the following command to install the library dependencies for Node.js: + + npm install + +# Running the samples + +## DICOM stores + + dicom_stores.js + + Commands: + dicom_stores.js createDicomStore Creates a new DICOM store within the parent dataset. + dicom_stores.js deleteDicomStore Deletes the DICOM store and removes all resources that + are contained within it. + dicom_stores.js getDicomStore Gets the specified DICOM store or returns NOT_FOUND if + it doesn't exist. + dicom_stores.js listDicomStores Lists the DICOM stores in the given dataset. + dicom_stores.js patchDicomStore Updates the DICOM store. + + dicom_stores.js importDicomObject Imports data into the DICOM store by copying it from the + specified source. + dicom_stores.js exportDicomInstanceGcs Exports data to a Cloud Storage bucket by copying it + from the DICOM store. + + Options: + --version Show version number [boolean] + --apiKey, -a The API key used for discovering the API. + [string] + --cloudRegion, -c [string] [default: "us-central1"] + --projectId, -p The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT + environment variables. [string] + --serviceAccount, -s The path to your service credentials JSON. + [string] + --help Show help [boolean] + + +## DICOMweb + + dicomweb.js + + Commands: + dicomweb.js dicomWebStoreInstance Handles the POST requests specified in the DICOMweb + standard. + dicomweb.js dicomWebSearchInstances Handles the GET requests specified in the DICOMweb + standard. + dicomweb.js dicomWebRetrieveStudy Handles the GET requests specified in the DICOMweb + standard. + dicomweb.js dicomWebDeleteStudy Handles DELETE requests. + + + Options: + --version Show version number [boolean] + --cloudRegion, -c [string] [default: "us-central1"] + --projectId, -p The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT + environment variables. [string] + --serviceAccount, -s The path to your service credentials JSON. + [string] + --help Show help [boolean] + +For more information, see https://cloud.google.com/healthcare/docs diff --git a/healthcare/dicom/dicom_stores.js b/healthcare/dicom/dicom_stores.js new file mode 100644 index 0000000000..de6daba881 --- /dev/null +++ b/healthcare/dicom/dicom_stores.js @@ -0,0 +1,341 @@ +/** + * Copyright 2018, Google, LLC + * Licensed under the Apache License, Version 2.0 (the `License`); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const {google} = require('googleapis'); + +// [START healthcare_create_dicom_store] +function createDicomStore (client, projectId, cloudRegion, datasetId, dicomStoreId) { + // Client retrieved in callback + // getClient(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + const parentName = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}`; + + const request = {parent: parentName, dicomStoreId: dicomStoreId}; + + client.projects.locations.datasets.dicomStores.create(request) + .then(() => { + console.log(`Created DICOM store: ${dicomStoreId}`); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_create_dicom_store] + +// [START healthcare_delete_dicom_store] +function deleteDicomStore (client, projectId, cloudRegion, datasetId, dicomStoreId) { + // Client retrieved in callback + // getClient(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + const dicomStoreName = + `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`; + + const request = {name: dicomStoreName}; + + client.projects.locations.datasets.dicomStores.delete(request) + .then(() => { + console.log(`Deleted DICOM store: ${dicomStoreId}`); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_delete_dicom_store] + +// [START healthcare_get_dicom_store] +function getDicomStore (client, projectId, cloudRegion, datasetId, dicomStoreId) { + // Client retrieved in callback + // getClient(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + const dicomStoreName = + `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`; + + const request = {name: dicomStoreName}; + + client.projects.locations.datasets.dicomStores.get(request) + .then(results => { + console.log('Got DICOM store:\n', results['data']); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_get_dicom_store] + +// [START healthcare_list_dicom_stores] +function listDicomStores (client, projectId, cloudRegion, datasetId) { + // Client retrieved in callback + // getClient(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + const parentName = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}`; + + const request = {parent: parentName}; + + client.projects.locations.datasets.dicomStores.list(request) + .then(results => { + console.log('DICOM stores:\n', results['data']['dicomStores']); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_list_dicom_stores] + +// [START healthcare_patch_dicom_store] +function patchDicomStore (client, projectId, cloudRegion, datasetId, dicomStoreId, pubsubTopic) { + // Client retrieved in callback + // getClient(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const pubsubTopic = 'my-topic' + const dicomStoreName = + `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`; + + const request = { + name: dicomStoreName, + updateMask: 'notificationConfig', + resource: { 'notificationConfig': { pubsubTopic: `projects/${projectId}/locations/${cloudRegion}/topics/${pubsubTopic}` } } + }; + + client.projects.locations.datasets.dicomStores.patch(request) + .then(results => { + console.log( + 'Patched DICOM store with Cloud Pub/Sub topic', results['data']['notificationConfig']['pubsubTopic']); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_patch_dicom_store] + +// [START healthcare_import_dicom_object] +function importDicomObject (client, projectId, cloudRegion, datasetId, dicomStoreId, contentUri) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const contentUri = 'my-bucket' + const dicomStoreName = + `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`; + + const request = { + name: dicomStoreName, + resource: { + inputConfig: { + gcsSource: { + contentUri: `gs://${contentUri}` + } + } + }}; + + client.projects.locations.datasets.dicomStores.import(request) + .then(results => { + console.log(`Imported DICOM objects from bucket ${contentUri}`); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_import_dicom_object] + +// [START healthcare_export_dicom_instance_gcs] +function exportDicomInstanceGcs (client, projectId, cloudRegion, datasetId, dicomStoreId, uriPrefix) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const uriPrefix = 'my-bucket' + const dicomStoreName = + `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`; + + const request = { + name: dicomStoreName, + resource: { + outputConfig: { + gcsDestination: { + uriPrefix: `gs://${uriPrefix}` + } + } + }}; + + client.projects.locations.datasets.dicomStores.export(request) + .then(results => { + console.log(`Exported DICOM instances to bucket ${uriPrefix}`); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_export_dicom_instance_gcs] + +// Returns an authorized API client by discovering the Healthcare API with +// the provided API key. +// [START healthcare_get_client] +function getClient (apiKey, serviceAccountJson, cb) { + const API_VERSION = 'v1alpha'; + const DISCOVERY_API = 'https://healthcare.googleapis.com/$discovery/rest'; + + google.auth + .getClient({scopes: ['https://www.googleapis.com/auth/cloud-platform']}) + .then(authClient => { + const discoveryUrl = `${DISCOVERY_API}?labels=CHC_ALPHA&version=${ + API_VERSION}&key=${apiKey}`; + + google.options({auth: authClient}); + + google.discoverAPI(discoveryUrl) + .then((client) => { + cb(client); + }) + .catch((err) => { + console.error(err); + }); + }); +} +// [END healthcare_get_client] + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + apiKey: { + alias: 'a', + default: process.env.API_KEY, + description: 'The API key used for discovering the API.', + requiresArg: true, + type: 'string' + }, + cloudRegion: { + alias: 'c', + default: 'us-central1', + requiresArg: true, + type: 'string' + }, + projectId: { + alias: 'p', + default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT, + description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.', + requiresArg: true, + type: 'string' + }, + serviceAccount: { + alias: 's', + default: process.env.GOOGLE_APPLICATION_CREDENTIALS, + description: 'The path to your service credentials JSON.', + requiresArg: true, + type: 'string' + } + }) + .command( + `createDicomStore `, + `Creates a new DICOM store within the parent dataset.`, + {}, + (opts) => { + const cb = function (client) { + createDicomStore(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `deleteDicomStore `, + `Deletes the DICOM store and removes all resources that are contained within it.`, + {}, + (opts) => { + const cb = function (client) { + deleteDicomStore(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `getDicomStore `, + `Gets the specified DICOM store or returns NOT_FOUND if it doesn't exist.`, + {}, + (opts) => { + const cb = function (client) { + getDicomStore(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `listDicomStores `, + `Lists the DICOM stores in the given dataset.`, + {}, + (opts) => { + const cb = function (client) { + listDicomStores(client, opts.projectId, opts.cloudRegion, opts.datasetId); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `patchDicomStore `, + `Updates the DICOM store.`, + {}, + (opts) => { + const cb = function (client) { + patchDicomStore(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.pubsubTopic); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `importDicomObject `, + `Imports data into the DICOM store by copying it from the specified source.`, + {}, + (opts) => { + const cb = function (client) { + importDicomObject(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.contentUri); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `exportDicomInstanceGcs `, + `Exports data to a Cloud Storage bucket by copying it from the DICOM store.`, + {}, + (opts) => { + const cb = function (client) { + exportDicomInstanceGcs(client, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.uriPrefix); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .wrap(120) + .recommendCommands() + .epilogue(`For more information, see https://cloud.google.com/healthcare/docs`) + .help() + .strict() + .argv; diff --git a/healthcare/dicom/dicomweb.js b/healthcare/dicom/dicomweb.js new file mode 100644 index 0000000000..93cd4f471e --- /dev/null +++ b/healthcare/dicom/dicomweb.js @@ -0,0 +1,243 @@ +/** + * Copyright 2018, Google, LLC + * Licensed under the Apache License, Version 2.0 (the `License`); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { GoogleToken } = require('gtoken'); +const request = require('request-promise'); +const fs = require('fs'); + +const BASE_URL = 'https://healthcare.googleapis.com/v1alpha'; + +function getToken (serviceAccountJson, cb) { + const gtoken = new GoogleToken({ + keyFile: `${serviceAccountJson}`, + scope: ['https://www.googleapis.com/auth/cloud-platform'] // or space-delimited string of scopes + }); + + gtoken.getToken(function (err, token) { + if (err) { + console.log('ERROR: ', err); + return; + } + cb(token); + }); +} + +// [START healthcare_dicomweb_store_instance] +function dicomWebStoreInstance (token, projectId, cloudRegion, datasetId, dicomStoreId, dcmFile, boundary) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const dcmFile = 'IMG.dcm' + // const boundary = 'DICOMwebBoundary' + const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`; + + const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies`; + + const binaryData = fs.readFileSync(dcmFile); + + const options = { + url: dicomWebPath, + headers: { + 'authorization': `Bearer ${token}`, + 'Content-Type': `multipart/related; type=application/dicom; boundary=${boundary}` + }, + body: binaryData, + method: 'POST' + }; + + request(options) + .then(results => { + console.log('Stored instance:\n'); + console.log(results); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_dicomweb_store_instance] + +// [START healthcare_dicomweb_search_instances] +function dicomWebSearchInstances (token, projectId, cloudRegion, datasetId, dicomStoreId) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`; + + const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/instances`; + + const options = { + url: dicomWebPath, + headers: { + 'authorization': `Bearer ${token}`, + 'Content-Type': 'application/dicom+json; charset=utf-8' + }, + method: 'GET' + }; + + request(options) + .then(results => { + console.log('Instances:\n'); + console.log(results); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_dicomweb_search_instances] + +// [START healthcare_dicomweb_retrieve_study] +function dicomWebRetrieveStudy (token, projectId, cloudRegion, datasetId, dicomStoreId, studyUid) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const studyUid = '1.2.345.678901.2.345.6789.0123456.7890.1234567890.123' + const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`; + + const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies/${studyUid}`; + + const options = { + url: dicomWebPath, + headers: { + 'authorization': `Bearer ${token}`, + 'Content-Type': 'application/dicom+json; charset=utf-8' + }, + method: 'GET' + }; + + request(options) + .then(results => { + console.log(`Retrieved study with UID: ${studyUid}`); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_dicomweb_retrieve_study] + +// [START healthcare_dicomweb_delete_study] +function dicomWebDeleteStudy (token, projectId, cloudRegion, datasetId, dicomStoreId, studyUid) { + // Token retrieved in callback + // getToken(serviceAccountJson, function(cb) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const datasetId = 'my-dataset'; + // const dicomStoreId = 'my-dicom-store'; + // const studyUid = '1.2.345.678901.2.345.6789.0123456.7890.1234567890.123' + const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`; + + const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies/${studyUid}`; + + const options = { + url: dicomWebPath, + headers: { + 'authorization': `Bearer ${token}`, + 'Content-Type': 'application/dicom+json; charset=utf-8' + }, + method: 'DELETE' + }; + + request(options) + .then(results => { + console.log('Deleted study.'); + }) + .catch(err => { + console.error(err); + }); +} +// [END healthcare_dicomweb_delete_study] + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + cloudRegion: { + alias: 'c', + default: 'us-central1', + requiresArg: true, + type: 'string' + }, + projectId: { + alias: 'p', + default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT, + description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.', + requiresArg: true, + type: 'string' + }, + serviceAccount: { + alias: 's', + default: process.env.GOOGLE_APPLICATION_CREDENTIALS, + description: 'The path to your service credentials JSON.', + requiresArg: true, + type: 'string' + } + }) + .command( + `dicomWebStoreInstance `, + `Handles the POST requests specified in the DICOMweb standard.`, + {}, + (opts) => { + const cb = function (token) { + dicomWebStoreInstance(token, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.dcmFile, opts.boundary); + }; + getToken(opts.serviceAccount, cb); + } + ) + .command( + `dicomWebSearchInstances `, + `Handles the GET requests specified in the DICOMweb standard.`, + {}, + (opts) => { + const cb = function (token) { + dicomWebSearchInstances(token, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId); + }; + getToken(opts.serviceAccount, cb); + } + ) + .command( + `dicomWebRetrieveStudy `, + `Handles the GET requests specified in the DICOMweb standard.`, + {}, + (opts) => { + const cb = function (token) { + dicomWebRetrieveStudy(token, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.studyUid); + }; + getToken(opts.serviceAccount, cb); + } + ) + .command( + `dicomWebDeleteStudy `, + `Handles DELETE requests.`, + {}, + (opts) => { + const cb = function (token) { + dicomWebDeleteStudy(token, opts.projectId, opts.cloudRegion, opts.datasetId, opts.dicomStoreId, opts.studyUid); + }; + getToken(opts.serviceAccount, cb); + } + ) + .wrap(120) + .recommendCommands() + .epilogue(`For more information, see https://cloud.google.com/healthcare/docs`) + .help() + .strict() + .argv; diff --git a/healthcare/dicom/package.json b/healthcare/dicom/package.json new file mode 100644 index 0000000000..605240d7a6 --- /dev/null +++ b/healthcare/dicom/package.json @@ -0,0 +1,38 @@ +{ + "name": "nodejs-docs-samples-healthcare", + "version": "0.0.1", + "private": true, + "license": "Apache-2.0", + "author": "Google LLC", + "repository": "GoogleCloudPlatform/nodejs-docs-samples", + "engines": { + "node": ">=6.0.0" + }, + "scripts": { + "test": "ava -T 1m --verbose system-test/*.test.js" + }, + "devDependencies": { + "@google-cloud/nodejs-repo-tools": "^2.3.1", + "ava": "^0.25.0" + }, + "dependencies": { + "googleapis": "^32.0.0", + "uuid": "^3.3.2", + "yargs": "^12.0.1", + "gtoken": "^2.3.0", + "request": "^2.87.0", + "request-promise": "^4.2.2" + }, + "cloud-repo-tools": { + "requiresKeyFile": true, + "requiresProjectId": true, + "test": { + "build": { + "requiredEnvVars": [ + "API_KEY", + "GCLOUD_PROJECT" + ] + } + } + } +} diff --git a/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE-edited.dcm b/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE-edited.dcm new file mode 100644 index 0000000000..1ce38558fd Binary files /dev/null and b/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE-edited.dcm differ diff --git a/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE.dcm b/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE.dcm new file mode 100644 index 0000000000..3e0064f21b Binary files /dev/null and b/healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE.dcm differ diff --git a/healthcare/dicom/system-test/dicom_stores.test.js b/healthcare/dicom/system-test/dicom_stores.test.js new file mode 100644 index 0000000000..4e4f4bafdb --- /dev/null +++ b/healthcare/dicom/system-test/dicom_stores.test.js @@ -0,0 +1,89 @@ +/** + * Copyright 2018, Google, LLC + * Licensed under the Apache License, Version 2.0 (the `License`); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const path = require(`path`); +const test = require(`ava`); +const tools = require(`@google-cloud/nodejs-repo-tools`); +const uuid = require(`uuid`); + +const cmdDataset = `node datasets.js`; +const cmd = `node dicom_stores.js`; +const cwdDatasets = path.join(__dirname, `../../datasets`); +const cwd = path.join(__dirname, `..`); +const datasetId = `nodejs-docs-samples-test-${uuid.v4()}`.replace(/-/gi, '_'); +const dicomStoreId = `nodejs-docs-samples-test-dicom-store${uuid.v4()}`.replace(/-/gi, '_'); +const pubsubTopic = `nodejs-docs-samples-test-pubsub${uuid.v4()}`.replace(/-/gi, '_'); + +const bucketName = process.env.GCLOUD_STORAGE_BUCKET; +const dcmFileName = `IM-0002-0001-JPEG-BASELINE.dcm`; +const contentUri = bucketName + '/' + dcmFileName; + +test.before(tools.checkCredentials); +test.before(async () => { + return tools.runAsync(`${cmdDataset} createDataset ${datasetId}`, cwdDatasets) + .then((results) => { + console.log(results); + return results; + }); +}); +test.after.always(async () => { + try { + await tools.runAsync(`${cmdDataset} deleteDataset ${datasetId}`, cwdDatasets); + } catch (err) { } // Ignore error +}); + +test.serial(`should create a DICOM store`, async t => { + const output = await tools.runAsync( + `${cmd} createDicomStore ${datasetId} ${dicomStoreId}`, cwd); + t.regex(output, /Created DICOM store/); +}); + +test.serial(`should get a DICOM store`, async t => { + const output = await tools.runAsync( + `${cmd} getDicomStore ${datasetId} ${dicomStoreId}`, cwd); + t.regex(output, /Got DICOM store/); +}); + +test.serial(`should patch a DICOM store`, async t => { + const output = await tools.runAsync( + `${cmd} patchDicomStore ${datasetId} ${dicomStoreId} ${pubsubTopic}`, cwd); + t.regex(output, /Patched DICOM store with Cloud Pub\/Sub topic/); +}); + +test.serial(`should list DICOM stores`, async t => { + const output = await tools.runAsync( + `${cmd} listDicomStores ${datasetId}`, cwd); + t.regex(output, /DICOM stores/); +}); + +test.serial(`should export a DICOM instance`, async t => { + const output = await tools.runAsync( + `${cmd} exportDicomInstanceGcs ${datasetId} ${dicomStoreId} ${bucketName}`, cwd); + t.regex(output, /Exported DICOM instances to bucket/); +}); + +test.serial(`should import a DICOM object from GCS`, async t => { + const output = await tools.runAsync( + `${cmd} importDicomObject ${datasetId} ${dicomStoreId} ${contentUri}`, cwd); + t.regex(output, /Imported DICOM objects from bucket/); +}); + +test(`should delete a DICOM store`, async t => { + const output = await tools.runAsync( + `${cmd} deleteDicomStore ${datasetId} ${dicomStoreId}`, cwd); + t.regex(output, /Deleted DICOM store/); +}); diff --git a/healthcare/dicom/system-test/dicomweb.test.js b/healthcare/dicom/system-test/dicomweb.test.js new file mode 100644 index 0000000000..bd0f03187a --- /dev/null +++ b/healthcare/dicom/system-test/dicomweb.test.js @@ -0,0 +1,77 @@ +/** + * Copyright 2018, Google, LLC + * Licensed under the Apache License, Version 2.0 (the `License`); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const path = require(`path`); +const test = require(`ava`); +const tools = require(`@google-cloud/nodejs-repo-tools`); +const uuid = require(`uuid`); + +const cmdDataset = `node datasets.js`; +const cmdDicomStore = `node dicom_stores.js`; +const cmd = `node dicomweb.js`; +const cwdDatasets = path.join(__dirname, `../../datasets`); +const cwd = path.join(__dirname, `..`); +const datasetId = `nodejs-docs-samples-test-${uuid.v4()}`.replace(/-/gi, '_'); +const dicomStoreId = `nodejs-docs-samples-test-dicom-store${uuid.v4()}`.replace(/-/gi, '_'); + +const dcmFile = `resources/IM-0002-0001-JPEG-BASELINE-edited.dcm`; +const boundary = `DICOMwebBoundary`; +// The studyUid is not assigned by the server and is part of the metadata +// of dcmFile. +const studyUid = `1.2.840.113619.2.176.3596.3364818.7819.1259708454.105`; + +test.before(tools.checkCredentials); +test.before(async () => { + return tools.runAsync(`${cmdDataset} createDataset ${datasetId}`, cwdDatasets) + .then((results) => { + console.log(results); + return results; + }); +}); +test.after.always(async () => { + try { + await tools.runAsync(`${cmdDataset} deleteDataset ${datasetId}`, cwdDatasets); + } catch (err) { } // Ignore error +}); + +test.serial(`should store a DICOM instance`, async t => { + await tools.runAsync(`${cmdDicomStore} createDicomStore ${datasetId} ${dicomStoreId}`, cwd); + const output = await tools.runAsync( + `${cmd} dicomWebStoreInstance ${datasetId} ${dicomStoreId} ${dcmFile} ${boundary}`, cwd); + t.regex(output, /Stored instance/); +}); + +test.serial(`should search DICOM instances`, async t => { + const output = await tools.runAsync( + `${cmd} dicomWebSearchInstances ${datasetId} ${dicomStoreId}`, cwd); + t.regex(output, /Instances/); +}); + +test.serial(`should retrieve a DICOM study`, async t => { + const output = await tools.runAsync( + `${cmd} dicomWebRetrieveStudy ${datasetId} ${dicomStoreId} ${studyUid}`, cwd); + t.regex(output, /Retrieved study/); +}); + +test.serial(`should delete a DICOM study`, async t => { + const output = await tools.runAsync( + `${cmd} dicomWebDeleteStudy ${datasetId} ${dicomStoreId} ${studyUid}`, cwd); + t.regex(output, /Deleted study/); + + // Clean up + await tools.runAsync(`${cmdDicomStore} deleteDicomStore ${datasetId} ${dicomStoreId}`, cwd); +});