ソースを参照

model-server: support post params

David Sehnal 5 年 前
コミット
c7cfedb874

+ 1 - 0
package.json

@@ -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",

+ 4 - 2
src/servers/model/query/schemas.ts

@@ -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 = {

+ 33 - 0
src/servers/model/server/api-schema.ts

@@ -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': {},
+                    }
+                }
+            }
         }
     };
 }

+ 20 - 0
src/servers/model/server/api-web.ts

@@ -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);
     }

+ 37 - 26
src/servers/model/server/api.ts

@@ -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(ret[key]);
+            if (p.validation) p.validation(el);
+        }
+        if (typeof el === 'undefined') continue;
+
+        if (p.groupName) {
+            if (typeof ret[p.groupName] === 'undefined') ret[p.groupName] = {};
+            ret[p.groupName][key] = el;
+        } else {
+            ret[key] = el;
         }
     }