Last active
November 6, 2021 10:01
-
-
Save gamache/e8e24eee5bd3f190de23 to your computer and use it in GitHub Desktop.
Revisions
-
gamache revised this gist
Jan 20, 2016 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ ## `priv/schema.json` ```json { @@ -76,7 +76,7 @@ defmodule JsonSchema do use GenServer def init(_) do schema = File.read!(Application.app_dir(:myapp) <> "/priv/schema.json") |> Poison.decode! |> ExJsonSchema.Schema.resolve {:ok, schema} -
gamache created this gist
Jan 20, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,129 @@ ## `schema.json` ```json { "$schema": "http://json-schema.org/draft-04/schema#", "title": "Events API Schema", "definitions": { "event_collection": { "type": "object", "required": ["events"], "properties": { "events": { "type": "array", "items": { "$ref": "#/definitions/event" } } } }, "event": { "type": "object", "required": ["name", "timestamp"], "properties": { "name": { "type": "string" }, "timestamp": { "type": ["string", "integer"], "format": "date-time" }, "attributes": { "type": "object" } } } } } ``` ## `json_schema.ex` ```elixir defmodule JsonSchema do @moduledoc ~S""" A service which validates objects according to types defined in `schema.json`. """ @doc ~S""" Validates an object by type. Returns a list of {msg, [columns]} tuples describing any validation errors, or [] if validation succeeded. """ def validate(server \\ :json_schema, object, type) do GenServer.call(server, {:validate, object, type}) end @doc ~S""" Returns true if the object is valid according to the specified type, false otherwise. """ def valid?(server \\ :json_schema, object, type) do [] == validate(server, object, type) end @doc ~S""" Converts the output of `validate/3` into a JSON-compatible structure, a list of error messages. """ def errors_to_json(errors) do errors |> Enum.map(fn ({msg, _cols}) -> msg end) end use GenServer def init(_) do schema = File.read!("./schema.json") |> Poison.decode! |> ExJsonSchema.Schema.resolve {:ok, schema} end def handle_call({:validate, object, type}, _from, schema) do errors = get_validation_errors(object, type, schema) |> transform_errors {:reply, errors, schema} end defp get_validation_errors(object, type, schema) do type_string = type |> to_string type_schema = schema.schema["definitions"][type_string] not_a_struct = case object do %{__struct__: _} -> Map.from_struct(object) _ -> object end string_keyed_object = ensure_key_strings(not_a_struct) ## validate throws a BadMapError on certain kinds of invalid ## input; absorb it (TODO fix ExJsonSchema upstream) try do ExJsonSchema.Validator.validate(schema, type_schema, string_keyed_object) rescue _ -> [{"Failed validation", []}] end end @doc ~S""" Makes sure that all the keys in the map are strings and not atoms. Works on nested data structures. """ defp ensure_key_strings(x) do cond do is_map x -> Enum.reduce x, %{}, fn({k,v}, acc) -> Map.put acc, to_string(k), ensure_key_strings(v) end is_list x -> Enum.map(x, fn (v) -> ensure_key_strings(v) end) true -> x end end end ```