blob: a42b5d37e520820f62bff5767188b2f0b31a8475 [file] [log] [blame]
Alex Klein2b236722019-06-19 15:44:26 -06001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Validation helpers for simple input validation in the API."""
7
8from __future__ import print_function
9
10import os
11
12from chromite.lib import cros_build_lib
13from chromite.lib import cros_logging as logging
14
15from google.protobuf import message as protobuf_message
16
17
18def _value(field, message):
19 """Helper function to fetch the value of the field.
20
21 Args:
22 field (str): The field name. Can be nested via . separation.
23 message (Message): The protobuf message it is being fetched from.
24
25 Returns:
26 str|None|int|list|Message|bool - The value of the field.
27 """
28 value = message
29 for part in field.split('.'):
30 if not isinstance(value, protobuf_message.Message):
31 value = None
32 break
33
34 try:
35 value = getattr(value, part)
36 except AttributeError as e:
37 cros_build_lib.Die('Invalid field: %s', e.message)
38
39 return value
40
Alex Klein69339cc2019-07-22 14:08:35 -060041
Alex Klein2b236722019-06-19 15:44:26 -060042#pylint: disable=docstring-misnamed-args
43def exists(*fields):
44 """Validate that the paths in |fields| exist.
45
46 Args:
47 fields (str): The fields being checked. Can be . separated nested
48 fields.
49 """
50 assert fields
51
52 def decorator(func):
53 def _exists(input_proto, *args, **kwargs):
54 for field in fields:
55 logging.debug('Validating %s exists.', field)
56
57 value = _value(field, input_proto)
58 if not value or not os.path.exists(value):
59 cros_build_lib.Die('%s path does not exist: %s' % (field, value))
60
61 return func(input_proto, *args, **kwargs)
62
63 return _exists
64
65 return decorator
66
67
68#pylint: disable=docstring-misnamed-args
69def require(*fields):
70 """Verify |fields| have all been set.
71
72 Args:
73 fields (str): The fields being checked. May be . separated nested
74 fields.
75 """
76 assert fields
77
78 def decorator(func):
79 def _require(input_proto, *args, **kwargs):
80 for field in fields:
81 logging.debug('Validating %s is set.', field)
82
83 value = _value(field, input_proto)
84 if not value:
85 cros_build_lib.Die('%s is required.', field)
86
87 return func(input_proto, *args, **kwargs)
88
89 return _require
90
91 return decorator
Alex Klein69339cc2019-07-22 14:08:35 -060092
93
94def validation_complete(func):
95 """Automatically skip the endpoint when called after all other validators.
96
97 This decorator MUST be applied after all other validate decorators.
98 The config can be checked manually if there is non-decorator validation, but
99 this is much cleaner if it is all done in decorators.
100 """
101 def _validate_only(request, response, configs, *args, **kwargs):
102 if configs.validate_only:
103 # Avoid calling the endpoint.
104 return 0
105 else:
106 return func(request, response, configs, *args, **kwargs)
107
108 return _validate_only