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
def perform(champ, external_id)
if champ.external_id == external_id && champ.data.nil?
data = champ.fetch_external_data
return if champ.external_id != external_id
return if champ.data.present?
return if (data = champ.fetch_external_data).blank?
if data.present?
champ.update!(data: data)
end
end
champ.update_with_external_data!(data: data)
end
end

View file

@ -217,6 +217,10 @@ class Champ < ApplicationRecord
raise NotImplemented.new(:fetch_external_data)
end
def update_with_external_data!(data:)
update!(data: data)
end
def clone
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_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
APIEducation::AnnuaireEducationAdapter.new(external_id).to_params
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

View file

@ -274,6 +274,7 @@ class TypeDeChamp < ApplicationRecord
TypeDeChamp.type_champs.fetch(:repetition),
TypeDeChamp.type_champs.fetch(:multiple_drop_down_list),
TypeDeChamp.type_champs.fetch(:epci),
TypeDeChamp.type_champs.fetch(:annuaire_education),
TypeDeChamp.type_champs.fetch(:dossier_link),
TypeDeChamp.type_champs.fetch(:siret),
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)
when TypeDeChamp.type_champs.fetch(:epci)
TypesDeChamp::PrefillEpciTypeDeChamp.new(type_de_champ, revision)
when TypeDeChamp.type_champs.fetch(:annuaire_education)
TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp.new(type_de_champ, revision)
else
new(type_de_champ, revision)
end

View file

@ -148,7 +148,8 @@ en:
rna_html: A RNA number
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>.
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:
title: Example
text: Short text
@ -165,6 +166,7 @@ en:
date: "2023-02-01"
datetime: "2023-02-01T10:30"
checkbox: "true"
annuaire_education: "0561383Z"
dossier_link: 42
rna: "W503726238"
siret: "13002526500013"

View file

@ -139,7 +139,8 @@ fr:
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.
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:
title: Exemple
text: Texte court
@ -157,6 +158,7 @@ fr:
date: "2023-02-01"
datetime: "2023-02-01T10:30"
checkbox: "true"
annuaire_education: "0561383Z"
dossier_link: 42
rna: "W503726238"
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)
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

View file

@ -602,4 +602,12 @@ describe Champ do
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

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", :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", :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", :dossier_link, "1"
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", :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", :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", :dossier_link, "1"
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", :departements, "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"]
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_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_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_epci
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_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_annuaire_education
end
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) }
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
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_dossier_link.libelle, with: dossier_link_value)
expect(page).to have_selector("input[value='Vonnas (01540)']")
expect(page).to have_content(annuaire_education_value.last)
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_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_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_commune) { create(:type_de_champ_communes, 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(:text_repetition_value) { "First repetition text" }
let(:integer_repetition_value) { "42" }
let(:annuaire_education_value) { '0050009H' }
let(:entry_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.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_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_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_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, 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(:integer_repetition_value) { "42" }
let(:dossier_link_value) { '42' }
let(:annuaire_education_value) { '0050009H' }
before do
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_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_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
JSON.parse(session.response.body)["dossier_url"].gsub("http://www.example.com", "")
end