Build API: Simplify the input/output message handling.

Remove the empty input/output logic until. Currently no use for it
and it resulted in confusing errors and unnecessary edge cases.
It can be reintroduced if needed, but for now, this simplification
is nicer.

BUG=None
TEST=manual

Change-Id: I4b3443af3a8334c026db3e6b22f8500951885de1
Reviewed-on: https://chromium-review.googlesource.com/1461268
Commit-Ready: Alex Klein <saklein@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
Reviewed-by: Alec Thilenius <athilenius@google.com>
diff --git a/api/build_api.py b/api/build_api.py
index fd15ceb..a970522 100644
--- a/api/build_api.py
+++ b/api/build_api.py
@@ -8,9 +8,7 @@
 from __future__ import print_function
 
 import importlib
-import os
 
-from google.protobuf import empty_pb2
 from google.protobuf import json_format
 from google.protobuf import symbol_database
 
@@ -27,6 +25,10 @@
   """Base error class for the module."""
 
 
+class InvalidInputFormatError(Error):
+  """Raised when the passed input protobuf can't be parsed."""
+
+
 # API Service Errors.
 class UnknownServiceError(Error):
   """Error raised when the requested service has not been registered."""
@@ -62,10 +64,10 @@
                            'called.')
 
   parser.add_argument(
-      '--input-json', type='path',
+      '--input-json', type='path', required=True,
       help='Path to the JSON serialized input argument protobuf message.')
   parser.add_argument(
-      '--output-json', type='path',
+      '--output-json', type='path', required=True,
       help='The path to which the result protobuf message should be written.')
 
   return parser
@@ -151,23 +153,17 @@
       raise UnknownMethodError('The %s method has not been defined in the %s '
                                'service.' % (method_name, service_name))
 
-    # Service method argument magic: do not pass the arguments when the method
-    # is expecting the Empty message. Additions of optional arguments/return
-    # values are still backwards compatible, but the implementation signature
-    # is simplified and more explicit about what its expecting.
-    args = []
     # Parse the input file to build an instance of the input message.
     input_msg = self._sym_db.GetPrototype(method_desc.input_type)()
-    if not isinstance(input_msg, empty_pb2.Empty):
+    try:
       json_format.Parse(input_json, input_msg, ignore_unknown_fields=True)
-      args.append(input_msg)
+    except json_format.ParseError as e:
+      raise InvalidInputFormatError(
+          'Unable to parse the input json: %s' % e.message)
 
     # Get an empty output message instance.
     output_msg = self._sym_db.GetPrototype(method_desc.output_type)()
-    if not isinstance(output_msg, empty_pb2.Empty):
-      args.append(output_msg)
 
-    # TODO(saklein) Do we need this? Are aliases useful? Maybe dump it.
     # Allow proto-based method name override.
     method_options = method_desc.GetOptions().Extensions[self._method_options]
     if method_options.HasField('implementation_name'):
@@ -181,7 +177,7 @@
     method_impl = self._GetMethod(module_name, method_name)
 
     # Successfully located; call and return.
-    method_impl(*args)
+    method_impl(input_msg, output_msg)
     return output_msg
 
   def _HandleChrootAssert(self, service_options, method_options):
@@ -245,13 +241,19 @@
   router = Router()
   RegisterServices(router)
 
-  if os.path.exists(opts.input_json):
+  try:
     input_proto = osutils.ReadFile(opts.input_json)
-  else:
-    input_proto = None
+  except IOError as e:
+    cros_build_lib.Die('Unable to read input file: %s' % e.message)
 
-  output_msg = router.Route(opts.service, opts.method, input_proto)
+  try:
+    output_msg = router.Route(opts.service, opts.method, input_proto)
+  except Error as e:
+    # Error derivatives are handled nicely, but let anything else bubble up.
+    cros_build_lib.Die(e.message)
 
-  if opts.output_json:
-    output_content = json_format.MessageToJson(output_msg)
+  output_content = json_format.MessageToJson(output_msg)
+  try:
     osutils.WriteFile(opts.output_json, output_content)
+  except IOError as e:
+    cros_build_lib.Die('Unable to write output file: %s' % e.message)