| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const { join } = require('path'); | 
					
						
							|  |  |  | const fse = require('fs-extra'); | 
					
						
							|  |  |  | const inquirer = require('inquirer'); | 
					
						
							|  |  |  | const execa = require('execa'); | 
					
						
							|  |  |  | const { merge, pick } = require('lodash'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const stopProcess = require('./utils/stop-process'); | 
					
						
							|  |  |  | const { trackUsage } = require('./utils/usage'); | 
					
						
							|  |  |  | const defaultConfigs = require('./utils/db-configs'); | 
					
						
							|  |  |  | const clientDependencies = require('./utils/db-client-dependencies'); | 
					
						
							|  |  |  | const dbQuestions = require('./utils/db-questions'); | 
					
						
							|  |  |  | const createProject = require('./create-project'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = async scope => { | 
					
						
							|  |  |  |   await trackUsage({ event: 'didChooseCustomDatabase', scope }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const configuration = await askDbInfosAndTest(scope).catch(error => { | 
					
						
							|  |  |  |     return trackUsage({ event: 'didNotConnectDatabase', scope, error }).then( | 
					
						
							|  |  |  |       () => { | 
					
						
							|  |  |  |         throw error; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 16:13:36 +02:00
										 |  |  |   console.log(); | 
					
						
							| 
									
										
										
										
											2019-07-08 15:39:08 +02:00
										 |  |  |   console.log('Creating a project with custom database options.'); | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |   await trackUsage({ event: 'didConnectDatabase', scope }); | 
					
						
							|  |  |  |   return createProject(scope, configuration); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const MAX_RETRIES = 5; | 
					
						
							|  |  |  | async function askDbInfosAndTest(scope) { | 
					
						
							|  |  |  |   let retries = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async function loop() { | 
					
						
							|  |  |  |     // else ask for the client name
 | 
					
						
							|  |  |  |     const { client, connection } = await askDatabaseInfos(scope); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const configuration = { | 
					
						
							|  |  |  |       client, | 
					
						
							|  |  |  |       connection, | 
					
						
							| 
									
										
										
										
											2019-07-03 11:20:14 +02:00
										 |  |  |       dependencies: clientDependencies({ scope, client }), | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 17:39:52 +01:00
										 |  |  |     return testDatabaseConnection({ | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |       scope, | 
					
						
							|  |  |  |       configuration, | 
					
						
							|  |  |  |     }) | 
					
						
							| 
									
										
										
										
											2019-07-08 15:39:08 +02:00
										 |  |  |       .then(result => { | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           result && | 
					
						
							|  |  |  |           result.shouldRetry === true && | 
					
						
							|  |  |  |           retries < MAX_RETRIES - 1 | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |           console.log('Retrying...'); | 
					
						
							|  |  |  |           retries++; | 
					
						
							|  |  |  |           return loop(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |       .then( | 
					
						
							|  |  |  |         () => fse.remove(scope.tmpPath), | 
					
						
							|  |  |  |         err => { | 
					
						
							|  |  |  |           return fse.remove(scope.tmpPath).then(() => { | 
					
						
							|  |  |  |             throw err; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ) | 
					
						
							| 
									
										
										
										
											2019-11-14 17:39:52 +01:00
										 |  |  |       .then(() => configuration) | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |       .catch(err => { | 
					
						
							|  |  |  |         if (retries < MAX_RETRIES - 1) { | 
					
						
							| 
									
										
										
										
											2019-07-08 16:13:36 +02:00
										 |  |  |           console.log(); | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |           console.log(`⛔️ Connection test failed: ${err.message}`); | 
					
						
							| 
									
										
										
										
											2019-07-08 16:13:36 +02:00
										 |  |  |           console.log(); | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if (scope.debug) { | 
					
						
							|  |  |  |             console.log('Full error log:'); | 
					
						
							|  |  |  |             console.log(err); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           console.log('Retrying...'); | 
					
						
							|  |  |  |           retries++; | 
					
						
							|  |  |  |           return loop(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         console.log(err); | 
					
						
							|  |  |  |         stopProcess( | 
					
						
							|  |  |  |           `️⛔️ Could not connect to your database after ${MAX_RETRIES} tries. Try to check your database configuration an retry.` | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return loop(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function testDatabaseConnection({ scope, configuration }) { | 
					
						
							|  |  |  |   const { client } = configuration; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (client === 'sqlite') return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await installDatabaseTestingDep({ | 
					
						
							|  |  |  |     scope, | 
					
						
							|  |  |  |     configuration, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const connectivityFile = join( | 
					
						
							|  |  |  |     scope.tmpPath, | 
					
						
							|  |  |  |     'node_modules', | 
					
						
							|  |  |  |     configuration.connection.connector, | 
					
						
							|  |  |  |     'lib', | 
					
						
							|  |  |  |     'utils', | 
					
						
							|  |  |  |     'connectivity.js' | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const tester = require(connectivityFile); | 
					
						
							|  |  |  |   return tester({ scope, connection: configuration.connection }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const SETTINGS_FIELDS = [ | 
					
						
							|  |  |  |   'database', | 
					
						
							|  |  |  |   'host', | 
					
						
							|  |  |  |   'srv', | 
					
						
							|  |  |  |   'port', | 
					
						
							|  |  |  |   'username', | 
					
						
							|  |  |  |   'password', | 
					
						
							|  |  |  |   'filename', | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const OPTIONS_FIELDS = ['authenticationDatabase']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function askDatabaseInfos(scope) { | 
					
						
							|  |  |  |   const { client } = await inquirer.prompt([ | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       type: 'list', | 
					
						
							|  |  |  |       name: 'client', | 
					
						
							|  |  |  |       message: 'Choose your default database client', | 
					
						
							|  |  |  |       choices: ['sqlite', 'postgres', 'mysql', 'mongo'], | 
					
						
							|  |  |  |       default: 'sqlite', | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const responses = await inquirer.prompt( | 
					
						
							|  |  |  |     dbQuestions[client].map(q => q({ scope, client })) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const connection = merge({}, defaultConfigs[client] || {}, { | 
					
						
							|  |  |  |     settings: pick(responses, SETTINGS_FIELDS), | 
					
						
							|  |  |  |     options: pick(responses, OPTIONS_FIELDS), | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (responses.ssl === true) { | 
					
						
							|  |  |  |     if (client === 'mongo') { | 
					
						
							|  |  |  |       connection.options.ssl = true; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       connection.settings.ssl = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     client, | 
					
						
							|  |  |  |     connection, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function installDatabaseTestingDep({ scope, configuration }) { | 
					
						
							| 
									
										
										
										
											2019-07-02 17:38:06 +02:00
										 |  |  |   let packageCmd = scope.useYarn | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |     ? `yarnpkg --cwd ${scope.tmpPath} add` | 
					
						
							|  |  |  |     : `npm install --prefix ${scope.tmpPath}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Manually create the temp directory for yarn
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:38:06 +02:00
										 |  |  |   if (scope.useYarn) { | 
					
						
							| 
									
										
										
										
											2019-06-20 16:38:15 +02:00
										 |  |  |     await fse.ensureDir(scope.tmpPath); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const depArgs = Object.keys(configuration.dependencies).map(dep => { | 
					
						
							|  |  |  |     return `${dep}@${configuration.dependencies[dep]}`; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const cmd = `${packageCmd} ${depArgs.join(' ')}`; | 
					
						
							|  |  |  |   await execa.shell(cmd); | 
					
						
							|  |  |  | } |