Build API: Add validation decorators.
Simplify the easy proto validation tasks by providing decorators
that can handle the simple validation cases.
BUG=None
TEST=run_tests
Change-Id: Ib799d43c7d0dca5312a58771ff67b610e9ff4f2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1669636
Reviewed-by: Alex Klein <saklein@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
Commit-Queue: Alex Klein <saklein@chromium.org>
Auto-Submit: Alex Klein <saklein@chromium.org>
diff --git a/api/validate.py b/api/validate.py
new file mode 100644
index 0000000..230eedc
--- /dev/null
+++ b/api/validate.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Validation helpers for simple input validation in the API."""
+
+from __future__ import print_function
+
+import os
+
+from chromite.lib import cros_build_lib
+from chromite.lib import cros_logging as logging
+
+from google.protobuf import message as protobuf_message
+
+
+def _value(field, message):
+ """Helper function to fetch the value of the field.
+
+ Args:
+ field (str): The field name. Can be nested via . separation.
+ message (Message): The protobuf message it is being fetched from.
+
+ Returns:
+ str|None|int|list|Message|bool - The value of the field.
+ """
+ value = message
+ for part in field.split('.'):
+ if not isinstance(value, protobuf_message.Message):
+ value = None
+ break
+
+ try:
+ value = getattr(value, part)
+ except AttributeError as e:
+ cros_build_lib.Die('Invalid field: %s', e.message)
+
+ return value
+
+#pylint: disable=docstring-misnamed-args
+def exists(*fields):
+ """Validate that the paths in |fields| exist.
+
+ Args:
+ fields (str): The fields being checked. Can be . separated nested
+ fields.
+ """
+ assert fields
+
+ def decorator(func):
+ def _exists(input_proto, *args, **kwargs):
+ for field in fields:
+ logging.debug('Validating %s exists.', field)
+
+ value = _value(field, input_proto)
+ if not value or not os.path.exists(value):
+ cros_build_lib.Die('%s path does not exist: %s' % (field, value))
+
+ return func(input_proto, *args, **kwargs)
+
+ return _exists
+
+ return decorator
+
+
+#pylint: disable=docstring-misnamed-args
+def require(*fields):
+ """Verify |fields| have all been set.
+
+ Args:
+ fields (str): The fields being checked. May be . separated nested
+ fields.
+ """
+ assert fields
+
+ def decorator(func):
+ def _require(input_proto, *args, **kwargs):
+ for field in fields:
+ logging.debug('Validating %s is set.', field)
+
+ value = _value(field, input_proto)
+ if not value:
+ cros_build_lib.Die('%s is required.', field)
+
+ return func(input_proto, *args, **kwargs)
+
+ return _require
+
+ return decorator