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
71 changes: 71 additions & 0 deletions app/jobs/reports/total_ial2_costs_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'csv'

module Reports
class TotalIal2CostsReport < BaseReport
REPORT_NAME = 'total-ial2-costs'.freeze
NUM_LOOKBACK_DAYS = 45

include GoodJob::ActiveJobExtensions::Concurrency

good_job_control_concurrency_with(
total_limit: 1,
key: -> { "#{REPORT_NAME}-#{arguments.first}" },
)

def perform(date)
results = transaction_with_timeout { query(date) }

save_report(REPORT_NAME, to_csv(results), extension: 'csv')
end

# @param [PG::Result]
# @return [String]
def to_csv(results)
CSV.generate do |csv|
csv << %w[
date
ial
cost_type
count
]

results.each do |row|
csv << row.values_at('date', 'ial', 'cost_type', 'count')
end
end
end

# @return [PG::Result]
def query(date)
finish = date.beginning_of_day
start = (finish - NUM_LOOKBACK_DAYS.days).beginning_of_day

params = {
start: ActiveRecord::Base.connection.quote(start),
finish: ActiveRecord::Base.connection.quote(finish),
}

sql = format(<<~SQL, params)
SELECT
DATE(sp_costs.created_at) AS date
, sp_costs.ial
, sp_costs.cost_type
, COUNT(*) AS count
FROM sp_costs
WHERE
%{start} <= sp_costs.created_at AND sp_costs.created_at <= %{finish}
AND sp_costs.ial > 1
GROUP BY
sp_costs.ial
, sp_costs.cost_type
, DATE(sp_costs.created_at)
ORDER BY
sp_costs.ial
, sp_costs.cost_type
, DATE(sp_costs.created_at)
SQL

ActiveRecord::Base.connection.execute(sql)
end
end
end
6 changes: 6 additions & 0 deletions config/initializers/job_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
cron: cron_24h,
args: -> { [Time.zone.today] },
},
# Total IAL2 Costs Report to S3
total_ial2_costs: {
class: 'Reports::TotalIal2CostsReport',
cron: cron_24h,
args: -> { [Time.zone.today] },
},
# SP Active Users Report to S3
sp_active_users_report: {
class: 'Reports::SpActiveUsersReport',
Expand Down
81 changes: 81 additions & 0 deletions spec/jobs/reports/total_ial2_costs_report_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require 'rails_helper'

RSpec.describe Reports::TotalIal2CostsReport do
subject(:report) { described_class.new }

describe '#perform' do
let(:issuer1) { 'issuer1' }
let(:issuer2) { 'issuer2' }

let!(:sp1) { create(:service_provider, issuer: issuer1, friendly_name: issuer1) }
let!(:sp2) { create(:service_provider, issuer: issuer2, friendly_name: issuer2) }

let(:date) { Date.new(2022, 4, 1) }
let(:yesterday) { Date.new(2022, 3, 31) }
let(:yesterday_utc) { yesterday.in_time_zone('UTC') }
let(:too_old) { Date.new(2021, 12, 31) }

before do
SpCost.create(
agency_id: 1,
issuer: issuer1,
cost_type: 'authentication',
created_at: yesterday_utc,
ial: 2,
)
SpCost.create(
agency_id: 2,
issuer: issuer2,
cost_type: 'authentication',
created_at: yesterday_utc,
ial: 2,
)

SpCost.create(
agency_id: 1, issuer: issuer1, cost_type: 'sms', created_at: yesterday_utc, ial: 2,
)

# rows that get ignored
SpCost.create(
agency_id: 2, issuer: issuer2, cost_type: 'user_added', created_at: too_old, ial: 2,
)
SpCost.create(
agency_id: 2, issuer: issuer2, cost_type: 'user_added', created_at: yesterday_utc, ial: 1,
)
end

it 'writes a CSV report to S3' do
expect(report).to receive(:save_report) do |report_name, body, extension:|
expect(report_name).to eq(described_class::REPORT_NAME)
expect(extension).to eq('csv')

csv = CSV.parse(body, headers: true)
expect(csv.length).to eq(2)

row = csv.first
expect(row['date']).to eq(yesterday.to_s)
expect(row['cost_type']).to eq('authentication')
expect(row['ial']).to eq(2.to_s)
expect(row['count']).to eq(2.to_s)

row = csv[1]
expect(row['date']).to eq(yesterday.to_s)
expect(row['cost_type']).to eq('sms')
expect(row['ial']).to eq(2.to_s)
expect(row['count']).to eq(1.to_s)
end

report.perform(date)
end
end

describe '#good_job_concurrency_key' do
let(:date) { Time.zone.today }

it 'is the job name and the date' do
job = described_class.new(date)
expect(job.good_job_concurrency_key).
to eq("#{described_class::REPORT_NAME}-#{date}")
end
end
end