From a4054053f71693f2d07865152b7d892af573376a Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Thu, 26 Sep 2024 18:27:58 +0200 Subject: [PATCH 1/8] refactor(admin): move siret input up --- app/views/administrateurs/services/_form.html.haml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/administrateurs/services/_form.html.haml b/app/views/administrateurs/services/_form.html.haml index 9c6040508..2ac6fb5ff 100644 --- a/app/views/administrateurs/services/_form.html.haml +++ b/app/views/administrateurs/services/_form.html.haml @@ -1,5 +1,12 @@ = form_with model: [ :admin, service], local: true do |f| + = render Dsfr::InputComponent.new(form: f, attribute: :siret, input_type: :text_field, opts: { placeholder: "14 chiffres, sans espace" }) do |c| + - c.with_hint do + = "Indiquez le numéro de SIRET de l’organisme dont ce service dépend. Rechercher le SIRET sur " + = link_to("annuaire-entreprises.data.gouv.fr", annuaire_link, **external_link_attributes) + %br + = "Nous préremplirons les informations de contact à partir de l’Annuaire Service Public correspondant." + = render Dsfr::InputComponent.new(form: f, attribute: :nom, input_type: :text_field) = render Dsfr::InputComponent.new(form: f, attribute: :organisme, input_type: :text_field) @@ -10,11 +17,6 @@ = f.select :type_organisme, Service.type_organismes.keys.map { |key| [ I18n.t("type_organisme.#{key}"), key] }, {}, class: 'fr-select' - = render Dsfr::InputComponent.new(form: f, attribute: :siret, input_type: :text_field, opts: { placeholder: "14 chiffres, sans espace" }) do |c| - - c.with_hint do - = "Indiquez le numéro de SIRET de l’organisme dont ce service dépend. Rechercher le SIRET sur " - = link_to("annuaire-entreprises.data.gouv.fr", annuaire_link, **external_link_attributes) - = render Dsfr::CalloutComponent.new(title: "Informations de contact") do |c| - c.with_body do Votre démarche sera hébergée par #{Current.application_name} – mais nous ne pouvons pas assurer le support des démarches. Et malgré la dématérialisation, les usagers se poseront parfois des questions légitimes sur le processus administratif. From 8dc47c1b93e8a192cfb99ec0e356564769a40aa3 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 7 Oct 2024 18:07:37 +0200 Subject: [PATCH 2/8] feat(service): prefill contact information from annuaire service public --- ...prefillable_from_service_public_concern.rb | 54 ++++++++++ app/models/service.rb | 2 + app/schemas/service-public.json | 76 +++++++++++++ .../annuaire_service_public_service.rb | 52 +++++++++ app/services/api_geo_service.rb | 15 +++ ..._service_public_failure_20004021000000.yml | 70 ++++++++++++ ..._service_public_success_20004021000060.yml | 100 ++++++++++++++++++ ...llable_from_service_public_concern_spec.rb | 93 ++++++++++++++++ 8 files changed, 462 insertions(+) create mode 100644 app/models/concerns/prefillable_from_service_public_concern.rb create mode 100644 app/schemas/service-public.json create mode 100644 app/services/annuaire_service_public_service.rb create mode 100644 spec/fixtures/cassettes/annuaire_service_public_failure_20004021000000.yml create mode 100644 spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml create mode 100644 spec/models/concerns/prefillable_from_service_public_concern_spec.rb diff --git a/app/models/concerns/prefillable_from_service_public_concern.rb b/app/models/concerns/prefillable_from_service_public_concern.rb new file mode 100644 index 000000000..ba421dc49 --- /dev/null +++ b/app/models/concerns/prefillable_from_service_public_concern.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module PrefillableFromServicePublicConcern + extend ActiveSupport::Concern + + included do + def prefill_from_siret + result = AnnuaireServicePublicService.new.(siret:) + # TODO: get organisme, … from API Entreprise + case result + in Dry::Monads::Success(data) + self.nom = data[:nom] if nom.blank? + self.email = data[:adresse_courriel] if email.blank? + self.telephone = data[:telephone]&.first&.dig("valeur") if telephone.blank? + self.horaires = denormalize_plage_ouverture(data[:plage_ouverture]) if horaires.blank? + self.adresse = APIGeoService.inline_service_public_address(data[:adresse]&.first) if adresse.blank? + else + # NOOP + end + + result + end + + private + + def denormalize_plage_ouverture(data) + return if data.blank? + + data.map do |range| + day_range = range.values_at('nom_jour_debut', 'nom_jour_fin').uniq.join(' au ') + + hours_range = (1..2).each_with_object([]) do |i, hours| + start_hour = range["valeur_heure_debut_#{i}"] + end_hour = range["valeur_heure_fin_#{i}"] + + if start_hour.present? && end_hour.present? + hours << "de #{format_time(start_hour)} à #{format_time(end_hour)}" + end + end + + result = day_range + result += " : #{hours_range.join(' et ')}" if hours_range.present? + result += " (#{range['commentaire']})" if range['commentaire'].present? + result + end.join("\n") + end + + def format_time(str_time) + Time.zone + .parse(str_time) + .strftime("%-H:%M") + end + end +end diff --git a/app/models/service.rb b/app/models/service.rb index 67d6ac761..487bc15af 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Service < ApplicationRecord + include PrefillableFromServicePublicConcern + has_many :procedures belongs_to :administrateur, optional: false diff --git a/app/schemas/service-public.json b/app/schemas/service-public.json new file mode 100644 index 000000000..f5ceb71c5 --- /dev/null +++ b/app/schemas/service-public.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://demarches-simplifiees.fr/service-public.schema.json", + "title": "Service Public", + "type": "object", + "properties": { + "total_count": { + "type": "integer" + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "plage_ouverture": { "type": ["string", "null"] }, + "site_internet": { "type": ["string", "null"] }, + "copyright": { "type": ["string", "null"] }, + "siren": { "type": ["string", "null"] }, + "ancien_code_pivot": { "type": ["string", "null"] }, + "reseau_social": { "type": ["string", "null"] }, + "texte_reference": { "type": ["string", "null"] }, + "partenaire": { "type": ["string", "null"] }, + "telecopie": { "type": ["string", "null"] }, + "nom": { "type": "string" }, + "siret": { "type": ["string", "null"] }, + "itm_identifiant": { "type": ["string", "null"] }, + "sigle": { "type": ["string", "null"] }, + "affectation_personne": { "type": ["string", "null"] }, + "date_modification": { "type": "string" }, + "adresse_courriel": { "type": ["string", "null"] }, + "service_disponible": { "type": ["string", "null"] }, + "organigramme": { "type": ["string", "null"] }, + "pivot": { "type": ["string", "null"] }, + "partenaire_identifiant": { "type": ["string", "null"] }, + "ancien_identifiant": { "type": ["string", "null"] }, + "id": { "type": "string" }, + "ancien_nom": { "type": ["string", "null"] }, + "commentaire_plage_ouverture": { "type": ["string", "null"] }, + "annuaire": { "type": ["string", "null"] }, + "tchat": { "type": ["string", "null"] }, + "hierarchie": { "type": ["string", "null"] }, + "categorie": { "type": "string" }, + "sve": { "type": ["string", "null"] }, + "telephone_accessible": { "type": ["string", "null"] }, + "application_mobile": { "type": ["string", "null"] }, + "version_type": { "type": "string" }, + "type_repertoire": { "type": ["string", "null"] }, + "telephone": { "type": ["string", "null"] }, + "version_etat_modification": { "type": ["string", "null"] }, + "date_creation": { "type": "string" }, + "partenaire_date_modification": { "type": ["string", "null"] }, + "mission": { "type": ["string", "null"] }, + "formulaire_contact": { "type": ["string", "null"] }, + "version_source": { "type": ["string", "null"] }, + "type_organisme": { "type": ["string", "null"] }, + "code_insee_commune": { "type": ["string", "null"] }, + "statut_de_diffusion": { "type": ["string", "null"] }, + "adresse": { "type": ["string", "null"] }, + "url_service_public": { "type": ["string", "null"] }, + "information_complementaire": { "type": ["string", "null"] }, + "date_diffusion": { "type": ["string", "null"] } + }, + "required": [ + "id", + "nom", + "categorie", + "adresse", + "adresse_courriel", + "telephone", + "plage_ouverture" + ] + } + } + }, + "required": ["total_count", "results"] +} diff --git a/app/services/annuaire_service_public_service.rb b/app/services/annuaire_service_public_service.rb new file mode 100644 index 000000000..7c5728fd8 --- /dev/null +++ b/app/services/annuaire_service_public_service.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class AnnuaireServicePublicService + include Dry::Monads[:result] + + def call(siret:) + result = API::Client.new.call(url: url(siret), schema:, timeout: 1.second) + + case result + in Success(body:) + result = body[:results].first + + if result.present? + Success( + result.slice(:nom, :adresse, :adresse_courriel).merge( + telephone: maybe_json_parse(result[:telephone]), + plage_ouverture: maybe_json_parse(result[:plage_ouverture]), + adresse: maybe_json_parse(result[:adresse]) + ) + ) + else + Failure(API::Client::Error[:not_found, 404, false, "No result found for this SIRET."]) + end + in Failure(code:, reason:) if code.in?(401..403) + Sentry.capture_message("#{self.class.name}: #{reason} code: #{code}", extra: { siret: }) + Failure(API::Client::Error[:unauthorized, code, false, reason]) + in Failure(type: :schema, code:, reason:) + reason.errors[0].first + Sentry.capture_exception(reason, extra: { siret:, code: }) + + Failure(API::Client::Error[:schema, code, false, reason]) + else + result + end + end + + private + + def schema + JSONSchemer.schema(Rails.root.join('app/schemas/service-public.json')) + end + + def url(siret) + "https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:#{siret}" + end + + def maybe_json_parse(value) + return nil if value.blank? + + JSON.parse(value) + end +end diff --git a/app/services/api_geo_service.rb b/app/services/api_geo_service.rb index c12804c2a..a6d051e36 100644 --- a/app/services/api_geo_service.rb +++ b/app/services/api_geo_service.rb @@ -263,6 +263,21 @@ class APIGeoService end end + def inline_service_public_address(address_data) + return nil if address_data.blank? + + components = [ + address_data['numero_voie'], + address_data['complement1'], + address_data['complement2'], + address_data['service_distribution'], + address_data['code_postal'], + address_data['nom_commune'] + ].compact_blank + + components.join(' ') + end + private def code_metropole?(result) diff --git a/spec/fixtures/cassettes/annuaire_service_public_failure_20004021000000.yml b/spec/fixtures/cassettes/annuaire_service_public_failure_20004021000000.yml new file mode 100644 index 000000000..f38317634 --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_failure_20004021000000.yml @@ -0,0 +1,70 @@ +--- +http_interactions: +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:20004021000000 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches.gouv.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Mon, 07 Oct 2024 14:41:59 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '33' + X-Ratelimit-Remaining: + - '999978' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-08 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 0, "results": []}' + recorded_at: Mon, 07 Oct 2024 14:41:59 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml b/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml new file mode 100644 index 000000000..92b868dbf --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml @@ -0,0 +1,100 @@ +--- +http_interactions: +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:20004021000060 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches.gouv.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Mon, 07 Oct 2024 14:41:57 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '2573' + X-Ratelimit-Remaining: + - '999979' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-08 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 1, "results": [{"plage_ouverture": "[{\"nom_jour_debut\": + \"Lundi\", \"nom_jour_fin\": \"Jeudi\", \"valeur_heure_debut_1\": \"08:00:00\", + \"valeur_heure_fin_1\": \"12:00:00\", \"valeur_heure_debut_2\": \"13:30:00\", + \"valeur_heure_fin_2\": \"17:30:00\", \"commentaire\": \"\"}, {\"nom_jour_debut\": + \"Vendredi\", \"nom_jour_fin\": \"Vendredi\", \"valeur_heure_debut_1\": \"08:00:00\", + \"valeur_heure_fin_1\": \"12:00:00\", \"valeur_heure_debut_2\": \"\", \"valeur_heure_fin_2\": + \"\", \"commentaire\": \"\"}]", "site_internet": "[{\"libelle\": \"\", \"valeur\": + \"https://www.cc-lacsgorgesverdon.fr/\"}]", "copyright": "Direction de l''information + l\u00e9gale et administrative (Premier ministre)", "siren": "200040210", "ancien_code_pivot": + "epci-83007-01", "reseau_social": null, "texte_reference": null, "partenaire": + null, "telecopie": null, "nom": "Communaut\u00e9 de communes - Lacs et Gorges + du Verdon", "siret": "20004021000060", "itm_identifiant": "999974", "sigle": + null, "affectation_personne": null, "date_modification": "31/01/2024 14:25:10", + "adresse_courriel": "redacted@email.fr", "service_disponible": null, "organigramme": + null, "pivot": "[{\"type_service_local\": \"epci\", \"code_insee_commune\": + [\"83007\"]}]", "partenaire_identifiant": null, "ancien_identifiant": null, + "id": "3b9ce22d-f7bd-46d9-82d1-8b44c8d08e39", "ancien_nom": null, "commentaire_plage_ouverture": + null, "annuaire": null, "tchat": null, "hierarchie": null, "categorie": "SL", + "sve": null, "telephone_accessible": null, "application_mobile": null, "version_type": + "Publiable", "type_repertoire": null, "telephone": "[{\"valeur\": \"04 94 + 70 00 00\", \"description\": \"\"}]", "version_etat_modification": null, "date_creation": + "11/05/2017 11:28:41", "partenaire_date_modification": null, "mission": null, + "formulaire_contact": "https://www.cc-lacsgorgesverdon.fr/contacts-comcom", + "version_source": null, "type_organisme": null, "code_insee_commune": "83007", + "statut_de_diffusion": "true", "adresse": "[{\"type_adresse\": \"Adresse\", + \"complement1\": \"\", \"complement2\": \"\", \"numero_voie\": \"242 avenue + Albert-1er\", \"service_distribution\": \"\", \"code_postal\": \"83630\", + \"nom_commune\": \"Aups\", \"pays\": \"\", \"continent\": \"\", \"longitude\": + \"6.22516\", \"latitude\": \"43.627448\", \"accessibilite\": \"ACC\", \"note_accessibilite\": + \"ascenseur\"}]", "url_service_public": "https://lannuaire.service-public.fr/provence-alpes-cote-d-azur/var/3b9ce22d-f7bd-46d9-82d1-8b44c8d08e39", + "information_complementaire": null, "date_diffusion": null}]}' + recorded_at: Mon, 07 Oct 2024 14:41:57 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/models/concerns/prefillable_from_service_public_concern_spec.rb b/spec/models/concerns/prefillable_from_service_public_concern_spec.rb new file mode 100644 index 000000000..2475972ae --- /dev/null +++ b/spec/models/concerns/prefillable_from_service_public_concern_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe PrefillableFromServicePublicConcern, type: :model do + let(:siret) { '20004021000060' } + let(:service) { build(:service, siret:) } + + describe '#prefill_from_siret' do + let(:service) { Service.new(siret:) } + subject { service.prefill_from_siret } + context 'when API call is successful' do + it 'prefills service attributes' do + VCR.use_cassette('annuaire_service_public_success_20004021000060') do + expect(subject).to be_success + + expect(service.nom).to eq("Communauté de communes - Lacs et Gorges du Verdon") + expect(service.email).to eq("redacted@email.fr") + expect(service.telephone).to eq("04 94 70 00 00") + expect(service.horaires).to eq("Lundi au Jeudi : de 8:00 à 12:00 et de 13:30 à 17:30\nVendredi : de 8:00 à 12:00") + expect(service.adresse).to eq("242 avenue Albert-1er 83630 Aups") + end + end + + it 'does not overwrite existing attributes' do + service.nom = "Existing Name" + service.email = "existing@email.com" + + VCR.use_cassette('annuaire_service_public_success_20004021000060') do + service.prefill_from_siret + + expect(service.nom).to eq("Existing Name") + expect(service.email).to eq("existing@email.com") + end + end + end + + context 'when API call do not find siret' do + let(:siret) { '20004021000000' } + it 'returns a failure result' do + VCR.use_cassette('annuaire_service_public_failure_20004021000000') do + expect(subject).to be_failure + end + end + end + end + + describe '#denormalize_plage_ouverture' do + it 'correctly formats opening hours with one time range' do + data = [ + { + "nom_jour_debut" => "Lundi", + "nom_jour_fin" => "Vendredi", + "valeur_heure_debut_1" => "09:00:00", + "valeur_heure_fin_1" => "17:00:00" + } + ] + expect(service.send(:denormalize_plage_ouverture, data)).to eq("Lundi au Vendredi : de 9:00 à 17:00") + end + + it 'correctly formats opening hours with two time ranges' do + data = [ + { + "nom_jour_debut" => "Lundi", + "nom_jour_fin" => "Jeudi", + "valeur_heure_debut_1" => "08:00:00", + "valeur_heure_fin_1" => "12:00:00", + "valeur_heure_debut_2" => "13:30:00", + "valeur_heure_fin_2" => "17:30:00" + }, { + "nom_jour_debut" => "Vendredi", + "nom_jour_fin" => "Vendredi", + "valeur_heure_debut_1" => "08:00:00", + "valeur_heure_fin_1" => "12:00:00" + } + ] + expect(service.send(:denormalize_plage_ouverture, data)).to eq("Lundi au Jeudi : de 8:00 à 12:00 et de 13:30 à 17:30\nVendredi : de 8:00 à 12:00") + end + + it 'includes comments when present' do + data = [ + { + "nom_jour_debut" => "Lundi", + "nom_jour_fin" => "Vendredi", + "valeur_heure_debut_1" => "09:00:00", + "valeur_heure_fin_1" => "17:00:00", + "commentaire" => "Fermé les jours fériés" + } + ] + expect(service.send(:denormalize_plage_ouverture, data)).to eq("Lundi au Vendredi : de 9:00 à 17:00 (Fermé les jours fériés)") + end + end +end From 21dc77e587a541729fa8c4f64f32b217630d47bf Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 7 Oct 2024 18:10:37 +0200 Subject: [PATCH 3/8] fix(input): don't show success feedback when value was not set --- app/components/dsfr/input_component.rb | 2 ++ app/components/dsfr/input_errorable.rb | 6 +++--- .../editable_champ/champ_label_component.rb | 2 ++ .../editable_champ/champ_label_content_component.rb | 2 ++ .../editable_champ/editable_champ_base_component.rb | 12 +++++++----- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/components/dsfr/input_component.rb b/app/components/dsfr/input_component.rb index feb1a40c2..39ddd0020 100644 --- a/app/components/dsfr/input_component.rb +++ b/app/components/dsfr/input_component.rb @@ -6,6 +6,8 @@ class Dsfr::InputComponent < ApplicationComponent delegate :object, to: :@form delegate :errors, to: :object + attr_reader :attribute + # use it to indicate detailed about the inputs, ex: https://www.systeme-de-design.gouv.fr/elements-d-interface/modeles-et-blocs-fonctionnels/demande-de-mot-de-passe # it uses aria-describedby on input and link it to yielded content renders_one :describedby diff --git a/app/components/dsfr/input_errorable.rb b/app/components/dsfr/input_errorable.rb index 2da8dee85..91fcfcde2 100644 --- a/app/components/dsfr/input_errorable.rb +++ b/app/components/dsfr/input_errorable.rb @@ -23,7 +23,7 @@ module Dsfr { "#{dsfr_group_classname}--error" => errors_on_attribute?, - "#{dsfr_group_classname}--valid" => !errors_on_attribute? && errors_on_another_attribute? + "#{dsfr_group_classname}--valid" => !errors_on_attribute? && errors_on_another_attribute? && object.public_send(attribute).present? } end @@ -51,9 +51,9 @@ module Dsfr def attribute_or_rich_body case @input_type when :rich_text_area - @attribute.to_s.sub(/\Arich_/, '').to_sym + attribute.to_s.sub(/\Arich_/, '').to_sym else - @attribute + attribute end end diff --git a/app/components/editable_champ/champ_label_component.rb b/app/components/editable_champ/champ_label_component.rb index fac9e4c28..3143ad276 100644 --- a/app/components/editable_champ/champ_label_component.rb +++ b/app/components/editable_champ/champ_label_component.rb @@ -3,6 +3,8 @@ class EditableChamp::ChampLabelComponent < ApplicationComponent include Dsfr::InputErrorable + attr_reader :attribute + def initialize(form:, champ:, seen_at: nil) @form, @champ, @seen_at = form, champ, seen_at @attribute = :value diff --git a/app/components/editable_champ/champ_label_content_component.rb b/app/components/editable_champ/champ_label_content_component.rb index d373b240f..94978c7b0 100644 --- a/app/components/editable_champ/champ_label_content_component.rb +++ b/app/components/editable_champ/champ_label_content_component.rb @@ -4,6 +4,8 @@ class EditableChamp::ChampLabelContentComponent < ApplicationComponent include ApplicationHelper include Dsfr::InputErrorable + attr_reader :attribute + def initialize(form:, champ:, seen_at: nil) @form, @champ, @seen_at = form, champ, seen_at @attribute = :value diff --git a/app/components/editable_champ/editable_champ_base_component.rb b/app/components/editable_champ/editable_champ_base_component.rb index 9d74d9698..5acb853c1 100644 --- a/app/components/editable_champ/editable_champ_base_component.rb +++ b/app/components/editable_champ/editable_champ_base_component.rb @@ -3,6 +3,13 @@ class EditableChamp::EditableChampBaseComponent < ApplicationComponent include Dsfr::InputErrorable + attr_reader :attribute + + def initialize(form:, champ:, seen_at: nil, opts: {}) + @form, @champ, @seen_at, @opts = form, champ, seen_at, opts + @attribute = :value + end + def dsfr_champ_container :div end @@ -14,9 +21,4 @@ class EditableChamp::EditableChampBaseComponent < ApplicationComponent def describedby_id @champ.describedby_id end - - def initialize(form:, champ:, seen_at: nil, opts: {}) - @form, @champ, @seen_at, @opts = form, champ, seen_at, opts - @attribute = :value - end end From 68cca713185e3b32487db2bc3bfd4359cbf61cbf Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 14 Oct 2024 22:25:08 +0200 Subject: [PATCH 4/8] feat(service): prefill contact information UI interactions --- .../administrateurs/services_controller.rb | 24 ++++++++- .../controllers/autosave_controller.ts | 5 +- .../administrateurs/services/_form.html.haml | 29 +++++++---- .../administrateurs/services/edit.html.haml | 2 +- .../administrateurs/services/new.html.haml | 2 +- config/locales/models/service/en.yml | 7 +-- config/locales/models/service/fr.yml | 7 +-- .../services_controller_spec.rb | 52 +++++++++++++++++-- 8 files changed, 104 insertions(+), 24 deletions(-) diff --git a/app/controllers/administrateurs/services_controller.rb b/app/controllers/administrateurs/services_controller.rb index 6ca2af356..ba87ccdef 100644 --- a/app/controllers/administrateurs/services_controller.rb +++ b/app/controllers/administrateurs/services_controller.rb @@ -18,7 +18,9 @@ module Administrateurs @service = Service.new(service_params) @service.administrateur = current_administrateur - if @service.save + if request.xhr? && params[:service][:siret].present? + handle_siret_update + elsif @service.save @service.enqueue_api_entreprise redirect_to admin_services_path(procedure_id: params[:procedure_id]), @@ -108,5 +110,25 @@ module Administrateurs def procedure current_administrateur.procedures.find(params[:procedure_id]) end + + def handle_siret_update + @service.assign_attributes(siret: params[:service][:siret]) + @service.validate + + if !@service.errors.include?(:siret) + result = @service.prefill_from_siret + prefilled = result.success? ? :success : :failure + end + + siret_errors = @service.errors.where(:siret) + @service.errors.clear + siret_errors.each { @service.errors.import(_1) } + + render turbo_stream: turbo_stream.replace( + "service_form", + partial: "administrateurs/services/form", + locals: { service: @service, prefilled:, procedure: @procedure } + ) + end end end diff --git a/app/javascript/controllers/autosave_controller.ts b/app/javascript/controllers/autosave_controller.ts index 0a3cc0570..96aca094d 100644 --- a/app/javascript/controllers/autosave_controller.ts +++ b/app/javascript/controllers/autosave_controller.ts @@ -252,7 +252,10 @@ export class AutosaveController extends ApplicationController { return httpRequest(form.action, { method: 'post', body: formData, - headers: { 'x-http-method-override': 'PATCH' }, + headers: { + 'x-http-method-override': + form.dataset.turboMethod?.toUpperCase() || 'PATCH' + }, signal: this.#abortController.signal, timeout: AUTOSAVE_TIMEOUT_DELAY }).turbo(); diff --git a/app/views/administrateurs/services/_form.html.haml b/app/views/administrateurs/services/_form.html.haml index 2ac6fb5ff..6386655b2 100644 --- a/app/views/administrateurs/services/_form.html.haml +++ b/app/views/administrateurs/services/_form.html.haml @@ -1,11 +1,21 @@ -= form_with model: [ :admin, service], local: true do |f| += form_with model: [:admin, service], id: "service_form", data: { turbo: token_list('true' => service.new_record?), controller: token_list('autosave' => service.new_record?), turbo_method: 'post' } do |f| = render Dsfr::InputComponent.new(form: f, attribute: :siret, input_type: :text_field, opts: { placeholder: "14 chiffres, sans espace" }) do |c| - - c.with_hint do - = "Indiquez le numéro de SIRET de l’organisme dont ce service dépend. Rechercher le SIRET sur " - = link_to("annuaire-entreprises.data.gouv.fr", annuaire_link, **external_link_attributes) - %br - = "Nous préremplirons les informations de contact à partir de l’Annuaire Service Public correspondant." + - if service.etablissement_infos.blank? && local_assigns[:prefilled].nil? + - c.with_hint do + = "Indiquez le numéro de SIRET de l’organisme dont ce service dépend. Rechercher le SIRET sur " + = link_to("annuaire-entreprises.data.gouv.fr", annuaire_link, **external_link_attributes) + - if service.new_record? + %br + = "Nous préremplirons les informations de contact à partir de l’Annuaire Service Public correspondant." + + .fr-mb-2w + - if local_assigns[:prefilled] == :success + %p.fr-info-text Génial ! Les informations du service ont été préremplies ci-dessous. Vérifiez-les et complétez-les le cas échéant. + - elsif local_assigns[:prefilled] == :failure + %p.fr-error-text + Une erreur a empêché le préremplissage des informations. + Vérifiez que le numéro de SIRET est correct et complétez les informations manuellement le cas échéant. = render Dsfr::InputComponent.new(form: f, attribute: :nom, input_type: :text_field) @@ -33,7 +43,6 @@ = render Dsfr::InputComponent.new(form: f, attribute: :horaires, input_type: :text_area) = render Dsfr::InputComponent.new(form: f, attribute: :adresse, input_type: :text_area) - - if procedure_id.present? - = hidden_field_tag :procedure_id, procedure_id - - = render Procedure::FixedFooterComponent.new(procedure: @procedure, form: f) + - if local_assigns[:procedure].present? + = hidden_field_tag :procedure_id, procedure.id + = render Procedure::FixedFooterComponent.new(procedure: procedure, form: f) diff --git a/app/views/administrateurs/services/edit.html.haml b/app/views/administrateurs/services/edit.html.haml index 0b056372c..3cd11f6fc 100644 --- a/app/views/administrateurs/services/edit.html.haml +++ b/app/views/administrateurs/services/edit.html.haml @@ -23,4 +23,4 @@ %p.mt-3 Si vous souhaitez modifier uniquement les informations pour ce service, créez un nouveau service puis associez-le à la démarche = render partial: 'form', - locals: { service: @service, procedure_id: @procedure.id } + locals: { service: @service, procedure: @procedure } diff --git a/app/views/administrateurs/services/new.html.haml b/app/views/administrateurs/services/new.html.haml index 691b864e7..27a39f2b5 100644 --- a/app/views/administrateurs/services/new.html.haml +++ b/app/views/administrateurs/services/new.html.haml @@ -8,4 +8,4 @@ %h1 Nouveau Service = render partial: 'form', - locals: { service: @service, procedure_id: @procedure.id } + locals: { service: @service, procedure: @procedure } diff --git a/config/locales/models/service/en.yml b/config/locales/models/service/en.yml index a28cd43a5..e9da428d0 100644 --- a/config/locales/models/service/en.yml +++ b/config/locales/models/service/en.yml @@ -14,6 +14,7 @@ en: service: attributes: siret: - format: "SIRET number %{message}" - length: "must contain exactly 14 digits" - checksum: "is invalid" + format: 'SIRET number %{message}' + length: 'must contain exactly 14 digits' + checksum: 'is invalid' + not_prefillable: 'Unable to pre-fill information for this SIRET, please fill it manually' diff --git a/config/locales/models/service/fr.yml b/config/locales/models/service/fr.yml index 1abe6afdb..40c35197c 100644 --- a/config/locales/models/service/fr.yml +++ b/config/locales/models/service/fr.yml @@ -34,9 +34,10 @@ fr: service: attributes: siret: - format: "Le numéro SIRET %{message}" - length: "doit comporter exactement 14 chiffres" - checksum: "est invalide" + format: 'Le numéro SIRET %{message}' + length: 'doit comporter exactement 14 chiffres' + checksum: 'est invalide' + not_prefillable: 'Impossible de préremplir les informations pour ce SIRET, veuillez les saisir manuellement' type_organisme: administration_centrale: 'Administration centrale' association: 'Association' diff --git a/spec/controllers/administrateurs/services_controller_spec.rb b/spec/controllers/administrateurs/services_controller_spec.rb index 380d23787..fbfb334f4 100644 --- a/spec/controllers/administrateurs/services_controller_spec.rb +++ b/spec/controllers/administrateurs/services_controller_spec.rb @@ -7,7 +7,47 @@ describe Administrateurs::ServicesController, type: :controller do describe '#create' do before do sign_in(admin.user) - post :create, params: params + end + + let(:xhr) { false } + subject { post :create, params:, xhr: } + + context 'when prefilling from a SIRET' do + let(:xhr) { true } + let(:params) do + { + procedure_id: procedure.id, + service: { siret: "20004021000060" } + } + end + + it "prefill from annuaire public" do + VCR.use_cassette('annuaire_service_public_success_20004021000060') do + subject + expect(response.body).to include('turbo-stream') + expect(assigns[:service].nom).to eq("Communauté de communes - Lacs et Gorges du Verdon") + expect(assigns[:service].adresse).to eq("242 avenue Albert-1er 83630 Aups") + end + end + end + + context 'when attempting to prefilling from unknown SIRET' do + let(:xhr) { true } + let(:params) do + { + procedure_id: procedure.id, + service: { siret: "20004021000000" } + } + end + + it "render an error" do + VCR.use_cassette('annuaire_service_public_failure_20004021000000') do + subject + expect(response.body).to include('turbo-stream') + expect(assigns[:service].nom).to be_nil + expect(assigns[:service].errors.key?(:siret)).to be_present + end + end end context 'when submitting a new service' do @@ -28,6 +68,7 @@ describe Administrateurs::ServicesController, type: :controller do end it do + subject expect(flash.alert).to be_nil expect(flash.notice).to eq('super service créé') expect(Service.last.nom).to eq('super service') @@ -47,9 +88,12 @@ describe Administrateurs::ServicesController, type: :controller do context 'when submitting an invalid service' do let(:params) { { service: { nom: 'super service' }, procedure_id: procedure.id } } - it { expect(flash.alert).not_to be_nil } - it { expect(response).to render_template(:new) } - it { expect(assigns(:service).nom).to eq('super service') } + it do + subject + expect(flash.alert).not_to be_nil + expect(response).to render_template(:new) + expect(assigns(:service).nom).to eq('super service') + end end end From 065d380b701c2d6b158e6f2841a333fa5dca0940 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 15 Oct 2024 18:26:19 +0200 Subject: [PATCH 5/8] feat(service): prefill type organisme from API Entreprise --- .../administrateurs/services_controller.rb | 10 +- ...prefillable_from_service_public_concern.rb | 37 +++++- .../administrateurs/services/_form.html.haml | 8 +- .../services_controller_spec.rb | 26 +++- ..._service_public_success_11004601800013.yml | 116 +++++++++++++++++ ..._service_public_success_19750664500013.yml | 120 +++++++++++++++++ ..._service_public_success_20004021000060.yml | 46 +++++++ ..._service_public_success_35600082800018.yml | 122 ++++++++++++++++++ ..._service_public_success_41816609600051.yml | 119 +++++++++++++++++ ...llable_from_service_public_concern_spec.rb | 46 ++++++- 10 files changed, 635 insertions(+), 15 deletions(-) create mode 100644 spec/fixtures/cassettes/annuaire_service_public_success_11004601800013.yml create mode 100644 spec/fixtures/cassettes/annuaire_service_public_success_19750664500013.yml create mode 100644 spec/fixtures/cassettes/annuaire_service_public_success_35600082800018.yml create mode 100644 spec/fixtures/cassettes/annuaire_service_public_success_41816609600051.yml diff --git a/app/controllers/administrateurs/services_controller.rb b/app/controllers/administrateurs/services_controller.rb index ba87ccdef..c025a0ff1 100644 --- a/app/controllers/administrateurs/services_controller.rb +++ b/app/controllers/administrateurs/services_controller.rb @@ -116,8 +116,14 @@ module Administrateurs @service.validate if !@service.errors.include?(:siret) - result = @service.prefill_from_siret - prefilled = result.success? ? :success : :failure + prefilled = case @service.prefill_from_siret + in [Dry::Monads::Result::Success, Dry::Monads::Result::Success] + :success + in [Dry::Monads::Result::Failure, Dry::Monads::Result::Success] | [Dry::Monads::Result::Success, Dry::Monads::Result::Failure] + :partial + else + :failure + end end siret_errors = @service.errors.where(:siret) diff --git a/app/models/concerns/prefillable_from_service_public_concern.rb b/app/models/concerns/prefillable_from_service_public_concern.rb index ba421dc49..4d7fb5f27 100644 --- a/app/models/concerns/prefillable_from_service_public_concern.rb +++ b/app/models/concerns/prefillable_from_service_public_concern.rb @@ -5,9 +5,9 @@ module PrefillableFromServicePublicConcern included do def prefill_from_siret - result = AnnuaireServicePublicService.new.(siret:) - # TODO: get organisme, … from API Entreprise - case result + result_sp = AnnuaireServicePublicService.new.(siret:) + + case result_sp in Dry::Monads::Success(data) self.nom = data[:nom] if nom.blank? self.email = data[:adresse_courriel] if email.blank? @@ -18,7 +18,19 @@ module PrefillableFromServicePublicConcern # NOOP end - result + result_api_ent = APIRechercheEntreprisesService.new.call(siret:) + case result_api_ent + in Dry::Monads::Success(data) + self.type_organisme = detect_type_organisme(data) if type_organisme.blank? + + # some services (etablissements, …) are not in service public, so we also try to prefill them with API Entreprise + self.nom = data[:nom_complet] if nom.blank? + self.adresse = data.dig(:siege, :geo_adresse) if adresse.blank? + else + # NOOP + end + + [result_sp, result_api_ent] end private @@ -45,6 +57,23 @@ module PrefillableFromServicePublicConcern end.join("\n") end + def detect_type_organisme(data) + # Cf https://recherche-entreprises.api.gouv.fr/docs/#tag/Recherche-textuelle/paths/~1search/get + type = if data.dig(:complements, :collectivite_territoriale).present? + :collectivite_territoriale + elsif data.dig(:complements, :est_association) + :association + elsif data[:section_activite_principale] == "P" + :etablissement_enseignement + elsif data[:nom_complet].match?(/MINISTERE|MINISTERIEL/) + :administration_centrale + else # we can't differentiate between operateur d'état, administration centrale and service déconcentré de l'état, set the most frequent + :service_deconcentre_de_l_etat + end + + Service.type_organismes[type] + end + def format_time(str_time) Time.zone .parse(str_time) diff --git a/app/views/administrateurs/services/_form.html.haml b/app/views/administrateurs/services/_form.html.haml index 6386655b2..f9791c198 100644 --- a/app/views/administrateurs/services/_form.html.haml +++ b/app/views/administrateurs/services/_form.html.haml @@ -11,7 +11,10 @@ .fr-mb-2w - if local_assigns[:prefilled] == :success - %p.fr-info-text Génial ! Les informations du service ont été préremplies ci-dessous. Vérifiez-les et complétez-les le cas échéant. + %p.fr-info-text Génial ! La plupart des informations du service ont été préremplies ci-dessous. Vérifiez-les et complétez-les le cas échéant. + - elsif local_assigns[:prefilled] == :partial + %p.fr-info-text + Nous avons prérempli certaines informations correspondant à ce SIRET. Complétez les autres manuellement. - elsif local_assigns[:prefilled] == :failure %p.fr-error-text Une erreur a empêché le préremplissage des informations. @@ -24,8 +27,9 @@ .fr-input-group = f.label :type_organisme, class: "fr-label" do Type d’organisme + = render EditableChamp::AsteriskMandatoryComponent.new - = f.select :type_organisme, Service.type_organismes.keys.map { |key| [ I18n.t("type_organisme.#{key}"), key] }, {}, class: 'fr-select' + = f.select :type_organisme, Service.type_organismes.keys.map { |key| [ I18n.t("type_organisme.#{key}"), key] }, { include_blank: true }, { class: "fr-select" , required: true } = render Dsfr::CalloutComponent.new(title: "Informations de contact") do |c| - c.with_body do diff --git a/spec/controllers/administrateurs/services_controller_spec.rb b/spec/controllers/administrateurs/services_controller_spec.rb index fbfb334f4..510086688 100644 --- a/spec/controllers/administrateurs/services_controller_spec.rb +++ b/spec/controllers/administrateurs/services_controller_spec.rb @@ -31,7 +31,7 @@ describe Administrateurs::ServicesController, type: :controller do end end - context 'when attempting to prefilling from unknown SIRET' do + context 'when attempting to prefilling from invalid SIRET' do let(:xhr) { true } let(:params) do { @@ -41,11 +41,29 @@ describe Administrateurs::ServicesController, type: :controller do end it "render an error" do - VCR.use_cassette('annuaire_service_public_failure_20004021000000') do + subject + expect(response.body).to include('turbo-stream') + expect(assigns[:service].nom).to be_nil + expect(assigns[:service].errors.key?(:siret)).to be_present + end + end + + context 'when attempting to prefilling from not service public SIRET' do + let(:xhr) { true } + let(:params) do + { + procedure_id: procedure.id, + service: { siret: "41816609600051" } + } + end + + it "render partial information" do + VCR.use_cassette('annuaire_service_public_success_41816609600051') do subject expect(response.body).to include('turbo-stream') - expect(assigns[:service].nom).to be_nil - expect(assigns[:service].errors.key?(:siret)).to be_present + expect(assigns[:service].nom).to eq("OCTO-TECHNOLOGY") + expect(assigns[:service].horaires).to be_nil + expect(assigns[:service].errors.key?(:siret)).not_to be_present end end end diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_11004601800013.yml b/spec/fixtures/cassettes/annuaire_service_public_success_11004601800013.yml new file mode 100644 index 000000000..36a6adc39 --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_success_11004601800013.yml @@ -0,0 +1,116 @@ +--- +http_interactions: +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:11004601800013 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Tue, 15 Oct 2024 16:24:17 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '33' + X-Ratelimit-Remaining: + - '999918' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-16 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 0, "results": []}' + recorded_at: Tue, 15 Oct 2024 16:24:17 GMT +- request: + method: get + uri: https://recherche-entreprises.api.gouv.fr/search?q=11004601800013 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - nginx/1.27.1 + Date: + - Tue, 15 Oct 2024 16:24:17 GMT + Content-Type: + - application/json + Content-Length: + - '3289' + Vary: + - Accept-Encoding + - Accept-Encoding + Annuaire-Entreprises-Instance-Number: + - '02' + X-Frame-Options: + - DENY + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + Access-Control-Allow-Headers: + - Content-Type + Access-Control-Allow-Origin: + - "*" + body: + encoding: ASCII-8BIT + string: !binary |- + eyJyZXN1bHRzIjpbeyJzaXJlbiI6IjExMDA0NjAxOCIsIm5vbV9jb21wbGV0IjoiTUlOSVNURVJFIERFIExBIENVTFRVUkUiLCJub21fcmFpc29uX3NvY2lhbGUiOiJNSU5JU1RFUkUgREUgTEEgQ1VMVFVSRSIsInNpZ2xlIjpudWxsLCJub21icmVfZXRhYmxpc3NlbWVudHMiOjMsIm5vbWJyZV9ldGFibGlzc2VtZW50c19vdXZlcnRzIjoxLCJzaWVnZSI6eyJhY3Rpdml0ZV9wcmluY2lwYWxlIjoiODQuMTFaIiwiYWN0aXZpdGVfcHJpbmNpcGFsZV9yZWdpc3RyZV9tZXRpZXIiOm51bGwsImFubmVlX3RyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6IjIwMjIiLCJhZHJlc3NlIjoiMTgyIFJVRSBTQUlOVC1IT05PUkUgNzUwMDEgUEFSSVMiLCJjYXJhY3RlcmVfZW1wbG95ZXVyIjoiTiIsImNlZGV4IjpudWxsLCJjb2RlX3BheXNfZXRyYW5nZXIiOm51bGwsImNvZGVfcG9zdGFsIjoiNzUwMDEiLCJjb21tdW5lIjoiNzUxMDEiLCJjb21wbGVtZW50X2FkcmVzc2UiOm51bGwsImNvb3Jkb25uZWVzIjoiNDguODYyMzk4LDIuMzM4OTAzIiwiZGF0ZV9jcmVhdGlvbiI6IjE5ODMtMDMtMDEiLCJkYXRlX2RlYnV0X2FjdGl2aXRlIjoiMjAwOC0wMS0wMSIsImRhdGVfZmVybWV0dXJlIjpudWxsLCJkYXRlX21pc2VfYV9qb3VyIjpudWxsLCJkYXRlX21pc2VfYV9qb3VyX2luc2VlIjoiMjAyNC0wMy0zMFQxMTozMDowNiIsImRlcGFydGVtZW50IjoiNzUiLCJkaXN0cmlidXRpb25fc3BlY2lhbGUiOm51bGwsImVwY2kiOiIyMDAwNTQ3ODEiLCJlc3Rfc2llZ2UiOnRydWUsImV0YXRfYWRtaW5pc3RyYXRpZiI6IkEiLCJnZW9fYWRyZXNzZSI6IjE4MiBSdWUgU2FpbnQtSG9ub3LDqSA3NTAwMSBQYXJpcyIsImdlb19pZCI6Ijc1MTAxXzg2MzVfMDAxODIiLCJpbmRpY2VfcmVwZXRpdGlvbiI6bnVsbCwibGF0aXR1ZGUiOiI0OC44NjIzOTgiLCJsaWJlbGxlX2NlZGV4IjpudWxsLCJsaWJlbGxlX2NvbW11bmUiOiJQQVJJUyIsImxpYmVsbGVfY29tbXVuZV9ldHJhbmdlciI6bnVsbCwibGliZWxsZV9wYXlzX2V0cmFuZ2VyIjpudWxsLCJsaWJlbGxlX3ZvaWUiOiJTQUlOVC1IT05PUkUiLCJsaXN0ZV9lbnNlaWduZXMiOm51bGwsImxpc3RlX2ZpbmVzcyI6bnVsbCwibGlzdGVfaWRfYmlvIjpudWxsLCJsaXN0ZV9pZGNjIjpudWxsLCJsaXN0ZV9pZF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpudWxsLCJsaXN0ZV9yZ2UiOm51bGwsImxpc3RlX3VhaSI6bnVsbCwibG9uZ2l0dWRlIjoiMi4zMzg5MDMiLCJub21fY29tbWVyY2lhbCI6bnVsbCwibnVtZXJvX3ZvaWUiOiIxODIiLCJyZWdpb24iOiIxMSIsInNpcmV0IjoiMTEwMDQ2MDE4MDAwMTMiLCJzdGF0dXRfZGlmZnVzaW9uX2V0YWJsaXNzZW1lbnQiOiJPIiwidHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjoiMjIiLCJ0eXBlX3ZvaWUiOiJSVUUifSwiYWN0aXZpdGVfcHJpbmNpcGFsZSI6Ijg0LjExWiIsImNhdGVnb3JpZV9lbnRyZXByaXNlIjoiUE1FIiwiY2FyYWN0ZXJlX2VtcGxveWV1ciI6bnVsbCwiYW5uZWVfY2F0ZWdvcmllX2VudHJlcHJpc2UiOiIyMDIyIiwiZGF0ZV9jcmVhdGlvbiI6IjE5ODEtMDYtMjMiLCJkYXRlX2Zlcm1ldHVyZSI6bnVsbCwiZGF0ZV9taXNlX2Ffam91ciI6IjIwMjQtMTAtMTRUMTQ6NTk6NDkiLCJkYXRlX21pc2VfYV9qb3VyX2luc2VlIjoiMjAyNC0wOS0yN1QxMToxMjoyOSIsImRhdGVfbWlzZV9hX2pvdXJfcm5lIjpudWxsLCJkaXJpZ2VhbnRzIjpbXSwiZXRhdF9hZG1pbmlzdHJhdGlmIjoiQSIsIm5hdHVyZV9qdXJpZGlxdWUiOiI3MTEzIiwic2VjdGlvbl9hY3Rpdml0ZV9wcmluY2lwYWxlIjoiTyIsInRyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6IjIyIiwiYW5uZWVfdHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjoiMjAyMiIsInN0YXR1dF9kaWZmdXNpb24iOiJPIiwibWF0Y2hpbmdfZXRhYmxpc3NlbWVudHMiOlt7ImFjdGl2aXRlX3ByaW5jaXBhbGUiOiI4NC4xMVoiLCJhbmNpZW5fc2llZ2UiOmZhbHNlLCJhbm5lZV90cmFuY2hlX2VmZmVjdGlmX3NhbGFyaWUiOiIyMDIyIiwiYWRyZXNzZSI6IjE4MiBSVUUgU0FJTlQtSE9OT1JFIDc1MDAxIFBBUklTIiwiY2FyYWN0ZXJlX2VtcGxveWV1ciI6Ik4iLCJjb2RlX3Bvc3RhbCI6Ijc1MDAxIiwiY29tbXVuZSI6Ijc1MTAxIiwiZGF0ZV9jcmVhdGlvbiI6IjE5ODMtMDMtMDEiLCJkYXRlX2RlYnV0X2FjdGl2aXRlIjoiMjAwOC0wMS0wMSIsImRhdGVfZmVybWV0dXJlIjpudWxsLCJlcGNpIjoiMjAwMDU0NzgxIiwiZXN0X3NpZWdlIjp0cnVlLCJldGF0X2FkbWluaXN0cmF0aWYiOiJBIiwiZ2VvX2lkIjoiNzUxMDFfODYzNV8wMDE4MiIsImxhdGl0dWRlIjoiNDguODYyMzk4IiwibGliZWxsZV9jb21tdW5lIjoiUEFSSVMiLCJsaXN0ZV9lbnNlaWduZXMiOm51bGwsImxpc3RlX2ZpbmVzcyI6bnVsbCwibGlzdGVfaWRfYmlvIjpudWxsLCJsaXN0ZV9pZGNjIjpudWxsLCJsaXN0ZV9pZF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpudWxsLCJsaXN0ZV9yZ2UiOm51bGwsImxpc3RlX3VhaSI6bnVsbCwibG9uZ2l0dWRlIjoiMi4zMzg5MDMiLCJub21fY29tbWVyY2lhbCI6bnVsbCwicmVnaW9uIjoiMTEiLCJzaXJldCI6IjExMDA0NjAxODAwMDEzIiwic3RhdHV0X2RpZmZ1c2lvbl9ldGFibGlzc2VtZW50IjoiTyIsInRyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6IjIyIn1dLCJmaW5hbmNlcyI6bnVsbCwiY29tcGxlbWVudHMiOnsiY29sbGVjdGl2aXRlX3RlcnJpdG9yaWFsZSI6bnVsbCwiY29udmVudGlvbl9jb2xsZWN0aXZlX3JlbnNlaWduZWUiOmZhbHNlLCJsaXN0ZV9pZGNjIjpudWxsLCJlZ2Fwcm9fcmVuc2VpZ25lZSI6ZmFsc2UsImVzdF9hc3NvY2lhdGlvbiI6ZmFsc2UsImVzdF9iaW8iOmZhbHNlLCJlc3RfZW50cmVwcmVuZXVyX2luZGl2aWR1ZWwiOmZhbHNlLCJlc3RfZW50cmVwcmVuZXVyX3NwZWN0YWNsZSI6ZmFsc2UsImVzdF9lc3MiOmZhbHNlLCJlc3RfZmluZXNzIjpmYWxzZSwiZXN0X29yZ2FuaXNtZV9mb3JtYXRpb24iOnRydWUsImVzdF9xdWFsaW9waSI6ZmFsc2UsImxpc3RlX2lkX29yZ2FuaXNtZV9mb3JtYXRpb24iOlsiMTE3NTU2MDg5NzUiXSwiZXN0X3JnZSI6ZmFsc2UsImVzdF9zZXJ2aWNlX3B1YmxpYyI6dHJ1ZSwiZXN0X3NpYWUiOmZhbHNlLCJlc3Rfc29jaWV0ZV9taXNzaW9uIjpmYWxzZSwiZXN0X3VhaSI6ZmFsc2UsImlkZW50aWZpYW50X2Fzc29jaWF0aW9uIjpudWxsLCJzdGF0dXRfZW50cmVwcmVuZXVyX3NwZWN0YWNsZSI6bnVsbCwidHlwZV9zaWFlIjpudWxsfX1dLCJ0b3RhbF9yZXN1bHRzIjoxLCJwYWdlIjoxLCJwZXJfcGFnZSI6MTAsInRvdGFsX3BhZ2VzIjoxfQ== + recorded_at: Tue, 15 Oct 2024 16:24:17 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_19750664500013.yml b/spec/fixtures/cassettes/annuaire_service_public_success_19750664500013.yml new file mode 100644 index 000000000..66250c90b --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_success_19750664500013.yml @@ -0,0 +1,120 @@ +--- +http_interactions: +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:19750664500013 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Tue, 15 Oct 2024 14:57:11 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '33' + X-Ratelimit-Remaining: + - '999997' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-16 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 0, "results": []}' + recorded_at: Tue, 15 Oct 2024 14:57:11 GMT +- request: + method: get + uri: https://recherche-entreprises.api.gouv.fr/search?q=19750664500013 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - nginx/1.27.1 + Date: + - Tue, 15 Oct 2024 14:57:12 GMT + Content-Type: + - application/json + Content-Length: + - '3310' + Vary: + - Accept-Encoding + - Accept-Encoding + Annuaire-Entreprises-Instance-Number: + - '02' + X-Frame-Options: + - DENY + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + Access-Control-Allow-Headers: + - Content-Type + Access-Control-Allow-Origin: + - "*" + body: + encoding: ASCII-8BIT + string: '{"results":[{"siren":"197506645","nom_complet":"LYCEE GENERAL ET TECHNOLOGIQUE + RACINE","nom_raison_sociale":"LYCEE GENERAL ET TECHNOLOGIQUE RACINE","sigle":null,"nombre_etablissements":1,"nombre_etablissements_ouverts":1,"siege":{"activite_principale":"85.31Z","activite_principale_registre_metier":null,"annee_tranche_effectif_salarie":"2022","adresse":"20 + RUE DU ROCHER 75008 PARIS","caractere_employeur":"N","cedex":null,"code_pays_etranger":null,"code_postal":"75008","commune":"75108","complement_adresse":null,"coordonnees":"48.876451,2.3223","date_creation":"1983-03-01","date_debut_activite":"2008-01-01","date_fermeture":null,"date_mise_a_jour":null,"date_mise_a_jour_insee":"2024-03-30T03:25:45","departement":"75","distribution_speciale":null,"epci":"200054781","est_siege":true,"etat_administratif":"A","geo_adresse":"20 + Rue du Rocher 75008 Paris","geo_id":"75108_8291_00020","indice_repetition":null,"latitude":"48.876451","libelle_cedex":null,"libelle_commune":"PARIS","libelle_commune_etranger":null,"libelle_pays_etranger":null,"libelle_voie":"DU + ROCHER","liste_enseignes":null,"liste_finess":null,"liste_id_bio":null,"liste_idcc":["9999"],"liste_id_organisme_formation":null,"liste_rge":null,"liste_uai":["0750664P"],"longitude":"2.3223","nom_commercial":null,"numero_voie":"20","region":"11","siret":"19750664500013","statut_diffusion_etablissement":"O","tranche_effectif_salarie":"22","type_voie":"RUE"},"activite_principale":"85.31Z","categorie_entreprise":"PME","caractere_employeur":null,"annee_categorie_entreprise":"2022","date_creation":"1965-05-01","date_fermeture":null,"date_mise_a_jour":"2024-10-14T15:00:03","date_mise_a_jour_insee":"2024-03-22T14:26:06","date_mise_a_jour_rne":null,"dirigeants":[],"etat_administratif":"A","nature_juridique":"7331","section_activite_principale":"P","tranche_effectif_salarie":"22","annee_tranche_effectif_salarie":"2022","statut_diffusion":"O","matching_etablissements":[{"activite_principale":"85.31Z","ancien_siege":false,"annee_tranche_effectif_salarie":"2022","adresse":"20 + RUE DU ROCHER 75008 PARIS","caractere_employeur":"N","code_postal":"75008","commune":"75108","date_creation":"1983-03-01","date_debut_activite":"2008-01-01","date_fermeture":null,"epci":"200054781","est_siege":true,"etat_administratif":"A","geo_id":"75108_8291_00020","latitude":"48.876451","libelle_commune":"PARIS","liste_enseignes":null,"liste_finess":null,"liste_id_bio":null,"liste_idcc":["9999"],"liste_id_organisme_formation":null,"liste_rge":null,"liste_uai":["0750664P"],"longitude":"2.3223","nom_commercial":null,"region":"11","siret":"19750664500013","statut_diffusion_etablissement":"O","tranche_effectif_salarie":"22"}],"finances":null,"complements":{"collectivite_territoriale":null,"convention_collective_renseignee":true,"liste_idcc":["9999"],"egapro_renseignee":false,"est_association":false,"est_bio":false,"est_entrepreneur_individuel":false,"est_entrepreneur_spectacle":false,"est_ess":false,"est_finess":false,"est_organisme_formation":false,"est_qualiopi":false,"liste_id_organisme_formation":null,"est_rge":false,"est_service_public":true,"est_siae":false,"est_societe_mission":false,"est_uai":true,"identifiant_association":null,"statut_entrepreneur_spectacle":null,"type_siae":null}}],"total_results":1,"page":1,"per_page":10,"total_pages":1}' + recorded_at: Tue, 15 Oct 2024 14:57:11 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml b/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml index 92b868dbf..cbeeede03 100644 --- a/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml +++ b/spec/fixtures/cassettes/annuaire_service_public_success_20004021000060.yml @@ -97,4 +97,50 @@ http_interactions: \"ascenseur\"}]", "url_service_public": "https://lannuaire.service-public.fr/provence-alpes-cote-d-azur/var/3b9ce22d-f7bd-46d9-82d1-8b44c8d08e39", "information_complementaire": null, "date_diffusion": null}]}' recorded_at: Mon, 07 Oct 2024 14:41:57 GMT +- request: + method: get + uri: https://recherche-entreprises.api.gouv.fr/search?q=20004021000060 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - nginx/1.27.1 + Date: + - Tue, 15 Oct 2024 13:53:25 GMT + Content-Type: + - application/json + Content-Length: + - '6715' + Vary: + - Accept-Encoding + - Accept-Encoding + Annuaire-Entreprises-Instance-Number: + - '02' + X-Frame-Options: + - DENY + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + Access-Control-Allow-Headers: + - Content-Type + Access-Control-Allow-Origin: + - "*" + body: + encoding: ASCII-8BIT + string: !binary |- + eyJyZXN1bHRzIjpbeyJzaXJlbiI6IjIwMDA0MDIxMCIsIm5vbV9jb21wbGV0IjoiQ09NTVVOQVVURSBERSBDT01NVU5FUyBMQUNTIEVUIEdPUkdFUyBEVSBWRVJET04gKENDTEdWKSIsIm5vbV9yYWlzb25fc29jaWFsZSI6IkNPTU1VTkFVVEUgREUgQ09NTVVORVMgTEFDUyBFVCBHT1JHRVMgRFUgVkVSRE9OIiwic2lnbGUiOiJDQ0xHViIsIm5vbWJyZV9ldGFibGlzc2VtZW50cyI6Nywibm9tYnJlX2V0YWJsaXNzZW1lbnRzX291dmVydHMiOjYsInNpZWdlIjp7ImFjdGl2aXRlX3ByaW5jaXBhbGUiOiI4NC4xMVoiLCJhY3Rpdml0ZV9wcmluY2lwYWxlX3JlZ2lzdHJlX21ldGllciI6bnVsbCwiYW5uZWVfdHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjpudWxsLCJhZHJlc3NlIjoiMjQyIEFWRU5VRSBBTEJFUlQgMUVSIDgzNjMwIEFVUFMiLCJjYXJhY3RlcmVfZW1wbG95ZXVyIjoiTyIsImNlZGV4IjpudWxsLCJjb2RlX3BheXNfZXRyYW5nZXIiOm51bGwsImNvZGVfcG9zdGFsIjoiODM2MzAiLCJjb21tdW5lIjoiODMwMDciLCJjb21wbGVtZW50X2FkcmVzc2UiOm51bGwsImNvb3Jkb25uZWVzIjoiNDMuNjMwNTI0LDYuMjIxMjE4IiwiZGF0ZV9jcmVhdGlvbiI6IjIwMjMtMDQtMTMiLCJkYXRlX2RlYnV0X2FjdGl2aXRlIjoiMjAyMy0wNC0xMyIsImRhdGVfZmVybWV0dXJlIjpudWxsLCJkYXRlX21pc2VfYV9qb3VyIjpudWxsLCJkYXRlX21pc2VfYV9qb3VyX2luc2VlIjoiMjAyNC0wMy0zMFQwOTozMzo1MCIsImRlcGFydGVtZW50IjoiODMiLCJkaXN0cmlidXRpb25fc3BlY2lhbGUiOm51bGwsImVwY2kiOiIyMDAwNDAyMTAiLCJlc3Rfc2llZ2UiOnRydWUsImV0YXRfYWRtaW5pc3RyYXRpZiI6IkEiLCJnZW9fYWRyZXNzZSI6IjI0MiBBdmVudWUgQWxiZXJ0IDFlciA4MzYzMCBBdXBzIiwiZ2VvX2lkIjoiODMwMDdfMDAyMF8wMDI0MiIsImluZGljZV9yZXBldGl0aW9uIjpudWxsLCJsYXRpdHVkZSI6IjQzLjYzMDUyNCIsImxpYmVsbGVfY2VkZXgiOm51bGwsImxpYmVsbGVfY29tbXVuZSI6IkFVUFMiLCJsaWJlbGxlX2NvbW11bmVfZXRyYW5nZXIiOm51bGwsImxpYmVsbGVfcGF5c19ldHJhbmdlciI6bnVsbCwibGliZWxsZV92b2llIjoiQUxCRVJUIDFFUiIsImxpc3RlX2Vuc2VpZ25lcyI6bnVsbCwibGlzdGVfZmluZXNzIjpudWxsLCJsaXN0ZV9pZF9iaW8iOm51bGwsImxpc3RlX2lkY2MiOlsiNTAyMSJdLCJsaXN0ZV9pZF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpudWxsLCJsaXN0ZV9yZ2UiOm51bGwsImxpc3RlX3VhaSI6bnVsbCwibG9uZ2l0dWRlIjoiNi4yMjEyMTgiLCJub21fY29tbWVyY2lhbCI6bnVsbCwibnVtZXJvX3ZvaWUiOiIyNDIiLCJyZWdpb24iOiI5MyIsInNpcmV0IjoiMjAwMDQwMjEwMDAwNjAiLCJzdGF0dXRfZGlmZnVzaW9uX2V0YWJsaXNzZW1lbnQiOiJPIiwidHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjpudWxsLCJ0eXBlX3ZvaWUiOiJBVkVOVUUifSwiYWN0aXZpdGVfcHJpbmNpcGFsZSI6Ijg0LjExWiIsImNhdGVnb3JpZV9lbnRyZXByaXNlIjoiUE1FIiwiY2FyYWN0ZXJlX2VtcGxveWV1ciI6bnVsbCwiYW5uZWVfY2F0ZWdvcmllX2VudHJlcHJpc2UiOiIyMDIyIiwiZGF0ZV9jcmVhdGlvbiI6IjIwMTQtMDEtMDEiLCJkYXRlX2Zlcm1ldHVyZSI6bnVsbCwiZGF0ZV9taXNlX2Ffam91ciI6IjIwMjQtMTAtMTRUMTU6MDA6MDciLCJkYXRlX21pc2VfYV9qb3VyX2luc2VlIjoiMjAyNC0wMy0yMlQxNDoyNjowNiIsImRhdGVfbWlzZV9hX2pvdXJfcm5lIjpudWxsLCJkaXJpZ2VhbnRzIjpbXSwiZXRhdF9hZG1pbmlzdHJhdGlmIjoiQSIsIm5hdHVyZV9qdXJpZGlxdWUiOiI3MzQ2Iiwic2VjdGlvbl9hY3Rpdml0ZV9wcmluY2lwYWxlIjoiTyIsInRyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6IjIxIiwiYW5uZWVfdHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjoiMjAyMiIsInN0YXR1dF9kaWZmdXNpb24iOiJPIiwibWF0Y2hpbmdfZXRhYmxpc3NlbWVudHMiOlt7ImFjdGl2aXRlX3ByaW5jaXBhbGUiOiI4NC4xMVoiLCJhbmNpZW5fc2llZ2UiOmZhbHNlLCJhbm5lZV90cmFuY2hlX2VmZmVjdGlmX3NhbGFyaWUiOm51bGwsImFkcmVzc2UiOiIyNDIgQVZFTlVFIEFMQkVSVCAxRVIgODM2MzAgQVVQUyIsImNhcmFjdGVyZV9lbXBsb3lldXIiOiJPIiwiY29kZV9wb3N0YWwiOiI4MzYzMCIsImNvbW11bmUiOiI4MzAwNyIsImRhdGVfY3JlYXRpb24iOiIyMDIzLTA0LTEzIiwiZGF0ZV9kZWJ1dF9hY3Rpdml0ZSI6IjIwMjMtMDQtMTMiLCJkYXRlX2Zlcm1ldHVyZSI6bnVsbCwiZXBjaSI6IjIwMDA0MDIxMCIsImVzdF9zaWVnZSI6dHJ1ZSwiZXRhdF9hZG1pbmlzdHJhdGlmIjoiQSIsImdlb19pZCI6IjgzMDA3XzAwMjBfMDAyNDIiLCJsYXRpdHVkZSI6IjQzLjYzMDUyNCIsImxpYmVsbGVfY29tbXVuZSI6IkFVUFMiLCJsaXN0ZV9lbnNlaWduZXMiOm51bGwsImxpc3RlX2ZpbmVzcyI6bnVsbCwibGlzdGVfaWRfYmlvIjpudWxsLCJsaXN0ZV9pZGNjIjpbIjUwMjEiXSwibGlzdGVfaWRfb3JnYW5pc21lX2Zvcm1hdGlvbiI6bnVsbCwibGlzdGVfcmdlIjpudWxsLCJsaXN0ZV91YWkiOm51bGwsImxvbmdpdHVkZSI6IjYuMjIxMjE4Iiwibm9tX2NvbW1lcmNpYWwiOm51bGwsInJlZ2lvbiI6IjkzIiwic2lyZXQiOiIyMDAwNDAyMTAwMDA2MCIsInN0YXR1dF9kaWZmdXNpb25fZXRhYmxpc3NlbWVudCI6Ik8iLCJ0cmFuY2hlX2VmZmVjdGlmX3NhbGFyaWUiOm51bGx9XSwiZmluYW5jZXMiOm51bGwsImNvbXBsZW1lbnRzIjp7ImNvbGxlY3Rpdml0ZV90ZXJyaXRvcmlhbGUiOnsiY29kZSI6IjIwMDA0MDIxMCIsImNvZGVfaW5zZWUiOm51bGwsImVsdXMiOlt7Im5vbSI6IkRBR1VFVCIsInByZW5vbXMiOiJDYXRoZXJpbmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU3LTEyIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJGIn0seyJub20iOiJNT1JERUxFVCIsInByZW5vbXMiOiJDaGFybGVzLUFudG9pbmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTUyLTAxIiwiZm9uY3Rpb24iOiIzZW1lIFZpY2UtcHLDqXNpZGVudCBkdSBjb25zZWlsIGNvbW11bmF1dGFpcmUiLCJzZXhlIjoiTSJ9LHsibm9tIjoiQ09OU1RBTlMiLCJwcmVub21zIjoiU2VyZ2UiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTY3LTAyIiwiZm9uY3Rpb24iOiI2ZW1lIFZpY2UtcHLDqXNpZGVudCBkdSBjb25zZWlsIGNvbW11bmF1dGFpcmUiLCJzZXhlIjoiTSJ9LHsibm9tIjoiQk9OQVZFTlRVUkUiLCJwcmVub21zIjoiTWFyaWUtRnJhbsOnb2lzZSIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NjQtMDQiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6IkYifSx7Im5vbSI6IkZBVVJFIiwicHJlbm9tcyI6IkFudG9pbmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU1LTEwIiwiZm9uY3Rpb24iOiIyZW1lIFZpY2UtcHLDqXNpZGVudCBkdSBjb25zZWlsIGNvbW11bmF1dGFpcmUiLCJzZXhlIjoiTSJ9LHsibm9tIjoiUEFOVEVMIiwicHJlbm9tcyI6IkJlcm5hcmQiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTUwLTA4IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJST1VYIiwicHJlbm9tcyI6Ik1hcmzDqG5lIiwiYW5uZWVfZGVfbmFpc3NhbmNlIjoiMTk3My0wMyIsImZvbmN0aW9uIjpudWxsLCJzZXhlIjoiRiJ9LHsibm9tIjoiVEVSUkFTU09OIiwicHJlbm9tcyI6Ik1hcmllIENocmlzdGluZSIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NTYtMDEiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6IkYifSx7Im5vbSI6IlZJTkNFTlRFTExJIiwicHJlbm9tcyI6IlBhdHJpY2siLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTUxLTEwIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJBTkdMSU9OSU4iLCJwcmVub21zIjoiSm9hbm5lbCIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NzAtMDYiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6Ik0ifSx7Im5vbSI6IlJPVVgiLCJwcmVub21zIjoiSmVhbi1QYXVsIiwiYW5uZWVfZGVfbmFpc3NhbmNlIjoiMTk1My0wOSIsImZvbmN0aW9uIjpudWxsLCJzZXhlIjoiTSJ9LHsibm9tIjoiUk9VVklFUiIsInByZW5vbXMiOiJBcm1hbmQiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTYxLTAzIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJCRUxMSU5JIiwicHJlbm9tcyI6Ik5hbnMiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTg5LTA1IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJDQVJMRVRUSSIsInByZW5vbXMiOiJSYXltb25kZSIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NDYtMTIiLCJmb25jdGlvbiI6IjFlciBWaWNlLXByw6lzaWRlbnQgZHUgY29uc2VpbCBjb21tdW5hdXRhaXJlIiwic2V4ZSI6IkYifSx7Im5vbSI6IlJJQk9VTEVUIiwicHJlbm9tcyI6IkdpbGJlcnQiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU0LTExIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJCT05ORVQiLCJwcmVub21zIjoiUmVuw6kiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU1LTAyIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJEQVJSSUdPTCIsInByZW5vbXMiOiJHw6lyYXJkIiwiYW5uZWVfZGVfbmFpc3NhbmNlIjoiMTk1MC0xMiIsImZvbmN0aW9uIjpudWxsLCJzZXhlIjoiTSJ9LHsibm9tIjoiRklMSVBQSSIsInByZW5vbXMiOiJBbGFpbiIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NTAtMTEiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6Ik0ifSx7Im5vbSI6IkpFQU5ORVJFVCIsInByZW5vbXMiOiJSZW7DqWUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU4LTA4IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJGIn0seyJub20iOiJMSU9OIiwicHJlbm9tcyI6IkplYW4gUGllcnJlIiwiYW5uZWVfZGVfbmFpc3NhbmNlIjoiMTk1MS0wMyIsImZvbmN0aW9uIjpudWxsLCJzZXhlIjoiTSJ9LHsibm9tIjoiTUFUSElFVSIsInByZW5vbXMiOiJGcmFuY2siLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTc1LTEwIiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJHVUlHVUVTIiwicHJlbm9tcyI6IkRlbmlzZSIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NTktMDEiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6IkYifSx7Im5vbSI6IkJSSUVVR05FIiwicHJlbm9tcyI6IkZhYmllbiIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NTctMTIiLCJmb25jdGlvbiI6IjRlbWUgVmljZS1wcsOpc2lkZW50IGR1IGNvbnNlaWwgY29tbXVuYXV0YWlyZSIsInNleGUiOiJNIn0seyJub20iOiJHQUdMSUFOTyIsInByZW5vbXMiOiJDaHJpc3RpYW4iLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTQ5LTA3IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJMQVZBTCIsInByZW5vbXMiOiJTdMOpcGhhbmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU5LTA4IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJNVVJBVC1EQVZJRCIsInByZW5vbXMiOiJQaGlsaXBwZSIsImFubmVlX2RlX25haXNzYW5jZSI6IjE5NDgtMTAiLCJmb25jdGlvbiI6bnVsbCwic2V4ZSI6Ik0ifSx7Im5vbSI6IkJBTEJJUyIsInByZW5vbXMiOiJSb2xsYW5kIiwiYW5uZWVfZGVfbmFpc3NhbmNlIjoiMTk1Ni0wNiIsImZvbmN0aW9uIjoiUHLDqXNpZGVudCBkdSBjb25zZWlsIGNvbW11bmF1dGFpcmUiLCJzZXhlIjoiTSJ9LHsibm9tIjoiQkFTU0UiLCJwcmVub21zIjoiSmVhbi1DbGF1ZGUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTU3LTA4IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJNIn0seyJub20iOiJCT1RUQUNDSEkiLCJwcmVub21zIjoiTHlkaWUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTY4LTA5IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJGIn0seyJub20iOiJDT05TVEFOUyIsInByZW5vbXMiOiJQaWVycmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTUwLTExIiwiZm9uY3Rpb24iOiI1ZW1lIFZpY2UtcHLDqXNpZGVudCBkdSBjb25zZWlsIGNvbW11bmF1dGFpcmUiLCJzZXhlIjoiTSJ9LHsibm9tIjoiTU9NQlJJQUwtRkFZQVVCT1NUIiwicHJlbm9tcyI6Ik1hcnRpbmUiLCJhbm5lZV9kZV9uYWlzc2FuY2UiOiIxOTQ2LTA4IiwiZm9uY3Rpb24iOm51bGwsInNleGUiOiJGIn1dLCJuaXZlYXUiOiJlcGNpIn0sImNvbnZlbnRpb25fY29sbGVjdGl2ZV9yZW5zZWlnbmVlIjp0cnVlLCJsaXN0ZV9pZGNjIjpbIjUwMjEiXSwiZWdhcHJvX3JlbnNlaWduZWUiOmZhbHNlLCJlc3RfYXNzb2NpYXRpb24iOmZhbHNlLCJlc3RfYmlvIjpmYWxzZSwiZXN0X2VudHJlcHJlbmV1cl9pbmRpdmlkdWVsIjpmYWxzZSwiZXN0X2VudHJlcHJlbmV1cl9zcGVjdGFjbGUiOmZhbHNlLCJlc3RfZXNzIjpmYWxzZSwiZXN0X2ZpbmVzcyI6ZmFsc2UsImVzdF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpmYWxzZSwiZXN0X3F1YWxpb3BpIjpmYWxzZSwibGlzdGVfaWRfb3JnYW5pc21lX2Zvcm1hdGlvbiI6bnVsbCwiZXN0X3JnZSI6ZmFsc2UsImVzdF9zZXJ2aWNlX3B1YmxpYyI6dHJ1ZSwiZXN0X3NpYWUiOmZhbHNlLCJlc3Rfc29jaWV0ZV9taXNzaW9uIjpmYWxzZSwiZXN0X3VhaSI6ZmFsc2UsImlkZW50aWZpYW50X2Fzc29jaWF0aW9uIjpudWxsLCJzdGF0dXRfZW50cmVwcmVuZXVyX3NwZWN0YWNsZSI6bnVsbCwidHlwZV9zaWFlIjpudWxsfX1dLCJ0b3RhbF9yZXN1bHRzIjoxLCJwYWdlIjoxLCJwZXJfcGFnZSI6MTAsInRvdGFsX3BhZ2VzIjoxfQ== + recorded_at: Tue, 15 Oct 2024 13:53:25 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_35600082800018.yml b/spec/fixtures/cassettes/annuaire_service_public_success_35600082800018.yml new file mode 100644 index 000000000..6414d37be --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_success_35600082800018.yml @@ -0,0 +1,122 @@ +--- +http_interactions: +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:35600082800018 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Tue, 15 Oct 2024 16:24:16 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '33' + X-Ratelimit-Remaining: + - '999919' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-16 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 0, "results": []}' + recorded_at: Tue, 15 Oct 2024 16:24:16 GMT +- request: + method: get + uri: https://recherche-entreprises.api.gouv.fr/search?q=35600082800018 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - nginx/1.27.1 + Date: + - Tue, 15 Oct 2024 16:24:16 GMT + Content-Type: + - application/json + Content-Length: + - '3374' + Vary: + - Accept-Encoding + - Accept-Encoding + - Accept-Encoding + Annuaire-Entreprises-Instance-Number: + - '01' + - '02' + X-Frame-Options: + - DENY + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + Access-Control-Allow-Headers: + - Content-Type + Access-Control-Allow-Origin: + - "*" + body: + encoding: ASCII-8BIT + string: '{"results":[{"siren":"356000828","nom_complet":"LA POSTE (REGION RHONE + ALPES)","nom_raison_sociale":"LA POSTE","sigle":null,"nombre_etablissements":2948,"nombre_etablissements_ouverts":0,"siege":{"activite_principale":"53.10Z","activite_principale_registre_metier":null,"annee_tranche_effectif_salarie":null,"adresse":"4 + QUAI DU POINT DU JOUR 92100 BOULOGNE-BILLANCOURT","caractere_employeur":"N","cedex":null,"code_pays_etranger":null,"code_postal":"92100","commune":"92012","complement_adresse":null,"coordonnees":"48.832893,2.259972","date_creation":"1991-01-01","date_debut_activite":"2011-12-31","date_fermeture":"2011-12-31","date_mise_a_jour":null,"date_mise_a_jour_insee":"2024-03-22T15:40:57","departement":"92","distribution_speciale":null,"epci":"200054781","est_siege":true,"etat_administratif":"F","geo_adresse":"4 + Quai du Point du Jour 92100 Boulogne-Billancourt","geo_id":"92012_7231_00004","indice_repetition":null,"latitude":"48.832893","libelle_cedex":null,"libelle_commune":"BOULOGNE-BILLANCOURT","libelle_commune_etranger":null,"libelle_pays_etranger":null,"libelle_voie":"DU + POINT DU JOUR","liste_enseignes":null,"liste_finess":null,"liste_id_bio":null,"liste_idcc":null,"liste_id_organisme_formation":null,"liste_rge":null,"liste_uai":null,"longitude":"2.259972","nom_commercial":null,"numero_voie":"4","region":"11","siret":"35600082800018","statut_diffusion_etablissement":"O","tranche_effectif_salarie":null,"type_voie":"QUAI"},"activite_principale":"53.10Z","categorie_entreprise":null,"caractere_employeur":null,"annee_categorie_entreprise":null,"date_creation":"1991-01-01","date_fermeture":"2011-12-31","date_mise_a_jour":"2024-10-14T15:38:40","date_mise_a_jour_insee":"2024-03-22T14:26:06","date_mise_a_jour_rne":null,"dirigeants":[],"etat_administratif":"C","nature_juridique":"4130","section_activite_principale":"H","tranche_effectif_salarie":null,"annee_tranche_effectif_salarie":null,"statut_diffusion":"O","matching_etablissements":[{"activite_principale":"53.10Z","ancien_siege":false,"annee_tranche_effectif_salarie":null,"adresse":"4 + QUAI DU POINT DU JOUR 92100 BOULOGNE-BILLANCOURT","caractere_employeur":"N","code_postal":"92100","commune":"92012","date_creation":"1991-01-01","date_debut_activite":"2011-12-31","date_fermeture":"2011-12-31","epci":"200054781","est_siege":true,"etat_administratif":"F","geo_id":"92012_7231_00004","latitude":"48.832893","libelle_commune":"BOULOGNE-BILLANCOURT","liste_enseignes":null,"liste_finess":null,"liste_id_bio":null,"liste_idcc":null,"liste_id_organisme_formation":null,"liste_rge":null,"liste_uai":null,"longitude":"2.259972","nom_commercial":null,"region":"11","siret":"35600082800018","statut_diffusion_etablissement":"O","tranche_effectif_salarie":null}],"finances":null,"complements":{"collectivite_territoriale":null,"convention_collective_renseignee":false,"liste_idcc":null,"egapro_renseignee":false,"est_association":false,"est_bio":false,"est_entrepreneur_individuel":false,"est_entrepreneur_spectacle":false,"est_ess":false,"est_finess":false,"est_organisme_formation":false,"est_qualiopi":false,"liste_id_organisme_formation":null,"est_rge":false,"est_service_public":true,"est_siae":false,"est_societe_mission":false,"est_uai":false,"identifiant_association":null,"statut_entrepreneur_spectacle":null,"type_siae":null}}],"total_results":1,"page":1,"per_page":10,"total_pages":1}' + recorded_at: Tue, 15 Oct 2024 16:24:16 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/cassettes/annuaire_service_public_success_41816609600051.yml b/spec/fixtures/cassettes/annuaire_service_public_success_41816609600051.yml new file mode 100644 index 000000000..dc50ca6b5 --- /dev/null +++ b/spec/fixtures/cassettes/annuaire_service_public_success_41816609600051.yml @@ -0,0 +1,119 @@ +--- +http_interactions: +- request: + method: get + uri: https://recherche-entreprises.api.gouv.fr/search?q=41816609600051 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - nginx/1.27.1 + Date: + - Thu, 17 Oct 2024 07:51:06 GMT + Content-Type: + - application/json + Content-Length: + - '3471' + Vary: + - Accept-Encoding + - Accept-Encoding + - Accept-Encoding + Annuaire-Entreprises-Instance-Number: + - '02' + X-Frame-Options: + - DENY + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + - 1; mode=block + Access-Control-Allow-Headers: + - Content-Type + Access-Control-Allow-Origin: + - "*" + body: + encoding: ASCII-8BIT + string: !binary |- + eyJyZXN1bHRzIjpbeyJzaXJlbiI6IjQxODE2NjA5NiIsIm5vbV9jb21wbGV0IjoiT0NUTy1URUNITk9MT0dZIiwibm9tX3JhaXNvbl9zb2NpYWxlIjoiT0NUTy1URUNITk9MT0dZIiwic2lnbGUiOm51bGwsIm5vbWJyZV9ldGFibGlzc2VtZW50cyI6Nywibm9tYnJlX2V0YWJsaXNzZW1lbnRzX291dmVydHMiOjEsInNpZWdlIjp7ImFjdGl2aXRlX3ByaW5jaXBhbGUiOiI2Mi4wMkEiLCJhY3Rpdml0ZV9wcmluY2lwYWxlX3JlZ2lzdHJlX21ldGllciI6bnVsbCwiYW5uZWVfdHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjoiMjAyMiIsImFkcmVzc2UiOiIzNCBBVkVOVUUgREUgTCdPUEVSQSA3NTAwMiBQQVJJUyIsImNhcmFjdGVyZV9lbXBsb3lldXIiOiJPIiwiY2VkZXgiOm51bGwsImNvZGVfcGF5c19ldHJhbmdlciI6bnVsbCwiY29kZV9wb3N0YWwiOiI3NTAwMiIsImNvbW11bmUiOiI3NTEwMiIsImNvbXBsZW1lbnRfYWRyZXNzZSI6bnVsbCwiY29vcmRvbm5lZXMiOiI0OC44Njg2NjIsMi4zMzMzODIiLCJkYXRlX2NyZWF0aW9uIjoiMjAxNi0xMS0yOCIsImRhdGVfZGVidXRfYWN0aXZpdGUiOiIyMDE2LTExLTI4IiwiZGF0ZV9mZXJtZXR1cmUiOm51bGwsImRhdGVfbWlzZV9hX2pvdXIiOm51bGwsImRhdGVfbWlzZV9hX2pvdXJfaW5zZWUiOiIyMDI0LTA1LTMxVDA1OjI5OjU4IiwiZGVwYXJ0ZW1lbnQiOiI3NSIsImRpc3RyaWJ1dGlvbl9zcGVjaWFsZSI6bnVsbCwiZXBjaSI6IjIwMDA1NDc4MSIsImVzdF9zaWVnZSI6dHJ1ZSwiZXRhdF9hZG1pbmlzdHJhdGlmIjoiQSIsImdlb19hZHJlc3NlIjoiMzQgQXZlbnVlIGRlIGwnT3DDqXJhIDc1MDAyIFBhcmlzIiwiZ2VvX2lkIjoiNzUxMDJfNjkwNF8wMDAzNCIsImluZGljZV9yZXBldGl0aW9uIjpudWxsLCJsYXRpdHVkZSI6IjQ4Ljg2ODY2MiIsImxpYmVsbGVfY2VkZXgiOm51bGwsImxpYmVsbGVfY29tbXVuZSI6IlBBUklTIiwibGliZWxsZV9jb21tdW5lX2V0cmFuZ2VyIjpudWxsLCJsaWJlbGxlX3BheXNfZXRyYW5nZXIiOm51bGwsImxpYmVsbGVfdm9pZSI6IkRFIEwnT1BFUkEiLCJsaXN0ZV9lbnNlaWduZXMiOm51bGwsImxpc3RlX2ZpbmVzcyI6bnVsbCwibGlzdGVfaWRfYmlvIjpudWxsLCJsaXN0ZV9pZGNjIjpbIjE0ODYiXSwibGlzdGVfaWRfb3JnYW5pc21lX2Zvcm1hdGlvbiI6bnVsbCwibGlzdGVfcmdlIjpudWxsLCJsaXN0ZV91YWkiOm51bGwsImxvbmdpdHVkZSI6IjIuMzMzMzgyIiwibm9tX2NvbW1lcmNpYWwiOm51bGwsIm51bWVyb192b2llIjoiMzQiLCJyZWdpb24iOiIxMSIsInNpcmV0IjoiNDE4MTY2MDk2MDAwNjkiLCJzdGF0dXRfZGlmZnVzaW9uX2V0YWJsaXNzZW1lbnQiOiJPIiwidHJhbmNoZV9lZmZlY3RpZl9zYWxhcmllIjoiNDEiLCJ0eXBlX3ZvaWUiOiJBVkVOVUUifSwiYWN0aXZpdGVfcHJpbmNpcGFsZSI6IjYyLjAyQSIsImNhdGVnb3JpZV9lbnRyZXByaXNlIjoiR0UiLCJjYXJhY3RlcmVfZW1wbG95ZXVyIjpudWxsLCJhbm5lZV9jYXRlZ29yaWVfZW50cmVwcmlzZSI6IjIwMjIiLCJkYXRlX2NyZWF0aW9uIjoiMTk5OC0wNC0wMSIsImRhdGVfZmVybWV0dXJlIjpudWxsLCJkYXRlX21pc2VfYV9qb3VyIjoiMjAyNC0xMC0xNlQxMzowODowMyIsImRhdGVfbWlzZV9hX2pvdXJfaW5zZWUiOiIyMDI0LTA1LTMxVDA1OjI5OjU4IiwiZGF0ZV9taXNlX2Ffam91cl9ybmUiOiIyMDI0LTA1LTE5VDE2OjQ5OjMzIiwiZGlyaWdlYW50cyI6W3sic2lyZW4iOm51bGwsImRlbm9taW5hdGlvbiI6IktQTUciLCJxdWFsaXRlIjoiQ29tbWlzc2FpcmUgYXV4IGNvbXB0ZXMgdGl0dWxhaXJlIiwidHlwZV9kaXJpZ2VhbnQiOiJwZXJzb25uZSBtb3JhbGUifV0sImV0YXRfYWRtaW5pc3RyYXRpZiI6IkEiLCJuYXR1cmVfanVyaWRpcXVlIjoiNTcxMCIsInNlY3Rpb25fYWN0aXZpdGVfcHJpbmNpcGFsZSI6IkoiLCJ0cmFuY2hlX2VmZmVjdGlmX3NhbGFyaWUiOiI0MSIsImFubmVlX3RyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6IjIwMjIiLCJzdGF0dXRfZGlmZnVzaW9uIjoiTyIsIm1hdGNoaW5nX2V0YWJsaXNzZW1lbnRzIjpbeyJhY3Rpdml0ZV9wcmluY2lwYWxlIjoiNjIuMDJBIiwiYW5jaWVuX3NpZWdlIjp0cnVlLCJhbm5lZV90cmFuY2hlX2VmZmVjdGlmX3NhbGFyaWUiOm51bGwsImFkcmVzc2UiOiI1MCBBVkVOVUUgREVTIENIQU1QUyBFTFlTRUVTIDc1MDA4IFBBUklTIiwiY2FyYWN0ZXJlX2VtcGxveWV1ciI6Ik8iLCJjb2RlX3Bvc3RhbCI6Ijc1MDA4IiwiY29tbXVuZSI6Ijc1MTA4IiwiZGF0ZV9jcmVhdGlvbiI6IjIwMDUtMDItMTciLCJkYXRlX2RlYnV0X2FjdGl2aXRlIjoiMjAxNi0xMS0yOCIsImRhdGVfZmVybWV0dXJlIjoiMjAxNi0xMS0yOCIsImVwY2kiOiIyMDAwNTQ3ODEiLCJlc3Rfc2llZ2UiOmZhbHNlLCJldGF0X2FkbWluaXN0cmF0aWYiOiJGIiwiZ2VvX2lkIjoiNzUxMDhfMTczM18wMDA1MCIsImxhdGl0dWRlIjoiNDguODcwMzcxIiwibGliZWxsZV9jb21tdW5lIjoiUEFSSVMiLCJsaXN0ZV9lbnNlaWduZXMiOm51bGwsImxpc3RlX2ZpbmVzcyI6bnVsbCwibGlzdGVfaWRfYmlvIjpudWxsLCJsaXN0ZV9pZGNjIjpudWxsLCJsaXN0ZV9pZF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpudWxsLCJsaXN0ZV9yZ2UiOm51bGwsImxpc3RlX3VhaSI6bnVsbCwibG9uZ2l0dWRlIjoiMi4zMDY4OTkiLCJub21fY29tbWVyY2lhbCI6bnVsbCwicmVnaW9uIjoiMTEiLCJzaXJldCI6IjQxODE2NjA5NjAwMDUxIiwic3RhdHV0X2RpZmZ1c2lvbl9ldGFibGlzc2VtZW50IjoiTyIsInRyYW5jaGVfZWZmZWN0aWZfc2FsYXJpZSI6bnVsbH1dLCJmaW5hbmNlcyI6eyIyMDIzIjp7ImNhIjoxNDYxMzk4MDksInJlc3VsdGF0X25ldCI6MTA2MjY2Mzh9fSwiY29tcGxlbWVudHMiOnsiY29sbGVjdGl2aXRlX3RlcnJpdG9yaWFsZSI6bnVsbCwiY29udmVudGlvbl9jb2xsZWN0aXZlX3JlbnNlaWduZWUiOnRydWUsImxpc3RlX2lkY2MiOlsiMTQ4NiJdLCJlZ2Fwcm9fcmVuc2VpZ25lZSI6dHJ1ZSwiZXN0X2Fzc29jaWF0aW9uIjpmYWxzZSwiZXN0X2JpbyI6ZmFsc2UsImVzdF9lbnRyZXByZW5ldXJfaW5kaXZpZHVlbCI6ZmFsc2UsImVzdF9lbnRyZXByZW5ldXJfc3BlY3RhY2xlIjpmYWxzZSwiZXN0X2VzcyI6ZmFsc2UsImVzdF9maW5lc3MiOmZhbHNlLCJlc3Rfb3JnYW5pc21lX2Zvcm1hdGlvbiI6dHJ1ZSwiZXN0X3F1YWxpb3BpIjp0cnVlLCJsaXN0ZV9pZF9vcmdhbmlzbWVfZm9ybWF0aW9uIjpbIjExNzU0ODkzNjc1Il0sImVzdF9yZ2UiOmZhbHNlLCJlc3Rfc2VydmljZV9wdWJsaWMiOmZhbHNlLCJlc3Rfc2lhZSI6ZmFsc2UsImVzdF9zb2NpZXRlX21pc3Npb24iOmZhbHNlLCJlc3RfdWFpIjpmYWxzZSwiaWRlbnRpZmlhbnRfYXNzb2NpYXRpb24iOm51bGwsInN0YXR1dF9lbnRyZXByZW5ldXJfc3BlY3RhY2xlIjpudWxsLCJ0eXBlX3NpYWUiOm51bGx9fV0sInRvdGFsX3Jlc3VsdHMiOjEsInBhZ2UiOjEsInBlcl9wYWdlIjoxMCwidG90YWxfcGFnZXMiOjF9 + recorded_at: Thu, 17 Oct 2024 07:51:06 GMT +- request: + method: get + uri: https://api-lannuaire.service-public.fr/api/explore/v2.1/catalog/datasets/api-lannuaire-administration/records?where=siret:41816609600051 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + Server: + - openresty + Date: + - Thu, 17 Oct 2024 07:51:06 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '33' + X-Ratelimit-Remaining: + - '999989' + X-Ratelimit-Limit: + - '1000000' + X-Ratelimit-Reset: + - '2024-10-18 00:00:00+00:00' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Vary: + - Accept-Language, Cookie, Host + Content-Language: + - fr-fr + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - POST, GET, OPTIONS + Access-Control-Max-Age: + - '1000' + Access-Control-Allow-Headers: + - Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-API-Analytics-Embed-Type, + ODS-API-Analytics-Embed-Referrer, ODS-Widgets-Version, Accept + Access-Control-Expose-Headers: + - ODS-Explore-API-Deprecation, Link, X-RateLimit-Remaining, X-RateLimit-Limit, + X-RateLimit-Reset, X-RateLimit-dataset-Remaining, X-RateLimit-dataset-Limit, + X-RateLimit-dataset-Reset + Strict-Transport-Security: + - max-age=31536000 + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + Referrer-Policy: + - strict-origin-when-cross-origin + Permissions-Policy: + - midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=() + Content-Security-Policy: + - upgrade-insecure-requests; + X-Ua-Compatible: + - IE=edge + body: + encoding: ASCII-8BIT + string: '{"total_count": 0, "results": []}' + recorded_at: Thu, 17 Oct 2024 07:51:06 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/models/concerns/prefillable_from_service_public_concern_spec.rb b/spec/models/concerns/prefillable_from_service_public_concern_spec.rb index 2475972ae..4580ac689 100644 --- a/spec/models/concerns/prefillable_from_service_public_concern_spec.rb +++ b/spec/models/concerns/prefillable_from_service_public_concern_spec.rb @@ -9,12 +9,13 @@ RSpec.describe PrefillableFromServicePublicConcern, type: :model do describe '#prefill_from_siret' do let(:service) { Service.new(siret:) } subject { service.prefill_from_siret } - context 'when API call is successful' do + context 'when API call is successful with collectivite' do it 'prefills service attributes' do VCR.use_cassette('annuaire_service_public_success_20004021000060') do - expect(subject).to be_success + expect(subject.all?(&:success?)).to be_truthy expect(service.nom).to eq("Communauté de communes - Lacs et Gorges du Verdon") + expect(service).to be_collectivite_territoriale expect(service.email).to eq("redacted@email.fr") expect(service.telephone).to eq("04 94 70 00 00") expect(service.horaires).to eq("Lundi au Jeudi : de 8:00 à 12:00 et de 13:30 à 17:30\nVendredi : de 8:00 à 12:00") @@ -39,7 +40,46 @@ RSpec.describe PrefillableFromServicePublicConcern, type: :model do let(:siret) { '20004021000000' } it 'returns a failure result' do VCR.use_cassette('annuaire_service_public_failure_20004021000000') do - expect(subject).to be_failure + expect(subject.all?(&:failure?)).to be_truthy + end + end + end + + context 'when SIRET is enseignement' do + let(:siret) { '19750664500013' } + it 'prefills for enseignement' do + VCR.use_cassette('annuaire_service_public_success_19750664500013') do + expect(subject.one?(&:success?)).to be_truthy + expect(service.nom).to eq("LYCEE GENERAL ET TECHNOLOGIQUE RACINE") + expect(service).to be_etablissement_enseignement + expect(service.adresse).to eq("20 Rue du Rocher 75008 Paris") + expect(service.horaires).to be_nil + end + end + end + + context 'when SIRET is a ministere' do + let(:siret) { '11004601800013' } + it 'prefills for administration centrale' do + VCR.use_cassette('annuaire_service_public_success_11004601800013') do + expect(subject.one?(&:success?)).to be_truthy + expect(service.nom).to eq("MINISTERE DE LA CULTURE") + expect(service).to be_administration_centrale + expect(service.adresse).to eq("182 Rue Saint-Honoré 75001 Paris") + expect(service.horaires).to be_nil + end + end + end + + context 'when SIRET is La Poste' do + let(:siret) { '35600082800018' } + it 'prefills for administration centrale' do + VCR.use_cassette('annuaire_service_public_success_35600082800018') do + expect(subject.one?(&:success?)).to be_truthy + expect(service.nom).to eq("LA POSTE (REGION RHONE ALPES)") + expect(service).to be_service_deconcentre_de_l_etat + expect(service.adresse).to eq("4 Quai du Point du Jour 92100 Boulogne-Billancourt") + expect(service.horaires).to be_nil end end end From 7742703081611c1a372045a6dede8da3f6d69e43 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 15 Oct 2024 18:35:18 +0200 Subject: [PATCH 6/8] refactor(service): concurrent prefill API calls --- app/controllers/recoveries_controller.rb | 2 +- ...prefillable_from_service_public_concern.rb | 28 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/controllers/recoveries_controller.rb b/app/controllers/recoveries_controller.rb index 6a34c3e55..0d38b4d00 100644 --- a/app/controllers/recoveries_controller.rb +++ b/app/controllers/recoveries_controller.rb @@ -68,7 +68,7 @@ class RecoveriesController < ApplicationController def structure_name # we know that the structure exists because # of the ensure_collectivite_territoriale guard - APIRechercheEntreprisesService.new.(siret:).value![:nom_complet] + APIRechercheEntreprisesService.new.call(siret:).value![:nom_complet] end def ensure_agent_connect_is_used diff --git a/app/models/concerns/prefillable_from_service_public_concern.rb b/app/models/concerns/prefillable_from_service_public_concern.rb index 4d7fb5f27..bcf4f8475 100644 --- a/app/models/concerns/prefillable_from_service_public_concern.rb +++ b/app/models/concerns/prefillable_from_service_public_concern.rb @@ -5,9 +5,22 @@ module PrefillableFromServicePublicConcern included do def prefill_from_siret - result_sp = AnnuaireServicePublicService.new.(siret:) + future_sp = Concurrent::Future.execute { AnnuaireServicePublicService.new.call(siret:) } + future_api_ent = Concurrent::Future.execute { APIRechercheEntreprisesService.new.call(siret:) } - case result_sp + result_sp = future_sp.value! + result_api_ent = future_api_ent.value! + + prefill_from_service_public(result_sp) + prefill_from_api_entreprise(result_api_ent) + + [result_sp, result_api_ent] + end + + private + + def prefill_from_service_public(result) + case result in Dry::Monads::Success(data) self.nom = data[:nom] if nom.blank? self.email = data[:adresse_courriel] if email.blank? @@ -17,24 +30,19 @@ module PrefillableFromServicePublicConcern else # NOOP end + end - result_api_ent = APIRechercheEntreprisesService.new.call(siret:) - case result_api_ent + def prefill_from_api_entreprise(result) + case result in Dry::Monads::Success(data) self.type_organisme = detect_type_organisme(data) if type_organisme.blank? - - # some services (etablissements, …) are not in service public, so we also try to prefill them with API Entreprise self.nom = data[:nom_complet] if nom.blank? self.adresse = data.dig(:siege, :geo_adresse) if adresse.blank? else # NOOP end - - [result_sp, result_api_ent] end - private - def denormalize_plage_ouverture(data) return if data.blank? From 384d089cb33d028c6826704c7d873f5da2a9250b Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Wed, 16 Oct 2024 16:49:00 +0200 Subject: [PATCH 7/8] feat(service): prefill first service with AgentConnect siret --- .../administrateurs/services_controller.rb | 23 +++++++---- .../administrateurs/services/new.html.haml | 2 +- .../services_controller_spec.rb | 38 +++++++++++++++++++ 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/app/controllers/administrateurs/services_controller.rb b/app/controllers/administrateurs/services_controller.rb index c025a0ff1..e3439307e 100644 --- a/app/controllers/administrateurs/services_controller.rb +++ b/app/controllers/administrateurs/services_controller.rb @@ -12,6 +12,12 @@ module Administrateurs def new @procedure = procedure @service = Service.new + + siret = current_administrateur.instructeur.last_agent_connect_information&.siret + if siret + @service.siret = siret + @prefilled = handle_siret_prefill + end end def create @@ -19,7 +25,13 @@ module Administrateurs @service.administrateur = current_administrateur if request.xhr? && params[:service][:siret].present? - handle_siret_update + @service.siret = params[:service][:siret] + prefilled = handle_siret_prefill + render turbo_stream: turbo_stream.replace( + "service_form", + partial: "administrateurs/services/form", + locals: { service: @service, prefilled:, procedure: @procedure } + ) elsif @service.save @service.enqueue_api_entreprise @@ -111,8 +123,7 @@ module Administrateurs current_administrateur.procedures.find(params[:procedure_id]) end - def handle_siret_update - @service.assign_attributes(siret: params[:service][:siret]) + def handle_siret_prefill @service.validate if !@service.errors.include?(:siret) @@ -130,11 +141,7 @@ module Administrateurs @service.errors.clear siret_errors.each { @service.errors.import(_1) } - render turbo_stream: turbo_stream.replace( - "service_form", - partial: "administrateurs/services/form", - locals: { service: @service, prefilled:, procedure: @procedure } - ) + prefilled end end end diff --git a/app/views/administrateurs/services/new.html.haml b/app/views/administrateurs/services/new.html.haml index 27a39f2b5..7477ad9b2 100644 --- a/app/views/administrateurs/services/new.html.haml +++ b/app/views/administrateurs/services/new.html.haml @@ -8,4 +8,4 @@ %h1 Nouveau Service = render partial: 'form', - locals: { service: @service, procedure: @procedure } + locals: { service: @service, procedure: @procedure, prefilled: @prefilled } diff --git a/spec/controllers/administrateurs/services_controller_spec.rb b/spec/controllers/administrateurs/services_controller_spec.rb index 510086688..77ff1a3ba 100644 --- a/spec/controllers/administrateurs/services_controller_spec.rb +++ b/spec/controllers/administrateurs/services_controller_spec.rb @@ -4,6 +4,44 @@ describe Administrateurs::ServicesController, type: :controller do let(:admin) { administrateurs(:default_admin) } let(:procedure) { create(:procedure, administrateur: admin) } + describe '#new' do + let(:admin) { administrateurs(:default_admin) } + let(:procedure) { create(:procedure, administrateur: admin) } + + before do + sign_in(admin.user) + end + + subject { get :new, params: { procedure_id: procedure.id } } + + context 'when admin has a SIRET from AgentConnect' do + let(:siret) { "20004021000060" } + + before do + agi = build(:agent_connect_information, siret:) + admin.instructeur.agent_connect_information << agi + end + + it 'prefills the SIRET and fetches service information' do + VCR.use_cassette("annuaire_service_public_success_#{siret}") do + subject + expect(assigns[:service].siret).to eq(siret) + expect(assigns[:service].nom).to eq("Communauté de communes - Lacs et Gorges du Verdon") + expect(assigns[:service].adresse).to eq("242 avenue Albert-1er 83630 Aups") + expect(assigns[:prefilled]).to eq(:success) + end + end + end + + context 'when admin has no SIRET from AgentConnect' do + it 'does not prefill the SIRET' do + subject + expect(assigns[:service].siret).to be_nil + expect(assigns[:prefilled]).to be_nil + end + end + end + describe '#create' do before do sign_in(admin.user) From 8b3634ea7658810b6a9a1c64bac3a7fffa51cefa Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 4 Nov 2024 12:47:42 +0100 Subject: [PATCH 8/8] refactor(service): dedicated route for prefill from siret --- .../administrateurs/services_controller.rb | 25 ++++++++++++------- .../administrateurs/services/_form.html.haml | 6 +++-- config/routes.rb | 1 + .../services_controller_spec.rb | 22 +++++++++------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/app/controllers/administrateurs/services_controller.rb b/app/controllers/administrateurs/services_controller.rb index e3439307e..7fc329efd 100644 --- a/app/controllers/administrateurs/services_controller.rb +++ b/app/controllers/administrateurs/services_controller.rb @@ -24,15 +24,7 @@ module Administrateurs @service = Service.new(service_params) @service.administrateur = current_administrateur - if request.xhr? && params[:service][:siret].present? - @service.siret = params[:service][:siret] - prefilled = handle_siret_prefill - render turbo_stream: turbo_stream.replace( - "service_form", - partial: "administrateurs/services/form", - locals: { service: @service, prefilled:, procedure: @procedure } - ) - elsif @service.save + if @service.save @service.enqueue_api_entreprise redirect_to admin_services_path(procedure_id: params[:procedure_id]), @@ -66,6 +58,19 @@ module Administrateurs end end + def prefill + @procedure = procedure + @service = Service.new(siret: params[:siret]) + + prefilled = handle_siret_prefill + + render turbo_stream: turbo_stream.replace( + "service_form", + partial: "administrateurs/services/form", + locals: { service: @service, prefilled:, procedure: @procedure } + ) + end + def add_to_procedure procedure = current_administrateur.procedures.find(procedure_params[:id]) service = services.find(procedure_params[:service_id]) @@ -137,6 +142,8 @@ module Administrateurs end end + # On prefill from SIRET, we only want to display errors for the SIRET input + # so we have to remove other errors (ie. required attributes not yet filled) siret_errors = @service.errors.where(:siret) @service.errors.clear siret_errors.each { @service.errors.import(_1) } diff --git a/app/views/administrateurs/services/_form.html.haml b/app/views/administrateurs/services/_form.html.haml index f9791c198..8a463a3d7 100644 --- a/app/views/administrateurs/services/_form.html.haml +++ b/app/views/administrateurs/services/_form.html.haml @@ -1,6 +1,8 @@ -= form_with model: [:admin, service], id: "service_form", data: { turbo: token_list('true' => service.new_record?), controller: token_list('autosave' => service.new_record?), turbo_method: 'post' } do |f| += form_with model: [:admin, service], id: "service_form" do |f| - = render Dsfr::InputComponent.new(form: f, attribute: :siret, input_type: :text_field, opts: { placeholder: "14 chiffres, sans espace" }) do |c| + = render Dsfr::InputComponent.new(form: f, attribute: :siret, input_type: :text_field, + opts: { placeholder: "14 chiffres, sans espace", + onblur: token_list("Turbo.visit('#{prefill_admin_services_path(procedure_id: procedure.id)}?siret=' + this.value)" => service.new_record?) }) do |c| - if service.etablissement_infos.blank? && local_assigns[:prefilled].nil? - c.with_hint do = "Indiquez le numéro de SIRET de l’organisme dont ce service dépend. Rechercher le SIRET sur " diff --git a/config/routes.rb b/config/routes.rb index d194eb920..170ce0b4f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -734,6 +734,7 @@ Rails.application.routes.draw do resources :services, except: [:show] do collection do patch 'add_to_procedure' + get ':procedure_id/prefill' => :prefill, as: :prefill end end diff --git a/spec/controllers/administrateurs/services_controller_spec.rb b/spec/controllers/administrateurs/services_controller_spec.rb index 77ff1a3ba..e8bc20c5d 100644 --- a/spec/controllers/administrateurs/services_controller_spec.rb +++ b/spec/controllers/administrateurs/services_controller_spec.rb @@ -42,20 +42,18 @@ describe Administrateurs::ServicesController, type: :controller do end end - describe '#create' do + describe '#prefill' do before do sign_in(admin.user) end - let(:xhr) { false } - subject { post :create, params:, xhr: } + subject { get :prefill, params:, xhr: true } context 'when prefilling from a SIRET' do - let(:xhr) { true } let(:params) do { procedure_id: procedure.id, - service: { siret: "20004021000060" } + siret: "20004021000060" } end @@ -70,11 +68,10 @@ describe Administrateurs::ServicesController, type: :controller do end context 'when attempting to prefilling from invalid SIRET' do - let(:xhr) { true } let(:params) do { procedure_id: procedure.id, - service: { siret: "20004021000000" } + siret: "20004021000000" } end @@ -87,11 +84,10 @@ describe Administrateurs::ServicesController, type: :controller do end context 'when attempting to prefilling from not service public SIRET' do - let(:xhr) { true } let(:params) do { procedure_id: procedure.id, - service: { siret: "41816609600051" } + siret: "41816609600051" } end @@ -105,6 +101,14 @@ describe Administrateurs::ServicesController, type: :controller do end end end + end + + describe '#create' do + before do + sign_in(admin.user) + end + + subject { post :create, params: } context 'when submitting a new service' do let(:params) do