Design Hub developer guide - plugins¶
Design Hub's functionality can be extended by plugins in several workflows to meet the expectations for different user groups and use-cases. Plugins are generally implemented as NodeJS modules implementing a simple stateless API, connecting to one or more web services or databases.
Table of contents:
Plugin types¶
Realtime plugins
Realtime plugins encapsulate functionality that describe 1 virtual compound in 1 specific area of phys-chem properties, ADMET, commercial availability, literature data, in-silico assay data, structure checks or modeling.
For more information specifically related to realtime plugins, check the Developer guide - real time plugins.
Resolver plugins
A resolver plugin converts a corporate identifier into a chemical structure using a structure database lookup.
For more information specifically related to resolver plugins, check the Developer guide - resolver plugins.
Storage plugins
A storage plugin establishes and maintains a flat database of all virtual compounds in Design Hub. Typically implemented with a SQL database connection, this can be used to support advanced analysis, visualizations, or a backup.
For more information specifically related to storage plugins, check the Developer guide - storage plugins.
Registry plugins
A registry plugin helps lookup the synthesis status and substance ID of a compound by periodically executing searches in a compound registration system or ELN.
For more information specifically related to registry plugins, check the Developer guide - registry plugins.
Import plugins
Import plugins load chemical structures into Design Hub based on user input or automatically, followed by deduplication and processing of records.
For more information specifically related to import plugins, check the Developer guide - import plugins.
Company plugins
A company plugin assists authorizing a user by fetching project and group membership for the user.
For more information specifically related to company plugins, check the Developer guide - company plugins.
Configuration¶
All plugins are loaded automatically during startup of the application. The definition of plugins to load is set in the configuration guide. For more, check the Configuration guide's servicesDirectory section.
Development¶
For the successful development of a plugin, we recommend the following preparation:
- an instance of Design Hub available for development purposes, i.e.: the ability to stop and start it, to try different configuration options
- familiarity with JavaScript, NodeJS and its module system
- good understanding of Promises / async await
We recommend checking these basic introduction materials to get familiar with NodeJS
NodeJS beginner guide, Youtube (78m),
Getting started with NodeJS, Youtube (19m),
Intro to NodeJS, Youtube (48m)
NodeJS module description: https://nodejs.org/api/modules.html
Promise introduction: https://web.dev/articles/promises?hl=en
Plugin lifecycle¶
All plugin types support optional init and close lifecycle hooks. Design Hub calls these functions with a ServiceContext object as the parameter.
ServiceContext¶
| Name | Type | Description |
|---|---|---|
domain |
string |
The domain this plugin instance belongs to |
dhUtils |
object |
Utility functions for chemical conversions, HTTP requests and more. See API of this.dhUtils |
logger |
object |
Context-specific logger with debug, info, warn, error methods |
schedule |
object |
node-schedule instance for scheduling recurring jobs |
init(context)¶
Called once per domain on application startup, after the plugin is loaded. Use this to initialize connections, set up scheduled jobs, or prepare resources.
In a multi-domain setup, init is called separately for each domain with its own ServiceContext.
close(context)¶
Called once per domain on application shutdown. Use this to clean up resources, close connections, or cancel scheduled jobs.
Example¶
function init(context) {
context.logger.info("plugin initialized");
context.schedule.scheduleJob("0 */30 * * * *", async () => {
// periodic task
});
}
function close(context) {
context.logger.info("plugin shutting down");
}
module.exports = {
name: "my-plugin",
init,
close,
// ... other exports
};
Utility functions¶
Most Design Hub plugins benefit from a library of commonly used functions. Design Hub provides these as a context object available via this.dhUtils inside every local plugin function call, including the following functions:
- chemical file format conversion using JChem Microservices IO, used whenever a non-Chemaxon backend is involved in the plugin functionality
- 2D rendering of chemical structures using JChem Microservices IO or Marvin Backend Image Generation Service, used whenever the (realtime) plugin needs to depict molecules
- a general purpose HTTP library for REST/JSON, multipart or form-urlencoded requests, with basic or Bearer token authentication
- a utility library for processing PDB files
No installation or configuration is required — this.dhUtils is automatically injected into the this context when Design Hub calls a local plugin. Remote plugins loaded from remoteServices do not receive dhUtils; use require only if you bundle your own copy of the utilities.
The @chemaxon/dh-utils npm package is deprecated. Plugins using require("@chemaxon/dh-utils") should migrate to this.dhUtils. See Migrating from @chemaxon/dh-utils below. The npm package will be removed in a future release.
Migrating from @chemaxon/dh-utils¶
Design Hub now injects utility functions into plugin callbacks. The following breaking changes apply when moving from the npm package to the built-in API.
How utilities are provided¶
Before (@chemaxon/dh-utils) |
After (built-in) |
|---|---|
npm install @chemaxon/dh-utils and require("@chemaxon/dh-utils") |
No install — use this.dhUtils in plugin callbacks |
| Module imported at top of file | Injected on this when Design Hub calls the plugin |
| Available everywhere in the plugin file | Available in plugin callbacks and in init / close as context.dhUtils |
| Same for local and remote plugins | Local plugins only — remote plugins (remoteServices) do not receive dhUtils |
Breaking API changes¶
| Area | Before | After |
|---|---|---|
| Context property | dhutils (lowercase, when documented on this) |
dhUtils (camelCase) — this.dhUtils, context.dhUtils |
| PDB helpers | dhutils.PDBUtilities.load(...) |
this.dhUtils.PDBUtilities.default.load(...) — datacollector plugins may also use this.pdbUtils.load(...) |
findLigandsFromFile(pdbFile) |
async, on PDBUtilities |
findResidues(pdbFile) — synchronous |
getCleanedImageURLs(...) |
separate method | Removed — use getImageURLs(...); coordinate cleaning happens when the useMarvinNext feature flag is enabled |
getImageURLs defaults |
width 400, height 260 |
width 500, height 400 |
getImageURLs parameters |
optional visualizationSettings argument |
Removed — use compoundVisualizationSettings in Configuration guide |
batchConvert return type |
string[] |
{ structure, successful, error? }[] — check successful per entry |
append return type |
documented as array | string — single merged multistructure file |
convertFile return type |
Buffer |
string |
req() default method |
GET |
POST |
| Service configuration | JMS_IO_URL, JMS_STRUCTURE_MANIPULATION_URL env vars in docs |
Design Hub config keys: jmsIo / DH_JMS_IO, jmsStructureManipulation / DH_JMS_STRUCTURE_MANIPULATION, marvinBigService / DH_MARVIN_BIG_SERVICE — see Configuration guide |
Migration steps¶
- Remove
@chemaxon/dh-utilsfrompackage.jsonand delete therequire/importat the top of the plugin. - Replace
dhutils.method(...)withthis.dhUtils.method(...)inside plugin callbacks (calculate,resolve,runImport, etc.). - In
init/close, usecontext.dhUtilsinstead of a module-level import. - Update PDB calls to use
this.dhUtils.PDBUtilities.default.*(orthis.pdbUtils.*in datacollector plugins). - Rename
findLigandsFromFiletofindResiduesand dropawaitif the call was only wrapped for the old async signature. - Replace
getCleanedImageURLswithgetImageURLsand ensureuseMarvinNextis enabled if cleaned coordinates are required. - Update
batchConvertcallers to read.structurefrom each result object and handlesuccessful === false. - Remove any plugin-local
.npmrc/ Artifactory setup that existed only for@chemaxon/dh-utils.
Before and after¶
const dhutils = require("@chemaxon/dh-utils");
async function calculate(body) {
const smiles = await dhutils.convert(body.compounds[0].mrvSource, "smiles");
const images = await dhutils.getCleanedImageURLs([body.compounds[0].mrvSource]);
const ligands = await dhutils.PDBUtilities.findLigandsFromFile(pdbContents);
return { client: { smiles, images, ligands } };
}
async function calculate(body) {
const smiles = await this.dhUtils.convert(body.compounds[0].mrvSource, "smiles");
const images = await this.dhUtils.getImageURLs([body.compounds[0].mrvSource]);
const ligands = this.dhUtils.PDBUtilities.default.findResidues(pdbContents);
return { client: { smiles, images, ligands } };
}
function init(context) {
context.dhUtils.get({ url: "https://example.com/health" });
}
Example usage¶
async function calculate(body) {
const smiles = await this.dhUtils.convert(body.compounds[0].mrvSource, "smiles");
}
API of this.dhUtils¶
async get(data): String | Buffer | Object
Sends an HTTP GET request using the NodeJS fetch API. For parameters see below at async req.
Returns (a Promise of) the response body depending on encoding specified in the request.
async post(data): String | Buffer | Object
Sends an HTTP POST request using the NodeJS fetch API. For parameters see below at async req.
Returns (a Promise of) the response body depending on encoding specified in the request.
async req(data): String | Buffer | Object
Sends an HTTP request using the NodeJS fetch API. Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
url |
URL or string |
yes | the HTTP URL to make the request to |
jar |
CookieJar |
no | See tough-cookie |
formData |
FormData |
no | See FormData Web API docs |
isMultipartFormData |
boolean |
no | When true, encodes formData fields as application/x-www-form-urlencoded instead of multipart |
json |
boolean |
no | Shorthand property to sending the body as stringified text with application/json headers and parsing the response as JSON as well |
method |
string |
no | POST (default), GET, PUT, PATCH, DELETE |
headers |
Object |
no | Key-value pair of request headers |
body |
any | no | Body/payload of the request |
followRedirect |
boolean |
no | Follow redirects |
timeout |
number |
no | Number of milliseconds after which the request is aborted and an error is thrown |
qs |
Object |
no | Key-value pair of URL encoded query strings (search params) to append to the path of the URL. See URLSearchParams |
auth |
Object |
no | Allows Basic authentication using Base64 encoded credentials when username and password is specified. Allows sending Bearer tokens when bearer is specified |
auth.username |
string |
no | Username of basic authentication for base64 encoding |
auth.password |
string |
no | Password of basic authentication for base64 encoding |
auth.bearer |
string |
no | Value of bearer token |
Returns (a Promise of) the response body depending on encoding specified in the request.
async convert(structure, toFormat): String
Converts the input chemical structure to the specified format using JChem Microservices IO.
Parameters:
structure(string): the chemical structure to converttoFormat(string): the desired format string. See File Formats.
Note: use of this method requires JChem Microservices IO to be configured. See Configuration guide — set jmsIo / DH_JMS_IO / ML_JMS_IO, or use the jchemMicroServices gateway.
Returns:
- the input chemical structure converted to the desired format.
async convertFile(fileContents, toFormat): string
Converts the input chemical structure file to the specified format using JChem Microservices IO.
Parameters:
fileContents(Buffer | String): the chemical structure file to converttoFormat(string): the desired format string. See File Formats.
Note: use of this method requires JChem Microservices IO to be configured. See Configuration guide — set jmsIo / DH_JMS_IO / ML_JMS_IO, or use the jchemMicroServices gateway.
Returns:
- the converted structure file contents as a string.
async batchConvert(structures, toFormat): { structure: string, successful: boolean, error?: string }[]
Converts the input chemical structures to the specified format using JChem Microservices IO.
Parameters:
structures(string[]): an array of chemical structures to converttoFormat(string): the desired format string. See File Formats.
Note: use of this method requires JChem Microservices IO to be configured. See Configuration guide — set jmsIo / DH_JMS_IO / ML_JMS_IO, or use the jchemMicroServices gateway.
Returns:
- an array of conversion results. Each entry contains the converted
structurewhensuccessfulistrue, or anerrormessage when conversion failed for that input.
async append(structures, toFormat): string
Merges multiple input chemical structures into a single multistructure file in the specified format using JChem Microservices IO.
Parameters:
structures(string[]): an array of chemical structurestoFormat(string): the desired format string. See File Formats.
Note: use of this method requires JChem Microservices IO to be configured. See Configuration guide — set jmsIo / DH_JMS_IO / ML_JMS_IO, or use the jchemMicroServices gateway.
Returns:
- the merged multistructure file as a string.
async clean(structure, dimension, format): string
Rearranges the atomic coordinates of the input structure.
Parameters:
structure(string): the chemical structure to processdimension(number): dimension to clean to. Values:2,3. Optional. Default:2.format(string): the desired format string. See File Formats. Optional. Default:mrv.
Note: use of this method requires JChem Microservices Structure Manipulation to be configured. See Configuration guide — set jmsStructureManipulation / DH_JMS_STRUCTURE_MANIPULATION / ML_JMS_STRUCURE_MANIPULATION, or use the jchemMicroServices gateway.
Returns:
- the input chemical structure with new atomic coordinates in the desired format.
async getImageURLs(structures, width = 500, height = 400): string[]
Generates data URLs with base64 encoded PNG images of the input chemical structures. When the useMarvinNext feature flag is enabled, images are rendered via Marvin Backend Image Generation Service; otherwise JChem Microservices IO is used.
Parameters:
structures(string[]): an array of chemical structures to render as imageswidth(number): width of the image. Optional. Default:500.height(number): height of the image. Optional. Default:400.
Visualization options are taken from the compoundVisualizationSettings application setting. See Configuration guide.
Note: use of this method requires JChem Microservices IO (jmsIo / DH_JMS_IO / ML_JMS_IO or jchemMicroServices) and, when useMarvinNext is enabled, Marvin Backend Image Generation Service (marvinBigService / DH_MARVIN_BIG_SERVICE).
Returns:
- an array of data URLs with base64 encoded PNG images as strings. Failed conversions produce
nullentries. For more details see Data URLs.
createFormData(data): FormData
Creates a FormData instance from a key-value object of string fields.
appendFileToFormData(file, formData?): FormData
Appends a file entry to an existing or new FormData instance. The file argument is an object with value (string or Buffer) and options (filename, optional contentType).
PDB helpers¶
PDB file operations are available on this.dhUtils.PDBUtilities.default. Datacollector plugins may also use the same API via this.pdbUtils.
async PDBUtilities.default.load(pdbCode): string
Downloads a PDB file with the specified 4 letter code from the Protein Data Bank.
Parameters:
pdbCode(string): the 4 letter code of a PDB file
Returns:
- the PDB file contents as a string
async PDBUtilities.default.findLigands(pdbCode): string[]
Downloads a PDB file with the specified 4 letter code from the Protein Data Bank and extracts the list of available ligands. Equivalent to calling load(pdbCode) followed by findResidues(pdb).
Parameters:
pdbCode(string): the 4 letter code of a PDB file
Returns:
- the residue codes of the available ligands in the file as an array of strings.
PDBUtilities.default.findResidues(pdbFile): string[]
Extracts the list of available ligands from the specified PDB file. Replaces findLigandsFromFile from the deprecated @chemaxon/dh-utils npm package.
Parameters:
pdbFile(string): the contents of the PDB file
Returns:
- the residue codes of the available ligands in the file as an array of strings.
async PDBUtilities.default.getResidue(pdbCode, ligandCode): string | false
Downloads a PDB file with the specified 4 letter code from the Protein Data Bank and extracts a specific ligand.
Parameters:
pdbCode(string): the 4 letter code of a PDB fileligandCode(string): code of the ligand to extract, e.g.:HEM,A:HEM, orA:HEM350
Returns:
- the ligand as a valid minimal PDB file, or
falseif the ligand was not found
PDBUtilities.default.getResidueFromFile(pdbFile, ligandCode): string | false
Extracts a specific ligand from the specified PDB file.
Parameters:
pdbFile(string): the contents of the PDB fileligandCode(string): code of the ligand to extract, e.g.:HEM,A:HEM, orA:HEM350
Returns:
- the ligand as a valid minimal PDB file, or
falseif the ligand was not found
History of changes¶
this.dhUtils (current)
- utility functions are provided via
this.dhUtilsin local plugin callbacks andcontext.dhUtilsininit/close. The@chemaxon/dh-utilsnpm package is deprecated — see Migrating from@chemaxon/dh-utils.
v5.0.3
getImageURLsuses Marvin Backend Image Generation Service for image rendering when theuseMarvinNextfeature flag is enabled
v4.2.18
- with version 4 of dh-utils, the deprecated
requestdependency is replaced with the Node.jsfetchAPI
v3.1.0
- added
appendmethod
v3.0.1
- package renamed from
ml-utilstodh-utils
v2.5.0
- added
convertFilemethod
v2.4.0
- added
PDBUtilities.findLigandsFromFilemethod to the@chemaxon/dh-utilsnpm package (onthis.dhUtils, usePDBUtilities.default.findResiduesinstead)
v2.2.2
- added
batchConvertmethod
v2.1.6
- added
cleanmethod
v2.0.0
- initial version with compatibility to JChem Microservices