From ba18332e40d2a99a5e2c9f954a5340ecbc7dee00 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:07:16 +0530 Subject: [PATCH 01/98] add more tests --- test/jake_test.exs | 223 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 191 insertions(+), 32 deletions(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index a81268a..b7125b2 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -1,49 +1,208 @@ defmodule JakeTest do - alias ExJsonSchema.Validator - use ExUnit.Case doctest Jake - @schemas [ - %{ - "type" => "object", - "properties" => %{ - "foo" => %{ - "type" => "string" - } - } - } - ] - - test "valid" do - Enum.each(@schemas, &verify/1) - end - + @tag timeout: 300_000 test "suite" do for path <- [ "draft4/type.json", - "draft4/anyOf.json" + "draft4/anyOf.json", + "draft4/required.json", + "draft4/allOf.json", + "draft4/enum.json", + "draft4/minimum.json", + "draft4/maximum.json", + "draft4/pattern.json", + "draft4/items.json", + "draft4/minItems.json", + "draft4/maxItems.json", + "draft4/uniqItems.json", + "draft4/minLength.json", + "draft4/maxLength.json", + "draft4/maxProperties.json", + "draft4/minProperties.json", + "draft4/additionalItems.json", + "draft4/properties.json", + "draft4/additionalProperties.json", + "draft4/multipleOf.json" ] do Path.wildcard("test_suite/tests/#{path}") - |> Enum.map(fn path -> File.read!(path) |> Jason.decode!() end) + |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) |> Enum.concat() |> Enum.map(fn %{"schema" => schema} -> verify(schema) end) end end def verify(schema) do - Jake.gen(schema) - |> Enum.take(100) - |> Enum.each(fn value -> - result = Validator.validate(schema, value) - - if result != :ok do - flunk( - "Invalid data: \nschema: #{inspect(schema)}\ngenerated: #{inspect(value)}\nerror: #{ - inspect(result) - }" - ) - end - end) + Poison.encode!(schema) |> test_generator() + end + + def test_generator(jschema) do + gen = Jake.generator(jschema) + schema = Poison.decode!(jschema) + IO.inspect(Enum.take(gen, 3)) + + Enum.take(gen, 100) + |> Enum.each(fn val -> ExJsonSchema.Validator.valid?(schema, val) end) + end + + test "test anyOf" do + jschema = ~s({"anyOf": [{"type": "object"}, {"type": "array"}]}) + assert test_generator(jschema) + end + + test "test allOf" do + jschema = ~s({"allOf": [{"type": "integer"}, {"maximum": 255}]}) + assert test_generator(jschema) + end + + test "test allOf mix types" do + jschema = ~s({"allOf": [{"type": "integer"}, {"type": "boolean"}]}) + assert test_generator(jschema) + end + + test "test type both integer string" do + jschema = ~s({"type": ["string", "integer"], "maxLength": 5, "minLength": 1, "maximum": 29}) + assert test_generator(jschema) + end + + test "test object with no properties" do + jschema = ~s({"type": "object"}) + assert test_generator(jschema) + end + + test "test object with properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "required":["name", "age"]}) + + assert test_generator(jschema) + end + + test "test object with properties minmax" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":1, "maxProperties": 50}) + + assert test_generator(jschema) + end + + test "test object with properties required" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": false, "minProperties":1, "maxProperties": 5, "required": ["age", "name"]}) + + assert test_generator(jschema) + end + + test "test object with additional properties and required" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":2, "maxProperties": 5, "required": ["age"]}) + + assert test_generator(jschema) + end + + test "test array items" do + jschema = + ~s({"type": "array", "items" : [{"type": "integer"}, {"type": "string", "maxLength": 10}, {"type": "boolean"}], "additionalItems": {"type": "boolean"} }) + + assert test_generator(jschema) + end + + test "test array item with bounds" do + jschema = + ~s({"type": "array", "items" : {"type": "string", "maxLength": 10, "minLength":5}, "minItems": 1, "maxItems": 100 }) + + assert test_generator(jschema) + end + + test "test array single item" do + jschema = + ~s({"type": "array", "items" : {"type": "string", "maxLength": 10}, "additionalItems":true}) + + assert test_generator(jschema) + end + + test "test string" do + jschema = ~s({"type": "string", "maxLength": 5, "minLength": 1}) + assert test_generator(jschema) + end + + test "test string regex" do + jschema = ~s({"type": "string", "pattern": "[a-zA-Z0-9_]{5,10}@abc[.]\(org|com|in\)"}) + assert test_generator(jschema) + end + + test "test string regex with length" do + jschema = + ~s({"type": "string", "pattern": "[a-zA-Z0-9_]{5,10}@abc[.]\(org|com|in\)", "minLength": 5, "maxLength": 20}) + + assert test_generator(jschema) + end + + test "test integer" do + jschema = ~s({"type": "integer", "maximum": 111, "minimum": -87, "multipleOf": 9}) + assert test_generator(jschema) + end + + test "test integer excl" do + jschema = + ~s({"type": "integer", "maximum": 120, "minimum": -87, "multipleOf": 6, "exclusiveMaximum": true}) + + assert test_generator(jschema) + end + + test "test number" do + jschema = ~s({"type": "number", "maximum": 7.5, "minimum": 3.6}) + assert test_generator(jschema) + end + + test "test number multiple" do + jschema = ~s({"type": "number", "maximum": 9.7, "minimum": 3.2, "multipleOf": 1.5}) + assert test_generator(jschema) + end + + test "test number multiple again" do + jschema = ~s({"type": "number", "maximum": 9.8, "minimum": -3.6, "multipleOf": 2}) + assert test_generator(jschema) + end + + test "test fraction" do + jschema = ~s({"type": "number", "maximum": 9.7, "minimum": 9.65, "multipleOf": 0.04}) + assert test_generator(jschema) + end + + test "test fraction excl" do + jschema = + ~s({"type": "number", "maximum": 8.1, "minimum": 7.79, "multipleOf": 0.3, "exclusiveMaximum": true, "exclusiveMinimum": true}) + + assert test_generator(jschema) + end + + test "test number negative" do + jschema = ~s({"type": "number", "maximum": -3, "minimum": -9}) + assert test_generator(jschema) + end + + test "test integer enum" do + jschema = ~s({"type": "integer", "enum": [30, -11, 18, 75, 99, -65, null, "abc"]}) + assert test_generator(jschema) + end + + test "test only enum" do + jschema = ~s({"enum": [1, 2, "hello", -3, "world"]}) + assert test_generator(jschema) + end + + test "test boolean" do + jschema = ~s({"type": "boolean"}) + assert test_generator(jschema) + end + + test "test null" do + jschema = ~s({"type": "null"}) + assert test_generator(jschema) + end + + test "test notype" do + jschema = ~s({"maxLength": 20, "minLength": 10, "minItems": 3}) + assert test_generator(jschema) end end From c3c6789325ee5e2eebd261540b4bd24113639524 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:08:04 +0530 Subject: [PATCH 02/98] modify Jake module --- lib/jake.ex | 97 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index a8554ae..b7a0e8e 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -9,31 +9,90 @@ defmodule Jake do "string" ] - def gen(%{"anyOf" => options} = spec) when is_list(options) do - Enum.map(options, fn option -> - gen(Map.merge(Map.drop(spec, ["anyOf"]), option)) - end) + def generator(jschema) do + IO.puts(jschema) + jschema |> Poison.decode!() |> gen_init() + end + + def gen_init(%{"anyOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["anyOf"]) + + for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n))) |> StreamData.one_of() end - def gen(%{"type" => type} = spec) when is_binary(type) do - module = String.to_existing_atom("Elixir.Jake.#{String.capitalize(type)}") - apply(module, :gen, [spec]) + def gen_init(%{"allOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["allOf"]) + + Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) + |> Map.merge(nmap) + |> Jake.gen_init() end - def gen(%{"type" => types} = spec) when is_list(types) do - Enum.map(types, fn type -> - gen(%{spec | "type" => type}) - end) - |> StreamData.one_of() + def gen_init(map) do + gen_all(map, map["enum"], map["type"]) end - # type not present - def gen(spec) do - Enum.map(@types, fn type -> - Map.put(spec, "type", type) - |> gen() - end) - |> StreamData.one_of() + def gen_all(map, enum, type) when enum != nil, do: gen_enum(enum, type) + + def gen_all(map, enum, type) when is_list(type) do + list = for n <- type, do: %{"type" => n} + nmap = Map.drop(map, ["type"]) + for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init()) |> StreamData.one_of() + end + + def gen_all(map, enum, type) when type in @types, do: gen_type(type, map) + + def gen_all(map, enum, type) when type == nil do + Jake.Notype.gen_notype(map, type) + end + + def gen_type(type, map) when type == "string" do + Jake.String.gen_string(map) + end + + def gen_type(type, map) when type in ["integer", "number"] do + Jake.Number.gen_number(map, type) + end + + def gen_type(type, map) when type == "boolean" do + StreamData.boolean() + end + + def gen_type(type, map) when type == "null" do + StreamData.constant(nil) + end + + def gen_type(type, map) when type == "array" do + Jake.Array.gen_array(map, type) + end + + def gen_type(type, map) when type == "object" do + Jake.Object.gen_object(map, type) + end + + def gen_enum(list, type) do + nlist = + case type do + x when x == "integer" -> + nlist = for n <- list, is_integer(n), do: n + + x when x == "number" -> + for n <- list, is_number(n), do: n + + x when x == "string" -> + for n <- list, is_binary(n), do: n + + x when x == "array" -> + for n <- list, is_list(n), do: n + + x when x == "object" -> + for n <- list, is_map(n), do: n + + _ -> + list + end + + StreamData.member_of(nlist) end end From 0907115223a5dd07a08cfb84599fb557bdcc326b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:08:59 +0530 Subject: [PATCH 03/98] handle type key when merging --- lib/jake/map_util.ex | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/jake/map_util.ex diff --git a/lib/jake/map_util.ex b/lib/jake/map_util.ex new file mode 100644 index 0000000..39bb606 --- /dev/null +++ b/lib/jake/map_util.ex @@ -0,0 +1,33 @@ +# Ref: https://stackoverflow.com/questions/38864001/elixir-how-to-deep-merge-maps +# Ref: https://github.com/activesphere/jake/blob/master/lib/jake/map_util.ex + +defmodule Jake.MapUtil do + def deep_merge(left, right) do + Map.merge(left, right, &deep_resolve/3) + end + + defp deep_resolve(_key, left, nil) do + left + end + + defp deep_resolve(_key, nil, right) do + right + end + + defp deep_resolve(key, left, right) when key == "type" do + case {is_list(left), is_list(right)} do + {x, y} when x and y -> left ++ right + {x, y} when x and not y -> left ++ [right] + {x, y} when not x and y -> [left] ++ right + {x, y} when not x and not y -> [left, right] + end + end + + defp deep_resolve(_key, left, right) when is_map(left) do + Map.merge(left, right) + end + + defp deep_resolve(_key, left, right) when is_list(left) do + left ++ right + end +end From 83346250dd00095e8948c470bc695e2627dc4d0b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:09:45 +0530 Subject: [PATCH 04/98] modify Array module --- lib/jake/array.ex | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index f369a72..b15107c 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -1,5 +1,99 @@ defmodule Jake.Array do - def gen(_) do - StreamData.constant([]) + @type_list [ + %{"type" => "integer"}, + %{"type" => "number"}, + %{"type" => "boolean"}, + %{"type" => "string"}, + %{"type" => "null"}, + nil + ] + + @min_items 0 + + @max_items 1000 + + def gen_array(map, type), do: arraytype(map, map["enum"], map["items"]) + + def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(enum, "array") + + def arraytype(map, enum, items) when is_list(items) or is_map(items) do + list = + if is_list(items) do + for n <- items, is_map(n), do: Jake.gen_init(n) + else + [Jake.gen_init(items)] + end + + {min, max} = get_min_max(map) + + case map["additionalItems"] do + x when (is_boolean(x) and x) or is_nil(x) -> + add_additional_items(list, true, max, min) + + x when is_map(x) -> + add_additional_items(list, x, max, min) + + _ -> + add_additional_items(list, false, max, min) + end + end + + def get_min_max(map) do + min = Map.get(map, "minItems", @min_items) + max = Map.get(map, "maxItems", @max_items) + {min, max} + end + + def arraytype(map, enum, items) when is_nil(items) and is_nil(enum) do + item = get_one_of() + {min, max} = get_min_max(map) + decide_min_max(map, item, min, max) + end + + def decide_min_max(map, item, min, max) + when is_integer(min) and is_integer(max) and min < max do + if map["uniqueItems"] do + StreamData.uniq_list_of(item, min_length: min, max_length: max) + else + StreamData.list_of(item, min_length: min, max_length: max) + end + end + + def get_one_of() do + for(n <- @type_list, is_map(n), do: Jake.gen_init(n)) |> StreamData.one_of() + end + + def add_additional_items(list, bool, max, min) when is_boolean(bool) and bool do + generate_list(list, get_one_of(), max, min) + end + + def add_additional_items(list, bool, max, min) when is_boolean(bool) and not bool do + if length(list) in min..max do + StreamData.fixed_list(list) + end + end + + def add_additional_items(list, map, max, min) when is_map(map) do + generate_list(list, Jake.gen_init(map), max, min) + end + + def generate_list(olist, additional, max, min) do + StreamData.bind(StreamData.fixed_list(olist), fn list -> + StreamData.bind_filter( + StreamData.list_of(additional), + fn + nlist + when (length(list) + length(nlist)) in min..max -> + {:cont, StreamData.constant(list ++ nlist)} + + nlist + when length(list) in min..max -> + {:cont, StreamData.constant(list)} + + nlist when true -> + :skip + end + ) + end) end end From d7cd3469e1977f533fa1d18f651659f2922c927b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:11:16 +0530 Subject: [PATCH 05/98] add separate module for cases when type not present --- lib/jake/notype.ex | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/jake/notype.ex diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex new file mode 100644 index 0000000..d6c0530 --- /dev/null +++ b/lib/jake/notype.ex @@ -0,0 +1,36 @@ +defmodule Jake.Notype do + @prop %{ + "minLength" => "string", + "maxLength" => "string", + "pattern" => "string", + "multipleOf" => "number", + "minimum" => "number", + "maximum" => "number", + "exclusiveMinimum" => "number", + "exclusiveMaximum" => "number", + "items" => "array", + "additionalItems" => "array", + "minItems" => "array", + "maxItems" => "array", + "uniqueItems" => "array", + "properties" => "object", + "additionalProperties" => "object", + "required" => "object", + "minProperties" => "object", + "maxProperties" => "object" + } + + def gen_notype(map, type) do + nmap = for {k, v} <- map, into: %{}, do: {k, v} + nlist = for {k, v} <- map, into: [], do: @prop[k] + + types = + Enum.reduce(nlist, nil, fn + x, acc when not is_nil(x) -> x + x, acc when is_nil(x) -> acc + end) + + nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap + if nmap["type"], do: Jake.gen_init(nmap) + end +end From 63a16bf0e42e430019c918454ecb374205b483e9 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:11:55 +0530 Subject: [PATCH 06/98] handle multipleOf property --- lib/jake/number.ex | 156 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index f282e4e..94a939e 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -1,5 +1,157 @@ defmodule Jake.Number do - def gen(_spec) do - StreamData.integer() + @num_min 0 + + @num_max 100_000 + + def gen_number(map, type) do + gen_number_init(map, map["enum"], type) + end + + def gen_number_init(map, enum, type) when is_list(enum), do: Jake.gen_enum(enum, type) + + def gen_number_init(map, enum, type) when type in ["integer", "number"] do + {step_left, step_right} = find_step(map, map["minimum"], map["maximum"]) + min = findmin(map, @num_min, step_left, type) + + max = + if map["maximum"] == nil and map["multipleOf"] != nil and map["multipleOf"] < 1 do + findmax(map, 10, step_right, type) + else + findmax(map, @num_max, step_right, type) + end + + random_number_gen(map["multipleOf"], type, min, max) + end + + def find_step(map, low, high) when is_number(low) and is_number(high) and low <= high do + mult = map["multipleOf"] + + if is_number(mult) do + step_left = mult * (trunc(low / mult) + 1) - low + step_right = high - mult * trunc(high / mult) + + case {step_left, step_right} do + {x, y} when x == 0 and y == 0 -> + {(mult * (low / mult + 1) - low) / 2, (high - mult * (high / mult - 1)) / 2} + + {x, y} when x == 0 -> + {(mult * (low / mult + 1) - low) / 2, y} + + {x, y} when y == 0 -> + {x, (high - mult * (high / mult - 1)) / 2} + + _ -> + {step_left, step_right} + end + else + {(high - low) / 1000, (high - low) / 1000} + end + end + + def find_step(map, low, high) when true, do: {0.001, 0.001} + + def random_number_gen(mult, type, min, max) when type == "integer" do + new_min = + case min do + x when is_float(x) -> + new_min = trunc(min) + 1 + + x when is_integer(x) -> + new_min = min + end + + new_max = + case max do + x when is_float(x) -> + new_max = trunc(x) + + x when is_integer(x) -> + new_max = max + end + + random_number_int(mult, new_min, new_max) + end + + def random_number_gen(mult, type, min, max) when type == "number" do + case {mult, min, max} do + {m, x, y} when is_integer(x) and is_integer(y) -> + random_number_int(m, x, y) + + {m, x, y} when is_number(m) -> + random_number_float(m * 1.0, x, y) + + _ -> + random_number_float(mult, min, max) + end + end + + def random_number_int(mult, min, max) when is_number(mult) and round(mult) == mult do + getmultipleof(round(mult), min, max) + end + + def random_number_int(mult, min, max) when is_number(mult) and round(mult) != mult do + getmultipleof(mult, min, max) + end + + def random_number_int(mult, min, max) when is_nil(mult) do + StreamData.integer(min..max) + end + + def random_number_float(mult, min, max) when is_float(mult) do + getmultipleof(mult, min, max) + end + + def random_number_float(mult, min, max) when mult == nil do + get_float_number(min, max) + end + + def findmax(map, max, _, type) when type == "integer" do + case {map["maximum"], map["exclusiveMaximum"]} do + {x, y} when is_integer(x) and y -> x - 1 + {x, y} when is_float(x) and y -> trunc(x) + {x, _} when is_number(x) -> x + _ -> max + end + end + + def findmax(map, max, step_right, type) when type == "number" do + case {map["maximum"], map["exclusiveMaximum"]} do + {x, y} when is_number(x) and y -> x - step_right + {x, _} when is_number(x) -> x + _ -> max + end + end + + def findmin(map, min, _, type) when type == "integer" do + case {map["minimum"], map["exclusiveMinimum"]} do + {x, y} when is_integer(x) and y -> x + 1 + {x, y} when is_float(x) and y -> trunc(x) + {x, _} when is_number(x) -> x + _ -> min + end + end + + def findmin(map, min, step_left, type) when type == "number" do + case {map["minimum"], map["exclusiveMinimum"]} do + {x, y} when is_number(x) and y -> x + step_left + {x, _} when is_number(x) -> x + _ -> min + end + end + + def getmultipleof(mult, min, max) when is_integer(mult) do + fn_mod = fn x -> rem(x, mult) == 0 end + StreamData.filter(StreamData.integer(min..max), fn_mod, 100) + end + + def getmultipleof(mult, min, max) when is_float(mult) do + fn_check = fn x, y -> x * y >= min and x * y <= max end + + for(n <- trunc(min / mult)..trunc(max / mult), fn_check.(n, mult), do: n * mult) + |> StreamData.member_of() + end + + def get_float_number(min, max) do + StreamData.filter(StreamData.float([{:min, min}, {:max, max}]), fn x -> true end, 100) end end From 7f576019dd0066f4657aa17799322dabf513a546 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:12:58 +0530 Subject: [PATCH 07/98] modify Object module --- lib/jake/object.ex | 178 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 7 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 9df648f..2abba70 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -1,13 +1,177 @@ defmodule Jake.Object do - def gen(%{"properties" => properties}) do - Enum.map(properties, fn {name, spec} -> - {name, Jake.gen(spec)} + @type_list [ + %{"type" => "integer"}, + %{"type" => "number"}, + %{"type" => "boolean"}, + %{"type" => "string"}, + %{"type" => "null"}, + nil + ] + + @min_properties 0 + + @max_properties 1000 + + def get_min_max(map) do + min = Map.get(map, "minProperties", @min_properties) + max = Map.get(map, "maxProperties", @max_properties) + {min, max} + end + + def gen_object(map, type), do: objectype(map, map["enum"], map["properties"]) + + def objectype(map, enum, properties) when enum != nil, do: Jake.gen_enum(enum, "object") + + def objectype(map, enum, properties) when is_nil(properties) and is_nil(enum) do + {min, max} = get_min_max(map) + decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + end + + def objectype(map, enum, properties) when is_map(properties) do + new_prop = for {k, v} <- properties, into: %{}, do: {k, Jake.gen_init(v)} + + req = + if map["required"] do + for n <- map["required"], into: %{}, do: {n, Map.get(new_prop, n)} + end + + non_req = + if is_map(req) and map_size(req) > 0 do + for {k, v} <- new_prop, req[k] == nil, into: %{}, do: {k, v} + end + + if is_nil(req) or map_size(req) == 0 do + check_additional_properties(map, 0, req, non_req, new_prop) + else + check_additional_properties(map, Map.size(req), req, non_req, new_prop) + end + end + + def bind_function(new_prop, additional, y, z) do + StreamData.bind(StreamData.optional_map(new_prop), fn mapn -> + StreamData.bind_filter( + additional, + fn + nmap + when (map_size(mapn) + map_size(nmap)) in y..z -> + {:cont, StreamData.constant(Map.merge(mapn, nmap))} + + nmap when map_size(mapn) in y..z -> + {:cont, StreamData.constant(mapn)} + + nmap when is_map(nmap) -> + :skip + end + ) end) - |> Enum.into(%{}) - |> StreamData.fixed_map() end - def gen(_) do - StreamData.fixed_map(%{}) + def bind_function_req(req, non_req, y, z) when is_map(non_req) or is_nil(non_req) do + StreamData.bind_filter( + StreamData.fixed_map(req), + fn + mapn when is_map(non_req) -> + {:cont, + StreamData.bind_filter(StreamData.optional_map(non_req), fn + nmap + when (map_size(mapn) + map_size(nmap)) in y..z -> + {:cont, StreamData.constant(Map.merge(mapn, nmap))} + + nmap + when map_size(mapn) in y..z -> + {:cont, StreamData.constant(mapn)} + + nmap when true -> + :skip + end)} + + mapn + when is_nil(non_req) and map_size(mapn) in y..z -> + {:cont, StreamData.constant(mapn)} + + mapn when is_nil(non_req) -> + :skip + end + ) + end + + def bind_function_req(req, non_req, y, z, add) when not is_nil(non_req) do + StreamData.bind( + StreamData.fixed_map(req), + fn + mapn when is_map(non_req) -> + StreamData.bind_filter(non_req, fn + nmap + when (map_size(mapn) + map_size(nmap)) in y..z -> + {:cont, StreamData.constant(Map.merge(mapn, nmap))} + + nmap + when map_size(mapn) in y..z -> + {:cont, StreamData.constant(mapn)} + + nmap when true -> + :skip + end) + end + ) + end + + def check_additional_properties(map, req_size, req, non_req, new_prop) + when is_nil(req) or req_size == 0 do + {min, max} = get_min_max(map) + + case {map["additionalProperties"], min, max} do + {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> + additional = objectype(map, nil, nil) + bind_function(new_prop, additional, y, z) + + {x, y, z} when is_boolean(x) and not x -> + StreamData.bind_filter( + StreamData.optional_map(new_prop), + fn + map + when map_size(map) in y..z -> + {:cont, StreamData.constant(map)} + + map when true -> + :skip + end + ) + + {x, y, z} when is_map(x) -> + obj = Jake.gen_init(x) + key = Jake.gen_init(%{"type" => "string"}) + bind_function(new_prop, StreamData.map_of(key, obj), y, z) + end + end + + def check_additional_properties(map, req_size, req, non_req, new_prop) when req_size > 0 do + {min, max} = get_min_max(map) + + case {map["additionalProperties"], min, max} do + {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> + additional = objectype(map, nil, nil) + val2 = bind_function(non_req, additional, y, z) + bind_function_req(req, val2, y, z, "additional") + + {x, y, z} when is_boolean(x) and not x -> + bind_function_req(req, non_req, y, z) + + {x, y, z} when is_map(x) -> + obj = Jake.gen_init(x) + key = Jake.gen_init(%{"type" => "string"}) + val1 = StreamData.map_of(key, obj, min_length: y, max_length: z) + val2 = bind_function(non_req, val1, y, z) + bind_function_req(req, val2, y, z, "additional") + end + end + + def decide_min_max(map, key, value, min, max) + when is_integer(min) and is_integer(max) and min <= max do + if map["additionalProperties"] != nil do + objectype(map, nil, %{}) + else + StreamData.map_of(key, value, min_length: min, max_length: max) + end end end From 61ab36b18719fd731d6282939f78c683aa022ae2 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:15:38 +0530 Subject: [PATCH 08/98] modify String module --- lib/jake/string.ex | 60 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 68eb689..8e1eea8 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -1,21 +1,45 @@ defmodule Jake.String do - def gen(spec) do - options = [] - - options = - if min_length = Map.get(spec, "minLength") do - Keyword.put(options, :min_length, min_length) - else - options - end - - options = - if max_length = Map.get(spec, "maxLength") do - Keyword.put(options, :max_length, max_length) - else - options - end - - StreamData.string(:ascii, options) + @strlen_min 1 + + @strlen_max 100 + + def gen_string(map), do: stringer(map, map["enum"], map["pattern"]) + + def find_min_max(map) do + min = Map.get(map, "minLength", @strlen_min) + max = Map.get(map, "maxLength", @strlen_max) + {min, max} + end + + def stringer(map, enum, pattern) when is_nil(enum) and is_nil(pattern) do + {min, max} = find_min_max(map) + + re = Randex.stream(~r/[a-zA-Z0-9\_]{#{min},#{max}}/) + + if min <= max do + StreamData.bind_filter(StreamData.string(:alphanumeric), fn + x when byte_size(x) in min..max -> + {:cont, StreamData.constant(x)} + + x when true -> + {:cont, Enum.take(re, 1) |> StreamData.member_of()} + end) + end + end + + def stringer(map, enum, pattern) when is_list(enum) do + Jake.gen_enum(map["enum"], "string") + end + + def stringer(map, enum, pattern) when is_binary(pattern) do + {min, max} = find_min_max(map) + pat = Randex.stream(~r/#{pattern}/, mod: Randex.Generator.StreamData) + + if min <= max do + StreamData.bind_filter(pat, fn + x when byte_size(x) in min..max -> {:cont, StreamData.constant(x)} + x when true -> :skip + end) + end end end From 693fc77191ecbdda710ac902a9285755b5f0551e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:16:56 +0530 Subject: [PATCH 09/98] remove files --- lib/jake/boolean.ex | 5 ----- lib/jake/integer.ex | 7 ------- lib/jake/null.ex | 5 ----- 3 files changed, 17 deletions(-) delete mode 100644 lib/jake/boolean.ex delete mode 100644 lib/jake/integer.ex delete mode 100644 lib/jake/null.ex diff --git a/lib/jake/boolean.ex b/lib/jake/boolean.ex deleted file mode 100644 index 3f6fba8..0000000 --- a/lib/jake/boolean.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Jake.Boolean do - def gen(_) do - StreamData.boolean() - end -end diff --git a/lib/jake/integer.ex b/lib/jake/integer.ex deleted file mode 100644 index b6e98be..0000000 --- a/lib/jake/integer.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule Jake.Integer do - def gen(spec) do - min = Map.get(spec, "minimum", -9_007_199_254_740_991) - max = Map.get(spec, "maximum", 9_007_199_254_740_991) - StreamData.integer(min..max) - end -end diff --git a/lib/jake/null.ex b/lib/jake/null.ex deleted file mode 100644 index 9c9ff2c..0000000 --- a/lib/jake/null.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Jake.Null do - def gen(_) do - StreamData.constant(nil) - end -end From 42a1beda1f3a73fd807826d10771889dea4e0cb5 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 12 Nov 2018 23:19:09 +0530 Subject: [PATCH 10/98] add and modify dependencies --- mix.exs | 6 ++++-- mix.lock | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 4c4c93e..f30ad15 100644 --- a/mix.exs +++ b/mix.exs @@ -20,8 +20,10 @@ defmodule Jake.MixProject do defp deps do [ {:stream_data, "~> 0.4"}, - {:ex_json_schema, "~> 0.5"}, - {:jason, "~> 1.1", only: :test} + {:jason, "~> 1.1", only: :test}, + {:randex, git: "https://github.com/ananthakumaran/randex", tag: "v0.4.0"}, + {:poison, "~>3.1"}, + {:ex_json_schema, "~> 0.5.4"} ] end end diff --git a/mix.lock b/mix.lock index ef9688e..da58a0e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,7 @@ %{ "ex_json_schema": {:hex, :ex_json_schema, "0.5.7", "14a1bcd716e432badb19e5f54fd0f3f0be47b34d1b5957944702be90d66a6cf6", [:mix], [], "hexpm"}, "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "randex": {:git, "https://github.com/ananthakumaran/randex", "9725cb0d5d7d0f7a1e951c8abdf27d70a3a5f38b", [tag: "v0.4.0"]}, "stream_data": {:hex, :stream_data, "0.4.2", "fa86b78c88ec4eaa482c0891350fcc23f19a79059a687760ddcf8680aac2799b", [:mix], [], "hexpm"}, } From 57923baa8fb7b3e78730840c7e224be943af2d53 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 13 Nov 2018 05:10:12 +0530 Subject: [PATCH 11/98] add comment --- lib/jake/string.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 8e1eea8..264e0e4 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -13,7 +13,6 @@ defmodule Jake.String do def stringer(map, enum, pattern) when is_nil(enum) and is_nil(pattern) do {min, max} = find_min_max(map) - re = Randex.stream(~r/[a-zA-Z0-9\_]{#{min},#{max}}/) if min <= max do @@ -25,6 +24,8 @@ defmodule Jake.String do {:cont, Enum.take(re, 1) |> StreamData.member_of()} end) end + + # StreamData.string(:alphanumeric, [{:max_length, max},{:min_length, min}]) end def stringer(map, enum, pattern) when is_list(enum) do From 47485ad23306b1cb3a019352dabceb442ff59511 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 03:17:57 +0530 Subject: [PATCH 12/98] lower order of gen_all for enum --- lib/jake.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index b7a0e8e..396bb0a 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -33,8 +33,6 @@ defmodule Jake do gen_all(map, map["enum"], map["type"]) end - def gen_all(map, enum, type) when enum != nil, do: gen_enum(enum, type) - def gen_all(map, enum, type) when is_list(type) do list = for n <- type, do: %{"type" => n} nmap = Map.drop(map, ["type"]) @@ -43,6 +41,8 @@ defmodule Jake do def gen_all(map, enum, type) when type in @types, do: gen_type(type, map) + def gen_all(map, enum, type) when enum != nil, do: gen_enum(enum, type) + def gen_all(map, enum, type) when type == nil do Jake.Notype.gen_notype(map, type) end From 5313a2b1dcd001cd5dfb25beb5de72e8cd5005fe Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 03:19:42 +0530 Subject: [PATCH 13/98] modify getmultipleof and get_float_number --- lib/jake/number.ex | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 94a939e..79fd1d5 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -1,7 +1,9 @@ defmodule Jake.Number do - @num_min 0 + @num_min -9_007_199_254_740_991 - @num_max 100_000 + @num_max 9_007_199_254_740_991 + + @max_mult 100 def gen_number(map, type) do gen_number_init(map, map["enum"], type) @@ -146,12 +148,11 @@ defmodule Jake.Number do def getmultipleof(mult, min, max) when is_float(mult) do fn_check = fn x, y -> x * y >= min and x * y <= max end - - for(n <- trunc(min / mult)..trunc(max / mult), fn_check.(n, mult), do: n * mult) - |> StreamData.member_of() + lo = trunc(min / mult) + for(n <- lo..(lo + @max_mult), fn_check.(n, mult), do: n * mult) |> StreamData.member_of() end def get_float_number(min, max) do - StreamData.filter(StreamData.float([{:min, min}, {:max, max}]), fn x -> true end, 100) + StreamData.float([{:min, min}, {:max, max}]) end end From c464a0206012228f389a755d3879be0a5de8fc89 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 03:21:25 +0530 Subject: [PATCH 14/98] remove usage of bind_filter from stringer --- lib/jake/string.ex | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 264e0e4..4982ce0 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -14,18 +14,7 @@ defmodule Jake.String do def stringer(map, enum, pattern) when is_nil(enum) and is_nil(pattern) do {min, max} = find_min_max(map) re = Randex.stream(~r/[a-zA-Z0-9\_]{#{min},#{max}}/) - - if min <= max do - StreamData.bind_filter(StreamData.string(:alphanumeric), fn - x when byte_size(x) in min..max -> - {:cont, StreamData.constant(x)} - - x when true -> - {:cont, Enum.take(re, 1) |> StreamData.member_of()} - end) - end - - # StreamData.string(:alphanumeric, [{:max_length, max},{:min_length, min}]) + StreamData.string(:alphanumeric, [{:max_length, max}, {:min_length, min}]) end def stringer(map, enum, pattern) when is_list(enum) do @@ -37,10 +26,7 @@ defmodule Jake.String do pat = Randex.stream(~r/#{pattern}/, mod: Randex.Generator.StreamData) if min <= max do - StreamData.bind_filter(pat, fn - x when byte_size(x) in min..max -> {:cont, StreamData.constant(x)} - x when true -> :skip - end) + StreamData.filter(pat, fn x -> String.length(x) in min..max end) end end end From e2cf5f72202cfaa6e238c1f8d8a073192215d59d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 18:12:45 +0530 Subject: [PATCH 15/98] use deep_merge again in in allOf gen_init --- lib/jake.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jake.ex b/lib/jake.ex index 396bb0a..d0b494d 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -25,7 +25,7 @@ defmodule Jake do nmap = Map.drop(map, ["allOf"]) Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) - |> Map.merge(nmap) + |> Jake.MapUtil.deep_merge(nmap) |> Jake.gen_init() end @@ -93,6 +93,7 @@ defmodule Jake do list end + IO.inspect(nlist) StreamData.member_of(nlist) end end From d302482be766798b23af90dee523427e2ee92c4e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 18:13:39 +0530 Subject: [PATCH 16/98] handle map item and list items separately --- lib/jake/array.ex | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index b15107c..d0de6fb 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -16,13 +16,8 @@ defmodule Jake.Array do def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(enum, "array") - def arraytype(map, enum, items) when is_list(items) or is_map(items) do - list = - if is_list(items) do - for n <- items, is_map(n), do: Jake.gen_init(n) - else - [Jake.gen_init(items)] - end + def arraytype(map, enum, items) when is_list(items) do + list = for n <- items, is_map(n), do: Jake.gen_init(n) {min, max} = get_min_max(map) @@ -38,6 +33,12 @@ defmodule Jake.Array do end end + def arraytype(map, enum, items) when is_map(items) do + {min, max} = get_min_max(map) + item = Jake.gen_init(items) + decide_min_max(map, item, min, max) + end + def get_min_max(map) do min = Map.get(map, "minItems", @min_items) max = Map.get(map, "maxItems", @max_items) From 0d61b1eff840b51568d1472be2e2da6219813038 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 18:18:14 +0530 Subject: [PATCH 17/98] add separate get_min_max method to fix exclusiveMaximum feature --- lib/jake/number.ex | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 79fd1d5..ee8f051 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -9,19 +9,19 @@ defmodule Jake.Number do gen_number_init(map, map["enum"], type) end + def get_min_max(map) do + min = Map.get(map, "minimum", @num_min) + max = Map.get(map, "maximum", @num_max) + {min, max} + end + def gen_number_init(map, enum, type) when is_list(enum), do: Jake.gen_enum(enum, type) def gen_number_init(map, enum, type) when type in ["integer", "number"] do - {step_left, step_right} = find_step(map, map["minimum"], map["maximum"]) + {min_i, max_i} = get_min_max(map) + {step_left, step_right} = find_step(map, min_i, max_i) min = findmin(map, @num_min, step_left, type) - - max = - if map["maximum"] == nil and map["multipleOf"] != nil and map["multipleOf"] < 1 do - findmax(map, 10, step_right, type) - else - findmax(map, @num_max, step_right, type) - end - + max = findmax(map, @num_max, step_right, type) random_number_gen(map["multipleOf"], type, min, max) end From 574d7a9e2b3a7d047b61020cde5de8a7cd1d0584 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 15 Nov 2018 18:19:41 +0530 Subject: [PATCH 18/98] fix test_generator method --- test/jake_test.exs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index b7125b2..aaea0e1 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -12,19 +12,19 @@ defmodule JakeTest do "draft4/enum.json", "draft4/minimum.json", "draft4/maximum.json", - "draft4/pattern.json", "draft4/items.json", "draft4/minItems.json", "draft4/maxItems.json", "draft4/uniqItems.json", + "draft4/pattern.json", "draft4/minLength.json", "draft4/maxLength.json", "draft4/maxProperties.json", "draft4/minProperties.json", "draft4/additionalItems.json", - "draft4/properties.json", "draft4/additionalProperties.json", - "draft4/multipleOf.json" + "draft4/multipleOf.json", + "draft4/properties.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) @@ -43,7 +43,17 @@ defmodule JakeTest do IO.inspect(Enum.take(gen, 3)) Enum.take(gen, 100) - |> Enum.each(fn val -> ExJsonSchema.Validator.valid?(schema, val) end) + |> Enum.each(fn val -> + result = ExJsonSchema.Validator.valid?(schema, val) + + if result == false do + flunk( + "Invalid data: \nschema: #{inspect(schema)}\ngenerated: #{inspect(val)}\nerror: #{ + inspect(result) + }" + ) + end + end) end test "test anyOf" do @@ -56,11 +66,6 @@ defmodule JakeTest do assert test_generator(jschema) end - test "test allOf mix types" do - jschema = ~s({"allOf": [{"type": "integer"}, {"type": "boolean"}]}) - assert test_generator(jschema) - end - test "test type both integer string" do jschema = ~s({"type": ["string", "integer"], "maxLength": 5, "minLength": 1, "maximum": 29}) assert test_generator(jschema) @@ -191,6 +196,13 @@ defmodule JakeTest do assert test_generator(jschema) end + test "test only enum with mix types" do + jschema = + ~s({"type": ["integer", "string"], "enum": [1, 2, "hello", -3, "world", null, true]}) + + assert test_generator(jschema) + end + test "test boolean" do jschema = ~s({"type": "boolean"}) assert test_generator(jschema) From 4d8c737ee73a6baf8437ebe1c6df14a7751ce955 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 16 Nov 2018 00:21:12 +0530 Subject: [PATCH 19/98] add rudimentary support for patternProperties --- lib/jake/object.ex | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 2abba70..0814821 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -28,7 +28,18 @@ defmodule Jake.Object do end def objectype(map, enum, properties) when is_map(properties) do - new_prop = for {k, v} <- properties, into: %{}, do: {k, Jake.gen_init(v)} + nproperties = check_pattern_properties(map, properties, map["patternProperties"]) + + pmap = + if nproperties != nil and is_list(nproperties) do + nlist = for n <- nproperties, length(n) > 0, do: Enum.fetch!(n, 0) + Enum.reduce(nlist, %{}, fn x, acc -> Map.merge(x, acc) end) + else + properties + end + + map = Map.put(map, "properties", pmap) + new_prop = for {k, v} <- pmap, into: %{}, do: {k, Jake.gen_init(v)} req = if map["required"] do @@ -47,6 +58,18 @@ defmodule Jake.Object do end end + def check_pattern_properties(map, properties, pprop) do + if pprop do + for {k, v} <- properties do + for {key, value} <- pprop, + Regex.match?(~r/#{key}/, k), + do: Map.put(properties, k, Map.merge(v, value)) + end + else + properties + end + end + def bind_function(new_prop, additional, y, z) do StreamData.bind(StreamData.optional_map(new_prop), fn mapn -> StreamData.bind_filter( From 9bdc74a6d6ba8483d0131ef6994ab79df6825905 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sat, 17 Nov 2018 16:19:30 +0530 Subject: [PATCH 20/98] add patternProperties to prop map --- lib/jake/notype.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index d6c0530..68fa92f 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -14,6 +14,7 @@ defmodule Jake.Notype do "maxItems" => "array", "uniqueItems" => "array", "properties" => "object", + "patternProperties" => "object", "additionalProperties" => "object", "required" => "object", "minProperties" => "object", From 87fae255487fb00832c588e034b6aac3eb4790a9 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sat, 17 Nov 2018 16:20:53 +0530 Subject: [PATCH 21/98] add dependencies support for object --- lib/jake/object.ex | 85 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 0814821..3806a46 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -24,7 +24,15 @@ defmodule Jake.Object do def objectype(map, enum, properties) when is_nil(properties) and is_nil(enum) do {min, max} = get_min_max(map) - decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + + if map["patternProperties"] do + nlist = + for {k, v} <- map["patternProperties"], + into: %{}, + do: {Randex.stream(~r/#{k}/, mod: Randex.Generator.StreamData), Jake.gen_init(v)} + else + decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + end end def objectype(map, enum, properties) when is_map(properties) do @@ -71,7 +79,14 @@ defmodule Jake.Object do end def bind_function(new_prop, additional, y, z) do - StreamData.bind(StreamData.optional_map(new_prop), fn mapn -> + prop = + if is_list(new_prop) do + StreamData.one_of(new_prop) + else + StreamData.optional_map(new_prop) + end + + StreamData.bind(prop, fn mapn -> StreamData.bind_filter( additional, fn @@ -89,13 +104,36 @@ defmodule Jake.Object do end) end - def bind_function_req(req, non_req, y, z) when is_map(non_req) or is_nil(non_req) do + def create_dep_list_map(new_prop, dep) do + dep_list = for {k, v} <- dep, do: k + {dep_map, non_dep_map} = Map.split(new_prop, dep_list) + IO.inspect(non_dep_map) + + list_with_map = + for {k, v} <- dep do + item = %{k => get_in(new_prop, [k])} + nmap = for i <- v, into: %{}, do: {i, get_in(new_prop, [i])} + StreamData.fixed_map(Map.merge(item, nmap)) + end + + list_with_map ++ [StreamData.optional_map(non_dep_map)] + end + + def bind_function_req(non_req, req, y, z) + when is_map(non_req) or is_nil(non_req) or is_list(non_req) do + prop = + if is_list(non_req) do + StreamData.one_of(non_req) + else + StreamData.optional_map(non_req) + end + StreamData.bind_filter( StreamData.fixed_map(req), fn - mapn when is_map(non_req) -> + mapn when is_map(non_req) or is_list(non_req) -> {:cont, - StreamData.bind_filter(StreamData.optional_map(non_req), fn + StreamData.bind_filter(prop, fn nmap when (map_size(mapn) + map_size(nmap)) in y..z -> {:cont, StreamData.constant(Map.merge(mapn, nmap))} @@ -118,7 +156,7 @@ defmodule Jake.Object do ) end - def bind_function_req(req, non_req, y, z, add) when not is_nil(non_req) do + def bind_function_req(non_req, req, y, z, add) when not is_nil(non_req) do StreamData.bind( StreamData.fixed_map(req), fn @@ -149,17 +187,7 @@ defmodule Jake.Object do bind_function(new_prop, additional, y, z) {x, y, z} when is_boolean(x) and not x -> - StreamData.bind_filter( - StreamData.optional_map(new_prop), - fn - map - when map_size(map) in y..z -> - {:cont, StreamData.constant(map)} - - map when true -> - :skip - end - ) + StreamData.filter(StreamData.optional_map(new_prop), fn nmap -> map_size(nmap) in y..z end) {x, y, z} when is_map(x) -> obj = Jake.gen_init(x) @@ -168,24 +196,37 @@ defmodule Jake.Object do end end + def check_dependencies(map, non_req) do + if map["dependencies"] do + create_dep_list_map(non_req, map["dependencies"]) + else + non_req + end + end + def check_additional_properties(map, req_size, req, non_req, new_prop) when req_size > 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> additional = objectype(map, nil, nil) - val2 = bind_function(non_req, additional, y, z) - bind_function_req(req, val2, y, z, "additional") + + check_dependencies(map, non_req) + |> bind_function(additional, y, z) + |> bind_function_req(req, y, z, "additional") {x, y, z} when is_boolean(x) and not x -> - bind_function_req(req, non_req, y, z) + check_dependencies(map, non_req) + |> bind_function_req(req, y, z) {x, y, z} when is_map(x) -> obj = Jake.gen_init(x) key = Jake.gen_init(%{"type" => "string"}) val1 = StreamData.map_of(key, obj, min_length: y, max_length: z) - val2 = bind_function(non_req, val1, y, z) - bind_function_req(req, val2, y, z, "additional") + + check_dependencies(map, non_req) + |> bind_function(val1, y, z) + |> bind_function_req(req, y, z, "additional") end end From 6887e9700f0b8662e1f115de6e08917266326845 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sat, 17 Nov 2018 16:21:24 +0530 Subject: [PATCH 22/98] remove unnecessary line --- lib/jake/string.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 4982ce0..6769d8d 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -13,7 +13,6 @@ defmodule Jake.String do def stringer(map, enum, pattern) when is_nil(enum) and is_nil(pattern) do {min, max} = find_min_max(map) - re = Randex.stream(~r/[a-zA-Z0-9\_]{#{min},#{max}}/) StreamData.string(:alphanumeric, [{:max_length, max}, {:min_length, min}]) end From f4abe43b1179fb33485c186fd04c6b2ee9771e2d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sat, 17 Nov 2018 16:23:01 +0530 Subject: [PATCH 23/98] add tests for object dependencies --- test/jake_test.exs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index aaea0e1..2e4c53f 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -24,7 +24,8 @@ defmodule JakeTest do "draft4/additionalItems.json", "draft4/additionalProperties.json", "draft4/multipleOf.json", - "draft4/properties.json" + "draft4/properties.json", + "draft4/patternProperties.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) @@ -83,6 +84,27 @@ defmodule JakeTest do assert test_generator(jschema) end + test "test object with required properties and dependencies" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}}) + + assert test_generator(jschema) + end + + test "test object with required properties, dependencies and no additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":false}) + + assert test_generator(jschema) + end + + test "test object with required properties, dependencies and no map of additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":{"type":"boolean"}}) + + assert test_generator(jschema) + end + test "test object with properties minmax" do jschema = ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":1, "maxProperties": 50}) From 9a4dc7fce6ebc5d82bea0f818df14cd1f2599ff1 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 04:02:06 +0530 Subject: [PATCH 24/98] add dependencies key to prop map --- lib/jake/notype.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 68fa92f..9d3904c 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -16,6 +16,7 @@ defmodule Jake.Notype do "properties" => "object", "patternProperties" => "object", "additionalProperties" => "object", + "dependencies" => "object", "required" => "object", "minProperties" => "object", "maxProperties" => "object" From ee27150efd9091ec7980e9d17f7b31083b1ac406 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 04:03:34 +0530 Subject: [PATCH 25/98] handle dependencies when property is null --- lib/jake/object.ex | 83 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 8 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 3806a46..d111400 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -31,7 +31,59 @@ defmodule Jake.Object do into: %{}, do: {Randex.stream(~r/#{k}/, mod: Randex.Generator.StreamData), Jake.gen_init(v)} else - decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + if map["dependencies"] do + decide_dep_and_properties(map) + else + decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + end + end + end + + def gen_with_no_prop(map) do + {min, max} = get_min_max(map) + + Jake.gen_init(%{"type" => "string"}) + |> StreamData.map_of(StreamData.term(), min_length: min, max_length: max) + end + + def decide_dep_and_properties(map) do + dep = map["dependencies"] + + list_with_map = + for {k, v} <- dep do + IO.inspect(v) + + if is_list(v) do + item = %{k => StreamData.term()} + nmap = for i <- v, into: %{}, do: {i, StreamData.term()} + Map.merge(item, nmap) + else + prop_list = for {kl, vl} <- v["properties"], do: kl + {k, prop_list, v["properties"]} + end + end + + resolve_dep(map, list_with_map) + end + + def resolve_dep(map, list_with_map) do + if is_map(List.first(list_with_map)) do + properties = Enum.reduce(list_with_map, %{}, fn x, acc -> Map.merge(acc, x) end) + check_additional_properties(map, 0, nil, nil, properties) + else + dependencies = + for({k, prop_list, prop_map} <- list_with_map, do: %{k => prop_list}) + |> Enum.reduce(%{}, fn x, acc -> Map.merge(x, acc) end) + + IO.inspect(dependencies) + + properties = + for({k, prop_list, prop_map} <- list_with_map, do: prop_map) + |> Enum.reduce(%{}, fn x, acc -> Map.merge(x, acc) end) + + map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) + IO.inspect(map) + objectype(map, nil, properties) end end @@ -86,6 +138,8 @@ defmodule Jake.Object do StreamData.optional_map(new_prop) end + IO.inspect(prop) + StreamData.bind(prop, fn mapn -> StreamData.bind_filter( additional, @@ -107,7 +161,7 @@ defmodule Jake.Object do def create_dep_list_map(new_prop, dep) do dep_list = for {k, v} <- dep, do: k {dep_map, non_dep_map} = Map.split(new_prop, dep_list) - IO.inspect(non_dep_map) + # IO.inspect(non_dep_map) list_with_map = for {k, v} <- dep do @@ -177,22 +231,35 @@ defmodule Jake.Object do ) end - def check_additional_properties(map, req_size, req, non_req, new_prop) + def check_additional_properties(map, req_size, req, _non_req, new_prop) when is_nil(req) or req_size == 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = objectype(map, nil, nil) - bind_function(new_prop, additional, y, z) + additional = gen_with_no_prop(map) + + check_dependencies(map, new_prop) + |> bind_function(additional, y, z) {x, y, z} when is_boolean(x) and not x -> - StreamData.filter(StreamData.optional_map(new_prop), fn nmap -> map_size(nmap) in y..z end) + val = check_dependencies(map, new_prop) + + prop = + if is_list(val) do + StreamData.one_of(val) + else + StreamData.optional_map(val) + end + + StreamData.filter(prop, fn nmap -> map_size(nmap) in y..z end) {x, y, z} when is_map(x) -> obj = Jake.gen_init(x) key = Jake.gen_init(%{"type" => "string"}) - bind_function(new_prop, StreamData.map_of(key, obj), y, z) + + check_dependencies(map, new_prop) + |> bind_function(StreamData.map_of(key, obj), y, z) end end @@ -209,7 +276,7 @@ defmodule Jake.Object do case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = objectype(map, nil, nil) + additional = gen_with_no_prop(map) check_dependencies(map, non_req) |> bind_function(additional, y, z) From e598f80ce1ed127248772a4b6ac1954a6e6c07f0 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 04:05:40 +0530 Subject: [PATCH 26/98] handle test cases of dependencies.json --- test/jake_test.exs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index 2e4c53f..5a5a406 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -25,7 +25,8 @@ defmodule JakeTest do "draft4/additionalProperties.json", "draft4/multipleOf.json", "draft4/properties.json", - "draft4/patternProperties.json" + "draft4/patternProperties.json", + "draft4/dependencies.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) @@ -98,13 +99,34 @@ defmodule JakeTest do assert test_generator(jschema) end - test "test object with required properties, dependencies and no map of additional properties" do + test "test object with required properties, dependencies and map of additional properties" do jschema = ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":{"type":"boolean"}}) assert test_generator(jschema) end + test "test object with dependencies and map of additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":{"type":"boolean"}}) + + assert test_generator(jschema) + end + + test "test object with dependencies and no additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":false}) + + assert test_generator(jschema) + end + + test "test object with dependencies and additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":true}) + + assert test_generator(jschema) + end + test "test object with properties minmax" do jschema = ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":1, "maxProperties": 50}) From da4e3fa0e5d36116db530f2436c4b8e7da3c984a Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 19:39:38 +0530 Subject: [PATCH 27/98] add default.json --- test/jake_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index 5a5a406..d8aade2 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -26,7 +26,8 @@ defmodule JakeTest do "draft4/multipleOf.json", "draft4/properties.json", "draft4/patternProperties.json", - "draft4/dependencies.json" + "draft4/dependencies.json", + "draft4/default.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) From b3803e9020694411f2fab0cc50730694364a4520 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 19:41:12 +0530 Subject: [PATCH 28/98] separate tuple and list generation methods --- lib/jake/array.ex | 60 ++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index d0de6fb..01b6a2d 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -12,28 +12,50 @@ defmodule Jake.Array do @max_items 1000 - def gen_array(map, type), do: arraytype(map, map["enum"], map["items"]) + def gen_array(%{"items" => items} = map, _type) do + case items do + item when is_map(item) -> + gen_list(map, item) + + item when is_list(item) -> + gen_tuple(map, item) + + _ -> + raise "Invalid items in array" + end + end + + def gen_array(map, _type), do: arraytype(map, map["enum"], map["items"]) def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(enum, "array") - def arraytype(map, enum, items) when is_list(items) do + def arraytype(map, enum, items) when is_nil(items) and is_nil(enum) do + item = get_one_of() + {min, max} = get_min_max(map) + decide_min_max(map, item, min, max) + end + + def gen_tuple(map, items) do list = for n <- items, is_map(n), do: Jake.gen_init(n) {min, max} = get_min_max(map) case map["additionalItems"] do + x when is_map(x) -> + add_additional_items(list, Jake.gen_init(x), max, min) + x when (is_boolean(x) and x) or is_nil(x) -> - add_additional_items(list, true, max, min) + add_additional_items(list, get_one_of(), max, min) - x when is_map(x) -> - add_additional_items(list, x, max, min) + x when is_boolean(x) and not x and length(list) in min..max -> + StreamData.fixed_list(list) _ -> - add_additional_items(list, false, max, min) + raise "Invalid items or length of list exceeds specified bounds" end end - def arraytype(map, enum, items) when is_map(items) do + def gen_list(map, items) do {min, max} = get_min_max(map) item = Jake.gen_init(items) decide_min_max(map, item, min, max) @@ -45,12 +67,6 @@ defmodule Jake.Array do {min, max} end - def arraytype(map, enum, items) when is_nil(items) and is_nil(enum) do - item = get_one_of() - {min, max} = get_min_max(map) - decide_min_max(map, item, min, max) - end - def decide_min_max(map, item, min, max) when is_integer(min) and is_integer(max) and min < max do if map["uniqueItems"] do @@ -64,21 +80,7 @@ defmodule Jake.Array do for(n <- @type_list, is_map(n), do: Jake.gen_init(n)) |> StreamData.one_of() end - def add_additional_items(list, bool, max, min) when is_boolean(bool) and bool do - generate_list(list, get_one_of(), max, min) - end - - def add_additional_items(list, bool, max, min) when is_boolean(bool) and not bool do - if length(list) in min..max do - StreamData.fixed_list(list) - end - end - - def add_additional_items(list, map, max, min) when is_map(map) do - generate_list(list, Jake.gen_init(map), max, min) - end - - def generate_list(olist, additional, max, min) do + def add_additional_items(olist, additional, max, min) do StreamData.bind(StreamData.fixed_list(olist), fn list -> StreamData.bind_filter( StreamData.list_of(additional), @@ -91,7 +93,7 @@ defmodule Jake.Array do when length(list) in min..max -> {:cont, StreamData.constant(list)} - nlist when true -> + _ -> :skip end ) From db8a83df4bc1eeb8a470f16d778b58db2e9c1224 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 19:41:47 +0530 Subject: [PATCH 29/98] remove get_float_number method --- lib/jake/number.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index ee8f051..b8bdc33 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -104,7 +104,7 @@ defmodule Jake.Number do end def random_number_float(mult, min, max) when mult == nil do - get_float_number(min, max) + StreamData.float([{:min, min}, {:max, max}]) end def findmax(map, max, _, type) when type == "integer" do @@ -151,8 +151,4 @@ defmodule Jake.Number do lo = trunc(min / mult) for(n <- lo..(lo + @max_mult), fn_check.(n, mult), do: n * mult) |> StreamData.member_of() end - - def get_float_number(min, max) do - StreamData.float([{:min, min}, {:max, max}]) - end end From 2a84eb78d06605b115987a59061552c3b6cbd939 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 18 Nov 2018 19:52:45 +0530 Subject: [PATCH 30/98] add decide_min_max with error message --- lib/jake/array.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index 01b6a2d..92e27a2 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -76,6 +76,10 @@ defmodule Jake.Array do end end + def decide_min_max(map, item, min, max) do + raise "Bounds of items not well defined" + end + def get_one_of() do for(n <- @type_list, is_map(n), do: Jake.gen_init(n)) |> StreamData.one_of() end From 265ae51881db190fcc3b589c3738aa924ae67479 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 02:38:42 +0530 Subject: [PATCH 31/98] add test cases for oneOf feature --- test/jake_test.exs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index d8aade2..9de54cd 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -27,7 +27,8 @@ defmodule JakeTest do "draft4/properties.json", "draft4/patternProperties.json", "draft4/dependencies.json", - "draft4/default.json" + "draft4/default.json", + "draft4/oneOf.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) @@ -69,6 +70,11 @@ defmodule JakeTest do assert test_generator(jschema) end + test "test oneOf" do + jschema = ~s({"type": "integer", "minimum": 29, "oneOf": [{"maximum": 255}, {"minimum": 25}]}) + assert test_generator(jschema) + end + test "test type both integer string" do jschema = ~s({"type": ["string", "integer"], "maxLength": 5, "minLength": 1, "maximum": 29}) assert test_generator(jschema) From 9c97664f670e818377f4f08fe1e165388743b45b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 02:39:06 +0530 Subject: [PATCH 32/98] add support for oneOf feature --- lib/jake.ex | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/jake.ex b/lib/jake.ex index d0b494d..a2f8fd1 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -21,6 +21,43 @@ defmodule Jake do |> StreamData.one_of() end + def gen_init(%{"oneOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["oneOf"]) + + tail_schema = fn tail -> + Enum.reduce(tail, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) + end + + nlist = + for {n, counter} <- Enum.with_index(options) do + hd = Map.merge(nmap, n) |> Jake.gen_init() + tail = List.delete_at(options, counter) |> tail_schema.() + {hd, tail} + end + + try_one_of(nlist, 0) + end + + def try_one_of(nlist, index) do + data = filter_mutually_exclusive(nlist, index) + + try do + Enum.take(data, 25) + data + rescue + _ -> filter_mutually_exclusive(nlist, index + 1) + end + end + + def filter_mutually_exclusive(nlist, index) do + if index < length(nlist) do + {head, tail_schema} = Enum.at(nlist, index) + StreamData.filter(head, fn hd -> not ExJsonSchema.Validator.valid?(tail_schema, hd) end) + else + raise "oneOf combination not possible" + end + end + def gen_init(%{"allOf" => options} = map) when is_list(options) do nmap = Map.drop(map, ["allOf"]) From 33f8a68c486ed380d24ad2109f199d84f241ce13 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 09:11:25 +0530 Subject: [PATCH 33/98] add not tests --- test/jake_test.exs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/jake_test.exs b/test/jake_test.exs index 9de54cd..482c9e0 100644 --- a/test/jake_test.exs +++ b/test/jake_test.exs @@ -28,7 +28,8 @@ defmodule JakeTest do "draft4/patternProperties.json", "draft4/dependencies.json", "draft4/default.json", - "draft4/oneOf.json" + "draft4/oneOf.json", + "draft4/not.json" ] do Path.wildcard("test_suite/tests/#{path}") |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) @@ -70,6 +71,11 @@ defmodule JakeTest do assert test_generator(jschema) end + test "test not" do + jschema = ~s({"not": {"type": "integer"}}) + assert test_generator(jschema) + end + test "test oneOf" do jschema = ~s({"type": "integer", "minimum": 29, "oneOf": [{"maximum": 255}, {"minimum": 25}]}) assert test_generator(jschema) From 2f1f279bf779e3547807e20733b5c05ce110a9f4 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 09:14:44 +0530 Subject: [PATCH 34/98] allow returning only type name if type is not nil --- lib/jake/notype.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 9d3904c..1cb47e7 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -32,7 +32,11 @@ defmodule Jake.Notype do x, acc when is_nil(x) -> acc end) - nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"], do: Jake.gen_init(nmap) + if type == nil do + nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap + if nmap["type"], do: Jake.gen_init(nmap) + else + types + end end end From c844aba53d5205a061675b128f347b3682454fa3 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 09:15:47 +0530 Subject: [PATCH 35/98] check for not feature when refining properties --- lib/jake/object.ex | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index d111400..25fabbf 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -98,9 +98,15 @@ defmodule Jake.Object do properties end - map = Map.put(map, "properties", pmap) - new_prop = for {k, v} <- pmap, into: %{}, do: {k, Jake.gen_init(v)} + fn_not_check = fn k, v -> + if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, + do: {"null", "null"}, + else: {k, Jake.gen_init(v)} + end + map = Map.put(map, "properties", pmap) + new_prop = for {k, v} <- pmap, into: %{}, do: fn_not_check.(k, v) + new_prop = if new_prop["null"] == "null", do: Map.drop(new_prop, ["null"]), else: new_prop req = if map["required"] do for n <- map["required"], into: %{}, do: {n, Map.get(new_prop, n)} From 36f917637414c84e70bfaa07759a342c33a16aa7 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 09:16:19 +0530 Subject: [PATCH 36/98] add support for not feature --- lib/jake.ex | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/jake.ex b/lib/jake.ex index a2f8fd1..dea1b54 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -66,6 +66,24 @@ defmodule Jake do |> Jake.gen_init() end + def gen_init(%{"not" => not_schema} = map) when is_map(not_schema) do + type_val = + if not_schema["type"] do + not_schema["type"] + else + Jake.Notype.gen_notype(not_schema, "return type") + end + + type = if type_val == nil, do: "null", else: type_val + nlist = if is_list(type), do: @types -- type, else: @types -- [type] + data = for(n <- nlist, do: Jake.gen_init(%{"type" => n})) |> StreamData.one_of() + + StreamData.filter(data, fn + x when type == "null" -> true + x -> not ExJsonSchema.Validator.valid?(not_schema, x) + end) + end + def gen_init(map) do gen_all(map, map["enum"], map["type"]) end From b6aaa84f75b5aec5a4401bf39831480f5a641038 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 23:24:30 +0530 Subject: [PATCH 37/98] fix generator for patternProperties --- lib/jake/object.ex | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 25fabbf..4af2fea 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -28,8 +28,9 @@ defmodule Jake.Object do if map["patternProperties"] do nlist = for {k, v} <- map["patternProperties"], - into: %{}, - do: {Randex.stream(~r/#{k}/, mod: Randex.Generator.StreamData), Jake.gen_init(v)} + do: build_and_verify_patterns(k, v, map["patternProperties"]) + + merge_patterns(nlist) else if map["dependencies"] do decide_dep_and_properties(map) @@ -39,6 +40,28 @@ defmodule Jake.Object do end end + def merge_patterns(nlist) do + merge_maps = fn list -> Enum.reduce(list, %{}, fn x, acc -> Map.merge(acc, x) end) end + + StreamData.bind(StreamData.fixed_list(nlist), fn list -> + StreamData.constant(merge_maps.(list)) + end) + end + + def build_and_verify_patterns(key, value, pprop) do + pprop_schema = %{"patternProperties" => pprop} + IO.inspect(pprop_schema) + nkey = Randex.stream(~r/#{key}/, mod: Randex.Generator.StreamData) + nval = Jake.gen_init(value) + + StreamData.bind(nkey, fn k -> + StreamData.bind_filter(nval, fn v -> + result = ExJsonSchema.Validator.valid?(pprop_schema, %{k => v}) + if result, do: {:cont, StreamData.constant(%{k => v})}, else: :skip + end) + end) + end + def gen_with_no_prop(map) do {min, max} = get_min_max(map) @@ -107,6 +130,7 @@ defmodule Jake.Object do map = Map.put(map, "properties", pmap) new_prop = for {k, v} <- pmap, into: %{}, do: fn_not_check.(k, v) new_prop = if new_prop["null"] == "null", do: Map.drop(new_prop, ["null"]), else: new_prop + req = if map["required"] do for n <- map["required"], into: %{}, do: {n, Map.get(new_prop, n)} @@ -167,7 +191,6 @@ defmodule Jake.Object do def create_dep_list_map(new_prop, dep) do dep_list = for {k, v} <- dep, do: k {dep_map, non_dep_map} = Map.split(new_prop, dep_list) - # IO.inspect(non_dep_map) list_with_map = for {k, v} <- dep do From fa4e0daba9cab0c27970a6bdc7a45a7b28bed03d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 19 Nov 2018 23:25:32 +0530 Subject: [PATCH 38/98] experiment with property based testing --- test/jake_test_property.exs | 269 ++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 test/jake_test_property.exs diff --git a/test/jake_test_property.exs b/test/jake_test_property.exs new file mode 100644 index 0000000..97f4f48 --- /dev/null +++ b/test/jake_test_property.exs @@ -0,0 +1,269 @@ +defmodule JakeTestProperty do + use ExUnitProperties + use ExUnit.Case + doctest Jake + + @tag timeout: 300_000 + property "suite" do + for path <- [ + "draft4/type.json", + "draft4/anyOf.json", + "draft4/required.json", + "draft4/allOf.json", + "draft4/enum.json", + "draft4/minimum.json", + "draft4/maximum.json", + "draft4/items.json", + "draft4/minItems.json", + "draft4/maxItems.json", + "draft4/uniqItems.json", + "draft4/pattern.json", + "draft4/minLength.json", + "draft4/maxLength.json", + "draft4/maxProperties.json", + "draft4/minProperties.json", + "draft4/additionalItems.json", + "draft4/additionalProperties.json", + "draft4/multipleOf.json", + "draft4/properties.json", + "draft4/patternProperties.json", + "draft4/dependencies.json", + "draft4/default.json", + "draft4/oneOf.json", + "draft4/not.json" + ] do + Path.wildcard("test_suite/tests/#{path}") + |> Enum.map(fn path -> File.read!(path) |> Poison.decode!() end) + |> Enum.concat() + |> Enum.map(fn %{"schema" => schema} -> verify(schema) end) + end + end + + def verify(schema) do + Poison.encode!(schema) |> test_generator_property() + end + + def test_generator_property(jschema) do + gen = Jake.generator(jschema) + schema = Poison.decode!(jschema) + IO.inspect(Enum.take(gen, 3)) + + check all a <- gen do + assert ExJsonSchema.Validator.valid?(schema, a) + end + end + + property "test anyOf" do + jschema = ~s({"anyOf": [{"type": "object"}, {"type": "array"}]}) + test_generator_property(jschema) + end + + property "test allOf" do + jschema = ~s({"allOf": [{"type": "integer"}, {"maximum": 255}]}) + test_generator_property(jschema) + end + + property "test not" do + jschema = ~s({"not": {"type": "integer"}}) + test_generator_property(jschema) + end + + property "test oneOf" do + jschema = ~s({"type": "integer", "minimum": 29, "oneOf": [{"maximum": 255}, {"minimum": 25}]}) + test_generator_property(jschema) + end + + property "test type both integer string" do + jschema = ~s({"type": ["string", "integer"], "maxLength": 5, "minLength": 1, "maximum": 29}) + test_generator_property(jschema) + end + + property "test object with no properties" do + jschema = ~s({"type": "object"}) + test_generator_property(jschema) + end + + property "test object with properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "required":["name", "age"]}) + + test_generator_property(jschema) + end + + property "test object with required properties and dependencies" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}}) + + test_generator_property(jschema) + end + + property "test object with required properties, dependencies and no additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":false}) + + test_generator_property(jschema) + end + + property "test object with required properties, dependencies and map of additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "required":["name"], "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":{"type":"boolean"}}) + + test_generator_property(jschema) + end + + property "test object with dependencies and map of additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":{"type":"boolean"}}) + + test_generator_property(jschema) + end + + property "test object with dependencies and no additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":false}) + + test_generator_property(jschema) + end + + property "test object with dependencies and additional properties" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}, "dt":{"type":"string", "pattern":"[0][1-9]|[1-2][0-9]|[3][0-1]"}, "address": {"type":"string"}}, "dependencies":{"dt":["age"], "age":["dt"]}, "additionalProperties":true}) + + test_generator_property(jschema) + end + + property "test object with properties minmax" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":1, "maxProperties": 50}) + + test_generator_property(jschema) + end + + property "test object with properties required" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": false, "minProperties":1, "maxProperties": 5, "required": ["age", "name"]}) + + test_generator_property(jschema) + end + + property "test object with additional properties and required" do + jschema = + ~s({"type": "object", "properties": {"name":{"type":"string", "maxLength": 10}, "age":{"type": "integer", "minimum": 1, "maximum": 125}}, "additionalProperties": {"type": "integer"}, "minProperties":2, "maxProperties": 5, "required": ["age"]}) + + test_generator_property(jschema) + end + + property "test array items" do + jschema = + ~s({"type": "array", "items" : [{"type": "integer"}, {"type": "string", "maxLength": 10}, {"type": "boolean"}], "additionalItems": {"type": "boolean"} }) + + test_generator_property(jschema) + end + + property "test array item with bounds" do + jschema = + ~s({"type": "array", "items" : {"type": "string", "maxLength": 10, "minLength":5}, "minItems": 1, "maxItems": 100 }) + + test_generator_property(jschema) + end + + property "test array single item" do + jschema = + ~s({"type": "array", "items" : {"type": "string", "maxLength": 10}, "additionalItems":true}) + + test_generator_property(jschema) + end + + property "test string" do + jschema = ~s({"type": "string", "maxLength": 5, "minLength": 1}) + test_generator_property(jschema) + end + + property "test string regex" do + jschema = ~s({"type": "string", "pattern": "[a-zA-Z0-9_]{5,10}@abc[.]\(org|com|in\)"}) + test_generator_property(jschema) + end + + property "test string regex with length" do + jschema = + ~s({"type": "string", "pattern": "[a-zA-Z0-9_]{5,10}@abc[.]\(org|com|in\)", "minLength": 5, "maxLength": 20}) + + test_generator_property(jschema) + end + + property "test integer" do + jschema = ~s({"type": "integer", "maximum": 111, "minimum": -87, "multipleOf": 9}) + test_generator_property(jschema) + end + + property "test integer excl" do + jschema = + ~s({"type": "integer", "maximum": 120, "minimum": -87, "multipleOf": 6, "exclusiveMaximum": true}) + + test_generator_property(jschema) + end + + property "test number" do + jschema = ~s({"type": "number", "maximum": 7.5, "minimum": 3.6}) + test_generator_property(jschema) + end + + property "test number multiple" do + jschema = ~s({"type": "number", "maximum": 9.7, "minimum": 3.2, "multipleOf": 1.5}) + test_generator_property(jschema) + end + + property "test number multiple again" do + jschema = ~s({"type": "number", "maximum": 9.8, "minimum": -3.6, "multipleOf": 2}) + test_generator_property(jschema) + end + + property "test fraction" do + jschema = ~s({"type": "number", "maximum": 9.7, "minimum": 9.65, "multipleOf": 0.04}) + test_generator_property(jschema) + end + + property "test fraction excl" do + jschema = + ~s({"type": "number", "maximum": 8.1, "minimum": 7.79, "multipleOf": 0.3, "exclusiveMaximum": true, "exclusiveMinimum": true}) + + test_generator_property(jschema) + end + + property "test number negative" do + jschema = ~s({"type": "number", "maximum": -3, "minimum": -9}) + test_generator_property(jschema) + end + + property "test integer enum" do + jschema = ~s({"type": "integer", "enum": [30, -11, 18, 75, 99, -65, null, "abc"]}) + test_generator_property(jschema) + end + + property "test only enum" do + jschema = ~s({"enum": [1, 2, "hello", -3, "world"]}) + test_generator_property(jschema) + end + + property "test only enum with mix types" do + jschema = + ~s({"type": ["integer", "string"], "enum": [1, 2, "hello", -3, "world", null, true]}) + + test_generator_property(jschema) + end + + property "test boolean" do + jschema = ~s({"type": "boolean"}) + test_generator_property(jschema) + end + + property "test null" do + jschema = ~s({"type": "null"}) + test_generator_property(jschema) + end + + property "test notype" do + jschema = ~s({"maxLength": 20, "minLength": 10, "minItems": 3}) + test_generator_property(jschema) + end +end From 6c0bbf12b92287cd1bd9c9b8ac8df26b0a679541 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 00:32:04 +0530 Subject: [PATCH 39/98] add map parameter to gen_enum --- lib/jake/array.ex | 2 +- lib/jake/number.ex | 2 +- lib/jake/object.ex | 2 +- lib/jake/string.ex | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index 92e27a2..ada284c 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -27,7 +27,7 @@ defmodule Jake.Array do def gen_array(map, _type), do: arraytype(map, map["enum"], map["items"]) - def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(enum, "array") + def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(map, enum, "array") def arraytype(map, enum, items) when is_nil(items) and is_nil(enum) do item = get_one_of() diff --git a/lib/jake/number.ex b/lib/jake/number.ex index b8bdc33..9c2caf9 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -15,7 +15,7 @@ defmodule Jake.Number do {min, max} end - def gen_number_init(map, enum, type) when is_list(enum), do: Jake.gen_enum(enum, type) + def gen_number_init(map, enum, type) when is_list(enum), do: Jake.gen_enum(map, enum, type) def gen_number_init(map, enum, type) when type in ["integer", "number"] do {min_i, max_i} = get_min_max(map) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 4af2fea..5016433 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -20,7 +20,7 @@ defmodule Jake.Object do def gen_object(map, type), do: objectype(map, map["enum"], map["properties"]) - def objectype(map, enum, properties) when enum != nil, do: Jake.gen_enum(enum, "object") + def objectype(map, enum, properties) when enum != nil, do: Jake.gen_enum(map, enum, "object") def objectype(map, enum, properties) when is_nil(properties) and is_nil(enum) do {min, max} = get_min_max(map) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 6769d8d..0813c27 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -17,7 +17,7 @@ defmodule Jake.String do end def stringer(map, enum, pattern) when is_list(enum) do - Jake.gen_enum(map["enum"], "string") + Jake.gen_enum(map, map["enum"], "string") end def stringer(map, enum, pattern) when is_binary(pattern) do From ae8e0c096d804a2972f03fa38b2ea97f8008eb33 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 00:33:36 +0530 Subject: [PATCH 40/98] validate enum values against existing schema --- lib/jake.ex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index dea1b54..a0c04fd 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -96,7 +96,7 @@ defmodule Jake do def gen_all(map, enum, type) when type in @types, do: gen_type(type, map) - def gen_all(map, enum, type) when enum != nil, do: gen_enum(enum, type) + def gen_all(map, enum, type) when enum != nil, do: gen_enum(map, enum, type) def gen_all(map, enum, type) when type == nil do Jake.Notype.gen_notype(map, type) @@ -126,7 +126,7 @@ defmodule Jake do Jake.Object.gen_object(map, type) end - def gen_enum(list, type) do + def gen_enum(map, list, type) do nlist = case type do x when x == "integer" -> @@ -148,7 +148,8 @@ defmodule Jake do list end - IO.inspect(nlist) - StreamData.member_of(nlist) + nmap = Map.drop(map, ["enum"]) + Enum.filter(nlist, fn x -> ExJsonSchema.Validator.valid?(nmap, x) end) + |> StreamData.member_of() end end From 1385cb328eea279c44a0476bc277a878d5cd625c Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 00:35:19 +0530 Subject: [PATCH 41/98] add test enum with constraints --- test/jake_test_property.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/jake_test_property.exs b/test/jake_test_property.exs index 97f4f48..7c1e9e4 100644 --- a/test/jake_test_property.exs +++ b/test/jake_test_property.exs @@ -252,6 +252,13 @@ defmodule JakeTestProperty do test_generator_property(jschema) end + property "test enum with constraints" do + jschema = + ~s({"type": ["integer", "string"], "enum": [1, 2, "four", "hello", -3, "worlds", null, true], "minimum": -1, "minLength": 5}) + + test_generator_property(jschema) + end + property "test boolean" do jschema = ~s({"type": "boolean"}) test_generator_property(jschema) From 4d7ccbfced322b84760a24509938fdd741a42535 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 01:22:27 +0530 Subject: [PATCH 42/98] modify gen_init and add gen_init_root --- lib/jake.ex | 77 +++++------------------------------------------------ 1 file changed, 6 insertions(+), 71 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index a0c04fd..b4972bb 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -14,79 +14,15 @@ defmodule Jake do jschema |> Poison.decode!() |> gen_init() end - def gen_init(%{"anyOf" => options} = map) when is_list(options) do - nmap = Map.drop(map, ["anyOf"]) - - for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n))) - |> StreamData.one_of() - end - - def gen_init(%{"oneOf" => options} = map) when is_list(options) do - nmap = Map.drop(map, ["oneOf"]) - - tail_schema = fn tail -> - Enum.reduce(tail, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) - end - - nlist = - for {n, counter} <- Enum.with_index(options) do - hd = Map.merge(nmap, n) |> Jake.gen_init() - tail = List.delete_at(options, counter) |> tail_schema.() - {hd, tail} - end - - try_one_of(nlist, 0) - end - - def try_one_of(nlist, index) do - data = filter_mutually_exclusive(nlist, index) - - try do - Enum.take(data, 25) - data - rescue - _ -> filter_mutually_exclusive(nlist, index + 1) - end - end - - def filter_mutually_exclusive(nlist, index) do - if index < length(nlist) do - {head, tail_schema} = Enum.at(nlist, index) - StreamData.filter(head, fn hd -> not ExJsonSchema.Validator.valid?(tail_schema, hd) end) + def gen_init(map) do + if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do + Jake.Mixed.gen_mixed(map) else - raise "oneOf combination not possible" + gen_init_root(map) end end - def gen_init(%{"allOf" => options} = map) when is_list(options) do - nmap = Map.drop(map, ["allOf"]) - - Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) - |> Jake.MapUtil.deep_merge(nmap) - |> Jake.gen_init() - end - - def gen_init(%{"not" => not_schema} = map) when is_map(not_schema) do - type_val = - if not_schema["type"] do - not_schema["type"] - else - Jake.Notype.gen_notype(not_schema, "return type") - end - - type = if type_val == nil, do: "null", else: type_val - nlist = if is_list(type), do: @types -- type, else: @types -- [type] - data = for(n <- nlist, do: Jake.gen_init(%{"type" => n})) |> StreamData.one_of() - - StreamData.filter(data, fn - x when type == "null" -> true - x -> not ExJsonSchema.Validator.valid?(not_schema, x) - end) - end - - def gen_init(map) do - gen_all(map, map["enum"], map["type"]) - end + def gen_init_root(map), do: gen_all(map, map["enum"], map["type"]) def gen_all(map, enum, type) when is_list(type) do list = for n <- type, do: %{"type" => n} @@ -148,8 +84,7 @@ defmodule Jake do list end - nmap = Map.drop(map, ["enum"]) - Enum.filter(nlist, fn x -> ExJsonSchema.Validator.valid?(nmap, x) end) + Enum.filter(nlist, fn x -> ExJsonSchema.Validator.valid?(map, x) end) |> StreamData.member_of() end end From ba76b77acf897c33e5d49d4cd07ba40a75d86098 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 01:23:25 +0530 Subject: [PATCH 43/98] add separate Mixed module --- lib/jake/mixed.ex | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 lib/jake/mixed.ex diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex new file mode 100644 index 0000000..5924be3 --- /dev/null +++ b/lib/jake/mixed.ex @@ -0,0 +1,81 @@ +defmodule Jake.Mixed do + @types [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + + def gen_mixed(%{"anyOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["anyOf"]) + + for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n))) + |> StreamData.one_of() + end + + def gen_mixed(%{"oneOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["oneOf"]) + + tail_schema = fn tail -> + Enum.reduce(tail, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) + end + + nlist = + for {n, counter} <- Enum.with_index(options) do + hd = Map.merge(nmap, n) |> Jake.gen_init() + tail = List.delete_at(options, counter) |> tail_schema.() + {hd, tail} + end + + try_one_of(nlist, 0) + end + + def try_one_of(nlist, index) do + data = filter_mutually_exclusive(nlist, index) + + try do + Enum.take(data, 25) + data + rescue + _ -> filter_mutually_exclusive(nlist, index + 1) + end + end + + def filter_mutually_exclusive(nlist, index) do + if index < length(nlist) do + {head, tail_schema} = Enum.at(nlist, index) + StreamData.filter(head, fn hd -> not ExJsonSchema.Validator.valid?(tail_schema, hd) end) + else + raise "oneOf combination not possible" + end + end + + def gen_mixed(%{"allOf" => options} = map) when is_list(options) do + nmap = Map.drop(map, ["allOf"]) + + Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) + |> Jake.MapUtil.deep_merge(nmap) + |> Jake.gen_init() + end + + def gen_mixed(%{"not" => not_schema} = map) when is_map(not_schema) do + type_val = + if not_schema["type"] do + not_schema["type"] + else + Jake.Notype.gen_notype(not_schema, "return type") + end + + type = if type_val == nil, do: "null", else: type_val + nlist = if is_list(type), do: @types -- type, else: @types -- [type] + data = for(n <- nlist, do: Jake.gen_init(%{"type" => n})) |> StreamData.one_of() + + StreamData.filter(data, fn + x when type == "null" -> true + x -> not ExJsonSchema.Validator.valid?(not_schema, x) + end) + end +end From e90b4678a92da21cc7b6d3b87dc7ec9acef898df Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 10:45:19 +0530 Subject: [PATCH 44/98] modify gen_enum --- lib/jake.ex | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index b4972bb..ddc7fe6 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -9,6 +9,15 @@ defmodule Jake do "string" ] + @type_map %{ + "number" => &Kernel.is_number/1, + "integer" => &Kernel.is_integer/1, + "string" => &Kernel.is_binary/1, + "array" => &Kernel.is_list/1, + "object" => &Kernel.is_map/1, + "boolean" => &Kernel.is_boolean/1 + } + def generator(jschema) do IO.puts(jschema) jschema |> Poison.decode!() |> gen_init() @@ -63,25 +72,13 @@ defmodule Jake do end def gen_enum(map, list, type) do - nlist = - case type do - x when x == "integer" -> - nlist = for n <- list, is_integer(n), do: n - - x when x == "number" -> - for n <- list, is_number(n), do: n - - x when x == "string" -> - for n <- list, is_binary(n), do: n + check_type = fn type, x -> @type_map[type] |> apply([x]) end - x when x == "array" -> - for n <- list, is_list(n), do: n - - x when x == "object" -> - for n <- list, is_map(n), do: n - - _ -> - list + nlist = + if type in @types do + for n <- list, check_type.(type, n), do: n + else + list end Enum.filter(nlist, fn x -> ExJsonSchema.Validator.valid?(map, x) end) From 923a3b5b026524b0f814f6521c1ff629ec23d7d0 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 14:31:43 +0530 Subject: [PATCH 45/98] remove unnecessary code --- lib/jake/object.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 5016433..276f319 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -225,7 +225,7 @@ defmodule Jake.Object do when map_size(mapn) in y..z -> {:cont, StreamData.constant(mapn)} - nmap when true -> + _ -> :skip end)} @@ -253,7 +253,7 @@ defmodule Jake.Object do when map_size(mapn) in y..z -> {:cont, StreamData.constant(mapn)} - nmap when true -> + _ -> :skip end) end From 99c4dbaa0e5c54170d6293bdc18285c718762c86 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 15:38:35 +0530 Subject: [PATCH 46/98] remove type checking from gen_enum --- lib/jake.ex | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index ddc7fe6..b918f1f 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -72,16 +72,7 @@ defmodule Jake do end def gen_enum(map, list, type) do - check_type = fn type, x -> @type_map[type] |> apply([x]) end - - nlist = - if type in @types do - for n <- list, check_type.(type, n), do: n - else - list - end - - Enum.filter(nlist, fn x -> ExJsonSchema.Validator.valid?(map, x) end) + Enum.filter(list, fn x -> ExJsonSchema.Validator.valid?(map, x) end) |> StreamData.member_of() end end From 48ff9d006e847245e0848883a7a51ec919a9ff3e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 16:36:46 +0530 Subject: [PATCH 47/98] remove type_map and code clean-up --- lib/jake.ex | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index b918f1f..87ff830 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -9,15 +9,6 @@ defmodule Jake do "string" ] - @type_map %{ - "number" => &Kernel.is_number/1, - "integer" => &Kernel.is_integer/1, - "string" => &Kernel.is_binary/1, - "array" => &Kernel.is_list/1, - "object" => &Kernel.is_map/1, - "boolean" => &Kernel.is_boolean/1 - } - def generator(jschema) do IO.puts(jschema) jschema |> Poison.decode!() |> gen_init() @@ -27,51 +18,49 @@ defmodule Jake do if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do Jake.Mixed.gen_mixed(map) else - gen_init_root(map) + gen_all(map, map["enum"], map["type"]) end end - def gen_init_root(map), do: gen_all(map, map["enum"], map["type"]) + def gen_all(map, enum, _type) when enum != nil, do: gen_enum(map, enum) - def gen_all(map, enum, type) when is_list(type) do + def gen_all(map, _enum, type) when is_list(type) do list = for n <- type, do: %{"type" => n} nmap = Map.drop(map, ["type"]) for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init()) |> StreamData.one_of() end - def gen_all(map, enum, type) when type in @types, do: gen_type(type, map) - - def gen_all(map, enum, type) when enum != nil, do: gen_enum(map, enum, type) + def gen_all(map, _enum, type) when type in @types, do: gen_type(type, map) - def gen_all(map, enum, type) when type == nil do + def gen_all(map, _enum, type) when type == nil do Jake.Notype.gen_notype(map, type) end def gen_type(type, map) when type == "string" do - Jake.String.gen_string(map) + Jake.String.gen_string(map, map["pattern"]) end def gen_type(type, map) when type in ["integer", "number"] do Jake.Number.gen_number(map, type) end - def gen_type(type, map) when type == "boolean" do + def gen_type(type, _map) when type == "boolean" do StreamData.boolean() end - def gen_type(type, map) when type == "null" do + def gen_type(type, _map) when type == "null" do StreamData.constant(nil) end def gen_type(type, map) when type == "array" do - Jake.Array.gen_array(map, type) + Jake.Array.gen_array(map) end def gen_type(type, map) when type == "object" do - Jake.Object.gen_object(map, type) + Jake.Object.gen_object(map, map["properties"]) end - def gen_enum(map, list, type) do + def gen_enum(map, list) do Enum.filter(list, fn x -> ExJsonSchema.Validator.valid?(map, x) end) |> StreamData.member_of() end From ed96a02347579c24694c940c439145a7146d9fd6 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 16:38:46 +0530 Subject: [PATCH 48/98] remove gen_enum --- lib/jake/array.ex | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index ada284c..2629112 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -12,7 +12,7 @@ defmodule Jake.Array do @max_items 1000 - def gen_array(%{"items" => items} = map, _type) do + def gen_array(%{"items" => items} = map) do case items do item when is_map(item) -> gen_list(map, item) @@ -25,11 +25,9 @@ defmodule Jake.Array do end end - def gen_array(map, _type), do: arraytype(map, map["enum"], map["items"]) + def gen_array(map), do: arraytype(map, map["items"]) - def arraytype(map, enum, items) when enum != nil, do: Jake.gen_enum(map, enum, "array") - - def arraytype(map, enum, items) when is_nil(items) and is_nil(enum) do + def arraytype(map, items) when is_nil(items) do item = get_one_of() {min, max} = get_min_max(map) decide_min_max(map, item, min, max) From ffca40c3ada11db6cc04c49ef5705385a7b6acbd Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 16:39:37 +0530 Subject: [PATCH 49/98] replace gen_number_init with gen_number --- lib/jake/number.ex | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 9c2caf9..4601dbb 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -5,19 +5,13 @@ defmodule Jake.Number do @max_mult 100 - def gen_number(map, type) do - gen_number_init(map, map["enum"], type) - end - def get_min_max(map) do min = Map.get(map, "minimum", @num_min) max = Map.get(map, "maximum", @num_max) {min, max} end - def gen_number_init(map, enum, type) when is_list(enum), do: Jake.gen_enum(map, enum, type) - - def gen_number_init(map, enum, type) when type in ["integer", "number"] do + def gen_number(map, type) do {min_i, max_i} = get_min_max(map) {step_left, step_right} = find_step(map, min_i, max_i) min = findmin(map, @num_min, step_left, type) From b7a842719efee9d77f939acdf10dfeba0677b1ed Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 16:40:54 +0530 Subject: [PATCH 50/98] remove type_list and rearrange code --- lib/jake/object.ex | 111 ++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 62 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 276f319..e75acda 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -1,13 +1,4 @@ defmodule Jake.Object do - @type_list [ - %{"type" => "integer"}, - %{"type" => "number"}, - %{"type" => "boolean"}, - %{"type" => "string"}, - %{"type" => "null"}, - nil - ] - @min_properties 0 @max_properties 1000 @@ -18,11 +9,7 @@ defmodule Jake.Object do {min, max} end - def gen_object(map, type), do: objectype(map, map["enum"], map["properties"]) - - def objectype(map, enum, properties) when enum != nil, do: Jake.gen_enum(map, enum, "object") - - def objectype(map, enum, properties) when is_nil(properties) and is_nil(enum) do + def gen_object(map, properties) when is_nil(properties) do {min, max} = get_min_max(map) if map["patternProperties"] do @@ -40,6 +27,44 @@ defmodule Jake.Object do end end + def gen_object(map, properties) when is_map(properties) do + nproperties = check_pattern_properties(map, properties, map["patternProperties"]) + + pmap = + if nproperties != nil and is_list(nproperties) do + nlist = for n <- nproperties, length(n) > 0, do: Enum.fetch!(n, 0) + Enum.reduce(nlist, %{}, fn x, acc -> Map.merge(x, acc) end) + else + properties + end + + fn_not_check = fn k, v -> + if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, + do: {"null", "null"}, + else: {k, Jake.gen_init(v)} + end + + map = Map.put(map, "properties", pmap) + new_prop = for {k, v} <- pmap, into: %{}, do: fn_not_check.(k, v) + new_prop = if new_prop["null"] == "null", do: Map.drop(new_prop, ["null"]), else: new_prop + + req = + if map["required"] do + for n <- map["required"], into: %{}, do: {n, Map.get(new_prop, n)} + end + + non_req = + if is_map(req) and map_size(req) > 0 do + for {k, v} <- new_prop, req[k] == nil, into: %{}, do: {k, v} + end + + if is_nil(req) or map_size(req) == 0 do + check_additional_properties(map, 0, req, non_req, new_prop) + else + check_additional_properties(map, Map.size(req), req, non_req, new_prop) + end + end + def merge_patterns(nlist) do merge_maps = fn list -> Enum.reduce(list, %{}, fn x, acc -> Map.merge(acc, x) end) end @@ -106,45 +131,7 @@ defmodule Jake.Object do map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) IO.inspect(map) - objectype(map, nil, properties) - end - end - - def objectype(map, enum, properties) when is_map(properties) do - nproperties = check_pattern_properties(map, properties, map["patternProperties"]) - - pmap = - if nproperties != nil and is_list(nproperties) do - nlist = for n <- nproperties, length(n) > 0, do: Enum.fetch!(n, 0) - Enum.reduce(nlist, %{}, fn x, acc -> Map.merge(x, acc) end) - else - properties - end - - fn_not_check = fn k, v -> - if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, - do: {"null", "null"}, - else: {k, Jake.gen_init(v)} - end - - map = Map.put(map, "properties", pmap) - new_prop = for {k, v} <- pmap, into: %{}, do: fn_not_check.(k, v) - new_prop = if new_prop["null"] == "null", do: Map.drop(new_prop, ["null"]), else: new_prop - - req = - if map["required"] do - for n <- map["required"], into: %{}, do: {n, Map.get(new_prop, n)} - end - - non_req = - if is_map(req) and map_size(req) > 0 do - for {k, v} <- new_prop, req[k] == nil, into: %{}, do: {k, v} - end - - if is_nil(req) or map_size(req) == 0 do - check_additional_properties(map, 0, req, non_req, new_prop) - else - check_additional_properties(map, Map.size(req), req, non_req, new_prop) + gen_object(map, properties) end end @@ -292,14 +279,6 @@ defmodule Jake.Object do end end - def check_dependencies(map, non_req) do - if map["dependencies"] do - create_dep_list_map(non_req, map["dependencies"]) - else - non_req - end - end - def check_additional_properties(map, req_size, req, non_req, new_prop) when req_size > 0 do {min, max} = get_min_max(map) @@ -326,10 +305,18 @@ defmodule Jake.Object do end end + def check_dependencies(map, non_req) do + if map["dependencies"] do + create_dep_list_map(non_req, map["dependencies"]) + else + non_req + end + end + def decide_min_max(map, key, value, min, max) when is_integer(min) and is_integer(max) and min <= max do if map["additionalProperties"] != nil do - objectype(map, nil, %{}) + gen_object(map, %{}) else StreamData.map_of(key, value, min_length: min, max_length: max) end From 592bdb1730303dc06894cea383238cc831755f1d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 20 Nov 2018 16:46:24 +0530 Subject: [PATCH 51/98] replace stringer with gen_string and remove enum as function parameter --- lib/jake/string.ex | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 0813c27..58bdbf8 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -3,24 +3,18 @@ defmodule Jake.String do @strlen_max 100 - def gen_string(map), do: stringer(map, map["enum"], map["pattern"]) - def find_min_max(map) do min = Map.get(map, "minLength", @strlen_min) max = Map.get(map, "maxLength", @strlen_max) {min, max} end - def stringer(map, enum, pattern) when is_nil(enum) and is_nil(pattern) do + def gen_string(map, pattern) when is_nil(pattern) do {min, max} = find_min_max(map) StreamData.string(:alphanumeric, [{:max_length, max}, {:min_length, min}]) end - def stringer(map, enum, pattern) when is_list(enum) do - Jake.gen_enum(map, map["enum"], "string") - end - - def stringer(map, enum, pattern) when is_binary(pattern) do + def gen_string(map, pattern) when is_binary(pattern) do {min, max} = find_min_max(map) pat = Randex.stream(~r/#{pattern}/, mod: Randex.Generator.StreamData) From 445acb9207a6e4b9d3561623a700a729e96bb35a Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:19:14 +0530 Subject: [PATCH 52/98] add support for ref --- lib/jake.ex | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 87ff830..2bc04e6 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -11,53 +11,58 @@ defmodule Jake do def generator(jschema) do IO.puts(jschema) - jschema |> Poison.decode!() |> gen_init() + map = jschema |> Poison.decode!() + gen_init(map, map) end - def gen_init(map) do + def gen_init(map, omap) do + map = Jake.Ref.expand_ref(map["$ref"], map, omap) + if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do - Jake.Mixed.gen_mixed(map) + Jake.Mixed.gen_mixed(map, omap) else - gen_all(map, map["enum"], map["type"]) + gen_all(map, map["enum"], map["type"], omap) end end - def gen_all(map, enum, _type) when enum != nil, do: gen_enum(map, enum) + def gen_all(map, enum, _type, _omap) when enum != nil, do: gen_enum(map, enum) - def gen_all(map, _enum, type) when is_list(type) do + def gen_all(map, _enum, type, omap) when is_list(type) do list = for n <- type, do: %{"type" => n} nmap = Map.drop(map, ["type"]) - for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init()) |> StreamData.one_of() + + for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init(omap)) + |> StreamData.one_of() end - def gen_all(map, _enum, type) when type in @types, do: gen_type(type, map) + def gen_all(map, _enum, type, omap) when type in @types, do: gen_type(type, map, omap) - def gen_all(map, _enum, type) when type == nil do - Jake.Notype.gen_notype(map, type) + def gen_all(map, _enum, type, omap) when type == nil do + Jake.Notype.gen_notype(map, type, omap) end - def gen_type(type, map) when type == "string" do + def gen_type(type, map, omap) when type == "string" do Jake.String.gen_string(map, map["pattern"]) end - def gen_type(type, map) when type in ["integer", "number"] do - Jake.Number.gen_number(map, type) + def gen_type(type, map, omap) when type in ["integer", "number"] do + Jake.Number.gen_number(map, type, omap) end - def gen_type(type, _map) when type == "boolean" do + def gen_type(type, _map, _omap) when type == "boolean" do StreamData.boolean() end - def gen_type(type, _map) when type == "null" do + def gen_type(type, _map, _omap) when type == "null" do StreamData.constant(nil) end - def gen_type(type, map) when type == "array" do - Jake.Array.gen_array(map) + def gen_type(type, map, omap) when type == "array" do + Jake.Array.gen_array(map, omap) end - def gen_type(type, map) when type == "object" do - Jake.Object.gen_object(map, map["properties"]) + def gen_type(type, map, omap) when type == "object" do + Jake.Object.gen_object(map, map["properties"], omap) end def gen_enum(map, list) do From b371e98fcc6162e4e811ca5fce2d0a39ddedee3c Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:20:22 +0530 Subject: [PATCH 53/98] add omap parameter to gen_array --- lib/jake/array.ex | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index 2629112..be46b48 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -12,38 +12,38 @@ defmodule Jake.Array do @max_items 1000 - def gen_array(%{"items" => items} = map) do + def gen_array(%{"items" => items} = map, omap) do case items do item when is_map(item) -> - gen_list(map, item) + gen_list(map, item, omap) item when is_list(item) -> - gen_tuple(map, item) + gen_tuple(map, item, omap) _ -> raise "Invalid items in array" end end - def gen_array(map), do: arraytype(map, map["items"]) + def gen_array(map, omap), do: arraytype(map, map["items"], omap) - def arraytype(map, items) when is_nil(items) do - item = get_one_of() + def arraytype(map, items, omap) when is_nil(items) do + item = get_one_of(omap) {min, max} = get_min_max(map) decide_min_max(map, item, min, max) end - def gen_tuple(map, items) do - list = for n <- items, is_map(n), do: Jake.gen_init(n) + def gen_tuple(map, items, omap) do + list = for n <- items, is_map(n), do: Jake.gen_init(n, omap) {min, max} = get_min_max(map) case map["additionalItems"] do x when is_map(x) -> - add_additional_items(list, Jake.gen_init(x), max, min) + add_additional_items(list, Jake.gen_init(x, omap), max, min) x when (is_boolean(x) and x) or is_nil(x) -> - add_additional_items(list, get_one_of(), max, min) + add_additional_items(list, get_one_of(omap), max, min) x when is_boolean(x) and not x and length(list) in min..max -> StreamData.fixed_list(list) @@ -53,9 +53,9 @@ defmodule Jake.Array do end end - def gen_list(map, items) do + def gen_list(map, items, omap) do {min, max} = get_min_max(map) - item = Jake.gen_init(items) + item = Jake.gen_init(items, omap) decide_min_max(map, item, min, max) end @@ -78,8 +78,8 @@ defmodule Jake.Array do raise "Bounds of items not well defined" end - def get_one_of() do - for(n <- @type_list, is_map(n), do: Jake.gen_init(n)) |> StreamData.one_of() + def get_one_of(omap) do + for(n <- @type_list, is_map(n), do: Jake.gen_init(n, omap)) |> StreamData.one_of() end def add_additional_items(olist, additional, max, min) do From 2608cb33044d42f904bc246f1e10fa9945b07506 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:21:30 +0530 Subject: [PATCH 54/98] rearrange code and add omap parameter to gen_mixed --- lib/jake/mixed.ex | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex index 5924be3..926eb1d 100644 --- a/lib/jake/mixed.ex +++ b/lib/jake/mixed.ex @@ -9,14 +9,14 @@ defmodule Jake.Mixed do "string" ] - def gen_mixed(%{"anyOf" => options} = map) when is_list(options) do + def gen_mixed(%{"anyOf" => options} = map, omap) when is_list(options) do nmap = Map.drop(map, ["anyOf"]) - for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n))) + for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n), omap)) |> StreamData.one_of() end - def gen_mixed(%{"oneOf" => options} = map) when is_list(options) do + def gen_mixed(%{"oneOf" => options} = map, omap) when is_list(options) do nmap = Map.drop(map, ["oneOf"]) tail_schema = fn tail -> @@ -25,7 +25,7 @@ defmodule Jake.Mixed do nlist = for {n, counter} <- Enum.with_index(options) do - hd = Map.merge(nmap, n) |> Jake.gen_init() + hd = Map.merge(nmap, n) |> Jake.gen_init(omap) tail = List.delete_at(options, counter) |> tail_schema.() {hd, tail} end @@ -33,49 +33,49 @@ defmodule Jake.Mixed do try_one_of(nlist, 0) end - def try_one_of(nlist, index) do - data = filter_mutually_exclusive(nlist, index) - - try do - Enum.take(data, 25) - data - rescue - _ -> filter_mutually_exclusive(nlist, index + 1) - end - end - - def filter_mutually_exclusive(nlist, index) do - if index < length(nlist) do - {head, tail_schema} = Enum.at(nlist, index) - StreamData.filter(head, fn hd -> not ExJsonSchema.Validator.valid?(tail_schema, hd) end) - else - raise "oneOf combination not possible" - end - end - - def gen_mixed(%{"allOf" => options} = map) when is_list(options) do + def gen_mixed(%{"allOf" => options} = map, omap) when is_list(options) do nmap = Map.drop(map, ["allOf"]) Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) |> Jake.MapUtil.deep_merge(nmap) - |> Jake.gen_init() + |> Jake.gen_init(omap) end - def gen_mixed(%{"not" => not_schema} = map) when is_map(not_schema) do + def gen_mixed(%{"not" => not_schema} = map, omap) when is_map(not_schema) do type_val = if not_schema["type"] do not_schema["type"] else - Jake.Notype.gen_notype(not_schema, "return type") + Jake.Notype.gen_notype(not_schema, "return type", omap) end type = if type_val == nil, do: "null", else: type_val nlist = if is_list(type), do: @types -- type, else: @types -- [type] - data = for(n <- nlist, do: Jake.gen_init(%{"type" => n})) |> StreamData.one_of() + data = for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap)) |> StreamData.one_of() StreamData.filter(data, fn x when type == "null" -> true x -> not ExJsonSchema.Validator.valid?(not_schema, x) end) end + + def try_one_of(nlist, index) do + data = filter_mutually_exclusive(nlist, index) + + try do + Enum.take(data, 25) + data + rescue + _ -> filter_mutually_exclusive(nlist, index + 1) + end + end + + def filter_mutually_exclusive(nlist, index) do + if index < length(nlist) do + {head, tail_schema} = Enum.at(nlist, index) + StreamData.filter(head, fn hd -> not ExJsonSchema.Validator.valid?(tail_schema, hd) end) + else + raise "oneOf combination not possible" + end + end end From ab21124e06e4869787b9c49d12983504ec7e4335 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:22:18 +0530 Subject: [PATCH 55/98] add omap parameter to gen_notype --- lib/jake/notype.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 1cb47e7..5a09c97 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -22,7 +22,7 @@ defmodule Jake.Notype do "maxProperties" => "object" } - def gen_notype(map, type) do + def gen_notype(map, type, omap) do nmap = for {k, v} <- map, into: %{}, do: {k, v} nlist = for {k, v} <- map, into: [], do: @prop[k] @@ -34,7 +34,7 @@ defmodule Jake.Notype do if type == nil do nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"], do: Jake.gen_init(nmap) + if nmap["type"], do: Jake.gen_init(nmap, omap) else types end From 953499b55ce80cd44e07c7d77860333c835de0b1 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:24:21 +0530 Subject: [PATCH 56/98] add omap parameter to gen_number --- lib/jake/number.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 4601dbb..8eac9d0 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -11,7 +11,7 @@ defmodule Jake.Number do {min, max} end - def gen_number(map, type) do + def gen_number(map, type, omap) do {min_i, max_i} = get_min_max(map) {step_left, step_right} = find_step(map, min_i, max_i) min = findmin(map, @num_min, step_left, type) From 97be7573d240104ca56ac8d8cd14e209d5c44fea Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:26:21 +0530 Subject: [PATCH 57/98] modify functions to support ref feature --- lib/jake/object.ex | 74 +++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index e75acda..cea8661 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -9,25 +9,32 @@ defmodule Jake.Object do {min, max} end - def gen_object(map, properties) when is_nil(properties) do + def gen_object(map, properties, omap) when is_nil(properties) do {min, max} = get_min_max(map) if map["patternProperties"] do nlist = for {k, v} <- map["patternProperties"], - do: build_and_verify_patterns(k, v, map["patternProperties"]) + do: build_and_verify_patterns(k, v, map["patternProperties"], omap) merge_patterns(nlist) else if map["dependencies"] do - decide_dep_and_properties(map) + decide_dep_and_properties(map, omap) else - decide_min_max(map, Jake.gen_init(%{"type" => "string"}), StreamData.term(), min, max) + decide_min_max( + map, + Jake.gen_init(%{"type" => "string"}, omap), + StreamData.term(), + min, + max, + omap + ) end end end - def gen_object(map, properties) when is_map(properties) do + def gen_object(map, properties, omap) when is_map(properties) do nproperties = check_pattern_properties(map, properties, map["patternProperties"]) pmap = @@ -41,7 +48,7 @@ defmodule Jake.Object do fn_not_check = fn k, v -> if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, do: {"null", "null"}, - else: {k, Jake.gen_init(v)} + else: {k, Jake.gen_init(v, omap)} end map = Map.put(map, "properties", pmap) @@ -59,9 +66,9 @@ defmodule Jake.Object do end if is_nil(req) or map_size(req) == 0 do - check_additional_properties(map, 0, req, non_req, new_prop) + check_additional_properties(map, 0, req, non_req, new_prop, omap) else - check_additional_properties(map, Map.size(req), req, non_req, new_prop) + check_additional_properties(map, Map.size(req), req, non_req, new_prop, omap) end end @@ -73,28 +80,32 @@ defmodule Jake.Object do end) end - def build_and_verify_patterns(key, value, pprop) do + def build_and_verify_patterns(key, value, pprop, omap) do pprop_schema = %{"patternProperties" => pprop} IO.inspect(pprop_schema) nkey = Randex.stream(~r/#{key}/, mod: Randex.Generator.StreamData) - nval = Jake.gen_init(value) + nval = Jake.gen_init(value, omap) StreamData.bind(nkey, fn k -> - StreamData.bind_filter(nval, fn v -> - result = ExJsonSchema.Validator.valid?(pprop_schema, %{k => v}) - if result, do: {:cont, StreamData.constant(%{k => v})}, else: :skip - end) + StreamData.bind_filter( + nval, + fn v -> + result = ExJsonSchema.Validator.valid?(pprop_schema, %{k => v}) + if result, do: {:cont, StreamData.constant(%{k => v})}, else: :skip + end, + 100 + ) end) end - def gen_with_no_prop(map) do + def gen_with_no_prop(map, omap) do {min, max} = get_min_max(map) - Jake.gen_init(%{"type" => "string"}) + Jake.gen_init(%{"type" => "string"}, omap) |> StreamData.map_of(StreamData.term(), min_length: min, max_length: max) end - def decide_dep_and_properties(map) do + def decide_dep_and_properties(map, omap) do dep = map["dependencies"] list_with_map = @@ -111,13 +122,13 @@ defmodule Jake.Object do end end - resolve_dep(map, list_with_map) + resolve_dep(map, list_with_map, omap) end - def resolve_dep(map, list_with_map) do + def resolve_dep(map, list_with_map, omap) do if is_map(List.first(list_with_map)) do properties = Enum.reduce(list_with_map, %{}, fn x, acc -> Map.merge(acc, x) end) - check_additional_properties(map, 0, nil, nil, properties) + check_additional_properties(map, 0, nil, nil, properties, omap) else dependencies = for({k, prop_list, prop_map} <- list_with_map, do: %{k => prop_list}) @@ -131,7 +142,7 @@ defmodule Jake.Object do map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) IO.inspect(map) - gen_object(map, properties) + gen_object(map, properties, omap) end end @@ -247,13 +258,13 @@ defmodule Jake.Object do ) end - def check_additional_properties(map, req_size, req, _non_req, new_prop) + def check_additional_properties(map, req_size, req, _non_req, new_prop, omap) when is_nil(req) or req_size == 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map) + additional = gen_with_no_prop(map, omap) check_dependencies(map, new_prop) |> bind_function(additional, y, z) @@ -271,20 +282,21 @@ defmodule Jake.Object do StreamData.filter(prop, fn nmap -> map_size(nmap) in y..z end) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x) - key = Jake.gen_init(%{"type" => "string"}) + obj = Jake.gen_init(x, omap) + key = Jake.gen_init(%{"type" => "string"}, omap) check_dependencies(map, new_prop) |> bind_function(StreamData.map_of(key, obj), y, z) end end - def check_additional_properties(map, req_size, req, non_req, new_prop) when req_size > 0 do + def check_additional_properties(map, req_size, req, non_req, new_prop, omap) + when req_size > 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map) + additional = gen_with_no_prop(map, omap) check_dependencies(map, non_req) |> bind_function(additional, y, z) @@ -295,8 +307,8 @@ defmodule Jake.Object do |> bind_function_req(req, y, z) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x) - key = Jake.gen_init(%{"type" => "string"}) + obj = Jake.gen_init(x, omap) + key = Jake.gen_init(%{"type" => "string"}, omap) val1 = StreamData.map_of(key, obj, min_length: y, max_length: z) check_dependencies(map, non_req) @@ -313,10 +325,10 @@ defmodule Jake.Object do end end - def decide_min_max(map, key, value, min, max) + def decide_min_max(map, key, value, min, max, omap) when is_integer(min) and is_integer(max) and min <= max do if map["additionalProperties"] != nil do - gen_object(map, %{}) + gen_object(map, %{}, omap) else StreamData.map_of(key, value, min_length: min, max_length: max) end From 968bc6ed6a8abb3c279add23c55635dfe1120f9b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:26:48 +0530 Subject: [PATCH 58/98] add Ref module --- lib/jake/ref.ex | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 lib/jake/ref.ex diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex new file mode 100644 index 0000000..3792499 --- /dev/null +++ b/lib/jake/ref.ex @@ -0,0 +1,86 @@ +defmodule Jake.Ref do + def expand_ref(ref, map, _omap) + when is_nil(ref) or is_map(ref) or ref == "#" do + map + end + + def expand_ref(ref, map, omap) when is_binary(ref) do + nmap = Map.drop(map, ["$ref"]) + uri = URI.decode(ref) + + ref_map = + if String.starts_with?(uri, "http") do + process_http_path(uri) + else + process_local_path(uri) |> get_head_list_path(omap) + end + + nmap = Map.merge(nmap, ref_map) + nref = nmap["$ref"] + if nref, do: expand_ref(nref, nmap, omap), else: nmap + end + + def get_head_list_path(path_list, omap) do + {head, tail} = Enum.split(path_list, -1) + IO.inspect({head, tail}) + + head_path = + if length(head) > 0 do + get_in(omap, head) + else + get_in(omap, path_list) + end + + tail = + if is_list(head_path) do + Enum.fetch!(tail, 0) + else + nil + end + + if tail != nil and is_numeric(tail) do + {index, ""} = Integer.parse(tail) + Enum.fetch!(head_path, index) + else + get_in(omap, path_list) + end + end + + def process_http_path(url) do + [url, local] = + if String.contains?(url, "#/") do + String.split(url, "#/") + else + [url, nil] + end + + {:ok, {{_, 200, _}, _, schema}} = :httpc.request(:get, {url, []}, [], []) + jschema = Poison.decode!(schema) + + if is_nil(local) do + jschema + else + process_local_path(local) |> get_head_list_path(jschema) + end + end + + def process_local_path(path) do + str = + String.replace(path, "~0", "~") + |> String.replace("#/", "", global: false) + + if String.contains?(str, "~1") do + strlist = String.split(str, "/") + for n <- strlist, do: String.replace(n, "~1", "/") + else + String.split(str, "/") + end + end + + def is_numeric(str) do + case Integer.parse(str) do + {_num, ""} -> true + _ -> false + end + end +end From 3264a8d33178fb97bbe3f90a60410444a56e6c8e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 10:27:19 +0530 Subject: [PATCH 59/98] add tests for ref feature --- test/jake_test_ref.ex | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 test/jake_test_ref.ex diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex new file mode 100644 index 0000000..75e1c7d --- /dev/null +++ b/test/jake_test_ref.ex @@ -0,0 +1,84 @@ +defmodule JakeTestRef do + use ExUnitProperties + use ExUnit.Case + doctest Jake + + def test_generator_property(jschema) do + gen = Jake.generator(jschema) + schema = Poison.decode!(jschema) + IO.inspect(Enum.take(gen, 3)) + + check all a <- gen do + assert ExJsonSchema.Validator.valid?(schema, a) + end + end + + property "test ref simple" do + jschema = ~s({"properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + }}) + test_generator_property(jschema) + end + + property "test ref escape pointer" do + jschema = ~s({"tilda~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"}, + "properties": { + "tilda": {"$ref": "#/tilda~0field"}, + "slash": {"$ref": "#/slash~1field"}, + "percent": {"$ref": "#/percent%25field"} + }}) + test_generator_property(jschema) + end + + property "test ref nested schema" do + jschema = ~s({"definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c"}) + test_generator_property(jschema) + end + + property "test ref overrides any sibling keywords" do + jschema = ~s({"definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + }}) + test_generator_property(jschema) + end + + property "test ref not reference" do + jschema = ~s({"properties": { + "$ref": {"type": "string"} + }}) + test_generator_property(jschema) + end + + property "test ref array index" do + jschema = ~s({"items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ]}) + test_generator_property(jschema) + end + + property "test ref root" do + jschema = ~s({"properties": { + "bar" : {"type":"integer"}, + "foo": {"$ref": "#"} + }, + "additionalProperties": false}) + test_generator_property(jschema) + end +end From 5d2f500c34a6180d84e7a2400ccf3af208eaa3c7 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 11:34:03 +0530 Subject: [PATCH 60/98] convert url string to charlist before using it with httpc --- lib/jake/ref.ex | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 3792499..c8486ad 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -14,8 +14,12 @@ defmodule Jake.Ref do else process_local_path(uri) |> get_head_list_path(omap) end - - nmap = Map.merge(nmap, ref_map) + nmap = + if ref_map != nil do + Map.merge(nmap, ref_map) + else + nmap + end nref = nmap["$ref"] if nref, do: expand_ref(nref, nmap, omap), else: nmap end @@ -53,10 +57,9 @@ defmodule Jake.Ref do else [url, nil] end - - {:ok, {{_, 200, _}, _, schema}} = :httpc.request(:get, {url, []}, [], []) + IO.inspect {url, local} + {:ok, {{_, 200, _}, _, schema}} = :httpc.request(:get, {to_charlist(url), []}, [], []) jschema = Poison.decode!(schema) - if is_nil(local) do jschema else From 6c434d2ad3989736b7f3c3ac2de516d3cb50bf4a Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 22 Nov 2018 11:34:33 +0530 Subject: [PATCH 61/98] add complex ref test --- test/jake_test_ref.ex | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex index 75e1c7d..8352189 100644 --- a/test/jake_test_ref.ex +++ b/test/jake_test_ref.ex @@ -81,4 +81,30 @@ defmodule JakeTestRef do "additionalProperties": false}) test_generator_property(jschema) end + + property "test complex ref" do + jschema = ~s({ "definitions": { + "address": { + "type": "object", + "additionalProperties": false, + "properties": { + "street_address": { "type": "string" }, + "city": { "type": "string" }, + "state": { "type": "string" } + }, + "required": ["street_address", "city", "state"] + } + }, + + "type": "object", + + "properties": { + "billing_address": { "$ref": "#/definitions/address" }, + "shipping_address": { "$ref": "#/definitions/address" } + }, + "additionalProperties": false + }) + test_generator_property(jschema) + end + end From fdeaa40870f45197ad1d97245bc3d0efa64cffcc Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 25 Nov 2018 02:20:37 +0530 Subject: [PATCH 62/98] add support for recursive ref --- lib/jake/ref.ex | 56 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index c8486ad..eec5f1b 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -14,19 +14,59 @@ defmodule Jake.Ref do else process_local_path(uri) |> get_head_list_path(omap) end - nmap = - if ref_map != nil do + + nmap = + if ref_map != nil do Map.merge(nmap, ref_map) - else + else nmap + end + + check_ref_string(nmap, omap, [], ref) + end + + def check_ref_string(nmap, omap, ref_list, ref) do + if ref in ref_list do + str = Poison.encode!(nmap) + + ref_map = + URI.decode(ref) + |> process_local_path() + |> get_head_list_path(omap) + |> Poison.encode!() + |> String.slice(1..-2) + + String.replace(str, ~r/"\$ref":"#{ref}"/, ref_map) + |> String.replace(~r/"\$ref":"#{ref}"/, "") + |> Poison.decode!() + else + ref_list = ref_list ++ [ref] + str = Poison.encode!(nmap) + relist = Regex.scan(~r/"\$ref":"(?[^"]*)"/, str) + if length(relist) == 0, do: nmap, else: find_replace_ref(str, relist, omap, ref_list) end - nref = nmap["$ref"] - if nref, do: expand_ref(nref, nmap, omap), else: nmap + end + + def find_replace_ref(str, relist, omap, ref_list) do + nlist = + for n <- relist do + [refstr, ref] = n + ref_map = URI.decode(ref) |> process_local_path() |> get_head_list_path(omap) + + map = + check_ref_string(ref_map, omap, ref_list, ref) + |> Poison.encode!() + |> String.slice(1..-2) + + {refstr, map} + end + + Enum.reduce(nlist, str, fn {ref, map} = x, acc -> String.replace(acc, "#{ref}", map) end) + |> Poison.decode!() end def get_head_list_path(path_list, omap) do {head, tail} = Enum.split(path_list, -1) - IO.inspect({head, tail}) head_path = if length(head) > 0 do @@ -57,9 +97,11 @@ defmodule Jake.Ref do else [url, nil] end - IO.inspect {url, local} + + IO.inspect({url, local}) {:ok, {{_, 200, _}, _, schema}} = :httpc.request(:get, {to_charlist(url), []}, [], []) jschema = Poison.decode!(schema) + if is_nil(local) do jschema else From 7a3316be9c85d3d14293c2cdc0bd0244f0e2eda9 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Sun, 25 Nov 2018 02:21:09 +0530 Subject: [PATCH 63/98] add recursive ref tests --- test/jake_test_ref.ex | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex index 8352189..27b366d 100644 --- a/test/jake_test_ref.ex +++ b/test/jake_test_ref.ex @@ -81,7 +81,39 @@ defmodule JakeTestRef do "additionalProperties": false}) test_generator_property(jschema) end - + + property "test ref simple recursive" do + jschema = ~s({"properties": { + "foo_bar" : {"type":"integer"}, + "bar" : {"$ref": "#/properties"}, + "foo": {"$ref": "#/properties/bar"} + }, + "additionalProperties": false}) + test_generator_property(jschema) + end + + property "test ref complex recursive" do + jschema = ~s({"definitions": { + "person": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "children": { + "type": "array", + "items": { "$ref": "#/definitions/person" } + + } + }, "required" : ["name"], "additionalProperties": false + } + }, + "type": "object", + "properties": { + "person": { "$ref": "#/definitions/person" } + }, "required": ["person"], "additionalProperties": false + }) + test_generator_property(jschema) + end + property "test complex ref" do jschema = ~s({ "definitions": { "address": { @@ -106,5 +138,4 @@ defmodule JakeTestRef do }) test_generator_property(jschema) end - end From 0270d3c515c64b52ae785789059b537f9194650e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 28 Nov 2018 07:32:11 +0530 Subject: [PATCH 64/98] add one more $ref test --- test/jake_test_ref.ex | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex index 27b366d..54a2e87 100644 --- a/test/jake_test_ref.ex +++ b/test/jake_test_ref.ex @@ -103,7 +103,7 @@ defmodule JakeTestRef do "items": { "$ref": "#/definitions/person" } } - }, "required" : ["name"], "additionalProperties": false + }, "required": ["name"], "additionalProperties": false } }, "type": "object", @@ -113,6 +113,28 @@ defmodule JakeTestRef do }) test_generator_property(jschema) end + + property "test ref complex recursive no required" do + jschema = ~s({"definitions": { + "person": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "children": { + "type": "array", + "items": { "$ref": "#/definitions/person" } + + } + }, "additionalProperties": false + } + }, + "type": "object", + "properties": { + "person": { "$ref": "#/definitions/person" } + }, "additionalProperties": false + }) + test_generator_property(jschema) + end property "test complex ref" do jschema = ~s({ "definitions": { From edef4da28d836b33f13c1129d5d7ea2f2c34bdb0 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 28 Nov 2018 07:33:08 +0530 Subject: [PATCH 65/98] generate empty list when item map is empty --- lib/jake/array.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index be46b48..e4de0ca 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -14,6 +14,9 @@ defmodule Jake.Array do def gen_array(%{"items" => items} = map, omap) do case items do + item when is_map(item) and map_size(item) == 0 -> + StreamData.constant([]) + item when is_map(item) -> gen_list(map, item, omap) From 1fcb3733ca3ef13d448f32e8bd3627163922e4e5 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 3 Jan 2019 17:06:13 +0530 Subject: [PATCH 66/98] resize generator for recursive ref --- lib/jake.ex | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 2bc04e6..0d6cebb 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -12,57 +12,66 @@ defmodule Jake do def generator(jschema) do IO.puts(jschema) map = jschema |> Poison.decode!() - gen_init(map, map) + StreamData.sized(fn size -> gen_init(map, map, size) end) end - def gen_init(map, omap) do - map = Jake.Ref.expand_ref(map["$ref"], map, omap) - + def gen_init(map, omap, size) do + IO.inspect size + {map, size} = + if size == 0 do + map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) + {map, 0} + else + map = Jake.Ref.expand_ref(map["$ref"], map, omap, false) + {map, trunc(size/2)} + end + gen = if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do - Jake.Mixed.gen_mixed(map, omap) + Jake.Mixed.gen_mixed(map, omap, size) else - gen_all(map, map["enum"], map["type"], omap) + gen_all(map, map["enum"], map["type"], omap, size) end + StreamData.resize(gen, size) end - def gen_all(map, enum, _type, _omap) when enum != nil, do: gen_enum(map, enum) + def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) - def gen_all(map, _enum, type, omap) when is_list(type) do + def gen_all(map, _enum, type, omap, size) when is_list(type) do list = for n <- type, do: %{"type" => n} nmap = Map.drop(map, ["type"]) - for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init(omap)) + for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init(omap, size)) |> StreamData.one_of() end - def gen_all(map, _enum, type, omap) when type in @types, do: gen_type(type, map, omap) + def gen_all(map, _enum, type, omap, size) when type in @types, do: gen_type(type, map, omap, size) - def gen_all(map, _enum, type, omap) when type == nil do - Jake.Notype.gen_notype(map, type, omap) + def gen_all(map, _enum, type, omap, size) when type == nil do + Jake.Notype.gen_notype(map, type, omap, size) end - def gen_type(type, map, omap) when type == "string" do - Jake.String.gen_string(map, map["pattern"]) + def gen_type(type, map, omap, size) when type == "string" do + Jake.String.gen_string(map, map["pattern"], size) end - def gen_type(type, map, omap) when type in ["integer", "number"] do - Jake.Number.gen_number(map, type, omap) + def gen_type(type, map, omap, size) when type in ["integer", "number"] do + Jake.Number.gen_number(map, type, omap, size) end - def gen_type(type, _map, _omap) when type == "boolean" do + def gen_type(type, _map, _omap, _size) when type == "boolean" do StreamData.boolean() end - def gen_type(type, _map, _omap) when type == "null" do + def gen_type(type, _map, _omap, _size) when type == "null" do StreamData.constant(nil) end - def gen_type(type, map, omap) when type == "array" do - Jake.Array.gen_array(map, omap) + def gen_type(type, map, omap, size) when type == "array" do + Jake.Array.gen_array(map, omap, size) end - def gen_type(type, map, omap) when type == "object" do - Jake.Object.gen_object(map, map["properties"], omap) + def gen_type(type, map, omap, size) when type == "object" do + Jake.Object.gen_object(map, map["properties"], omap, size) end def gen_enum(map, list) do From 1393cc9529249db01ba6d81097f69c77bd069c8d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 3 Jan 2019 17:07:36 +0530 Subject: [PATCH 67/98] add check_ref parameter to expand_ref --- lib/jake/ref.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index eec5f1b..3c325f3 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -1,10 +1,10 @@ defmodule Jake.Ref do - def expand_ref(ref, map, _omap) + def expand_ref(ref, map, _omap, _check_ref) when is_nil(ref) or is_map(ref) or ref == "#" do map end - def expand_ref(ref, map, omap) when is_binary(ref) do + def expand_ref(ref, map, omap, check_ref) when is_binary(ref) do nmap = Map.drop(map, ["$ref"]) uri = URI.decode(ref) @@ -21,8 +21,11 @@ defmodule Jake.Ref do else nmap end - - check_ref_string(nmap, omap, [], ref) + if check_ref do + check_ref_string(nmap, omap, [], ref) + else + nmap + end end def check_ref_string(nmap, omap, ref_list, ref) do From 17540f4471c511465e06f70f939a4a627916641d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 3 Jan 2019 17:08:22 +0530 Subject: [PATCH 68/98] add size parameter to generator function --- lib/jake/array.ex | 28 +++++++++++------------ lib/jake/mixed.ex | 22 +++++++++--------- lib/jake/notype.ex | 4 ++-- lib/jake/number.ex | 2 +- lib/jake/object.ex | 57 +++++++++++++++++++++++----------------------- lib/jake/string.ex | 4 ++-- 6 files changed, 59 insertions(+), 58 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index e4de0ca..9bc5c85 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -12,41 +12,41 @@ defmodule Jake.Array do @max_items 1000 - def gen_array(%{"items" => items} = map, omap) do + def gen_array(%{"items" => items} = map, omap, size) do case items do item when is_map(item) and map_size(item) == 0 -> StreamData.constant([]) item when is_map(item) -> - gen_list(map, item, omap) + gen_list(map, item, omap, size) item when is_list(item) -> - gen_tuple(map, item, omap) + gen_tuple(map, item, omap, size) _ -> raise "Invalid items in array" end end - def gen_array(map, omap), do: arraytype(map, map["items"], omap) + def gen_array(map, omap, size), do: arraytype(map, map["items"], omap, size) - def arraytype(map, items, omap) when is_nil(items) do - item = get_one_of(omap) + def arraytype(map, items, omap, size) when is_nil(items) do + item = get_one_of(omap, size) {min, max} = get_min_max(map) decide_min_max(map, item, min, max) end - def gen_tuple(map, items, omap) do - list = for n <- items, is_map(n), do: Jake.gen_init(n, omap) + def gen_tuple(map, items, omap, size) do + list = for n <- items, is_map(n), do: Jake.gen_init(n, omap, size) {min, max} = get_min_max(map) case map["additionalItems"] do x when is_map(x) -> - add_additional_items(list, Jake.gen_init(x, omap), max, min) + add_additional_items(list, Jake.gen_init(x, omap, size), max, min) x when (is_boolean(x) and x) or is_nil(x) -> - add_additional_items(list, get_one_of(omap), max, min) + add_additional_items(list, get_one_of(omap, size), max, min) x when is_boolean(x) and not x and length(list) in min..max -> StreamData.fixed_list(list) @@ -56,9 +56,9 @@ defmodule Jake.Array do end end - def gen_list(map, items, omap) do + def gen_list(map, items, omap, size) do {min, max} = get_min_max(map) - item = Jake.gen_init(items, omap) + item = Jake.gen_init(items, omap, size) decide_min_max(map, item, min, max) end @@ -81,8 +81,8 @@ defmodule Jake.Array do raise "Bounds of items not well defined" end - def get_one_of(omap) do - for(n <- @type_list, is_map(n), do: Jake.gen_init(n, omap)) |> StreamData.one_of() + def get_one_of(omap, size) do + for(n <- @type_list, is_map(n), do: Jake.gen_init(n, omap, size)) |> StreamData.one_of() end def add_additional_items(olist, additional, max, min) do diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex index 926eb1d..cb573ba 100644 --- a/lib/jake/mixed.ex +++ b/lib/jake/mixed.ex @@ -9,14 +9,14 @@ defmodule Jake.Mixed do "string" ] - def gen_mixed(%{"anyOf" => options} = map, omap) when is_list(options) do + def gen_mixed(%{"anyOf" => options} = map, omap, size) when is_list(options) do nmap = Map.drop(map, ["anyOf"]) - for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n), omap)) + for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n), omap, size)) |> StreamData.one_of() end - def gen_mixed(%{"oneOf" => options} = map, omap) when is_list(options) do + def gen_mixed(%{"oneOf" => options} = map, omap, size) when is_list(options) do nmap = Map.drop(map, ["oneOf"]) tail_schema = fn tail -> @@ -25,33 +25,33 @@ defmodule Jake.Mixed do nlist = for {n, counter} <- Enum.with_index(options) do - hd = Map.merge(nmap, n) |> Jake.gen_init(omap) + hd = Map.merge(nmap, n) |> Jake.gen_init(omap, size) tail = List.delete_at(options, counter) |> tail_schema.() {hd, tail} end - try_one_of(nlist, 0) + try_one_of(nlist, 0, size) end - def gen_mixed(%{"allOf" => options} = map, omap) when is_list(options) do + def gen_mixed(%{"allOf" => options} = map, omap, size) when is_list(options) do nmap = Map.drop(map, ["allOf"]) Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) |> Jake.MapUtil.deep_merge(nmap) - |> Jake.gen_init(omap) + |> Jake.gen_init(omap, size) end - def gen_mixed(%{"not" => not_schema} = map, omap) when is_map(not_schema) do + def gen_mixed(%{"not" => not_schema} = map, omap, size) when is_map(not_schema) do type_val = if not_schema["type"] do not_schema["type"] else - Jake.Notype.gen_notype(not_schema, "return type", omap) + Jake.Notype.gen_notype(not_schema, "return type", omap, size) end type = if type_val == nil, do: "null", else: type_val nlist = if is_list(type), do: @types -- type, else: @types -- [type] - data = for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap)) |> StreamData.one_of() + data = for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap, size)) |> StreamData.one_of() StreamData.filter(data, fn x when type == "null" -> true @@ -59,7 +59,7 @@ defmodule Jake.Mixed do end) end - def try_one_of(nlist, index) do + def try_one_of(nlist, index, _size) do data = filter_mutually_exclusive(nlist, index) try do diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 5a09c97..4ebf04b 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -22,7 +22,7 @@ defmodule Jake.Notype do "maxProperties" => "object" } - def gen_notype(map, type, omap) do + def gen_notype(map, type, omap, size) do nmap = for {k, v} <- map, into: %{}, do: {k, v} nlist = for {k, v} <- map, into: [], do: @prop[k] @@ -34,7 +34,7 @@ defmodule Jake.Notype do if type == nil do nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"], do: Jake.gen_init(nmap, omap) + if nmap["type"], do: Jake.gen_init(nmap, omap, size) else types end diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 8eac9d0..31e80d9 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -11,7 +11,7 @@ defmodule Jake.Number do {min, max} end - def gen_number(map, type, omap) do + def gen_number(map, type, omap, _size) do {min_i, max_i} = get_min_max(map) {step_left, step_right} = find_step(map, min_i, max_i) min = findmin(map, @num_min, step_left, type) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index cea8661..a608a1b 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -9,32 +9,33 @@ defmodule Jake.Object do {min, max} end - def gen_object(map, properties, omap) when is_nil(properties) do + def gen_object(map, properties, omap, size) when is_nil(properties) do {min, max} = get_min_max(map) if map["patternProperties"] do nlist = for {k, v} <- map["patternProperties"], - do: build_and_verify_patterns(k, v, map["patternProperties"], omap) + do: build_and_verify_patterns(k, v, map["patternProperties"], omap, size) merge_patterns(nlist) else if map["dependencies"] do - decide_dep_and_properties(map, omap) + decide_dep_and_properties(map, omap, size) else decide_min_max( map, - Jake.gen_init(%{"type" => "string"}, omap), + Jake.gen_init(%{"type" => "string"}, omap, size), StreamData.term(), min, max, - omap + omap, + size ) end end end - def gen_object(map, properties, omap) when is_map(properties) do + def gen_object(map, properties, omap, size) when is_map(properties) do nproperties = check_pattern_properties(map, properties, map["patternProperties"]) pmap = @@ -48,7 +49,7 @@ defmodule Jake.Object do fn_not_check = fn k, v -> if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, do: {"null", "null"}, - else: {k, Jake.gen_init(v, omap)} + else: {k, Jake.gen_init(v, omap, size)} end map = Map.put(map, "properties", pmap) @@ -66,9 +67,9 @@ defmodule Jake.Object do end if is_nil(req) or map_size(req) == 0 do - check_additional_properties(map, 0, req, non_req, new_prop, omap) + check_additional_properties(map, 0, req, non_req, new_prop, omap, size) else - check_additional_properties(map, Map.size(req), req, non_req, new_prop, omap) + check_additional_properties(map, Map.size(req), req, non_req, new_prop, omap, size) end end @@ -80,11 +81,11 @@ defmodule Jake.Object do end) end - def build_and_verify_patterns(key, value, pprop, omap) do + def build_and_verify_patterns(key, value, pprop, omap, size) do pprop_schema = %{"patternProperties" => pprop} IO.inspect(pprop_schema) nkey = Randex.stream(~r/#{key}/, mod: Randex.Generator.StreamData) - nval = Jake.gen_init(value, omap) + nval = Jake.gen_init(value, omap, size) StreamData.bind(nkey, fn k -> StreamData.bind_filter( @@ -98,14 +99,14 @@ defmodule Jake.Object do end) end - def gen_with_no_prop(map, omap) do + def gen_with_no_prop(map, omap, size) do {min, max} = get_min_max(map) - Jake.gen_init(%{"type" => "string"}, omap) + Jake.gen_init(%{"type" => "string"}, omap, size) |> StreamData.map_of(StreamData.term(), min_length: min, max_length: max) end - def decide_dep_and_properties(map, omap) do + def decide_dep_and_properties(map, omap, size) do dep = map["dependencies"] list_with_map = @@ -122,13 +123,13 @@ defmodule Jake.Object do end end - resolve_dep(map, list_with_map, omap) + resolve_dep(map, list_with_map, omap, size) end - def resolve_dep(map, list_with_map, omap) do + def resolve_dep(map, list_with_map, omap, size) do if is_map(List.first(list_with_map)) do properties = Enum.reduce(list_with_map, %{}, fn x, acc -> Map.merge(acc, x) end) - check_additional_properties(map, 0, nil, nil, properties, omap) + check_additional_properties(map, 0, nil, nil, properties, omap, size) else dependencies = for({k, prop_list, prop_map} <- list_with_map, do: %{k => prop_list}) @@ -142,7 +143,7 @@ defmodule Jake.Object do map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) IO.inspect(map) - gen_object(map, properties, omap) + gen_object(map, properties, omap, size) end end @@ -258,13 +259,13 @@ defmodule Jake.Object do ) end - def check_additional_properties(map, req_size, req, _non_req, new_prop, omap) + def check_additional_properties(map, req_size, req, _non_req, new_prop, omap, size) when is_nil(req) or req_size == 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map, omap) + additional = gen_with_no_prop(map, omap, size) check_dependencies(map, new_prop) |> bind_function(additional, y, z) @@ -282,21 +283,21 @@ defmodule Jake.Object do StreamData.filter(prop, fn nmap -> map_size(nmap) in y..z end) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x, omap) - key = Jake.gen_init(%{"type" => "string"}, omap) + obj = Jake.gen_init(x, omap, size) + key = Jake.gen_init(%{"type" => "string"}, omap, size) check_dependencies(map, new_prop) |> bind_function(StreamData.map_of(key, obj), y, z) end end - def check_additional_properties(map, req_size, req, non_req, new_prop, omap) + def check_additional_properties(map, req_size, req, non_req, new_prop, omap, size) when req_size > 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map, omap) + additional = gen_with_no_prop(map, omap, size) check_dependencies(map, non_req) |> bind_function(additional, y, z) @@ -307,8 +308,8 @@ defmodule Jake.Object do |> bind_function_req(req, y, z) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x, omap) - key = Jake.gen_init(%{"type" => "string"}, omap) + obj = Jake.gen_init(x, omap, size) + key = Jake.gen_init(%{"type" => "string"}, omap, size) val1 = StreamData.map_of(key, obj, min_length: y, max_length: z) check_dependencies(map, non_req) @@ -325,10 +326,10 @@ defmodule Jake.Object do end end - def decide_min_max(map, key, value, min, max, omap) + def decide_min_max(map, key, value, min, max, omap, size) when is_integer(min) and is_integer(max) and min <= max do if map["additionalProperties"] != nil do - gen_object(map, %{}, omap) + gen_object(map, %{}, omap, size) else StreamData.map_of(key, value, min_length: min, max_length: max) end diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 58bdbf8..19610ea 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -9,12 +9,12 @@ defmodule Jake.String do {min, max} end - def gen_string(map, pattern) when is_nil(pattern) do + def gen_string(map, pattern, _size) when is_nil(pattern) do {min, max} = find_min_max(map) StreamData.string(:alphanumeric, [{:max_length, max}, {:min_length, min}]) end - def gen_string(map, pattern) when is_binary(pattern) do + def gen_string(map, pattern, _size) when is_binary(pattern) do {min, max} = find_min_max(map) pat = Randex.stream(~r/#{pattern}/, mod: Randex.Generator.StreamData) From 1c8b801613b269da418164f47b20c395dd68a964 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Thu, 3 Jan 2019 17:09:52 +0530 Subject: [PATCH 69/98] remove debug info --- lib/jake.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/jake.ex b/lib/jake.ex index 0d6cebb..a3641a3 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -16,7 +16,6 @@ defmodule Jake do end def gen_init(map, omap, size) do - IO.inspect size {map, size} = if size == 0 do map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) From a145b7ece5506ad9586d62fd65ce7ff8703c8b67 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 4 Jan 2019 10:46:09 +0530 Subject: [PATCH 70/98] make size double initially --- lib/jake.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jake.ex b/lib/jake.ex index a3641a3..639931c 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -12,7 +12,7 @@ defmodule Jake do def generator(jschema) do IO.puts(jschema) map = jschema |> Poison.decode!() - StreamData.sized(fn size -> gen_init(map, map, size) end) + StreamData.sized(fn size -> gen_init(map, map, 2*size) end) end def gen_init(map, omap, size) do From 8a9a80de3d5b58c7f9a61c023edceb40cda2c00d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 4 Jan 2019 12:44:09 +0530 Subject: [PATCH 71/98] use bind in gen_init --- lib/jake.ex | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 639931c..0038831 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -12,25 +12,30 @@ defmodule Jake do def generator(jschema) do IO.puts(jschema) map = jschema |> Poison.decode!() - StreamData.sized(fn size -> gen_init(map, map, 2*size) end) + StreamData.sized(fn size -> gen_init(map, map, 2 * size) end) end def gen_init(map, omap, size) do - {map, size} = - if size == 0 do + {map, size} = + if size == 0 do map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) {map, 0} - else + else map = Jake.Ref.expand_ref(map["$ref"], map, omap, false) - {map, trunc(size/2)} - end - gen = - if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do - Jake.Mixed.gen_mixed(map, omap, size) - else - gen_all(map, map["enum"], map["type"], omap, size) - end - StreamData.resize(gen, size) + {map, trunc(size / 2)} + end + + StreamData.bind( + StreamData.constant(nil), + fn _ -> + if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do + Jake.Mixed.gen_mixed(map, omap, size) + else + gen_all(map, map["enum"], map["type"], omap, size) + end + end + ) + |> StreamData.resize(size) end def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) @@ -43,7 +48,8 @@ defmodule Jake do |> StreamData.one_of() end - def gen_all(map, _enum, type, omap, size) when type in @types, do: gen_type(type, map, omap, size) + def gen_all(map, _enum, type, omap, size) when type in @types, + do: gen_type(type, map, omap, size) def gen_all(map, _enum, type, omap, size) when type == nil do Jake.Notype.gen_notype(map, type, omap, size) From a4fd93d019ff9c15f22e959dfc3c922642a8a505 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 4 Jan 2019 12:44:40 +0530 Subject: [PATCH 72/98] remove debug info --- lib/jake/object.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index a608a1b..3a534ca 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -83,7 +83,7 @@ defmodule Jake.Object do def build_and_verify_patterns(key, value, pprop, omap, size) do pprop_schema = %{"patternProperties" => pprop} - IO.inspect(pprop_schema) + # IO.inspect(pprop_schema) nkey = Randex.stream(~r/#{key}/, mod: Randex.Generator.StreamData) nval = Jake.gen_init(value, omap, size) @@ -111,7 +111,7 @@ defmodule Jake.Object do list_with_map = for {k, v} <- dep do - IO.inspect(v) + # IO.inspect(v) if is_list(v) do item = %{k => StreamData.term()} @@ -135,14 +135,14 @@ defmodule Jake.Object do for({k, prop_list, prop_map} <- list_with_map, do: %{k => prop_list}) |> Enum.reduce(%{}, fn x, acc -> Map.merge(x, acc) end) - IO.inspect(dependencies) + # IO.inspect(dependencies) properties = for({k, prop_list, prop_map} <- list_with_map, do: prop_map) |> Enum.reduce(%{}, fn x, acc -> Map.merge(x, acc) end) map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) - IO.inspect(map) + # IO.inspect(map) gen_object(map, properties, omap, size) end end @@ -167,7 +167,7 @@ defmodule Jake.Object do StreamData.optional_map(new_prop) end - IO.inspect(prop) + # IO.inspect(prop) StreamData.bind(prop, fn mapn -> StreamData.bind_filter( From 7dfcff33d77398f7184f32cd7ef645854f7ec17e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 4 Jan 2019 12:45:24 +0530 Subject: [PATCH 73/98] format code --- lib/jake/ref.ex | 5 +++-- test/jake_test_ref.ex | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 3c325f3..b36a699 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -21,10 +21,11 @@ defmodule Jake.Ref do else nmap end + if check_ref do - check_ref_string(nmap, omap, [], ref) + check_ref_string(nmap, omap, [], ref) else - nmap + nmap end end diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex index 54a2e87..a7861b0 100644 --- a/test/jake_test_ref.ex +++ b/test/jake_test_ref.ex @@ -113,7 +113,7 @@ defmodule JakeTestRef do }) test_generator_property(jschema) end - + property "test ref complex recursive no required" do jschema = ~s({"definitions": { "person": { From def8fdee452ef5700f0f5f76038e958c2294d746 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 4 Jan 2019 14:51:12 +0530 Subject: [PATCH 74/98] add separate get_lazy_streamkey function --- lib/jake.ex | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 0038831..9c93bae 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -16,26 +16,28 @@ defmodule Jake do end def gen_init(map, omap, size) do - {map, size} = - if size == 0 do - map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) - {map, 0} - else - map = Jake.Ref.expand_ref(map["$ref"], map, omap, false) - {map, trunc(size / 2)} - end - StreamData.bind( - StreamData.constant(nil), - fn _ -> - if map["allOf"] || map["oneOf"] || map["anyOf"] || map["not"] do - Jake.Mixed.gen_mixed(map, omap, size) + get_lazy_streamkey(map, omap, size), + fn {nmap, nsize} -> + if nmap["allOf"] || nmap["oneOf"] || nmap["anyOf"] || nmap["not"] do + Jake.Mixed.gen_mixed(nmap, omap, nsize) else - gen_all(map, map["enum"], map["type"], omap, size) + gen_all(nmap, nmap["enum"], nmap["type"], omap, nsize) end + |> StreamData.resize(nsize) end ) - |> StreamData.resize(size) + end + + def get_lazy_streamkey(map, omap, size) do + if size == 0 do + map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) + {map, 0} + else + map = Jake.Ref.expand_ref(map["$ref"], map, omap, false) + {map, trunc(size / 2)} + end + |> StreamData.constant() end def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) From 84e40bcd2fc827bfd896cfc748c10f2364a0e0d4 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 7 Jan 2019 12:36:59 +0530 Subject: [PATCH 75/98] obtain tuple when expanding ref --- lib/jake.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 9c93bae..4ff9083 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -31,13 +31,14 @@ defmodule Jake do def get_lazy_streamkey(map, omap, size) do if size == 0 do - map = Jake.Ref.expand_ref(map["$ref"], map, omap, true) + {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, true) {map, 0} else - map = Jake.Ref.expand_ref(map["$ref"], map, omap, false) + {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, false) {map, trunc(size / 2)} end - |> StreamData.constant() + |> + StreamData.constant() end def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) From 762da758c328a2c7ed37c17f5683b85c78daaa57 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 7 Jan 2019 12:39:12 +0530 Subject: [PATCH 76/98] return whether ref exists or not along with map --- lib/jake/ref.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index b36a699..f558766 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -1,7 +1,7 @@ defmodule Jake.Ref do def expand_ref(ref, map, _omap, _check_ref) when is_nil(ref) or is_map(ref) or ref == "#" do - map + {map, false} end def expand_ref(ref, map, omap, check_ref) when is_binary(ref) do @@ -23,9 +23,9 @@ defmodule Jake.Ref do end if check_ref do - check_ref_string(nmap, omap, [], ref) + {check_ref_string(nmap, omap, [], ref), true} else - nmap + {nmap, true} end end From ec064bf46e7f24b8823236238dde205288edbdea Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 7 Jan 2019 12:41:00 +0530 Subject: [PATCH 77/98] check ref key if type not present --- lib/jake/notype.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 4ebf04b..89b6799 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -34,7 +34,7 @@ defmodule Jake.Notype do if type == nil do nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"], do: Jake.gen_init(nmap, omap, size) + if nmap["type"] || nmap["$ref"], do: Jake.gen_init(nmap, omap, size) else types end From 6d3cb88df13a15202666661c1ae7c285356d4c8b Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Mon, 7 Jan 2019 12:57:21 +0530 Subject: [PATCH 78/98] check whether ref points to root or not --- lib/jake/notype.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 89b6799..4bc3615 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -34,7 +34,7 @@ defmodule Jake.Notype do if type == nil do nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"] || nmap["$ref"], do: Jake.gen_init(nmap, omap, size) + if nmap["type"] || (nmap["$ref"] && nmap["$ref"] != "#"), do: Jake.gen_init(nmap, omap, size) else types end From 622947eba20ebece6235286b1032dca4b1986e7d Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 11:04:35 +0530 Subject: [PATCH 79/98] modify get_lazy_streamkey --- lib/jake.ex | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 4ff9083..e98d060 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -30,15 +30,8 @@ defmodule Jake do end def get_lazy_streamkey(map, omap, size) do - if size == 0 do - {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, true) - {map, 0} - else - {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, false) - {map, trunc(size / 2)} - end - |> - StreamData.constant() + {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, false) + StreamData.constant({map, trunc(size / 2)}) end def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) From 3dfb63d2ae74f2f956ea0a705b2b1d683e8ac1d4 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 11:13:16 +0530 Subject: [PATCH 80/98] fix not property --- lib/jake/mixed.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex index cb573ba..0228e3c 100644 --- a/lib/jake/mixed.ex +++ b/lib/jake/mixed.ex @@ -42,6 +42,8 @@ defmodule Jake.Mixed do end def gen_mixed(%{"not" => not_schema} = map, omap, size) when is_map(not_schema) do + nmap = Map.drop(map, ["not"]) + nmap_type = nmap["type"] type_val = if not_schema["type"] do not_schema["type"] @@ -51,7 +53,12 @@ defmodule Jake.Mixed do type = if type_val == nil, do: "null", else: type_val nlist = if is_list(type), do: @types -- type, else: @types -- [type] - data = for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap, size)) |> StreamData.one_of() + data = + if nmap_type || (is_map(nmap) && map_size(nmap) > 0) do + Jake.gen_init(nmap, omap, size) + else + for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap, size)) |> StreamData.one_of() + end StreamData.filter(data, fn x when type == "null" -> true From 75c974c19a2ba5570976f304e3972d1cbfbe5eb5 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 11:17:49 +0530 Subject: [PATCH 81/98] remove checking ref --- lib/jake/ref.ex | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index f558766..497ad79 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -21,12 +21,7 @@ defmodule Jake.Ref do else nmap end - - if check_ref do - {check_ref_string(nmap, omap, [], ref), true} - else - {nmap, true} - end + {nmap, true} end def check_ref_string(nmap, omap, ref_list, ref) do From d31413d87f542b9c220c729b6935f334747ffb09 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 16:48:52 +0530 Subject: [PATCH 82/98] add separate expand_ref for root ref --- lib/jake/ref.ex | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 497ad79..4ef9000 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -1,20 +1,25 @@ defmodule Jake.Ref do def expand_ref(ref, map, _omap, _check_ref) - when is_nil(ref) or is_map(ref) or ref == "#" do + when is_nil(ref) or is_map(ref) do {map, false} end - + + def expand_ref(ref, map, omap, _check_ref) + when ref == "#" do + nmap = Map.drop(map, ["$ref"]) |> Map.merge(omap) + {nmap, true} + end + def expand_ref(ref, map, omap, check_ref) when is_binary(ref) do nmap = Map.drop(map, ["$ref"]) uri = URI.decode(ref) - + ref_map = if String.starts_with?(uri, "http") do process_http_path(uri) else process_local_path(uri) |> get_head_list_path(omap) end - nmap = if ref_map != nil do Map.merge(nmap, ref_map) From 6fa2978d236f9eb0930b03e8f48100ff21bc2761 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 16:52:48 +0530 Subject: [PATCH 83/98] rewrite filter for debugging --- lib/jake/mixed.ex | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex index 0228e3c..6007d12 100644 --- a/lib/jake/mixed.ex +++ b/lib/jake/mixed.ex @@ -60,9 +60,13 @@ defmodule Jake.Mixed do for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap, size)) |> StreamData.one_of() end - StreamData.filter(data, fn - x when type == "null" -> true - x -> not ExJsonSchema.Validator.valid?(not_schema, x) + StreamData.filter(data, + fn x -> + if type == "null" do + true + else + not ExJsonSchema.Validator.valid?(not_schema, x) + end end) end From 0068f16cd9984b406e0988d2cd8adf7707eae886 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Tue, 8 Jan 2019 16:54:30 +0530 Subject: [PATCH 84/98] modify complex recursive ref test --- test/jake_test_ref.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jake_test_ref.ex b/test/jake_test_ref.ex index a7861b0..d58c317 100644 --- a/test/jake_test_ref.ex +++ b/test/jake_test_ref.ex @@ -119,7 +119,7 @@ defmodule JakeTestRef do "person": { "type": "object", "properties": { - "name": { "type": "string" }, + "name": { "type": "string", "minLength": 5 }, "children": { "type": "array", "items": { "$ref": "#/definitions/person" } From 0ad2defe6962696d432f618de691651a6a4308cd Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 9 Jan 2019 13:57:02 +0530 Subject: [PATCH 85/98] add few more tests for checking "not" property --- test/jake_test_property.exs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/jake_test_property.exs b/test/jake_test_property.exs index 7c1e9e4..97717d2 100644 --- a/test/jake_test_property.exs +++ b/test/jake_test_property.exs @@ -67,6 +67,16 @@ defmodule JakeTestProperty do jschema = ~s({"not": {"type": "integer"}}) test_generator_property(jschema) end + + property "test not string foo" do + jschema = ~s({"type": "object", "properties": {"foo":{"not": {"type":"string"}}, "bar": {"type":"integer"}}}) + test_generator_property(jschema) + end + + property "test forbidden foo" do + jschema = ~s({"type": "object", "properties": {"foo":{"not": {}}, "bar": {"type":"integer"}}}) + test_generator_property(jschema) + end property "test oneOf" do jschema = ~s({"type": "integer", "minimum": 29, "oneOf": [{"maximum": 255}, {"minimum": 25}]}) From 2dcc31580c5ac885c39e2134452ba4de5269833e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 9 Jan 2019 15:02:48 +0530 Subject: [PATCH 86/98] remove check_ref_string and related functions --- lib/jake/ref.ex | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 4ef9000..5d84771 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -29,46 +29,6 @@ defmodule Jake.Ref do {nmap, true} end - def check_ref_string(nmap, omap, ref_list, ref) do - if ref in ref_list do - str = Poison.encode!(nmap) - - ref_map = - URI.decode(ref) - |> process_local_path() - |> get_head_list_path(omap) - |> Poison.encode!() - |> String.slice(1..-2) - - String.replace(str, ~r/"\$ref":"#{ref}"/, ref_map) - |> String.replace(~r/"\$ref":"#{ref}"/, "") - |> Poison.decode!() - else - ref_list = ref_list ++ [ref] - str = Poison.encode!(nmap) - relist = Regex.scan(~r/"\$ref":"(?[^"]*)"/, str) - if length(relist) == 0, do: nmap, else: find_replace_ref(str, relist, omap, ref_list) - end - end - - def find_replace_ref(str, relist, omap, ref_list) do - nlist = - for n <- relist do - [refstr, ref] = n - ref_map = URI.decode(ref) |> process_local_path() |> get_head_list_path(omap) - - map = - check_ref_string(ref_map, omap, ref_list, ref) - |> Poison.encode!() - |> String.slice(1..-2) - - {refstr, map} - end - - Enum.reduce(nlist, str, fn {ref, map} = x, acc -> String.replace(acc, "#{ref}", map) end) - |> Poison.decode!() - end - def get_head_list_path(path_list, omap) do {head, tail} = Enum.split(path_list, -1) From 3383b23c6068d90318380d006fce815de4ccdbaa Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 9 Jan 2019 15:04:15 +0530 Subject: [PATCH 87/98] remove check_ref parameter from expand_ref --- lib/jake/ref.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 5d84771..71d5df1 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -1,16 +1,16 @@ defmodule Jake.Ref do - def expand_ref(ref, map, _omap, _check_ref) + def expand_ref(ref, map, _omap) when is_nil(ref) or is_map(ref) do {map, false} end - def expand_ref(ref, map, omap, _check_ref) + def expand_ref(ref, map, omap) when ref == "#" do nmap = Map.drop(map, ["$ref"]) |> Map.merge(omap) {nmap, true} end - def expand_ref(ref, map, omap, check_ref) when is_binary(ref) do + def expand_ref(ref, map, omap) when is_binary(ref) do nmap = Map.drop(map, ["$ref"]) uri = URI.decode(ref) From dc103d3a95ac324640f271507fbd45916e0b21b9 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 9 Jan 2019 15:19:32 +0530 Subject: [PATCH 88/98] fix typo --- lib/jake.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index e98d060..35d600b 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -20,7 +20,7 @@ defmodule Jake do get_lazy_streamkey(map, omap, size), fn {nmap, nsize} -> if nmap["allOf"] || nmap["oneOf"] || nmap["anyOf"] || nmap["not"] do - Jake.Mixed.gen_mixed(nmap, omap, nsize) +e Jake.Mixed.gen_mixed(nmap, omap, nsize) else gen_all(nmap, nmap["enum"], nmap["type"], omap, nsize) end @@ -30,7 +30,7 @@ defmodule Jake do end def get_lazy_streamkey(map, omap, size) do - {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap, false) + {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap) StreamData.constant({map, trunc(size / 2)}) end From 4acc52501324d44a81f91c434a3306b8de3dc71c Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Wed, 9 Jan 2019 15:27:01 +0530 Subject: [PATCH 89/98] remove unused variables warning --- lib/jake.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index 35d600b..a14947f 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -20,7 +20,7 @@ defmodule Jake do get_lazy_streamkey(map, omap, size), fn {nmap, nsize} -> if nmap["allOf"] || nmap["oneOf"] || nmap["anyOf"] || nmap["not"] do -e Jake.Mixed.gen_mixed(nmap, omap, nsize) + Jake.Mixed.gen_mixed(nmap, omap, nsize) else gen_all(nmap, nmap["enum"], nmap["type"], omap, nsize) end @@ -30,7 +30,7 @@ e Jake.Mixed.gen_mixed(nmap, omap, nsize) end def get_lazy_streamkey(map, omap, size) do - {map, ref} = Jake.Ref.expand_ref(map["$ref"], map, omap) + {map, _} = Jake.Ref.expand_ref(map["$ref"], map, omap) StreamData.constant({map, trunc(size / 2)}) end @@ -51,7 +51,7 @@ e Jake.Mixed.gen_mixed(nmap, omap, nsize) Jake.Notype.gen_notype(map, type, omap, size) end - def gen_type(type, map, omap, size) when type == "string" do + def gen_type(type, map, _omap, size) when type == "string" do Jake.String.gen_string(map, map["pattern"], size) end From 4a63d2a09df67369eecb7158b3dbd28238cda6bb Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:15:15 +0530 Subject: [PATCH 90/98] mix format --- test/jake_test_property.exs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/jake_test_property.exs b/test/jake_test_property.exs index 97717d2..817ebc8 100644 --- a/test/jake_test_property.exs +++ b/test/jake_test_property.exs @@ -67,9 +67,11 @@ defmodule JakeTestProperty do jschema = ~s({"not": {"type": "integer"}}) test_generator_property(jschema) end - + property "test not string foo" do - jschema = ~s({"type": "object", "properties": {"foo":{"not": {"type":"string"}}, "bar": {"type":"integer"}}}) + jschema = + ~s({"type": "object", "properties": {"foo":{"not": {"type":"string"}}, "bar": {"type":"integer"}}}) + test_generator_property(jschema) end From 3b5f6f1a48c10e1e9af419f294604040bd192b82 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:17:07 +0530 Subject: [PATCH 91/98] remove omap and size parameters from gen_object --- lib/jake/object.ex | 58 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/jake/object.ex b/lib/jake/object.ex index 3a534ca..1db564c 100644 --- a/lib/jake/object.ex +++ b/lib/jake/object.ex @@ -9,33 +9,32 @@ defmodule Jake.Object do {min, max} end - def gen_object(map, properties, omap, size) when is_nil(properties) do + def gen_object(map, properties, schema) when is_nil(properties) do {min, max} = get_min_max(map) if map["patternProperties"] do nlist = for {k, v} <- map["patternProperties"], - do: build_and_verify_patterns(k, v, map["patternProperties"], omap, size) + do: build_and_verify_patterns(k, v, map["patternProperties"], schema) merge_patterns(nlist) else if map["dependencies"] do - decide_dep_and_properties(map, omap, size) + decide_dep_and_properties(map, schema) else decide_min_max( map, - Jake.gen_init(%{"type" => "string"}, omap, size), + Jake.gen_init(Map.put(schema, "map", %{"type" => "string"})), StreamData.term(), min, max, - omap, - size + schema ) end end end - def gen_object(map, properties, omap, size) when is_map(properties) do + def gen_object(map, properties, schema) when is_map(properties) do nproperties = check_pattern_properties(map, properties, map["patternProperties"]) pmap = @@ -49,7 +48,7 @@ defmodule Jake.Object do fn_not_check = fn k, v -> if v["not"] != nil and is_map(v["not"]) and map_size(v["not"]) == 0, do: {"null", "null"}, - else: {k, Jake.gen_init(v, omap, size)} + else: {k, Jake.gen_init(Map.put(schema, "map", v))} end map = Map.put(map, "properties", pmap) @@ -67,9 +66,9 @@ defmodule Jake.Object do end if is_nil(req) or map_size(req) == 0 do - check_additional_properties(map, 0, req, non_req, new_prop, omap, size) + check_additional_properties(map, 0, req, non_req, new_prop, schema) else - check_additional_properties(map, Map.size(req), req, non_req, new_prop, omap, size) + check_additional_properties(map, Map.size(req), req, non_req, new_prop, schema) end end @@ -81,11 +80,11 @@ defmodule Jake.Object do end) end - def build_and_verify_patterns(key, value, pprop, omap, size) do + def build_and_verify_patterns(key, value, pprop, schema) do pprop_schema = %{"patternProperties" => pprop} # IO.inspect(pprop_schema) nkey = Randex.stream(~r/#{key}/, mod: Randex.Generator.StreamData) - nval = Jake.gen_init(value, omap, size) + nval = Map.put(schema, "map", value) |> Jake.gen_init() StreamData.bind(nkey, fn k -> StreamData.bind_filter( @@ -99,14 +98,15 @@ defmodule Jake.Object do end) end - def gen_with_no_prop(map, omap, size) do + def gen_with_no_prop(map, schema) do {min, max} = get_min_max(map) - Jake.gen_init(%{"type" => "string"}, omap, size) + Map.put(schema, "map", %{"type" => "string"}) + |> Jake.gen_init() |> StreamData.map_of(StreamData.term(), min_length: min, max_length: max) end - def decide_dep_and_properties(map, omap, size) do + def decide_dep_and_properties(map, schema) do dep = map["dependencies"] list_with_map = @@ -123,13 +123,13 @@ defmodule Jake.Object do end end - resolve_dep(map, list_with_map, omap, size) + resolve_dep(map, list_with_map, schema) end - def resolve_dep(map, list_with_map, omap, size) do + def resolve_dep(map, list_with_map, schema) do if is_map(List.first(list_with_map)) do properties = Enum.reduce(list_with_map, %{}, fn x, acc -> Map.merge(acc, x) end) - check_additional_properties(map, 0, nil, nil, properties, omap, size) + check_additional_properties(map, 0, nil, nil, properties, schema) else dependencies = for({k, prop_list, prop_map} <- list_with_map, do: %{k => prop_list}) @@ -143,7 +143,7 @@ defmodule Jake.Object do map = Map.put(map, "properties", properties) |> Map.put("dependencies", dependencies) # IO.inspect(map) - gen_object(map, properties, omap, size) + gen_object(map, properties, schema) end end @@ -259,13 +259,13 @@ defmodule Jake.Object do ) end - def check_additional_properties(map, req_size, req, _non_req, new_prop, omap, size) + def check_additional_properties(map, req_size, req, _non_req, new_prop, schema) when is_nil(req) or req_size == 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map, omap, size) + additional = gen_with_no_prop(map, schema) check_dependencies(map, new_prop) |> bind_function(additional, y, z) @@ -283,21 +283,21 @@ defmodule Jake.Object do StreamData.filter(prop, fn nmap -> map_size(nmap) in y..z end) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x, omap, size) - key = Jake.gen_init(%{"type" => "string"}, omap, size) + obj = Map.put(schema, "map", x) |> Jake.gen_init() + key = Map.put(schema, "map", %{"type" => "string"}) |> Jake.gen_init() check_dependencies(map, new_prop) |> bind_function(StreamData.map_of(key, obj), y, z) end end - def check_additional_properties(map, req_size, req, non_req, new_prop, omap, size) + def check_additional_properties(map, req_size, req, non_req, new_prop, schema) when req_size > 0 do {min, max} = get_min_max(map) case {map["additionalProperties"], min, max} do {x, y, z} when is_nil(x) or (is_boolean(x) and x) -> - additional = gen_with_no_prop(map, omap, size) + additional = gen_with_no_prop(map, schema) check_dependencies(map, non_req) |> bind_function(additional, y, z) @@ -308,8 +308,8 @@ defmodule Jake.Object do |> bind_function_req(req, y, z) {x, y, z} when is_map(x) -> - obj = Jake.gen_init(x, omap, size) - key = Jake.gen_init(%{"type" => "string"}, omap, size) + obj = Map.put(schema, "map", x) |> Jake.gen_init() + key = Map.put(schema, "map", %{"type" => "string"}) |> Jake.gen_init() val1 = StreamData.map_of(key, obj, min_length: y, max_length: z) check_dependencies(map, non_req) @@ -326,10 +326,10 @@ defmodule Jake.Object do end end - def decide_min_max(map, key, value, min, max, omap, size) + def decide_min_max(map, key, value, min, max, schema) when is_integer(min) and is_integer(max) and min <= max do if map["additionalProperties"] != nil do - gen_object(map, %{}, omap, size) + gen_object(map, %{}, schema) else StreamData.map_of(key, value, min_length: min, max_length: max) end From 1ca1c20c558c1ba98ac07d1d99e515b2dd9f3ed7 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:18:15 +0530 Subject: [PATCH 92/98] remove omap and size parameters from gen_number --- lib/jake/number.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jake/number.ex b/lib/jake/number.ex index 31e80d9..4601dbb 100644 --- a/lib/jake/number.ex +++ b/lib/jake/number.ex @@ -11,7 +11,7 @@ defmodule Jake.Number do {min, max} end - def gen_number(map, type, omap, _size) do + def gen_number(map, type) do {min_i, max_i} = get_min_max(map) {step_left, step_right} = find_step(map, min_i, max_i) min = findmin(map, @num_min, step_left, type) From 7a866689ef74ac2a7d17888b2a1155cdf34d594e Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:18:54 +0530 Subject: [PATCH 93/98] remove size parameter from gen_string --- lib/jake/string.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jake/string.ex b/lib/jake/string.ex index 19610ea..58bdbf8 100644 --- a/lib/jake/string.ex +++ b/lib/jake/string.ex @@ -9,12 +9,12 @@ defmodule Jake.String do {min, max} end - def gen_string(map, pattern, _size) when is_nil(pattern) do + def gen_string(map, pattern) when is_nil(pattern) do {min, max} = find_min_max(map) StreamData.string(:alphanumeric, [{:max_length, max}, {:min_length, min}]) end - def gen_string(map, pattern, _size) when is_binary(pattern) do + def gen_string(map, pattern) when is_binary(pattern) do {min, max} = find_min_max(map) pat = Randex.stream(~r/#{pattern}/, mod: Randex.Generator.StreamData) From b025f98cd7670e24f3bb15c27dd40e02bcb8d6b6 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:19:58 +0530 Subject: [PATCH 94/98] remove omap and size parameters from gen_array --- lib/jake/array.ex | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/jake/array.ex b/lib/jake/array.ex index 9bc5c85..a78083f 100644 --- a/lib/jake/array.ex +++ b/lib/jake/array.ex @@ -12,41 +12,41 @@ defmodule Jake.Array do @max_items 1000 - def gen_array(%{"items" => items} = map, omap, size) do + def gen_array(%{"items" => items} = map, schema) do case items do item when is_map(item) and map_size(item) == 0 -> StreamData.constant([]) item when is_map(item) -> - gen_list(map, item, omap, size) + gen_list(map, item, schema) item when is_list(item) -> - gen_tuple(map, item, omap, size) + gen_tuple(map, item, schema) _ -> raise "Invalid items in array" end end - def gen_array(map, omap, size), do: arraytype(map, map["items"], omap, size) + def gen_array(map, schema), do: arraytype(map, map["items"], schema) - def arraytype(map, items, omap, size) when is_nil(items) do - item = get_one_of(omap, size) + def arraytype(map, items, schema) when is_nil(items) do + item = get_one_of(schema) {min, max} = get_min_max(map) decide_min_max(map, item, min, max) end - def gen_tuple(map, items, omap, size) do - list = for n <- items, is_map(n), do: Jake.gen_init(n, omap, size) + def gen_tuple(map, items, schema) do + list = for n <- items, is_map(n), do: Map.put(schema, "map", n) |> Jake.gen_init() {min, max} = get_min_max(map) case map["additionalItems"] do x when is_map(x) -> - add_additional_items(list, Jake.gen_init(x, omap, size), max, min) + add_additional_items(list, Jake.gen_init(Map.put(schema, "map", x)), max, min) x when (is_boolean(x) and x) or is_nil(x) -> - add_additional_items(list, get_one_of(omap, size), max, min) + add_additional_items(list, get_one_of(schema), max, min) x when is_boolean(x) and not x and length(list) in min..max -> StreamData.fixed_list(list) @@ -56,9 +56,9 @@ defmodule Jake.Array do end end - def gen_list(map, items, omap, size) do + def gen_list(map, items, schema) do {min, max} = get_min_max(map) - item = Jake.gen_init(items, omap, size) + item = Map.put(schema, "map", items) |> Jake.gen_init() decide_min_max(map, item, min, max) end @@ -81,8 +81,9 @@ defmodule Jake.Array do raise "Bounds of items not well defined" end - def get_one_of(omap, size) do - for(n <- @type_list, is_map(n), do: Jake.gen_init(n, omap, size)) |> StreamData.one_of() + def get_one_of(schema) do + for(n <- @type_list, is_map(n), do: Map.put(schema, "map", n) |> Jake.gen_init()) + |> StreamData.one_of() end def add_additional_items(olist, additional, max, min) do From 79adbb55980193474e6a1234ef40fbc78c245cc6 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:21:08 +0530 Subject: [PATCH 95/98] remove omap and size parameters from gen_mixed --- lib/jake/mixed.ex | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/jake/mixed.ex b/lib/jake/mixed.ex index 6007d12..aeda46c 100644 --- a/lib/jake/mixed.ex +++ b/lib/jake/mixed.ex @@ -9,14 +9,14 @@ defmodule Jake.Mixed do "string" ] - def gen_mixed(%{"anyOf" => options} = map, omap, size) when is_list(options) do + def gen_mixed(%{"anyOf" => options} = map, schema) when is_list(options) do nmap = Map.drop(map, ["anyOf"]) - for(n <- options, is_map(n), do: Jake.gen_init(Map.merge(nmap, n), omap, size)) - |> StreamData.one_of() + nlist = for(n <- options, is_map(n), do: Map.merge(nmap, n)) + for(n <- nlist, do: Map.put(schema, "map", n) |> Jake.gen_init()) |> StreamData.one_of() end - def gen_mixed(%{"oneOf" => options} = map, omap, size) when is_list(options) do + def gen_mixed(%{"oneOf" => options} = map, schema) when is_list(options) do nmap = Map.drop(map, ["oneOf"]) tail_schema = fn tail -> @@ -25,52 +25,59 @@ defmodule Jake.Mixed do nlist = for {n, counter} <- Enum.with_index(options) do - hd = Map.merge(nmap, n) |> Jake.gen_init(omap, size) + hd = Map.put(schema, "map", Map.merge(nmap, n)) |> Jake.gen_init() tail = List.delete_at(options, counter) |> tail_schema.() {hd, tail} end - try_one_of(nlist, 0, size) + try_one_of(nlist, 0) end - def gen_mixed(%{"allOf" => options} = map, omap, size) when is_list(options) do + def gen_mixed(%{"allOf" => options} = map, schema) when is_list(options) do nmap = Map.drop(map, ["allOf"]) - Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) - |> Jake.MapUtil.deep_merge(nmap) - |> Jake.gen_init(omap, size) + map = + Enum.reduce(options, %{}, fn x, acc -> Jake.MapUtil.deep_merge(acc, x) end) + |> Jake.MapUtil.deep_merge(nmap) + + Map.put(schema, "map", map) |> Jake.gen_init() end - def gen_mixed(%{"not" => not_schema} = map, omap, size) when is_map(not_schema) do + def gen_mixed(%{"not" => not_schema} = map, schema) when is_map(not_schema) do nmap = Map.drop(map, ["not"]) nmap_type = nmap["type"] + type_val = if not_schema["type"] do not_schema["type"] else - Jake.Notype.gen_notype(not_schema, "return type", omap, size) + Jake.Notype.gen_notype("return type", schema) end type = if type_val == nil, do: "null", else: type_val nlist = if is_list(type), do: @types -- type, else: @types -- [type] - data = - if nmap_type || (is_map(nmap) && map_size(nmap) > 0) do - Jake.gen_init(nmap, omap, size) - else - for(n <- nlist, do: Jake.gen_init(%{"type" => n}, omap, size)) |> StreamData.one_of() - end - StreamData.filter(data, - fn x -> + data = + if nmap_type || (is_map(nmap) && map_size(nmap) > 0) do + Map.put(schema, "map", nmap) |> Jake.gen_init() + else + for(n <- nlist, do: Map.put(schema, "map", %{"type" => n}) |> Jake.gen_init()) + |> StreamData.one_of() + end + + StreamData.filter( + data, + fn x -> if type == "null" do - true + true else - not ExJsonSchema.Validator.valid?(not_schema, x) + not ExJsonSchema.Validator.valid?(not_schema, x) end - end) + end + ) end - def try_one_of(nlist, index, _size) do + def try_one_of(nlist, index) do data = filter_mutually_exclusive(nlist, index) try do From 7baa611786bfe94f3c7bd5b4ec3c63ca813ad1c4 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:22:15 +0530 Subject: [PATCH 96/98] remove omap and size parameters from gen_notype --- lib/jake/notype.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/jake/notype.ex b/lib/jake/notype.ex index 4bc3615..44c97c3 100644 --- a/lib/jake/notype.ex +++ b/lib/jake/notype.ex @@ -22,7 +22,8 @@ defmodule Jake.Notype do "maxProperties" => "object" } - def gen_notype(map, type, omap, size) do + def gen_notype(type, schema) do + map = schema["map"] nmap = for {k, v} <- map, into: %{}, do: {k, v} nlist = for {k, v} <- map, into: [], do: @prop[k] @@ -34,7 +35,9 @@ defmodule Jake.Notype do if type == nil do nmap = if not is_nil(types), do: Map.put(nmap, "type", types), else: nmap - if nmap["type"] || (nmap["$ref"] && nmap["$ref"] != "#"), do: Jake.gen_init(nmap, omap, size) + + if nmap["type"] || (nmap["$ref"] && nmap["$ref"] != "#"), + do: Map.put(schema, "map", nmap) |> Jake.gen_init() else types end From a2fb44a205289ca60936faafde004634b9b75b78 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:23:25 +0530 Subject: [PATCH 97/98] mix format --- lib/jake/ref.ex | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/jake/ref.ex b/lib/jake/ref.ex index 71d5df1..eba075e 100644 --- a/lib/jake/ref.ex +++ b/lib/jake/ref.ex @@ -3,29 +3,31 @@ defmodule Jake.Ref do when is_nil(ref) or is_map(ref) do {map, false} end - + def expand_ref(ref, map, omap) when ref == "#" do - nmap = Map.drop(map, ["$ref"]) |> Map.merge(omap) + nmap = Map.drop(map, ["$ref"]) |> Map.merge(omap) {nmap, true} end - + def expand_ref(ref, map, omap) when is_binary(ref) do nmap = Map.drop(map, ["$ref"]) uri = URI.decode(ref) - + ref_map = if String.starts_with?(uri, "http") do process_http_path(uri) else process_local_path(uri) |> get_head_list_path(omap) end + nmap = if ref_map != nil do Map.merge(nmap, ref_map) else nmap end + {nmap, true} end From f8dc9113020c30f90102bcb423b5b54d4a2101d4 Mon Sep 17 00:00:00 2001 From: kanishka-linux Date: Fri, 11 Jan 2019 18:24:35 +0530 Subject: [PATCH 98/98] pass single schema parameter to gen_init --- lib/jake.ex | 61 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/lib/jake.ex b/lib/jake.ex index a14947f..2d4ca35 100644 --- a/lib/jake.ex +++ b/lib/jake.ex @@ -12,67 +12,76 @@ defmodule Jake do def generator(jschema) do IO.puts(jschema) map = jschema |> Poison.decode!() - StreamData.sized(fn size -> gen_init(map, map, 2 * size) end) + + StreamData.sized(fn size -> + Map.put(%{}, "map", map) |> Map.put("omap", map) |> Map.put("size", 2 * size) |> gen_init() + end) end - def gen_init(map, omap, size) do + def gen_init(schema) do StreamData.bind( - get_lazy_streamkey(map, omap, size), + get_lazy_streamkey(schema), fn {nmap, nsize} -> + nschema = Map.put(schema, "map", nmap) |> Map.put("size", nsize) + if nmap["allOf"] || nmap["oneOf"] || nmap["anyOf"] || nmap["not"] do - Jake.Mixed.gen_mixed(nmap, omap, nsize) + Jake.Mixed.gen_mixed(nmap, nschema) else - gen_all(nmap, nmap["enum"], nmap["type"], omap, nsize) + gen_all(nschema, nmap["enum"], nmap["type"]) end |> StreamData.resize(nsize) end ) end - def get_lazy_streamkey(map, omap, size) do - {map, _} = Jake.Ref.expand_ref(map["$ref"], map, omap) - StreamData.constant({map, trunc(size / 2)}) + def get_lazy_streamkey(schema) do + {map, _} = + get_in(schema, ["map", "$ref"]) |> Jake.Ref.expand_ref(schema["map"], schema["omap"]) + + StreamData.constant({map, trunc(schema["size"] / 2)}) end - def gen_all(map, enum, _type, _omap, _size) when enum != nil, do: gen_enum(map, enum) + def gen_all(schema, enum, _type) when enum != nil, do: gen_enum(schema["map"], enum) - def gen_all(map, _enum, type, omap, size) when is_list(type) do + def gen_all(schema, _enum, type) when is_list(type) do list = for n <- type, do: %{"type" => n} - nmap = Map.drop(map, ["type"]) + nmap = schema["map"] |> Map.drop(["type"]) - for(n <- list, is_map(n), do: Map.merge(n, nmap) |> Jake.gen_init(omap, size)) + for(n <- list, is_map(n), do: Map.put(schema, "map", Map.merge(n, nmap)) |> Jake.gen_init()) |> StreamData.one_of() end - def gen_all(map, _enum, type, omap, size) when type in @types, - do: gen_type(type, map, omap, size) + def gen_all(schema, _enum, type) when type in @types, + do: gen_type(type, schema) - def gen_all(map, _enum, type, omap, size) when type == nil do - Jake.Notype.gen_notype(map, type, omap, size) + def gen_all(schema, _enum, type) when type == nil do + Jake.Notype.gen_notype(type, schema) end - def gen_type(type, map, _omap, size) when type == "string" do - Jake.String.gen_string(map, map["pattern"], size) + def gen_type(type, schema) when type == "string" do + map = schema["map"] + Jake.String.gen_string(map, map["pattern"]) end - def gen_type(type, map, omap, size) when type in ["integer", "number"] do - Jake.Number.gen_number(map, type, omap, size) + def gen_type(type, schema) when type in ["integer", "number"] do + Jake.Number.gen_number(schema["map"], type) end - def gen_type(type, _map, _omap, _size) when type == "boolean" do + def gen_type(type, schema) when type == "boolean" do StreamData.boolean() end - def gen_type(type, _map, _omap, _size) when type == "null" do + def gen_type(type, schema) when type == "null" do StreamData.constant(nil) end - def gen_type(type, map, omap, size) when type == "array" do - Jake.Array.gen_array(map, omap, size) + def gen_type(type, schema) when type == "array" do + Jake.Array.gen_array(schema["map"], schema) end - def gen_type(type, map, omap, size) when type == "object" do - Jake.Object.gen_object(map, map["properties"], omap, size) + def gen_type(type, schema) when type == "object" do + map = schema["map"] + Jake.Object.gen_object(map, map["properties"], schema) end def gen_enum(map, list) do