Skip to content

Commit

Permalink
Fix compile time issues
Browse files Browse the repository at this point in the history
  • Loading branch information
doorgan committed Jun 30, 2024
1 parent db74868 commit f3d0c45
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 13 deletions.
16 changes: 12 additions & 4 deletions lib/channel_spec/operations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ defmodule ChannelSpec.Operations do
has_payload? = Keyword.has_key?(params, :payload)
has_replies? = Keyword.has_key?(params, :replies)

params = Macro.prewalk(params, &expand_alias(&1, __CALLER__))

if not has_payload? and not has_replies? do
raise ArgumentError, "An operation must have at least a payload or replies schema"
end
Expand Down Expand Up @@ -141,6 +143,7 @@ defmodule ChannelSpec.Operations do
line = __CALLER__.line
module = __CALLER__.module
line_metadata = Macro.escape(%{"event" => %{line: line}})
schema = Macro.prewalk(schema, &expand_alias(&1, __CALLER__))

quote location: :keep,
bind_quoted: [
Expand All @@ -165,6 +168,11 @@ defmodule ChannelSpec.Operations do
end
end

defp expand_alias({:__aliases__, _, _} = alias, env),
do: Macro.expand(alias, %{env | function: {:__attr__, 3}})

defp expand_alias(other, _env), do: other

defmacro __before_compile__(_env) do
quote location: :keep do
operations_map =
Expand Down Expand Up @@ -235,20 +243,20 @@ defmodule ChannelSpec.Operations do
end

defp validate_schema!(path, definition, schema) do
if not is_map(schema) do
if is_map(schema) or is_atom(schema) do
schema
else
path_string = Enum.join(path, ".")

raise %OperationError{
message: """
The schema for #{path_string} is not a valid schema map.
The schema for #{path_string} is not a valid schema map or module.
""",
module: definition.module,
file: definition.file,
line: get_line(definition.line_metadata, path)
}
end

schema
end

defp get_line(_line_metadata, []) do
Expand Down
35 changes: 35 additions & 0 deletions lib/channel_spec/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,41 @@ defmodule ChannelSpec.Schema do

defp compile_refs(schema, refs, opts)

defp compile_refs(%{schema: schema} = operation, refs, opts) do
{schema, refs} =
if schema[:replies] do
{replies, refs} =
for {reply, schema} <- schema[:replies], reduce: {%{}, refs} do
{replies, refs} ->
{schema, refs} = compile_refs(schema, refs, opts)
{Map.put(replies, reply, schema), refs}
end

{Map.put(schema, :replies, replies), refs}
else
{schema, refs}
end

{schema, refs} =
if schema[:payload] do
{payload, refs} = compile_refs(schema[:payload], refs, opts)
{Map.put(schema, :payload, payload), refs}
else
{schema, refs}
end

{%{operation | schema: schema}, refs}
end

defp compile_refs(module, refs, opts) when is_atom(module) do
if function_exported?(module, :schema, 0) do
schema = module.schema()
compile_refs(schema, refs, opts)
else
{module, refs}
end
end

defp compile_refs(%{"$ref": ref} = schema, refs, opts) do
{ref_string, refs} =
if Keyword.get(opts, :follow_refs, true) do
Expand Down
3 changes: 3 additions & 0 deletions lib/channel_spec/serializer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ defmodule Serializer do
{key, map} when is_map(map) ->
{key, to_schema(map)}

{key, [atom | _] = list} when is_atom(atom) ->
{key, list}

{key, list} when is_list(list) ->
{key, Enum.map(list, &to_schema/1)}

Expand Down
18 changes: 9 additions & 9 deletions test/channel_spec/operations_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,34 @@ defmodule ChannelSpec.OperationsTest do
} = operations["foo"]
end

test "validates that the payload schema is a map", %{mod: mod} do
test "validates that the payload schema is valid", %{mod: mod} do
assert %Operations.OperationError{} =
err =
catch_error(
defmodule :"#{mod}" do
use ChannelSpec.Operations

operation :foo, payload: :string
operation :foo, payload: 123
def foo(_params, _context, socket), do: {:noreply, socket}
end
)

assert err.message == "The schema for payload is not a valid schema map.\n"
assert err.message == "The schema for payload is not a valid schema map or module.\n"
end

test "validates that the replies schemas are maps", %{mod: mod} do
test "validates that the replies schemas are valid", %{mod: mod} do
assert %Operations.OperationError{} =
err =
catch_error(
defmodule :"#{mod}" do
use ChannelSpec.Operations

operation :foo, replies: %{ok: :string}
operation :foo, replies: %{ok: 123}
def foo(_params, _context, socket), do: {:noreply, socket}
end
)

assert err.message == "The schema for replies.ok is not a valid schema map.\n"
assert err.message == "The schema for replies.ok is not a valid schema map or module.\n"
end

test "validates that at least the payload schema is defined" do
Expand Down Expand Up @@ -182,19 +182,19 @@ defmodule ChannelSpec.OperationsTest do
} = subscriptions
end

test "validates that the schema is a map", %{mod: mod} do
test "validates that the schema is valid", %{mod: mod} do
assert %Operations.OperationError{} =
err =
catch_error(
defmodule :"#{mod}" do
use ChannelSpec.Operations

subscription "foo", :string
subscription "foo", 123
def foo(_params, _context, socket), do: {:noreply, socket}
end
)

assert err.message == ~s(The schema for subscription "foo" is not a valid schema map.\n)
assert err.message == ~s(The schema for subscription "foo" is not a valid schema map or module.\n)
end
end
end

0 comments on commit f3d0c45

Please sign in to comment.