Skip to content

Commit

Permalink
PR for "#4: Add support for 'anyOf' and 'allOf' keywords" (#31)
Browse files Browse the repository at this point in the history
* #4: Adds support for the 'anyOf' keyword

* #4: Adds support for the 'allOf' keyword

Resolves #4
  • Loading branch information
dragonwasrobot authored Apr 8, 2017
1 parent cdefc49 commit 8746ba0
Show file tree
Hide file tree
Showing 14 changed files with 1,509 additions and 8 deletions.
18 changes: 15 additions & 3 deletions lib/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ defmodule JS2E.Parser do
"""

require Logger
alias JS2E.Parsers.{ArrayParser, UnionParser, PrimitiveParser,
DefinitionsParser, OneOfParser, ObjectParser,
EnumParser, TypeReferenceParser}
alias JS2E.Parsers.{ArrayParser, ObjectParser, EnumParser, PrimitiveParser,
DefinitionsParser, AllOfParser, AnyOfParser, OneOfParser,
UnionParser, TypeReferenceParser}
alias JS2E.{TypePath, Types}
alias JS2E.Types.SchemaDefinition

Expand Down Expand Up @@ -167,6 +167,8 @@ defmodule JS2E.Parser do
{&ref_type?/1, &TypeReferenceParser.parse/5},
{&enum_type?/1, &EnumParser.parse/5},
{&union_type?/1, &UnionParser.parse/5},
{&all_of_type?/1, &AllOfParser.parse/5},
{&any_of_type?/1, &AnyOfParser.parse/5},
{&one_of_type?/1, &OneOfParser.parse/5},
{&object_type?/1, &ObjectParser.parse/5},
{&array_type?/1, &ArrayParser.parse/5},
Expand Down Expand Up @@ -215,6 +217,16 @@ defmodule JS2E.Parser do
Map.has_key?(schema_node, "enum")
end

@spec all_of_type?(map) :: boolean
defp all_of_type?(schema_node) do
Map.get(schema_node, "allOf")
end

@spec any_of_type?(map) :: boolean
defp any_of_type?(schema_node) do
Map.get(schema_node, "anyOf")
end

@spec one_of_type?(map) :: boolean
defp one_of_type?(schema_node) do
Map.get(schema_node, "oneOf")
Expand Down
96 changes: 96 additions & 0 deletions lib/parsers/all_of_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule JS2E.Parsers.AllOfParser do
@moduledoc ~S"""
Parses a JSON schema allOf type:
{
"allOf": [
{
"type": "object",
"properties": {
"color": {
"$ref": "#/color"
},
"title": {
"type": "string"
},
"radius": {
"type": "number"
}
},
"required": [ "color", "radius" ]
},
{
"type": "string"
}
]
}
Into an `JS2E.Types.AllOfType`.
"""

require Logger
alias JS2E.{Types, TypePath, Parser}
alias JS2E.Parsers.Util
alias JS2E.Types.AllOfType

@doc ~S"""
Parses a JSON schema allOf type into an `JS2E.Types.AllOfType`.
"""
@spec parse(map, URI.t, URI.t, TypePath.t, String.t)
:: Types.typeDictionary
def parse(schema_node, parent_id, id, path, name) do
Logger.debug "Parsing '#{inspect path}' as allOf type"

descendants_types_dict =
schema_node
|> Map.get("allOf")
|> create_descendants_type_dict(parent_id, path)
Logger.debug "Descendants types dict: #{inspect descendants_types_dict}"

all_of_types =
descendants_types_dict
|> create_types_list(path)
Logger.debug "AllOf types: #{inspect all_of_types}"

all_of_type = %AllOfType{name: name,
path: path,
types: all_of_types}
Logger.debug "Parsed allOf type: #{inspect all_of_type}"

all_of_type
|> Util.create_type_dict(path, id)
|> Map.merge(descendants_types_dict)
end

@spec create_descendants_type_dict([map], URI.t, TypePath.t)
:: Types.typeDictionary
defp create_descendants_type_dict(types, parent_id, path) do
types
|> Enum.reduce({%{}, 0}, fn(child_node, {type_dict, idx}) ->

child_name = to_string idx
child_types = Parser.parse_type(child_node, parent_id, path, child_name)

{Map.merge(type_dict, child_types), idx + 1}
end)
|> elem(0)
end

@spec create_types_list(Types.typeDictionary, TypePath.t) :: [TypePath.t]
defp create_types_list(type_dict, path) do
type_dict
|> Enum.reduce(%{}, fn({child_abs_path, child_type}, reference_dict) ->

child_type_path = TypePath.add_child(path, child_type.name)

if child_type_path == TypePath.from_string(child_abs_path) do
Map.merge(reference_dict, %{child_type.name => child_type_path})
else
reference_dict
end

end)
|> Map.values()
end

end
96 changes: 96 additions & 0 deletions lib/parsers/any_of_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule JS2E.Parsers.AnyOfParser do
@moduledoc ~S"""
Parses a JSON schema anyOf type:
{
"anyOf": [
{
"type": "object",
"properties": {
"color": {
"$ref": "#/color"
},
"title": {
"type": "string"
},
"radius": {
"type": "number"
}
},
"required": [ "color", "radius" ]
},
{
"type": "string"
}
]
}
Into an `JS2E.Types.AnyOfType`.
"""

require Logger
alias JS2E.{Types, TypePath, Parser}
alias JS2E.Parsers.Util
alias JS2E.Types.AnyOfType

@doc ~S"""
Parses a JSON schema anyOf type into an `JS2E.Types.AnyOfType`.
"""
@spec parse(map, URI.t, URI.t, TypePath.t, String.t)
:: Types.typeDictionary
def parse(schema_node, parent_id, id, path, name) do
Logger.debug "Parsing '#{inspect path}' as anyOf type"

descendants_types_dict =
schema_node
|> Map.get("anyOf")
|> create_descendants_type_dict(parent_id, path)
Logger.debug "Descendants types dict: #{inspect descendants_types_dict}"

any_of_types =
descendants_types_dict
|> create_types_list(path)
Logger.debug "AnyOf types: #{inspect any_of_types}"

any_of_type = %AnyOfType{name: name,
path: path,
types: any_of_types}
Logger.debug "Parsed anyOf type: #{inspect any_of_type}"

any_of_type
|> Util.create_type_dict(path, id)
|> Map.merge(descendants_types_dict)
end

@spec create_descendants_type_dict([map], URI.t, TypePath.t)
:: Types.typeDictionary
defp create_descendants_type_dict(types, parent_id, path) do
types
|> Enum.reduce({%{}, 0}, fn(child_node, {type_dict, idx}) ->

child_name = to_string idx
child_types = Parser.parse_type(child_node, parent_id, path, child_name)

{Map.merge(type_dict, child_types), idx + 1}
end)
|> elem(0)
end

@spec create_types_list(Types.typeDictionary, TypePath.t) :: [TypePath.t]
defp create_types_list(type_dict, path) do
type_dict
|> Enum.reduce(%{}, fn({child_abs_path, child_type}, reference_dict) ->

child_type_path = TypePath.add_child(path, child_type.name)

if child_type_path == TypePath.from_string(child_abs_path) do
Map.merge(reference_dict, %{child_type.name => child_type_path})
else
reference_dict
end

end)
|> Map.values()
end

end
3 changes: 2 additions & 1 deletion lib/parsers/one_of_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ defmodule JS2E.Parsers.OneOfParser do
types
|> Enum.reduce({%{}, 0}, fn(child_node, {type_dict, idx}) ->

child_name = "#{idx}"
child_name = to_string idx
child_types = Parser.parse_type(child_node, parent_id, path, child_name)

{Map.merge(type_dict, child_types), idx + 1}
Expand All @@ -88,6 +88,7 @@ defmodule JS2E.Parsers.OneOfParser do
else
reference_dict
end

end)
|> Map.values()
end
Expand Down
11 changes: 9 additions & 2 deletions lib/printer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ defmodule JS2E.Printer do
require Logger
alias JS2E.{TypePath, Types}
alias JS2E.Printers.{ArrayPrinter, EnumPrinter, ObjectPrinter,
OneOfPrinter, PrimitivePrinter, UnionPrinter,
PreamblePrinter, TypeReferencePrinter, Util}
AllOfPrinter, AnyOfPrinter, OneOfPrinter,
PrimitivePrinter, UnionPrinter, PreamblePrinter,
TypeReferencePrinter, Util}
alias JS2E.Types.{PrimitiveType, TypeReference, SchemaDefinition}

@primitive_types ["boolean", "null", "string", "number", "integer"]
Expand Down Expand Up @@ -97,6 +98,8 @@ defmodule JS2E.Printer do
"EnumType" => &EnumPrinter.print_type/3,
"ObjectType" => &ObjectPrinter.print_type/3,
"PrimitiveType" => &PrimitivePrinter.print_type/3,
"AllOfType" => &AllOfPrinter.print_type/3,
"AnyOfType" => &AnyOfPrinter.print_type/3,
"OneOfType" => &OneOfPrinter.print_type/3,
"UnionType" => &UnionPrinter.print_type/3,
"TypeReference" => &TypeReferencePrinter.print_type/3
Expand All @@ -123,6 +126,8 @@ defmodule JS2E.Printer do
"EnumType" => &EnumPrinter.print_decoder/3,
"ObjectType" => &ObjectPrinter.print_decoder/3,
"PrimitiveType" => &PrimitivePrinter.print_decoder/3,
"AllOfType" => &AllOfPrinter.print_decoder/3,
"AnyOfType" => &AnyOfPrinter.print_decoder/3,
"OneOfType" => &OneOfPrinter.print_decoder/3,
"UnionType" => &UnionPrinter.print_decoder/3,
"TypeReference" => &TypeReferencePrinter.print_decoder/3
Expand Down Expand Up @@ -150,6 +155,8 @@ defmodule JS2E.Printer do
"EnumType" => &EnumPrinter.print_encoder/3,
"ObjectType" => &ObjectPrinter.print_encoder/3,
"PrimitiveType" => &PrimitivePrinter.print_encoder/3,
"AllOfType" => &AllOfPrinter.print_encoder/3,
"AnyOfType" => &AnyOfPrinter.print_encoder/3,
"OneOfType" => &OneOfPrinter.print_encoder/3,
"UnionType" => &UnionPrinter.print_encoder/3,
"TypeReference" => &TypeReferencePrinter.print_encoder/3
Expand Down
Loading

0 comments on commit 8746ba0

Please sign in to comment.