@pryv/boiler 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.licenser.yml +72 -0
- package/LICENSE +28 -0
- package/README.md +168 -0
- package/examples/configs/default-config.yml +7 -0
- package/examples/configs/extra-config.js +8 -0
- package/examples/configs/extra-config.json +3 -0
- package/examples/configs/extra-config.yml +2 -0
- package/examples/configs/extra-js-async.js +11 -0
- package/examples/configs/test-config.yml +16 -0
- package/examples/doubleinit.js +9 -0
- package/examples/extraConfigs +0 -0
- package/examples/index.js +100 -0
- package/examples/plugins/plugin-async.js +10 -0
- package/examples/plugins/plugin-sync.js +10 -0
- package/examples/remotes/ondisk.json +3 -0
- package/package.json +37 -0
- package/src/airbrake.js +52 -0
- package/src/compute-learn.js +109 -0
- package/src/config.js +441 -0
- package/src/index.js +115 -0
- package/src/lib/nconf-yaml.js +13 -0
- package/src/logging.js +286 -0
package/src/config.js
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Load configuration in the following order (1st prevails)
|
|
8
|
+
*
|
|
9
|
+
* .0 'memory' -> empty, use when doing 'config.set()'
|
|
10
|
+
* .1 'override-file' -> Loaded at from override-config.yml (if present)
|
|
11
|
+
* .2 'test' -> empty, used by test to override any other config parameter
|
|
12
|
+
* .3 'argv' -> Loaded from arguments
|
|
13
|
+
* .4 'env' -> Loaded from environement variables
|
|
14
|
+
* .5 'base' -> Loaded from ${process.env.NODE_ENV}-config.yml (if present) or --config parameter
|
|
15
|
+
* .6 and next -> Loaded from extras
|
|
16
|
+
* .end
|
|
17
|
+
* . 'default-file' -> Loaded from ${baseDir}/default-config.yml
|
|
18
|
+
* . 'defaults' -> Hard coded defaults for logger
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const nconf = require('nconf');
|
|
25
|
+
nconf.formats.yaml = require('./lib/nconf-yaml');
|
|
26
|
+
|
|
27
|
+
const superagent = require('superagent');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Default values for Logger
|
|
31
|
+
*/
|
|
32
|
+
const defaults = {
|
|
33
|
+
logs: {
|
|
34
|
+
console: {
|
|
35
|
+
active: true,
|
|
36
|
+
level: 'info',
|
|
37
|
+
format: {
|
|
38
|
+
color: true,
|
|
39
|
+
time: true,
|
|
40
|
+
aligned: true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
file: {
|
|
44
|
+
active: true,
|
|
45
|
+
path: 'application.log',
|
|
46
|
+
rotation: {
|
|
47
|
+
isActive: false
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
airbrake: {
|
|
51
|
+
active: false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Config manager
|
|
60
|
+
*/
|
|
61
|
+
class Config {
|
|
62
|
+
store;
|
|
63
|
+
logger;
|
|
64
|
+
extraAsync;
|
|
65
|
+
baseConfigDir;
|
|
66
|
+
learnDirectoryAndFilename;
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
this.extraAsync = [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @private
|
|
74
|
+
* Init Config with Files should be called just once when starting an APP
|
|
75
|
+
* @param {Object} options
|
|
76
|
+
* @param {string} appName
|
|
77
|
+
* @param {string} [learnDirectory] - (optional) if set, all .get() calls will be tracked in this files in this directory
|
|
78
|
+
* @param {string} [options.baseConfigDir] - (optional) directory to use to look for configs (default, env)
|
|
79
|
+
* @param {Array<ConfigFile|ConfigPlugin|ConfigData|ConfigRemoteURL|ConfigRemoteURLFromKey>} [options.extras] - (optional) and array of extra files or plugins to load (synchronously or async)
|
|
80
|
+
* @param {Object} logging
|
|
81
|
+
* @returns {Config} this
|
|
82
|
+
*/
|
|
83
|
+
initSync(options, logging) {
|
|
84
|
+
this.appName = options.appName;
|
|
85
|
+
this.learnDirectoryAndFilename = getLearnFilename(options.appName, options.learnDirectory);
|
|
86
|
+
|
|
87
|
+
const logger = this.logger = logging.getLogger('config');
|
|
88
|
+
const store = this.store = new nconf.Provider();
|
|
89
|
+
|
|
90
|
+
const baseConfigDir = this.baseConfigDir = options.baseConfigDir || process.cwd();
|
|
91
|
+
logger.debug('Init with baseConfigDir: ' + baseConfigDir);
|
|
92
|
+
|
|
93
|
+
// 0. memory at top
|
|
94
|
+
store.use('memory');
|
|
95
|
+
|
|
96
|
+
// 1. eventual ovverride-config.yml
|
|
97
|
+
loadFile('override-file', path.resolve(baseConfigDir, 'override-config.yml'));
|
|
98
|
+
|
|
99
|
+
// 2. put a 'test' store up in the list that could be overwitten afterward and override other options
|
|
100
|
+
// override 'test' store with store.add('test', {type: 'literal', store: {....}});
|
|
101
|
+
store.use('test', { type: 'literal', store: {} });
|
|
102
|
+
|
|
103
|
+
// get config from arguments and env variables
|
|
104
|
+
// memory must come first for config.set() to work without loading config files
|
|
105
|
+
// 3. `process.env`
|
|
106
|
+
// 4. `process.argv`
|
|
107
|
+
store.argv({parseValues: true}).env({parseValues: true, separator: '__'});
|
|
108
|
+
|
|
109
|
+
// 5. Values in `${NODE_ENV}-config.yml` or from --config parameter
|
|
110
|
+
let configFile;
|
|
111
|
+
if (store.get('config')) {
|
|
112
|
+
configFile = store.get('config')
|
|
113
|
+
} else if (store.get('NODE_ENV')) {
|
|
114
|
+
configFile = path.resolve(baseConfigDir, store.get('NODE_ENV') + '-config.yml');
|
|
115
|
+
}
|
|
116
|
+
if (configFile) {
|
|
117
|
+
loadFile('base', configFile);
|
|
118
|
+
} else {
|
|
119
|
+
// book 'base' slot
|
|
120
|
+
store.use('base', { type: 'literal', store: {} });
|
|
121
|
+
logger.debug('Booked [base] empty as no --config or NODE_ENV was set');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// load extra config files & plugins
|
|
125
|
+
if (options.extras) {
|
|
126
|
+
for (let extra of options.extras) {
|
|
127
|
+
if (extra.file) {
|
|
128
|
+
loadFile(extra.scope, extra.file);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (extra.plugin) {
|
|
132
|
+
const name = extra.plugin.load(this);
|
|
133
|
+
logger.debug('Loaded plugin: ' + name + ' ' + extra.plugin.load.then);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (extra.data) {
|
|
137
|
+
const conf = extra.key ? {[extra.key]: extra.data} : extra.data;
|
|
138
|
+
store.use(extra.scope, { type: 'literal', store: conf });
|
|
139
|
+
logger.debug('Loaded [' + extra.scope + '] from DATA: ' + (extra.key ? ' under [' + extra.key + ']' : ''));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (extra.url || extra.urlFromKey || extra.fileAsync) {
|
|
143
|
+
// register scope in the chain to keep order of configs
|
|
144
|
+
store.use(extra.scope, { type: 'literal', store: {} });
|
|
145
|
+
logger.debug('Booked [' + extra.scope +'] for async Loading ');
|
|
146
|
+
this.extraAsync.push(extra);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (extra.pluginAsync) {
|
|
150
|
+
logger.debug('Added 1 plugin for async Loading ');
|
|
151
|
+
this.extraAsync.push(extra);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
logger.warn('Unkown extra in config init', extra);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
// .end-1 load default and custom config from configs/default-config.json
|
|
160
|
+
loadFile('default-file', path.resolve(baseConfigDir, 'default-config.yml'));
|
|
161
|
+
|
|
162
|
+
// .end load hard coded defaults
|
|
163
|
+
store.defaults(defaults);
|
|
164
|
+
|
|
165
|
+
// init Logger
|
|
166
|
+
logging.initLoggerWithConfig(this);
|
|
167
|
+
return this;
|
|
168
|
+
|
|
169
|
+
// --- helpers --/
|
|
170
|
+
|
|
171
|
+
function loadFile(scope, filePath) {
|
|
172
|
+
|
|
173
|
+
if (fs.existsSync(filePath)) {
|
|
174
|
+
|
|
175
|
+
if (filePath.endsWith('.js')) { // JS file
|
|
176
|
+
const conf = require(filePath);
|
|
177
|
+
store.use(scope, { type: 'literal', store: conf });
|
|
178
|
+
} else { // JSON or YAML
|
|
179
|
+
const options = { file: filePath }
|
|
180
|
+
if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) { options.format = nconf.formats.yaml }
|
|
181
|
+
store.file(scope, options);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
logger.debug('Loaded [' + scope + '] from file: ' + filePath)
|
|
185
|
+
} else {
|
|
186
|
+
logger.debug('Cannot find file: ' + filePath + ' for scope [' + scope + ']');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async initASync() {
|
|
192
|
+
const store = this.store;
|
|
193
|
+
const logger = this.logger;
|
|
194
|
+
const baseConfigDir = this.baseConfigDir;
|
|
195
|
+
|
|
196
|
+
async function loadUrl(scope, key, url) {
|
|
197
|
+
if (typeof url === 'undefined' || url === null) {
|
|
198
|
+
logger.warn('Null or Undefined Url for [' + scope +']');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let res = null;
|
|
203
|
+
if (isFileUrl(url)) {
|
|
204
|
+
res = loadFromFile(url);
|
|
205
|
+
} else {
|
|
206
|
+
res = await loadFromUrl(url);
|
|
207
|
+
}
|
|
208
|
+
const conf = key ? {[key]: res} : res;
|
|
209
|
+
store.add(scope, { type: 'literal', store: conf });
|
|
210
|
+
logger.debug('Loaded [' + scope + '] from URL: ' + url + (key ? ' under [' + key + ']' : ''));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// load remote config files
|
|
214
|
+
for (let extra of this.extraAsync) {
|
|
215
|
+
if (extra.url) {
|
|
216
|
+
await loadUrl(extra.scope, extra.key, extra.url);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (extra.urlFromKey) {
|
|
220
|
+
const url = store.get(extra.urlFromKey);
|
|
221
|
+
await loadUrl(extra.scope, extra.key, url);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (extra.pluginAsync) {
|
|
226
|
+
const name = await extra.pluginAsync.load(this);
|
|
227
|
+
logger.debug('Loaded async plugin: ' + name);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (extra.fileAsync) {
|
|
232
|
+
const filePath = path.resolve(baseConfigDir, extra.fileAsync);
|
|
233
|
+
|
|
234
|
+
if (! fs.existsSync(filePath)) {
|
|
235
|
+
logger.warn('Cannot find file: ' + filePath + ' for scope [' + extra.scope + ']');
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (! filePath.endsWith('.js')) {
|
|
239
|
+
logger.warn('Cannot only load .js file: ' + filePath + ' for scope [' + extra.scope + ']');
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const conf = await require(filePath)();
|
|
244
|
+
store.add(extra.scope, { type: 'literal', store: conf });
|
|
245
|
+
|
|
246
|
+
logger.debug('Loaded in scope [' + extra.scope + ']async .js file: ' + filePath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
logger.debug('Config fully Loaded');
|
|
251
|
+
saveConfig(this.learnDirectoryAndFilename, this.store);
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Return true if key as value
|
|
257
|
+
* @param {string} key
|
|
258
|
+
* @returns {boolean}
|
|
259
|
+
*/
|
|
260
|
+
has(key) {
|
|
261
|
+
if (! this.store) { throw(new Error('Config not yet initialized'))}
|
|
262
|
+
const value = this.store.get(key);
|
|
263
|
+
return (typeof value !== 'undefined');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Retreive value
|
|
268
|
+
* @param {string} key
|
|
269
|
+
*/
|
|
270
|
+
get(key) {
|
|
271
|
+
if (! this.store) { throw(new Error('Config not yet initialized'))}
|
|
272
|
+
learn(this.learnDirectoryAndFilename, key);
|
|
273
|
+
const value = this.store.get(key);
|
|
274
|
+
if (typeof value === 'undefined') this.logger.debug('get: [' + key +'] is undefined');
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Retreive value and store info that applies
|
|
280
|
+
* @param {string} key
|
|
281
|
+
*/
|
|
282
|
+
getScopeAndValue(key) {
|
|
283
|
+
if (! this.store) { throw(new Error('Config not yet initialized'))};
|
|
284
|
+
for (let scopeName of Object.keys(this.store.stores)) {
|
|
285
|
+
const store = this.store.stores[scopeName];
|
|
286
|
+
const value = store.get(key);
|
|
287
|
+
if (typeof value !== 'undefined') {
|
|
288
|
+
const res = {
|
|
289
|
+
value: value,
|
|
290
|
+
scope: scopeName
|
|
291
|
+
}
|
|
292
|
+
if (store.type === 'file') {
|
|
293
|
+
res.info = 'From file: ' + store.file
|
|
294
|
+
} else {
|
|
295
|
+
info = 'Type: ' + store.type
|
|
296
|
+
}
|
|
297
|
+
return res;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Set value
|
|
305
|
+
* @param {string} key
|
|
306
|
+
* @param {Object} value
|
|
307
|
+
*/
|
|
308
|
+
set(key, value) {
|
|
309
|
+
if (! this.store) { throw(new Error('Config not yet initialized'))}
|
|
310
|
+
this.store.set(key, value);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Inject Test Config and override any other option
|
|
315
|
+
* @param {Object} configObject;
|
|
316
|
+
*/
|
|
317
|
+
injectTestConfig(configObject) {
|
|
318
|
+
this.replaceScopeConfig('test', configObject);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Replace a scope config set
|
|
323
|
+
* @param {string} scope;
|
|
324
|
+
* @param {Object} configObject;
|
|
325
|
+
*/
|
|
326
|
+
replaceScopeConfig(scope, configObject) {
|
|
327
|
+
if (! this.store) { throw(new Error('Config not yet initialized'))}
|
|
328
|
+
this.logger.debug('Replace ['+ scope + '] with: ', configObject);
|
|
329
|
+
this.store.add(scope, {type: 'literal', store: configObject});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = Config;
|
|
335
|
+
|
|
336
|
+
// --- remote and local json ressource loader ---- //
|
|
337
|
+
|
|
338
|
+
const FILE_PROTOCOL = 'file://';
|
|
339
|
+
const FILE_PROTOCOL_LENGTH = FILE_PROTOCOL.length;
|
|
340
|
+
|
|
341
|
+
async function loadFromUrl(serviceInfoUrl ) {
|
|
342
|
+
const res = await superagent.get(serviceInfoUrl);
|
|
343
|
+
return res.body;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function loadFromFile(fileUrl ) {
|
|
347
|
+
const filePath = stripFileProtocol(fileUrl);
|
|
348
|
+
|
|
349
|
+
if (isRelativePath(filePath)) {
|
|
350
|
+
const serviceCorePath = path.resolve(__dirname, '../../../../../'); // assuming /node_modules/@pryv/boiler/src/
|
|
351
|
+
fileUrl = path.resolve(serviceCorePath, filePath);
|
|
352
|
+
fileUrl = 'file://' + fileUrl;
|
|
353
|
+
} else {
|
|
354
|
+
// absolute path, do nothing.
|
|
355
|
+
}
|
|
356
|
+
const res = JSON.parse(
|
|
357
|
+
fs.readFileSync(stripFileProtocol(fileUrl), 'utf8')
|
|
358
|
+
);
|
|
359
|
+
return res;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
function isFileUrl(filePath) {
|
|
364
|
+
return filePath.startsWith(FILE_PROTOCOL);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isRelativePath(filePath) {
|
|
368
|
+
return !path.isAbsolute(filePath);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function stripFileProtocol(filePath) {
|
|
372
|
+
return filePath.substring(FILE_PROTOCOL_LENGTH);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
// -------- learning mode ------- //
|
|
377
|
+
|
|
378
|
+
function getLearnFilename(appName, learnDirectory) {
|
|
379
|
+
if (! learnDirectory) return;
|
|
380
|
+
let i = 0;
|
|
381
|
+
let res;
|
|
382
|
+
do {
|
|
383
|
+
res = path.join(learnDirectory, appName + i );
|
|
384
|
+
i++;
|
|
385
|
+
} while(fs.existsSync(res + '-config.json'));
|
|
386
|
+
return res;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function learn(learnDirectoryAndFilename, key) {
|
|
390
|
+
if (learnDirectoryAndFilename) {
|
|
391
|
+
const caller_line = (new Error()).stack.split('\n')[3]; // get callee name and line
|
|
392
|
+
const index = caller_line.indexOf("at ");
|
|
393
|
+
str = key + ';' + caller_line.slice(index+3, caller_line.length) + '\n';
|
|
394
|
+
fs.appendFileSync(learnDirectoryAndFilename + '-calls.csv', str);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function saveConfig(learnDirectoryAndFilename, store) {
|
|
399
|
+
if (learnDirectoryAndFilename) {
|
|
400
|
+
let i = 0;
|
|
401
|
+
let filename;
|
|
402
|
+
do {
|
|
403
|
+
filename =learnDirectoryAndFilename + '-config.json';
|
|
404
|
+
i++;
|
|
405
|
+
} while(fs.existsSync(filename));
|
|
406
|
+
fs.writeFileSync(filename, JSON.stringify({stores: store.stores, config: store.get()}, null, 2));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @typedef ConfigFile
|
|
413
|
+
* @property {string} scope - scope for nconf hierachical load
|
|
414
|
+
* @property {string} file - the config file (.yml, .json, .js)
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* @typedef ConfigPlugin
|
|
419
|
+
* @property {Object} plugin
|
|
420
|
+
* @property {Function} plugin.load - a function that takes the "nconf store" as argument and returns the "name" of the plugin
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* @typedef ConfigData
|
|
425
|
+
* @property {string} scope - scope for nconf hierachical load
|
|
426
|
+
* @property {string} [key] - (optional) key to load result of url. If null loaded at root of the config
|
|
427
|
+
* @property {object} data - the data to load
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* @typedef ConfigRemoteURL
|
|
432
|
+
* @property {string} scope - scope for nconf hierachical load
|
|
433
|
+
* @property {string} [key] - (optional) key to load result of url. If null loaded at root of the config
|
|
434
|
+
* @property {string} url - the url to the config definition
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* @typedef ConfigRemoteURLFromKey
|
|
438
|
+
* @property {string} scope - scope for nconf hierachical load
|
|
439
|
+
* @property {string} [key] - (optional) key to load result of url. If null override
|
|
440
|
+
* @property {string} urlFromKey - retrieve url from config matching this key
|
|
441
|
+
*/
|
package/src/index.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pryv Boiler module.
|
|
8
|
+
* @module boiler
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const Config = require('./config');
|
|
12
|
+
const logging = require('./logging');
|
|
13
|
+
const airbrake = require('./airbrake');
|
|
14
|
+
|
|
15
|
+
const config = new Config();
|
|
16
|
+
|
|
17
|
+
const boiler = {
|
|
18
|
+
/**
|
|
19
|
+
* notify Airbrake.
|
|
20
|
+
* If initalize, arguments will be passed to airbrake.notify()
|
|
21
|
+
*/
|
|
22
|
+
notifyAirbrake: airbrake.notifyAirbrake,
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* get a Logger
|
|
26
|
+
* @param {string} name
|
|
27
|
+
* @returns {Logger}
|
|
28
|
+
*/
|
|
29
|
+
getLogger: logging.getLogger,
|
|
30
|
+
/**
|
|
31
|
+
* Prefered way to get the configuration
|
|
32
|
+
* @returns {Promise}
|
|
33
|
+
*/
|
|
34
|
+
getConfig: getConfig,
|
|
35
|
+
/**
|
|
36
|
+
* get the configuration.
|
|
37
|
+
* If the configuration is not fully initialized throw an error
|
|
38
|
+
* @param {boolean} warnOnly - Only warns about potential misuse of config
|
|
39
|
+
* @returns {Config}
|
|
40
|
+
*/
|
|
41
|
+
getConfigUnsafe: getConfigUnsafe,
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Init Boiler, should be called just once when starting an APP
|
|
45
|
+
* @param {Object} options
|
|
46
|
+
* @param {string} options.appName - the name of the Application used by Logger and debug
|
|
47
|
+
* @param {string} [options.baseConfigDir] - (optional) directory to use to look for configs
|
|
48
|
+
* @param {Array<ConfigFile|ConfigRemoteURL|ConfigRemoteURLFromKey|ConfigPlugin>} [options.extraConfigs] - (optional) and array of extra files to load
|
|
49
|
+
* @param {Function} [fullyLoadedCallback] - (optional) called when the config is fully loaded
|
|
50
|
+
*/
|
|
51
|
+
init: init
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let logger;
|
|
55
|
+
let configInitialized = false;
|
|
56
|
+
let configInitCalledWithName = null;
|
|
57
|
+
|
|
58
|
+
function init (options, fullyLoadedCallback) {
|
|
59
|
+
if (configInitCalledWithName) {
|
|
60
|
+
logger.warn('Skipping initalization! boiler is already initialized with appName: ' + configInitCalledWithName);
|
|
61
|
+
return boiler;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// append the value of process.env.PRYV_BOILER_SUFFIX if present
|
|
65
|
+
options.appNameWithoutPostfix = options.appName;
|
|
66
|
+
if (process.env.PRYV_BOILER_SUFFIX) options.appName += process.env.PRYV_BOILER_SUFFIX;
|
|
67
|
+
|
|
68
|
+
logging.setGlobalName(options.appName);
|
|
69
|
+
configInitCalledWithName = options.appName;
|
|
70
|
+
config.initSync({
|
|
71
|
+
baseConfigDir: options.baseConfigDir,
|
|
72
|
+
extras: options.extraConfigs,
|
|
73
|
+
appName: options.appNameWithoutPostfix,
|
|
74
|
+
learnDirectory: process.env.CONFIG_LEARN_DIR
|
|
75
|
+
}, logging);
|
|
76
|
+
|
|
77
|
+
logger = logging.getLogger('boiler');
|
|
78
|
+
airbrake.setUpAirbrakeIfNeeded(config, logger);
|
|
79
|
+
|
|
80
|
+
config.initASync().then((config) => {
|
|
81
|
+
configInitialized = true;
|
|
82
|
+
// airbrake config might come from async settings, so we try twice.
|
|
83
|
+
airbrake.setUpAirbrakeIfNeeded(config, logger);
|
|
84
|
+
if (fullyLoadedCallback) fullyLoadedCallback(config);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return boiler;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function getConfig () {
|
|
91
|
+
if (!configInitCalledWithName) {
|
|
92
|
+
throw (new Error('boiler must be initialized with init() before using getConfig()'));
|
|
93
|
+
}
|
|
94
|
+
/* eslint-disable-next-line no-unmodified-loop-condition */
|
|
95
|
+
while (!configInitialized) {
|
|
96
|
+
await new Promise(resolve => setTimeout(resolve, 100)); // wait 100ms
|
|
97
|
+
}
|
|
98
|
+
return config;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getConfigUnsafe (warnOnly) {
|
|
102
|
+
if (!configInitCalledWithName) {
|
|
103
|
+
throw (new Error('boiler must be initialized with init() before using getConfigUnsafe()'));
|
|
104
|
+
}
|
|
105
|
+
if (!configInitialized) {
|
|
106
|
+
if (warnOnly) {
|
|
107
|
+
logger.warn('Warning! config loaded before being fully initialized');
|
|
108
|
+
} else {
|
|
109
|
+
throw (new Error('Config loaded before being fully initialized'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return config;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = boiler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
const yaml = require('js-yaml');
|
|
6
|
+
|
|
7
|
+
exports.stringify = function (obj, options) {
|
|
8
|
+
return yaml.dump(obj, options);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
exports.parse = function (obj, options) {
|
|
12
|
+
return yaml.load(obj, options);
|
|
13
|
+
};
|