UNPKG

5.78 kBJavaScriptView Raw
1const UnauthorizedError = require('../errors/UnauthorizedError')
2const logger = require('../logger')
3const { SETTINGS_FOLDER, EXTENSIONS_FOLDER, PIPELINES_FOLDER, TRUSTED_PIPELINES_FOLDER, THEMES_FOLDER } = require('../app/Constants')
4const inquirer = require('inquirer')
5const path = require('path')
6const fsEx = require('fs-extra')
7const utils = require('../utils/utils')
8
9const FOLDERS = [SETTINGS_FOLDER, EXTENSIONS_FOLDER, PIPELINES_FOLDER, TRUSTED_PIPELINES_FOLDER, THEMES_FOLDER]
10
11class InitAction {
12 /**
13 * @param {Command} caporal
14 * @param {AppSettings} appSettings
15 * @param {UserSettings} userSettings
16 * @param {DcHttpClient} dcHttpClient
17 */
18 static register (caporal, appSettings, userSettings, dcHttpClient) {
19 caporal
20 .command('init')
21 .description('Init the sdk')
22 .option('--appId <appId>', 'set the Sandbox App ID you want to initialize')
23 .option('--force', 'won\t ask for permission for overwriting previously set application')
24 .action(async (args, options) => {
25 try {
26 // istanbul ignore next
27 const appId = await new InitAction(appSettings, userSettings, dcHttpClient).run(options)
28 if (!appId) return logger.info('Init was aborted by user')
29 logger.info(`The Application "${appId}" was successfully initialized`)
30 } catch (err) /* istanbul ignore next */ {
31 logger.error(err.message)
32 process.exit(1)
33 }
34 })
35 }
36
37 /**
38 * @param {AppSettings} appSettings
39 * @param {UserSettings} userSettings
40 * @param {DcHttpClient} dcHttpClient
41 */
42 constructor (appSettings, userSettings, dcHttpClient) {
43 this.appSettings = appSettings
44 this.userSettings = userSettings
45 this.dcHttpClient = dcHttpClient
46 }
47
48 /**
49 * @param {object} options
50 * @return {string|boolean} The app ID or boolean false if the action was aborted.
51 * @throws {Error} if initialization fails.
52 */
53 async run (options) {
54 this.options = options
55 let appSettingsExists = true
56
57 // may throw an error if the user is not logged in
58 await this.userSettings.validate()
59
60 try {
61 await this.appSettings.validate()
62 } catch (err) {
63 appSettingsExists = false
64 logger.debug('App settings are not present')
65 }
66
67 if (appSettingsExists) {
68 if (!this.options.force) {
69 const ignoreRunningProcesses = await this.checkForRunningProcesses(inquirer.prompt)
70 if (!ignoreRunningProcesses) return false
71
72 const ignoreAlreadyInitialized = await this.permitDeletion(inquirer.prompt, await this.appSettings.getId())
73 if (!ignoreAlreadyInitialized) return false
74 }
75
76 const appId = await this.getAppId(inquirer.prompt)
77 return this.initApplication(appId, this.userSettings, true)
78 }
79
80 const appId = await this.getAppId(inquirer.prompt)
81 if (!appId) throw new Error('Sandbox App ID (--appId) is invalid')
82
83 await Promise.all(FOLDERS.map(folder => {
84 if (process.env.APP_PATH) folder = path.join(process.env.APP_PATH, folder)
85 return fsEx.ensureDir(folder)
86 }))
87
88 return this.initApplication(appId, this.userSettings, false)
89 }
90
91 /**
92 * @param {Prompt} prompt
93 * @return {string} The app ID.
94 */
95 async getAppId (prompt) {
96 if (this.options.appId) return this.options.appId
97
98 const answers = await prompt([{
99 type: 'input',
100 name: 'appId',
101 message: 'Enter your Sandbox App ID:'
102 }])
103
104 return answers.appId
105 }
106
107 /**
108 * @param {Prompt} prompt
109 * @param {String} appId
110 * @return {boolean} True if already initialized project should be re-initialized, false otherwise.
111 */
112 async permitDeletion (prompt, appId) {
113 const contentToBeDeleted = [
114 ' - project related settings',
115 ' - generated extension/theme config files',
116 ' - active (trusted) pipelines'
117 ].join('\n')
118
119 logger.info(`The application ${appId} is currently initialized.\n\nReinit will delete the following project content:\n${contentToBeDeleted}`)
120 const answers = await prompt({
121 type: 'input',
122 name: 'overwrite',
123 default: 'n',
124 message: `Do you really want to overwrite your current application (${appId})? (y/N)`
125 })
126
127 return answers.overwrite.charAt(0).toLowerCase() === 'y'
128 }
129
130 /**
131 * @param {Prompt} prompt
132 * @return {boolean} True if no processes running or reinit should be done anyway, false otherwise.
133 */
134 async checkForRunningProcesses (prompt) {
135 const processes = []
136 if (await utils.getProcessId('backend', SETTINGS_FOLDER)) processes.push('backend')
137 if (await utils.getProcessId('frontend', SETTINGS_FOLDER)) processes.push('frontend')
138
139 if (processes.length > 0) {
140 const answers = await prompt({
141 type: 'input',
142 name: 'ignore',
143 default: 'n',
144 message: `Processe(s): ${processes.join(', ')} running, reinit can cause problems in these processes; ignore? (y,N)`
145 })
146
147 return answers.ignore.charAt(0).toLowerCase() === 'y'
148 }
149
150 return true
151 }
152
153 /**
154 * @param {String} appId
155 * @param {UserSettings} userSettings
156 * @param {boolean} reset
157 * @return {string} The app ID.
158 */
159 async initApplication (appId, userSettings, reset) {
160 try {
161 await this.dcHttpClient.getApplicationData(appId)
162 if (reset) await utils.resetProject()
163 await this.appSettings.setId(appId)
164
165 return appId
166 } catch (err) {
167 logger.debug(err)
168 if (err instanceof UnauthorizedError) throw new Error('You\'re not logged in! Please run `sgconnect login` again.')
169 throw new Error(`The application ${appId} is not available or permissions are missing (message: ${err.message}). Please check the application at developer.shopgate.com!`)
170 }
171 }
172}
173
174module.exports = InitAction