-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1765 from lud/elixir-jsv
Work in progress: Harness for elixir-jsv
- Loading branch information
Showing
9 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 characters
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
] |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Bowtie specific | ||
|
||
# Revert global gitignore | ||
!lib | ||
|
||
# We always want fresh deps | ||
mix.lock | ||
|
||
|
||
# Elixir generic .gitignore | ||
|
||
## The directory Mix will write compiled artifacts to. | ||
/_build/ | ||
|
||
## If you run "mix test --cover", coverage assets end up here. | ||
/cover/ | ||
|
||
## The directory Mix downloads your dependencies sources to. | ||
/deps/ | ||
|
||
## Where third-party dependencies like ExDoc output generated docs. | ||
/doc/ | ||
|
||
## Ignore .fetch files in case you like to edit your project deps locally. | ||
/.fetch | ||
|
||
## If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
## Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
## Ignore package tarball (built via "mix hex.build"). | ||
bowtie_jsv-*.tar | ||
|
||
## Temporary files, for example, from tests. | ||
/tmp/ |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Builder image ------------------------------------------------------------- # | ||
FROM elixir:1.18-alpine | ||
|
||
WORKDIR /app | ||
|
||
# Install Elixir package manager | ||
RUN mix local.hex --force && mix local.rebar --force | ||
|
||
# Install and compile dependencies | ||
COPY mix.exs mix.exs | ||
RUN mix deps.get && mix deps.compile | ||
|
||
# Copy the actual code | ||
COPY lib lib | ||
|
||
# Generate a production release | ||
ENV MIX_ENV=prod | ||
RUN mix compile && mix release --overwrite | ||
|
||
# Runner image -------------------------------------------------------------- # | ||
FROM alpine | ||
|
||
RUN apk update && apk add --no-cache openssl ncurses-libs libstdc++ | ||
COPY --from=0 /app/_build/prod/rel/bowtie_jsv /bowtie_jsv | ||
|
||
ENTRYPOINT [ "/bowtie_jsv/bin/bowtie_jsv", "start" ] |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
defmodule BowtieJSV do | ||
@dialects [ | ||
"http://json-schema.org/draft-07/schema#", | ||
"https://json-schema.org/draft/2020-12/schema" | ||
] | ||
|
||
def run() do | ||
debug("starting") | ||
loop_lines(%{}) | ||
debug("finished") | ||
end | ||
|
||
def debug(msg) do | ||
IO.puts(:stderr, msg) | ||
end | ||
|
||
def debug(value, label) do | ||
IO.inspect(:stderr, value, label: label) | ||
end | ||
|
||
defp loop_lines(state) do | ||
case IO.read(:stdio, :line) do | ||
:eof -> | ||
debug("EOF received") | ||
|
||
line -> | ||
{:reply, resp, state} = handle_raw_command(line, state) | ||
debug(resp, "resp") | ||
IO.puts(JSON.encode!(resp)) | ||
loop_lines(state) | ||
end | ||
end | ||
|
||
defp handle_raw_command(json, state) do | ||
cmd = JSON.decode!(json) | ||
handle_command(cmd, state) | ||
end | ||
|
||
defp handle_command(%{"cmd" => "start", "version" => 1}, state) do | ||
{:reply, start_response(), state} | ||
end | ||
|
||
defp handle_command(%{"cmd" => "stop"}, _state) do | ||
System.halt() | ||
end | ||
|
||
defp handle_command(%{"cmd" => "dialect", "dialect" => dialect}, state) do | ||
{:reply, %{ok: true}, Map.put(state, :default_dialect, dialect)} | ||
end | ||
|
||
defp handle_command(%{"cmd" => "run", "seq" => tseq, "case" => tcase}, state) do | ||
%{"schema" => raw_schema, "tests" => tests} = tcase | ||
registry = Map.get(tcase, "registry") | ||
debug(tcase, "tcase") | ||
|
||
root = build_schema(raw_schema, registry, state) | ||
|
||
results = Enum.map(tests, fn test -> run_test(test, root) end) | ||
|
||
{:reply, %{seq: tseq, results: results}, state} | ||
rescue | ||
e -> {:reply, errored(e, __STACKTRACE__, %{seq: tseq}), state} | ||
end | ||
|
||
defp build_schema(raw_schema, registry, state) do | ||
JSV.build!(raw_schema, | ||
resolver: {BowtieJSV.Resolver, registry: registry}, | ||
default_meta: state.default_dialect | ||
) | ||
end | ||
|
||
defp run_test(test, root) do | ||
%{"instance" => data} = test | ||
|
||
case JSV.validate(data, root) do | ||
{:ok, _} -> %{valid: true} | ||
{:error, verr} -> %{valid: false, output: JSV.normalize_error(verr)} | ||
end | ||
rescue | ||
e -> errored(e, __STACKTRACE__) | ||
end | ||
|
||
defp start_response do | ||
%{ | ||
version: 1, | ||
implementation: %{ | ||
language: "elixir", | ||
name: "jsv", | ||
version: jsv_vsn(), | ||
dialects: @dialects, | ||
documentation: "https://hexdocs.pm/jsv/", | ||
homepage: "https://github.com/lud/jsv", | ||
issues: "https://github.com/lud/jsv/issues", | ||
source: "https://github.com/lud/jsv" | ||
} | ||
} | ||
end | ||
|
||
defp jsv_vsn do | ||
{:ok, jsv_vsn} = :application.get_key(:jsv, :vsn) | ||
List.to_string(jsv_vsn) | ||
end | ||
|
||
defp errored(e, stacktrace, additional_data \\ %{}) do | ||
errorred_payload = %{ | ||
errored: true, | ||
context: %{ | ||
message: Exception.message(e), | ||
traceback: Exception.format_stacktrace(stacktrace) | ||
} | ||
} | ||
|
||
Map.merge(errorred_payload, additional_data) | ||
end | ||
end |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
defmodule BowtieJSV.App do | ||
def start(_, _) do | ||
children = [{Task, fn -> BowtieJSV.run() end}] | ||
|
||
opts = [strategy: :one_for_one, name: BowtieJSV.Supervisor] | ||
Supervisor.start_link(children, opts) | ||
end | ||
end |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
defmodule BowtieJSV.Resolver do | ||
alias JSV.Resolver.Embedded | ||
@behaviour JSV.Resolver | ||
|
||
@impl true | ||
def resolve(uri, opts) do | ||
registry = make_registry(opts) | ||
|
||
case registry do | ||
%{^uri => schema} -> | ||
BowtieJSV.debug(["resolving from registry: ", uri]) | ||
{:ok, schema} | ||
|
||
_ -> | ||
BowtieJSV.debug(["resolving from embedded: ", uri]) | ||
Embedded.resolve(uri, []) | ||
end | ||
end | ||
|
||
defp make_registry(opts) do | ||
case Keyword.get(opts, :registry) do | ||
nil -> %{} | ||
map -> map | ||
end | ||
end | ||
end |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
defmodule BowtieJSV.MixProject do | ||
use Mix.Project | ||
|
||
def project do | ||
[ | ||
app: :bowtie_jsv, | ||
version: "0.1.0", | ||
elixir: "~> 1.17", | ||
start_permanent: Mix.env() == :prod, | ||
deps: deps(), | ||
escript: [main_module: BowtieJSV, name: "bowtie_jsv", include_priv_for: [:jsv]], | ||
releases: [ | ||
bowtie_jsv: [ | ||
include_executables_for: [:unix], | ||
include_erts: true | ||
] | ||
] | ||
] | ||
end | ||
|
||
# Run "mix help compile.app" to learn about applications. | ||
def application do | ||
[ | ||
extra_applications: [:logger], | ||
mod: {BowtieJSV.App, []} | ||
] | ||
end | ||
|
||
# Run "mix help deps" to learn about dependencies. | ||
defp deps do | ||
[ | ||
{:jsv, "~> 0.3"} | ||
] | ||
end | ||
end |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/usr/bin/bash -ev | ||
trap exit INT | ||
|
||
docker build --progress plain -t localhost/jsv-bowtie . | ||
|
||
docker run --rm -i localhost/jsv-bowtie <<EOF | ||
{"cmd": "start", "version": 1} | ||
{"cmd": "dialect", "dialect": "https://json-schema.org/draft/2020-12/schema" } | ||
{"cmd": "run", "seq": 11, "case": {"description": "test case 2", "schemaxxx": {"const": 37}, "tests": [{"description": "not 37", "instance": {}}, {"description": "is 37", "instance": 37}] }} | ||
{"cmd": "run", "seq": 11, "case": {"description": "test case 2", "schema": {"format": "fail_if_uppercase"}, "tests": [{"description": "not uppercase", "instance": "hello"}, {"description": "is uppercase", "instance": "HELLO"}] }} | ||
{"cmd": "stop"} | ||
EOF | ||
|
||
docker run --rm -i localhost/jsv-bowtie <<EOF | ||
{"cmd": "start", "version": 1} | ||
{"cmd": "dialect", "dialect": "https://json-schema.org/draft/2020-12/schema" } | ||
{"cmd": "run", "seq": 11, "case": {"description": "test case 1", "schema": {}, "tests": [{"description": "a test", "instance": {}}] }} | ||
{"cmd": "run", "seq": 11, "case": {"description": "test case 2", "schema": {"const": 37}, "tests": [{"description": "not 37", "instance": {}}, {"description": "is 37", "instance": 37}] }} | ||
{"cmd": "stop"} | ||
EOF | ||
|
||
bowtie smoke -i localhost/jsv-bowtie | ||
bowtie suite -x -i localhost/jsv-bowtie draft2020-12 | bowtie summary --show failures | ||
bowtie suite -x -i localhost/jsv-bowtie draft7 | bowtie summary --show failures |