Skip to content

Commit

Permalink
Generator can mount operations in main app schema
Browse files Browse the repository at this point in the history
  • Loading branch information
mcelicalderon committed Jun 11, 2020
1 parent 03f2c3c commit 687ebaf
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 25 deletions.
60 changes: 41 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ GraphQL interface on top of the [Devise Token Auth](https://github.com/lynndylan
* [Table of Contents](#table-of-contents)
* [Introduction](#introduction)
* [Installation](#installation)
* [Running the Generator](#running-the-generator)
* [Mounting the Schema in a Separate Route](#mounting-the-schema-in-a-separate-route)
* [Mounting Operations in Your Own Schema](#mounting-operations-in-your-own-schema)
* [Important](#important)
* [Usage](#usage)
* [Mounting Auth Schema on a Separate Route](#mounting-auth-schema-on-a-separate-route)
Expand All @@ -30,12 +33,13 @@ GraphQL interface on top of the [Devise Token Auth](https://github.com/lynndylan
* [More Configuration Options](#more-configuration-options)
* [Devise Token Auth Initializer](#devise-token-auth-initializer)
* [Devise Initializer](#devise-initializer)
* [GraphQL Interpreter](#graphql-interpreter)
* [Using Alongside Standard Devise](#using-alongside-standard-devise)
* [Future Work](#future-work)
* [Contributing](#contributing)
* [License](#license)

<!-- Added by: mcelicalderon, at: Wed Jun 10 00:48:00 -05 2020 -->
<!-- Added by: mcelicalderon, at: Wed Jun 10 22:10:26 -05 2020 -->

<!--te-->

Expand All @@ -59,20 +63,23 @@ gem 'graphql_devise'
```

And then execute:
```bash
$ bundle
```

$ bundle

Next, you need to run the generator:

$ bundle exec rails generate graphql_devise:install

### Running the Generator
Graphql Devise generator will execute `Devise` and `Devise Token Auth`
generators for you. These will make the required changes for the gems to
work correctly. All configurations for [Devise](https://github.com/plataformatec/devise) and
[Devise Token Auth](https://github.com/lynndylanhurley/devise_token_auth) are available,
so you can read the docs there to customize your options.
Configurations are done via initializer files as usual, one per gem.

#### Mounting the Schema in a Separate Route
```bash
$ bundle exec rails generate graphql_devise:install
```

The generator accepts 2 params: `user_class` and `mount_path`. The params
will be used to mount the route in `config/routes.rb`. For instance the executing:

Expand All @@ -92,16 +99,24 @@ Will do the following:
`Admin` could be any model name you are going to be using for authentication,
and `api/auth` could be any mount path you would like to use for auth.

#### Mounting Operations in Your Own Schema
Now you can provide to the generator an option specifying
the name of your GQL schema. Doing this will skip the insertion of the mount method in the
routes file and will also add our `SchemaPlugin` to the specified schema. `user_class` param is still optional (`Admin`) in the following example.

```bash
$ bundle exec rails g graphql_devise:install Admin --mount MySchema
```

### Important
Remember this gem mounts a completely separate GraphQL schema on a separate controller in the route
provided by the `at` option in the `mount_graphql_devise_for` method in the `config/routes.rb` file by default. If no `at`
Remember that by default this gem mounts a completely separate GraphQL schema on a separate controller in the route
provided by the `at` option in the `mount_graphql_devise_for` method in the `config/routes.rb` file. If no `at`
option is provided, the route will be `/graphql_auth`.

**Starting with `v0.12.0`** you can opt-in to a new behavior where you actually load this gem's
queries and mutations into your own application's schema. If you do this, but also run the
generator, you will have to remove the generated lines from your `config/routes.rb` file.
You can actually mount a resource's auth schema in a separate route and in your app's
schema at the same time, but that's probably not a common scenario. More on this in the next section.
**Starting with `v0.12.0`** you can opt-in to load this gem's queries and mutations into your
own application's schema. You can actually mount a resource's auth schema in a separate route
and in your app's schema at the same time, but that's probably not a common scenario. More on
this in the next section.

## Usage
### Mounting Auth Schema on a Separate Route
Expand Down Expand Up @@ -141,9 +156,7 @@ options go [here](#available-mount-options)

### Mounting Operations Into Your Own Schema
Starting with `v0.12.0` you can now mount the GQL operations provided by this gem into your
app's main schema. If you used the generator, remember that the mount method might have
been included in your `config/routes.rb` file and you can remove it if you are using this
mechanism.
app's main schema.

```ruby
# app/graphql/dummy_schema.rb
Expand Down Expand Up @@ -183,7 +196,7 @@ options. This should be the same `MutationType` you provide to the `mutation` me
in your schema.
1. `resource_loaders`: This is an optional array of `GraphqlDevise::ResourceLoader` instances.
Here is where you specify the operations that you want to load into your app's schema.
If no loader is provided no operations will be added to your schema, but you will still be
If no loader is provided, no operations will be added to your schema, but you will still be
able to authenticate queries and mutations selectively. More on this in the controller
authentication [section](#authenticating-controller-actions).
1. `authenticate_default`: This is a boolean value which is `true` by default. This value
Expand All @@ -193,7 +206,8 @@ every root level field requires authentication unless specified otherwise using
authentication unless specified otherwise using the `authenticate: true` option on the field.
1. `unauthenticated_proc`: This param is optional. Here you can provide a proc that receives
one argument (field name) and is called whenever a field that requires authentication
is called without an authenticated resource.
is called without an authenticated resource. By default a `GraphQL::ExecutionError` will be
raised if authentication fails. This will provide a GQL like error message on the response.

### Available Mount Options
Both the `mount_graphql_devise_for` method and the `GraphqlDevise::ResourceLoader` class
Expand Down Expand Up @@ -454,6 +468,14 @@ In this section the most important configurations will be highlighted.

**Note:** Remember this gem adds a layer on top of Devise, so some configurations might not apply.

### GraphQL Interpreter
GraphQL-Ruby `>= 1.9.0` includes a new runtime module which you may use for your schema.
Eventually, it will become the default. You can read more about it
[here](https://graphql-ruby.org/queries/interpreter).

This gem supports schemas using the interpreter and it is recommended as it introduces several
improvements which focus mainly on performance.

### Using Alongside Standard Devise
The DeviseTokenAuth gem allows experimental use of the standard Devise gem to be configured at the same time, for more
information you can check [this answer here](https://github.com/lynndylanhurley/devise_token_auth/blob/2a32f18ccce15638a74e72f6cfde5cf15a808d3f/docs/faq.md#can-i-use-this-gem-alongside-standard-devise).
Expand Down
33 changes: 28 additions & 5 deletions lib/generators/graphql_devise/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class InstallGenerator < ::Rails::Generators::Base
argument :user_class, type: :string, default: 'User'
argument :mount_path, type: :string, default: 'auth'

class_option :mount, type: :string, default: 'separate_route'

def execute_devise_installer
generate 'devise:install'
end
Expand All @@ -29,15 +31,20 @@ def execute_dta_installer

def mount_resource_route
routes_file = 'config/routes.rb'
gem_route = "mount_graphql_devise_for '#{user_class}', at: '#{mount_path}'"
dta_route = "mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'"

if file_contains_str?(routes_file, gem_route)
if options['mount'] != 'separate_route'
gsub_file(routes_file, /^\s+#{Regexp.escape(dta_route + "\n")}/i, '')

say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
else
gsub_file(routes_file, /#{Regexp.escape(dta_route)}/i, gem_route)
gem_route = "mount_graphql_devise_for '#{user_class}', at: '#{mount_path}'"

if file_contains_str?(routes_file, gem_route)
gsub_file(routes_file, /^\s+#{Regexp.escape(dta_route + "\n")}/i, '')

say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
else
gsub_file(routes_file, /#{Regexp.escape(dta_route)}/i, gem_route)
end
end
end

Expand Down Expand Up @@ -65,6 +72,22 @@ def set_change_headers_on_each_request_false
)
end

def mount_in_schema
return if options['mount'] == 'separate_route'

inject_into_file "app/graphql/#{options['mount'].underscore}.rb", after: "< GraphQL::Schema\n" do
<<-RUBY
use GraphqlDevise::SchemaPlugin.new(
query: Types::QueryType,
mutation: Types::MutationType,
resource_loaders: [
GraphqlDevise::ResourceLoader.new('#{user_class}'),
]
)
RUBY
end
end

private

def file_contains_str?(filename, regex_str)
Expand Down
2 changes: 1 addition & 1 deletion lib/graphql_devise/mutations/sign_up.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def resolve(confirm_success_url: nil, **attrs)

{ authenticatable: resource }
else
resource.clean_up_passwords if resource.respond_to?(:clean_up_passwords)
resource.try(:clean_up_passwords)
raise_user_error_list(
I18n.t('graphql_devise.registration_failed'),
errors: resource.errors.full_messages
Expand Down
21 changes: 21 additions & 0 deletions spec/generators/graphql_devise/install_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
run_generator(args)
end

context 'when mount option is schema' do
let(:args) { ['Admin', '--mount', 'GqldDummySchema'] }

it 'mounts the SchemaPlugin' do
assert_file 'config/initializers/devise.rb'
assert_file 'config/initializers/devise_token_auth.rb', /^\s{2}#{Regexp.escape('config.change_headers_on_each_request = false')}/
assert_file 'config/locales/devise.en.yml'

assert_migration 'db/migrate/devise_token_auth_create_admins.rb'

assert_file 'app/models/admin.rb', /^\s{2}devise :.+include GraphqlDevise::Concerns::Model/m

assert_file 'app/controllers/application_controller.rb', /^\s{2}include GraphqlDevise::Concerns::SetUserByToken/

assert_file 'app/graphql/gqld_dummy_schema.rb', /\s+#{Regexp.escape("GraphqlDevise::ResourceLoader.new('Admin')")}/
end
end

context 'when passing no params to the generator' do
let(:args) { [] }

Expand Down Expand Up @@ -59,5 +77,8 @@ def create_rails_project
FileUtils.cd(File.join(destination_root, '..')) do
`rails new gqld_dummy -S -C --skip-action-mailbox --skip-action-text -T --skip-spring --skip-bundle --skip-keeps -G --skip-active-storage -J --skip-listen --skip-bootsnap`
end
FileUtils.cd(File.join(destination_root, '../gqld_dummy')) do
`rails generate graphql:install`
end
end
end

0 comments on commit 687ebaf

Please sign in to comment.