-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[VACMS-19451]Alternative Banners fetch banner data from graphql to db (…
…#19511) * add initial thougnts/notes * create banner job to pull and update db, only fetching banner data * cleanup notes and variable names * Move fetching out of main job into banner services for building and updating * Further refinement of builder/updater/job process * get builder creating banners using parsed response data * params refinement * rubocop cleanup * separate banner profiles from updater * adjust scope to simplify queries a notch for easier understanding * add error response to controller when path is not provided for #by_path * remove no longer used #enabled? * cleanup updater * cleanup requirements * appease the rubocop * destroy any lingering banners that are no longer being provided * fixup query and vamc model naming * pluck > map * remove job to be handled with VAMC-19452 * remove leftover banner spec and adjust #by_path testing in appropriate banner spec * add logging to builder and updater * add updater spec * add spec for builder * update settings used, appease the rubocop * avoid alternative_banners wording * missed a spec * rebasing added the job too soon * return when rendering error * 422 is more accurate than 400 * [VAMC-19452]Update Alternative Banners DB every 10min with sidekiq job (#19550) * adjust updater to return error when parsing failed, introduce job to work with updater * avoid alternative_banners wording * add job to 10m rotation * add flipper to enable the update all job * make linter happy * be more specific when re-raising error * update updater spec to include error * Use Job in periodic_jobs.rb Co-authored-by: Eli Selkin <[email protected]> --------- Co-authored-by: Eli Selkin <[email protected]>
- Loading branch information
1 parent
1415707
commit 4d6a935
Showing
15 changed files
with
498 additions
and
26 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# frozen_string_literal: true | ||
|
||
module Banners | ||
class UpdateAllJob | ||
include Sidekiq::Job | ||
|
||
STATSD_KEY_PREFIX = 'banners.sidekiq.update_all_banners' | ||
|
||
sidekiq_options retry: 5 | ||
|
||
sidekiq_retries_exhausted do |msg, _ex| | ||
job_id = msg['jid'] | ||
job_class = msg['class'] | ||
error_class = msg['error_class'] | ||
error_message = msg['error_message'] | ||
|
||
StatsD.increment("#{STATSD_KEY_PREFIX}.exhausted") | ||
|
||
message = "#{job_class} retries exhausted" | ||
Rails.logger.error(message, { job_id:, error_class:, error_message: }) | ||
rescue => e | ||
Rails.logger.error( | ||
"Failure in #{job_class}#sidekiq_retries_exhausted", | ||
{ | ||
messaged_content: e.message, | ||
job_id:, | ||
pre_exhaustion_failure: { | ||
error_class:, | ||
error_message: | ||
} | ||
} | ||
) | ||
|
||
raise e | ||
end | ||
|
||
def perform | ||
return unless enabled? | ||
|
||
Banners.update_all | ||
rescue Banners::Updater::BannerDataFetchError => e | ||
StatsD.increment("#{STATSD_KEY_PREFIX}.banner_data_fetch_error") | ||
Rails.logger.error( | ||
'Banner data fetch failed', | ||
{ error_message: e.message, error_class: e.class.name } | ||
) | ||
raise e # Re-raise to trigger Sidekiq retries | ||
end | ||
|
||
private | ||
|
||
def enabled? | ||
Flipper.enabled?(:banner_update_alternative_banners) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
query bannerAlerts { | ||
nodeQuery( | ||
limit: 500, | ||
filter: {conditions: [ | ||
{field: "status", value: "1", operator: EQUAL}, | ||
{field: "type", value: "full_width_banner_alert"}, | ||
{field: "field_banner_alert_vamcs", operator: IS_NOT_NULL} | ||
]} | ||
) { | ||
count | ||
entities { | ||
... on NodeFullWidthBannerAlert { | ||
title | ||
fieldBody { | ||
processed | ||
} | ||
entityId | ||
fieldAlertType | ||
fieldAlertDismissable | ||
fieldAlertFindFacilitiesCta | ||
fieldAlertOperatingStatusCta | ||
fieldAlertEmailUpdatesButton | ||
fieldAlertInheritanceSubpages | ||
fieldOperatingStatusSendemail | ||
fieldLastSavedByAnEditor | ||
fieldBannerAlertSituationinfo { | ||
processed | ||
} | ||
fieldSituationUpdates { | ||
entity { | ||
... on ParagraphSituationUpdate { | ||
fieldWysiwyg { | ||
processed | ||
} | ||
} | ||
} | ||
} | ||
fieldBannerAlertVamcs { | ||
entity { | ||
... on NodeVamcOperatingStatusAndAlerts { | ||
title | ||
entityUrl { | ||
path | ||
} | ||
fieldOffice { | ||
entity { | ||
... on NodeHealthCareRegionPage { | ||
fieldVamcEhrSystem | ||
title | ||
entityUrl { | ||
path | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
fieldAdministration { | ||
entity{ | ||
... on TaxonomyTermAdministration { | ||
entityId | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,15 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'banners/builder' | ||
require 'banners/engine' | ||
require 'banners/updater' | ||
|
||
module Banners | ||
# Your code goes here... | ||
def self.build(banner_props) | ||
Builder.perform(banner_props) | ||
end | ||
|
||
def self.update_all | ||
Updater.perform | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_string_literal: true | ||
|
||
module Banners | ||
class Builder | ||
STATSD_KEY_PREFIX = 'banners.builder' | ||
|
||
def self.perform(banner_data) | ||
banner = new(banner_data).banner | ||
|
||
if banner.update(banner_data) | ||
log_success(banner_data[:entity_id]) | ||
banner | ||
else | ||
log_failure(banner_data[:entity_id]) | ||
false | ||
end | ||
end | ||
|
||
def initialize(banner_data) | ||
@banner_data = banner_data | ||
end | ||
|
||
def banner | ||
@banner ||= Banner.find_or_initialize_by(entity_id: banner_data[:entity_id]) | ||
end | ||
|
||
attr_reader :banner_data | ||
|
||
private | ||
|
||
def self.log_failure(entity_id) | ||
StatsD.increment("#{STATSD_KEY_PREFIX}.failure", tags: ["entitiy_id:#{entity_id}"]) | ||
end | ||
|
||
def self.log_success(entity_id) | ||
StatsD.increment("#{STATSD_KEY_PREFIX}.success", tags: ["entitiy_id:#{entity_id}"]) | ||
end | ||
|
||
private_class_method :log_failure, :log_success | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# frozen_string_literal: true | ||
|
||
module Banners | ||
module Profile | ||
class Vamc | ||
# Converts the GraphQL response into a hash that can be used to create or update a VAMC banner | ||
def self.parsed_banner(graphql_banner_response) | ||
{ | ||
entity_id: graphql_banner_response['entityId'], | ||
headline: graphql_banner_response['title'], | ||
alert_type: graphql_banner_response['fieldAlertType'], | ||
entity_bundle: 'full_width_banner_alert', | ||
content: graphql_banner_response['fieldBody']['processed'], | ||
context: graphql_banner_response['fieldBannerAlertVamcs'], | ||
show_close: graphql_banner_response['fieldAlertDismissable'], | ||
operating_status_cta: graphql_banner_response['fieldAlertOperatingStatusCta'], | ||
email_updates_button: graphql_banner_response['fieldAlertEmailUpdatesButton'], | ||
find_facilities_cta: graphql_banner_response['fieldAlertFindFacilitiesCta'], | ||
limit_subpage_inheritance: graphql_banner_response['fieldAlertLimitSubpageInheritance'] || false | ||
} | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'banners/builder' | ||
require 'banners/profile/vamc' | ||
|
||
module Banners | ||
class Updater | ||
STATSD_KEY_PREFIX = 'banners.updater' | ||
|
||
class BannerDataFetchError < StandardError; end | ||
|
||
# If banners are to be added in the future, include them here by adding | ||
# another #update_{type_of}_banners method for #perform to call | ||
def self.perform | ||
banner_updater = new | ||
banner_updater.update_vamc_banners | ||
end | ||
|
||
def update_vamc_banners | ||
if vamcs_banner_data.all? { |banner_data| Builder.perform(Profile::Vamc.parsed_banner(banner_data)) } | ||
destroy_missing_banners(vamcs_banner_data.pluck('entityId')) | ||
log_success('vamc') | ||
true | ||
else | ||
log_failure('vamc') | ||
false | ||
end | ||
end | ||
|
||
private | ||
|
||
def connection | ||
@connection ||= Faraday.new(Settings.banners.drupal_url, faraday_options) do |faraday| | ||
faraday.request :url_encoded | ||
faraday.request :authorization, :basic, Settings.banners.drupal_username, | ||
Settings.banners.drupal_password | ||
faraday.adapter faraday_adapter | ||
end | ||
end | ||
|
||
def destroy_missing_banners(entity_ids_to_keep) | ||
Banner.where.not(entity_id: entity_ids_to_keep).destroy_all | ||
end | ||
|
||
def faraday_adapter | ||
Rails.env.production? ? Faraday.default_adapter : :net_http_socks | ||
end | ||
|
||
def faraday_options | ||
options = { ssl: { verify: false } } | ||
options[:proxy] = { uri: URI.parse('socks://localhost:2001') } unless Rails.env.production? | ||
options | ||
end | ||
|
||
def log_failure(banner_type) | ||
StatsD.increment("#{STATSD_KEY_PREFIX}.failure", tags: ["banner_type:#{banner_type}"]) | ||
end | ||
|
||
def log_success(banner_type) | ||
StatsD.increment("#{STATSD_KEY_PREFIX}.success", tags: ["banner_type:#{banner_type}"]) | ||
end | ||
|
||
def vamcs_banner_data | ||
banner_graphql_query = Rails.root.join('modules', 'banners', 'config', 'vamcs_graphql_query.txt') | ||
body = { query: File.read(banner_graphql_query) } | ||
|
||
response = connection.post do |req| | ||
req.path = 'graphql' | ||
req.body = body.to_json | ||
req.options.timeout = 300 | ||
end | ||
|
||
begin | ||
JSON.parse(response.body).dig('data', 'nodeQuery', 'entities') | ||
rescue JSON::ParserError => e | ||
raise BannerDataFetchError, "Failed to parse VAMC banner data: #{e.message}" | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.