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

Deduplicate directives when building schema #1242

Merged
merged 4 commits into from
May 13, 2023

Conversation

MaeIsBad
Copy link
Contributor

When importing directives from the prototype some of them were duplicated, which caused multiple functions with the same clause to be generated.
This should (partially?) fix #1233

Thanks @maartenvanvliet for looking into the original issue 💙

When importing directives from the prototype some of them were
duplicated, which caused multiple functions with the same clause
to be generated.
Copy link
Contributor

@benwilson512 benwilson512 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey thanks for taking a look at this! I have some concerns about solving it this way that I have noted.


directive_artifacts =
(schema.directive_artifacts ++ build_directives(blueprint))
|> Enum.uniq_by(fn v -> v.identifier end)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I am missing something, shouldn't directives with duplicate identifiers be flagged with errors just like objects with duplicate identifiers? Silently removing them doesn't seem good either because there is no guarantee that the directives are logically identical.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if duplicate directives cause an error, currently it will cause the code generation to always fail, since absinthe imports duplicate directives from the prototype schema.

I'm not a fan for a few reasons but would using {v.expand, v.identifier} instead work for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that this doesn't change the current behavior.
Absinthe already will overwrite directives if build is called with duplicates, all this does is remove a confusing elixir warning that the users can't really deal with themselves

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a check for duplicate directive identifiers, only this happens before the prototype directives are imported into the schema.

I think alternatively we can prevent the issue from happening by not importing the default directives into the prototype schema. They are of no use there as include/skip only work at the runtime level of a document's execution.

--- a/lib/absinthe/schema/prototype/notation.ex
+++ b/lib/absinthe/schema/prototype/notation.ex
@@ -42,6 +42,10 @@ defmodule Absinthe.Schema.Prototype.Notation do
           Absinthe.Phase.Schema.TypeExtensionImports,
           {Absinthe.Phase.Schema.TypeExtensionImports, []}
         )
+        |> Absinthe.Pipeline.replace(
+          Absinthe.Phase.Schema.DirectiveImports,
+          {Absinthe.Phase.Schema.DirectiveImports, []}
+        )
       end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! So that's the issue then. Could we simply do without(Absinthe.Phase.Schema.DirectiveImports) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maartenvanvliet you mentioned here that

Whilst investigating, I found a similar bug happens with the extend notation, this can also cause multiple functions with the same function heads to be generated

is this the same issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's related, the Schema.Notation module does a pass over the blueprint to lift the functions defined in the schema to the schema module.
When it walks over the blueprint with a custom scalar, that is later extended using extend if will generate the same lifted function for both of them.

E.g.

  def __absinthe_function__(
      {Absinthe.Blueprint.Schema.ScalarTypeDefinition, :my_custom_scalar},
      :serialize
  ) do
    nil
  end

will be generated twice.

What I am as of yet unsure of is whether the first or second generated function can simply be removed. In the above case the second can be removed as serialize cannot be overridden by the extended scalar. I don't know whether there are cases currently where the second cannot be removed.

Regardless, supporting the overriding of serialize and similar in the extend syntax is something I've been thinking about for a while. Though it is beyond the graphql spec it would make it easier to override a custom scalar or even a resolver. A use case I have is overriding the introspection resolvers with my own.

I'll have a look at the directive issue you're having

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this solution is(I think) that on every "level" of schema inheritance a new :deprecated directive is defined

directive :deprecated do

which still causes more of the same errors to appear

I cannot reproduce this. Do you have a repo showing this issue? Ideally with the OTP/elixir version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, I am unable to reproduce this today as well, no idea why I thought that was what was happening yesterday.

I found that using the schema do notation causes more errors to be generated

defmodule MyAppWeb.Schema do
  use Absinthe.Schema

  schema do
   field :query, :query
   field :subscription, :subscription
  end

  subscription do
   field :name, :string
  end

  query do
    field :item, :string
  end
end

this doesn't happen if I remove the subscription

Compiling 1 file (.ex)
warning: this clause cannot match because a previous clause at line 1 always matches
  lib/asdf.ex:1

warning: this clause cannot match because a previous clause at line 1 always matches
  lib/asdf.ex:1

warning: this clause cannot match because a previous clause at line 1 always matches
  lib/asdf.ex:1

warning: this clause cannot match because a previous clause at line 1 always matches
  lib/asdf.ex:1

Compilation failed due to warnings while using the --warnings-as-errors option

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the schema notation has the same issues as the extend notation. The fix should be similar as well.

@MaeIsBad MaeIsBad closed this Apr 13, 2023
@MaeIsBad MaeIsBad reopened this Apr 13, 2023
MaeIsBad and others added 2 commits April 13, 2023 14:32
They are of no use there as include/skip only work at the runtime level
of a document's execution and the default directives are already
included when generating a schema.
Importing the same directives multiple times caused issues due to
absinthe generating multiple functions with the same clause.

Co-authored-by: Maarten van Vliet <maartenvanvliet@gmail.com>
Co-authored-by: Ben Wilson <benwilson512@gmail.com>
@benwilson512
Copy link
Contributor

@maartenvanvliet do you have any concerns with not importing the default directives into prototypes?

@maartenvanvliet
Copy link
Contributor

@maartenvanvliet do you have any concerns with not importing the default directives into prototypes?

No, that would work.

@benwilson512
Copy link
Contributor

Thanks for handling this @MaeIsBad !

@benwilson512 benwilson512 merged commit 086393b into absinthe-graphql:master May 13, 2023
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

Successfully merging this pull request may close these issues.

Schema compilation with absinthe 1.7.1 generates warnings with elixir version < 1.14
3 participants