Browse Source

model-server: wip config refactoring

David Sehnal 5 years ago
parent
commit
f6d11a59a3

+ 129 - 18
src/servers/model/config.ts

@@ -1,10 +1,10 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-const config = {
+const DefaultModelServerConfig = {
     /**
      * Determine if and how long to cache entries after a request.
      */
@@ -50,6 +50,7 @@ const config = {
     /**
      * Provide a property config or a path a JSON file with the config.
      */
+    // TODO: finish this
     customProperties: <import('./property-provider').ModelPropertyProviderConfig | string>{
         sources: [
             // 'pdbe',
@@ -79,23 +80,133 @@ const config = {
         }
     },
 
+
     /**
-     * Maps a request identifier to a filename.
-     *
-     * @param source
-     *   Source of the data.
-     * @param id
-     *   Id provided in the request.
+     * Default source for fileMapping.
      */
-    mapFile(source: string, id: string) {
-        switch (source.toLowerCase()) {
-            case 'pdb': return `e:/test/quick/${id}_updated.cif`;
-            // case 'pdb': return `e:/test/mol-star/model/out/${id}_updated.bcif`;
-            // case 'pdb-bcif': return `c:/test/mol-star/model/out/${id}_updated.bcif`;
-            // case 'pdb-cif': return `c:/test/mol-star/model/out/${id}_updated.cif`;
-            default: return void 0;
-        }
-    }
+    defaultSource: 'pdb-cif' as string,
+
+    /**
+     * Maps a request identifier to a filename given a 'source' and 'id' variables.
+     * 
+     * /static query uses 'pdb-cif' and 'pdb-bcif' source names.
+     */
+    fileMapping: [
+        ['pdb-cif', 'e:/test/quick/${id}_updated.cif'],
+        // ['pdb-bcif', 'e:/test/quick/${id}.bcif'],
+    ] as [string, string][]
 };
 
-export default config;
+export type ModelServerConfig = typeof DefaultModelServerConfig
+export const ModelServerConfig = DefaultModelServerConfig
+
+export let mapSourceAndIdToFilename: (source: string, id: string) => string = () => {
+    throw new Error('call setupConfig to initialize this function');
+}
+
+export function setupConfig(cfg?: ModelServerConfig) {
+    if (cfg) Object.assign(ModelServerConfig, cfg);
+    if (!ModelServerConfig.fileMapping) return;
+
+    mapSourceAndIdToFilename = new Function('source', 'id', [
+        'switch (source.toLowerCase()) {',
+        ...ModelServerConfig.fileMapping.map(([source, path]) => `case '${source.toLowerCase()}': return \`${path}\`;`),
+        '}',
+    ].join('\n')) as any;
+}
+
+// const config = {
+//     /**
+//      * Determine if and how long to cache entries after a request.
+//      */
+//     cacheParams: {
+//         useCache: true,
+//         maxApproximateSizeInBytes: 2 * 1014 * 1024 * 1024, // 2 GB
+//         entryTimeoutInMs: 10 * 60 * 1000 // 10 minutes
+//     },
+
+//     /**
+//      * Node (V8) sometimes exhibits GC related issues  that significantly slow down the execution
+//      * (https://github.com/nodejs/node/issues/8670).
+//      *
+//      * Therefore an option is provided that automatically shuts down the server.
+//      * For this to work, the server must be run using a deamon (i.e. forever.js on Linux
+//      * or IISnode on Windows) so that the server is automatically restarted when the shutdown happens.
+//      */
+//     shutdownParams: {
+//         // 0 for off, server will shut down after this amount of minutes.
+//         timeoutMinutes: 24 * 60 /* a day */,
+
+//         // modifies the shutdown timer by +/- timeoutVarianceMinutes (to avoid multiple instances shutting at the same time)
+//         timeoutVarianceMinutes: 60
+//     },
+
+//     defaultPort: 1337,
+
+//     /**
+//      * Specify the prefix of the API, i.e.
+//      * <host>/<apiPrefix>/<API queries>
+//      */
+//     appPrefix: '/ModelServer',
+
+//     /**
+//      * The maximum time the server dedicates to executing a query.
+//      * Does not include the time it takes to read and export the data.
+//      */
+//     maxQueryTimeInMs: 5 * 1000,
+
+//     /** Maximum number of requests before "server busy" */
+//     maxQueueLength: 30,
+
+//     /**
+//      * Provide a property config or a path a JSON file with the config.
+//      */
+//     customProperties: <import('./property-provider').ModelPropertyProviderConfig | string>{
+//         sources: [
+//             // 'pdbe',
+//             // 'rcsb',
+//             // '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: ''
+//             }
+//         }
+//     },
+
+//     /**
+//      * Maps a request identifier to a filename.
+//      *
+//      * @param source
+//      *   Source of the data.
+//      * @param id
+//      *   Id provided in the request.
+//      */
+//     mapFile(source: string, id: string) {
+//         switch (source.toLowerCase()) {
+//             case 'pdb': return `e:/test/quick/${id}_updated.cif`;
+//             // case 'pdb': return `e:/test/mol-star/model/out/${id}_updated.bcif`;
+//             // case 'pdb-bcif': return `c:/test/mol-star/model/out/${id}_updated.bcif`;
+//             // case 'pdb-cif': return `c:/test/mol-star/model/out/${id}_updated.cif`;
+//             default: return void 0;
+//         }
+//     }
+// };
+
+// export default config;

+ 1 - 1
src/servers/model/property-provider.ts

@@ -6,7 +6,7 @@
 
 import * as fs from 'fs'
 import { Model } from '../../mol-model/structure';
-import Config from './config';
+import { ModelServerConfig as Config } from './config';
 import { ConsoleLogger } from '../../mol-util/console-logger';
 
 // TODO enable dynamic imports again

+ 3 - 13
src/servers/model/server.ts

@@ -6,7 +6,7 @@
 
 import * as express from 'express'
 import * as compression from 'compression'
-import ServerConfig from './config'
+import { ModelServerConfig as ServerConfig, setupConfig } from './config'
 import { ConsoleLogger } from '../../mol-util/console-logger';
 import { PerformanceMonitor } from '../../mol-util/performance-monitor';
 import { initWebApi } from './server/api-web';
@@ -45,20 +45,10 @@ function startServer() {
     let app = express();
     app.use(compression(<any>{ level: 6, memLevel: 9, chunkSize: 16 * 16384, filter: () => true }));
 
-    // app.get(ServerConfig.appPrefix + '/documentation', (req, res) => {
-    //     res.writeHead(200, { 'Content-Type': 'text/html' });
-    //     res.write(Documentation.getHTMLDocs(ServerConfig.appPrefix));
-    //     res.end();
-    // });
+    // TODO: read JSON file
+    setupConfig();
 
     initWebApi(app);
-
-    // app.get('*', (req, res) => {
-    //     res.writeHead(200, { 'Content-Type': 'text/html' });
-    //     res.write(Documentation.getHTMLDocs(ServerConfig.appPrefix));
-    //     res.end();
-    // });
-
     app.listen(port);
 }
 

+ 1 - 1
src/servers/model/server/api-schema.ts

@@ -6,7 +6,7 @@
 
 import VERSION from '../version'
 import { QueryParamInfo, QueryParamType, QueryDefinition, CommonQueryParamsInfo, QueryList } from './api';
-import ServerConfig from '../config';
+import { ModelServerConfig as ServerConfig } from '../config';
 
 export const shortcutIconLink = `<link rel='shortcut icon' href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAnUExURQAAAMIrHrspHr0oH7soILonHrwqH7onILsoHrsoH7soH7woILwpIKgVokoAAAAMdFJOUwAQHzNxWmBHS5XO6jdtAmoAAACZSURBVDjLxZNRCsQgDAVNXmwb9f7nXZEaLRgXloXOhwQdjMYYwpOLw55fBT46KhbOKhmRR2zLcFJQj8UR+HxFgArIF5BKJbEncC6NDEdI5SatBRSDJwGAoiFDONrEJXWYhGMIcRJGCrb1TOtDahfUuQXd10jkFYq0ViIrbUpNcVT6redeC1+b9tH2WLR93Sx2VCzkv/7NjfABxjQHksGB7lAAAAAASUVORK5CYII=' />`
 

+ 3 - 3
src/servers/model/server/api-web.ts

@@ -7,7 +7,7 @@
 import * as fs from 'fs';
 import * as path from 'path';
 import * as express from 'express';
-import Config from '../config';
+import { ModelServerConfig as Config, ModelServerConfig, mapSourceAndIdToFilename } from '../config';
 import { ConsoleLogger } from '../../../mol-util/console-logger';
 import { resolveJob } from './query';
 import { JobManager } from './jobs';
@@ -92,7 +92,7 @@ function mapQuery(app: express.Express, queryName: string, queryDefinition: Quer
         const queryParams = normalizeRestQueryParams(queryDefinition, req.query);
         const commonParams = normalizeRestCommonParams(req.query);
         const jobId = JobManager.add({
-            sourceId: commonParams.data_source || 'pdb',
+            sourceId: commonParams.data_source || ModelServerConfig.defaultSource,
             entryId,
             queryName: queryName as any,
             queryParams,
@@ -107,7 +107,7 @@ export function initWebApi(app: express.Express) {
     app.get(makePath('static/:format/:id'), async (req, res) => {
         const binary = req.params.format === 'bcif';
         const id = req.params.id;
-        const fn = Config.mapFile(binary ? 'pdb-bcif' : 'pdb-cif', id);
+        const fn = mapSourceAndIdToFilename(binary ? 'pdb-bcif' : 'pdb-cif', id);
         if (!fn || !fs.existsSync(fn)) {
             res.status(404);
             res.end();

+ 1 - 1
src/servers/model/server/cache.ts

@@ -6,7 +6,7 @@
 
 import { ConsoleLogger } from '../../../mol-util/console-logger'
 import { LinkedList } from '../../../mol-data/generic';
-import ServerConfig from '../config';
+import { ModelServerConfig as ServerConfig } from '../config';
 
 interface CacheEntry<T> {
     key: string,

+ 1 - 1
src/servers/model/server/query.ts

@@ -12,7 +12,7 @@ import { Progress } from '../../../mol-task';
 import { now } from '../../../mol-util/now';
 import { ConsoleLogger } from '../../../mol-util/console-logger';
 import { PerformanceMonitor } from '../../../mol-util/performance-monitor';
-import Config from '../config';
+import { ModelServerConfig as Config } from '../config';
 import Version from '../version';
 import { Job } from './jobs';
 import { createStructureWrapperFromJob, StructureWrapper, resolveStructures } from './structure-wrapper';

+ 2 - 2
src/servers/model/server/structure-wrapper.ts

@@ -7,7 +7,7 @@
 import { Structure, Model } from '../../../mol-model/structure';
 import { PerformanceMonitor } from '../../../mol-util/performance-monitor';
 import { Cache } from './cache';
-import Config from '../config';
+import { ModelServerConfig as Config, mapSourceAndIdToFilename } from '../config';
 import { CIF, CifFrame, CifBlock } from '../../../mol-io/reader/cif'
 import * as util from 'util'
 import * as fs from 'fs'
@@ -109,7 +109,7 @@ export async function readDataAndFrame(filename: string, key?: string): Promise<
 }
 
 export async function readStructureWrapper(key: string, sourceId: string | '_local_', entryId: string, propertyProvider: ModelPropertiesProvider | undefined) {
-    const filename = sourceId === '_local_' ? entryId : Config.mapFile(sourceId, entryId);
+    const filename = sourceId === '_local_' ? entryId : mapSourceAndIdToFilename(sourceId, entryId);
     if (!filename) throw new Error(`Cound not map '${key}' to a valid filename.`);
     if (!fs.existsSync(filename)) throw new Error(`Could not find source file for '${key}'.`);