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
12 changes: 11 additions & 1 deletion app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,22 @@ def default_template_folder
private

def create_careerplug_webhook
return if ENV['CAREERPLUG_WEBHOOK_SECRET'].blank? || ENV['CAREERPLUG_WEBHOOK_URL'].blank?
unless ENV['CAREERPLUG_WEBHOOK_URL'].present? && ENV['CAREERPLUG_WEBHOOK_SECRET'].present?
report_missing_webhook_config("account id=#{id}") unless Rails.env.local?
return
end

webhook_urls.create!(
url: ENV.fetch('CAREERPLUG_WEBHOOK_URL'),
events: %w[form.started form.completed submission.completed form.changes_requested template.preferences_updated],
secret: { 'X-CareerPlug-Secret' => ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') }
)
end

def report_missing_webhook_config(scope)
message = format('CAREERPLUG_WEBHOOK_URL/CAREERPLUG_WEBHOOK_SECRET are not set; ' \
'skipping webhook creation for %<scope>s', scope:)
Rails.logger.error(message)
Airbrake.notify(message)
end
end
14 changes: 13 additions & 1 deletion app/models/partnership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,25 @@ def default_template_folder(author)
author: author)
end

private

def create_careerplug_webhook
return if ENV['CAREERPLUG_WEBHOOK_SECRET'].blank? || ENV['CAREERPLUG_WEBHOOK_URL'].blank?
unless ENV['CAREERPLUG_WEBHOOK_URL'].present? && ENV['CAREERPLUG_WEBHOOK_SECRET'].present?
report_missing_webhook_config("partnership id=#{id}") unless Rails.env.local?
return
end

webhook_urls.create!(
url: ENV.fetch('CAREERPLUG_WEBHOOK_URL'),
events: %w[template.preferences_updated],
secret: { 'X-CareerPlug-Secret' => ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') }
)
end

def report_missing_webhook_config(scope)
message = format('CAREERPLUG_WEBHOOK_URL/CAREERPLUG_WEBHOOK_SECRET are not set; ' \
'skipping webhook creation for %<scope>s', scope:)
Rails.logger.error(message)
Airbrake.notify(message)
end
end
19 changes: 19 additions & 0 deletions config/initializers/careerplug_webhook_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

# Fail loudly (but do not block boot) if CareerPlug webhook configuration is
# missing outside of local dev/test. Without these env vars, the Account/
# Partnership create callbacks skip WebhookUrl creation, which leaves ATS form
# submissions stuck at "assigned" forever. A missing config should be impossible
# to miss on deploy, so this logs a prominent error and reports to error
# tracking. Non-blocking so that console/migrate access remains available for
# recovery.
unless Rails.env.local?
missing = %w[CAREERPLUG_WEBHOOK_URL CAREERPLUG_WEBHOOK_SECRET].reject { |key| ENV[key].present? }

unless missing.empty?
message = "CareerPlug webhook config missing in #{Rails.env}: #{missing.join(', ')}. " \
'New accounts/partnerships will not get webhooks until this is fixed.'
Rails.logger.error("[careerplug_webhook_config] #{message}")
Airbrake.notify(message)
end
end
82 changes: 82 additions & 0 deletions spec/initializers/careerplug_webhook_config_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'rails_helper'

# rubocop:disable RSpec/DescribeClass The initializer is a top-level script, not a class.
RSpec.describe 'careerplug_webhook_config initializer' do
let(:initializer_path) { Rails.root.join('config/initializers/careerplug_webhook_config.rb').to_s }

# The initializer is a one-shot top-level script; re-evaluate it under the
# current stubs to exercise each branch.
def load_initializer
load initializer_path
end

before do
allow(Rails.logger).to receive(:error)
allow(Airbrake).to receive(:notify)
end

context 'when in a local environment (development/test)' do
before { allow(Rails.env).to receive(:local?).and_return(true) }

it 'does not log an error or report to Airbrake' do
load_initializer

expect(Rails.logger).not_to have_received(:error)
expect(Airbrake).not_to have_received(:notify)
end
end

context 'when not local with both vars missing' do
before do
allow(Rails.env).to receive(:local?).and_return(false)
stub_const('ENV', ENV.to_h.except('CAREERPLUG_WEBHOOK_URL', 'CAREERPLUG_WEBHOOK_SECRET'))
end

it 'logs a prominent error naming both missing vars' do
load_initializer

expect(Rails.logger).to have_received(:error)
.with(/CareerPlug webhook config missing.*CAREERPLUG_WEBHOOK_URL.*CAREERPLUG_WEBHOOK_SECRET/)
end

it 'reports the missing config to Airbrake' do
load_initializer

expect(Airbrake).to have_received(:notify).with(/CareerPlug webhook config missing/)
end
end

context 'when not local with only one var missing' do
before do
allow(Rails.env).to receive(:local?).and_return(false)
stub_const('ENV', ENV.to_h.merge('CAREERPLUG_WEBHOOK_URL' => 'https://example.com/events')
.except('CAREERPLUG_WEBHOOK_SECRET'))
end

it 'logs an error naming only the missing var' do
load_initializer

expect(Rails.logger).to have_received(:error).with(/missing.*CAREERPLUG_WEBHOOK_SECRET/)
end
end

context 'when not local with both vars present' do
before do
allow(Rails.env).to receive(:local?).and_return(false)
stub_const('ENV', ENV.to_h.merge(
'CAREERPLUG_WEBHOOK_URL' => 'https://www.careerplug.com/api/docuseal/events',
'CAREERPLUG_WEBHOOK_SECRET' => 'secret'
))
end

it 'does not log an error or report to Airbrake' do
load_initializer

expect(Rails.logger).not_to have_received(:error)
expect(Airbrake).not_to have_received(:notify)
end
end
end
# rubocop:enable RSpec/DescribeClass
16 changes: 16 additions & 0 deletions spec/models/account_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,27 @@
context 'when env vars are missing' do
before do
stub_const('ENV', ENV.to_h.except('CAREERPLUG_WEBHOOK_URL', 'CAREERPLUG_WEBHOOK_SECRET'))
allow(Rails.env).to receive(:local?).and_return(false)
end

it 'does not create a webhook' do
expect { create(:account) }.not_to change(WebhookUrl, :count)
end

it 'logs a prominent error with the account id' do
allow(Rails.logger).to receive(:error)
create(:account)

expect(Rails.logger).to have_received(:error).with(/account id=\d+/)
end

it 'reports the missing config to Airbrake' do
allow(Airbrake).to receive(:notify)
create(:account)

expect(Airbrake).to have_received(:notify)
.with(%r{CAREERPLUG_WEBHOOK_URL/CAREERPLUG_WEBHOOK_SECRET are not set})
end
end
end

Expand Down
16 changes: 16 additions & 0 deletions spec/models/partnership_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,27 @@
context 'when env vars are missing' do
before do
stub_const('ENV', ENV.to_h.except('CAREERPLUG_WEBHOOK_URL', 'CAREERPLUG_WEBHOOK_SECRET'))
allow(Rails.env).to receive(:local?).and_return(false)
end

it 'does not create a webhook' do
expect { create(:partnership) }.not_to change(WebhookUrl, :count)
end

it 'logs a prominent error with the partnership id' do
allow(Rails.logger).to receive(:error)
create(:partnership)

expect(Rails.logger).to have_received(:error).with(/partnership id=\d+/)
end

it 'reports the missing config to Airbrake' do
allow(Airbrake).to receive(:notify)
create(:partnership)

expect(Airbrake).to have_received(:notify)
.with(%r{CAREERPLUG_WEBHOOK_URL/CAREERPLUG_WEBHOOK_SECRET are not set})
end
end
end

Expand Down
Loading