From cb4db31b3b5c503b15713dc897cd171e9b7c7cb9 Mon Sep 17 00:00:00 2001
From: josecoll <jose.coll@r3cev.com>
Date: Thu, 13 Sep 2018 13:46:39 +0100
Subject: [PATCH] CORDA-1892 - CRaSH shell flow start fix for similar flow
 names. (#3874) (#3928)

* CORDA-1892 CRaSH shell flow start fix for similar flow names. (#3874)

* Added fix and associated unit test.

* Fixed broken unit test + added another test case + used NoOpFlows + use Mock output object to assert correct result output.

* Remove unnecessary additional println.

* Minor cleanup in test code.

* Relax nameFragment matching to cater for fully qualified and simple Flow classname specifications.

* Remove superfluous check.

* Minor fix + added additional Unit Test cases to cover all scenarios.

* Reverted back to original behaviour + extra check to avoid ambiguity for exact match.

* Changes following final PR review comments.

* Revert to non visible latch as no associated integration tests defined in this release.
---
 .../net/corda/node/shell/InteractiveShell.kt     | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt
index d13997d2c3..c6f232a453 100644
--- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt
+++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt
@@ -220,26 +220,32 @@ object InteractiveShell {
      */
     @JvmStatic
     fun runFlowByNameFragment(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer) {
-        val matches =
+        val matches = try {
             rpcOps.registeredFlows().filter { nameFragment in it }
-
+        } catch (e: PermissionException) {
+            output.println(e.message ?: "Access denied", Color.red)
+            return
+        }
         if (matches.isEmpty()) {
             output.println("No matching flow found, run 'flow list' to see your options.", Color.red)
             return
-        } else if (matches.size > 1) {
+        } else if (matches.size > 1 && matches.find { it.endsWith(nameFragment)} == null) {
             output.println("Ambiguous name provided, please be more specific. Your options are:")
             matches.forEachIndexed { i, s -> output.println("${i + 1}. $s", Color.yellow) }
             return
         }
 
-        val clazz: Class<FlowLogic<*>> = uncheckedCast(Class.forName(matches.single()))
+        val flowName = matches.find { it.endsWith(nameFragment)} ?: matches.single()
+        val clazz: Class<FlowLogic<*>> = uncheckedCast(Class.forName(flowName))
         try {
             // Show the progress tracker on the console until the flow completes or is interrupted with a
             // Ctrl-C keypress.
             val stateObservable = runFlowFromString({ clazz, args -> rpcOps.startTrackedFlowDynamic(clazz, *args) }, inputData, clazz)
 
             val latch = CountDownLatch(1)
-            ansiProgressRenderer.render(stateObservable, { latch.countDown() })
+            ansiProgressRenderer.render(stateObservable, latch::countDown)
+            // Wait for the flow to end and the progress tracker to notice. By the time the latch is released
+            // the tracker is done with the screen.
             while (!Thread.currentThread().isInterrupted) {
                 try {
                     latch.await()