Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
ba18332
add more tests
kanishka-linux Nov 12, 2018
c3c6789
modify Jake module
kanishka-linux Nov 12, 2018
0907115
handle type key when merging
kanishka-linux Nov 12, 2018
8334625
modify Array module
kanishka-linux Nov 12, 2018
d7cd346
add separate module for cases when type not present
kanishka-linux Nov 12, 2018
63a16bf
handle multipleOf property
kanishka-linux Nov 12, 2018
7f57601
modify Object module
kanishka-linux Nov 12, 2018
61ab36b
modify String module
kanishka-linux Nov 12, 2018
693fc77
remove files
kanishka-linux Nov 12, 2018
42a1bed
add and modify dependencies
kanishka-linux Nov 12, 2018
57923ba
add comment
kanishka-linux Nov 12, 2018
47485ad
lower order of gen_all for enum
kanishka-linux Nov 14, 2018
5313a2b
modify getmultipleof and get_float_number
kanishka-linux Nov 14, 2018
c464a02
remove usage of bind_filter from stringer
kanishka-linux Nov 14, 2018
e2cf5f7
use deep_merge again in in allOf gen_init
kanishka-linux Nov 15, 2018
d302482
handle map item and list items separately
kanishka-linux Nov 15, 2018
0d61b1e
add separate get_min_max method to fix exclusiveMaximum feature
kanishka-linux Nov 15, 2018
574d7a9
fix test_generator method
kanishka-linux Nov 15, 2018
4d8c737
add rudimentary support for patternProperties
kanishka-linux Nov 15, 2018
9bdc74a
add patternProperties to prop map
kanishka-linux Nov 17, 2018
87fae25
add dependencies support for object
kanishka-linux Nov 17, 2018
6887e97
remove unnecessary line
kanishka-linux Nov 17, 2018
f4abe43
add tests for object dependencies
kanishka-linux Nov 17, 2018
9a4dc7f
add dependencies key to prop map
kanishka-linux Nov 17, 2018
ee27150
handle dependencies when property is null
kanishka-linux Nov 17, 2018
e598f80
handle test cases of dependencies.json
kanishka-linux Nov 17, 2018
da4e3fa
add default.json
kanishka-linux Nov 18, 2018
b3803e9
separate tuple and list generation methods
kanishka-linux Nov 18, 2018
db8a83d
remove get_float_number method
kanishka-linux Nov 18, 2018
2a84eb7
add decide_min_max with error message
kanishka-linux Nov 18, 2018
265ae51
add test cases for oneOf feature
kanishka-linux Nov 18, 2018
9c97664
add support for oneOf feature
kanishka-linux Nov 18, 2018
33f8a68
add not tests
kanishka-linux Nov 19, 2018
2f1f279
allow returning only type name if type is not nil
kanishka-linux Nov 19, 2018
c844aba
check for not feature when refining properties
kanishka-linux Nov 19, 2018
36f9176
add support for not feature
kanishka-linux Nov 19, 2018
b6aaa84
fix generator for patternProperties
kanishka-linux Nov 19, 2018
fa4e0da
experiment with property based testing
kanishka-linux Nov 19, 2018
6c0bbf1
add map parameter to gen_enum
kanishka-linux Nov 19, 2018
ae8e0c0
validate enum values against existing schema
kanishka-linux Nov 19, 2018
1385cb3
add test enum with constraints
kanishka-linux Nov 19, 2018
4d7ccbf
modify gen_init and add gen_init_root
kanishka-linux Nov 19, 2018
ba76b77
add separate Mixed module
kanishka-linux Nov 19, 2018
e90b467
modify gen_enum
kanishka-linux Nov 20, 2018
923a3b5
remove unnecessary code
kanishka-linux Nov 20, 2018
99c4dba
remove type checking from gen_enum
kanishka-linux Nov 20, 2018
48ff9d0
remove type_map and code clean-up
kanishka-linux Nov 20, 2018
ed96a02
remove gen_enum
kanishka-linux Nov 20, 2018
ffca40c
replace gen_number_init with gen_number
kanishka-linux Nov 20, 2018
b7a8427
remove type_list and rearrange code
kanishka-linux Nov 20, 2018
592bdb1
replace stringer with gen_string and remove enum as function parameter
kanishka-linux Nov 20, 2018
445acb9
add support for ref
kanishka-linux Nov 22, 2018
b371e98
add omap parameter to gen_array
kanishka-linux Nov 22, 2018
2608cb3
rearrange code and add omap parameter to gen_mixed
kanishka-linux Nov 22, 2018
ab21124
add omap parameter to gen_notype
kanishka-linux Nov 22, 2018
953499b
add omap parameter to gen_number
kanishka-linux Nov 22, 2018
97be757
modify functions to support ref feature
kanishka-linux Nov 22, 2018
968bc6e
add Ref module
kanishka-linux Nov 22, 2018
3264a8d
add tests for ref feature
kanishka-linux Nov 22, 2018
5d2f500
convert url string to charlist before using it with httpc
kanishka-linux Nov 22, 2018
6c434d2
add complex ref test
kanishka-linux Nov 22, 2018
fdeaa40
add support for recursive ref
kanishka-linux Nov 24, 2018
7a3316b
add recursive ref tests
kanishka-linux Nov 24, 2018
0270d3c
add one more $ref test
kanishka-linux Nov 28, 2018
edef4da
generate empty list when item map is empty
kanishka-linux Nov 28, 2018
1fcb373
resize generator for recursive ref
kanishka-linux Jan 3, 2019
1393cc9
add check_ref parameter to expand_ref
kanishka-linux Jan 3, 2019
17540f4
add size parameter to generator function
kanishka-linux Jan 3, 2019
1c8b801
remove debug info
kanishka-linux Jan 3, 2019
a145b7e
make size double initially
kanishka-linux Jan 4, 2019
8a9a80d
use bind in gen_init
kanishka-linux Jan 4, 2019
a4fd93d
remove debug info
kanishka-linux Jan 4, 2019
7dfcff3
format code
kanishka-linux Jan 4, 2019
def8fde
add separate get_lazy_streamkey function
kanishka-linux Jan 4, 2019
84e40bc
obtain tuple when expanding ref
kanishka-linux Jan 7, 2019
762da75
return whether ref exists or not along with map
kanishka-linux Jan 7, 2019
ec064bf
check ref key if type not present
kanishka-linux Jan 7, 2019
6d3cb88
check whether ref points to root or not
kanishka-linux Jan 7, 2019
622947e
modify get_lazy_streamkey
kanishka-linux Jan 8, 2019
3dfb63d
fix not property
kanishka-linux Jan 8, 2019
75c974c
remove checking ref
kanishka-linux Jan 8, 2019
d31413d
add separate expand_ref for root ref
kanishka-linux Jan 8, 2019
6fa2978
rewrite filter for debugging
kanishka-linux Jan 8, 2019
0068f16
modify complex recursive ref test
kanishka-linux Jan 8, 2019
0ad2def
add few more tests for checking "not" property
kanishka-linux Jan 9, 2019
2dcc315
remove check_ref_string and related functions
kanishka-linux Jan 9, 2019
3383b23
remove check_ref parameter from expand_ref
kanishka-linux Jan 9, 2019
dc103d3
fix typo
kanishka-linux Jan 9, 2019
4acc525
remove unused variables warning
kanishka-linux Jan 9, 2019
4a63d2a
mix format
kanishka-linux Jan 11, 2019
3b5f6f1
remove omap and size parameters from gen_object
kanishka-linux Jan 11, 2019
1ca1c20
remove omap and size parameters from gen_number
kanishka-linux Jan 11, 2019
7a86668
remove size parameter from gen_string
kanishka-linux Jan 11, 2019
b025f98
remove omap and size parameters from gen_array
kanishka-linux Jan 11, 2019
79adbb5
remove omap and size parameters from gen_mixed
kanishka-linux Jan 11, 2019
7baa611
remove omap and size parameters from gen_notype
kanishka-linux Jan 11, 2019
a2fb44a
mix format
kanishka-linux Jan 11, 2019
f8dc911
pass single schema parameter to gen_init
kanishka-linux Jan 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 70 additions & 18 deletions lib/jake.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,83 @@ 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))
def generator(jschema) do
IO.puts(jschema)
map = jschema |> Poison.decode!()

StreamData.sized(fn size ->
Map.put(%{}, "map", map) |> Map.put("omap", map) |> Map.put("size", 2 * size) |> gen_init()
end)
|> 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(schema) do
StreamData.bind(
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, nschema)
else
gen_all(nschema, nmap["enum"], nmap["type"])
end
|> StreamData.resize(nsize)
end
)
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 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

# type not present
def gen(spec) do
Enum.map(@types, fn type ->
Map.put(spec, "type", type)
|> gen()
end)
def gen_all(schema, enum, _type) when enum != nil, do: gen_enum(schema["map"], enum)

def gen_all(schema, _enum, type) when is_list(type) do
list = for n <- type, do: %{"type" => n}
nmap = schema["map"] |> Map.drop(["type"])

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(schema, _enum, type) when type in @types,
do: gen_type(type, schema)

def gen_all(schema, _enum, type) when type == nil do
Jake.Notype.gen_notype(type, schema)
end

def gen_type(type, schema) when type == "string" do
map = schema["map"]
Jake.String.gen_string(map, map["pattern"])
end

def gen_type(type, schema) when type in ["integer", "number"] do
Jake.Number.gen_number(schema["map"], type)
end

def gen_type(type, schema) when type == "boolean" do
StreamData.boolean()
end

def gen_type(type, schema) when type == "null" do
StreamData.constant(nil)
end

def gen_type(type, schema) when type == "array" do
Jake.Array.gen_array(schema["map"], schema)
end

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
Enum.filter(list, fn x -> ExJsonSchema.Validator.valid?(map, x) end)
|> StreamData.member_of()
end
end
107 changes: 105 additions & 2 deletions lib/jake/array.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,108 @@
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(%{"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, schema)

item when is_list(item) ->
gen_tuple(map, item, schema)

_ ->
raise "Invalid items in array"
end
end

def gen_array(map, schema), do: arraytype(map, map["items"], schema)

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, 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(Map.put(schema, "map", x)), max, min)

x when (is_boolean(x) and x) or is_nil(x) ->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

      x when is_map(x) ->
        generate_list(list, Jake.gen_init(map), max, min)
      x when x == true or x == nil -> 
        generate_list(list, get_one_of(), max, min)
      _ -> 
        unless length(list) in min..max, do: raise ...
        StreamData.fixed_list(list)

Avoid extra indirection? Otherwise it becomes difficult to understand the code.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better. I'll apply necessary changes.

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)

_ ->
raise "Invalid items or length of list exceeds specified bounds"
end
end

def gen_list(map, items, schema) do
{min, max} = get_min_max(map)
item = Map.put(schema, "map", items) |> Jake.gen_init()
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)
{min, max}
end

def decide_min_max(map, item, min, max)
when is_integer(min) and is_integer(max) and min < max do
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this doesn't match? If it should throw error, then being explicit about it might be better.

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 decide_min_max(map, item, min, max) do
raise "Bounds of items not well defined"
end

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
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)}

_ ->
:skip
end
)
end)
end
end
5 changes: 0 additions & 5 deletions lib/jake/boolean.ex

This file was deleted.

7 changes: 0 additions & 7 deletions lib/jake/integer.ex

This file was deleted.

33 changes: 33 additions & 0 deletions lib/jake/map_util.ex
Original file line number Diff line number Diff line change
@@ -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
99 changes: 99 additions & 0 deletions lib/jake/mixed.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
defmodule Jake.Mixed do
@types [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]

def gen_mixed(%{"anyOf" => options} = map, schema) when is_list(options) do
nmap = Map.drop(map, ["anyOf"])

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, schema) 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.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)
end

def gen_mixed(%{"allOf" => options} = map, schema) when is_list(options) do
nmap = Map.drop(map, ["allOf"])

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, 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("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
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
else
not ExJsonSchema.Validator.valid?(not_schema, x)
end
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
Loading