Skip to content

Commit

Permalink
Rescue from specific errors (#1113)
Browse files Browse the repository at this point in the history
* rescue from specific errors

* fix codeclimate issues

* add some changes to the rescues

* add i18n prefix to MissingTranslationError

* convert url to uri

* rescue from Google::Apis::ClientError

* reduce lines to 10

* add google api key and secret

* stub google errors

* add spec for server error

* stub calendar uploader

* add specs for update access token

* use double quotes

* fix typos and take the blame from Google :lol:
  • Loading branch information
hmasila authored Oct 13, 2018
1 parent c297be5 commit e02bd00
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 44 deletions.
4 changes: 1 addition & 3 deletions app/controllers/categories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,11 @@ def quick_create
private

# Use callbacks to share common setup or constraints between actions.
# rubocop:disable RescueStandardError
def set_category
@category = Category.friendly.find(params[:id])
rescue
rescue ActiveRecord::RecordNotFound
redirect_to_path(categories_path)
end
# rubocop:enable RescueStandardError

def category_params
params.require(:category).permit(:name, :description)
Expand Down
4 changes: 1 addition & 3 deletions app/controllers/medications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@ def redirect_to_medication(medication)
end

# Use callbacks to share common setup or constraints between actions.
# rubocop:disable RescueStandardError
def set_medication
@medication = Medication.friendly.find(params[:id])
rescue
rescue ActiveRecord::RecordNotFound
redirect_to_path(medications_path)
end
# rubocop:enable RescueStandardError

def medication_params
params.require(:medication).permit(
Expand Down
4 changes: 1 addition & 3 deletions app/controllers/moments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,11 @@ def destroy

private

# rubocop:disable RescueStandardError
def set_moment
@moment = Moment.friendly.find(params[:id])
rescue
rescue ActiveRecord::RecordNotFound
redirect_to_path(moments_path)
end
# rubocop:enable RescueStandardError

def moment_params
params.require(:moment).permit(
Expand Down
4 changes: 1 addition & 3 deletions app/controllers/moods_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,11 @@ def quick_create
private

# Use callbacks to share common setup or constraints between actions.
# rubocop:disable RescueStandardError
def set_mood
@mood = Mood.friendly.find(params[:id])
rescue
rescue ActiveRecord::RecordNotFound
redirect_to_path(moods_path)
end
# rubocop:enable RescueStandardError

def mood_params
params.require(:mood).permit(:name, :description)
Expand Down
4 changes: 1 addition & 3 deletions app/controllers/strategies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,11 @@ def render_errors(strategy)
end

# Use callbacks to share common setup or constraints between actions.
# rubocop:disable RescueStandardError
def set_strategy
@strategy = Strategy.friendly.find(params[:id])
rescue
rescue ActiveRecord::RecordNotFound
redirect_to_path(strategies_path)
end
# rubocop:enable RescueStandardError

def strategy_params
params.require(:strategy).permit(
Expand Down
4 changes: 1 addition & 3 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ def html_options
{ class: 'htmlOptions' }
end

# rubocop:disable RescueStandardError
def i18n_set?(key)
I18n.t key, raise: true
rescue
rescue I18n::MissingTranslationData
false
end
# rubocop:enable RescueStandardError

def active?(link_path, environment = {})
current_page?(link_path) ||
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/date_time_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module DateTimeHelper
def format_date(date_str)
begin
date_formatted = date_str.to_date
rescue StandardError
rescue ArgumentError, NoMethodError
date_formatted = Date.strptime(date_str, '%m/%d/%Y')
end
I18n.l(date_formatted, format: :long)
Expand Down
17 changes: 6 additions & 11 deletions app/helpers/medication_refill_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
module MedicationRefillHelper
include CalendarHelper
# Save refill date to Google calendar
# rubocop:disable RescueStandardError
def save_refill_to_google_calendar(medication)
return true unless current_user.google_oauth2_enabled? &&
new_cal_refill_reminder_needed?(medication)

begin
args = calendar_uploader_params(medication)
CalendarUploader.new(args).upload_event
rescue
return_to_sign_in
else
true
end
args = calendar_uploader_params(medication)
CalendarUploader.new(args).upload_event
rescue Google::Apis::ClientError
return_to_sign_in
rescue Google::Apis::ServerError
redirect_to_medication(medication)
end
# rubocop:enable RescueStandardError

def calendar_uploader_params(medication)
{ summary: "Refill for #{medication.name}",
Expand Down
15 changes: 8 additions & 7 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class User < ApplicationRecord
pending_from_ally: 2
}.freeze

OAUTH_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'

# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :uid,
Expand Down Expand Up @@ -141,14 +143,13 @@ def set_defaults
@meeting_notify.nil? && @comment_notify = true
end

OAUTH_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'

def update_access_token
refresh_token_params = { 'refresh_token' => refresh_token,
'client_id' => nil,
'client_secret' => nil,
'grant_type' => 'refresh_token' }
response = Net::HTTP.post_form(User::OAUTH_TOKEN_URL, refresh_token_params)
params = { 'refresh_token' => refresh_token,
'client_id' => ENV['GOOGLE_CLIENT_ID'],
'client_secret' => ENV['GOOGLE_CLIENT_SECRET'],
'grant_type' => 'refresh_token' }

response = Net::HTTP.post_form(URI.parse(OAUTH_TOKEN_URL), params)
decoded_response = JSON.parse(response.body)
new_expiration_time = Time.zone.now + decoded_response['expires_in']
new_access_token = decoded_response['access_token']
Expand Down
2 changes: 1 addition & 1 deletion spec/factories/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
name { 'Fancy Medication Name' }
dosage { 10 }
dosage_unit { 'tablet' }
refill { 0o1 / 0o1 / 2020 }
refill { "01/01/2020" }
strength { 12 }
strength_unit { 'mg' }
total { '30' }
Expand Down
7 changes: 6 additions & 1 deletion spec/features/user_creates_a_medication_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
end

context 'with Google Calendar reminders checked' do
before do
CalendarUploader.stub_chain(:new, :upload_event).and_return(true)
end

it 'activates reminders' do
find('#medication_refill_reminder').click
find('#medication_take_medication_reminder').click
Expand All @@ -69,7 +73,8 @@

context 'when uploader raises an error' do
before do
CalendarUploader.stub_chain(:new, :upload_event).and_raise(StandardError)
CalendarUploader.stub_chain(:new, :upload_event)
.and_raise(Google::Apis::ClientError.new('error'))
end

it 'redirects to sign in' do
Expand Down
59 changes: 56 additions & 3 deletions spec/helpers/medication_refill_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
let(:user) { FactoryBot.create(:user1) }
let(:medication) { FactoryBot.create(:medication, user_id: user.id) }
let(:exception_text) { 'RESCUE INVOKED' }
let(:client_error_exception_text) { 'REDIRECTED TO SIGN IN BECAUSE RESCUE WAS INVOKED' }
let(:server_error_exception_text) { 'REDIRECTED TO MEDICATION_PATH BECAUSE RESCUE WAS INVOKED' }

before do
allow_any_instance_of(helper.class).to receive(:return_to_sign_in).and_return(exception_text)
allow_any_instance_of(helper.class).to receive(:return_to_sign_in).and_return(client_error_exception_text)
allow_any_instance_of(helper.class).to receive(:redirect_to_medication).and_return(server_error_exception_text)
allow_any_instance_of(User).to receive(:google_access_token).and_return("token")
sign_in user
end

Expand All @@ -22,11 +26,60 @@
allow_any_instance_of(helper.class).to receive(:new_cal_refill_reminder_needed?).and_return(true)
end

it { expect(helper.save_refill_to_google_calendar(medication)).to eq(exception_text) }
context 'when upload event fails with client error' do
before do
allow_any_instance_of(CalendarUploader).to receive(:upload_event)
.and_raise(Google::Apis::ClientError.new(exception_text))
end

it 'redirects to sign_in' do
expect(helper.save_refill_to_google_calendar(medication)).to eq(client_error_exception_text)
end
end

context 'when upload event fails with server error' do
before do
allow_any_instance_of(CalendarUploader).to receive(:upload_event)
.and_raise(Google::Apis::ServerError.new(exception_text))
end

it 'redirects to medication path' do
expect(helper.save_refill_to_google_calendar(medication)).to eq(server_error_exception_text)
end
end

context 'when upload event passes' do
before do
allow_any_instance_of(CalendarUploader).to receive(:upload_event).and_return(true)
end

it { expect(helper.save_refill_to_google_calendar(medication)).to eq(true) }
end
end

context 'when the user has not google oauth2 enabled and/or they no need a new refill reminder' do
context "when the user has not google oauth2 enabled and/or they don't need a new refill reminder" do
it { expect(helper.save_refill_to_google_calendar(medication)).to eq(true) }
end
end

describe '#calendar_uploader_params' do
let(:user) { FactoryBot.create(:user1) }
let(:medication) { FactoryBot.create(:medication, user_id: user.id) }
let(:exception_text) { 'RESCUE INVOKED' }

before do
allow_any_instance_of(User).to receive(:google_access_token).and_return("token")
sign_in user
end

it 'returns a hash' do
result = {
summary: "Refill for #{medication.name}",
date: medication.refill,
access_token: user.google_access_token,
email: user.email
}
expect(helper.calendar_uploader_params(medication)).to eq(result)
end
end
end
53 changes: 51 additions & 2 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# Table name: users
#
# id :integer not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# email :string default(''), not null
# encrypted_password :string default(''), not null
# reset_password_token :string
# reset_password_sent_at :datetime
# remember_created_at :datetime
Expand Down Expand Up @@ -150,4 +150,53 @@
expect(result).to eq [group_only_ally_belongs_to]
end
end

describe '#update_access_token' do
let!(:user) do
User.create(name: 'some name',
email: '[email protected]',
password: 'asdfasdf',
token: 'some token')
end

request = {
'refresh_token' => nil,
'client_id' => ENV['GOOGLE_CLIENT_ID'],
'client_secret' => ENV['GOOGLE_CLIENT_SECRET'],
'grant_type' => 'refresh_token'
}

context 'when request is successful' do
before do
response = {
'access_token': 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3',
'token_type': 'bearer',
'expires_in': 3600,
'refresh_token': 'IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk',
'scope': 'create'
}.to_json

Net::HTTP.stub(:post_form).with(URI.parse(User::OAUTH_TOKEN_URL), request) { double(body: response) }
end

it 'returns a new access token' do
expect(user.update_access_token).to eq('MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3')
end
end

context 'when request is unsuccessful' do
before do
response = {
'error': 'invalid request',
'error_description': 'Could not determine client ID from request.'
}.to_json

Net::HTTP.stub(:post_form).with(URI.parse(User::OAUTH_TOKEN_URL), request) { double(body: response) }
end

it 'returns a new access token' do
expect { user.update_access_token }.to raise_error(NoMethodError)
end
end
end
end

0 comments on commit e02bd00

Please sign in to comment.