diff --git a/app/controllers/recurring_job/send_account_reset_notifications_controller.rb b/app/controllers/recurring_job/send_account_reset_notifications_controller.rb index df144c09c7c..5efed553b1b 100644 --- a/app/controllers/recurring_job/send_account_reset_notifications_controller.rb +++ b/app/controllers/recurring_job/send_account_reset_notifications_controller.rb @@ -1,9 +1,10 @@ module RecurringJob class SendAccountResetNotificationsController < BaseController def create - count = AccountReset::GrantRequestsAndSendEmails.new.call - analytics.track_event(Analytics::ACCOUNT_RESET, event: :notifications, count: count) - render plain: 'ok' + render( + plain: 'This endpoint has been removed in favor of idp-jobs.', + status: :gone, + ) end private diff --git a/app/services/account_reset/grant_requests_and_send_emails.rb b/app/services/account_reset/grant_requests_and_send_emails.rb index 51139c50cf5..9d275e09699 100644 --- a/app/services/account_reset/grant_requests_and_send_emails.rb +++ b/app/services/account_reset/grant_requests_and_send_emails.rb @@ -8,6 +8,14 @@ def call ).order('requested_at ASC').each do |arr| notifications_sent += 1 if grant_request_and_send_email(arr) end + + # TODO: rewrite analytics so that we can generate events even from + # background jobs where we have no request or user objects + # analytics.track_event(Analytics::ACCOUNT_RESET, + # event: :notifications, count: notifications_sent) + + Rails.logger.info("Sent #{notifications_sent} account_reset notifications") + notifications_sent end @@ -26,6 +34,7 @@ def sql_query_for_users_eligible_to_delete_their_accounts def grant_request_and_send_email(arr) user = arr.user return false unless AccountReset::GrantRequest.new(user).call + arr = arr.reload user.confirmed_email_addresses.each do |email_address| UserMailer.account_reset_granted(email_address, arr).deliver_later diff --git a/config/application.rb b/config/application.rb index d0cb8ab3e8e..53f0d34cffd 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,5 +1,6 @@ require File.expand_path('../boot', __FILE__) require 'rails/all' +require_relative '../lib/upaya_log_formatter' Bundler.require(*Rails.groups) @@ -25,6 +26,9 @@ class Application < Rails::Application event.payload.except(:params, :headers) end + # Use a custom log formatter to get timestamp + config.log_formatter = Upaya::UpayaLogFormatter.new + require 'headers_filter' config.middleware.insert_before 0, HeadersFilter require 'utf8_sanitizer' diff --git a/config/environments/development.rb b/config/environments/development.rb index 7b2d9deee40..9b72b3931b3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -26,6 +26,9 @@ config.lograge.ignore_actions = ['Users::SessionsController#active'] config.lograge.formatter = Lograge::Formatters::Json.new + # Override log formatter + config.log_formatter = Upaya::DevelopmentUpayaLogFormatter.new + # Bullet gem config config.after_initialize do Bullet.enable = true diff --git a/config/initializers/job_configurations.rb b/config/initializers/job_configurations.rb index 496624ed0aa..303e2621786 100644 --- a/config/initializers/job_configurations.rb +++ b/config/initializers/job_configurations.rb @@ -1,8 +1,17 @@ -# rubocop:disable Metrics/LineLength +# Daily GPO letter mailings JobRunner::Runner.configurations << JobRunner::JobConfiguration.new( name: 'Send GPO letter', interval: 24 * 60 * 60, timeout: 300, - callback: -> { UspsConfirmationUploader.new.run unless HolidayService.observed_holiday?(Time.zone.today) }, + callback: lambda { + UspsConfirmationUploader.new.run unless HolidayService.observed_holiday?(Time.zone.today) + }, +) + +# Send account deletion confirmation notifications +JobRunner::Runner.configurations << JobRunner::JobConfiguration.new( + name: 'Account reset notice', + interval: 5 * 60, # 5 minutes + timeout: 4 * 60, + callback: -> { AccountReset::GrantRequestsAndSendEmails.new.call }, ) -# rubocop:enable Metrics/LineLength diff --git a/lib/lambdas/account_reset_lambda.rb b/lib/lambdas/account_reset_lambda.rb deleted file mode 100644 index 3b7a16a0630..00000000000 --- a/lib/lambdas/account_reset_lambda.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'net/http' - -class AccountResetLambda - def initialize(url, auth_token) - @url = url - @auth_token = auth_token - end - - def send_notifications - Kernel.puts "Sending delayed account reset notifications to #{@url}" - time = now - results = post - Kernel.puts "Response #{results.code} #{results.message}: #{results.body}" - duration = now - time - Kernel.puts "Completed in #{duration.round(2)} seconds" - end - - private - - def post - http = Net::HTTP.new(uri.host, uri.port) - http.open_timeout = 60 - http.read_timeout = 300 - http.use_ssl = true if uri.scheme == 'https' - req = Net::HTTP::Post.new(uri.path, 'X-API-AUTH-TOKEN' => @auth_token) - http.request(req) - end - - def uri - @uri ||= URI.parse(@url) - end - - def now - # TimeZone extensions do not apply when running in the lambda environment - Time.now.to_f # rubocop:disable Rails/TimeZone - end -end - -# This lambda is triggered by cloudwatch to run on a recurring basis. -# It is invoked with the following parameters: -# AccountResetLambda.new(ENV['LOGIN_GOV_URL'], ENV['X_API_AUTH_TOKEN']).send_notifications diff --git a/lib/upaya_log_formatter.rb b/lib/upaya_log_formatter.rb new file mode 100644 index 00000000000..70ae63d3694 --- /dev/null +++ b/lib/upaya_log_formatter.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Upaya + class UpayaLogFormatter < ::Logger::Formatter + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + # If message looks like JSON, print it directly. This is a hack to avoid + # needing to change the analytics ETL Lambdas that parse the pageview + # JSON logs. + # If the Analytics ETL lambda is no longer in use or has had more + # sophisticated parsing added, then this could be removed. + if msg.is_a?(String) && msg.start_with?('{') && msg.end_with?('}') + "#{msg}\n" + else + # Otherwise, use default Ruby log format + super + end + end + end + + class DevelopmentUpayaLogFormatter < UpayaLogFormatter + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + # If message contains terminal escapes, print it directly. This is useful + # in development because rails dev logs contain SQL queries with ANSI + # terminal escapes that should be printed as-is without timestamps. + if msg.is_a?(String) && msg.include?("\u001b[") + "#{msg}\n" + else + # Otherwise, see parent + super + end + end + end +end diff --git a/spec/config/initializers/job_configurations.rb b/spec/config/initializers/job_configurations.rb new file mode 100644 index 00000000000..36b4885f1fb --- /dev/null +++ b/spec/config/initializers/job_configurations.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +describe JobRunner::Runner do + describe '.configurations' do + it 'has the GPO letter job' do + job = JobRunner::Runner.configurations.find { |c| c.name == 'Send GPO letter' } + expect(job).to be_instance_of(JobRunner::JobConfiguration) + expect(job.interval).to eq 24 * 60 * 60 + + stub = instance_double(UspsConfirmationUploader) + expect(UspsConfirmationUploader).to receive(:new).and_return(stub) + expect(stub).to receive(:run).and_return('the GPO test worked') + + expect(job.callback.call).to eq 'the GPO test worked' + end + + it 'runs the account reset job' do + job = JobRunner::Runner.configurations.find { |c| c.name == 'Account reset notice' } + expect(job).to be_instance_of(JobRunner::JobConfiguration) + expect(job.interval).to eq 300 + + service = instance_double(AccountReset::GrantRequestsAndSendEmails) + expect(AccountReset::GrantRequestsAndSendEmails).to receive(:new).and_return(service) + expect(service).to receive(:call).and_return('the reset test worked') + + expect(job.callback.call).to eq 'the reset test worked' + end + end +end diff --git a/spec/controllers/recurring_job/send_account_reset_notifications_controller_spec.rb b/spec/controllers/recurring_job/send_account_reset_notifications_controller_spec.rb index 6b571ac2640..a6299b001aa 100644 --- a/spec/controllers/recurring_job/send_account_reset_notifications_controller_spec.rb +++ b/spec/controllers/recurring_job/send_account_reset_notifications_controller_spec.rb @@ -2,23 +2,23 @@ describe RecurringJob::SendAccountResetNotificationsController do describe '#create' do - it_behaves_like 'a recurring job controller', Figaro.env.account_reset_auth_token - context 'with a good auth token' do before do request.headers['X-API-AUTH-TOKEN'] = Figaro.env.account_reset_auth_token end - it 'grants account reset requests and sends emails' do + # controller is disabled + it 'grants account reset requests and sends emails', skip: true do service = instance_double(AccountReset::GrantRequestsAndSendEmails) allow(AccountReset::GrantRequestsAndSendEmails).to receive(:new).and_return(service) allow(service).to receive(:call).and_return(7) - stub_analytics - expect(@analytics).to receive(:track_event). - with(Analytics::ACCOUNT_RESET, event: :notifications, count: 7) + post :create + end + it 'returns 410 Gone to indicate the endpoint is deprecated' do post :create + expect(response).to have_http_status(:gone) end end end diff --git a/spec/lib/lambdas/account_reset_lambda_spec.rb b/spec/lib/lambdas/account_reset_lambda_spec.rb deleted file mode 100644 index 7672b968408..00000000000 --- a/spec/lib/lambdas/account_reset_lambda_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'rails_helper' -require 'lambdas/account_reset_lambda' - -describe AccountResetLambda do - let(:subject) { AccountResetLambda } - let(:auth_token) { 'abc123' } - let(:test_url) { 'https://fakelogin.gov/path1/path2' } - - describe '#send_notifications' do - it 'calls the url supplying the auth token in the header' do - suppress_output do - stub_request(:post, test_url). - with(headers: { 'X-Api-Auth-Token' => auth_token }). - to_return(status: 200, body: '', headers: {}) - - subject.new(test_url, auth_token).send_notifications - end - end - end -end diff --git a/spec/lib/upaya_log_formatter_spec.rb b/spec/lib/upaya_log_formatter_spec.rb new file mode 100644 index 00000000000..c78d8b7edfe --- /dev/null +++ b/spec/lib/upaya_log_formatter_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' +require 'upaya_log_formatter' + +RSpec.describe Upaya::UpayaLogFormatter do + describe '.call' do + it 'prints expected standard messages' do + now = Time.utc(2019, 1, 2, 3, 4, 5) + expect(Upaya::UpayaLogFormatter.new.call('INFO', now, 'progname', 'hello')).to eq( + "I, [2019-01-02T03:04:05.000000 ##{Process.pid}] INFO -- progname: hello\n", + ) + end + + it 'prints JSON-like messages as-is' do + expect( + Upaya::UpayaLogFormatter.new.call('INFO', Time.zone.now, 'progname', '{"hello"}'), + ).to eq('{"hello"}' + "\n") + end + end +end + +describe Upaya::DevelopmentUpayaLogFormatter do + describe '.call' do + it 'prints ANSI escaped messages as-is' do + now = Time.utc(2019, 1, 2, 3, 4, 5) + msg = "\e[1;31mhello\e[m" + expect(Upaya::DevelopmentUpayaLogFormatter.new.call('INFO', now, 'progname', msg)).to eq( + msg + "\n", + ) + end + + it 'prints expected messages otherwise' do + now = Time.utc(2019, 1, 2, 3, 4, 5) + expect(Upaya::DevelopmentUpayaLogFormatter.new.call('INFO', now, 'progname', 'hello')).to eq( + "I, [2019-01-02T03:04:05.000000 ##{Process.pid}] INFO -- progname: hello\n", + ) + end + end +end