Skip to content

Instantly share code, notes, and snippets.

@cloud8421
Last active January 4, 2017 14:11
Show Gist options
  • Save cloud8421/9850d9fe0ad26bbe6baeec2aef209dc4 to your computer and use it in GitHub Desktop.
Save cloud8421/9850d9fe0ad26bbe6baeec2aef209dc4 to your computer and use it in GitHub Desktop.
JSON schema based validator with automatic code and docs generation

This module requires the following dependencies:

    [{:ex_doc, "~> 0.14.3", only: :dev},
     {:poison, "~> 3.0"},
     {:ex_json_schema, "~> 0.5.2"}]

Note that ex_doc is optional as you can still use iex-based documentation.

The generated module docs will include a prettified copy of all schemas found in priv/json_schemas.

defmodule MyApp.Validator do
@moduledoc_intro """
This module provides the ability to validate JSON payloads via JSON schema.
All validations methods are generated based on the contents
of priv/json_schemas.
For each schema file, this module will generate two
functions (`validate/2` and `schema_for/1`).
So if you have a `priv/json_schemas/message-new.json` file, this
module will create:
- `validate("message-new", payload)`
- `schema_for("message-new")`
It's important to call these files with the same payload names used for events and actions
in all channels.
For more information about how this code is generated, see the following resources:
- <http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html#quote/2-binding-and-unquote-fragments>
- <http://elixir-lang.org/docs/stable/elixir/Macro.html#escape/1>
"""
schema_files = :code.priv_dir(:my_app)
|> List.to_string
|> Path.join("json_schemas")
|> Path.join("*.json")
|> Path.wildcard
@spec validate(String.t, ExJsonSchema.data) :: :ok | {:error, ExJsonSchema.Validator.errors}
@spec schema_for(String.t) :: ExJsonSchema.data
Module.register_attribute(__MODULE__, :schemas, accumulate: true)
for file <- schema_files do
@external_resource file
file_name = Path.basename(file, ".json")
content = file
|> File.read!
|> Poison.decode!
Module.put_attribute(__MODULE__, :schemas, {file_name, content})
@doc """
Validate a map with string keys against a specific schema, identified
by a payload name.
"""
def validate(unquote(file_name), payload) do
unquote(file_name)
|> schema_for
|> ExJsonSchema.Validator.validate(payload)
end
@doc """
Returns the schema in map form for a given payload name
#{file_name}
"""
def schema_for(unquote(file_name)), do: unquote(Macro.escape(content))
end
moduledoc_template = """
<%= intro %>
# Available schemas
<%= for {file_name, content} <- schemas do %>
## <%= file_name %>
```json
<%= Poison.encode!(content, pretty: true) %>
```
<%= end %>
"""
rendered = EEx.eval_string(moduledoc_template, intro: @moduledoc_intro,
schemas: Module.get_attribute(__MODULE__, :schemas))
@moduledoc rendered
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment