index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /**
  2. * Module dependencies
  3. */
  4. var assert = require('assert');
  5. var util = require('util');
  6. var url = require('url');
  7. var _ = require('@sailshq/lodash');
  8. var flaverr = require('flaverr');
  9. var qs = require('qs');
  10. var normalizeDatabase = require('./private/normalize-database');
  11. var normalizeUser = require('./private/normalize-user');
  12. var normalizePort = require('./private/normalize-port');
  13. var normalizeHost = require('./private/normalize-host');
  14. var normalizePassword = require('./private/normalize-password');
  15. /**
  16. * normalizeDatastoreConfig()
  17. *
  18. * Normalize (mutate) the provided datastore config dictionary (in-place).
  19. *
  20. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  21. * NOTES:
  22. * • This implementation was originally taken from sails-mongo
  23. * https://github.com/balderdashy/sails-mongo/tree/2816d81359a5846550c90bd1dbfa98967ac13786/lib/private/normalize-datastore-config
  24. * • All modifications are performed in-place!
  25. * • Top-level overrides like `host`, `port`, `user`, etc. are normalized and validated.
  26. * • Top-level overrides like `host`, `port`, `user`, etc. take precedence over whatever is in the URL.
  27. * • Normalized versions of top-level overrides like `host`, `port`, `user`, etc. °°ARE°° sucked into the URL automatically.
  28. * • Recognized URL pieces like the host, port, user, etc. **ARE NOT** attached as top-level props automatically.
  29. * • Recognized URL pieces like the host, port, user, etc. **ARE** validated and normalized individually, rebuilding the URL if necessary.
  30. * • Miscellanous properties **ARE NOT** sucked in to the URL automatically.
  31. * • Miscellaneous querystring opts in the URL °°ARE°° attached automatically as top-level props.
  32. * · They are left as-is in the URL as well.
  33. * · They are treated as strings (e.g. `?foo=0&bar=false` becomes `{foo:'0', bar: 'false'}`)
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  35. * @param {Dictionary} dsConfig
  36. * ˚¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\
  37. * ˙ url: {String?} ::
  38. * ˙ host: {String?} ::
  39. * ˙ port: {String?} ::
  40. * ˙ user: {String?} ::
  41. * ˙ password: {String?} ::
  42. * ˙ database: {String?} ::
  43. *
  44. * @param {Array?} whitelist
  45. * Optional. If provided, this is an array of strings indicating which custom settings
  46. * are recognized and should be allowed. The standard `url`/`host`/`database` etc. are
  47. * always allowed, no matter what. e.g. `['ssl', 'replicaSet']`
  48. *
  49. * @param {String?} expectedProtocolPrefix
  50. * Optional. If specified, this restricts `dsConfig.url` to use a mandatory protocol (e.g. "mongodb")
  51. * If no protocol is included (or if it is simply `://`), then this mandatory protocol
  52. * will be tacked on automatically.
  53. *
  54. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  55. * ╔═╗╦ ╦╔╦╗╦ ╦╦═╗╔═╗
  56. * ╠╣ ║ ║ ║ ║ ║╠╦╝║╣
  57. * ╚ ╚═╝ ╩ ╚═╝╩╚═╚═╝
  58. * FUTURE:
  59. *
  60. * @param {Number} defaultPort
  61. * @param {String} tolerateNoDatabase
  62. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  63. */
  64. module.exports = function normalizeDatastoreConfig (dsConfig, whitelist, expectedProtocolPrefix) {
  65. // Sanity checks
  66. assert(_.isObject(dsConfig), '`dsConfig` should exist and be a dictionary!');
  67. assert(_.isUndefined(whitelist) || _.isArray(whitelist), 'If provided, 2nd argument should be a whitelist of valid custom settings-- e.g. [\'ssl\', \'replicaSet\']');
  68. assert(_.isUndefined(expectedProtocolPrefix) || _.isString(expectedProtocolPrefix), 'If provided, 2nd argument should be a string (e.g. "mongodb") representing the prefix for the expected protocol.');
  69. // Default top-level things.
  70. // If items in BASELINE_PROPS are included in the querystring of the connection url,
  71. // they are allowed to remain, but are not automatically applied at the top-level.
  72. // (Note that this whitelist applies to overrides AND to querystring-encoded values)
  73. var BASELINE_PROPS = [
  74. 'adapter',
  75. 'schema',
  76. 'identity',
  77. 'url',
  78. 'protocolPrefix',
  79. 'user',
  80. 'password',
  81. 'host',
  82. 'port',
  83. 'database',
  84. ];
  85. // Have a look at the datastore config to get an idea of what's there.
  86. var hasUrl = !_.isUndefined(dsConfig.url);
  87. var hasUserOverride = !_.isUndefined(dsConfig.user);
  88. var hasPasswordOverride = !_.isUndefined(dsConfig.password);
  89. var hasHostOverride = !_.isUndefined(dsConfig.host);
  90. var hasPortOverride = !_.isUndefined(dsConfig.port);
  91. var hasDatabaseOverride = !_.isUndefined(dsConfig.database);
  92. // ┌┐┌┌─┐┬─┐┌┬┐┌─┐┬ ┬┌─┐┌─┐ ╔═╗╦ ╦╔═╗╦═╗╦═╗╦╔╦╗╔═╗╔═╗
  93. // ││││ │├┬┘│││├─┤│ │┌─┘├┤ ║ ║╚╗╔╝║╣ ╠╦╝╠╦╝║ ║║║╣ ╚═╗
  94. // ┘└┘└─┘┴└─┴ ┴┴ ┴┴─┘┴└─┘└─┘ ╚═╝ ╚╝ ╚═╝╩╚═╩╚═╩═╩╝╚═╝╚═╝
  95. // ┬ ┌─┐ ┌─┐┌─┐┌┬┐┌┬┐┬┌┐┌┌─┐┌─┐ ┌┬┐┬ ┬┌─┐┌┬┐ ┌┬┐┌─┐┬┌─┌─┐
  96. // │ ├┤ └─┐├┤ │ │ │││││ ┬└─┐ │ ├─┤├─┤ │ │ ├─┤├┴┐├┤
  97. // ooo ┴o└─┘o └─┘└─┘ ┴ ┴ ┴┘└┘└─┘└─┘ ┴ ┴ ┴┴ ┴ ┴ ┴ ┴ ┴┴ ┴└─┘
  98. // ┌─┐┬─┐┌─┐┌─┐┌─┐┌┬┐┌─┐┌┐┌┌─┐┌─┐ ┌─┐┬ ┬┌─┐┬─┐ ┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬─┐┌┬┐
  99. // ├─┘├┬┘├┤ │ ├┤ ││├┤ ││││ ├┤ │ │└┐┌┘├┤ ├┬┘ └─┐ │ ├─┤│││ ││├─┤├┬┘ ││
  100. // ┴ ┴└─└─┘└─┘└─┘─┴┘└─┘┘└┘└─┘└─┘ └─┘ └┘ └─┘┴└─ └─┘ ┴ ┴ ┴┘└┘─┴┘┴ ┴┴└──┴┘
  101. // ┌─┐┬ ┬┬ ┬┌┐┌┬┌─┌─┐ ┌─┐┌─┐ ┌┬┐┬ ┬┌─┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌ ┬ ┬┬─┐┬
  102. // │ ├─┤│ ││││├┴┐└─┐ │ │├┤ │ ├─┤├┤ │ │ │││││││├┤ │ │ ││ ││││ │ │├┬┘│
  103. // └─┘┴ ┴└─┘┘└┘┴ ┴└─┘ └─┘└ ┴ ┴ ┴└─┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘ └─┘┴└─┴─┘
  104. try {
  105. if (hasUserOverride) {
  106. dsConfig.user = normalizeUser(dsConfig.user);
  107. }
  108. if (hasPasswordOverride) {
  109. dsConfig.password = normalizePassword(dsConfig.password);
  110. }
  111. if (hasHostOverride) {
  112. dsConfig.host = normalizeHost(dsConfig.host);
  113. }
  114. if (hasPortOverride) {
  115. dsConfig.port = normalizePort(dsConfig.port);
  116. }
  117. if (hasDatabaseOverride) {
  118. dsConfig.database = normalizeDatabase(dsConfig.database);
  119. }
  120. } catch (e) {
  121. switch (e.code) {
  122. case 'E_BAD_CONFIG': throw flaverr('E_BAD_CONFIG', new Error(
  123. 'Invalid override specified. '+e.message+'\n'+
  124. '--\n'+
  125. 'Please correct this and try again... Or better yet, specify a `url`! '+
  126. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  127. ));
  128. default: throw e;
  129. }
  130. }//</catch>
  131. // Strip out any overrides w/ undefined values.
  132. // (And along the way, check overrides against whitelist if relevant)
  133. var unrecognizedKeys;
  134. _.each(Object.keys(dsConfig), function (key) {
  135. if (_.isUndefined(dsConfig[key])) {
  136. delete dsConfig[key];
  137. }
  138. if (whitelist && !_.contains(whitelist, key) && !_.contains(BASELINE_PROPS, key)) {
  139. unrecognizedKeys = unrecognizedKeys || [];
  140. unrecognizedKeys.push(key);
  141. }
  142. });
  143. if (unrecognizedKeys) {
  144. throw flaverr('E_BAD_CONFIG', new Error(
  145. 'Unrecognized options (`'+unrecognizedKeys+'`) specified as config overrides.\n'+
  146. 'This adapter expects only whitelisted properties.\n'+
  147. '--\n'+
  148. 'See http://sailsjs.com/config/datastores#?the-connection-url for info,\n'+
  149. 'or visit https://sailsjs.com/support for more help.'
  150. ));
  151. }
  152. // ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐ ┌─┐┌┐ ┌─┐┌─┐┌┐┌┌─┐┌─┐ ┌─┐┌─┐ ┬ ┬┬─┐┬
  153. // ├─┤├─┤│││ │││ ├┤ ├─┤├┴┐└─┐├┤ ││││ ├┤ │ │├┤ │ │├┬┘│
  154. // ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘ ┴ ┴└─┘└─┘└─┘┘└┘└─┘└─┘ └─┘└ └─┘┴└─┴─┘
  155. // ┌─ ┌┐ ┌─┐┌─┐┬┌─┬ ┬┌─┐┬─┐┌┬┐┌─┐ ┌─┐┌─┐┌┬┐┌─┐┌─┐┌┬┐┬┌┐ ┬┬ ┬┌┬┐┬ ┬ ─┐
  156. // │─── ├┴┐├─┤│ ├┴┐│││├─┤├┬┘ ││└─┐───│ │ ││││├─┘├─┤ │ │├┴┐││ │ │ └┬┘ ───│
  157. // └─ └─┘┴ ┴└─┘┴ ┴└┴┘┴ ┴┴└──┴┘└─┘ └─┘└─┘┴ ┴┴ ┴ ┴ ┴ ┴└─┘┴┴─┘┴ ┴ ┴ ─┘
  158. // If a URL config value was not given, ensure that all the various pieces
  159. // needed to create one exist. Then build a URL and attach it to the datastore config.
  160. if (!hasUrl) {
  161. // Invent a connection URL on the fly.
  162. var inventedUrl = (expectedProtocolPrefix||'db')+'://';
  163. // ^^FUTURE: Potentially use `protocolPrefix` here if one was specified
  164. // If no .protocolPrefix property is already defined on dsConfig, and an
  165. // explicit expected protocol prefix was specified, then attach that.
  166. if (expectedProtocolPrefix && dsConfig.protocolPrefix === undefined) {
  167. dsConfig.protocolPrefix = expectedProtocolPrefix;
  168. }
  169. // FUTURE: Appropriately URL/URIComponent-encode this stuff as we build the url
  170. // If authentication info was specified, add it:
  171. if (hasPasswordOverride && hasUserOverride) {
  172. inventedUrl += dsConfig.user+':'+dsConfig.password+'@';
  173. }
  174. else if (!hasPasswordOverride && hasUserOverride) {
  175. inventedUrl += dsConfig.user+'@';
  176. }
  177. else if (hasPasswordOverride && !hasUserOverride) {
  178. throw flaverr('E_BAD_CONFIG', new Error(
  179. 'No `url` was specified, so tried to infer an appropriate connection URL from other properties. '+
  180. 'However, it looks like a `password` was specified, but no `user` was specified to go along with it.\n'+
  181. '--\n'+
  182. 'Please remove `password` or also specify a `user`. Or better yet, specify a `url`! '+
  183. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  184. ));
  185. }
  186. // If a host was specified, use it.
  187. if (hasHostOverride) {
  188. inventedUrl += dsConfig.host;
  189. }
  190. else {
  191. throw flaverr('E_BAD_CONFIG', new Error(
  192. 'No `url` was specified, and no appropriate connection URL can be inferred (tried to use '+
  193. '`host: '+util.inspect(dsConfig.host)+'`).\n'+
  194. '--\n'+
  195. 'Please specify a `host`... Or better yet, specify a `url`! '+
  196. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  197. ));
  198. // Or alternatively...
  199. // ```
  200. // inventedUrl += 'localhost';
  201. // ```
  202. }
  203. // If a port was specified, use it.
  204. if (hasPortOverride) {
  205. inventedUrl += ':'+dsConfig.port;
  206. }
  207. // If a database was specified, use it.
  208. if (hasDatabaseOverride) {
  209. inventedUrl += '/'+dsConfig.database;
  210. }
  211. else {
  212. throw flaverr('E_BAD_CONFIG', new Error(
  213. 'No `url` was specified, and no appropriate connection URL can be inferred (tried to use '+
  214. '`database: '+util.inspect(dsConfig.database)+'`).\n'+
  215. '--\n'+
  216. 'Please specify a `database`... Or better yet, specify a `url`! '+
  217. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  218. ));
  219. }
  220. // - - - - - - - - - - - - - - - - - - - - - - - -
  221. // FUTURE: Log a compatibility warning..? Maybe.
  222. //
  223. // > To help standardize configuration for end users, adapter authors
  224. // > are encouraged to support the `url` setting, if conceivable.
  225. // >
  226. // > Read more here:
  227. // > http://sailsjs.com/config/datastores#?the-connection-url
  228. // - - - - - - - - - - - - - - - - - - - - - - - -
  229. // Now save our invented URL as `url`.
  230. dsConfig.url = inventedUrl;
  231. }
  232. // ┌─┐┌─┐┬─┐┌─┐┌─┐ ┬ ┌┐┌┌─┐┬─┐┌┬┐┌─┐┬ ┬┌─┐┌─┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌ ╦ ╦╦═╗╦
  233. // ├─┘├─┤├┬┘└─┐├┤ ┌┼─ ││││ │├┬┘│││├─┤│ │┌─┘├┤ │ │ │││││││├┤ │ │ ││ ││││ ║ ║╠╦╝║
  234. // ┴ ┴ ┴┴└─└─┘└─┘ └┘ ┘└┘└─┘┴└─┴ ┴┴ ┴┴─┘┴└─┘└─┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘ ╚═╝╩╚═╩═╝
  235. // Otherwise, normalize & parse the connection URL.
  236. else {
  237. // Perform a basic sanity check & string coercion.
  238. if (!_.isString(dsConfig.url) || dsConfig.url === '') {
  239. throw flaverr('E_BAD_CONFIG', new Error(
  240. 'Invalid `url` specified. Must be a non-empty string.\n'+
  241. '--\n'+
  242. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  243. ));
  244. }
  245. // Before beginning to do further string parsing, look for a little loophole:
  246. // If the given URL includes a comma, we'll assume it's a complex URL like you get
  247. // from Mongo Atlas and trust that it contains all the info we need.
  248. //
  249. // > But note that this _does not_ handle automatically attaching host, password,
  250. // > port, etc. to the `dsConfig` dictionary. It also doesn't do any verification
  251. // > of these aspects of the URL, which means it could be entirely invalid.
  252. if (dsConfig.url.indexOf(',') > -1) {
  253. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  254. // FUTURE: Implement explicit parsing for this kind of URL instead of just bailing silently.
  255. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  256. return;
  257. }//•
  258. // IWMIH, this is the general case where we're actually going to validate the URL like normal.
  259. // First, make sure there's a protocol:
  260. // We don't actually care about the protocol... but the underlying library (e.g. `mongodb`) might.
  261. // Plus, more importantly, Node's `url.parse()` returns funky results if the argument doesn't
  262. // have one. So we'll add one if necessary.
  263. // > See https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax
  264. var urlToParse;
  265. if (dsConfig.url.match(/^:\/\//)) {
  266. urlToParse = dsConfig.url.replace(/^:\/\//, (expectedProtocolPrefix||'db')+'://');
  267. }
  268. else if (!dsConfig.url.match(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//)) {
  269. urlToParse = (expectedProtocolPrefix||'db')+'://'+dsConfig.url;
  270. }
  271. else {
  272. urlToParse = dsConfig.url;
  273. }
  274. // console.log('dsConfig.url',dsConfig.url);
  275. // console.log('\n\n**********\nurl to parse:',urlToParse, (new Error()).stack);
  276. // Now attempt to parse out the URL's pieces and validate each one.
  277. var parsedConnectionStr = url.parse(urlToParse);
  278. // Ensure a valid protocol.
  279. // Validate that a protocol was found before other pieces
  280. // (otherwise other parsed info could be very weird and wrong)
  281. if (!parsedConnectionStr.protocol) {
  282. throw flaverr('E_BAD_CONFIG', new Error(
  283. 'Could not parse provided URL ('+util.inspect(dsConfig.url,{depth:5})+').\n'+
  284. '(If you continue to experience issues, try checking that the URL begins with an '+
  285. 'appropriate protocol; e.g. `mysql://` or `mongo://`.\n'+
  286. '--\n'+
  287. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  288. ));
  289. }
  290. // If relevant, validate that the RIGHT protocol was found.
  291. if (expectedProtocolPrefix) {
  292. if (parsedConnectionStr.protocol !== expectedProtocolPrefix+':') {
  293. throw flaverr('E_BAD_CONFIG', new Error(
  294. 'Provided URL ('+util.inspect(dsConfig.url,{depth:5})+') has an invalid protocol.\n'+
  295. 'If included, the protocol must be "'+expectedProtocolPrefix+'://".\n'+
  296. '--\n'+
  297. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  298. ));
  299. }
  300. // FUTURE: Potentially also check `.protocolPrefix` prop if one was specified
  301. }//>-
  302. // If the connection string contains a specific protocol, save it in the
  303. // dsconfig -- unless a `protocolPrefix` property is already defined.
  304. if (parsedConnectionStr !== 'db:' && dsConfig.protocolPrefix === undefined) {
  305. dsConfig.protocolPrefix = (parsedConnectionStr.protocol||'').replace(/\:$/,'');
  306. }
  307. // Parse authentication credentials from url, if specified.
  308. var userInUrl;
  309. var passwordInUrl;
  310. if (parsedConnectionStr.auth && _.isString(parsedConnectionStr.auth)) {
  311. var authPieces = parsedConnectionStr.auth.split(/:/);
  312. if (authPieces[0]) {
  313. userInUrl = authPieces[0];
  314. }//>-
  315. if (authPieces[1]) {
  316. passwordInUrl = authPieces[1];
  317. }
  318. }
  319. // Parse the rest of the standard information from the URL.
  320. var hostInUrl = parsedConnectionStr.hostname;
  321. var portInUrl = parsedConnectionStr.port;
  322. var databaseInUrl = parsedConnectionStr.pathname;
  323. // And finally parse the non-standard info from the URL's querystring.
  324. var miscOptsInUrlQs;
  325. try {
  326. miscOptsInUrlQs = qs.parse(parsedConnectionStr.query);
  327. } catch (e) {
  328. throw flaverr('E_BAD_CONFIG', new Error(
  329. 'Could not parse query string from URL: `'+dsConfig.url+'`. '+
  330. 'Details: '+e.stack
  331. ));
  332. }
  333. // Now normalize + restore parsed values back into overrides.
  334. // > • Note that we prefer overrides to URL data here.
  335. // > • Also remember that overrides have already been normalized/validated above.
  336. // > • And finally, also note that we enforce the whitelist for non-standard props
  337. // > here, if relevant. This is so we can provide a clear error message about where
  338. // > the whitelist violation came from.
  339. try {
  340. if (userInUrl && !hasUserOverride) {
  341. dsConfig.user = normalizeUser(userInUrl);
  342. }
  343. if (passwordInUrl && !hasPasswordOverride) {
  344. dsConfig.password = normalizePassword(passwordInUrl);
  345. }
  346. if (hostInUrl && !hasHostOverride) {
  347. dsConfig.host = normalizeHost(hostInUrl);
  348. }
  349. if (portInUrl && !hasPortOverride) {
  350. dsConfig.port = normalizePort(portInUrl);
  351. }
  352. if (databaseInUrl && !hasDatabaseOverride) {
  353. databaseInUrl = _.trim(databaseInUrl, '/');
  354. dsConfig.database = normalizeDatabase(databaseInUrl);
  355. }
  356. _.each(miscOptsInUrlQs, function (val, key) {
  357. if (whitelist && !_.contains(whitelist, key)) {
  358. throw flaverr('E_BAD_CONFIG', new Error(
  359. 'Unrecognized option (`'+key+'`) specified in query string of connection URL.\n'+
  360. '(This adapter expects only standard, whitelisted properties.)\n'+
  361. '--\n'+
  362. 'See http://sailsjs.com/config/datastores#?the-connection-url for info, or visit\n)'+
  363. 'https://sailsjs.com/support for more help.'
  364. ));
  365. }
  366. if (_.contains(BASELINE_PROPS, key)) {
  367. // Currently, we ignore these-- we'll technically put them back in the URL later below,
  368. // but we're careful not to also stick them at the top level (because they would interfere
  369. // with other reserved properties).
  370. //
  371. // We're leaving it this way for now in the interest of flexibility. But there's another way:
  372. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  373. // FUTURE: consider bringing this back instead, for clarity:
  374. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  375. // throw flaverr('E_BAD_CONFIG', new Error(
  376. // 'Unexpected option (`'+key+'`) is NEVER allowed in the query string of a connection URL.\n'+
  377. // '--\n'+
  378. // 'See http://sailsjs.com/config/datastores#?the-connection-url for info, or visit\n)'+
  379. // 'https://sailsjs.com/support for more help.'
  380. // ));
  381. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  382. }
  383. else if (_.isUndefined(dsConfig[key])) {
  384. dsConfig[key] = val;
  385. }
  386. });//</_.each()>
  387. } catch (e) {
  388. switch (e.code) {
  389. case 'E_BAD_CONFIG': throw flaverr('E_BAD_CONFIG', new Error(
  390. 'Could not process connection url. '+e.message+'\n'+
  391. '--\n'+
  392. 'Please correct this and try again.\n'+
  393. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  394. ));
  395. default: throw e;
  396. }
  397. }//</catch>
  398. // And finally, rebuild the URL
  399. var rebuiltUrl = '';
  400. // Start with the protocol...
  401. rebuiltUrl += parsedConnectionStr.protocol+'//';
  402. // FUTURE: Potentially use `protocolPrefix` here if one was specified
  403. // If user/password were specified in the url OR as overrides, use them.
  404. if (dsConfig.user && dsConfig.password) {
  405. rebuiltUrl += dsConfig.user+':'+dsConfig.password+'@';
  406. }
  407. else if (!dsConfig.password && dsConfig.user) {
  408. rebuiltUrl += dsConfig.user+'@';
  409. }
  410. else if (dsConfig.password && !dsConfig.user) {
  411. throw flaverr('E_BAD_CONFIG', new Error(
  412. 'It looks like a `password` was specified, but no `user` was specified to go along with it.\n'+
  413. '--\n'+
  414. 'Please remove `password` or also specify a `user`. '+
  415. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  416. ));
  417. }
  418. // If a host was specified in the url OR as an override, use it.
  419. // (prefer override)
  420. if (dsConfig.host) {
  421. rebuiltUrl += dsConfig.host;
  422. }
  423. else {
  424. throw flaverr('E_BAD_CONFIG', new Error(
  425. 'No host could be determined from configuration (tried to use '+
  426. '`host: '+util.inspect(dsConfig.host)+'`).\n'+
  427. '--\n'+
  428. 'Please specify a `host` or, better yet, include it in the `url`. '+
  429. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  430. ));
  431. // Or alternatively...
  432. // ```
  433. // rebuiltUrl += 'localhost';
  434. // ```
  435. }
  436. // If a port was specified in the url OR as an override, use it.
  437. // (prefer override)
  438. if (dsConfig.port) {
  439. rebuiltUrl += ':'+dsConfig.port;
  440. }
  441. // If a database was specified in the url OR as an override, use it.
  442. // (prefer override)
  443. if (dsConfig.database) {
  444. rebuiltUrl += '/'+dsConfig.database;
  445. }
  446. else {
  447. throw flaverr('E_BAD_CONFIG', new Error(
  448. 'No database could be determined from configuration (tried to use '+
  449. '`database: '+util.inspect(dsConfig.database)+'`).\n'+
  450. '--\n'+
  451. 'Please specify a `database` or, better yet, include it in the `url`. '+
  452. '(See http://sailsjs.com/config/datastores#?the-connection-url for more info.)'
  453. ));
  454. }
  455. // Reattach any non-standard querystring options from the URL.
  456. // > If there were any non-standard options, we'll **LEAVE THEM IN** the URL
  457. // > when we rebuild it. But note that we did fold them into the dsConfig
  458. // > dictionary as well earlier.
  459. var newQs = qs.stringify(miscOptsInUrlQs);
  460. if (newQs.length > 0) {
  461. rebuiltUrl += '?'+newQs;
  462. }
  463. // Now save our rebuilt URL as `url`.
  464. dsConfig.url = rebuiltUrl;
  465. }
  466. };