Skip to content
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

The given atom is not a module #91

Closed
wbyrnetx opened this issue May 1, 2020 · 1 comment
Closed

The given atom is not a module #91

wbyrnetx opened this issue May 1, 2020 · 1 comment

Comments

@wbyrnetx
Copy link

wbyrnetx commented May 1, 2020

I've been following this guide for setting up absinthe + dataloader + ecto.
https://hexdocs.pm/absinthe/ecto.html

I cannot for the life of me figure out why none of the example in that guide (or the many others I've searched) do not work for my application.

When I make this GraphQL query:

{ families { id, name, traits { id } }}

I get an exception that reads The given atom - :traits - is not a module..

** (exit) an exception was raised:
    ** (Dataloader.GetError)   The given atom - :traits - is not a module.

  This can happen if you intend to pass an Ecto struct in your call to
  `dataloader/4` but pass something other than a struct.

        (dataloader 1.0.7) lib/dataloader/ecto.ex:468: Dataloader.Source.Dataloader.Ecto.validate_queryable/1
        (dataloader 1.0.7) lib/dataloader/ecto.ex:442: Dataloader.Source.Dataloader.Ecto.get_keys/2
        (dataloader 1.0.7) lib/dataloader/ecto.ex:370: Dataloader.Source.Dataloader.Ecto.load/3
        (elixir 1.10.3) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
        (dataloader 1.0.7) lib/dataloader.ex:128: Dataloader.load_many/4
        (absinthe 1.4.16) lib/absinthe/resolution/helpers.ex:255: Absinthe.Resolution.Helpers.do_dataloader/6
        (absinthe 1.4.16) lib/absinthe/resolution.ex:209: Absinthe.Resolution.call/2
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:209: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:168: Absinthe.Phase.Document.Execution.Resolution.do_resolve_field/4
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:87: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:257: Absinthe.Phase.Document.Execution.Resolution.build_result/4
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:53: Absinthe.Phase.Document.Execution.Resolution.perform_resolution/3
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:24: Absinthe.Phase.Document.Execution.Resolution.resolve_current/3
        (absinthe 1.4.16) lib/absinthe/pipeline.ex:274: Absinthe.Pipeline.run_phase/3
        (absinthe_plug 1.4.7) lib/absinthe/plug.ex:421: Absinthe.Plug.run_query/4

Here are the relevant parts of my Schema module...

The query block:

query do
  @desc "Get all families"
  field :families, list_of(:family) do
    resolve &Resolvers.Catalog.list_families/3
  end
end

The resolver

def list_families(_parent, _args, _resultion) do
  families = Family
    |> Repo.all()
    |> Enum.map(&Map.drop &1, [:__meta__, :__struct__, :traits])

  {:ok, families}
end

The family object:

object :family do
  field :id, :id
  field :name, :string
  field :traits, list_of(:trait), resolve: dataloader(Catalog)
end

The dataloader

def data() do
  Dataloader.Ecto.new(Repo)
end

The trait object:

object :trait do
  field :id, :id
  field :name, :string
end

I am under the impression that Dataloader should be able to figure out which Ecto schema I'm referring to because the association name matches the names of the GraphQL field.

Here is the schema for family

schema "families" do
  field :name, :string
  timestamps()
  many_to_many :traits, Trait, join_through: "families_traits"
end

I don't know if my exception is an issue with Dataloader or not, but I do believe there is an issue with the exception message. It refers to a function dataloader/4 which I do not see anywhere. Should that say dataloader/3?

Also... when I try to remedy the exception by using Absinthe.Resolution.Helpers.dataloader/3 and explicitly passing the Ecto schema to use.. I get another exception that makes even less sense to me.

Here is the family object with dataloader/3

object :family do
  field :id, :id
  field :name, :string
  field :traits, list_of(:trait), resolve: dataloader(Catalog, Trait, [])
end

And here's the exception

Request: POST /api/graphql
** (exit) an exception was raised:
    ** (Dataloader.GetError) {{:badmatch, :error}, [{Dataloader.Source.Dataloader.Ecto, :"-run_batch/2-fun-1-", 3, [file: 'lib/dataloader/ecto.ex', line: 548]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: 'lib/enum.ex', line: 2111]}, {Dataloader.Source.Dataloader.Ecto, :run_batch, 2, [file: 'lib/dataloader/ecto.ex', line: 547]}, {Task.Supervised, :invoke_mfa, 2, [file: 'lib/task/supervised.ex', line: 90]}, {Task.Supervised, :reply, 5, [file: 'lib/task/supervised.ex', line: 35]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}
        (dataloader 1.0.7) lib/dataloader.ex:198: Dataloader.do_get/2
        (absinthe 1.4.16) lib/absinthe/resolution/helpers.ex:257: anonymous fn/5 in Absinthe.Resolution.Helpers.do_dataloader/6
        (absinthe 1.4.16) lib/absinthe/middleware/dataloader.ex:33: Absinthe.Middleware.Dataloader.get_result/2
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:209: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:168: Absinthe.Phase.Document.Execution.Resolution.do_resolve_field/4
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:77: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:87: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:77: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:53: Absinthe.Phase.Document.Execution.Resolution.perform_resolution/3
        (absinthe 1.4.16) lib/absinthe/phase/document/execution/resolution.ex:24: Absinthe.Phase.Document.Execution.Resolution.resolve_current/3
        (absinthe 1.4.16) lib/absinthe/pipeline.ex:274: Absinthe.Pipeline.run_phase/3
        (absinthe_plug 1.4.7) lib/absinthe/plug.ex:421: Absinthe.Plug.run_query/4
        (absinthe_plug 1.4.7) lib/absinthe/plug.ex:247: Absinthe.Plug.call/2
        (phoenix 1.4.17) lib/phoenix/router/route.ex:41: Phoenix.Router.Route.call/2
        (phoenix 1.4.17) lib/phoenix/router.ex:288: Phoenix.Router.__call__/2
        (myapp 0.1.0) lib/myapp_web/endpoint.ex:1: MyappWeb.Endpoint.plug_builder_call/2
        (myapp 0.1.0) lib/plug/debugger.ex:132: MyappWeb.Endpoint."call (overridable 3)"/2
@wbyrnetx
Copy link
Author

wbyrnetx commented May 1, 2020

Mystery solved... it was the Map.drop in my resolver causing the issue.

def list_families(_parent, _args, _resolution) do
  families = Family
    |> Repo.all()
    |> Enum.map(&Map.drop &1, [:__meta__, :__struct__, :traits])

  {:ok, families}
end

Without the __meta__ and __struct__ keys the dataloader does not know what to do with the association. The call to Map.drop was a carry-over from my controller when I did not want to preload the association.

@wbyrnetx wbyrnetx closed this as completed May 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant