Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
/*.sqlite3
/spec/dummy/db/development.sqlite3
/spec/dummy/db/test.sqlite3
/*.gem
5 changes: 5 additions & 0 deletions app/graphql/graphql_devise/mutations/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def client
controller.token.client if controller.token.present?
end
end

def set_auth_headers(resource)
auth_headers = resource.create_new_auth_token
response.headers.merge!(auth_headers)
end
end
end
end
5 changes: 0 additions & 5 deletions app/graphql/graphql_devise/mutations/login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ def resolve(email:, password:)

private

def set_auth_headers(resource)
auth_headers = resource.create_new_auth_token
response.headers.merge!(auth_headers)
end

def invalid_for_authentication?(resource, password)
valid_password = resource.valid_password?(password)

Expand Down
61 changes: 61 additions & 0 deletions app/graphql/graphql_devise/mutations/sign_up.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module GraphqlDevise
module Mutations
class SignUp < Base
argument :email, String, required: true
argument :password, String, required: true
argument :password_confirmation, String, required: true
argument :confirm_success_url, String, required: false
argument :config_name, String, required: false

def resolve(confirm_success_url: nil, config_name: nil, **attrs)
resource = resource_class.new(provider: provider, **attrs)

if resource.present?
resource.skip_confirmation_notification! if resource.respond_to?(:skip_confirmation_notification!)

if resource.save
yield resource if block_given?

if confirmable_enabled?(resource) && !resource.confirmed?
# user will require email authentication
resource.send_confirmation_instructions(
client_config: config_name,
redirect_url: confirm_success_url
)
end

set_auth_headers(resource) if active_for_authentication?(resource)

{ authenticable: resource }
else
clean_up_passwords(resource)
raise_user_error_list(
I18n.t('graphql_devise.registration_failed'),
errors: resource.errors.full_messages
)
end
else
raise_user_error(I18n.t('graphql_devise.resource_build_failed'))
end
end

protected

def confirmable_enabled?(resource)
resource.respond_to?(:confirmed_at)
end

def active_for_authentication?(resource)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This one seems redundant when you can call that directly on resource

resource.active_for_authentication?
end

def provider
:email
end

def clean_up_passwords(resource)
controller.send(:clean_up_passwords, resource)
end
end
end
end
15 changes: 15 additions & 0 deletions app/helpers/graphql_devise/mailer_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module GraphqlDevise
module MailerHelper
def confirmation_query(token:, config:, redirect_url:)
raw = <<-GRAPHQL
confirmAccount($token:ID!,$clientConfig:String,redirect:String!){
userConfirmAccount(token:$token,clientConfig:$clientConfig,redirect:$redirect
){
success,errors
}
}&variables={token:"#{token}",clientConfig:"#{config}",redirect:"#{redirect_url}"}
GRAPHQL
ERB::Util.url_encode(raw.gsub("\n", '').gsub(' ', ''))
end
end
end
5 changes: 5 additions & 0 deletions app/views/devise/mailer/confirmation_instructions.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<p><%= t(:welcome).capitalize + ' ' + @email %>!</p>

<p><%= t '.confirm_link_msg' %> </p>

<p><%= link_to t('.confirm_account_link'), api_v1_graphql_auth_url, query: confirmation_query(token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url']).html_safe %></p>
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
en:
graphql_devise:
registration_failed: "User couldn't be registered"
resource_build_failed: "Resource couldn't be built, execution stopped."
not_authenticated: "User is not logged in."
user_not_found: "User was not found or was not logged in."
passwords:
Expand Down
1 change: 1 addition & 0 deletions graphql_devise.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'factory_bot'
spec.add_development_dependency 'faker'
spec.add_development_dependency 'pry'
spec.add_development_dependency 'pry-byebug'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec-rails'
spec.add_development_dependency 'rubocop', '0.68.1'
Expand Down
4 changes: 2 additions & 2 deletions lib/graphql_devise.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'rails'
require 'graphql_devise/engine'
require 'devise_token_auth'
require 'graphql'
require 'devise_token_auth'
require 'graphql_devise/engine'
require 'graphql_devise/version'
require 'graphql_devise/error_codes'
require 'graphql_devise/user_error'
Expand Down
5 changes: 3 additions & 2 deletions lib/graphql_devise/rails/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# frozen_string_literal: true

module ActionDispatch::Routing
class Mapper
def mount_graphql_devise_for(resource, opts = {})
Expand All @@ -21,6 +19,7 @@ def mount_graphql_devise_for(resource, opts = {})
default_mutations = {
login: GraphqlDevise::Mutations::Login,
logout: GraphqlDevise::Mutations::Logout,
sign_up: GraphqlDevise::Mutations::SignUp,
update_password: GraphqlDevise::Mutations::UpdatePassword
}.freeze

Expand All @@ -38,6 +37,8 @@ def mount_graphql_devise_for(resource, opts = {})
GraphqlDevise::Types::MutationType.field("#{mapping_name}_#{action}", mutation: used_mutation)
end

Devise.mailer.send(:add_template_helper, GraphqlDevise::MailerHelper)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There might be an engine way not to need this, but is the method not public?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Let me take a look, but it is private doc


devise_scope mapping_name.to_sym do
post "#{path}/graphql_auth", to: 'graphql_devise/graphql#auth'
end
Expand Down
12 changes: 12 additions & 0 deletions spec/dummy/app/graphql/mutations/sign_up.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Mutations
class SignUp < GraphqlDevise::Mutations::SignUp
argument :name, String, required: false

field :user, Types::UserType, null: true

def resolve(email:, **attrs)
original_payload = super
original_payload.merge(user: original_payload[:authenticable])
end
end
end
1 change: 1 addition & 0 deletions spec/dummy/config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
Expand Down
3 changes: 2 additions & 1 deletion spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Rails.application.routes.draw do
mount_graphql_devise_for 'User', at: 'api/v1', mutations: {
login: Mutations::Login
login: Mutations::Login,
sign_up: Mutations::SignUp
}
end
71 changes: 71 additions & 0 deletions spec/requests/mutations/sign_up_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'rails_helper'

RSpec.describe '' do
include_context 'with graphql query request'

let(:name) { Faker::Name.name }
let(:password) { Faker::Internet.password }
let(:email) { Faker::Internet.email }
let(:redirect) { Faker::Internet.url }
let(:query) do
<<-GRAPHQL
mutation {
userSignUp(
email: "#{email}"
name: "#{name}"
password: "#{password}"
passwordConfirmation: "#{password}"
confirmSuccessUrl: "#{redirect}"
) {
user {
email
name
}
}
}
GRAPHQL
end

context 'when params are correct' do
it 'creates a new resource that requires confirmation' do
expect { post_request }.to(
change(User, :count).by(1)
.and(change(ActionMailer::Base.deliveries, :count).by(1))
)

user = User.last
expect(user).not_to be_active_for_authentication
expect(user.confirmed_at).to be_nil
expect(user.valid_password?(password)).to be_truthy
expect(json_response[:data][:userSignUp]).to include(
user: {
email: email,
name: name
}
)

email = ActionMailer::Base.deliveries.last
query = ERB::Util.url_encode("confirmAccount($token:ID!,$clientConfig:String,redirect:String!){userConfirmAccount(token:$token,clientConfig:$clientConfig,redirect:$redirect){success,errors}}&variables={token:\"#{user.confirmation_token}\",clientConfig:\"default\",redirect:\"#{redirect}\"}").html_safe
expect(email.body.encoded).to match(/query="#{query}"/)
end
end

context 'when required params are missing' do
let(:email) { '' }

it 'does *NOT* create resource a resource nor send an email' do
expect { post_request }.to(
not_change(User, :count)
.and(not_change(ActionMailer::Base.deliveries, :count))
)

expect(json_response[:data][:userSignUp]).to be_nil
expect(json_response[:errors]).to containing_exactly(
hash_including(
message: "User couldn't be registered",
extensions: { code: 'USER_ERROR', detailed_errors: ["Email can't be blank"] }
)
)
end
end
end
1 change: 1 addition & 0 deletions spec/support/matchers/not_change_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RSpec::Matchers.define_negated_matcher :not_change, :change