Skip to content

Commit

Permalink
Merge pull request #1005 from solvedata/Fix-description-function-calls
Browse files Browse the repository at this point in the history
Fix description function calls
  • Loading branch information
benwilson512 authored Dec 24, 2020
2 parents ecf55d4 + 5ef5b90 commit eb697f3
Show file tree
Hide file tree
Showing 18 changed files with 1,994 additions and 14 deletions.
79 changes: 65 additions & 14 deletions lib/absinthe/schema/notation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,12 @@ defmodule Absinthe.Schema.Notation do

__CALLER__
|> recordable!(:object, @placement[:object])
|> record!(Schema.ObjectTypeDefinition, identifier, attrs, block)
|> record!(
Schema.ObjectTypeDefinition,
identifier,
attrs |> Keyword.update(:description, nil, &wrap_in_unquote/1),
block
)
end

@placement {:interfaces, [under: [:object]]}
Expand Down Expand Up @@ -407,6 +412,7 @@ defmodule Absinthe.Schema.Notation do
|> expand_ast(caller)
|> Keyword.delete(:args)
|> Keyword.delete(:meta)
|> Keyword.update(:description, nil, &wrap_in_unquote/1)
|> handle_deprecate

{attrs, block}
Expand Down Expand Up @@ -719,7 +725,7 @@ defmodule Absinthe.Schema.Notation do
defmacro scalar(identifier, attrs, do: block) do
__CALLER__
|> recordable!(:scalar, @placement[:scalar])
|> record!(Schema.ScalarTypeDefinition, identifier, attrs, block)
|> record_scalar!(identifier, attrs, block)
end

@doc """
Expand All @@ -730,13 +736,13 @@ defmodule Absinthe.Schema.Notation do
defmacro scalar(identifier, do: block) do
__CALLER__
|> recordable!(:scalar, @placement[:scalar])
|> record!(Schema.ScalarTypeDefinition, identifier, [], block)
|> record_scalar!(identifier, [], block)
end

defmacro scalar(identifier, attrs) do
__CALLER__
|> recordable!(:scalar, @placement[:scalar])
|> record!(Schema.ScalarTypeDefinition, identifier, attrs, nil)
|> record_scalar!(identifier, attrs, nil)
end

@placement {:serialize, [under: [:scalar]]}
Expand Down Expand Up @@ -951,7 +957,12 @@ defmodule Absinthe.Schema.Notation do
defmacro input_object(identifier, attrs \\ [], do: block) do
__CALLER__
|> recordable!(:input_object, @placement[:input_object])
|> record!(Schema.InputObjectTypeDefinition, identifier, attrs, block)
|> record!(
Schema.InputObjectTypeDefinition,
identifier,
attrs |> Keyword.update(:description, nil, &wrap_in_unquote/1),
block
)
end

# UNIONS
Expand Down Expand Up @@ -982,7 +993,12 @@ defmodule Absinthe.Schema.Notation do
defmacro union(identifier, attrs \\ [], do: block) do
__CALLER__
|> recordable!(:union, @placement[:union])
|> record!(Schema.UnionTypeDefinition, identifier, attrs, block)
|> record!(
Schema.UnionTypeDefinition,
identifier,
attrs |> Keyword.update(:description, nil, &wrap_in_unquote/1),
block
)
end

@placement {:types, [under: [:union]]}
Expand Down Expand Up @@ -1086,10 +1102,11 @@ defmodule Absinthe.Schema.Notation do
|> expand_ast(env)
|> Keyword.update(:values, [], fn values ->
Enum.map(values, fn ident ->
value_attrs = handle_enum_value_attrs(ident, module: env.module)
value_attrs = handle_enum_value_attrs(ident, [], env)
struct!(Schema.EnumValueDefinition, value_attrs)
end)
end)
|> Keyword.update(:description, nil, &wrap_in_unquote/1)
end

@placement {:value, [under: [:enum]]}
Expand Down Expand Up @@ -1313,6 +1330,7 @@ defmodule Absinthe.Schema.Notation do
raw_attrs
|> Keyword.put_new(:name, to_string(identifier))
|> Keyword.put_new(:type, type)
|> Keyword.update(:description, nil, &wrap_in_unquote/1)
|> handle_deprecate
end

Expand Down Expand Up @@ -1341,6 +1359,7 @@ defmodule Absinthe.Schema.Notation do
attrs
|> Keyword.put(:identifier, identifier)
|> Keyword.put_new(:name, to_string(identifier))
|> Keyword.update(:description, nil, &wrap_in_unquote/1)

scoped_def(env, Schema.DirectiveDefinition, identifier, attrs, block)
end
Expand Down Expand Up @@ -1423,16 +1442,27 @@ defmodule Absinthe.Schema.Notation do
scoped_def(env, :enum, identifier, attrs, block)
end

defp reformat_description(text), do: String.trim(text)

@doc false
# Record a description in the current scope
def record_description!(env, text_block) do
text = reformat_description(text_block)
text = wrap_in_unquote(text_block)

put_attr(env.module, {:desc, text})
end

def handle_enum_value_attrs(identifier, raw_attrs) do
@doc false
# Record a scalar
def record_scalar!(env, identifier, attrs, block_or_nil) do
record!(
env,
Schema.ScalarTypeDefinition,
identifier,
attrs |> Keyword.update(:description, nil, &wrap_in_unquote/1),
block_or_nil
)
end

def handle_enum_value_attrs(identifier, raw_attrs, env) do
value =
case Keyword.get(raw_attrs, :as, identifier) do
value when is_tuple(value) ->
Expand All @@ -1445,18 +1475,19 @@ defmodule Absinthe.Schema.Notation do
end

raw_attrs
|> expand_ast(raw_attrs)
|> expand_ast(env)
|> Keyword.put(:identifier, identifier)
|> Keyword.put(:value, value)
|> Keyword.put_new(:name, String.upcase(to_string(identifier)))
|> Keyword.delete(:as)
|> Keyword.update(:description, nil, &wrap_in_unquote/1)
|> handle_deprecate
end

@doc false
# Record an enum value in the current scope
def record_value!(env, identifier, raw_attrs) do
attrs = handle_enum_value_attrs(identifier, raw_attrs)
attrs = handle_enum_value_attrs(identifier, raw_attrs, env)
record!(env, Schema.EnumValueDefinition, identifier, attrs, [])
end

Expand All @@ -1467,7 +1498,7 @@ defmodule Absinthe.Schema.Notation do
values
|> expand_ast(env)
|> Enum.map(fn ident ->
value_attrs = handle_enum_value_attrs(ident, module: env.module)
value_attrs = handle_enum_value_attrs(ident, [], env)
struct!(Schema.EnumValueDefinition, value_attrs)
end)

Expand Down Expand Up @@ -1506,6 +1537,14 @@ defmodule Absinthe.Schema.Notation do
put_attr(env.module, {:middleware, [new_middleware]})
end

# We wrap the value (from the user) in an `unquote` call, so that when the schema `blueprint` is
# placed into `__absinthe_blueprint__` via `unquote(Macro.escape(blueprint, unquote: true))` the
# value get's unquoted. This allows us to evaluate function calls in the scope of the schema
# module.
defp wrap_in_unquote(value) do
{:unquote, [], [value]}
end

# ------------------------------

@doc false
Expand Down Expand Up @@ -1888,6 +1927,18 @@ defmodule Absinthe.Schema.Notation do

defp expand_ast(ast, env) do
Macro.prewalk(ast, fn
# We don't want to expand `@bla` into `Module.get_attribute(module, @bla)` because this
# function call will fail if the module is already compiled. Remember that the ast gets put
# into a generated `__absinthe_blueprint__` function which is called at "__after_compile__"
# time. This will be after a module has been compiled if there are multiple modules in the
# schema (in the case of an `import_types`).
#
# Also see test "test/absinthe/type/import_types_test.exs"
# "__absinthe_blueprint__ is callable at runtime even if there is a module attribute"
# and it's comment for more information
{:@, _, _} = node ->
node

{_, _, _} = node ->
Macro.expand(node, env)

Expand Down
43 changes: 43 additions & 0 deletions test/absinthe/execution/arguments/scalar_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Absinthe.Execution.Arguments.ScalarTest do
use Absinthe.Case, async: true

alias Absinthe.Blueprint.Input
alias Absinthe.Fixtures.Scalar

@schema Absinthe.Fixtures.ArgumentsSchema

Expand Down Expand Up @@ -58,4 +60,45 @@ defmodule Absinthe.Execution.Arguments.ScalarTest do
)
end
end

describe "scalar keyword description evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Scalar.TestSchemaDescriptionKeyword.__absinthe_type__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end

describe "scalar description attribute evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Absinthe.Fixtures.FunctionEvaluationHelpers.filter_test_params_for_description_attribute()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Scalar.TestSchemaDescriptionAttribute.__absinthe_type__(unquote(test_label))

assert type.description == unquote(expected_value)
end
end)
end

describe "scalar description macro evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Scalar.TestSchemaDescriptionMacro.__absinthe_type__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end
end
58 changes: 58 additions & 0 deletions test/absinthe/type/directive_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Absinthe.Type.DirectiveTest do
use Absinthe.Case, async: true

alias Absinthe.Schema
alias Absinthe.Fixtures.Directive

defmodule TestSchema do
use Absinthe.Schema
Expand Down Expand Up @@ -224,4 +225,61 @@ defmodule Absinthe.Type.DirectiveTest do
Absinthe.run(@query, Absinthe.Fixtures.ContactSchema)
end
end

describe "directive keyword description evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Directive.TestSchemaDescriptionKeyword.__absinthe_directive__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end

describe "directive description attribute evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Absinthe.Fixtures.FunctionEvaluationHelpers.filter_test_params_for_description_attribute()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type =
Directive.TestSchemaDescriptionAttribute.__absinthe_directive__(unquote(test_label))

assert type.description == unquote(expected_value)
end
end)
end

describe "directive description macro evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Directive.TestSchemaDescriptionMacro.__absinthe_directive__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end

describe "directive arg keyword description evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type =
Directive.TestSchemaArgDescriptionKeyword.__absinthe_directive__(unquote(test_label))

assert type.args[:arg_example].description == unquote(expected_value)
end
end)
end
end
56 changes: 56 additions & 0 deletions test/absinthe/type/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Absinthe.Type.EnumTest do
use Absinthe.Case, async: true

alias Absinthe.Type
alias Absinthe.Fixtures.Enums

defmodule TestSchema do
use Absinthe.Schema
Expand Down Expand Up @@ -71,4 +72,59 @@ defmodule Absinthe.Type.EnumTest do
assert %Type.Enum.Value{name: "RED", value: :red, description: nil} = type.values[:red]
end
end

describe "enum value description evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type =
Enums.TestSchemaValueDescriptionKeyword.__absinthe_type__(:description_keyword_argument)

assert type.values[unquote(test_label)].description == unquote(expected_value)
end
end)
end

describe "enum description keyword evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Enums.TestSchemaDescriptionKeyword.__absinthe_type__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end

describe "enum description attribute evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Absinthe.Fixtures.FunctionEvaluationHelpers.filter_test_params_for_description_attribute()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Enums.TestSchemaDescriptionAttribute.__absinthe_type__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end

describe "enum description macro evaluation" do
Absinthe.Fixtures.FunctionEvaluationHelpers.function_evaluation_test_params()
|> Enum.each(fn %{
test_label: test_label,
expected_value: expected_value
} ->
test "for #{test_label} (evaluates description to '#{expected_value}')" do
type = Enums.TestSchemaDescriptionMacro.__absinthe_type__(unquote(test_label))
assert type.description == unquote(expected_value)
end
end)
end
end
Loading

0 comments on commit eb697f3

Please sign in to comment.