Build API: Recommend services when unrecognized value provided
The available services are not dependent on the passed arguments,
and are knowable when we are parsing the arguments, so make them
available via recommendations through a simple similarity matching
system to make manual testing/debugging easier.
BUG=None
TEST=manual
Change-Id: Ia6968b10ff5267116a82757e61bd3538f6fb7880
Reviewed-on: https://chromium-review.googlesource.com/1461265
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Alex Klein <saklein@chromium.org>
Reviewed-by: Lann Martin <lannm@chromium.org>
diff --git a/api/build_api.py b/api/build_api.py
index 3df89e0..7005514 100644
--- a/api/build_api.py
+++ b/api/build_api.py
@@ -20,6 +20,7 @@
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import osutils
+from chromite.utils import matching
class Error(Exception):
@@ -53,11 +54,7 @@
def GetParser():
- """Build the argument parser.
-
- The API parser comprises a subparser hierarchy. The general form is:
- `script service method`, e.g. `build_api image test`.
- """
+ """Build the argument parser."""
parser = commandline.ArgumentParser(description=__doc__)
parser.add_argument('service_method',
@@ -74,11 +71,20 @@
return parser
-def _ParseArgs(argv):
+def _ParseArgs(argv, router):
"""Parse and validate arguments."""
parser = GetParser()
opts = parser.parse_args(argv)
+ methods = router.ListMethods()
+ if opts.service_method not in methods:
+ matched = matching.GetMostLikelyMatchedObject(methods, opts.service_method,
+ matched_score_threshold=0.6)
+ error = 'Unrecognized service name.'
+ if matched:
+ error += '\nDid you mean: \n%s' % '\n'.join(matched)
+ parser.error(error)
+
parts = opts.service_method.split('/')
if len(parts) != 2:
@@ -126,6 +132,15 @@
self._services[svc.full_name] = (svc, module_name)
+ def ListMethods(self):
+ """List all methods registered with the router."""
+ services = []
+ for service_name, (svc, _module) in self._services.items():
+ for method_name in svc.methods_by_name.keys():
+ services.append('%s/%s' % (service_name, method_name))
+
+ return sorted(services)
+
def Route(self, service_name, method_name, input_json):
"""Dispatch the request.
@@ -238,11 +253,11 @@
def main(argv):
- opts = _ParseArgs(argv)
-
router = Router()
RegisterServices(router)
+ opts = _ParseArgs(argv, router)
+
try:
input_proto = osutils.ReadFile(opts.input_json)
except IOError as e: