Skip to content

Commit

Permalink
Remove deprecated ensureValueSets and ensureValueSetsInLibrary methods (
Browse files Browse the repository at this point in the history
#23)

* Remove deprecated ensureValueSets and ensureValueSetsInLibrary methods

* Fix typo: ensureValueSetsinLibrary --> ensureValueSetsInLibrary
  • Loading branch information
cmoesel authored Nov 30, 2022
1 parent 59bf4ca commit 10e9e60
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 596 deletions.
51 changes: 30 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ valueset "Diabetes": 'https://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.
// or valueset "Diabetes": '2.16.840.1.113883.3.464.1003.103.12.1001' version '20190315'
```

When using the canonical URL as a Value Set identifier, it is also possible to embed the version directly in the URL, using a vertical bar (`|`) to separate the identifier and version:
When using the canonical URL as a Value Set identifier, it is also possible to embed the version directly in the URL,
using a vertical bar (`|`) to separate the identifier and version:

```
valueset "Diabetes": 'https://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.103.12.1001|20190315'
```

The embedded version, however, is only supported for the canonical URL form of value sets. It is not supported for URN or OID identifiers.
The embedded version, however, is only supported for the canonical URL form of value sets. It is not supported for URN
or OID identifiers.

## Credentials Required

Expand All @@ -50,35 +52,42 @@ prevents the code service from having to make multiple calls to VSAC for the sam

The second argument to the `CodeService` constructor is a boolean indicating if the code service should begin by
loading existing value sets from the cache. If true, it will initialize the code service from the cache (if the
cache exists and is populated). If false, `ensureValueSets` will re-download any value sets passed to it, overwriting
the cache.
cache exists and is populated). If false, `ensureValueSetsWithAPIKey` will re-download any value sets passed to it,
overwriting the cache.

## Using UMLS Credentials

Downloading value set definitions from VSAC requires a valid UMLS account. The code service's `ensureValueSets`
function allows a UMLS username and password to be passed in. Alternately, the UMLS username and password can be
provided via `UMLS_USER_NAME` and `UMLS_PASSWORD` environment variables.

**NOTE**: As of Jan 1 2021 VSAC will no longer accept accept username and password and will require an API key. The code
service's `ensureValueSetsWithAPIKey` allows a UMLS API key to be passed in. Alternatively, the UMLS API key can be
provided via `UMLS_API_KEY` environment variables.
Downloading value set definitions from VSAC requires a valid UMLS account. The code service's
`ensureValueSetsWithAPIKey` function allows a UMLS API key to be passed in. Alternately, the UMLS API key can be
provided via the `UMLS_API_KEY` environment variable.

## Downloading Value Set Definitions

The `ensureValueSets` and `ensureValueSetsInLibrary` functions are the only functions that attempt to download value
sets from VSAC. Before they make a request to VSAC, they will check the cache. If the value set is already in the
cache, they will not make a request to VSAC. Otherwise, they will use VSAC's SVS2 API to download the expanded codes
from the value set.
The `ensureValueSetsWithAPIKey` and `ensureValueSetsInLibraryWithAPIKey` functions are the only functions that attempt
to download value sets from VSAC. Before they make a request to VSAC, they will check the cache. If the value set is
already in the cache, they will not make a request to VSAC. Otherwise, they will use VSAC's SVS2 API to download the
expanded codes from the value set.

The `findValueSet` and `findValueSets` functions (including the legacy `findValueSetsByOid` function) do not reach out
to VSAC, so implementations should call `ensureValueSetsInLibrary` or `ensureValueSets` before attempting to execute
CQL. If `ensureValueSets` is used, the implementor is responsible for passing in the OIDs / versions that will be
needed.
to VSAC, so implementations should call `ensureValueSetsInLibraryWithAPIKey` or `ensureValueSetsWithAPIKey` before
attempting to execute CQL. If `ensureValueSetsWithAPIKey` is used, the implementor is responsible for passing in the
OIDs / versions that will be needed.

## Breaking Changes in Version 2.0.0

Version 2.0.0 of the code service removes the old `ensureValueSets` and `ensureValueSetsInLibrary` methods that
accepted a UMLS user name and password. As of Jan 1, 2021, VSAC no longer allows API authentication using username and
password. Instead, implementations should now use `ensureValueSetsWithAPIKey` and `ensureValueSetsInLibraryWithApiKey`,
each of which allows an API key to be passed in (or to be specified via the `UMLS_API_KEY` environment variables).

In addition, version 2.0.0 switched its communication library from [request](https://www.npmjs.com/package/request)
to [node-fetch](https://www.npmjs.com/package/node-fetch). This should be transparent in most cases, but some thrown
errors may have a different format since they are thrown by _node-fetch_ rather than _request_.

## Example

The following is a simple example of setting up the VSAC Code Service, calling `ensureValueSets`, and passing the
Code Service into the CQL Execution engine:
The following is a simple example of setting up the VSAC Code Service, calling `ensureValueSetsInLibraryWithApiKey`,
and passing the Code Service into the CQL Execution engine:

```js
const vsac = require('cql-exec-vsac');
Expand All @@ -89,7 +98,7 @@ const vsac = require('cql-exec-vsac');
// Set up the code service, loading from the cache if it exists
const codeService = new vsac.CodeService('/path/to/vsac_cache', true);
// Ensure value sets in the library and its dependencies, downloading any missing value sets
codeService.ensureValueSetsInLibrary(library, true, 'myUMLSUserName', 'myUMLSPassword')
codeService.ensureValueSetsInLibraryWithApiKey(library, true, 'myUmlsApiKey')
.then(() => {
// Value sets are loaded, so execute!
const executor = new cql.Executor(lib, codeService, parameters);
Expand Down
6 changes: 1 addition & 5 deletions manual-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ const VALUESET = {
};

async function main() {
if (
env['UMLS_USER_NAME'] == null &&
env['UMLS_PASSWORD'] == null &&
env['UMLS_API_KEY'] == null
) {
if (env['UMLS_API_KEY'] == null) {
console.error('This test requires you to set the UMLS_API_KEY environment variable');
process.exit(1);
}
Expand Down
79 changes: 2 additions & 77 deletions src/CodeService.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const fs = require('fs');
const proc = require('process');
const env = proc.env;
const path = require('path');
const { downloadFromVSAC, downloadFromVSACWithAPIKey } = require('./download-vsac');
const { downloadFromVSACWithAPIKey } = require('./download-vsac');
const extractOidAndVersion = require('./extractOidAndVersion');

/**
Expand Down Expand Up @@ -62,55 +62,6 @@ class CodeService {
}
}

/**
* Given a list of value set references, will ensure that each has a local
* definition. If a local definition does not exist, the value set will
* be downloaded using the VSAC API.
* @deprecated As of Jan 1 2021 VSAC will no longer accept accept username and password.
* Please use ensureValueSetsWithAPIKey() instead.
* @param {Object} valueSetList - an array of objects, each containing "name"
* and "id" properties, with an optional "version" property
* @returns {Promise.<undefined,Error>} A promise that returns nothing when
* resolved and returns an error when rejected.
*/
ensureValueSets(
valueSetList = [],
umlsUserName = env['UMLS_USER_NAME'],
umlsPassword = env['UMLS_PASSWORD'],
caching = true
) {
console.warn(
'WARNING! As of Jan 1 2021 VSAC will no longer accept accept username and password. As such ' +
'ensureValueSets() has been deprecated, please use the new ensureValueSetsWithAPIKey() function instead!'
);
// First, filter out the value sets we already have
const filteredVSList = valueSetList.filter(vs => {
const result = this.findValueSet(vs.id, vs.version);
return typeof result === 'undefined';
});
// Now download from VSAC if necessary
if (filteredVSList.length == 0) {
return Promise.resolve();
} else if (
typeof umlsUserName === 'undefined' ||
umlsUserName == null ||
typeof umlsPassword === 'undefined' ||
umlsPassword == null
) {
return Promise.reject(
'Failed to download value sets since UMLS_USER_NAME and/or UMLS_PASSWORD is not set.'
);
} else {
return downloadFromVSAC(
umlsUserName,
umlsPassword,
filteredVSList,
this.cache,
this.valueSets,
caching
);
}
}
/**
* Given a list of value set references, will ensure that each has a local
* definition. If a local definition does not exist, the value set will
Expand Down Expand Up @@ -142,38 +93,12 @@ class CodeService {
}
}

/**
* Given a library, will detect referenced value sets and ensure that each has a local definition. If a local definition
* does not exist, the value set will be downloaded using the VSAC API.
* @deprecated As of Jan 1 2021 VSAC will no longer accept accept username and password. Please use
* ensureValueSetsInLibraryWithAPIKey() instead.
* @param {Object} library - the CQL Library object to look for referenced value sets in
* @param {boolean} checkIncluded - indicates if "included" libraries should also be checked
* @param {string} umlsUserName - the UMLS username to use when downloading value sets (defaults to env "UMLS_USER_NAME")
* @param {string} umlsPassword - the UMLS password to use when downloading value sets (defaults to env "UMLS_PASSWORD")
* @returns {Promise.<undefined,Error>} A promise that returns nothing when resolved and returns an error when rejected.
*/
ensureValueSetsInLibrary(
library,
checkIncluded = true,
umlsUserName = env['UMLS_USER_NAME'],
umlsPassword = env['UMLS_PASSWORD'],
caching = true
) {
console.warn(
'WARNING! As of Jan 1 2021 VSAC will no longer accept accept username and password. As such ' +
'ensureValueSetsInLibrary() has been deprecated, please use the new ensureValueSetsInLibraryWithAPIKey() function instead!'
);
const valueSets = extractSetOfValueSetsFromLibrary(library, checkIncluded);
return this.ensureValueSets(Array.from(valueSets), umlsUserName, umlsPassword, caching);
}

/**
* Given a library, will detect referenced value sets and ensure that each has a local definition. If a local definition
* does not exist, the value set will be downloaded using the VSAC API.
* @param {Object} library - the CQL Library object to look for referenced value sets in
* @param {boolean} checkIncluded - indicates if "included" libraries should also be checked
* @param {string} umlsAPIKey - the UMLS API Key to use when downloading value sets (defaults to env "UMLS_USER_NAME")
* @param {string} umlsAPIKey - the UMLS API Key to use when downloading value sets
* @returns {Promise.<undefined,Error>} A promise that returns nothing when resolved and returns an error when rejected.
*/
ensureValueSetsInLibraryWithAPIKey(
Expand Down
83 changes: 1 addition & 82 deletions src/download-vsac.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,6 @@ const parseVSACXML = require('./parse-vsac');
const extractOidAndVersion = require('./extractOidAndVersion');
const debug = require('debug')('vsac'); // To turn on DEBUG: $ export DEBUG=vsac

/*
* @deprecated: As of Jan 1 2021 VSAC will no longer accept accept username and password.
* Please use downloadFromVSACWithAPIKey instead.
*/
function downloadFromVSAC(username, password, input, output, vsDB = {}, caching = true) {
const oidsAndVersions = [];
Object.keys(input).forEach(key => {
let [id, version] = [input[key].id, input[key].version];
const [oid, embeddedVersion] = extractOidAndVersion(id);
if (version == null && embeddedVersion != null) {
version = embeddedVersion;
}
if (vsDB[oid] == null || vsDB[oid][version] == null) {
oidsAndVersions.push({ oid, version });
}
});
if (oidsAndVersions.length) {
output = path.resolve(output);
if (caching && !fs.existsSync(output)) {
mkdirp.sync(output);
}
return getTicketGrantingTicket(username, password)
.then(ticketGrantingTicket => {
const promises = oidsAndVersions.map(({ oid, version }) => {
// Catch errors and convert to resolutions returning an error. This ensures Promise.all waits for all promises.
// See: http://stackoverflow.com/questions/31424561/wait-until-all-es6-promises-complete-even-rejected-promises
return downloadValueSet(ticketGrantingTicket, oid, version, output, vsDB, caching).catch(
err => {
debug(`Error downloading valueset ${oid}${version || ''}`, err);
return new Error(`Error downloading valueset: ${oid}${version || ''}`);
}
);
});
return Promise.all(promises);
})
.then(results => {
const errors = results.filter(r => r instanceof Error);
if (results.length - errors.length > 0) {
// There were results, so write the file first before resolving/rejecting
return writeFile(
path.join(output, 'valueset-db.json'),
JSON.stringify(vsDB, null, 2),
caching
).then(
result => (errors.length == 0 ? result : Promise.reject(errors)),
err => {
errors.push(err);
return Promise.reject(errors);
}
);
}
if (errors.length > 0) {
return Promise.reject(errors);
}
});
} else {
return Promise.resolve();
}
}

function downloadFromVSACWithAPIKey(apiKey, input, output, vsDB = {}, caching = true) {
const oidsAndVersions = [];
Object.keys(input).forEach(key => {
Expand Down Expand Up @@ -122,27 +62,6 @@ function downloadFromVSACWithAPIKey(apiKey, input, output, vsDB = {}, caching =
}
}

/*
* @deprecated: As of Jan 1 2021 VSAC will no longer accept accept username and password.
* Please use getTicketGrantingTicketWithAPIKey instead.
*/
function getTicketGrantingTicket(username, password) {
debug('Getting TGT');
const params = new URLSearchParams();
params.append('username', username);
params.append('password', password);
const options = {
method: 'POST',
body: params
};
return fetch('https://vsac.nlm.nih.gov/vsac/ws/Ticket', options).then(res => {
if (!res.ok) {
throw new Error(res.status);
}
return res.text();
});
}

function getTicketGrantingTicketWithAPIKey(apiKey) {
debug('Getting TGT');
const params = new URLSearchParams();
Expand Down Expand Up @@ -223,4 +142,4 @@ function writeFile(file, data, caching = true) {
});
}

module.exports = { downloadFromVSAC, downloadFromVSACWithAPIKey };
module.exports = { downloadFromVSACWithAPIKey };
Loading

0 comments on commit 10e9e60

Please sign in to comment.