diff --git a/app/lib/api_particulier/api.rb b/app/lib/api_particulier/api.rb index 1238872ee..ba2207c22 100644 --- a/app/lib/api_particulier/api.rb +++ b/app/lib/api_particulier/api.rb @@ -4,6 +4,7 @@ class APIParticulier::API INTROSPECT_RESOURCE_NAME = "introspect" COMPOSITION_FAMILIALE_RESOURCE_NAME = "v2/composition-familiale" AVIS_IMPOSITION_RESOURCE_NAME = "v2/avis-imposition" + SITUATION_POLE_EMPLOI = "v2/situations-pole-emploi" TIMEOUT = 20 @@ -29,6 +30,10 @@ class APIParticulier::API referenceAvis: reference_avis.to_i.to_s.rjust(13, "0")) end + def situation_pole_emploi(identifiant) + get(SITUATION_POLE_EMPLOI, identifiant: identifiant) + end + private def get(resource_name, params = {}) diff --git a/app/lib/api_particulier/pole_emploi_adapter.rb b/app/lib/api_particulier/pole_emploi_adapter.rb new file mode 100644 index 000000000..1ecc12cb8 --- /dev/null +++ b/app/lib/api_particulier/pole_emploi_adapter.rb @@ -0,0 +1,46 @@ +class APIParticulier::PoleEmploiAdapter + class InvalidSchemaError < ::StandardError + def initialize(errors) + super(errors.map(&:to_json).join("\n")) + end + end + + def initialize(api_particulier_token, identifiant, requested_sources) + @api = APIParticulier::API.new(api_particulier_token) + @identifiant = identifiant + @requested_sources = requested_sources + end + + def to_params + @api.situation_pole_emploi(@identifiant) + .tap { |d| ensure_valid_schema!(d) } + .then { |d| extract_requested_sources(d) } + end + + private + + def ensure_valid_schema!(data) + if !schemer.valid?(data) + errors = schemer.validate(data).to_a + raise InvalidSchemaError.new(errors) + end + end + + def schemer + @schemer ||= JSONSchemer.schema(Rails.root.join('app/schemas/situation-pole-emploi.json')) + end + + def extract_requested_sources(data) + @requested_sources['pole_emploi']&.map do |(scope, sources)| + case scope + when 'adresse' + sources.map { |source| { scope => data[scope].slice(*source) } } + when 'identifiant', 'contact', 'inscription' + sources.map { |source| { scope => data.slice(*source) } } + else + { scope => data.slice(*sources) } + end + end + &.flatten&.reduce(&:deep_merge) || {} + end +end diff --git a/app/schemas/situation-pole-emploi.json b/app/schemas/situation-pole-emploi.json new file mode 100644 index 000000000..01d6619ed --- /dev/null +++ b/app/schemas/situation-pole-emploi.json @@ -0,0 +1,87 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://demarches-simplifiees.fr/situation-pole-emploi.schema.json", + "title": "situation pole emploi", + "type": "object", + "properties": { + "identifiant": { + "type": "string" + }, + "civilite": { + "type": "string" + }, + "nom": { + "type": "string" + }, + "nomUsage": { + "type": "string" + }, + "prenom": { + "type": "string" + }, + "sexe": { + "enum": ["F", "M"] + }, + "dateNaissance": { + "format": "date", + "type": "string" + }, + "codeCertificationCNAV": { + "type": "string" + }, + "telephone": { + "type": "string" + }, + "telephone2": { + "type": "string" + }, + "email": { + "type": "string" + }, + "$defs": { + "adresse": { + "type": "object", + "properties": { + "codePostal": { + "type": "string" + }, + "INSEECommune": { + "type": "string" + }, + "localite": { + "type": "string" + }, + "ligneVoie": { + "type": "string" + }, + "ligneComplementDestinataire": { + "type": "string" + }, + "ligneComplementAdresse": { + "type": "string" + }, + "ligneComplementDistribution": { + "type": "string" + }, + "ligneNom": { + "type": "string" + } + } + } + }, + "dateInscription": { + "format": "date", + "type": "string" + }, + "dateCessationInscription": { + "format": "date", + "type": "string" + }, + "codeCategorieInscription": { + "type": "integer" + }, + "libelleCategroiieInscription": { + "type": "string" + } + } +} diff --git a/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi.yml b/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi.yml new file mode 100644 index 000000000..2e7c01f4a --- /dev/null +++ b/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://particulier.api.gouv.fr/api/v2/situations-pole-emploi?identifiant=georges_moustaki_77 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Accept: + - application/json + X-Api-Key: + - 06fd8675601267d2988cbbdef56ecb0de1d45223 + Expect: + - '' + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 16 Mar 2021 17:01:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '651' + Connection: + - keep-alive + Keep-Alive: + - timeout=5 + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Range,Content-Range,X-Content-Range,X-API-Key + Etag: + - W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0" + Server: + - nginx + Strict-Transport-Security: + - max-age=15552000 + - max-age=15552000 + Vary: + - Origin, Accept + X-Gravitee-Request-Id: + - 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5 + X-Gravitee-Transaction-Id: + - f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b + body: + encoding: ASCII-8BIT + string: '{ + "identifiant": "georges_moustaki_77", + "civilite": "M.", + "nom": "Moustaki", + "nomUsage": "Moustaki", + "prenom": "Georges", + "sexe": "M", + "dateNaissance": "1934-05-03", + "adresse": { + "INSEECommune": "75118", + "codePostal": "75018", + "localite": "75018 Paris", + "ligneVoie": "3 rue des Huttes", + "ligneNom": "MOUSTAKI" + }, + "email": "georges@moustaki.fr", + "telephone": "0629212921", + "dateInscription": "1965-05-03", + "dateCessationInscription": "1966-05-03", + "codeCertificationCNAV": "VC", + "codeCategorieInscription": 1, + "libelleCategorieInscription": "PERSONNE SANS EMPLOI DISPONIBLE DUREE INDETERMINEE PLEIN TPS" + }' + recorded_at: Tue, 16 Mar 2021 17:01:18 GMT +recorded_with: VCR 6.0.0 diff --git a/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi_invalid.yml b/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi_invalid.yml new file mode 100644 index 000000000..d5af6fb39 --- /dev/null +++ b/spec/fixtures/cassettes/api_particulier/success/situation_pole_emploi_invalid.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: get + uri: https://particulier.api.gouv.fr/api/v2/situations-pole-emploi?identifiant=georges_moustaki_77 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Accept: + - application/json + X-Api-Key: + - 06fd8675601267d2988cbbdef56ecb0de1d45223 + Expect: + - '' + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 16 Mar 2021 17:01:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '747' + Connection: + - keep-alive + Keep-Alive: + - timeout=5 + Access-Control-Allow-Credentials: + - '651' + Access-Control-Expose-Headers: + - Range,Content-Range,X-Content-Range,X-API-Key + Etag: + - W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0" + Server: + - nginx + Strict-Transport-Security: + - max-age=15552000 + - max-age=15552000 + Vary: + - Origin, Accept + X-Gravitee-Request-Id: + - 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5 + X-Gravitee-Transaction-Id: + - f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b + body: + encoding: ASCII-8BIT + string: '{ + "identifiant": "georges_moustaki_77", + "civilite": "M.", + "nom": "Moustaki", + "nomUsage": "Moustaki", + "prenom": "Georges", + "sexe": "X", + "dateNaissance": "1934-05-03", + "adresse": { + "INSEECommune": "75118", + "codePostal": "75018", + "localite": "75018 Paris", + "ligneVoie": "3 rue des Huttes", + "ligneNom": "MOUSTAKI" + }, + "email": "georges@moustaki.fr", + "telephone": "0629212921", + "dateInscription": "1965-05-03", + "dateCessationInscription": "1966-05-03", + "codeCertificationCNAV": "VC", + "codeCategorieInscription": 1, + "libelleCategorieInscription": "PERSONNE SANS EMPLOI DISPONIBLE DUREE INDETERMINEE PLEIN TPS" + }' + recorded_at: Tue, 16 Mar 2021 17:01:18 GMT +recorded_with: VCR 6.0.0 diff --git a/spec/fixtures/files/api_particulier/situation_pole_emploi.json b/spec/fixtures/files/api_particulier/situation_pole_emploi.json new file mode 100644 index 000000000..a0402f3d3 --- /dev/null +++ b/spec/fixtures/files/api_particulier/situation_pole_emploi.json @@ -0,0 +1,29 @@ +{ + "identite": { + "identifiant": "georges_moustaki_77", + "civilite": "M.", + "nom": "Moustaki", + "nomUsage": "Moustaki", + "prenom": "Georges", + "sexe": "M", + "dateNaissance": "1934-05-03" + }, + "adresse": { + "INSEECommune": "75118", + "codePostal": "75018", + "localite": "75018 Paris", + "ligneVoie": "3 rue des Huttes", + "ligneNom": "MOUSTAKI" + }, + "contact": { + "email": "georges@moustaki.fr", + "telephone": "0629212921" + }, + "inscription": { + "dateInscription": "1965-05-03", + "dateCessationInscription": "1966-05-03", + "codeCertificationCNAV": "VC", + "codeCategorieInscription": 1, + "libelleCategorieInscription": "PERSONNE SANS EMPLOI DISPONIBLE DUREE INDETERMINEE PLEIN TPS" + } +} diff --git a/spec/lib/api_particulier/pole_emploi_adapter_spec.rb b/spec/lib/api_particulier/pole_emploi_adapter_spec.rb new file mode 100644 index 000000000..62322997e --- /dev/null +++ b/spec/lib/api_particulier/pole_emploi_adapter_spec.rb @@ -0,0 +1,63 @@ +describe APIParticulier::PoleEmploiAdapter do + let(:adapter) { described_class.new(api_particulier_token, identifiant, requested_sources) } + + before { stub_const('API_PARTICULIER_URL', 'https://particulier.api.gouv.fr/api') } + + describe '#to_params' do + let(:api_particulier_token) { '06fd8675601267d2988cbbdef56ecb0de1d45223' } + let(:identifiant) { 'georges_moustaki_77' } + + subject { VCR.use_cassette(cassette) { adapter.to_params } } + + context 'when the api answer is valid' do + let(:cassette) { 'api_particulier/success/situation_pole_emploi' } + + context 'when the token has all the pole emploi scopes' do + context 'and all the sources are requested' do + let(:requested_sources) do + { + 'pole_emploi' => { + 'identite' => ['identifiant', 'civilite', 'nom', 'nomUsage', 'prenom', 'sexe', 'dateNaissance'], + 'adresse' => ['INSEECommune', 'codePostal', 'localite', 'ligneVoie', 'ligneComplementDestinataire', 'ligneComplementAdresse', 'ligneComplementDistribution', 'ligneNom'], + 'contact' => ['email', 'telephone', 'telephone2'], + 'inscription' => ['dateInscription', 'dateCessationInscription', 'codeCertificationCNAV', 'codeCategorieInscription', 'libelleCategorieInscription'] + } + } + end + + let(:result) { JSON.parse(File.read('spec/fixtures/files/api_particulier/situation_pole_emploi.json')) } + + it { is_expected.to eq(result) } + end + + context 'when no sources is requested' do + let(:requested_sources) { {} } + + it { is_expected.to eq({}) } + end + + context 'when an address name is requested' do + let(:requested_sources) { { 'pole_emploi' => { 'adresse' => ['ligneNom'] } } } + + it { is_expected.to eq('adresse' => { 'ligneNom' => 'MOUSTAKI' }) } + end + + context 'when a first name is requested' do + let(:requested_sources) { { 'pole_emploi' => { 'identite' => ['prenom'] } } } + + it { is_expected.to eq('identite' => { 'prenom' => 'Georges' }) } + end + end + end + + context 'when the api answer is invalid' do + let(:cassette) { 'api_particulier/success/situation_pole_emploi_invalid' } + + context 'when no sources is requested' do + let(:requested_sources) { {} } + + it { expect { subject }.to raise_error(APIParticulier::PoleEmploiAdapter::InvalidSchemaError) } + end + end + end +end