Skip to content

Commit fc22b25

Browse files
committed
Make interactive config generation nicer
Don't ask for non-related fields when working with functions (closes #242) Add basic test for function config generation (closes #249)
1 parent 6622375 commit fc22b25

File tree

3 files changed

+156
-101
lines changed

3 files changed

+156
-101
lines changed

src/commands/config.js

Lines changed: 122 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ const volumeValidation = input => {
4444

4545
const writeConfig = (configPath, newConfig) => {
4646
// init config object
47-
const config = {name: newConfig.name, restart: newConfig.restart};
47+
const config = {name: newConfig.name};
48+
if (newConfig.restart && newConfig.restart.length) {
49+
config.restart = newConfig.restart;
50+
}
4851
if (newConfig.domain && newConfig.domain.length) {
4952
config.domain = newConfig.domain;
5053
}
@@ -116,80 +119,7 @@ const writeConfig = (configPath, newConfig) => {
116119
console.log(chalk.green('Config created!'));
117120
};
118121

119-
exports.command = ['config', 'init'];
120-
exports.describe = 'generate new config file for current project';
121-
exports.builder = {
122-
func: {
123-
alias: 'f',
124-
description: 'generate a new config for function deployment',
125-
},
126-
domain: {
127-
alias: ['d', 'domain'],
128-
description: 'sets the domain (enables non-interactive mode)',
129-
},
130-
project: {
131-
alias: ['p', 'project'],
132-
description: 'sets the project name (enables non-interactive mode)',
133-
},
134-
name: {
135-
alias: ['n', 'name'],
136-
description: 'sets the name (enables non-interactive mode)',
137-
},
138-
restart: {
139-
alias: ['r', 'restart'],
140-
description: 'sets the restart option (enables non-interactive mode)',
141-
},
142-
hostname: {
143-
alias: ['hostname'],
144-
description: 'sets the hostname (enables non-interactive mode)',
145-
},
146-
};
147-
exports.handler = async ({func, ...args} = {}) => {
148-
const workdir = process.cwd();
149-
const folderName = path.basename(workdir);
150-
const nonInteractive = Object.keys(args).some(key => args[key].length > 0);
151-
const configPath = path.join(workdir, 'exoframe.json');
152-
let defaultConfig = {
153-
name: folderName,
154-
domain: '',
155-
project: '',
156-
restart: 'on-failure:2',
157-
env: undefined,
158-
labels: undefined,
159-
hostname: '',
160-
template: '',
161-
rateLimit: {
162-
period: '1s',
163-
average: 1,
164-
burst: 5,
165-
},
166-
basicAuth: false,
167-
function: false,
168-
};
169-
try {
170-
fs.statSync(configPath);
171-
console.log(chalk.green('Config already exists! Editing..'));
172-
defaultConfig = JSON.parse(fs.readFileSync(configPath).toString());
173-
} catch (e) {
174-
// check if config didn't exist
175-
if (e.message.includes('ENOENT')) {
176-
console.log('Creating new config..');
177-
} else {
178-
// if there was any parsing error - show message and die
179-
console.log(chalk.red('Error parsing existing config! Please make sure it is valid and try again.'));
180-
return;
181-
}
182-
}
183-
184-
if (func) {
185-
console.log('Creating new config for function deployment..');
186-
// set function flag to true
187-
defaultConfig.function = true;
188-
// write config to file
189-
writeConfig(configPath, defaultConfig);
190-
return;
191-
}
192-
122+
const generatePrompts = defaultConfig => {
193123
// ask user for values
194124
// generate and show choices
195125
const prompts = [];
@@ -201,19 +131,47 @@ exports.handler = async ({func, ...args} = {}) => {
201131
validate,
202132
filter,
203133
});
134+
135+
// function deployment part
136+
prompts.push({
137+
type: 'confirm',
138+
name: 'function',
139+
message: 'Deploy as function? [optional]:',
140+
default: Boolean(defaultConfig.function),
141+
});
142+
prompts.push({
143+
type: 'input',
144+
name: 'functionType',
145+
message: 'Function type [http, worker, trigger, custom]:',
146+
default: defaultConfig.function && defaultConfig.function.type ? defaultConfig.function.type : 'http',
147+
filter,
148+
when: answers => answers.function,
149+
});
150+
prompts.push({
151+
type: 'input',
152+
name: 'functionRoute',
153+
message: 'Function route [optional]:',
154+
default: defaultConfig.function && defaultConfig.function.route ? defaultConfig.function.route : '',
155+
filter,
156+
when: answers => answers.function,
157+
});
158+
159+
// default stuff
204160
prompts.push({
205161
type: 'input',
206162
name: 'domain',
207163
message: 'Domain [optional]:',
208164
default: defaultConfig.domain,
209165
filter,
166+
when: answers => !answers.function,
210167
});
211168
prompts.push({
212169
type: 'input',
213170
name: 'project',
214171
message: 'Project [optional]:',
215172
default: defaultConfig.project,
216173
filter,
174+
when: answers => !answers.function,
217175
});
218176
prompts.push({
219177
type: 'input',
@@ -226,6 +184,7 @@ exports.handler = async ({func, ...args} = {}) => {
226184
: '',
227185
filter,
228186
validate: pairValidation,
187+
when: answers => !answers.function,
229188
});
230189
prompts.push({
231190
type: 'input',
@@ -238,6 +197,7 @@ exports.handler = async ({func, ...args} = {}) => {
238197
: '',
239198
filter,
240199
validate: pairValidation,
200+
when: answers => !answers.function,
241201
});
242202
prompts.push({
243203
type: 'input',
@@ -246,12 +206,14 @@ exports.handler = async ({func, ...args} = {}) => {
246206
default: defaultConfig.volumes ? defaultConfig.volumes.join(', ') : '',
247207
filter,
248208
validate: volumeValidation,
209+
when: answers => !answers.function,
249210
});
250211
prompts.push({
251212
type: 'confirm',
252213
name: 'enableRatelimit',
253214
message: 'Enable rate-limit? [optional]',
254-
default: !!defaultConfig.rateLimit,
215+
default: defaultConfig.rateLimit && defaultConfig.rateLimit.enabled,
216+
when: answers => !answers.function,
255217
});
256218
prompts.push({
257219
type: 'input',
@@ -283,27 +245,31 @@ exports.handler = async ({func, ...args} = {}) => {
283245
message: 'Hostname [optional]:',
284246
default: defaultConfig.hostname,
285247
filter,
248+
when: answers => !answers.function,
286249
});
287250
prompts.push({
288251
type: 'list',
289252
name: 'restart',
290253
message: 'Restart policy [optional]:',
291254
default: defaultConfig.restart,
292-
choices: ['no', 'on-failure:2', 'always'],
255+
choices: ['', 'no', 'on-failure:2', 'always'],
256+
when: answers => !answers.function,
293257
});
294258
prompts.push({
295259
type: 'input',
296260
name: 'template',
297261
message: 'Template [optional]:',
298262
default: defaultConfig.template,
299263
filter,
264+
when: answers => !answers.function,
300265
});
301266
// docker image deployment part
302267
prompts.push({
303268
type: 'confirm',
304269
name: 'deployWithImage',
305270
message: 'Deploy using docker image? [optional]:',
306271
default: Boolean(defaultConfig.image),
272+
when: answers => !answers.function,
307273
});
308274
prompts.push({
309275
type: 'input',
@@ -322,36 +288,13 @@ exports.handler = async ({func, ...args} = {}) => {
322288
when: ({deployWithImage}) => deployWithImage,
323289
});
324290

325-
// function deployment part
326-
prompts.push({
327-
type: 'confirm',
328-
name: 'function',
329-
message: 'Deploy as function? [optional]:',
330-
default: Boolean(defaultConfig.function),
331-
});
332-
prompts.push({
333-
type: 'input',
334-
name: 'functionType',
335-
message: 'Function type [http, worker, trigger, custom]:',
336-
default: defaultConfig.function && defaultConfig.function.type ? defaultConfig.function.type : 'http',
337-
filter,
338-
when: answers => answers.function,
339-
});
340-
prompts.push({
341-
type: 'input',
342-
name: 'functionRoute',
343-
message: 'Function route [optional]:',
344-
default: defaultConfig.function && defaultConfig.function.route ? defaultConfig.function.route : '',
345-
filter,
346-
when: answers => answers.function,
347-
});
348-
349291
// basic auth part
350292
prompts.push({
351293
type: 'confirm',
352294
name: 'basicAuth',
353295
message: 'Add a basic auth user? [optional]:',
354296
default: Boolean(defaultConfig.basicAuth),
297+
when: answers => !answers.function,
355298
});
356299
// prompts for recursive questions
357300
const recursivePrompts = [];
@@ -386,6 +329,83 @@ exports.handler = async ({func, ...args} = {}) => {
386329
}
387330
};
388331

332+
return {prompts, askForUsers};
333+
};
334+
335+
exports.command = ['config', 'init'];
336+
exports.describe = 'generate new config file for current project';
337+
exports.builder = {
338+
func: {
339+
alias: 'f',
340+
description: 'generate a new config for function deployment',
341+
},
342+
domain: {
343+
alias: 'd',
344+
description: 'sets the domain (enables non-interactive mode)',
345+
},
346+
project: {
347+
alias: 'p',
348+
description: 'sets the project name (enables non-interactive mode)',
349+
},
350+
name: {
351+
alias: 'n',
352+
description: 'sets the name (enables non-interactive mode)',
353+
},
354+
restart: {
355+
alias: 'r',
356+
description: 'sets the restart option (enables non-interactive mode)',
357+
},
358+
hostname: {
359+
description: 'sets the hostname (enables non-interactive mode)',
360+
},
361+
};
362+
exports.handler = async ({_, $0, func, ...args} = {}) => {
363+
const workdir = process.cwd();
364+
const folderName = path.basename(workdir);
365+
const nonInteractive = Object.keys(args).some(key => args[key].length > 0);
366+
const configPath = path.join(workdir, 'exoframe.json');
367+
let defaultConfig = {
368+
name: folderName,
369+
domain: '',
370+
project: '',
371+
restart: '',
372+
env: undefined,
373+
labels: undefined,
374+
hostname: '',
375+
template: '',
376+
rateLimit: {
377+
enabled: false,
378+
period: '1s',
379+
average: 1,
380+
burst: 5,
381+
},
382+
basicAuth: false,
383+
function: false,
384+
};
385+
try {
386+
fs.statSync(configPath);
387+
console.log(chalk.green('Config already exists! Editing..'));
388+
defaultConfig = JSON.parse(fs.readFileSync(configPath).toString());
389+
} catch (e) {
390+
// check if config didn't exist
391+
if (e.message.includes('ENOENT')) {
392+
console.log('Creating new config..');
393+
} else {
394+
// if there was any parsing error - show message and die
395+
console.log(chalk.red('Error parsing existing config! Please make sure it is valid and try again.'));
396+
return;
397+
}
398+
}
399+
400+
if (func) {
401+
console.log('Creating new config for function deployment..');
402+
// set function flag to true
403+
defaultConfig.function = true;
404+
// write config to file
405+
writeConfig(configPath, defaultConfig);
406+
return;
407+
}
408+
389409
let newConfig = defaultConfig;
390410

391411
if (nonInteractive) {
@@ -405,6 +425,7 @@ exports.handler = async ({func, ...args} = {}) => {
405425
overrideFromArgument('hostname', args.hostname);
406426

407427
if (!nonInteractive) {
428+
const {prompts, askForUsers} = generatePrompts(defaultConfig);
408429
// get values from user
409430
newConfig = await inquirer.prompt(prompts);
410431

test/__snapshots__/config.test.js.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ Array [
1111
]
1212
`;
1313

14+
exports[`Should generate config file for functions 1`] = `
15+
Array [
16+
Array [
17+
"Creating new config..",
18+
],
19+
Array [
20+
"Creating new config for function deployment..",
21+
],
22+
Array [
23+
"Config created!",
24+
],
25+
]
26+
`;
27+
1428
exports[`Should generate the config with parameters 1`] = `
1529
Array [
1630
Array [

test/config.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,23 @@ test('Should generate config file', done => {
137137
done();
138138
});
139139
});
140+
141+
// test config generation
142+
test('Should generate config file for functions', done => {
143+
// spy on console
144+
const consoleSpy = sinon.spy(console, 'log');
145+
// execute login
146+
config({func: true}).then(() => {
147+
// first check console output
148+
expect(consoleSpy.args).toMatchSnapshot();
149+
// then check config changes
150+
const cfg = yaml.safeLoad(fs.readFileSync(configPath, 'utf8'));
151+
expect(cfg.name).toEqual('exoframe-cli');
152+
expect(cfg.function).toEqual(true);
153+
// restore console
154+
console.log.restore();
155+
// remove corrupted config
156+
fs.unlinkSync(configPath);
157+
done();
158+
});
159+
});

0 commit comments

Comments
 (0)