Keystone API Validation with JSD
This post is an attempt to capture some information about JSD.
After a few attempts to standardize a validation schema for the keystone API, it was clear the schema could become unmanageable. I don’t believe the unmanageability stemmed from defining different resource properties but more so the edge cases that a schema had to check for. The problem was that I couldn’t come up with a single schema, which represented a resource, that would scale operations on that resource while maintaining the integrity of the values. This was also probably due to the fact the schema didn’t contain any logic. So this made it hard for the schema to adjust itself depending on what we were asking it to do. To solve this, the schema was split according to operation. This meant what was required for a PUT would be captured in a creation schema, what was required for a PATCH would be captured in a schema specific to updates, and the rest could be shared by both. This covered our use case, but could it be made better? Could we have a schema that was smart enough to enforce validation with respect to the operation? An example of this problem would be that you have an API that expects some attribute of a resource to be provided and valid on creation, but the update operation only requires that at least one resource attribute be updated.
As a by-product, David Stanek started writing JSD. By using JSD, we could represent our schemas as classes. The ideal advantages of this would be that schema definitions would be more maintainable and the objects could generate the schema’s JSON according to the operation. The following are the property requirements of a resource as described by David:
- A property is required and cannot be null – it must appear in the dictionary and have a valid value
- A property is required and can be null – it must appear in the dictionary but it can appear as null
- A property is optional and cannot be null – it may appear in the dictionary and have a valid value if provided
- A property is optional and can be null – it may appear in the dictionary and can be a valid value or null if provided
Filling the above criteria should allow us to do the following:
- On create, required properties must have a valid value in the object. Optional properties can appear inthe object and can be null, which is the same effect as not being provided in the object.
- On update, required properties may be in the object and if they are they must be valid. Optional properties can appear with a valid value or null.
The current schema that defines a create project operation in keystone looks like:
Which is a different schema than the schema that would be used for update project operations:
Let’s try defining a class for this using JSD.
We can create an instance of this class and inspect the schema project = Project(required=True).json().
Which provides a great starting point for defining a schema, and it’s much more manageable! Once we have a little more flexibility in JSD, we could define something like:
With url = Url(required=True).json() resulting in:
The Url class in this case could be a building block for constructing other types of schemas:
We could then use the following at the point of validation:
And as long as the class name of the object matches the resource name (i.e. the schema class isn’t named EndpointSchema )the validation layer could figure out what resource is being modified. In that case, the above would turn into: