Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* MONGODB READY SERVER
*
* Copyright Stanford GSB DARC Team
*
* Created by W. Ross Morrow, Research Computing Specialist
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
'use strict';
// mongodb resources
const _mongodb = require( 'mongodb' );
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* LOCAL DATA
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// local variables
var _config = null ,
_log = null ,
_util = null ,
_client = null ,
_col = null ,
_server = null ;
// (default) MongoDB collection names... pretty nondescript
var mongodbNames = {
"options" : "options" ,
"errors" : "errors" ,
};
const DEFAULT_MONGODB_TIMEOUT = 60000; // one minute
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* PROCESS OPTIONS
*
* Here we can do any option processing, after reading options from the database.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var _proco = null;
const noopOptionProcessing = ( o ) => ( o );
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* (WRAPPED) MONGODB LOAD ROUTINE
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// load client
const loadMongoDBClient = ( config ) => {
return new Promise( ( resolve , reject ) => {
// local variables
var col = { } , waitingForDB , waitingTimer;
// define collection names to search for
if( config.mdb.names ) {
mongodbNames = { ...mongodbNames , ..._config.mdb.names };
}
// why are we waiting? To (try to) run sync instead of async?
// Promises are much nicer. But this is really about printing.
waitingForDB = true;
waitingTimer = setInterval( () => {
_log.info( "Waiting for MongoDB connection..." )
} , 1000 );
// connect to MongoDB
let mdbOptions = { useNewUrlParser: true , useUnifiedTopology: true };
_mongodb.MongoClient.connect( config.mdb.uri , mdbOptions , ( err , client ) => {
if( err ) { return reject(err); }
// write out connection event to log
_log.info( "Connected to MongoDB." );
// clear the print interval
waitingForDB = false; clearInterval( waitingTimer );
// get list of collections to compare against
client.db( config.mdb.db ).listCollections( {} , { nameOnly : true } ).toArray( ( err , items ) => {
// list of collection names
var names = items.map( i => i.name );
if( _config.mdb.names ) {
// this is the list of __desired__ collections __missing__
var missing = Object.values( mongodbNames ).filter( (i) => ( names.indexOf(i) < 0 ) );
// process different possibilities...
if( missing.length === 0 ) { // there are NO missing collections
// define the collection references
Object.keys( mongodbNames ).forEach( c => {
// wrapper reference for each collection "c", named "mongodbNames[c]" in this MongoDB instance
col[c] = client.db( config.mdb.db ).collection( mongodbNames[c] );
// log that we loaded this collection
_log.info( ` Loaded collection "${c}" (from ${mongodbNames[c]})` );
} );
// we're done
resolve( { client : client , col : col } );
} else { // there are SOME missing collections
let done = 0 , todo = Object.keys( mongodbNames ).length;
// define the collection references
Object.keys( mongodbNames ).forEach( c => {
if( missing.indexOf( mongodbNames[c] ) >= 0 ) { // collection does NOT exist
// create collection... is the callback ok? need coordinator?
client.db( config.mdb.db ).createCollection( mongodbNames[c] , ( error , newcol ) => {
// wrapper reference for each collection "c", named "mongodbNames[c]" in this MongoDB instance
col[c] = newcol;
// log that we created this collection
_log.info( ` Created collection "${mongodbNames[c]}" (for ${c})` );
//
done += 1; if( done == todo ) { resolve( {client:client,col:col} ); }
} );
} else {
// wrapper reference for each collection "c", named "mongodbNames[c]" in this MongoDB instance
col[c] = client.db( _config.mdb.db ).collection( mongodbNames[c] );
// log that we loaded this collection
_log.info( ` Loaded collection "${c}" (from ${mongodbNames[c]})` );
//
done += 1; if( done == todo ) { resolve( {client:client,col:col} ); }
}
} else {
names.forEach( c => {
col[c] = client.db( _config.mdb.db ).collection( c )
} );
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
}
} );
// always close the connection when the process exits
process.on( 'exit' , client.close );
} );
} );
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* MONGODB LOAD ROUTINE(S) FOR US
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// function to (re-)load MongoDB given a config. Can be null, in which case the local module version
// will be copied and used
const reloadMongoDB = ( config ) => {
return new Promise( ( resolve , reject ) => {
// make a local copy of the default config, as we don't want to overwrite this
if( ! config ) { config = Object.assign( _config , {} ); }
if( ! config.mdb.uri ) {
return reject( 'Configuration passed to mongodb.reloadMongoDB must have a mongodb field with connection information.' );
}
// make sure to close the current, locally stored client before creating another one
if( _client ) { _client.close(); _client = null; _col = {}; }
var timeout = setTimeout(
// take some kind of action, given that we may not be connected to MongoDB...
() => { reject( "MongoDB reload timed out" ); } ,
( _config.mdb.timeout ? _config.mdb.timeout : DEFAULT_MONGODB_TIMEOUT )
);
// here is where we setup MongoDB, using the more technical routine above
loadMongoDBClient( config )
.then( result => {
// don't timeout
clearTimeout( timeout );
// assign local references to the client and collections (which have "rebooted")
_client = result.client , _col = result.col;
// get options, and proceed from there
var _opts = _client.db( config.mdb.db ).collection( "options" );
// what to do * after * getting options... this will be the callback
// to the "find options" mongodb call below
const withOptions = ( err , item ) => {
if( err ) { return reject( err ); }
config = { ..._proco( item ) , ...config };
resolve( { config : config , col : _col } ); // note we're just passing on...
}
// figure out which options to get, and then call the function defined above
if( ! config.options || config.options === "" ) {
_log.info( "Using most recent options stored in MongoDB" );
_opts.findOne( {} , { sort : { $natural : -1 } } , withOptions );
} else {
_log.info( `Using specific options: ${config.options}` );
_opts.findOne( { "name" : config.options } , { sort : { $natural : -1 } } , withOptions );
}
// rely on callback withOptions passed to the _opts.findOne call above
} ).catch( reject );
} );
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* MODULE EXPORTS
*
* We export a function that expects config, log, and utility objects, and returns functions
* relevant to loading and working with any MongoDB client thus created.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
module.exports = ( c , l , u ) => {
_config = c , _log = l , _util = u; // store local references to the config, log, and util modules
_proco = noopOptionProcessing; // set "trivial" option processing
return {
setOptionProcessor : ( f ) => { _proco = f; } ,
reloadMongoDB : reloadMongoDB ,
getClient : () => { return _client; } ,
getCollections : () => { return _col; } ,
closeClient : () => { _col = {}; if( _client ) { _client.close(); } } ,
};
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2020+, Stanford GSB DARC Team
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */