Controller Schemas - department-of-veterans-affairs/caseflow GitHub Wiki

Controller schemas are a way to perform validation on input data received by Rails controllers, as well as automatically generate documentation about the fields and their expected values. The generated documentation is served by the Rails app at /route_docs.

Adding a new schema

  1. Add include ValidationConcern to the controller class, which uses a :before_action hook to perform any desired validation, before a controller method is called.

  2. For a specific endpoint method, e.g. def action defined on ExampleController, validation is enabled by prepending a line like validates :action, using ExampleSchemas.action. Methods without this line will skip schema validation.

  3. Continuing with the above example, create a new file for an ExampleSchemas class in the app/schemas/ directory, with a class method action that calls ControllerSchema.params or ControllerSchema.json and returns the resulting schema.

    • Note that the naming of this new class and class method, as well as the directory location, are determined by convention to mirror the controller class layout. Following convention is encouraged, although choosing different names merely requires updating the validates line.
  4. Fill out the schema as desired, using the schema definition DSL.

ControllerSchema DSL

Although under the hood we use dry-schema which comes with a DSL, ControllerSchema provides its own DSL as a thin layer of abstraction more in line with our documentation needs. An example is the easiest way to get a sense for this DSL:

def update_account
  ControllerSchema.json do
    integer :id
    string :name
    bool :paid_account
    date :date_of_birth
    datetime :last_login, nullable: true
    string :team, optional: true, included_in?: %w[Instinct Mystic Valor], doc: "Pokemon Go team affiliation"
  end
end

Each line in the schema defines a field in the input, and takes the form type :key, additional: arguments

Supported types

Supported types currently include bool, date, datetime, integer, and string, and are listed in ControllerSchema::SUPPORTED_TYPES. Additional types can be added as long as they are supported by dry-schema, although dry-types makes it easy to implement new types.

Additional arguments

  • optional: By default, fields are required, i.e. the key must be present in the input data. Optional fields don't have this restriction.
  • nullable: By default, fields must not have null values. Nullable fields don't have this restriction.
  • included_in: Specify a list of allowed values, for enumeration-style validation. Note that making a field nullable will implicitly include null as an allowed value.
  • doc: Provide additional context for this field.

Note that a field can be both required and nullable, or both optional and non-nullable (although this latter combination is probably rare).