| # Build API Validation |
| |
| This doc provides quick reference/usage information about the Build API |
| `validate` decorators. |
| The [validation tutorial](../tutorials/3_hello_validation.md) provides a |
| tutorial format, but starting at [the beginning](../tutorials/0_introduction.md) |
| is recommended if you are new to the Build API. |
| |
| Validation should be done using the `validate` decorators whenever possible. |
| In particular, the decorators handle validation only calls for you if all |
| validation can be done with the decorators. |
| If you do need to do custom validation in the controller, you will also need to |
| handle the validation only call by checking the config for the validation only |
| call type and manually returning early with a success return code after the |
| validation is complete. |
| |
| |
| ## Addressing Nested Fields |
| |
| All validation decorators support addressing arbitrarily nested fields for any |
| field or subfield argument. |
| This is done by simply adding '.' between field names as one would do if |
| accessing the fields directly from the message object itself. |
| As with accessing properties of the object itself, it works for any arbitrary |
| nesting as long as there are no repeated fields in the specified path. |
| See the protobuf message below commented with the string used to address each |
| field. |
| |
| ```protobuf |
| message Example { |
| message Foo { |
| message Bar { |
| // 'foo.bar.id'. |
| int32 id = 1; |
| message Baz { |
| // 'foo.bar.bazzes.name' will NOT work. |
| string name = 1; |
| } |
| // 'foo.bar.bazzes'. |
| repeated Baz bazzes = 2; |
| } |
| // 'foo.bar'. |
| Bar bar = 1; |
| } |
| // 'foo'. |
| Foo foo = 1; |
| } |
| ``` |
| |
| |
| ## each_in |
| |
| `each_in(field: str, subfield: str, values: Iterable, optional=False)` |
| |
| `each_in` will check that each value in a repeated field, or each value in a |
| subfield of a repeated message, is in the given values. |
| Passing `optional=True` allows only validating the field when it is set to |
| _some_ value. |
| This is the equivalent of [`is_in`](#@validate.is_in) for repeated fields. |
| |
| The following example illustrates the two use cases for `each_in`; a repeated |
| scalar field, and a repeated message field. |
| |
| ```protobuf |
| message ExampleRequest { |
| repeated string names = 1; |
| |
| message SubMessage { |
| // Can be validated. |
| int32 id = 1; |
| // Can't be validated because it's a repeated field in a repeated |
| // message. |
| repeated int32 cant_be_validated = 2; |
| } |
| repeated SubMessage submessages = 2; |
| } |
| ``` |
| |
| ```python |
| # Repeated scalar field, checks every `names` value is either 'foo' or 'bar'. |
| # Python equivalent: |
| # input_proto.names and [x in ['foo', 'bar'] for x in input_proto.names] |
| @validate.each_in('names', None, ['foo', 'bar']) |
| # Repeated message field, checks the `id` field of every `submessage` is in |
| # 1-10, but `submessages` may be empty. |
| # Python equivalent: |
| # all(x.id in range(10) for x in input_proto.submessages) |
| @validate.each_in('submessages', 'id', range(10), optional=True) |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## exists |
| |
| `exists(*fields: str)` |
| |
| `exists` verifies the file or directory pointed to by the given fields exist. |
| `exists` can be given as many fields as need to be checked. |
| This has the side effect of also requiring the field to be set, technically |
| making `require` decorators for these fields redundant, but even so are not |
| discouraged. |
| |
| ```python |
| # Validate input_proto.path1 and input_proto.path2 exist. |
| # Python equivalent: |
| # os.path.exists(x) for x in (input_proto.path1, input_proto.path2) |
| @validate.exists('path1', 'path2') |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## is_in |
| |
| `is_in(field: str, values: Iterable)` |
| |
| `is_in` verifies the given field has one of the given values. |
| |
| ```python |
| # Python equivalent: |
| # input_proto.id in range(1, 10) |
| @validate.is_in('id', range(1, 10)) |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## require |
| |
| `require(*fields: str)` |
| |
| `require` verifies each field has a value set. |
| Since protobuf gives back falsey values when not set, this validator is not |
| capable of distinguishing between an unset field and one that were set with |
| a falsey value (e.g. 0, empty string, False). |
| |
| ```python |
| # Require `foo` and `bar` are set. |
| # Python equivalent: |
| # input_proto.foo and input_proto.bar |
| @validate.require('foo', 'bar') |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## require_any |
| |
| `require_any(*fields: str)` |
| |
| `require_any` verifies that at least one of the given fields has been set. |
| It has the same semantics as [`require`](#require) for validating values. |
| This is useful when there are multiple fields that can be used specify an |
| argument that is required, but any are acceptable. |
| For example, when transitioning between two fields, it may be easiest to |
| support using either of them, but the endpoint only needs one of them to be set. |
| |
| ```python |
| # Require either 'id' or 'identifier' is set. |
| # Python equivalent: |
| # input_proto.id or input_proto.identifier |
| @validate.require_any('id', 'identifier') |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## require_each |
| |
| `require_each(field: str, subfields: Iterable[str], allow_empty=True)` |
| |
| `require_each` verifies every entry in the repeated `field` has each of the |
| given `subfields` set. |
| By default, the validator allows `field` itself to be empty, only verifying |
| that when it is populated the fields are set. |
| Setting `allow_empty` to False also requires the field itself is not empty. |
| |
| ```python |
| # Require any `foos` have `bar` and `baz` set. |
| # Python equivalent: |
| # all(x.bar and x.baz for x in input_proto.foos) |
| @validate.require('foos', ['bar', 'baz']) |
| # Require all `points` have `x` and `y` set. |
| # Python equivalent: |
| # input_proto.points and all(p.x and p.y for p in input_proto.points) |
| @validate.require('points', ['x', 'y'], allow_empty=False) |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |
| |
| |
| ## validation_complete |
| |
| `validation_complete()` |
| |
| When all validation can be done with `validate` decorators, |
| `validation_complete` handles validation only calls. |
| It should be used in every instance it can be used. |
| It must be the last `validate` decorator when used. |
| |
| ```python |
| @validate.require('foo') |
| @validate.validation_complete |
| def Example(input_proto, output_proto, config): |
| pass |
| ``` |