UNPKG

3.83 kBPlain TextView Raw
1import InvalidJsonBodyError from '../errors/invalid-json-body-error'
2import { constantCase } from 'change-case'
3import { create as createLogger } from '../common/log'
4import { Config as ConfigSchemaTyping } from '../schemas/Config'
5const log = createLogger('config')
6const schema = require('../schemas/Config.json')
7const {
8 extractDefaultsFromSchema
9} = require('../lib/utils')
10import Ajv = require('ajv')
11
12const ajv = new Ajv()
13
14const ENV_PREFIX = 'CONNECTOR_'
15
16const BOOLEAN_VALUES = {
17 '1': true,
18 'true': true,
19 '0': false,
20 'false': false,
21 '': false
22}
23
24export default class Config extends ConfigSchemaTyping {
25 // TODO: These fields are already all defined in the config schema, however
26 // they are defined as optional and as a result, TypeScript thinks that they
27 // may not be set. However, when we construct a new Config instance, we load
28 // the defaults from the schema, so these *will* always be set. These
29 // declarations make TypeScript happy.
30 public store!: string
31 public quoteExpiry!: number
32 public routeExpiry!: number
33 public minMessageWindow!: number
34 public maxHoldTime!: number
35 public routeBroadcastInterval!: number
36
37 protected _validate: Ajv.ValidateFunction
38 protected _validateAccount: Ajv.ValidateFunction
39
40 constructor () {
41 super()
42
43 this.loadDefaults()
44
45 this._validate = ajv.compile(schema)
46 this._validateAccount = ajv.compile(schema.properties.accounts.additionalProperties)
47 }
48
49 loadDefaults () {
50 Object.assign(this, extractDefaultsFromSchema(schema))
51 }
52
53 loadFromEnv (env?: NodeJS.ProcessEnv) {
54 if (!env) {
55 env = process.env
56 }
57
58 // Copy all env vars starting with ENV_PREFIX into a set so we can check off
59 // the ones we recognize and warn the user about any we don't recognize.
60 const unrecognizedEnvKeys = new Set(
61 Object.keys(env).filter(key => key.startsWith(ENV_PREFIX))
62 )
63
64 const config = {}
65 for (let key of Object.keys(schema.properties)) {
66 const envKey = ENV_PREFIX + constantCase(key)
67 const envValue = env[envKey]
68
69 unrecognizedEnvKeys.delete(envKey)
70
71 if (typeof envValue === 'string') {
72 switch (schema.properties[key].type) {
73 case 'string':
74 config[key] = envValue
75 break
76 case 'object':
77 case 'array':
78 try {
79 config[key] = JSON.parse(envValue)
80 } catch (err) {
81 log.error('unable to parse config. key=%s', envKey)
82 }
83 break
84 case 'boolean':
85 config[key] = BOOLEAN_VALUES[envValue] || false
86 break
87 case 'integer':
88 case 'number':
89 config[key] = Number(envValue)
90 break
91 default:
92 throw new TypeError('Unknown JSON schema type: ' + schema.properties[key].type)
93 }
94 }
95 }
96
97 for (const key of unrecognizedEnvKeys) {
98 log.warn('unrecognized environment variable. key=%s', key)
99 }
100
101 this.validate(config)
102
103 Object.assign(this, config)
104 }
105
106 loadFromOpts (opts: object) {
107 this.validate(opts)
108
109 Object.assign(this, opts)
110 }
111
112 validate (config: object) {
113 if (!this._validate(config)) {
114 const firstError = this._validate.errors && this._validate.errors[0]
115 ? this._validate.errors[0]
116 : { message: 'unknown validation error', dataPath: '' }
117 throw new InvalidJsonBodyError('config failed to validate. error=' + firstError.message + ' dataPath=' + firstError.dataPath, this._validate.errors || [])
118 }
119 }
120
121 validateAccount (id: string, accountInfo: any) {
122 if (!this._validateAccount(accountInfo)) {
123 throw new InvalidJsonBodyError('account config failed to validate. id=' + id, this._validateAccount.errors || [])
124 }
125 }
126
127 get (key: string) {
128 return this[key]
129 }
130}