-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: include command "container" as alias for option "--docker"
This is the first step to create a "snyk container test" or "snyk container monitor" commands that doesn't require Docker engine running. Although it's not relevant to all users, more flexibility here is both more technically correct, future proofs our technology, and is important to several separate strategic partners. The option "--docker" is still available and the CLI is still compatible with this option. Future documentation improvement will give preference for the new command "container" rather than the option "--docker".
- Loading branch information
Arthur Granado
committed
May 19, 2020
1 parent
505720d
commit 6bc0085
Showing
5 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import * as abbrev from 'abbrev'; | ||
import { UnsupportedOptionCombinationError, CustomError } from '../lib/errors'; | ||
|
||
interface ModeData { | ||
allowedCommands: Array<string>; | ||
config: (args) => []; | ||
} | ||
|
||
const modes: Record<string, ModeData> = { | ||
container: { | ||
allowedCommands: ['test', 'monitor'], | ||
config: (args): [] => { | ||
args['docker'] = true; | ||
|
||
return args; | ||
}, | ||
}, | ||
}; | ||
|
||
export function parseMode(mode: string, args): string { | ||
if (isValidMode(mode)) { | ||
const command: string = args._[0]; | ||
|
||
if (isValidCommand(mode, command)) { | ||
configArgs(mode, args); | ||
mode = args._.shift(); | ||
} | ||
} | ||
|
||
return mode; | ||
} | ||
|
||
export function modeValidation(args: object) { | ||
const mode = args['command']; | ||
const commands: Array<string> = args['options']._; | ||
|
||
if (isValidMode(mode) && commands.length <= 1) { | ||
const allowed = modes[mode].allowedCommands | ||
.join(', ') | ||
.replace(/, ([^,]*)$/, ' or $1'); | ||
const message = `use snyk ${mode} with ${allowed}`; | ||
|
||
throw new CustomError(message); | ||
} | ||
|
||
const command = commands[0]; | ||
if (isValidMode(mode) && !isValidCommand(mode, command)) { | ||
const notSupported = [mode, command]; | ||
|
||
throw new UnsupportedOptionCombinationError(notSupported); | ||
} | ||
} | ||
|
||
function isValidMode(mode: string): boolean { | ||
return Object.keys(modes).includes(mode); | ||
} | ||
|
||
function isValidCommand(mode: string, command: string): boolean { | ||
const aliases = abbrev(modes[mode].allowedCommands); | ||
|
||
return Object.keys(aliases).includes(command); | ||
} | ||
|
||
function configArgs(mode: string, args): [] { | ||
return modes[mode].config(args); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import { test } from 'tap'; | ||
import { | ||
UnsupportedOptionCombinationError, | ||
CustomError, | ||
} from '../src/lib/errors'; | ||
import { parseMode, modeValidation } from '../src/cli/modes'; | ||
|
||
test('when is missing command', (c) => { | ||
c.test('should do nothing', (t) => { | ||
const cliCommand = 'container'; | ||
const cliArgs = { | ||
_: [], | ||
'package-manager': 'pip', | ||
}; | ||
|
||
const command = parseMode(cliCommand, cliArgs); | ||
|
||
t.equal(command, cliCommand); | ||
t.equal(cliArgs, cliArgs); | ||
t.notOk(cliArgs['docker']); | ||
t.end(); | ||
}); | ||
c.end(); | ||
}); | ||
|
||
test('when is not a valid mode', (c) => { | ||
c.test('should do nothing', (t) => { | ||
const cliCommand = 'test'; | ||
const cliArgs = { | ||
_: [], | ||
'package-manager': 'pip', | ||
}; | ||
|
||
const command = parseMode(cliCommand, cliArgs); | ||
|
||
t.equal(command, cliCommand); | ||
t.equal(cliArgs, cliArgs); | ||
t.notOk(cliArgs['docker']); | ||
t.end(); | ||
}); | ||
c.end(); | ||
}); | ||
|
||
test('when is a valid mode', (c) => { | ||
c.test('when is allowed command', (d) => { | ||
d.test( | ||
'"container test" should set docker option and test command', | ||
(t) => { | ||
const expectedCommand = 'test'; | ||
const expectedArgs = { | ||
_: [], | ||
docker: true, | ||
'package-manager': 'pip', | ||
}; | ||
const cliCommand = 'container'; | ||
const cliArgs = { | ||
_: ['test'], | ||
'package-manager': 'pip', | ||
}; | ||
|
||
const command = parseMode(cliCommand, cliArgs); | ||
|
||
t.equal(command, expectedCommand); | ||
t.same(cliArgs, expectedArgs); | ||
t.ok(cliArgs['docker']); | ||
t.end(); | ||
}, | ||
); | ||
|
||
d.test('when there is a command alias', (t) => { | ||
t.test('"container t" should set docker option and test command', (t) => { | ||
const expectedCommand = 't'; | ||
const expectedArgs = { | ||
_: [], | ||
docker: true, | ||
'package-manager': 'pip', | ||
}; | ||
const cliCommand = 'container'; | ||
const cliArgs = { | ||
_: ['t'], | ||
'package-manager': 'pip', | ||
}; | ||
|
||
const command = parseMode(cliCommand, cliArgs); | ||
|
||
t.equal(command, expectedCommand); | ||
t.same(cliArgs, expectedArgs); | ||
t.ok(cliArgs['docker']); | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
d.end(); | ||
}); | ||
|
||
c.test('when is not allowed command', (d) => { | ||
d.test( | ||
'"container protect" should not set docker option and return same command', | ||
(t) => { | ||
const expectedCommand = 'container'; | ||
const expectedArgs = { | ||
_: ['protect'], | ||
'package-manager': 'pip', | ||
}; | ||
const cliCommand = 'container'; | ||
const cliArgs = { | ||
_: ['protect'], | ||
'package-manager': 'pip', | ||
}; | ||
|
||
const command = parseMode(cliCommand, cliArgs); | ||
|
||
t.equal(command, expectedCommand); | ||
t.same(cliArgs, expectedArgs); | ||
t.notOk(cliArgs['docker']); | ||
t.end(); | ||
}, | ||
); | ||
d.end(); | ||
}); | ||
|
||
c.test('mode validation', (d) => { | ||
d.test('when there is no command, throw error', (t) => { | ||
const args = { | ||
command: 'container', | ||
options: { | ||
_: ['container'], | ||
}, | ||
}; | ||
|
||
try { | ||
modeValidation(args); | ||
} catch (err) { | ||
t.ok(err instanceof CustomError, 'should throw CustomError'); | ||
t.equal( | ||
err.message, | ||
'use snyk container with test or monitor', | ||
'should have error message', | ||
); | ||
t.end(); | ||
} | ||
}); | ||
|
||
d.test('when command is not valid, throw error', (t) => { | ||
const args = { | ||
command: 'container', | ||
options: { | ||
_: ['protect', 'container'], | ||
}, | ||
}; | ||
|
||
try { | ||
modeValidation(args); | ||
} catch (err) { | ||
t.ok( | ||
err instanceof UnsupportedOptionCombinationError, | ||
'should throw UnsupportedOptionCombinationError', | ||
); | ||
t.equal( | ||
err.message, | ||
'The following option combination is not currently supported: container + protect', | ||
'should have error message', | ||
); | ||
t.end(); | ||
} | ||
}); | ||
|
||
d.test('when command is valid, do nothing', (t) => { | ||
const args = { | ||
command: 'container', | ||
options: { | ||
_: ['test', 'container'], | ||
}, | ||
}; | ||
|
||
modeValidation(args); | ||
|
||
t.ok('should not throw error'); | ||
t.end(); | ||
}); | ||
|
||
d.test('when there is no valid mode, do nothing', (t) => { | ||
const args = { | ||
command: 'test', | ||
options: { | ||
_: ['test'], | ||
}, | ||
}; | ||
|
||
modeValidation(args); | ||
|
||
t.ok('should not throw error'); | ||
t.end(); | ||
}); | ||
|
||
d.test('when there is no mode, do nothing', (t) => { | ||
const args = { | ||
command: '', | ||
options: { | ||
_: [], | ||
}, | ||
}; | ||
|
||
modeValidation(args); | ||
|
||
t.ok('should not throw error'); | ||
t.end(); | ||
}); | ||
d.end(); | ||
}); | ||
c.end(); | ||
}); |