Build API Endpoint Tutorial Part 3

This README is a continuation of Hello World Tutorial Part 2, creating a “Hello world!” endpoint from scratch. This tutorial assumes you have already executed all the steps in Part 1 and 2, and have all the code in place.

Hello World Part 3: Validation

In this tutorial, we'll be looking at argument validation in Build API endpoints.

“Hello, !”?!?

What happens when you don‘t pass a value for target? In Part 2, if you tried running ./hello__hello before updating hello__hello_input.json, you may have already seen this output. Let’s look at it now.

$> cd ~/chromiumos/chromite/api/contrib/call_scripts/
$> mv hello__hello_input.json hello__hello_input.json.bak
$> echo "{}" > hello__hello_input.json
$> ./hello__hello
Running chromite.api.HelloService/Hello
15:18:22: DEBUG: Services registered successfully.
Hello, !
Completed chromite.api.HelloService/Hello
Success!
Return Code: 0
Result:
{}

Most importantly:

Hello, !

So what happened?

Since we didn't specify target, proto used the default value for the field. The default value of every field in proto3 is the False-y/empty/0 value for the type. Since target is a string, the default value is an empty string, while the numeric types and enums default to 0, and bools default to False.

How do we fix it?

Validation! Saying hello to nothing doesn‘t seem very useful, so we should check in the endpoint that we do always have a target. The Build API has a series of validation decorators for checking the contents of the input proto. For now, we’ll just look at the validation for our new endpoint.

What we want is for target to always have a value. For that, we use the @validate.require decorator. As the name suggests, the require validator requires that the field has a value. In practice, this means that the field must have a non-Falsey value!

Important: Because protobuf's default values are the Falsey values, and so consequently protobuf does not differentiate between passing a Falsey value and not passing a value at all, neither can we!

chromite/api/controller/hello.py:

from chromite.api import validate
from chromite.lib import hello_lib

@validate.require('target')
@validate.validation_complete
def Hello(input_proto, output_proto, config_proto):
    hello_lib.hello(target=input_proto.target)

So, what do we have here?

@validate.require('target'): This line is our actual validation, requiring input_proto.target to have a value.

@validate.validation_complete: This line should be added on every function where all validation is completed by the @validate decorators. As a part of the Build API extended functionality, it allows validation-only calls to endpoints. These validation-only calls execute the input validation, and then exit. The purpose is to allow validation of client implementation on old, branched versions of the Build API.

Validating validation

Let's check that worked.

$> cd ~/chromiumos/chromite/api/contrib/call_scripts/
$> ./hello__hello
Running chromite.api.HelloService/Hello
16:23:34: DEBUG: Services registered successfully.
16:23:34: DEBUG: Validating target is set.
16:23:34: ERROR: target is required.
Completed chromite.api.HelloService/Hello
Return Code: 1
Result:

Instead of “Hello, !”, we got an error! Now let's check our old “moon” target to make sure it still works.

$> mv ./hello__hello_input.json.bak ./hello__hello_input.json
$> ./hello__hello
Running chromite.api.HelloService/Hello
16:29:47: DEBUG: Services registered successfully.
16:29:47: DEBUG: Validating target is set.
Hello, moon!
Completed chromite.api.HelloService/Hello
Success!
Return Code: 0
Result:
{}

Success!

Up Next

Continue to Part 4, where we'll look at mock calls and the faux decorators.