From b94030083aa87b5d16fcacab74ba3eadace879ae Mon Sep 17 00:00:00 2001 From: Laurent Bossavit Date: Thu, 5 Dec 2024 15:44:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(anct)=20fetch=20and=20display=20organ?= =?UTF-8?q?ization=20names=20of=20communes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANCT-specific extraction of organization names for communes, front end changes to match. --- CHANGELOG.md | 1 + src/backend/people/settings.py | 6 ++++ src/backend/plugins/organizations.py | 33 ++++++++++--------- ...est_name_from_siret_organization_plugin.py | 33 +++++++++++++++++++ .../apps/desk/src/core/auth/api/types.ts | 7 ++++ .../src/features/header/AccountDropdown.tsx | 7 +++- .../apps/e2e/__tests__/app-desk/siret.spec.ts | 13 ++++++++ .../env.d/preprod/values.desk.yaml.gotmpl | 2 +- .../env.d/production/values.desk.yaml.gotmpl | 2 +- 9 files changed, 86 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 693c9f7ef..fce829e08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to ### Added +- ✨(anct) fetch and display organization names of communes #583 - 🧑‍💻(oidc) add ability to pull registration ID (e.g. SIRET) from OIDC #577 ### Fixed diff --git a/src/backend/people/settings.py b/src/backend/people/settings.py index f1f5e7a34..a9cbe7870 100755 --- a/src/backend/people/settings.py +++ b/src/backend/people/settings.py @@ -641,6 +641,8 @@ class Development(Base): OIDC_ORGANIZATION_REGISTRATION_ID_FIELD = "siret" + ORGANIZATION_PLUGINS = ["plugins.organizations.NameFromSiretOrganizationPlugin"] + def __init__(self): """In dev, force installs needed for Swagger API.""" # pylint: disable=invalid-name @@ -686,6 +688,10 @@ class Test(Base): # this is a dev credentials for mail provisioning API MAIL_PROVISIONING_API_CREDENTIALS = "bGFfcmVnaWU6cGFzc3dvcmQ=" + OIDC_ORGANIZATION_REGISTRATION_ID_FIELD = "siret" + + ORGANIZATION_PLUGINS = ["plugins.organizations.NameFromSiretOrganizationPlugin"] + ORGANIZATION_REGISTRATION_ID_VALIDATORS = [ { "NAME": "django.core.validators.RegexValidator", diff --git a/src/backend/plugins/organizations.py b/src/backend/plugins/organizations.py index 0108542b6..6bc67c56e 100644 --- a/src/backend/plugins/organizations.py +++ b/src/backend/plugins/organizations.py @@ -33,20 +33,17 @@ def _extract_name_from_organization_data(organization_data): logger.warning("Empty list 'liste_enseignes' in %s", organization_data) return None - def _get_organization_name_from_siret(self, siret): - """Return the organization name from the SIRET.""" - try: - response = requests.get(self._api_url.format(siret=siret), timeout=10) - response.raise_for_status() - data = response.json() - except requests.RequestException as exc: - logger.exception("%s: Unable to fetch organization name from SIRET", exc) - return None - + def get_organization_name_from_results(self, data, siret): + """Return the organization name from the results of a SIRET search.""" for result in data["results"]: + nature = "nature_juridique" + commune = nature in result and result[nature] == "7210" for organization in result["matching_etablissements"]: if organization.get("siret") == siret: - return self._extract_name_from_organization_data(organization) + if commune: + return organization["libelle_commune"].title() + + return self._extract_name_from_organization_data(organization) logger.warning("No organization name found for SIRET %s", siret) return None @@ -63,10 +60,16 @@ def run_after_create(self, organization): # In the nominal case, there is only one registration ID because # the organization as been created from it. - name = self._get_organization_name_from_siret( - organization.registration_id_list[0] - ) - if not name: + try: + siret = organization.registration_id_list[0] + response = requests.get(self._api_url.format(siret=siret), timeout=10) + response.raise_for_status() + data = response.json() + name = self.get_organization_name_from_results(data, siret) + if not name: + return + except requests.RequestException as exc: + logger.exception("%s: Unable to fetch organization name from SIRET", exc) return organization.name = name diff --git a/src/backend/plugins/tests/organizations/test_name_from_siret_organization_plugin.py b/src/backend/plugins/tests/organizations/test_name_from_siret_organization_plugin.py index b7b33eedb..56c8ac224 100644 --- a/src/backend/plugins/tests/organizations/test_name_from_siret_organization_plugin.py +++ b/src/backend/plugins/tests/organizations/test_name_from_siret_organization_plugin.py @@ -6,6 +6,8 @@ from core.models import Organization from core.plugins.loader import get_organization_plugins +from plugins.organizations import NameFromSiretOrganizationPlugin + pytestmark = pytest.mark.django_db @@ -135,3 +137,34 @@ def test_organization_plugins_run_after_create_name_already_set( name="Magic WOW", registration_id_list=["12345678901234"] ) assert organization.name == "Magic WOW" + + +def test_extract_name_from_org_data_when_commune( + organization_plugins_settings, +): + """Test the name is extracted correctly for a French commune.""" + data = { + "results": [ + { + "nom_complet": "COMMUNE DE VARZY", + "nom_raison_sociale": "COMMUNE DE VARZY", + "siege": { + "libelle_commune": "VARZY", + "liste_enseignes": ["MAIRIE"], + "siret": "21580304000017", + }, + "nature_juridique": "7210", + "matching_etablissements": [ + { + "siret": "21580304000017", + "libelle_commune": "VARZY", + "liste_enseignes": ["MAIRIE"], + } + ], + } + ] + } + + plugin = NameFromSiretOrganizationPlugin() + name = plugin.get_organization_name_from_results(data, "21580304000017") + assert name == "Varzy" diff --git a/src/frontend/apps/desk/src/core/auth/api/types.ts b/src/frontend/apps/desk/src/core/auth/api/types.ts index 54d981ffc..54227a64f 100644 --- a/src/frontend/apps/desk/src/core/auth/api/types.ts +++ b/src/frontend/apps/desk/src/core/auth/api/types.ts @@ -9,6 +9,7 @@ export interface User { id: string; email: string; name?: string; + organization?: Organization; abilities?: { mailboxes: UserAbilities; contacts: UserAbilities; @@ -16,6 +17,12 @@ export interface User { }; } +export interface Organization { + id: string; + name: string; + registration_id_list: [string]; +} + export type UserAbilities = { can_view?: boolean; can_create?: boolean; diff --git a/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx b/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx index 276ac520b..29c6c7e55 100644 --- a/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx +++ b/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx @@ -14,7 +14,12 @@ export const AccountDropdown = () => { - {userName} + + {userName} + {userData?.organization?.registration_id_list?.at(0) && ( + {userData?.organization?.name} + )} + diff --git a/src/frontend/apps/e2e/__tests__/app-desk/siret.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/siret.spec.ts index b6b78779c..b867ffe56 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/siret.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/siret.spec.ts @@ -21,3 +21,16 @@ test.describe('OIDC interop with SIRET', () => { }); }); }); + +test.describe('When a commune, display commune name below user name', () => { + test('it checks the name is added below the user name', async ({ page }) => { + const header = page.locator('header').first(); + await expect(header.getByAltText('Marianne Logo')).toBeVisible(); + + const logout = page.getByRole('button', { + name: new RegExp(`Marie Devarzy`, 'i'), + }); + + await expect(logout.getByText('Varzy')).toBeVisible(); + }); +}); diff --git a/src/helm/env.d/preprod/values.desk.yaml.gotmpl b/src/helm/env.d/preprod/values.desk.yaml.gotmpl index fb71d0bfa..92b3e3ed0 100644 --- a/src/helm/env.d/preprod/values.desk.yaml.gotmpl +++ b/src/helm/env.d/preprod/values.desk.yaml.gotmpl @@ -55,7 +55,7 @@ backend: OIDC_RP_SCOPES: "openid email siret" OIDC_REDIRECT_ALLOWED_HOSTS: https://desk-preprod.beta.numerique.gouv.fr OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}" - ORGANIZATION_PLUGINS: "plugins.organizations.NameFromSiretOrganizationPlugin" + ORGANIZATION_PLUGINS: ["plugins.organizations.NameFromSiretOrganizationPlugin"] ORGANIZATION_REGISTRATION_ID_VALIDATORS: '[{"NAME": "django.core.validators.RegexValidator", "OPTIONS": {"regex": "^[0-9]{14}$"}}]' LOGIN_REDIRECT_URL: https://desk-preprod.beta.numerique.gouv.fr LOGIN_REDIRECT_URL_FAILURE: https://desk-preprod.beta.numerique.gouv.fr diff --git a/src/helm/env.d/production/values.desk.yaml.gotmpl b/src/helm/env.d/production/values.desk.yaml.gotmpl index 54bd8c8a1..13214a156 100644 --- a/src/helm/env.d/production/values.desk.yaml.gotmpl +++ b/src/helm/env.d/production/values.desk.yaml.gotmpl @@ -44,7 +44,7 @@ backend: OIDC_OP_TOKEN_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/token OIDC_OP_USER_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/userinfo OIDC_OP_LOGOUT_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/session/end - ORGANIZATION_PLUGINS: "plugins.organizations.NameFromSiretOrganizationPlugin" + ORGANIZATION_PLUGINS: ["plugins.organizations.NameFromSiretOrganizationPlugin"] OIDC_ORGANIZATION_REGISTRATION_ID_FIELD: "siret" OIDC_RP_CLIENT_ID: secretKeyRef: