mirror of
https://github.com/corda/corda.git
synced 2025-02-21 09:51:57 +00:00
CORDA-2028 - Fix use of required paramers/options on command line (#4040)
* Make required parameters work with --install-shell-extensions and make errors look a bit more errorey * Make blobinspector required parameter work the way it used to * Fix compilation Error
This commit is contained in:
parent
5d84640d1f
commit
9c8a1cd14a
@ -34,8 +34,8 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised binary blobs to text") {
|
class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised binary blobs to text") {
|
||||||
@Parameters(index = "*..0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class])
|
@Parameters(index = "0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class])
|
||||||
var source: MutableList<URL> = mutableListOf()
|
var source: URL? = null
|
||||||
|
|
||||||
@Option(names = ["--format"], paramLabel = "type", description = ["Output format. Possible values: [YAML, JSON]"])
|
@Option(names = ["--format"], paramLabel = "type", description = ["Output format. Possible values: [YAML, JSON]"])
|
||||||
private var formatType: OutputFormatType = OutputFormatType.YAML
|
private var formatType: OutputFormatType = OutputFormatType.YAML
|
||||||
@ -61,8 +61,7 @@ class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun run(out: PrintStream): Int {
|
fun run(out: PrintStream): Int {
|
||||||
require(source.count() == 1) { "You must specify URL or file path to the blob" }
|
val inputBytes = source!!.readBytes()
|
||||||
val inputBytes = source.first().readBytes()
|
|
||||||
val bytes = parseToBinaryRelaxed(inputFormatType, inputBytes)
|
val bytes = parseToBinaryRelaxed(inputFormatType, inputBytes)
|
||||||
?: throw IllegalArgumentException("Error: this input does not appear to be encoded in Corda's AMQP extended format, sorry.")
|
?: throw IllegalArgumentException("Error: this input does not appear to be encoded in Corda's AMQP extended format, sorry.")
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class BlobInspectorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun run(resourceName: String): String {
|
private fun run(resourceName: String): String {
|
||||||
blobInspector.source = mutableListOf(javaClass.getResource(resourceName))
|
blobInspector.source = javaClass.getResource(resourceName)
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
blobInspector.run(PrintStream(WriterOutputStream(writer, UTF_8)))
|
blobInspector.run(PrintStream(WriterOutputStream(writer, UTF_8)))
|
||||||
val output = writer.toString()
|
val output = writer.toString()
|
||||||
|
@ -21,8 +21,6 @@ import java.util.concurrent.Callable
|
|||||||
interface Validated {
|
interface Validated {
|
||||||
companion object {
|
companion object {
|
||||||
val logger = contextLogger()
|
val logger = contextLogger()
|
||||||
const val RED = "\u001B[31m"
|
|
||||||
const val RESET = "\u001B[0m"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,8 +34,8 @@ interface Validated {
|
|||||||
fun validate() {
|
fun validate() {
|
||||||
val errors = validator()
|
val errors = validator()
|
||||||
if (errors.isNotEmpty()) {
|
if (errors.isNotEmpty()) {
|
||||||
logger.error(RED + "Exceptions when parsing command line arguments:")
|
logger.error(ShellConstants.RED + "Exceptions when parsing command line arguments:")
|
||||||
logger.error(errors.joinToString("\n") + RESET)
|
logger.error(errors.joinToString("\n") + ShellConstants.RESET)
|
||||||
CommandLine(this).usage(System.err)
|
CommandLine(this).usage(System.err)
|
||||||
exitProcess(ExitCodes.FAILURE)
|
exitProcess(ExitCodes.FAILURE)
|
||||||
}
|
}
|
||||||
@ -55,6 +53,11 @@ object CordaSystemUtils {
|
|||||||
fun getOsName(): String = System.getProperty(OS_NAME)
|
fun getOsName(): String = System.getProperty(OS_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ShellConstants {
|
||||||
|
const val RED = "\u001B[31m"
|
||||||
|
const val RESET = "\u001B[0m"
|
||||||
|
}
|
||||||
|
|
||||||
fun CordaCliWrapper.start(args: Array<String>) {
|
fun CordaCliWrapper.start(args: Array<String>) {
|
||||||
this.args = args
|
this.args = args
|
||||||
|
|
||||||
@ -66,27 +69,47 @@ fun CordaCliWrapper.start(args: Array<String>) {
|
|||||||
cmd.registerConverter(Path::class.java) { Paths.get(it).toAbsolutePath().normalize() }
|
cmd.registerConverter(Path::class.java) { Paths.get(it).toAbsolutePath().normalize() }
|
||||||
cmd.commandSpec.name(alias)
|
cmd.commandSpec.name(alias)
|
||||||
cmd.commandSpec.usageMessage().description(description)
|
cmd.commandSpec.usageMessage().description(description)
|
||||||
|
cmd.commandSpec.parser().collectErrors(true)
|
||||||
try {
|
try {
|
||||||
val defaultAnsiMode = if (CordaSystemUtils.isOsWindows()) { Help.Ansi.ON } else { Help.Ansi.AUTO }
|
val defaultAnsiMode = if (CordaSystemUtils.isOsWindows()) {
|
||||||
val results = cmd.parseWithHandlers(RunLast().useOut(System.out).useAnsi(defaultAnsiMode),
|
Help.Ansi.ON
|
||||||
DefaultExceptionHandler<List<Any>>().useErr(System.err).useAnsi(defaultAnsiMode),
|
} else {
|
||||||
*args)
|
Help.Ansi.AUTO
|
||||||
// If an error code has been returned, use this and exit
|
}
|
||||||
results?.firstOrNull()?.let {
|
|
||||||
if (it is Int) {
|
val results = cmd.parse(*args)
|
||||||
exitProcess(it)
|
val app = cmd.getCommand<CordaCliWrapper>()
|
||||||
} else {
|
if (cmd.isUsageHelpRequested) {
|
||||||
|
cmd.usage(System.out, defaultAnsiMode)
|
||||||
|
exitProcess(ExitCodes.SUCCESS)
|
||||||
|
}
|
||||||
|
if (cmd.isVersionHelpRequested) {
|
||||||
|
cmd.printVersionHelp(System.out, defaultAnsiMode)
|
||||||
|
exitProcess(ExitCodes.SUCCESS)
|
||||||
|
}
|
||||||
|
if (app.installShellExtensionsParser.installShellExtensions) {
|
||||||
|
System.out.println("Install shell extensions: ${app.installShellExtensionsParser.installShellExtensions}")
|
||||||
|
// ignore any parsing errors and run the program
|
||||||
|
exitProcess(app.call())
|
||||||
|
}
|
||||||
|
val allErrors = results.flatMap { it.parseResult?.errors() ?: emptyList() }
|
||||||
|
if (allErrors.any()) {
|
||||||
|
val parameterExceptions = allErrors.asSequence().filter { it is ParameterException }
|
||||||
|
if (parameterExceptions.any()) {
|
||||||
|
System.err.println("${ShellConstants.RED}${parameterExceptions.map{ it.message }.joinToString()}${ShellConstants.RESET}")
|
||||||
|
parameterExceptions.filter { it is UnmatchedArgumentException}.forEach { (it as UnmatchedArgumentException).printSuggestions(System.out) }
|
||||||
|
usage(cmd, System.out, defaultAnsiMode)
|
||||||
exitProcess(ExitCodes.FAILURE)
|
exitProcess(ExitCodes.FAILURE)
|
||||||
}
|
}
|
||||||
|
throw allErrors.first()
|
||||||
}
|
}
|
||||||
// If no results returned, picocli ran something without invoking the main program, e.g. --help or --version, so exit successfully
|
exitProcess(app.call())
|
||||||
exitProcess(ExitCodes.SUCCESS)
|
} catch (e: Exception) {
|
||||||
} catch (e: ExecutionException) {
|
|
||||||
val throwable = e.cause ?: e
|
val throwable = e.cause ?: e
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
throwable.printStackTrace()
|
throwable.printStackTrace()
|
||||||
} else {
|
} else {
|
||||||
System.err.println("*ERROR*: ${throwable.rootMessage ?: "Use --verbose for more details"}")
|
System.err.println("${ShellConstants.RED}${throwable.rootMessage ?: "Use --verbose for more details"}${ShellConstants.RESET}")
|
||||||
}
|
}
|
||||||
exitProcess(ExitCodes.FAILURE)
|
exitProcess(ExitCodes.FAILURE)
|
||||||
}
|
}
|
||||||
@ -126,7 +149,7 @@ abstract class CordaCliWrapper(val alias: String, val description: String) : Cal
|
|||||||
var loggingLevel: Level = Level.INFO
|
var loggingLevel: Level = Level.INFO
|
||||||
|
|
||||||
@Mixin
|
@Mixin
|
||||||
private lateinit var installShellExtensionsParser: InstallShellExtensionsParser
|
lateinit var installShellExtensionsParser: InstallShellExtensionsParser
|
||||||
|
|
||||||
// This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before).
|
// This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before).
|
||||||
// Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used.
|
// Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user