blob: f3659b8abf754f8143a62fe8172bdc3d08d5dcfc [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
Alex Klein2008aee2019-08-20 16:25:27 -06006"""Validation helpers for simple input validation in the API.
7
8Note: Every validator MUST respect config.do_validation. This is an internally
9set config option that allows the mock call decorators to be placed before or
10after the validation decorators, rather than forcing an ordering that could then
11produce incorrect outputs if missed.
12"""
Alex Klein2b236722019-06-19 15:44:26 -060013
14from __future__ import print_function
15
Alex Klein4de25e82019-08-05 15:58:39 -060016import functools
Alex Klein2b236722019-06-19 15:44:26 -060017import os
18
19from chromite.lib import cros_build_lib
20from chromite.lib import cros_logging as logging
21
Mike Frysinger17844a02019-08-24 18:21:02 -040022# TODO(vapier): Re-enable check once we upgrade to pylint-1.8+.
23# pylint: disable=no-name-in-module
Alex Klein2b236722019-06-19 15:44:26 -060024from google.protobuf import message as protobuf_message
Mike Frysinger17844a02019-08-24 18:21:02 -040025# pylint: enable=no-name-in-module
Alex Klein2b236722019-06-19 15:44:26 -060026
27
28def _value(field, message):
29 """Helper function to fetch the value of the field.
30
31 Args:
32 field (str): The field name. Can be nested via . separation.
33 message (Message): The protobuf message it is being fetched from.
34
35 Returns:
36 str|None|int|list|Message|bool - The value of the field.
37 """
38 value = message
39 for part in field.split('.'):
40 if not isinstance(value, protobuf_message.Message):
41 value = None
42 break
43
44 try:
45 value = getattr(value, part)
46 except AttributeError as e:
Mike Frysinger6b5c3cd2019-08-27 16:51:00 -040047 cros_build_lib.Die('Invalid field: %s', e)
Alex Klein2b236722019-06-19 15:44:26 -060048
49 return value
50
Alex Klein69339cc2019-07-22 14:08:35 -060051
Mike Frysinger88e02c12019-10-01 15:05:36 -040052# pylint: disable=docstring-misnamed-args
Alex Klein2b236722019-06-19 15:44:26 -060053def exists(*fields):
54 """Validate that the paths in |fields| exist.
55
56 Args:
57 fields (str): The fields being checked. Can be . separated nested
58 fields.
59 """
60 assert fields
61
62 def decorator(func):
Alex Klein4de25e82019-08-05 15:58:39 -060063 @functools.wraps(func)
Alex Klein2008aee2019-08-20 16:25:27 -060064 def _exists(input_proto, output_proto, config, *args, **kwargs):
65 if config.do_validation:
66 for field in fields:
67 logging.debug('Validating %s exists.', field)
Alex Klein2b236722019-06-19 15:44:26 -060068
Alex Klein2008aee2019-08-20 16:25:27 -060069 value = _value(field, input_proto)
70 if not value or not os.path.exists(value):
71 cros_build_lib.Die('%s path does not exist: %s' % (field, value))
Alex Klein2b236722019-06-19 15:44:26 -060072
Alex Klein2008aee2019-08-20 16:25:27 -060073 return func(input_proto, output_proto, config, *args, **kwargs)
Alex Klein2b236722019-06-19 15:44:26 -060074
75 return _exists
76
77 return decorator
78
79
Alex Klein231d2da2019-07-22 16:44:45 -060080def is_in(field, values):
81 """Validate |field| does not contain |value|.
82
83 Args:
84 field (str): The field being checked. May be . separated nested fields.
85 values (list): The possible values field may take.
86 """
87 assert field
88 assert values
89
90 def decorator(func):
Alex Klein4de25e82019-08-05 15:58:39 -060091 @functools.wraps(func)
Alex Klein2008aee2019-08-20 16:25:27 -060092 def _is_in(input_proto, output_proto, config, *args, **kwargs):
93 if config.do_validation:
94 logging.debug('Validating %s is in %r', field, values)
95 value = _value(field, input_proto)
Alex Klein231d2da2019-07-22 16:44:45 -060096
Alex Klein2008aee2019-08-20 16:25:27 -060097 if value not in values:
98 cros_build_lib.Die('%s (%r) must be in %r', field, value, values)
Alex Klein231d2da2019-07-22 16:44:45 -060099
Alex Klein2008aee2019-08-20 16:25:27 -0600100 return func(input_proto, output_proto, config, *args, **kwargs)
Alex Klein231d2da2019-07-22 16:44:45 -0600101
102 return _is_in
103
104 return decorator
105
106
Mike Frysinger88e02c12019-10-01 15:05:36 -0400107# pylint: disable=docstring-misnamed-args
Alex Klein2b236722019-06-19 15:44:26 -0600108def require(*fields):
109 """Verify |fields| have all been set.
110
111 Args:
Alex Klein231d2da2019-07-22 16:44:45 -0600112 fields (str): The fields being checked. May be . separated nested fields.
Alex Klein2b236722019-06-19 15:44:26 -0600113 """
114 assert fields
115
116 def decorator(func):
Alex Klein4de25e82019-08-05 15:58:39 -0600117 @functools.wraps(func)
Alex Klein2008aee2019-08-20 16:25:27 -0600118 def _require(input_proto, output_proto, config, *args, **kwargs):
119 if config.do_validation:
120 for field in fields:
121 logging.debug('Validating %s is set.', field)
Alex Klein2b236722019-06-19 15:44:26 -0600122
Alex Klein2008aee2019-08-20 16:25:27 -0600123 value = _value(field, input_proto)
124 if not value:
125 cros_build_lib.Die('%s is required.', field)
Alex Klein2b236722019-06-19 15:44:26 -0600126
Alex Klein2008aee2019-08-20 16:25:27 -0600127 return func(input_proto, output_proto, config, *args, **kwargs)
Alex Klein2b236722019-06-19 15:44:26 -0600128
129 return _require
130
131 return decorator
Alex Klein69339cc2019-07-22 14:08:35 -0600132
133
134def validation_complete(func):
135 """Automatically skip the endpoint when called after all other validators.
136
137 This decorator MUST be applied after all other validate decorators.
138 The config can be checked manually if there is non-decorator validation, but
139 this is much cleaner if it is all done in decorators.
140 """
Alex Klein4de25e82019-08-05 15:58:39 -0600141
142 @functools.wraps(func)
Alex Klein69339cc2019-07-22 14:08:35 -0600143 def _validate_only(request, response, configs, *args, **kwargs):
144 if configs.validate_only:
145 # Avoid calling the endpoint.
146 return 0
147 else:
148 return func(request, response, configs, *args, **kwargs)
149
150 return _validate_only