Merge pull request #8625 from demarches-simplifiees/prefill/annuaire-education

feat(dossier): prefill annuaire education champ
This commit is contained in:
Sébastien Carceles 2023-03-01 08:20:59 +00:00 committed by GitHub
commit 3c0eaae24c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 219 additions and 12 deletions

View file

@ -1,11 +1,9 @@
class ChampFetchExternalDataJob < ApplicationJob class ChampFetchExternalDataJob < ApplicationJob
def perform(champ, external_id) def perform(champ, external_id)
if champ.external_id == external_id && champ.data.nil? return if champ.external_id != external_id
data = champ.fetch_external_data return if champ.data.present?
return if (data = champ.fetch_external_data).blank?
if data.present? champ.update_with_external_data!(data: data)
champ.update!(data: data)
end
end
end end
end end

View file

@ -217,6 +217,10 @@ class Champ < ApplicationRecord
raise NotImplemented.new(:fetch_external_data) raise NotImplemented.new(:fetch_external_data)
end end
def update_with_external_data!(data:)
update!(data: data)
end
def clone def clone
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id] champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id]
value_attributes = private? ? [] : [:value, :value_json, :data, :external_id] value_attributes = private? ? [] : [:value, :value_json, :data, :external_id]

View file

@ -28,4 +28,15 @@ class Champs::AnnuaireEducationChamp < Champs::TextChamp
def fetch_external_data def fetch_external_data
APIEducation::AnnuaireEducationAdapter.new(external_id).to_params APIEducation::AnnuaireEducationAdapter.new(external_id).to_params
end end
def update_with_external_data!(data:)
if data&.is_a?(Hash) && data['nom_etablissement'].present? && data['nom_commune'].present? && data['identifiant_de_l_etablissement'].present?
update!(
data: data,
value: "#{data['nom_etablissement']}, #{data['nom_commune']} (#{data['identifiant_de_l_etablissement']})"
)
else
update!(data: data)
end
end
end end

View file

@ -274,6 +274,7 @@ class TypeDeChamp < ApplicationRecord
TypeDeChamp.type_champs.fetch(:repetition), TypeDeChamp.type_champs.fetch(:repetition),
TypeDeChamp.type_champs.fetch(:multiple_drop_down_list), TypeDeChamp.type_champs.fetch(:multiple_drop_down_list),
TypeDeChamp.type_champs.fetch(:epci), TypeDeChamp.type_champs.fetch(:epci),
TypeDeChamp.type_champs.fetch(:annuaire_education),
TypeDeChamp.type_champs.fetch(:dossier_link), TypeDeChamp.type_champs.fetch(:dossier_link),
TypeDeChamp.type_champs.fetch(:siret), TypeDeChamp.type_champs.fetch(:siret),
TypeDeChamp.type_champs.fetch(:rna) TypeDeChamp.type_champs.fetch(:rna)

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp < TypesDeChamp::PrefillTypeDeChamp
def to_assignable_attributes(champ, value)
return nil if value.blank?
{
id: champ.id,
external_id: value,
value: value
}
end
end

View file

@ -27,6 +27,8 @@ class TypesDeChamp::PrefillTypeDeChamp < SimpleDelegator
TypesDeChamp::PrefillCommuneTypeDeChamp.new(type_de_champ, revision) TypesDeChamp::PrefillCommuneTypeDeChamp.new(type_de_champ, revision)
when TypeDeChamp.type_champs.fetch(:epci) when TypeDeChamp.type_champs.fetch(:epci)
TypesDeChamp::PrefillEpciTypeDeChamp.new(type_de_champ, revision) TypesDeChamp::PrefillEpciTypeDeChamp.new(type_de_champ, revision)
when TypeDeChamp.type_champs.fetch(:annuaire_education)
TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp.new(type_de_champ, revision)
else else
new(type_de_champ, revision) new(type_de_champ, revision)
end end

View file

@ -148,7 +148,8 @@ en:
rna_html: A RNA number rna_html: A RNA number
repetition_html: A array of hashes with possible values for each field of the repetition. repetition_html: A array of hashes with possible values for each field of the repetition.
epci_html: An array of the department code and the <a href="https://geo.api.gouv.fr/epcis" target="_blank" rel="noopener noreferrer">EPCI one</a>. epci_html: An array of the department code and the <a href="https://geo.api.gouv.fr/epcis" target="_blank" rel="noopener noreferrer">EPCI one</a>.
dossier_link_html: The file ID, as integer. annuaire_education_html: An educational institution code, as defined by the <a href="https://api.gouv.fr/les-api/api-annuaire-education" target="_blank" rel="noopener noreferrer">Éducation Nationale</a> directory
dossier_link_html: The file ID, as integer
examples: examples:
title: Example title: Example
text: Short text text: Short text
@ -165,6 +166,7 @@ en:
date: "2023-02-01" date: "2023-02-01"
datetime: "2023-02-01T10:30" datetime: "2023-02-01T10:30"
checkbox: "true" checkbox: "true"
annuaire_education: "0561383Z"
dossier_link: 42 dossier_link: 42
rna: "W503726238" rna: "W503726238"
siret: "13002526500013" siret: "13002526500013"

View file

@ -139,7 +139,8 @@ fr:
siret_html: Un numéro de SIRET siret_html: Un numéro de SIRET
repetition_html: Un tableau de dictionnaires avec les valeurs possibles pour chaque champ de la répétition. repetition_html: Un tableau de dictionnaires avec les valeurs possibles pour chaque champ de la répétition.
epci_html: Un tableau contenant le code de département et <a href="https://geo.api.gouv.fr/epcis" target="_blank" rel="noopener noreferrer">celui de l'EPCI</a>. epci_html: Un tableau contenant le code de département et <a href="https://geo.api.gouv.fr/epcis" target="_blank" rel="noopener noreferrer">celui de l'EPCI</a>.
dossier_link_html: L'identifiant du dossier, sous forme de nombre entier. annuaire_education_html: Un code d'établissement scolaire, tel que défini par <a href="https://api.gouv.fr/les-api/api-annuaire-education" target="_blank" rel="noopener noreferrer">l'Annuaire de l'Éducation Nationale</a>
dossier_link_html: L'identifiant du dossier, sous forme de nombre entier
examples: examples:
title: Exemple title: Exemple
text: Texte court text: Texte court
@ -157,6 +158,7 @@ fr:
date: "2023-02-01" date: "2023-02-01"
datetime: "2023-02-01T10:30" datetime: "2023-02-01T10:30"
checkbox: "true" checkbox: "true"
annuaire_education: "0561383Z"
dossier_link: 42 dossier_link: 42
rna: "W503726238" rna: "W503726238"
siret: "13002526500013" siret: "13002526500013"

View file

@ -0,0 +1,14 @@
{
"nhits": 0,
"parameters": {
"dataset": "fr-en-annuaire-education",
"rows": 1,
"start": 0,
"refine": {
"identifiant_de_l_etablissement": "0341247"
},
"format": "json",
"timezone": "UTC"
},
"records": []
}

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ChampFetchExternalDataJob, type: :job do
let(:champ) { Struct.new(:external_id, :data).new(champ_external_id, data) }
let(:external_id) { "an ID" }
let(:champ_external_id) { "an ID" }
let(:data) { nil }
let(:fetched_data) { nil }
subject(:perform_job) { described_class.perform_now(champ, external_id) }
before do
allow(champ).to receive(:fetch_external_data).and_return(fetched_data)
allow(champ).to receive(:update_with_external_data!)
end
shared_examples "a champ non-updater" do
it 'does not update the champ' do
perform_job
expect(champ).not_to have_received(:update_with_external_data!)
end
end
context 'when external_id matches the champ external_id and the champ data is nil' do
it 'fetches external data' do
perform_job
expect(champ).to have_received(:fetch_external_data)
end
context 'when the fetched data is present' do
let(:fetched_data) { "data" }
it 'updates the champ' do
perform_job
expect(champ).to have_received(:update_with_external_data!).with(data: fetched_data)
end
end
context 'when the fetched data is blank' do
it_behaves_like "a champ non-updater"
end
end
context 'when external_id does not match the champ external_id' do
let(:champ_external_id) { "something else" }
it_behaves_like "a champ non-updater"
end
context 'when the champ data is present' do
let(:data) { "present" }
it_behaves_like "a champ non-updater"
end
end

View file

@ -38,4 +38,13 @@ describe APIEducation::AnnuaireEducationAdapter do
expect { subject }.to raise_exception(APIEducation::AnnuaireEducationAdapter::InvalidSchemaError) expect { subject }.to raise_exception(APIEducation::AnnuaireEducationAdapter::InvalidSchemaError)
end end
end end
context "when responds with empty schema" do
let(:body) { File.read('spec/fixtures/files/api_education/annuaire_education_empty.json') }
let(:status) { 200 }
it '#to_params returns nil' do
expect(subject).to eq(nil)
end
end
end end

View file

@ -602,4 +602,12 @@ describe Champ do
end end
end end
end end
describe '#update_with_external_data!' do
let(:champ) { create(:champ_siret) }
let(:data) { "data" }
subject { champ.update_with_external_data!(data: data) }
it { expect { subject }.to change { champ.reload.data }.to(data) }
end
end end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Champs::AnnuaireEducationChamp do
describe '#update_with_external_data!' do
let(:champ) { create(:champ_annuaire_education, data: "any data") }
subject { champ.update_with_external_data!(data: data) }
shared_examples "a data updater (without updating the value)" do |data|
it { expect { subject }.to change { champ.reload.data }.to(data) }
it { expect { subject }.not_to change { champ.reload.value } }
end
context 'when data is nil' do
let(:data) { nil }
it_behaves_like "a data updater (without updating the value)", nil
end
context 'when data is empty' do
let(:data) { '' }
it_behaves_like "a data updater (without updating the value)", ''
end
context 'when data is inconsistent' do
let(:data) { { 'yo' => 'lo' } }
it_behaves_like "a data updater (without updating the value)", { 'yo' => 'lo' }
end
context 'when data is consistent' do
let(:data) {
{
'nom_etablissement': "karrigel an ankou",
'nom_commune' => 'kumun',
'identifiant_de_l_etablissement' => '666667'
}.with_indifferent_access
}
it { expect { subject }.to change { champ.reload.data }.to(data) }
it { expect { subject }.to change { champ.reload.value }.to('karrigel an ankou, kumun (666667)') }
end
end
end

View file

@ -140,6 +140,7 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ public value that is authorized", :drop_down_list, "value" it_behaves_like "a champ public value that is authorized", :drop_down_list, "value"
it_behaves_like "a champ public value that is authorized", :departements, "03" it_behaves_like "a champ public value that is authorized", :departements, "03"
it_behaves_like "a champ public value that is authorized", :communes, ['01', '01457'] it_behaves_like "a champ public value that is authorized", :communes, ['01', '01457']
it_behaves_like "a champ public value that is authorized", :annuaire_education, "0050009H"
it_behaves_like "a champ public value that is authorized", :multiple_drop_down_list, ["val1", "val2"] it_behaves_like "a champ public value that is authorized", :multiple_drop_down_list, ["val1", "val2"]
it_behaves_like "a champ public value that is authorized", :dossier_link, "1" it_behaves_like "a champ public value that is authorized", :dossier_link, "1"
it_behaves_like "a champ public value that is authorized", :epci, ['01', '200042935'] it_behaves_like "a champ public value that is authorized", :epci, ['01', '200042935']
@ -182,6 +183,7 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ private value that is authorized", :siret, "13002526500013" it_behaves_like "a champ private value that is authorized", :siret, "13002526500013"
it_behaves_like "a champ private value that is authorized", :departements, "03" it_behaves_like "a champ private value that is authorized", :departements, "03"
it_behaves_like "a champ private value that is authorized", :communes, ['01', '01457'] it_behaves_like "a champ private value that is authorized", :communes, ['01', '01457']
it_behaves_like "a champ private value that is authorized", :annuaire_education, "0050009H"
it_behaves_like "a champ private value that is authorized", :multiple_drop_down_list, ["val1", "val2"] it_behaves_like "a champ private value that is authorized", :multiple_drop_down_list, ["val1", "val2"]
it_behaves_like "a champ private value that is authorized", :dossier_link, "1" it_behaves_like "a champ private value that is authorized", :dossier_link, "1"
it_behaves_like "a champ private value that is authorized", :epci, ['01', '200042935'] it_behaves_like "a champ private value that is authorized", :epci, ['01', '200042935']
@ -223,7 +225,6 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ public value that is unauthorized", :regions, "value" it_behaves_like "a champ public value that is unauthorized", :regions, "value"
it_behaves_like "a champ public value that is unauthorized", :departements, "value" it_behaves_like "a champ public value that is unauthorized", :departements, "value"
it_behaves_like "a champ public value that is unauthorized", :communes, "value" it_behaves_like "a champ public value that is unauthorized", :communes, "value"
it_behaves_like "a champ public value that is unauthorized", :annuaire_education, "value"
it_behaves_like "a champ public value that is unauthorized", :multiple_drop_down_list, ["value"] it_behaves_like "a champ public value that is unauthorized", :multiple_drop_down_list, ["value"]
context "when the public type de champ is unauthorized because of wrong value format (repetition)" do context "when the public type de champ is unauthorized because of wrong value format (repetition)" do

View file

@ -253,6 +253,7 @@ describe TypeDeChamp do
it_behaves_like "a prefillable type de champ", :type_de_champ_checkbox it_behaves_like "a prefillable type de champ", :type_de_champ_checkbox
it_behaves_like "a prefillable type de champ", :type_de_champ_drop_down_list it_behaves_like "a prefillable type de champ", :type_de_champ_drop_down_list
it_behaves_like "a prefillable type de champ", :type_de_champ_repetition it_behaves_like "a prefillable type de champ", :type_de_champ_repetition
it_behaves_like "a prefillable type de champ", :type_de_champ_annuaire_education
it_behaves_like "a prefillable type de champ", :type_de_champ_multiple_drop_down_list it_behaves_like "a prefillable type de champ", :type_de_champ_multiple_drop_down_list
it_behaves_like "a prefillable type de champ", :type_de_champ_epci it_behaves_like "a prefillable type de champ", :type_de_champ_epci
it_behaves_like "a prefillable type de champ", :type_de_champ_dossier_link it_behaves_like "a prefillable type de champ", :type_de_champ_dossier_link
@ -271,7 +272,6 @@ describe TypeDeChamp do
it_behaves_like "a non-prefillable type de champ", :type_de_champ_mesri it_behaves_like "a non-prefillable type de champ", :type_de_champ_mesri
it_behaves_like "a non-prefillable type de champ", :type_de_champ_carte it_behaves_like "a non-prefillable type de champ", :type_de_champ_carte
it_behaves_like "a non-prefillable type de champ", :type_de_champ_address it_behaves_like "a non-prefillable type de champ", :type_de_champ_address
it_behaves_like "a non-prefillable type de champ", :type_de_champ_annuaire_education
end end
describe '#normalize_libelle' do describe '#normalize_libelle' do

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_annuaire_education, procedure: procedure) }
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#to_assignable_attributes' do
let(:champ) { create(:champ_annuaire_education, type_de_champ: type_de_champ) }
subject { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when the value is nil' do
let(:value) { nil }
it { is_expected.to eq(nil) }
end
context 'when the value is empty' do
let(:value) { '' }
it { is_expected.to eq(nil) }
end
context 'when the value is present' do
let(:value) { '0050009H' }
it { is_expected.to match({ id: champ.id, external_id: '0050009H', value: '0050009H' }) }
end
end
end

View file

@ -57,6 +57,12 @@ RSpec.describe TypesDeChamp::PrefillTypeDeChamp, type: :model do
it { expect(built).to be_kind_of(TypesDeChamp::PrefillEpciTypeDeChamp) } it { expect(built).to be_kind_of(TypesDeChamp::PrefillEpciTypeDeChamp) }
end end
context 'when the type de champ is an annuaire_education' do
let(:type_de_champ) { build(:type_de_champ_annuaire_education) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp) }
end
context 'when any other type de champ' do context 'when any other type de champ' do
let(:type_de_champ) { build(:type_de_champ_date, procedure: procedure) } let(:type_de_champ) { build(:type_de_champ_date, procedure: procedure) }

View file

@ -26,5 +26,6 @@ shared_examples "the user has got a prefilled dossier, owned by themselves" do
expect(page).to have_field(type_de_champ_epci.libelle, with: epci_value.last) expect(page).to have_field(type_de_champ_epci.libelle, with: epci_value.last)
expect(page).to have_field(type_de_champ_dossier_link.libelle, with: dossier_link_value) expect(page).to have_field(type_de_champ_dossier_link.libelle, with: dossier_link_value)
expect(page).to have_selector("input[value='Vonnas (01540)']") expect(page).to have_selector("input[value='Vonnas (01540)']")
expect(page).to have_content(annuaire_education_value.last)
end end
end end

View file

@ -13,6 +13,7 @@ describe 'Prefilling a dossier (with a GET request):', js: true do
let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) } let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) }
let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) } let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) }
let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) } let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) }
let(:type_de_champ_annuaire_education) { create(:type_de_champ_annuaire_education, procedure: procedure) }
let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) } let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) }
let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) } let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) } let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) }
@ -36,6 +37,7 @@ describe 'Prefilling a dossier (with a GET request):', js: true do
let(:integer_repetition_libelle) { sub_type_de_champs_repetition.second.libelle } let(:integer_repetition_libelle) { sub_type_de_champs_repetition.second.libelle }
let(:text_repetition_value) { "First repetition text" } let(:text_repetition_value) { "First repetition text" }
let(:integer_repetition_value) { "42" } let(:integer_repetition_value) { "42" }
let(:annuaire_education_value) { '0050009H' }
let(:entry_path) { let(:entry_path) {
commencer_path( commencer_path(
@ -54,7 +56,8 @@ describe 'Prefilling a dossier (with a GET request):', js: true do
"champ_#{sub_type_de_champs_repetition.first.to_typed_id_for_query}": text_repetition_value, "champ_#{sub_type_de_champs_repetition.first.to_typed_id_for_query}": text_repetition_value,
"champ_#{sub_type_de_champs_repetition.second.to_typed_id_for_query}": integer_repetition_value "champ_#{sub_type_de_champs_repetition.second.to_typed_id_for_query}": integer_repetition_value
} }
] ],
"champ_#{type_de_champ_annuaire_education.to_typed_id_for_query}" => annuaire_education_value
) )
} }

View file

@ -13,6 +13,7 @@ describe 'Prefilling a dossier (with a POST request):', js: true do
let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) } let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) }
let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) } let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) }
let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) } let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) }
let(:type_de_champ_annuaire_education) { create(:type_de_champ_annuaire_education, procedure: procedure) }
let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) } let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) } let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) }
let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) } let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) }
@ -36,6 +37,7 @@ describe 'Prefilling a dossier (with a POST request):', js: true do
let(:text_repetition_value) { "First repetition text" } let(:text_repetition_value) { "First repetition text" }
let(:integer_repetition_value) { "42" } let(:integer_repetition_value) { "42" }
let(:dossier_link_value) { '42' } let(:dossier_link_value) { '42' }
let(:annuaire_education_value) { '0050009H' }
before do before do
allow(Rails).to receive(:cache).and_return(memory_store) allow(Rails).to receive(:cache).and_return(memory_store)
@ -160,7 +162,8 @@ describe 'Prefilling a dossier (with a POST request):', js: true do
"champ_#{type_de_champ_multiple_drop_down_list.to_typed_id_for_query}" => multiple_drop_down_list_values, "champ_#{type_de_champ_multiple_drop_down_list.to_typed_id_for_query}" => multiple_drop_down_list_values,
"champ_#{type_de_champ_epci.to_typed_id_for_query}" => epci_value, "champ_#{type_de_champ_epci.to_typed_id_for_query}" => epci_value,
"champ_#{type_de_champ_dossier_link.to_typed_id_for_query}" => dossier_link_value, "champ_#{type_de_champ_dossier_link.to_typed_id_for_query}" => dossier_link_value,
"champ_#{type_de_champ_commune.to_typed_id_for_query}" => commune_value "champ_#{type_de_champ_commune.to_typed_id_for_query}" => commune_value,
"champ_#{type_de_champ_annuaire_education.to_typed_id_for_query}" => annuaire_education_value
}.to_json }.to_json
JSON.parse(session.response.body)["dossier_url"].gsub("http://www.example.com", "") JSON.parse(session.response.body)["dossier_url"].gsub("http://www.example.com", "")
end end