blob: ccdb2f9eb9a7d27bf27e8e45be62b978c12bf988 [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
Alex Klein231d2da2019-07-22 16:44:45 -060068def is_in(field, values):
69 """Validate |field| does not contain |value|.
70
71 Args:
72 field (str): The field being checked. May be . separated nested fields.
73 values (list): The possible values field may take.
74 """
75 assert field
76 assert values
77
78 def decorator(func):
79 def _is_in(input_proto, *args, **kwargs):
80 logging.debug('Validating %s is in %r', field, values)
81 value = _value(field, input_proto)
82
83 if value not in values:
84 cros_build_lib.Die('%s (%r) must be in %r', field, value, values)
85
86 return func(input_proto, *args, **kwargs)
87
88 return _is_in
89
90 return decorator
91
92
Alex Klein2b236722019-06-19 15:44:26 -060093#pylint: disable=docstring-misnamed-args
94def require(*fields):
95 """Verify |fields| have all been set.
96
97 Args:
Alex Klein231d2da2019-07-22 16:44:45 -060098 fields (str): The fields being checked. May be . separated nested fields.
Alex Klein2b236722019-06-19 15:44:26 -060099 """
100 assert fields
101
102 def decorator(func):
103 def _require(input_proto, *args, **kwargs):
104 for field in fields:
105 logging.debug('Validating %s is set.', field)
106
107 value = _value(field, input_proto)
108 if not value:
109 cros_build_lib.Die('%s is required.', field)
110
111 return func(input_proto, *args, **kwargs)
112
113 return _require
114
115 return decorator
Alex Klein69339cc2019-07-22 14:08:35 -0600116
117
118def validation_complete(func):
119 """Automatically skip the endpoint when called after all other validators.
120
121 This decorator MUST be applied after all other validate decorators.
122 The config can be checked manually if there is non-decorator validation, but
123 this is much cleaner if it is all done in decorators.
124 """
125 def _validate_only(request, response, configs, *args, **kwargs):
126 if configs.validate_only:
127 # Avoid calling the endpoint.
128 return 0
129 else:
130 return func(request, response, configs, *args, **kwargs)
131
132 return _validate_only