Skip to content

Commit 97d1c7e

Browse files
aaronamcelicalderon
authored andcommitted
Added ResendConfirmation GraphQL method. (#35)
* Updated documentation for the mutation and query graphql methods. * Added ResendConfirmation GraphQL method. * Fixed a misspelling * Add github templates for issues (#36) * Merged master into resend_confirmation. * Added ResendConfirmation GraphQL method. * Pulled upstream master and then rebased master on send_confirmation * Pulled upstream master and rebased master in resend_confirmation * Removed rebased code to seperate the Resend Confirmation changes. * Removed rebased code to seperate the Resend Confirmation changes. * Code review fixes and test enhancements per @00dav00's request. * Code review requests per @mcelicalderon and @00dav00 * Moved reliance for Resend Instructions from Devise to Graphql Devise. * Some RSpec styling changes per @mcelicalderon's request.
1 parent ee76d39 commit 97d1c7e

10 files changed

Lines changed: 139 additions & 152 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 0 additions & 81 deletions
This file was deleted.

.github/ISSUE_TEMPLATE/enhancement.md

Lines changed: 0 additions & 38 deletions
This file was deleted.

.github/ISSUE_TEMPLATE/question.md

Lines changed: 0 additions & 16 deletions
This file was deleted.

README.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,24 +156,23 @@ The install generator can do this for you because it executes DTA installer.
156156
See [Installation](#Installation) for details.
157157

158158
### Making Requests
159-
Here is a list of the available mutations and queries assuming your mounted model is `User`.
159+
Here is a list of the available mutations and queries assuming your mounted model
160+
is `User`.
160161

161162
#### Mutations
162-
1. `userLogin(email: String!, password: String!): UserLoginPayload`
163-
1. `userLogout: UserLogoutPayload`
164-
1. `userSignUp(email: String!, password: String!, passwordConfirmation: String!, confirmSuccessUrl: String): UserSignUpPayload`
165-
166-
The parameter `confirmSuccessUrl` is optional unless you are using the `confirmable` plugin from Devise in your `resource`'s model. If you have `confirmable` set up, you will have to provide it unless you have `config.default_confirm_success_url` set in `config/initializers/devise_token_auth.rb`.
167-
1. `userUpdatePassword(password: String!, passwordConfirmation: String!, currentPassword: String): UserUpdatePasswordPayload`
168-
169-
The parameter `currentPassword` is optional if you have `config.check_current_password_before_update` set to false (disabled by default) or the `resource` model supports the `recoverable` Devise plugin and the `resource`'s `allow_password_change` attribute is set to true.
170-
1. `userSendResetPassword(email: String!, redirectUrl: String!): UserSendReserPasswordPayload`
171-
163+
1. userLogin
164+
1. userLogout
165+
1. userSignUp
166+
1. userUpdatePassword
167+
1. userSendPasswordReset
168+
1. `userResendConfirmation(email: String!, redirectUrl: String!): UserResendConfirmationPayload`
169+
170+
The `UserResendConfirmationPayload` will return the `authenticable` resource that was sent the confirmation instructions but also has a `message: String!` that can be used to notify a user what to do after the instructions were sent to them and a `success: Boolean!` to indicate success.
172171
#### Queries
173-
1. `userConfirmAccount(confirmationToken: String!, redirectUrl: String!): User`
174-
1. `userCheckPasswordToken(resetPasswordToken: String!, redirectUrl: String): User`
172+
1. userConfirmAccount
173+
1. userCheckPasswordToken
175174

176-
The reason for having two queries is that these two are going to be accessed when clicking on
175+
The reason for having 2 queries is that these 2 are going to be accessed when clicking on
177176
the confirmation and reset password email urls. There is no limitation for making mutation
178177
requests using the `GET` method on the Rails side, but looks like there might be a limitation
179178
on the [Apollo Client](https://www.apollographql.com/docs/apollo-server/v1/requests/#get-requests).
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module GraphqlDevise
2+
module Mutations
3+
class ResendConfirmation < Base
4+
argument :email, String, required: true, prepare: ->(email, _) { email.downcase }
5+
argument :redirect_url, String, required: true
6+
7+
field :message, String, null: false
8+
9+
def resolve(email:, redirect_url:)
10+
resource = controller.find_resource(:uid, email)
11+
12+
if resource
13+
yield resource if block_given?
14+
15+
raise_user_error(I18n.t('errors.messages.already_confirmed')) if resource.confirmed?
16+
17+
resource.send_confirmation_instructions({
18+
redirect_url: redirect_url,
19+
template_path: ['graphql_devise/mailer']
20+
})
21+
22+
{
23+
authenticable: resource,
24+
message: I18n.t('graphql_devise.confirmations.send_instructions', email: email)
25+
}
26+
else
27+
raise_user_error(I18n.t('graphql_devise.confirmations.user_not_found', email: email))
28+
end
29+
end
30+
end
31+
end
32+
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p><%= t(:welcome).capitalize + ' ' + @email %>!</p>
22

3-
<p><%= t '.confirm_link_msg' %> </p>
3+
<p><%= t '.confirm_link_msg' %></p>
44

55
<p><%= link_to t('.confirm_account_link'), url_for(controller: 'graphql_devise/graphql', action: :auth, **confirmation_query(resource_name: @resource.class.to_s, redirect_url: message['redirect-url'], token: @token)) %></p>

config/locales/en.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ en:
1919
not_confirmed: "A confirmation email was sent to your account at '%{email}'. You must follow the instructions in the email before your account can be activated"
2020
confirmations:
2121
invalid_token: "Invalid confirmation token. Please try again"
22+
user_not_found: "Unable to find user with email '%{email}'."
23+
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
2224
mailer:
25+
confirmation_instructions:
26+
confirm_link_msg: "You can confirm your account email through the link below:"
27+
confirm_account_link: "Confirm my account"
2328
unlock_instructions:
2429
account_lock_msg: "Your account has been locked due to an excessive number of unsuccessful sign in attempts."
30+
errors:
31+
messages:
32+
already_confirmed: "Email was already confirmed, please try signing in"

lib/graphql_devise/rails/routes.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ def mount_graphql_devise_for(resource, opts = {})
1414
logout: GraphqlDevise::Mutations::Logout,
1515
sign_up: GraphqlDevise::Mutations::SignUp,
1616
update_password: GraphqlDevise::Mutations::UpdatePassword,
17-
send_password_reset: GraphqlDevise::Mutations::SendPasswordReset
17+
send_password_reset: GraphqlDevise::Mutations::SendPasswordReset,
18+
resend_confirmation: GraphqlDevise::Mutations::ResendConfirmation
1819
}.freeze
1920
default_queries = {
2021
confirm_account: GraphqlDevise::Resolvers::ConfirmAccount,

spec/dummy/config/locales/devise.en.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ en:
5555
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
5656
errors:
5757
messages:
58-
already_confirmed: "was already confirmed, please try signing in"
5958
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
6059
expired: "has expired, please request a new one"
6160
not_found: "not found"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
require 'rails_helper'
2+
3+
RSpec.describe 'Resend confirmation' do
4+
include_context 'with graphql query request'
5+
6+
let(:user) { create(:user, confirmed_at: nil) }
7+
let(:email) { user.email }
8+
let(:id) { user.id }
9+
let(:redirect) { Faker::Internet.url }
10+
let(:query) do
11+
<<-GRAPHQL
12+
mutation {
13+
userResendConfirmation(
14+
email:"#{email}",
15+
redirectUrl:"#{redirect}"
16+
) {
17+
message
18+
authenticable {
19+
id
20+
email
21+
}
22+
}
23+
}
24+
GRAPHQL
25+
end
26+
27+
context 'when params are correct' do
28+
it 'sends an email to the user with confirmation url and returns a success message' do
29+
expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1)
30+
expect(json_response[:data][:userResendConfirmation]).to include(
31+
authenticable: {
32+
id: id,
33+
email: email
34+
},
35+
message: "You will receive an email with instructions for how to confirm your email address in a few minutes."
36+
)
37+
38+
email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded)
39+
link = email.css('a').first
40+
confirm_link_msg_text = email.css('p')[1].inner_html
41+
confirm_account_link_text = link.inner_html
42+
43+
expect(confirm_link_msg_text).to eq("You can confirm your account email through the link below:")
44+
expect(confirm_account_link_text).to eq("Confirm my account")
45+
46+
# TODO: Move to feature spec
47+
expect do
48+
get link['href']
49+
user.reload
50+
end.to change(user, :confirmed_at).from(NilClass).to(ActiveSupport::TimeWithZone)
51+
end
52+
53+
context 'when the user has already been confirmed' do
54+
before { user.confirm }
55+
56+
it 'does *NOT* send an email and raises an error' do
57+
expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count)
58+
expect(json_response[:data][:userResendConfirmation]).to be_nil
59+
expect(json_response[:errors]).to contain_exactly(
60+
hash_including(
61+
message: "Email was already confirmed, please try signing in",
62+
extensions: { code: 'USER_ERROR' }
63+
)
64+
)
65+
end
66+
end
67+
end
68+
69+
context "when the email isn't in the system" do
70+
let(:email) { 'nothere@gmail.com' }
71+
72+
it 'does *NOT* send an email and raises an error' do
73+
expect { post_request }.to not_change(ActionMailer::Base.deliveries, :count)
74+
expect(json_response[:data][:userResendConfirmation]).to be_nil
75+
expect(json_response[:errors]).to contain_exactly(
76+
hash_including(
77+
message: "Unable to find user with email '#{email}'.",
78+
extensions: { code: 'USER_ERROR' }
79+
)
80+
)
81+
end
82+
end
83+
end

0 commit comments

Comments
 (0)