diff --git a/.allow_skipping_tests b/.allow_skipping_tests index c13b5cec83..4a0787e960 100644 --- a/.allow_skipping_tests +++ b/.allow_skipping_tests @@ -47,6 +47,8 @@ helpers/all_casa_admins/casa_orgs_helper.rb helpers/contact_types_helper.rb helpers/date_helper.rb helpers/template_helper.rb +helpers/api_base_helper.rb +helpers/request_header_helper.rb jobs/application_job.rb mailers/application_mailer.rb models/application_record.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 72f8c487a6..5d61e63af1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -30,17 +30,30 @@ def after_sign_out_path_for(resource_or_scope) protected + def handle_short_url(url_list) + hash_of_short_urls = {} + url_list.each_with_index { |val, index| + # call short io service to shorten url + # create an entry in hash if api is success + short_io_service = ShortUrlService.new + response = short_io_service.create_short_url(val) + short_url = short_io_service.short_url + hash_of_short_urls[index] = response.code == 201 || response.code == 200 ? short_url : nil + } + hash_of_short_urls + end + # volunteer/supervisor/casa_admin controller uses to send SMS # returns appropriate flash notice for SMS - def deliver_sms_to(phone_number, body_msg) - if phone_number.blank? + def deliver_sms_to(resource, body_msg) + if resource.phone_number.blank? return "blank" end acc_sid = current_user.casa_org.twilio_account_sid api_key = current_user.casa_org.twilio_api_key_sid api_secret = current_user.casa_org.twilio_api_key_secret body = body_msg - to = phone_number + to = resource.phone_number from = current_user.casa_org.twilio_phone_number twilio = TwilioService.new(api_key, api_secret, acc_sid) diff --git a/app/controllers/casa_admins_controller.rb b/app/controllers/casa_admins_controller.rb index 19279ce3e6..e1ae225bc0 100644 --- a/app/controllers/casa_admins_controller.rb +++ b/app/controllers/casa_admins_controller.rb @@ -1,4 +1,6 @@ class CasaAdminsController < ApplicationController + include SmsBodyHelper + before_action :set_admin, except: [:index, :new, :create] before_action :require_organization! after_action :verify_authorized @@ -37,12 +39,18 @@ def create service = ::CreateCasaAdminService.new(current_organization, params, current_user) @casa_admin = service.build authorize @casa_admin + sms_status = "blank" begin casa_admin = service.create! - body_msg = SMSBodyText.account_activation_msg("admin", request.base_url) - sms_status = deliver_sms_to casa_admin.phone_number, body_msg - + if !casa_admin.phone_number.blank? + raw_token = casa_admin.raw_invitation_token + base_domain = request.base_url + "/users/edit" + invitation_url = Rails.application.routes.url_helpers.accept_user_invitation_url(invitation_token: raw_token, host: request.base_url) + hash_of_short_urls = handle_short_url([invitation_url, base_domain]) + body_msg = account_activation_msg("admin", hash_of_short_urls) + sms_status = deliver_sms_to casa_admin, body_msg + end respond_to do |format| format.html { redirect_to casa_admins_path, notice: sms_acct_creation_notice("admin", sms_status) } format.json { render json: @casa_admin, status: :created } diff --git a/app/controllers/supervisors_controller.rb b/app/controllers/supervisors_controller.rb index 49a1a4f39f..87a792046e 100644 --- a/app/controllers/supervisors_controller.rb +++ b/app/controllers/supervisors_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class SupervisorsController < ApplicationController + include SmsBodyHelper + before_action :available_volunteers, only: [:edit, :update, :index] before_action :set_supervisor, only: [:edit, :update, :activate, :deactivate, :resend_invitation, :change_to_admin] before_action :all_volunteers_ever_assigned, only: [:update] @@ -31,8 +33,12 @@ def create if @supervisor.save @supervisor.invite!(current_user) - body_msg = SMSBodyText.account_activation_msg("supervisor", request.base_url) - sms_status = deliver_sms_to @supervisor.phone_number, body_msg + # call short io api here + raw_token = @supervisor.raw_invitation_token + invitation_url = Rails.application.routes.url_helpers.accept_user_invitation_url(invitation_token: raw_token, host: request.base_url) + hash_of_short_urls = @supervisor.phone_number.blank? ? {0 => nil, 1 => nil} : handle_short_url([invitation_url, request.base_url + "/users/edit"]) + body_msg = account_activation_msg("supervisor", hash_of_short_urls) + sms_status = deliver_sms_to @supervisor, body_msg redirect_to edit_supervisor_path(@supervisor), notice: sms_acct_creation_notice("supervisor", sms_status) else render new_supervisor_path diff --git a/app/controllers/volunteers_controller.rb b/app/controllers/volunteers_controller.rb index 6475d5b570..0bab1733b6 100644 --- a/app/controllers/volunteers_controller.rb +++ b/app/controllers/volunteers_controller.rb @@ -1,4 +1,6 @@ class VolunteersController < ApplicationController + include SmsBodyHelper + before_action :set_volunteer, except: %i[index new create datatable stop_impersonating] after_action :verify_authorized, except: %i[stop_impersonating] @@ -30,8 +32,12 @@ def create if @volunteer.save @volunteer.invite!(current_user) - body_msg = SMSBodyText.account_activation_msg("volunteer", request.base_url) - sms_status = deliver_sms_to @volunteer.phone_number, body_msg + # call short io api here + raw_token = @volunteer.raw_invitation_token + invitation_url = Rails.application.routes.url_helpers.accept_user_invitation_url(invitation_token: raw_token, host: request.base_url) + hash_of_short_urls = @volunteer.phone_number.blank? ? {0 => nil, 1 => nil} : handle_short_url([invitation_url, request.base_url + "/users/edit"]) + body_msg = account_activation_msg("volunteer", hash_of_short_urls) + sms_status = deliver_sms_to @volunteer, body_msg redirect_to edit_volunteer_path(@volunteer), notice: sms_acct_creation_notice("volunteer", sms_status) else render :new diff --git a/app/helpers/api_base_helper.rb b/app/helpers/api_base_helper.rb new file mode 100644 index 0000000000..e0017c2b4f --- /dev/null +++ b/app/helpers/api_base_helper.rb @@ -0,0 +1,3 @@ +module ApiBaseHelper + SHORT_IO = "https://api.short.io/" +end diff --git a/app/helpers/request_header_helper.rb b/app/helpers/request_header_helper.rb new file mode 100644 index 0000000000..aecf8aa4c5 --- /dev/null +++ b/app/helpers/request_header_helper.rb @@ -0,0 +1,4 @@ +module RequestHeaderHelper + ACCEPT_JSON = {"Accept" => "application/json"} + CONTENT_TYPE_JSON = {"Content-Type" => "application/json"} +end diff --git a/app/helpers/sms_body_helper.rb b/app/helpers/sms_body_helper.rb new file mode 100644 index 0000000000..09abdf8918 --- /dev/null +++ b/app/helpers/sms_body_helper.rb @@ -0,0 +1,20 @@ +module SmsBodyHelper + def account_activation_msg(resource = "primorgens", hash_of_links = {}) + password_link = hash_of_links[0] + edit_link = hash_of_links[1] + first_msg = "A CASA #{resource} account was created for you." + second_msg = "First, set your password here #{hash_of_links[0]}." + third_msg = "Then visit #{hash_of_links[1]} to change your text message settings." + # default msg + body_msg = first_msg + " " + "Please check your email to set up your password. Go to profile edit page to change SMS settings." + + if password_link && edit_link + body_msg = first_msg + " " + second_msg + " " + third_msg + elsif password_link.nil? && edit_link + body_msg = first_msg + " " + "Please check your email to set up your password." + " " + third_msg + elsif hash_of_links[0] && hash_of_links[1].nil? + body_msg = first_msg + " " + second_msg + " " + "Go to profile edit page to change SMS settings." + end + body_msg + end +end diff --git a/app/services/short_url_service.rb b/app/services/short_url_service.rb index bc2be84cbf..b51f9853ec 100644 --- a/app/services/short_url_service.rb +++ b/app/services/short_url_service.rb @@ -3,10 +3,12 @@ class ShortUrlService attr_reader :short_url + include ApiBaseHelper + include RequestHeaderHelper include HTTParty - base_uri ApiBaseUrl::SHORT_IO - headers RequestHeader::ACCEPT_JSON - headers RequestHeader::CONTENT_TYPE_JSON + base_uri SHORT_IO + headers ACCEPT_JSON + headers CONTENT_TYPE_JSON def initialize validate_credentials diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb deleted file mode 100644 index be531efcc5..0000000000 --- a/config/initializers/constants.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ApiBaseUrl - SHORT_IO = "https://api.short.io/" -end - -module RequestHeader - ACCEPT_JSON = {"Accept" => "application/json"} - CONTENT_TYPE_JSON = {"Content-Type" => "application/json"} -end - -module SMSBodyText - def self.account_activation_msg(resource, base_url = "hello kitty") - "A CASA #{resource} account was created for you. - Visit #{base_url + "/users/edit"} to change text messaging settings." - end -end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 407fbe2dd3..00a9382b0c 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1,19 +1,34 @@ require "rails_helper" +require "support/stubbed_requests/webmock_helper" RSpec.describe ApplicationController, type: :controller do let(:volunteer) { create(:volunteer) } - # stub an index action in application controller + # add domains to blacklist you want to stub + blacklist = ["api.short.io"] + web_mock = WebMockHelper.new(blacklist) + web_mock.stub_network_connection + # stub application controller methods controller do def index render plain: "hello there..." end + + # input => array of urls + # output => hash of valid short urls {id => short url/nil} + def handle_short_url(url_list) + super + end end + before do # authorize user # sign in as an admin allow(controller).to receive(:authenticate_user!).and_return(true) allow(controller).to receive(:current_user).and_return(volunteer) + @short_io_stub = WebMockHelper.short_io_stub_sms + @short_io_error_stub = WebMockHelper.short_io_error_stub end + describe "#index" do it "does not store URL path for POST" do path = "/index" @@ -24,4 +39,24 @@ def index expect(session[session_key]).to be nil end end + + describe "handle_short_url" do + it "returns a hash of shortened urls" do + input_list = ["www.clubpenguin.com", "www.miniclip.com"] + output_hash = controller.handle_short_url(input_list) + expect(output_hash[0]).to eq("https://42ni.short.gy/jzTwdF") + expect(output_hash[1]).to eq("https://42ni.short.gy/jzTwdF") + expect(output_hash.length).to eq(2) + expect(@short_io_stub).to have_been_requested.times(2) + end + + it "returns a hash with a mix of valid/invalid short urls" do + input_list = ["www.clubpenguin.com", "www.badrequest.com", "www.miniclip.com"] + output_hash = controller.handle_short_url(input_list) + expect(output_hash[1]).to eq(nil) + expect(output_hash.length).to eq(3) + expect(@short_io_stub).to have_been_requested.times(3) + expect(@short_io_error_stub).to have_been_requested.times(1) + end + end end diff --git a/spec/helpers/sms_body_helper_spec.rb b/spec/helpers/sms_body_helper_spec.rb new file mode 100644 index 0000000000..484fb57c5f --- /dev/null +++ b/spec/helpers/sms_body_helper_spec.rb @@ -0,0 +1,25 @@ +require "rails_helper" + +RSpec.describe SmsBodyHelper do + describe "#account_activation_msg" do + it "correct short links provided" do + expected_response = account_activation_msg("primogems", {0 => "www.pasta.com", 1 => "www.yogurt.com"}) + expect(expected_response).to include("First, set your password here www.pasta.com. Then visit www.yogurt.com to change your text message settings.") + end + + it "incorrect short links provided" do + expected_response = account_activation_msg("primogems", {0 => nil, 1 => nil}) + expect(expected_response).to include("Please check your email to set up your password. Go to profile edit page to change SMS settings.") + end + + it "set up password link invalid" do + expected_response = account_activation_msg("primogems", {0 => nil, 1 => "www.carfax.com"}) + expect(expected_response).to include("Please check your email to set up your password. Then visit www.carfax.com to change your text message settings.") + end + + it "link to users/edit invalid" do + expected_response = account_activation_msg("primogems", {0 => "www.yummy.com", 1 => nil}) + expect(expected_response).to include("First, set your password here www.yummy.com. Go to profile edit page to change SMS settings.") + end + end +end diff --git a/spec/requests/casa_admins_spec.rb b/spec/requests/casa_admins_spec.rb index 9ebe14c8c1..2f936603e9 100644 --- a/spec/requests/casa_admins_spec.rb +++ b/spec/requests/casa_admins_spec.rb @@ -329,6 +329,7 @@ sign_in_as_admin @twilio_activation_success_stub = WebMockHelper.twilio_activation_success_stub("admin") @twilio_activation_error_stub = WebMockHelper.twilio_activation_error_stub("admin") + @short_io_stub = WebMockHelper.short_io_stub_sms end context "when successfully" do @@ -365,6 +366,7 @@ it "sends SMS when phone number is provided " do params[:phone_number] = "+12222222222" subject + expect(@short_io_stub).to have_been_requested.times(2) expect(@twilio_activation_success_stub).to have_been_requested.times(1) expect(response).to have_http_status(:redirect) follow_redirect! @@ -373,6 +375,7 @@ it "does not send SMS when phone number not given" do subject + expect(@short_io_stub).to have_been_requested.times(0) expect(@twilio_activation_success_stub).to have_been_requested.times(0) expect(@twilio_activation_error_stub).to have_been_requested.times(0) expect(response).to have_http_status(:redirect) diff --git a/spec/requests/supervisors_spec.rb b/spec/requests/supervisors_spec.rb index 516f23b677..4931cbfc8c 100644 --- a/spec/requests/supervisors_spec.rb +++ b/spec/requests/supervisors_spec.rb @@ -180,6 +180,7 @@ before do @twilio_activation_success_stub = WebMockHelper.twilio_activation_success_stub("supervisor") @twilio_activation_error_stub = WebMockHelper.twilio_activation_error_stub("supervisor") + @short_io_stub = WebMockHelper.short_io_stub_sms end let(:params) do @@ -205,6 +206,7 @@ sign_in admin params[:supervisor][:phone_number] = "+12222222222" post supervisors_url, params: params + expect(@short_io_stub).to have_been_requested.times(2) expect(@twilio_activation_success_stub).to have_been_requested.times(1) expect(response).to have_http_status(:redirect) follow_redirect! @@ -214,6 +216,7 @@ it "does not send a SMS if phone number not given" do sign_in admin post supervisors_url, params: params + expect(@short_io_stub).to have_been_requested.times(0) expect(@twilio_activation_success_stub).to have_been_requested.times(0) expect(@twilio_activation_error_stub).to have_been_requested.times(0) expect(response).to have_http_status(:redirect) diff --git a/spec/requests/volunteers_spec.rb b/spec/requests/volunteers_spec.rb index 254041c1f1..87d9512960 100644 --- a/spec/requests/volunteers_spec.rb +++ b/spec/requests/volunteers_spec.rb @@ -98,6 +98,7 @@ sign_in admin @twilio_activation_success_stub = WebMockHelper.twilio_activation_success_stub("volunteer") @twilio_activation_error_stub = WebMockHelper.twilio_activation_error_stub("volunteer") + @short_io_stub = WebMockHelper.short_io_stub_sms end context "with valid params" do @@ -132,6 +133,7 @@ it "sends a SMS when phone number exists" do params[:volunteer][:phone_number] = "+12222222222" post volunteers_url, params: params + expect(@short_io_stub).to have_been_requested.times(2) expect(@twilio_activation_success_stub).to have_been_requested.times(1) expect(response).to have_http_status(:redirect) follow_redirect! @@ -140,6 +142,7 @@ it "does not send a SMS when phone number is not provided" do post volunteers_url, params: params + expect(@short_io_stub).to have_been_requested.times(0) expect(@twilio_activation_success_stub).to have_been_requested.times(0) expect(@twilio_activation_error_stub).to have_been_requested.times(0) expect(response).to have_http_status(:redirect) diff --git a/spec/support/stubbed_requests/short_io_api.rb b/spec/support/stubbed_requests/short_io_api.rb index fb81f061ba..f6a64d88c8 100644 --- a/spec/support/stubbed_requests/short_io_api.rb +++ b/spec/support/stubbed_requests/short_io_api.rb @@ -14,6 +14,28 @@ def short_io_stub(base_url = "https://www.google.com") .to_return(status: 200, body: "{\"shortURL\":\"https://42ni.short.gy/jzTwdF\"}", headers: {}) end + def short_io_stub_sms + WebMock.stub_request(:post, "https://api.short.io/links") + .with( + headers: { + "Accept" => "application/json", + "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", + "Authorization" => "1337", + "Content-Type" => "application/json", + "User-Agent" => "Ruby" + } + ) + .to_return(status: 200, body: "{\"shortURL\":\"https://42ni.short.gy/jzTwdF\"}", headers: {}) + end + + def short_io_error_stub + WebMock.stub_request(:post, "https://api.short.io/links") + .with( + body: {originalURL: "www.badrequest.com", domain: "42ni.short.gy"}.to_json + ) + .to_return(status: 401, body: "{\"shortURL\":\"https://42ni.short.gy/jzTwdF\"}", headers: {}) + end + def short_io_stub_localhost(base_url = "http://localhost:3000/case_contacts/new") WebMock.stub_request(:post, "https://api.short.io/links") .with(