Skip to content

Commit

Permalink
improvement(cli): updated ui mode routine
Browse files Browse the repository at this point in the history
  • Loading branch information
hoonoh committed Oct 22, 2019
1 parent 38b8b3e commit b5eb90c
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 239 deletions.
6 changes: 3 additions & 3 deletions src/cli-ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('cli-ui', () => {
expect(result.familyType.sort()).toEqual(Array.from(instanceFamilyCompute).sort());
expect(result.size.sort()).toEqual(getFamilySize(['compute']).sort());
expect(result.productDescription).toHaveLength(0);
expect(result.maxPrice).toBeFalsy();
expect(result.priceMax).toBeFalsy();
expect(result.limit).toBeFalsy();
expect(result.accessKeyId).toBeFalsy();
}
Expand Down Expand Up @@ -89,7 +89,7 @@ describe('cli-ui', () => {
);
expect(result.size.sort()).toEqual(getFamilySize(['general', 'memory']).sort());
expect(result.productDescription).toHaveLength(0);
expect(result.maxPrice).toBeFalsy();
expect(result.priceMax).toBeFalsy();
expect(result.limit).toBeFalsy();
expect(result.accessKeyId).toBeFalsy();
}
Expand Down Expand Up @@ -126,7 +126,7 @@ describe('cli-ui', () => {
expect(result.familyType.sort()).toEqual(['c4', 'r5', 'f1'].sort());
expect(result.size.sort()).toEqual(['nano', 'micro', 'small', 'medium', 'large'].sort());
expect(result.productDescription.sort()).toEqual(['Linux/UNIX', 'SUSE Linux'].sort());
expect(result.maxPrice).toEqual(0.5);
expect(result.priceMax).toEqual(0.5);
expect(result.limit).toEqual(21);
expect(result.accessKeyId).toEqual('accessKeyId');
expect(result.secretAccessKey).toEqual('secretAccessKey');
Expand Down
314 changes: 157 additions & 157 deletions src/cli-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Answer2 = {
familyType: InstanceFamilyType[];
size: InstanceSize[];
productDescription: ProductDescription[];
maxPrice: number;
priceMax: number;
limit: number;
accessKeyId: string;
secretAccessKey: string;
Expand All @@ -31,170 +31,170 @@ const onCancel = (): void => {
process.exit();
};

export const ui = async (): Promise<Answers | undefined> => {
try {
/* istanbul ignore next */
const inject = process.env.UI_INJECT ? JSON.parse(process.env.UI_INJECT) : undefined;
/* istanbul ignore next */
export const ui = async (): Promise<Answers> => {
let inject: (string[] | string | number)[] | undefined;
/* istanbul ignore next */
if (process.env.UI_INJECT) {
inject = JSON.parse(process.env.UI_INJECT);
if (inject) prompt.inject(inject.splice(0, 2));
const question1 = [
{
type: 'autocompleteMultiselect',
name: 'region',
message: 'Select regions (select none for default regions)',
instructions: false,
choices: allRegions.reduce(
(list, region) => {
list.push({
title: `${regionNames[region]} - ${region}`,
value: region,
});
return list;
},
[] as Choice[],
),
},
{
type: 'multiselect',
name: 'family',
message: 'Select EC2 Family',
instructions: false,
choices: Object.keys(instanceFamily).reduce(
(list, family) => {
list.push({
title: family,
value: family,
});
return list;
},
[] as Choice[],
),
},
];
}

const answer1: Answer1 = await prompt(question1, { onCancel });
const question1 = [
{
type: 'autocompleteMultiselect',
name: 'region',
message: 'Select regions (select none for default regions)',
instructions: false,
choices: allRegions.reduce(
(list, region) => {
list.push({
title: `${regionNames[region]} - ${region}`,
value: region,
});
return list;
},
[] as Choice[],
),
},
{
type: 'multiselect',
name: 'family',
message: 'Select EC2 Family',
instructions: false,
choices: Object.keys(instanceFamily).reduce(
(list, family) => {
list.push({
title: family,
value: family,
});
return list;
},
[] as Choice[],
),
},
];

const familyTypePreSelectedSet = new Set<InstanceFamilyType>();
const sizePreSelectedSet = new Set<InstanceSize>();
if (answer1.family.length > 0) {
answer1.family.forEach(family => {
instanceFamily[family].forEach((type: InstanceFamilyType) => {
familyTypePreSelectedSet.add(type);
allInstances
.filter(instance => instance.startsWith(type))
.forEach(instance => {
sizePreSelectedSet.add(instance.split('.').pop() as InstanceSize);
});
});
});
}
const answer1: Answer1 = await prompt(question1, { onCancel });

const familyTypePreSelected = Array.from(familyTypePreSelectedSet);
const sizePreSelected = Array.from(sizePreSelectedSet);
const familyTypePreSelectedSet = new Set<InstanceFamilyType>();
const sizePreSelectedSet = new Set<InstanceSize>();
if (answer1.family.length > 0) {
answer1.family.forEach(family => {
instanceFamily[family].forEach((type: InstanceFamilyType) => {
familyTypePreSelectedSet.add(type);
allInstances
.filter(instance => instance.startsWith(type))
.forEach(instance => {
sizePreSelectedSet.add(instance.split('.').pop() as InstanceSize);
});
});
});
}

const generateFamilyHint = (type: string): string => {
const familyCopy = answer1.family.concat();
if (familyCopy.length > 0) {
const last = familyCopy.pop();
const list = familyCopy.length ? `${familyCopy.join(', ')} and ${last}` : last;
return `Instance family ${type} related to '${list}' families are pre-selected`;
}
return 'select none to include all';
};
const familyTypePreSelected = Array.from(familyTypePreSelectedSet);
const sizePreSelected = Array.from(sizePreSelectedSet);

/* istanbul ignore next */
if (inject) {
familyTypePreSelected.forEach(type => {
if (typeof inject[0] === 'object' && !inject[0].includes(type)) inject[0].push(type);
});
sizePreSelected.forEach(size => {
if (typeof inject[1] === 'object' && !inject[1].includes(size)) inject[1].push(size);
});
const generateFamilyHint = (type: string): string => {
const familyCopy = answer1.family.concat();
if (familyCopy.length > 0) {
const last = familyCopy.pop();
const list = familyCopy.length ? `${familyCopy.join(', ')} and ${last}` : last;
return `Instance family ${type} related to '${list}' families are pre-selected`;
}
return 'select none to include all';
};

const question2 = [
{
type: 'autocompleteMultiselect',
name: 'familyType',
message: 'Select EC2 Family Type',
hint: generateFamilyHint('types'),
instructions: false,
choices: instanceFamilyTypes.reduce(
(list, familyType) => {
list.push({
title: familyType,
value: familyType,
selected: familyTypePreSelected.includes(familyType),
});
return list;
},
[] as (Choice | { selected: boolean })[],
),
},
{
type: 'autocompleteMultiselect',
name: 'size',
message: 'Select EC2 Family Size',
hint: generateFamilyHint('sizes'),
instructions: false,
choices: instanceSizes.reduce(
(list, size) => {
list.push({
title: size,
value: size,
selected: sizePreSelected.includes(size),
});
return list;
},
[] as (Choice | { selected: boolean })[],
),
},
{
type: 'autocompleteMultiselect',
name: 'productDescription',
message: `Select EC2 Product description (select none to include all)`,
instructions: false,
choices: allProductDescriptions.map(desc => ({
title: desc,
value: desc,
})),
},
{
type: 'number',
name: 'maxPrice',
message: `Select maximum price`,
initial: 5,
float: true,
round: 4,
increment: 0.0001,
min: 0.0015,
},
{
type: 'number',
name: 'limit',
message: `Select result limit`,
initial: 20,
min: 1,
},
{
type: 'text',
name: 'accessKeyId',
message: `Enter AWS accessKeyId (optional)`,
},
{
type: (prev: string | undefined): string | undefined => (prev ? 'invisible' : undefined),
name: 'secretAccessKey',
message: `Enter AWS secretAccessKey`,
},
];
/* istanbul ignore next */
if (inject) {
familyTypePreSelected.forEach(type => {
if (inject && typeof inject[0] === 'object' && !inject[0].includes(type))
inject[0].push(type);
});
sizePreSelected.forEach(size => {
if (inject && typeof inject[1] === 'object' && !inject[1].includes(size))
inject[1].push(size);
});
}

/* istanbul ignore next */
if (inject) prompt.inject(inject);
const answer2: Answer2 = await prompt(question2, { onCancel });
const question2 = [
{
type: 'autocompleteMultiselect',
name: 'familyType',
message: 'Select EC2 Family Type',
hint: generateFamilyHint('types'),
instructions: false,
choices: instanceFamilyTypes.reduce(
(list, familyType) => {
list.push({
title: familyType,
value: familyType,
selected: familyTypePreSelected.includes(familyType),
});
return list;
},
[] as (Choice | { selected: boolean })[],
),
},
{
type: 'autocompleteMultiselect',
name: 'size',
message: 'Select EC2 Family Size',
hint: generateFamilyHint('sizes'),
instructions: false,
choices: instanceSizes.reduce(
(list, size) => {
list.push({
title: size,
value: size,
selected: sizePreSelected.includes(size),
});
return list;
},
[] as (Choice | { selected: boolean })[],
),
},
{
type: 'autocompleteMultiselect',
name: 'productDescription',
message: `Select EC2 Product description (select none to include all)`,
instructions: false,
choices: allProductDescriptions.map(desc => ({
title: desc,
value: desc,
})),
},
{
type: 'number',
name: 'priceMax',
message: `Select maximum price`,
initial: 5,
float: true,
round: 4,
increment: 0.0001,
min: 0.0015,
},
{
type: 'number',
name: 'limit',
message: `Select result limit`,
initial: 20,
min: 1,
},
{
type: 'text',
name: 'accessKeyId',
message: `Enter AWS accessKeyId (optional)`,
},
{
type: (prev: string | undefined): string | undefined => (prev ? 'invisible' : undefined),
name: 'secretAccessKey',
message: `Enter AWS secretAccessKey`,
},
];

return { ...answer1, ...answer2 };
} catch (error) {
/* istanbul ignore next */
return undefined;
}
/* istanbul ignore next */
if (inject) prompt.inject(inject);
const answer2: Answer2 = await prompt(question2, { onCancel });

return { ...answer1, ...answer2 };
};
15 changes: 13 additions & 2 deletions src/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,26 @@ describe('cli', () => {
expect(consoleMockCallJoin()).toMatchSnapshot();
});

it('should handle invalid usage of accessKeyId and secretAccessKey', async () => {
it('should handle missing accessKeyId', async () => {
let caughtError = false;
try {
await main(['--secretAccessKey', 'rand']);
} catch (error) {
caughtError = true;
}
expect(caughtError).toBeTruthy();
expect(consoleMockCallJoin()).toContain('`accessKeyId` missing.');
});

it('should handle missing secretAccessKey', async () => {
let caughtError = false;
try {
await main(['--accessKeyId', 'rand']);
} catch (error) {
caughtError = true;
}
expect(caughtError).toBeTruthy();
expect(consoleMockCallJoin()).toMatchSnapshot();
expect(consoleMockCallJoin()).toContain('`secretAccessKey` missing.');
});

describe('ui mode', () => {
Expand Down
Loading

0 comments on commit b5eb90c

Please sign in to comment.