From d13c475170c1c9ded27e46a48c06626dcc82dd21 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Mon, 14 Oct 2024 15:58:32 +0200 Subject: [PATCH] fix(api_entreprise): better handle api entreprise errors --- app/controllers/users/dossiers_controller.rb | 4 +-- app/jobs/api_entreprise/etablissement_job.rb | 8 ++++++ .../cron/backfill_siret_degraded_mode_job.rb | 1 + app/lib/api_entreprise/api.rb | 4 +-- ...rna_champ_association_fetchable_concern.rb | 12 ++++++--- ...t_champ_etablissement_fetchable_concern.rb | 8 +++--- app/services/api_entreprise_service.rb | 25 +++++++++++++++++-- 7 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 app/jobs/api_entreprise/etablissement_job.rb diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index b7166d911..fbc164888 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -188,8 +188,8 @@ module Users sanitized_siret = siret_model.siret etablissement = begin APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id) - rescue => error - if error.is_a?(APIEntreprise::API::Error::ServiceUnavailable) || (error.try(:network_error?) && !APIEntrepriseService.api_insee_up?) + rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error + if APIEntrepriseService.service_unavailable_error?(error, target: :insee) # TODO: notify ops APIEntrepriseService.create_etablissement_as_degraded_mode(@dossier, sanitized_siret, current_user.id) else diff --git a/app/jobs/api_entreprise/etablissement_job.rb b/app/jobs/api_entreprise/etablissement_job.rb new file mode 100644 index 000000000..fac8732f6 --- /dev/null +++ b/app/jobs/api_entreprise/etablissement_job.rb @@ -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 diff --git a/app/jobs/cron/backfill_siret_degraded_mode_job.rb b/app/jobs/cron/backfill_siret_degraded_mode_job.rb index 30233f0a6..bb80ed637 100644 --- a/app/jobs/cron/backfill_siret_degraded_mode_job.rb +++ b/app/jobs/cron/backfill_siret_degraded_mode_job.rb @@ -1,5 +1,6 @@ # 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 self.schedule_expression = "every 2 hour" diff --git a/app/lib/api_entreprise/api.rb b/app/lib/api_entreprise/api.rb index 0c6b93bc4..1977b5ee0 100644 --- a/app/lib/api_entreprise/api.rb +++ b/app/lib/api_entreprise/api.rb @@ -138,10 +138,10 @@ class APIEntreprise::API end end + SERVICE_UNAVAILABLE_ERRORS = ["01000", "01001", "01002", "02002", "03002", "28002", "29002", "31002", "34002"] def service_unavailable?(response) - return true if response.code == 503 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 diff --git a/app/models/concerns/rna_champ_association_fetchable_concern.rb b/app/models/concerns/rna_champ_association_fetchable_concern.rb index 6fe19ba2d..b1e95c97e 100644 --- a/app/models/concerns/rna_champ_association_fetchable_concern.rb +++ b/app/models/concerns/rna_champ_association_fetchable_concern.rb @@ -12,10 +12,14 @@ module RNAChampAssociationFetchableConcern return clear_association!(:invalid) unless valid_champ_value? 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'])) - rescue APIEntreprise::API::Error => error - error_key = :network_error if error.try(:network_error?) && !APIEntrepriseService.api_djepva_up? - clear_association!(error_key) + update!(data:, value_json: APIGeoService.parse_rna_address(data['adresse'])) + rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error + if APIEntrepriseService.service_unavailable_error?(error, target: :djepva) + clear_association!(:network_error) + else + Sentry.capture_exception(error, extra: { dossier_id:, rna: }) + clear_association!(nil) + end end private diff --git a/app/models/concerns/siret_champ_etablissement_fetchable_concern.rb b/app/models/concerns/siret_champ_etablissement_fetchable_concern.rb index ad88d9e74..e6dbba3e3 100644 --- a/app/models/concerns/siret_champ_etablissement_fetchable_concern.rb +++ b/app/models/concerns/siret_champ_etablissement_fetchable_concern.rb @@ -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!(: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)) - rescue => error - if error.try(:network_error?) && !APIEntrepriseService.api_insee_up? + update!(etablissement:) + rescue APIEntreprise::API::Error, APIEntrepriseToken::TokenError => error + if APIEntrepriseService.service_unavailable_error?(error, target: :insee) update!( etablissement: APIEntrepriseService.create_etablissement_as_degraded_mode(self, siret, user.id) ) @etablissement_fetch_error_key = :api_entreprise_down false 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') end end diff --git a/app/services/api_entreprise_service.rb b/app/services/api_entreprise_service.rb index 4d038993b..f73ccbcb0 100644 --- a/app/services/api_entreprise_service.rb +++ b/app/services/api_entreprise_service.rb @@ -23,6 +23,10 @@ class APIEntrepriseService etablissement = dossier_or_champ.build_etablissement(etablissement_params) 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) etablissement @@ -45,15 +49,25 @@ class APIEntrepriseService return nil if etablissement_params.empty? etablissement.update!(etablissement_params) + + if etablissement.champ.present? + etablissement.champ.update!(value_json: APIGeoService.parse_etablissement_address(etablissement)) + end + + etablissement end def perform_later_fetch_jobs(etablissement, procedure_id, user_id, wait: nil) - [ + jobs = [ APIEntreprise::EntrepriseJob, APIEntreprise::ExtraitKbisJob, APIEntreprise::TvaJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, 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) end @@ -69,6 +83,13 @@ class APIEntrepriseService api_up?("https://entreprise.api.gouv.fr/ping/djepva/api-association") 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 def api_up?(url)