Coder Social home page Coder Social logo

samesystem / graphql_rails Goto Github PK

View Code? Open in Web Editor NEW
153.0 153.0 12.0 584 KB

GraphQL on Rails. Write GraphQL server side in rails way

Home Page: https://samesystem.github.io/graphql_rails

License: MIT License

Ruby 99.06% Shell 0.05% HTML 0.89%
graphql ruby ruby-on-rails

graphql_rails's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql_rails's Issues

Allow to have enum permitted attributes

Now it's only posible to specify enums for input types like this:

class MoModel
  graphql.input do |c|
    c.attribute(:field, enum: %i[value1 value2]
  end
end

but it's not possible to specify enum values via permit method. It would be nice to be able to do something like this:

class MyModel
  graphql do |c|
    c.attribute(:field).permit_input(:some_attribute, enum: %i[value1 value2])
  end
end

or

class MyModel
  graphql do |c|
    c.attribute(:field).permit_enum(:some_attribute, values: %i[value1 value2])
  end
end

or maybe just having easy way to define enum types easy:

MyEnum = GraphqlRails::Enum.new(values: %i[value1 value2])

class MyModel
  graphql do |c|
    c.attribute(:field).permit_input(:some_attribute, type: 'MyEnum')
  end
end

It would be cool if that can be done on controller attributes too.

Would be good to have nested examples for queries

I just discovered this gem through rubygems. I really like the simplicity and familiarity of using this for rails developers. :)
Good job! 👍

I'm interested to see how I can use this to get nested queries like this:

{
  users(first:10) {
    email
    groups(first:10) {
      title
      description
      members(first:5) {
        email
      }
    }
  }
}

Is this automatically available based on ID type (for example from User has_many :groups, through: :group_memberships)?

Thanks!

Allow to add suffix instead of prefix for custom resource routes

Here is the problem:

resources :messages do
  query :info
end

this generates field infoMessages. It would be nice to add info part to the end of field, so it would be messagesInfo instead. My idea is to have something like this:

resources :messages do
  query :info, suffix: true
end

Add serializers/decorators

Although it's possible to decorate record in calendar action in most cases, but there are no easy way how to do this when return type is Connection (paginated)

graphql_rails crashes when resource route is defined in camelcase

this one crashes:

class ItemsController < GraphqlRails::Controller
  action(:some_action)
end

GraphqlRails::Router.draw do
  resources :items do
    mutation(:someAction)
  end

this one crashes too:

class ItemsController < GraphqlRails::Controller
  action(:someAction)
end

GraphqlRails::Router.draw do
  resources :items do
    mutation(:some_action)
  end

this one is ok:

class ItemsController < GraphqlRails::Controller
  action(:some_action)
end

GraphqlRails::Router.draw do
  resources :items do
    mutation(:some_action)
  end

this one is ok too:

class ItemsController < GraphqlRails::Controller
  action(:someAction)
end

GraphqlRails::Router.draw do
  resources :items do
    mutation(:someAction)
  end

GraphqlRails should allow to use mixed names too or at least it should crash with clear message

Schema dump is not working as expected

The issue occured when I was trying to migrate from graphql and start using graphql_rails gem.
graphql gem generates schema name from Rails app name with "#{Rails.application.class.parent_name}Schema". (Even though you can pass an option as a schema name but it is not mentioned in documentation for some reason)
And graphql_rails builds schema name from options passed to schema:dump task or takes an option and [['graphql', name.presence, 'schema'].compact.join('_')](https://github.com/samesystem/graphql_rails/blob/master/lib/graphql_rails/tasks/schema.rake#L23)
So when I try to dump a schema when using graphql_rails it doesn't find my schema file and throws an error

RET bundle exec rake graphql_rails:schema:dump machik
rake aborted!
NoMethodError: undefined method `to_definition' for nil:NilClass
/home/jokubas/work/bastardos/rails/graphql_rails/lib/graphql_rails/tasks/schema.rake:17:in `call'
/home/jokubas/work/bastardos/rails/graphql_rails/lib/graphql_rails/tasks/schema.rake:9:in `call'

We need to unify graphql and graphql_rails schema name generating

Required inner list type works incorrectly

Types::ExampleType.to_non_null_type.to_list_type.to_non_null_type will end up looking as [ExampleType!] in schema. This is not how it is supposed to work. A quick workaround is to add to_graphql which would then build correct graphql definition type, but that's a workaround rather than a good solution.

input object is not converted to hash when input is on object level

class ItemsFilterInput
  includes GraphqlRails::Model

  graphql.input do |c|
    c.attribute(:item_name).type(:string)
  end
end

cłass MyModelWithInputs
  includes GraphqlRails::Model

  graphql do |c|
    c.attribute(:items).permit(filter: 'ItemsFilterInput')
  end

  def items(filter: nil)
    puts filter
  end
end

the problem with this code is that argument filter in MyModelWithInputs#items method is graphql object and not hash. This is not right - it should be Hash

Make router global

Ir would be nice to have global router so we do not need to assign it's value to some constant

ActionDispatch::Http::ParameterFilter deprecation warning

DEPRECATION WARNING: ActionDispatch::Http::ParameterFilter is deprecated and will be removed from Rails 6.1. Use ActiveSupport::ParameterFilter instead. (called from parameter_filter_class at /path/graphql_rails/lib/graphql_rails/controller/log_controller_action.rb:67)

Make default actions even more strict

It would be great to have standardized crud actions, like:

  • all input params will be kept under "input" key
  • index response is always paginated
  • show action always permits Id

Allow to have array enum type

Currently we can only define enum as non-list type via:

attribute(...).enum(%i[val1 val2])

But there is no way how to define enum list. Not sure for best DSL yet, but I'm considering something like that

attribute(...)
  .enum(%i[val1 val2], list: true, required_inner: true, required_list: true)

or

attribute(...).enum_list(%i[val1 val2], required_inner: true, required_list: true)

Also we need to make sure that it will play nice with #152 too

Simplified way to define enum types

it would be nice to define enum types directly in model, something like this:

class User
  graphql do |c|
    c.attribute :type, enum: %i[ADMIN MANAGER GUEST]
  end
end

Attribute formatting is not changeable from model definition

Currently, we're not able to pass any options for attribute defined inside GraphqlRails::Model, e.g:
c.attribute :name, camelize: false is not an option. However, that's possible from the controller definition.

It would be extremely helpful if this was also available in models since that's how the majority of definitions are done.

add support for `:json`, and `:date` datatypes

graphql-ruby already has support for GraphQL::Types::ISO8601DateTime and GraphQL::Types::JSON (even tough they are not default GraphQL types). I would suggest to include them in graphql_rails, so instead of

action(:create).permit(date: GraphQL::Types::ISO8601DateTime, data: GraphQL::Types::JSON)

we could use

action(:create).permit(date: :date_time, data: :json)

Empty enums raises non human friendly error

It does not make sense to have empty enum, but in case it happens, graphql_rails raises exeption which sounds like this: GraphQL::RequiredImplementationMissingError: Anonymous class should declare a graphql_name`. What is should raise is something like this:

GraphRails::InvalidEnumError enum <ENUM_NAME> defined in <CLASS_NAME> must have at least one option`

use new graphql-ruby DSL

graphql-ruby moved to new, class-based DSL witch makes working with it easier. We already use this DSL in some places, but in other places we are still using old style DSL. Those DSL's are not very compatible so it slows down development of a new features

Add generators

It would be nice to type rake graphql_rails:install which generates:

  • router (app/graphql/graphql_router.rb)
  • Ruby on Rails controller which triggers GraphqlRails (app/controllers/graphql_controller.rb)
  • default graphql controller (app/controllers/graphql/application_graphql_controller.rb). Bonus - make it mountable
  • it would be nice to enable integrations if those are found when generator is triggered

add sentry/kibana loggers

With GraphqlRails it's possible to log actions to kibana, sentry and other monitoring tools. We need to add those loggers as part of GraphqlRails gem (at least for now)

Inference based on conventions

Hello. I can't quite tell if this library will infer any controller actions and model based on conventions or if I am supposed to always specify it.

resources :users

We could infer:

model('User')
action(:show).returns_single
action(:index).returns_list
action(:create).returns_single
action(:update).returns_single
action(:destroy).returns_single

I don't see these lines in the example, but they appear to be required?

Always underscore params

It's easy to do it manually, but in most cases it would be nice to do so by default. Manual way:

class ApplicationGraphqlController < GraphqlRails::Controller
  protected

  def params
    super.deep_transform_keys(&:underscore).with_indifferent_access
  end
end

Autogenerate input types too

It should be possible to generate input types too. One of the options might be to generate nested permit params on controller level

class UsersController < GrapqhlRails::Controller
  action(:create)
    .permit(input: [:name!, :surname!, { friends_count: :int }])

  action(:update)
    .permit(input: [:name!, :surname!, { friends_count: :int }])
    .permit(id!)
end

this would generate two input types based on controller UserCreateInputType and UserUpdateInputType

Install generator not found

Hi 👋 this project looks awesome. I want to test it out, but unfortunately I'm having an issue after install. The generator does not appear to be available:

Could not find generator 'graphql_rails:install'. Maybe you meant 'graphql:install', 'responders:install' or 'pundit:install'
Run `rails generate --help` for more options.

I confirmed that it is not in my list when I do rails generate.

Bundle confirms the gem is installed:

Using graphql_rails 0.8.0

This is a Rails 5.2 app.

add groups to router

It would be nice to define multiple routers as groups:

Router.draw do
  resources :shared_users

  group :web do
    resources :web_users
  end

  group :mobile do
    resources :mobile_users
  end
end

allow to pass decorator options when decorating object

It would be nice to if we could pass additional arguments to decorator when doing decorate(object, with: Decorator). Improved dsl could look like this:

decorate(object, arg1, arg2).with(Decorator)

In simple case this could be converted to Decorator.new(object, arg1, arg2)

Improve errors system

It's possible to customize errors for each project by extending GraphqlRails::Error class, but it's not very intuitive and it's not documented. We need to do both: improve errors API and document it

Support model types

Problem

It would be great if this DSL supported model types, and not only scalar ones.

Example case

  graphql do |c|
    c.attribute :images, type: '[Image]'
  end

Current behavior

It returns an error message claiming that type [Image] does not exist.

Expected behavior

It should allow referencing other models as well, so we can chain types as deeply as we want.

add group to models

With #116 it's now possible to have multiple graphql schemas using groups method. It would be nice to have such thing on model level, something like this:

class User
  include GraphqlRails::Model
 
  graphql do |c|
    c.attribute :secret, group: :internal
    
    c.group(:testing) do |testing|
      testing.attribute :testing_id
    end
  end
end

Add more options for permitted fields

Using input types in controllers is very limited. It's hard to add required input, also there is no way to add description for each input. I suggest to add permit_input method which allows to add only one field at the time, but allows to pass various options. Here is an example:

class User
  graphql.input(:create_user) do |c|
  end
end

class UsersController < GraphqlRails:Controller
  action(:create)
    .permit_input(:input, type: `User!', input_for: 'create_user', description: '...')
end

another options would be to extend existing permit method and allow to accept hash, like this:

class UsersController < GraphqlRails:Controller
  action(:create)
    .permit(input: { type: `User!', input_for: 'create_user', description: '...' })
end

at the moment i can think of few options:

  • input_for which uses named input
  • type which can use string instead of class (to avoid unnecessary class load)
  • description - small contribute to documentation

Validate params in tests

Currently when we test graphql controller we do not validate input type. So test will pass with nil attributes even if schema says that it's not allowed to provide nil value. We need to find a way how to take in to account permitted params in tests

Deletion of ExampleUsersController unexpected error

Deletion of ExampleUsersController and resources :example_users route causes strange error

Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.0ms | Allocations: 2230)                                  
RuntimeError (Unexpected parent_type: ):                                                                                               
app/controllers/graphql_controller.rb:7:in `execute'

Other controllers are untouched.
Except that I have new graphql controller which is app/controllers/sessions_controller.rb

Video how to reproduce

The same problem also occurs if all graphql controllers are deleted and graphql_router is empty

GraphqlRouter = GraphqlRails::Router.draw do
  scope module: :graphql do
  end
end

Fail in Rails 6.1

After upgrade to Rails 6.1 we getting error :

 NameError:
rails_1     |        uninitialized constant ActionDispatch::Http::ParameterFilter
rails_1     |        Did you mean?  ActionDispatch::Http::Parameters
rails_1     |      # /data/bundles/base/ruby/2.7.0/gems/graphql_rails-1.1.0/lib/graphql_rails/controller/log_controller_action.rb:54:in `filtered_params'
rails_1     |      # /data/bundles/base/ruby/2.7.0/gems/graphql_rails-1.1.0/lib/graphql_rails/controller/log_controller_action.rb:40:in `default_payload
....

which comes from https://github.com/samesystem/graphql_rails/blob/master/lib/graphql_rails/controller/log_controller_action.rb#L54 .

I found this from previous Rails

ParameterFilter inherits from ActionDispatch::Http::ParameterFilter which will be removed from Rails 6.1:

DEPRECATION WARNING: ActionDispatch::Http::ParameterFilter is deprecated and will be removed from Rails 6.1. Use ActiveSupport::ParameterFilter instead.

Do not convert cursor to base64

It's opinionated, but this gem is all about it. By default when we returning paginated response we return cursor which is index_number converted to base64. I think this adds unnecessary complexity. We should stop converting it into base64. Here is the way:

module Graphql
  module PlainCursorEncoder
    def self.encode(plain, _nonce)
      plain
    end

    def self.decode(plain, _nonce)
      plain
    end
  end
end
GraphqlRails::Router.draw do
  cursor_encoder(Graphql::PlainCursorEncoder)
  # ...
end

now we need to find a way how to make this default

Circular dependecy leads to infinity loop and Stack level too deep error

Since we can have two objects that obtain each other, we can in theory end up with very deep query depth, as you could do as seen below, where you first request posts, which includes users, which again includes posts.

It makes sense to be able to do this though, as it minimises how many requests are needed, but it can also be very expensive if not handled correctly.

Since graphQL is only used internally, we’ll initially support these sort of circular dependencies.

Issue with dynamic type definition

Lets assume I have two models Foo and Bar

class Foo < ApplicationRecord
  ...
  has_many :bars
  ...

  graphql do |c|
    c.attribute :id, type: :id
    ...
    c.attribute :bars, type: Bar.graphql.graphql_type.to_list_type
  end
end
class Bar < ApplicationRecord
  ...
  has_many :foos
  ...

  graphql do |c|
    c.attribute :id, type: :id
    ...
    c.attribute :foos, type: Foo.graphql.graphql_type.to_list_type
  end
end

I would expect bars to appear as Foo type's attributes, but it doesn't, unless I remove c.attribute :foos, type: Foo.graphql.graphql_type.to_list_type from Bar's graphql attributes definition, or change dynamic type definition Bar.graphql.graphql_type.to_list_type to '[Bar]'

Incomplete schema in multithreaded environment.

When working with multithreaded server, schema generation seems to be generated faulty in one of the async requests.

Reproduction steps

  • Start up new server (so the schema is not set yet)
  • Fire multiple async graphql requests. If your schema is big enough (takes some time to be generated) you'll experience one of the requests to fail with messages from graphql gem indicating that some types are without fields.
    Field Query.users's return type is invalid: User must define at least 1 field. 0 defined.

As this is tricky to reproduce, you can do it from rails console too.
5.times { Thread.new { ::GraphqlRouter.graphql_schema } }. One of the threads will fail and throw an error.

Allow to define custom model in each controller

It would be nice if we could provide model for each controller. In most cases I endup writting something like this:

class MyController < Graphql::Controller
  action(:index).returns('[MyCustomClass!]!')
  action(:show).returns('MyCustomClass!')
  action(:update).returns('MyCustomClass!')
end

I think we could add model config option for controller, like this:

class MyController < Graphql::Controller
  model 'MyCustomClass'

  action(:index)
  action(:show)
  action(:update)
end

max_page_size is ignored

It's possible to define custom max_page_size in controller action like this:

class MyController < GraphqlRails::Controller
  action(:index).paginated(max_page_size: 999)
end

sadly this does not work as promised - it still returns 200 records max

Add namespace to routes

Currently there is no namespace in routes, but it would be nice to have one. For example

GraphqlRails::Router.draw do
  namespace :admin do
    resources :users
  end
end

I would expect that this will generate something like this:

query {
  admin {
    users
    user(...)
  }
}

mutation {
  admin {
    createUser(...)
    updateUser(...)
    destroyUser(...)
  }
}

Allow to have underscore model field names

All model fields are camelized and there is no way how to change this. It would be nice to have option to do so, something like this:

class User
  include GraphqlRails::Model

  graphql do |c|
    c.attribute :underscored_name, type: :string!, name_format: :original
  end
end

Release automation

With automated CI, we currently lack automated/semi-automated release flow.
There's no obvious release rules or guidelines, so this needs to be discussed first.

Total field on Connection is not implemented

When trying to access the total field on connection, it returns error.

My query

{
  brand(id: "1") {
    id
    products(first: 10) {
      nodes {
        id
      }
      total
    }
  }
}

It si configured using

...
c.attribute(:products, type: '[Catalog::Product]').paginated
...

And it returns

Failed to implement ProductConnection.total, tried:
          - `#<Class:0x00007fed248f6c40>#total`, which did not exist
          - `GraphQL::Relay::RelationConnection#total`, which did not exist
          - Looking up hash key `:total` or `"total"` on `#<GraphQL::Relay::RelationConnection:0x00007fed2803d7d0>`, but it wasn't a Hash
          To implement this field, define one of the methods above (and check for typos)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.