Skip to content
Snippets Groups Projects
Commit 52bf9b87 authored by Alexander Rose's avatar Alexander Rose
Browse files

Merge branch 'master' of https://github.com/molstar/molstar

parents faae40c9 ac8d7121
Branches
No related tags found
No related merge requests found
......@@ -102,6 +102,7 @@
"@types/react-dom": "^16.9.4",
"@types/swagger-ui-dist": "3.0.4",
"argparse": "^1.0.10",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"express": "^4.17.1",
"graphql": "^14.5.8",
......
......@@ -58,25 +58,25 @@ const DefaultModelServerConfig = {
// 'wwpdb'
],
params: {
PDBe: {
UseFileSource: false,
API: {
residuewise_outlier_summary: 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry',
preferred_assembly: 'https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary',
struct_ref_domain: 'https://www.ebi.ac.uk/pdbe/api/mappings/sequence_domains'
},
File: {
residuewise_outlier_summary: 'e:/test/mol-star/model/props/'
}
},
RCSB: {
API: {
assembly_symmetry: 'https://rest-staging.rcsb.org/graphql'
}
},
wwPDB: {
chemCompBondTablePath: ''
}
// PDBe: {
// UseFileSource: false,
// API: {
// residuewise_outlier_summary: 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry',
// preferred_assembly: 'https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary',
// struct_ref_domain: 'https://www.ebi.ac.uk/pdbe/api/mappings/sequence_domains'
// },
// File: {
// residuewise_outlier_summary: 'e:/test/mol-star/model/props/'
// }
// },
// RCSB: {
// API: {
// assembly_symmetry: 'https://rest-staging.rcsb.org/graphql'
// }
// },
// wwPDB: {
// chemCompBondTablePath: ''
// }
}
},
......
......@@ -20,7 +20,8 @@ const InteractionCategories = new Set([
'struct_conn_type',
'pdbx_struct_mod_residue',
'chem_comp_bond',
'atom_sites'
'atom_sites',
'atom_site'
]);
const AssemblyCategories = new Set([
......@@ -39,7 +40,8 @@ const AssemblyCategories = new Set([
'struct_conn_type',
'pdbx_struct_mod_residue',
'chem_comp_bond',
'atom_sites'
'atom_sites',
'atom_site'
]);
export const QuerySchemas = {
......
......@@ -8,7 +8,7 @@ import * as express from 'express'
import * as compression from 'compression'
import * as fs from 'fs'
import * as argparse from 'argparse'
import { ModelServerConfig as ServerConfig, setupConfig } from './config'
import { ModelServerConfig as ServerConfig, setupConfig, ModelServerConfig } from './config'
import { ConsoleLogger } from '../../mol-util/console-logger';
import { PerformanceMonitor } from '../../mol-util/performance-monitor';
import { initWebApi } from './server/api-web';
......@@ -46,20 +46,22 @@ const cmdParser = new argparse.ArgumentParser({
});
cmdParser.addArgument(['--cfg'], { help: 'Config file path.', required: false });
cmdParser.addArgument(['--printcfg'], { help: 'Prints out current config and exits.', required: false, nargs: 0 });
interface CmdArgs {
cfg?: string
cfg?: string,
printcfg?: boolean
}
const cmdArgs = cmdParser.parseArgs() as CmdArgs;
const cfg = cmdArgs.cfg ? JSON.parse(fs.readFileSync(cmdArgs.cfg, 'utf8')) : void 0;
setupConfig(cfg);
function startServer() {
let app = express();
app.use(compression(<any>{ level: 6, memLevel: 9, chunkSize: 16 * 16384, filter: () => true }));
const cfg = cmdArgs.cfg ? JSON.parse(fs.readFileSync(cmdArgs.cfg, 'utf8')) : void 0;
setupConfig(cfg);
initWebApi(app);
const port = process.env.port || ServerConfig.defaultPort;
......@@ -71,8 +73,12 @@ function startServer() {
console.log(``);
}
if (cmdArgs.printcfg !== null) {
console.log(JSON.stringify(ModelServerConfig, null, 2));
} else {
startServer();
if (ServerConfig.shutdownParams && ServerConfig.shutdownParams.timeoutMinutes > 0) {
setupShutdown();
}
}
\ No newline at end of file
......@@ -50,6 +50,13 @@ function getPaths() {
}
function getQueryInfo(def: QueryDefinition) {
const jsonExample: any = {};
def.jsonParams.forEach(p => {
if (!p.exampleValues || !p.exampleValues.length) return;
jsonExample[p.name] = p.exampleValues[0];
});
return {
get: {
tags: ['General'],
......@@ -69,6 +76,32 @@ function getQueryInfo(def: QueryDefinition) {
}
}
}
},
post: {
tags: ['General'],
summary: def.description,
operationId: def.name + '-post',
parameters: [
{ $ref: '#/components/parameters/id' },
...CommonQueryParamsInfo.map(getParamInfo)
],
requestBody: {
content: {
'application/json': {
schema: { type: 'object' },
example: jsonExample
}
}
},
responses: {
200: {
description: def.description,
content: {
'text/plain': {},
'application/octet-stream': {},
}
}
}
}
};
}
......
......@@ -7,6 +7,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as express from 'express';
import * as bodyParser from 'body-parser'
import { ModelServerConfig as Config, ModelServerConfig, mapSourceAndIdToFilename } from '../config';
import { ConsoleLogger } from '../../../mol-util/console-logger';
import { resolveJob } from './query';
......@@ -101,9 +102,26 @@ function mapQuery(app: express.Express, queryName: string, queryDefinition: Quer
responseMap.set(jobId, res);
if (JobManager.size === 1) processNextJob();
});
app.post(makePath('v1/:id/' + queryName), (req, res) => {
const entryId = req.params.id;
const queryParams = req.body;
const commonParams = normalizeRestCommonParams(req.query);
const jobId = JobManager.add({
sourceId: commonParams.data_source || ModelServerConfig.defaultSource,
entryId,
queryName: queryName as any,
queryParams,
options: { modelNums: commonParams.model_nums, binary: commonParams.encoding === 'bcif' }
});
responseMap.set(jobId, res);
if (JobManager.size === 1) processNextJob();
});
}
export function initWebApi(app: express.Express) {
app.use(bodyParser.json({ limit: '1mb' }));
app.get(makePath('static/:format/:id'), async (req, res) => {
const binary = req.params.format === 'bcif';
const id = req.params.id;
......@@ -149,6 +167,8 @@ export function initWebApi(app: express.Express) {
// if (JobManager.size === 1) processNextJob();
// });
app.use(bodyParser.json({ limit: '20mb' }));
for (const q of QueryList) {
mapQuery(app, q.name, q.definition);
}
......
......@@ -24,14 +24,15 @@ export interface QueryParamInfo<T extends string | number = string | number> {
defaultValue?: any,
exampleValues?: any[],
validation?: (v: T) => void,
supportedValues?: string[]
supportedValues?: string[],
groupName?: string
}
export interface QueryDefinition<Params = any> {
name: string,
niceName: string,
exampleId: string, // default is 1cbs
query: (params: any, structure: Structure) => StructureQuery,
query: (params: Params, structure: Structure) => StructureQuery,
description: string,
jsonParams: QueryParamInfo[],
restParams: QueryParamInfo[],
......@@ -53,20 +54,20 @@ export interface CommonQueryParamsInfo {
}
export const AtomSiteSchemaElement = {
label_entity_id: { type: QueryParamType.String },
label_entity_id: { type: QueryParamType.String, groupName: 'atom_site' },
label_asym_id: { type: QueryParamType.String },
auth_asym_id: { type: QueryParamType.String },
label_asym_id: { type: QueryParamType.String, groupName: 'atom_site' },
auth_asym_id: { type: QueryParamType.String, groupName: 'atom_site'},
label_comp_id: { type: QueryParamType.String },
auth_comp_id: { type: QueryParamType.String },
label_seq_id: { type: QueryParamType.Integer },
auth_seq_id: { type: QueryParamType.Integer },
pdbx_PDB_ins_code: { type: QueryParamType.String },
label_comp_id: { type: QueryParamType.String, groupName: 'atom_site' },
auth_comp_id: { type: QueryParamType.String, groupName: 'atom_site' },
label_seq_id: { type: QueryParamType.Integer, groupName: 'atom_site' },
auth_seq_id: { type: QueryParamType.Integer, groupName: 'atom_site' },
pdbx_PDB_ins_code: { type: QueryParamType.String, groupName: 'atom_site' },
label_atom_id: { type: QueryParamType.String },
auth_atom_id: { type: QueryParamType.String },
type_symbol: { type: QueryParamType.String }
label_atom_id: { type: QueryParamType.String, groupName: 'atom_site' },
auth_atom_id: { type: QueryParamType.String, groupName: 'atom_site' },
type_symbol: { type: QueryParamType.String, groupName: 'atom_site' }
}
export interface AtomSiteSchemaElement {
......@@ -92,7 +93,7 @@ const AtomSiteTestJsonParam: QueryParamInfo = {
name: 'atom_site',
type: QueryParamType.JSON,
description: 'Object or array of objects describing atom properties. Names are same as in wwPDB mmCIF dictionary of the atom_site category.',
exampleValues: [{ label_comp_id: 'ALA' }, { label_seq_id: 123, label_asym_id: 'A' }]
exampleValues: [[{ label_seq_id: 30, label_asym_id: 'A' }, { label_seq_id: 31, label_asym_id: 'A' }], { label_comp_id: 'ALA' }]
};
export const AtomSiteTestRestParams = (function() {
......@@ -127,7 +128,9 @@ const QueryMap = {
'atoms': Q<{ atom_site: AtomSiteSchema }>({
niceName: 'Atoms',
description: 'Atoms satisfying the given criteria.',
query: p => Queries.combinators.merge(getAtomsTests(p).map(test => Queries.generators.atoms(test))),
query: p => {
return Queries.combinators.merge(getAtomsTests(p.atom_site).map(test => Queries.generators.atoms(test)));
},
jsonParams: [ AtomSiteTestJsonParam ],
restParams: AtomSiteTestRestParams
}),
......@@ -157,11 +160,11 @@ const QueryMap = {
}],
filter: QuerySchemas.assembly
}),
'residueInteraction': Q<AtomSiteSchema & { radius: number }>({
'residueInteraction': Q<{ atom_site: AtomSiteSchema, radius: number }>({
niceName: 'Residue Interaction',
description: 'Identifies all residues within the given radius from the source residue. Takes crystal symmetry into account.',
query(p) {
const tests = getAtomsTests(p);
const tests = getAtomsTests(p.atom_site);
const center = Queries.combinators.merge(tests.map(test => Queries.generators.atoms({
...test,
entityTest: test.entityTest
......@@ -177,12 +180,11 @@ const QueryMap = {
restParams: [ ...AtomSiteTestRestParams, RadiusParam ],
filter: QuerySchemas.interaction
}),
'residueSurroundings': Q<AtomSiteSchema & { radius: number }>({
'residueSurroundings': Q<{ atom_site: AtomSiteSchema, radius: number }>({
niceName: 'Residue Surroundings',
description: 'Identifies all residues within the given radius from the source residue.',
query(p) {
const tests = getAtomsTests(p);
const center = Queries.combinators.merge(tests.map(test => Queries.generators.atoms(test)));
const center = Queries.combinators.merge(getAtomsTests(p.atom_site).map(test => Queries.generators.atoms(test)));
return Queries.modifiers.includeSurroundings(center, { radius: p.radius, wholeResidues: true });
},
jsonParams: [ AtomSiteTestJsonParam, RadiusParam ],
......@@ -220,20 +222,29 @@ function _normalizeQueryParams(params: { [p: string]: string }, paramList: Query
for (const p of paramList) {
const key = p.name;
const value = params[key];
let el: any;
if (typeof value === 'undefined' || (typeof value !== 'undefined' && value !== null && value['length'] === 0)) {
if (p.required) {
throw `The parameter '${key}' is required.`;
}
if (typeof p.defaultValue !== 'undefined') ret[key] = p.defaultValue;
if (typeof p.defaultValue !== 'undefined') el = p.defaultValue;
} else {
switch (p.type) {
case QueryParamType.JSON: ret[key] = JSON.parse(value); break;
case QueryParamType.String: ret[key] = value; break;
case QueryParamType.Integer: ret[key] = parseInt(value); break;
case QueryParamType.Float: ret[key] = parseFloat(value); break;
case QueryParamType.JSON: el = JSON.parse(value); break;
case QueryParamType.String: el = value; break;
case QueryParamType.Integer: el = parseInt(value); break;
case QueryParamType.Float: el = parseFloat(value); break;
}
if (p.validation) p.validation(el);
}
if (typeof el === 'undefined') continue;
if (p.validation) p.validation(ret[key]);
if (p.groupName) {
if (typeof ret[p.groupName] === 'undefined') ret[p.groupName] = {};
ret[p.groupName][key] = el;
} else {
ret[key] = el;
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment