From e0b53b7a00a3b596eedde331288609ac82ac78fc Mon Sep 17 00:00:00 2001 From: Benedict Noero Date: Thu, 8 Nov 2018 16:48:15 -0500 Subject: [PATCH 1/3] Healthcare API alpha DICOM samples --- healthcare/dicom/README.md | 68 ++++ healthcare/dicom/dicom_stores.js | 341 ++++++++++++++++++ healthcare/dicom/dicomweb.js | 243 +++++++++++++ healthcare/dicom/package.json | 38 ++ .../IM-0002-0001-JPEG-BASELINE-edited.dcm | Bin 0 -> 18133 bytes .../resources/IM-0002-0001-JPEG-BASELINE.dcm | Bin 0 -> 18056 bytes .../dicom/system-test/dicom_stores.test.js | 89 +++++ healthcare/dicom/system-test/dicomweb.test.js | 77 ++++ 8 files changed, 856 insertions(+) create mode 100644 healthcare/dicom/README.md create mode 100644 healthcare/dicom/dicom_stores.js create mode 100644 healthcare/dicom/dicomweb.js create mode 100644 healthcare/dicom/package.json create mode 100644 healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE-edited.dcm create mode 100644 healthcare/dicom/resources/IM-0002-0001-JPEG-BASELINE.dcm create mode 100644 healthcare/dicom/system-test/dicom_stores.test.js create mode 100644 healthcare/dicom/system-test/dicomweb.test.js diff --git a/healthcare/dicom/README.md b/healthcare/dicom/README.md new file mode 100644 index 0000000000..a9924e8778 --- /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 0000000000000000000000000000000000000000..1ce38558fdb434b61e8b696b1911b95f6b01d6fd GIT binary patch literal 18133 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 CNUf6q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3e0064f21b0986ab7ed36a783b735ee947b8046b GIT binary patch literal 18056 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 { + 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..9f4175e947 --- /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); +}); From cd05289ba99cefd06d3930b3f82ad08eced4e52c Mon Sep 17 00:00:00 2001 From: Benedict Noero Date: Thu, 8 Nov 2018 16:49:44 -0500 Subject: [PATCH 2/3] Fix README formatting --- healthcare/dicom/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/healthcare/dicom/README.md b/healthcare/dicom/README.md index a9924e8778..cadd6f61eb 100644 --- a/healthcare/dicom/README.md +++ b/healthcare/dicom/README.md @@ -56,13 +56,13 @@ Run the following command to install the library dependencies for Node.js: 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] + 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 From 09f5c5913565de2293458f4e22ed2cb18aef34a0 Mon Sep 17 00:00:00 2001 From: Benedict Noero Date: Thu, 8 Nov 2018 17:12:05 -0500 Subject: [PATCH 3/3] Fix lint errors --- healthcare/dicom/system-test/dicomweb.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healthcare/dicom/system-test/dicomweb.test.js b/healthcare/dicom/system-test/dicomweb.test.js index 9f4175e947..bd0f03187a 100644 --- a/healthcare/dicom/system-test/dicomweb.test.js +++ b/healthcare/dicom/system-test/dicomweb.test.js @@ -29,7 +29,7 @@ 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` +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`; @@ -51,7 +51,7 @@ test.after.always(async () => { 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); + `${cmd} dicomWebStoreInstance ${datasetId} ${dicomStoreId} ${dcmFile} ${boundary}`, cwd); t.regex(output, /Stored instance/); });