-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use compile-time functions or module attributes in schemas #946
Comments
I just had the same issue using a macro defined directly inside the type module but if I extract it into it's own module and define the macro there it does compile properly. You can surely make the macro dynamic, but I'm not really experiences with this 😉 and it fits my needs. defmodule MyApp.GraphQL.Schema.Macros do
defmacro currency_enum do
quote do
enum :currency, values: unquote(Money.Currency.known_current_currencies())
end
end
end
defmodule MyApp.GraphQL.Schema.Types do
require MyApp.GraphQL.Schema.Macros
MyApp.GraphQL.Schema.Macros.currency_enum()
# ...other type definitions...
end |
Wanted to confirm @benvp's recommendation. An absinthe codebase where I had defined it within the same module did not work, but works in a different project where I was importing a custom helper from another module: defmodule Hutti.GraphQL.Helpers do
@doc "Generate an Absinthe Notation Enum from an EctoEnum definition"
defmacro enum_from_ecto(name, module) when is_atom(name) do
name = Macro.expand(name, __CALLER__)
module = Macro.expand(module, __CALLER__)
values = module.__enum_map__()
quote do
enum(unquote(name), values: unquote(values))
end
end
# ...
end Usage: enum_from_ecto(:post_type, MyApp.Post.Type) |
@sheharyarn it's a really nice use of macro. However, when we try to use it, we get a compiler warning from the file calling it:
|
Looks like you have (at least) two clauses of the same macro/function defined and the second one can never match. https://elixirforum.com/t/help-in-understanding-code-that-always-matches/11812 |
Is there a good reason to capture
|
I'm actually going to have a go at making a PR to get the old 1.4 behaviour back (Being able to evaluate the functions at compile time) @benwilson512 Do you remember why you decided to with the |
I've had a go at making 1.5 eval code. My solution was to disable the explicit raises in notation.ex when a value is a tuple (e.g. a function call AST), marking it with metadata, and in compile.ex in The problem with the approach is that compile.ex puts the resulting AST in a module called @benwilson512 Thoughts? |
@dylan-chong another option would be to use |
Oh that's a neat trick! Any thoughts on the |
@dylan-chong If you use |
Holy crap you're right! That's magical! 🙏
the error is
i have definitely defined this in the EnumTest module
Is this because the nested module (TestSchema) is compiled before the outer module (EnumTest)? |
@dylan-chong Yes that is why, and no that can't be avoided. All functions called within the schema must exist in modules outside the schema, and outside any modules that wrap the schema. |
Interesting. Thanks, I'll try and put up a PR in the next couple weeks with some tests too |
Hi folks, here's an example of dynamic values using Hopefully this addresses the various needs in this thread... |
Thanks for replying @binaryseed the hydrate function doesn't suit our needs. We have a thousand+ fields that look like the following
This would be incredibly tedious to do for all our fields. Not to mention, hydrate is not the most convenient api We do need to keep the values inline, otherwise we're going to have to stay with Absinthe 1.4 Re this:
I've had to push it back, but still keen to get around to it at some point (unless someone beats me to it) |
PR #1005 for fixing the descriptions part of the problem (Fixes 95% of the function evaluation problems, but not enum values just yet). Will continue with those in the future |
As per the comments in #1005, the descriptions are now evaluated, but the following are not:
i've had a go at evaluating enum values. here is the diff so far diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex
index 120d922a..cc702baa 100644
--- a/lib/absinthe/schema/notation.ex
+++ b/lib/absinthe/schema/notation.ex
@@ -1463,22 +1463,18 @@ defmodule Absinthe.Schema.Notation do
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) ->
- raise Absinthe.Schema.Notation.Error,
- "Invalid Enum value for #{inspect(identifier)}. " <>
- "Must be a literal term, dynamic values must use `hydrate`"
-
- value ->
- value
- end
+ value = wrap_in_unquote(Keyword.get(raw_attrs, :as, identifier))
raw_attrs
|> expand_ast(env)
|> Keyword.put(:identifier, identifier)
|> Keyword.put(:value, value)
- |> Keyword.put_new(:name, String.upcase(to_string(identifier)))
+ |> Keyword.put_new_lazy(:name, fn ->
+ quote do
+ unquote(wrap_in_unquote(identifier))
+ |> String.upcase()
+ end
+ end)
|> Keyword.delete(:as)
|> Keyword.update(:description, nil, &wrap_in_unquote/1)
|> handle_deprecate
diff --git a/test/absinthe/type/enum_test.exs b/test/absinthe/type/enum_test.exs
index 98dc8a01..c60e42bd 100644
--- a/test/absinthe/type/enum_test.exs
+++ b/test/absinthe/type/enum_test.exs
@@ -127,4 +127,16 @@ defmodule Absinthe.Type.EnumTest do
end
end)
end
+
+ describe "enum values (list) 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 only value to '#{expected_value}')" do
+ type = Enums.TestSchemaValues.__absinthe_type__(unquote(test_label))
+ assert type.values == [unquote(expected_value)]
+ end
+ end)
end
diff --git a/test/support/fixtures/enums.ex b/test/support/fixtures/enums.ex
index d4d30836..ef8dfdd6 100644
--- a/test/support/fixtures/enums.ex
+++ b/test/support/fixtures/enums.ex
@@ -182,4 +182,31 @@ defmodule Absinthe.Fixtures.Enums do
description "hello #{@module_attribute}"
end
end
+
+ defmodule TestSchemaValues do
+ use Absinthe.Schema
+ @module_attribute "goodbye"
+
+ defmodule NestedModule do
+ def nested_function(arg1) do
+ arg1
+ end
+ end
+
+ query do
+ end
+
+ def test_function(arg1) do
+ arg1
+ end
+
+ enum :normal_string, values: ["string"]
+ enum :local_function_call, values: [test_function("red")]
+ enum :function_call_using_absolute_path_to_current_module, values: [Absinthe.Fixtures.Enums.TestSchemaDescriptionMacro.test_function("red")]
+ enum :standard_library_function, values: [String.replace("red", "e", "a")]
+ enum :function_in_nested_module, values: [NestedModule.nested_function("hello")]
+ enum :external_module_function_call, values: [Absinthe.Fixtures.FunctionEvaluationHelpers.external_function("hello")]
+ enum :module_attribute_string_concat, values: ["hello " <> @module_attribute]
+ enum :interpolation_of_module_attribute, values: ["hello #{@module_attribute}"]
+ end
end however there is compile time validation that runs before the values can be evaluated. so this is going to require some architectural changes, which is beyond me.
|
@benwilson512 @binaryseed Is enum value evaluation something that you'd plan to do in the future? |
@dylan-chong yes, I hope to have something out this week addressing it. |
Has this been fixed? I need something like this because I'm getting the values for the enums from config enum(:templates_mails_types) do
Application.get_env(:app,:mail_templates) |> Enum.map(fn {a,b} -> a end)
|> values
end |
Hey @matheuscamarques the canonical way of doing this now would be with a custom compilation phase instead of trying to do this via macros https://hexdocs.pm/absinthe/Absinthe.Schema.html#module-custom-schema-manipulation-in-progress |
@matheuscamarques you can still use a custom macro as suggested in some of the comments above, if you find the blueprint compilation process too complicated |
Environment
Elixir: v1.9.2 (compiled with Erlang/OTP 20)
Absinthe: v1.5.1
Overview
When using module attributes or functions in
enum
, the code fails to compile. This bug was previously recorded in #448 and was marked resolved inmaster
and closed, but after upgrading to Absinthe 1.5 still doesn't work.This is different from #945 where functions could previously be called in the
value/2
attributes inenum
'sdo
block.Relevant Code
Examples of code that cause the compilation to fail:
Expected behavior
For the application to compile and work correctly.
Actual behavior
The code fails with the following error (when used with the module attribute)
Previous Workarounds
Before Absinthe 1.5, the workarounds mentioned in #448 would allow getting around the problem, but now they don't work either:
The text was updated successfully, but these errors were encountered: