Merge pull request #10931 from tchak/better-handle-api-entreprise-jobs

fix(api_entreprise): better handle api entreprise errors
This commit is contained in:
Paul Chavard 2024-10-17 09:30:09 +00:00 committed by GitHub
commit ad7458a12f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 48 additions and 14 deletions

View file

@ -188,8 +188,8 @@ module Users
sanitized_siret = siret_model.siret sanitized_siret = siret_model.siret
etablissement = begin etablissement = begin
APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id) APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id)
rescue => error rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error
if error.is_a?(APIEntreprise::API::Error::ServiceUnavailable) || (error.try(:network_error?) && !APIEntrepriseService.api_insee_up?) if APIEntrepriseService.service_unavailable_error?(error, target: :insee)
# TODO: notify ops # TODO: notify ops
APIEntrepriseService.create_etablissement_as_degraded_mode(@dossier, sanitized_siret, current_user.id) APIEntrepriseService.create_etablissement_as_degraded_mode(@dossier, sanitized_siret, current_user.id)
else else

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
class APIEntreprise::EtablissementJob < APIEntreprise::Job
def perform(etablissement_id, procedure_id)
find_etablissement(etablissement_id)
APIEntrepriseService.update_etablissement_from_degraded_mode(etablissement, procedure_id)
end
end

View file

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
# TODO: remove this job in a few days once all failed etablissements are queued as separate jobs
class Cron::BackfillSiretDegradedModeJob < Cron::CronJob class Cron::BackfillSiretDegradedModeJob < Cron::CronJob
self.schedule_expression = "every 2 hour" self.schedule_expression = "every 2 hour"

View file

@ -138,10 +138,10 @@ class APIEntreprise::API
end end
end end
SERVICE_UNAVAILABLE_ERRORS = ["01000", "01001", "01002", "02002", "03002", "28002", "29002", "31002", "34002"]
def service_unavailable?(response) def service_unavailable?(response)
return true if response.code == 503
if response.code == 502 || response.code == 504 if response.code == 502 || response.code == 504
parse_response_errors(response).any? { _1.is_a?(Hash) && ["01000", "01001", "01002", "02002", "03002"].include?(_1[:code]) } parse_response_errors(response).any? { _1.is_a?(Hash) && _1[:code]&.in?(SERVICE_UNAVAILABLE_ERRORS) }
end end
end end

View file

@ -12,10 +12,14 @@ module RNAChampAssociationFetchableConcern
return clear_association!(:invalid) unless valid_champ_value? return clear_association!(:invalid) unless valid_champ_value?
return clear_association!(:not_found) if (data = APIEntreprise::RNAAdapter.new(rna, procedure_id).to_params).blank? return clear_association!(:not_found) if (data = APIEntreprise::RNAAdapter.new(rna, procedure_id).to_params).blank?
update!(data: data, value_json: APIGeoService.parse_rna_address(data['adresse'])) update!(data:, value_json: APIGeoService.parse_rna_address(data['adresse']))
rescue APIEntreprise::API::Error => error rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error
error_key = :network_error if error.try(:network_error?) && !APIEntrepriseService.api_djepva_up? if APIEntrepriseService.service_unavailable_error?(error, target: :djepva)
clear_association!(error_key) clear_association!(:network_error)
else
Sentry.capture_exception(error, extra: { dossier_id:, rna: })
clear_association!(nil)
end
end end
private private

View file

@ -11,16 +11,16 @@ module SiretChampEtablissementFetchableConcern
return clear_etablissement!(:invalid_checksum) if invalid_because?(siret, :checksum) # i18n-tasks-use t('errors.messages.invalid_siret_checksum') return clear_etablissement!(:invalid_checksum) if invalid_because?(siret, :checksum) # i18n-tasks-use t('errors.messages.invalid_siret_checksum')
return clear_etablissement!(:not_found) unless (etablissement = APIEntrepriseService.create_etablissement(self, siret, user&.id)) # i18n-tasks-use t('errors.messages.siret_not_found') return clear_etablissement!(:not_found) unless (etablissement = APIEntrepriseService.create_etablissement(self, siret, user&.id)) # i18n-tasks-use t('errors.messages.siret_not_found')
update!(etablissement: etablissement, value_json: APIGeoService.parse_etablissement_address(etablissement)) update!(etablissement:)
rescue => error rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error
if error.try(:network_error?) && !APIEntrepriseService.api_insee_up? if APIEntrepriseService.service_unavailable_error?(error, target: :insee)
update!( update!(
etablissement: APIEntrepriseService.create_etablissement_as_degraded_mode(self, siret, user.id) etablissement: APIEntrepriseService.create_etablissement_as_degraded_mode(self, siret, user.id)
) )
@etablissement_fetch_error_key = :api_entreprise_down @etablissement_fetch_error_key = :api_entreprise_down
false false
else else
Sentry.capture_exception(error, extra: { dossier_id: dossier_id, siret: siret }) Sentry.capture_exception(error, extra: { dossier_id:, siret: })
clear_etablissement!(:network_error) # i18n-tasks-use t('errors.messages.siret_network_error') clear_etablissement!(:network_error) # i18n-tasks-use t('errors.messages.siret_network_error')
end end
end end

View file

@ -23,6 +23,10 @@ class APIEntrepriseService
etablissement = dossier_or_champ.build_etablissement(etablissement_params) etablissement = dossier_or_champ.build_etablissement(etablissement_params)
etablissement.save! etablissement.save!
if dossier_or_champ.is_a?(Champ)
dossier_or_champ.update!(value_json: APIGeoService.parse_etablissement_address(etablissement))
end
perform_later_fetch_jobs(etablissement, procedure_id, user_id) perform_later_fetch_jobs(etablissement, procedure_id, user_id)
etablissement etablissement
@ -45,15 +49,25 @@ class APIEntrepriseService
return nil if etablissement_params.empty? return nil if etablissement_params.empty?
etablissement.update!(etablissement_params) etablissement.update!(etablissement_params)
if etablissement.champ.present?
etablissement.champ.update!(value_json: APIGeoService.parse_etablissement_address(etablissement))
end
etablissement
end end
def perform_later_fetch_jobs(etablissement, procedure_id, user_id, wait: nil) def perform_later_fetch_jobs(etablissement, procedure_id, user_id, wait: nil)
[ jobs = [
APIEntreprise::EntrepriseJob, APIEntreprise::ExtraitKbisJob, APIEntreprise::TvaJob, APIEntreprise::EntrepriseJob, APIEntreprise::ExtraitKbisJob, APIEntreprise::TvaJob,
APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob,
APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob,
APIEntreprise::BilansBdfJob APIEntreprise::BilansBdfJob
].each do |job| ]
if etablissement.as_degraded_mode?
jobs << APIEntreprise::EtablissementJob
end
jobs.each do |job|
job.set(wait:).perform_later(etablissement.id, procedure_id) job.set(wait:).perform_later(etablissement.id, procedure_id)
end end
@ -69,6 +83,13 @@ class APIEntrepriseService
api_up?("https://entreprise.api.gouv.fr/ping/djepva/api-association") api_up?("https://entreprise.api.gouv.fr/ping/djepva/api-association")
end end
def service_unavailable_error?(error, target:)
return false if !error.try(:network_error?)
return true if target == :insee && !APIEntrepriseService.api_insee_up?
return true if target == :djepva && !APIEntrepriseService.api_djepva_up?
error.is_a?(APIEntreprise::API::Error::ServiceUnavailable)
end
private private
def api_up?(url) def api_up?(url)