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

Override graphql_name to allow for shared mutations #225

Open
TomasBarry opened this issue Apr 7, 2022 · 2 comments
Open

Override graphql_name to allow for shared mutations #225

TomasBarry opened this issue Apr 7, 2022 · 2 comments

Comments

@TomasBarry
Copy link
Contributor

What is the problem the enhancement will solve?

In our system, we have multiple different resources. We would like to have a single mutation per operation that can be shared between the various resources.

Essentially, be able to use a Union Type for the authenticable resource where all resources can share a common mutation rather than having to implement one mutation per resource per operation.

Describe the solution you have in mind

Allow for the overriding of graphql_name in a custom mutation that inherits from one of the GraphQLDevise operations:

module Mutations
  # A single login mutation that can be used to log in any resource
  class ResourceLogin < GraphqlDevise::Mutations::Login
    # A union type representing the Authenticable types (those types that can
    # be logged in). This allows for us to have a single login mutation rather
    # than one mutation per resource. We can use the mutation as:
    #
    # mutation {
    #   resourceLogin(
    #     email: "[email protected]",
    #     password: "some_password"
    #   ) {
    #     authenticatable {
    #       ... on ResourceA {
    #         id
    #       }
    #     }
    #   }
    # }
    class Authenticable < Types::BaseUnion
      description 'An authenticatable resource'
      possible_types(
        Types::ResourceAType,
        Types::ResourceBType
      )

      def self.resolve_type(object, _context)
        case object
        when ResourceA then Types::ResourceAType
        when ResourceB then Types::ResourceBType
        else raise GraphQL::ExecutionError, "Unhandled Authenticable #{object.class}"
        end
      end
    end

    graphql_name 'resource_login'

    field :authenticatable, Authenticable, null: false

    def resolve(email:, password:)
      super do |resource|
        # custom behaviour
      end
    end
  end
end

Then the schema file can mount resources like:

GraphqlDevise::ResourceLoader.new(
  ResourceA,
  only: [
    :login
  ],
  operations: {
    login: Mutations::ResourceLogin
  }
),
GraphqlDevise::ResourceLoader.new(
  ResourceB,
  only: [
    :login
  ],
  operations: {
    login: Mutations::ResourceLogin
  }
)

Describe alternatives you've considered

One mutation per resource.

Additional context

It seems that there is no customisation of the mutation name available in https://github.com/graphql-devise/graphql_devise/blob/v0.18.2/lib/graphql_devise/mount_method/operation_preparers/default_operation_preparer.rb

It appears that the above ignores any custom graphql_name setting in a mutation linked to an operation.

@00dav00
Copy link
Contributor

00dav00 commented Apr 10, 2022

Hi @TomasBarry , I see your point here, I guess the graphql_name could be honored in case it is explicitly set in the class instead of concatenating the resource and action. We will have to check the implications of this more carefully before implementing. Are you interested in opening a PR for this?

About using a single mutation to login all resources, there are 2 things that I would like to mention:

  • At the moment additional_operations key won't add the resource namespace to operations in the gql schema, perhaps you can use this config key to accomplish what you need?
  • This method is executed when looking up the resource by it's provider (email in most cases). To accomplish this, the mutation classes have a variable called resource_klass set so that the gem knows which AR model to use when building the DB query. Have you considered how having a single mutation to login all resources would work for this part of the code?

@lagparty
Copy link

Hi, I'm having the same issue with two authenticatable models. I thought I could share the same mutation between them by using the GraphqlDevise::ResourceLoader, but sometimes it used one model or the other (I think this is caused by sharing a class-level instance variable).

I have no issue with duplicating code in the meantime, but I'm worried that I could accidentally share logins between different models. Would it be safe to do this? (in the sense that userLogin and userLogout won't collide with adminLogin and adminLogout)

class SchemaA < GraphQL::Schema # this one is public
  use GraphqlDevise::SchemaPlugin.new(
    query: Types::QueryTypeA,
    mutation: Types::MutationTypeA,
    public_introspection: true,
    resource_loaders: [
      GraphqlDevise::ResourceLoader.new(
        User,
        only: %i[
          login
          logout
          register
          update_password_with_token
          send_password_reset_with_token
          confirm_registration_with_token
        ],
        operations: {
          login: Mutations::UserLogin,
        }
      )
    ]
  )
  # ...etc
end
class SchemaB < GraphQL::Schema # this one is private
  use GraphqlDevise::SchemaPlugin.new(
    query: Types::QueryTypeB,
    mutation: Types::MutationTypeB,
    resource_loaders: [
      GraphqlDevise::ResourceLoader.new(
        Admin,
        only: %i[
          login
          logout
        ],
        operations: {
          login: Mutations::AdminLogin
        }
      )
    ]
  )
  # ...etc
end

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

3 participants