diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index b04237199..d8b7392d2 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -112,18 +112,20 @@ module Users end sanitized_siret = siret_model.siret - begin - etablissement = APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id) - rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::BadGateway, APIEntreprise::API::Error::TimedOut, APIEntreprise::API::Error::ServiceUnavailable, APIEntrepriseToken::TokenError => e - if e.is_a?(APIEntrepriseToken::TokenError) || APIEntrepriseService.api_up? - # probably random error, invite user to retry - Sentry.capture_exception(e, extra: { dossier_id: @dossier.id, siret: sanitized_siret }) - return render_siret_error(t('errors.messages.siret_network_error')) - else - # global API Entreprise error. TODO: degraded mode + notify ops - return render_siret_error(t('errors.messages.siret_api_down')) - end - end + etablissement = begin + APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id) + rescue => error + if error.try(:network_error?) && !APIEntrepriseService.api_up? + # TODO: notify ops + APIEntrepriseService.create_etablissement_as_degraded_mode(@dossier, sanitized_siret, current_user.id) + else + Sentry.capture_exception(error, extra: { dossier_id: @dossier.id, siret: }) + + # probably random error, invite user to retry + return render_siret_error(t('errors.messages.siret_network_error')) + end + end + if etablissement.nil? return render_siret_error(t('errors.messages.siret_unknown')) end diff --git a/app/lib/api_entreprise/api/error.rb b/app/lib/api_entreprise/api/error.rb index f0aa3de7d..7a37417f5 100644 --- a/app/lib/api_entreprise/api/error.rb +++ b/app/lib/api_entreprise/api/error.rb @@ -15,4 +15,8 @@ class APIEntreprise::API::Error < ::StandardError super(msg) end + + def network_error? + true + end end diff --git a/app/lib/api_entreprise/api/error/bad_format_request.rb b/app/lib/api_entreprise/api/error/bad_format_request.rb index 52cf8e517..147718e0e 100644 --- a/app/lib/api_entreprise/api/error/bad_format_request.rb +++ b/app/lib/api_entreprise/api/error/bad_format_request.rb @@ -1,2 +1,5 @@ class APIEntreprise::API::Error::BadFormatRequest < APIEntreprise::API::Error + def network_error? + false + end end diff --git a/app/lib/api_entreprise/api/error/resource_not_found.rb b/app/lib/api_entreprise/api/error/resource_not_found.rb index 4fc38f86e..f6cb118f7 100644 --- a/app/lib/api_entreprise/api/error/resource_not_found.rb +++ b/app/lib/api_entreprise/api/error/resource_not_found.rb @@ -1,2 +1,5 @@ class APIEntreprise::API::Error::ResourceNotFound < APIEntreprise::API::Error + def network_error? + false + end end diff --git a/app/models/etablissement.rb b/app/models/etablissement.rb index 8082085bf..fb6c6fc4d 100644 --- a/app/models/etablissement.rb +++ b/app/models/etablissement.rb @@ -222,6 +222,10 @@ class Etablissement < ApplicationRecord SpreadsheetArchitect.send("to_#{format}".to_sym, bilans_bdf_data) end + def as_degraded_mode? + adresse.nil? # TOOD: maybe dedicated column or more robust way + end + private def bilans_new_keys diff --git a/app/services/api_entreprise_service.rb b/app/services/api_entreprise_service.rb index 67351ea7f..ac363cb49 100644 --- a/app/services/api_entreprise_service.rb +++ b/app/services/api_entreprise_service.rb @@ -1,41 +1,62 @@ class APIEntrepriseService - # create etablissement with EtablissementAdapter - # enqueue api_entreprise jobs to retrieve - # all informations we can get about a SIRET. - # - # Returns nil if the SIRET is unknown - # - # Raises a APIEntreprise::API::Error::RequestFailed exception on transient errors - # (timeout, 5XX HTTP error code, etc.) - def self.create_etablissement(dossier_or_champ, siret, user_id = nil) - etablissement_params = APIEntreprise::EtablissementAdapter.new(siret, dossier_or_champ.procedure.id).to_params - return nil if etablissement_params.empty? + class << self + # create etablissement with EtablissementAdapter + # enqueue api_entreprise jobs to retrieve + # all informations we can get about a SIRET. + # + # Returns nil if the SIRET is unknown + # + # Raises a APIEntreprise::API::Error::RequestFailed exception on transient errors + # (timeout, 5XX HTTP error code, etc.) + # + def create_etablissement(dossier_or_champ, siret, user_id = nil) + procedure_id = dossier_or_champ.procedure.id - etablissement = dossier_or_champ.build_etablissement(etablissement_params) - etablissement.save! + etablissement_params = APIEntreprise::EtablissementAdapter.new(siret, procedure_id).to_params + return nil if etablissement_params.empty? - [ - APIEntreprise::EntrepriseJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, - APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, - APIEntreprise::BilansBdfJob - ].each do |job| - job.perform_later(etablissement.id, dossier_or_champ.procedure.id) + etablissement = dossier_or_champ.build_etablissement(etablissement_params) + etablissement.save! + + perform_later_fetch_jobs(etablissement, procedure_id, user_id) + + etablissement end - APIEntreprise::AttestationFiscaleJob.perform_later(etablissement.id, dossier_or_champ.procedure.id, user_id) - etablissement - end + def create_etablissement_as_degraded_mode(dossier_or_champ, siret, user_id = nil) + etablissement = dossier_or_champ.build_etablissement(siret: siret) + etablissement.save! - def self.api_up?(uname = "apie_2_etablissements") - statuses = APIEntreprise::API.new.current_status.fetch(:results) + procedure_id = dossier_or_champ.procedure.id - # find results having uname = apie_2_etablissements - status = statuses.find { |result| result[:uname] == uname } + perform_later_fetch_jobs(etablissement, procedure_id, user_id, wait: 30.minutes) - status.fetch(:code) == 200 - rescue => e - Sentry.capture_exception(e, extra: { uname: uname }) + etablissement + end - nil + def perform_later_fetch_jobs(etablissement, procedure_id, user_id, wait: nil) + [ + APIEntreprise::EntrepriseJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, + APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, + APIEntreprise::BilansBdfJob + ].each do |job| + job.set(wait:).perform_later(etablissement.id, procedure_id) + end + + APIEntreprise::AttestationFiscaleJob.set(wait:).perform_later(etablissement.id, procedure_id, user_id) + end + + def api_up?(uname = "apie_2_etablissements") + statuses = APIEntreprise::API.new.current_status.fetch(:results) + + # find results having uname = apie_2_etablissements + status = statuses.find { |result| result[:uname] == uname } + + status.fetch(:code) == 200 + rescue => e + Sentry.capture_exception(e, extra: { uname: uname }) + + nil + end end end diff --git a/app/views/users/dossiers/etablissement.html.haml b/app/views/users/dossiers/etablissement.html.haml index 6fca32e50..bbedeb3d2 100644 --- a/app/views/users/dossiers/etablissement.html.haml +++ b/app/views/users/dossiers/etablissement.html.haml @@ -8,7 +8,23 @@ %h1 Informations sur l’établissement - etablissement = @dossier.etablissement - - if etablissement.diffusable_commercialement == false + + - if etablissement.as_degraded_mode? + = render Dsfr::CalloutComponent.new(title: "Nous n'avons pas pu vérifier votre SIRET", theme: :warning, icon: "fr-icon-feedback-fill") do |c| + - c.with_body do + %p + L'annuaire INSEE est indisponible, nous ne pouvons pas vérifier votre SIRET. + %br + %br + Veuillez vérifier par vous-même que le numéro + %strong= etablissement.siret + correspond bien à votre entreprise : + + %p + = link_to annuaire_link(etablissement.siret), class: "fr-btn fr-btn--secondary", **external_link_attributes do + Vérifier dans l'annuaire des entreprises + + - elsif etablissement.diffusable_commercialement == false %p= t('warning_for_private_info', scope: 'views.shared.dossiers.identite_entreprise', etablissement: raison_sociale_or_name(etablissement)) - else diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a04492bad..ff8847661 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -370,7 +370,6 @@ fr: procedure_not_found: "La démarche n’existe pas" siret_unknown: 'Désolé, nous n’avons pas trouvé d’établissement enregistré correspondant à ce numéro SIRET.' siret_network_error: 'Désolé, la récupération des informations SIRET est temporairement indisponible. Veuillez réessayer dans quelques instants.' - siret_api_down: "L'INSEE rencontre actuellement un problème majeur qui empêche le traitement des informations SIRET. Il n'y a pas encore de date de résolution annoncée." siret_not_found: 'Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.' # etablissement_fail: 'Désolé, nous n’avons pas réussi à enregistrer l’établissement correspondant à ce numéro SIRET' france_connect: diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 82ecfc697..ab2ffac25 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -273,7 +273,11 @@ describe Users::DossiersController, type: :controller do let(:api_etablissement_status) { 502 } let(:api_current_status_response) { File.read('spec/fixtures/files/api_entreprise/current_status.json').tr('200', '502') } - it_behaves_like 'the request fails with an error', I18n.t('errors.messages.siret_api_down') + it "create an etablissement only with SIRET as degraded mode" do + dossier.reload + expect(dossier.etablissement.siret).to eq(siret) + expect(dossier.etablissement).to be_as_degraded_mode + end end context 'when API-Entreprise doesn’t know this SIRET' do diff --git a/spec/services/api_entreprise_service_spec.rb b/spec/services/api_entreprise_service_spec.rb index 5fc681438..93ec39539 100644 --- a/spec/services/api_entreprise_service_spec.rb +++ b/spec/services/api_entreprise_service_spec.rb @@ -1,4 +1,16 @@ describe APIEntrepriseService do + shared_examples 'schedule fetch of all etablissement params' do + [ + APIEntreprise::EntrepriseJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, + APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, + APIEntreprise::BilansBdfJob + ].each do |job| + it "should enqueue #{job.class.name}" do + expect { subject }.to have_enqueued_job(job) + end + end + end + describe '#create_etablissement' do before do stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/) @@ -23,15 +35,7 @@ describe APIEntrepriseService do expect(subject[:siret]).to eq(siret) end - [ - APIEntreprise::EntrepriseJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob, - APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob, - APIEntreprise::BilansBdfJob - ].each do |job| - it "should enqueue #{job.class.name}" do - expect { subject }.to have_enqueued_job(job) - end - end + it_behaves_like 'schedule fetch of all etablissement params' end context 'when etablissement api down' do @@ -53,6 +57,24 @@ describe APIEntrepriseService do end end + describe '#create_etablissement_as_degraded_mode' do + let(:siret) { '41816609600051' } + let(:procedure) { create(:procedure) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:user_id) { 12 } + + subject(:etablissement) { APIEntrepriseService.create_etablissement_as_degraded_mode(dossier, siret, user_id) } + + it 'should create an etablissement with minimumal attributes' do + etablissement = subject + + expect(etablissement.siret).to eq(siret) + expect(etablissement).to be_as_degraded_mode + end + + it_behaves_like 'schedule fetch of all etablissement params' + end + describe "#api_up?" do subject { described_class.api_up? } let(:body) { File.read('spec/fixtures/files/api_entreprise/current_status.json') } diff --git a/spec/views/users/dossiers/etablissement.html.haml_spec.rb b/spec/views/users/dossiers/etablissement.html.haml_spec.rb index 8457012d7..d5302bc96 100644 --- a/spec/views/users/dossiers/etablissement.html.haml_spec.rb +++ b/spec/views/users/dossiers/etablissement.html.haml_spec.rb @@ -27,4 +27,13 @@ describe 'users/dossiers/etablissement.html.haml', type: :view do it 'prépare le footer' do expect(footer).to have_selector('footer') end + + context 'etablissement as degraded mode' do + let(:etablissement) { Etablissement.create!(siret: '41816609600051') } + + it "affiche une notice avec un lien de vérification vers l'annuaire" do + expect(rendered).to have_text(etablissement.siret) + expect(rendered).to have_link("Vérifier dans l'annuaire des entreprises", href: "https://annuaire-entreprises\.data\.gouv\.fr/rechercher?terme=#{etablissement.siret}") + end + end end