Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
634 additions
and
7,482 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,212 @@ | ||
var convict = require('convict') | ||
var fs = require('fs') | ||
const convict = require('convict') | ||
const fs = require('fs') | ||
|
||
// Define a schema | ||
var conf = convict({ | ||
env: { | ||
doc: "The applicaton environment.", | ||
format: ["production", "development", "test", "qa"], | ||
default: "development", | ||
env: "NODE_ENV", | ||
arg: "node_env" | ||
}, | ||
hosts: { | ||
doc: "", | ||
format: Array, | ||
default: [ | ||
{ | ||
host: "127.0.0.1", | ||
port: 27017 | ||
} | ||
] | ||
}, | ||
username: { | ||
doc: "", | ||
const DATABASE_SCHEMA = { | ||
authDatabase: { | ||
doc: 'The database to authenticate against when supplying a username and password', | ||
format: String, | ||
default: "", | ||
env: "DB_USERNAME" | ||
default: '', | ||
envTemplate: 'DB_{database}_AUTH_SOURCE' | ||
}, | ||
password: { | ||
doc: "", | ||
authMechanism: { | ||
doc: 'If no authentication mechanism is specified or the mechanism DEFAULT is specified, the driver will attempt to authenticate using the SCRAM-SHA-1 authentication method if it is available on the MongoDB server. If the server does not support SCRAM-SHA-1 the driver will authenticate using MONGODB-CR.', | ||
format: String, | ||
default: "", | ||
env: "DB_PASSWORD" | ||
default: '', | ||
envTemplate: 'DB_{database}_AUTH_MECHANISM' | ||
}, | ||
authMechanism: { | ||
doc: "If no authentication mechanism is specified or the mechanism DEFAULT is specified, the driver will attempt to authenticate using the SCRAM-SHA-1 authentication method if it is available on the MongoDB server. If the server does not support SCRAM-SHA-1 the driver will authenticate using MONGODB-CR.", | ||
hosts: { | ||
default: '', | ||
doc: 'Database hosts', | ||
format: String, | ||
default: "DEFAULT", | ||
env: "DB_AUTH_MECHANISM" | ||
envTemplate: 'DB_{database}_HOSTS' | ||
}, | ||
authDatabase: { | ||
doc: "The database to authenticate against when supplying a username and password", | ||
id: { | ||
default: '', | ||
doc: 'Database unique identifier', | ||
format: String | ||
}, | ||
maxPoolSize: { | ||
doc: 'The maximum number of connections in the connection pool', | ||
format: Number, | ||
default: 0, | ||
envTemplate: 'DB_{database}_MAX_POOL' | ||
}, | ||
password: { | ||
doc: '', | ||
format: String, | ||
default: "admin", | ||
env: "DB_AUTH_SOURCE" | ||
default: '', | ||
envTemplate: 'DB_{database}_PASSWORD' | ||
}, | ||
database: { | ||
doc: "", | ||
readPreference: { | ||
doc: 'Choose how MongoDB routes read operations to the members of a replica set - see https://docs.mongodb.com/manual/reference/read-preference/', | ||
format: ['primary', 'primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest'], | ||
default: 'secondaryPreferred' | ||
}, | ||
replicaSet: { | ||
doc: '', | ||
format: String, | ||
default: "test", | ||
env: "DB_NAME" | ||
default: '' | ||
}, | ||
ssl: { | ||
doc: "", | ||
doc: '', | ||
format: Boolean, | ||
default: false | ||
}, | ||
replicaSet: { | ||
doc: "", | ||
username: { | ||
doc: '', | ||
format: String, | ||
default: "" | ||
default: '', | ||
envTemplate: 'DB_{database}_USERNAME' | ||
} | ||
} | ||
|
||
const MAIN_SCHEMA = { | ||
database: { | ||
default: 'api', | ||
doc: 'The name of the default database to be used', | ||
env: 'DB_NAME', | ||
format: String | ||
}, | ||
readPreference: { | ||
doc: "Choose how MongoDB routes read operations to the members of a replica set - see https://docs.mongodb.com/manual/reference/read-preference/", | ||
format: ['primary', 'primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest'], | ||
default: 'secondaryPreferred' | ||
databases: { | ||
default: [], | ||
doc: 'Configuration block for each of the databases used throughout the application', | ||
format: Array | ||
}, | ||
enableCollectionDatabases: { | ||
doc: "", | ||
format: Boolean, | ||
default: false | ||
default: false, | ||
doc: 'Whether to use a database specified in the collection endpoint', | ||
format: Boolean | ||
}, | ||
env: { | ||
arg: 'node_env', | ||
default: 'development', | ||
doc: 'The applicaton environment.', | ||
env: 'NODE_ENV', | ||
format: ['production', 'development', 'test', 'qa'] | ||
} | ||
} | ||
|
||
function transformLegacyDatabaseBlock (name, block) { | ||
const hosts = block.hosts.map(({host, port}) => { | ||
return `${host}:${port || 27017}` | ||
}).join(',') | ||
|
||
let newBlock = { | ||
id: name | ||
} | ||
|
||
Object.keys(block).forEach(key => { | ||
if (block[key] !== undefined && block[key] !== '') { | ||
newBlock[key] = block[key] | ||
} | ||
}) | ||
|
||
newBlock.hosts = hosts | ||
|
||
return newBlock | ||
} | ||
|
||
let mainConfig = convict(MAIN_SCHEMA) | ||
|
||
const loadConfig = () => { | ||
// Load environment dependent configuration. | ||
const environment = mainConfig.get('env') | ||
const filePath = `./config/mongodb.${environment}.json` | ||
|
||
try { | ||
const configFile = fs.readFileSync(filePath, 'utf8') | ||
const data = JSON.parse(configFile) | ||
|
||
// Checking for legacy database blocks, which consist of objects | ||
// where keys are database names. | ||
if (data.databases && !Array.isArray(data.databases)) { | ||
data.databases = Object.keys(data.databases).map(name => { | ||
return transformLegacyDatabaseBlock(name, data.databases[name]) | ||
}) | ||
|
||
const exampleConfig = JSON.stringify({ | ||
databases: data.databases | ||
}, null, 2) | ||
|
||
console.warn( | ||
`The current MongoDB configuration uses a \`databases\` object. This syntax has been deprecated and will be removed in a future release. Please update your database configuration to:\n\n${exampleConfig}` | ||
) | ||
} | ||
|
||
data.databases = data.databases || [] | ||
|
||
const defaultDatabaseBlock = data.databases.find(({id}) => { | ||
return id === data.database | ||
}) | ||
|
||
// Checking for legacy syntax, where database details for the default | ||
// database are declared at the root level, instead of inside the | ||
// `databases` property. | ||
if (!defaultDatabaseBlock && Array.isArray(data.hosts)) { | ||
const legacyBlock = { | ||
authDatabase: data.authDatabase, | ||
authMechanism: data.authMechanism, | ||
hosts: data.hosts, | ||
maxPoolSize: data.maxPoolSize, | ||
password: data.password, | ||
readPreference: data.readPreference, | ||
replicaSet: data.replicaSet, | ||
ssl: data.ssl, | ||
username: data.username, | ||
} | ||
const newBlock = transformLegacyDatabaseBlock(data.database, legacyBlock) | ||
|
||
data.databases.push(newBlock) | ||
|
||
const exampleConfig = JSON.stringify({ | ||
databases: [newBlock] | ||
}, null, 2) | ||
|
||
console.warn( | ||
`The current MongoDB configuration uses a \`hosts\` array at the root level. This syntax has been deprecated and will be removed in a future release. Please update your database configuration to:\n\n${exampleConfig}` | ||
) | ||
} | ||
|
||
mainConfig.load(data) | ||
mainConfig.validate() | ||
|
||
// Validating databases. | ||
const databases = mainConfig.get('databases') | ||
|
||
databases.forEach((database, databaseIndex) => { | ||
const databaseConfig = convict(DATABASE_SCHEMA) | ||
|
||
databaseConfig.load(database) | ||
databaseConfig.validate() | ||
|
||
const schema = databaseConfig.getSchema().properties | ||
|
||
// Listening for database-specific environment variables. | ||
// e.g. DB_testdb_USERNAME | ||
Object.keys(schema).forEach(key => { | ||
if (typeof schema[key].envTemplate === 'string') { | ||
const envVar = schema[key].envTemplate.replace( | ||
'{database}', | ||
databaseIndex | ||
) | ||
|
||
if (process.env[envVar]) { | ||
mainConfig.set( | ||
`databases[${databaseIndex}].${key}`, | ||
process.env[envVar] | ||
) | ||
} | ||
} | ||
}) | ||
}) | ||
|
||
return mainConfig | ||
} catch (error) { | ||
console.error(error) | ||
} | ||
}) | ||
} | ||
|
||
// Load environment dependent configuration | ||
var env = conf.get('env') | ||
conf.loadFile('./config/mongodb.' + env + '.json') | ||
loadConfig() | ||
|
||
module.exports = conf | ||
module.exports = mainConfig | ||
module.exports.loadConfig = loadConfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,39 @@ | ||
{ | ||
"hosts": [ | ||
{ | ||
"host": "127.0.0.1", | ||
"port": 27017 | ||
} | ||
], | ||
"username": "", | ||
"password": "", | ||
"database": "testdb", | ||
"ssl": false, | ||
"replicaSet": "", | ||
"enableCollectionDatabases": true, | ||
"databases": { | ||
"secondary": { | ||
"hosts": [ | ||
{ | ||
"host": "127.0.0.1", | ||
"port": 27018 | ||
} | ||
], | ||
"username": "", | ||
"password": "", | ||
"replicaSet": "", | ||
"ssl": false | ||
"databases": [ | ||
{ | ||
"id": "authdb", | ||
"hosts": "127.0.0.1:27017", | ||
"username": "johndoe", | ||
"password": "topsecret" | ||
}, | ||
{ | ||
"id": "defaultdb", | ||
"hosts": "127.0.0.1:27017" | ||
}, | ||
{ | ||
"id": "invaliddb", | ||
"hosts": "127.0.0.1:27019" | ||
}, | ||
"testdb": { | ||
"hosts": [ | ||
{ | ||
"host": "127.0.0.1", | ||
"port": 27017 | ||
} | ||
] | ||
} | ||
} | ||
} | ||
{ | ||
"id": "replicadb", | ||
"hosts": "127.0.0.1:27017", | ||
"replicaSet": "rs0" | ||
}, | ||
{ | ||
"id": "somedb", | ||
"hosts": "127.0.0.1:27017", | ||
"username": "johndoe", | ||
"password": "topsecret" | ||
}, | ||
{ | ||
"id": "secondary", | ||
"hosts": "127.0.0.1:27018" | ||
}, | ||
{ | ||
"id": "testdb", | ||
"hosts": "127.0.0.1:27017" | ||
} | ||
], | ||
"enableCollectionDatabases": true | ||
} |
Oops, something went wrong.