Reimplement predemandes API over the new rdv-status API (#76412)
gitea/ants-hub/pipeline/head This commit looks good Details

To simplify migration from the old to the new API.
This commit is contained in:
Benjamin Dauvergne 2023-11-15 12:14:36 +01:00
parent 2f9cd366b3
commit 0fa00339c6
2 changed files with 93 additions and 113 deletions

View File

@ -12,7 +12,6 @@ import re
import time
import jsonschema
import requests
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import OperationalError
@ -25,7 +24,6 @@ from django.views.generic import View
from ants_hub.api.ants import ANTSError, get_status_of_predemandes
from ants_hub.data.models import (
Collectivite,
Config,
Horaire,
HoraireList,
Lieu,
@ -35,9 +33,7 @@ from ants_hub.data.models import (
TypeDeRdv,
)
from ants_hub.interval import IntervalSet
from ants_hub.timezone import localtime, make_naive, now
from .ants import ANTS_TIMEZONE
from ants_hub.timezone import now
logger = logging.getLogger('ants_hub.api.chrono')
@ -422,78 +418,39 @@ class RendezVousDisponibleView(View):
rendez_vous_disponibles = csrf_exempt(authenticate(RendezVousDisponibleView.as_view()))
IDENTIFIANT_PREDEMANDE_RE = re.compile(r'^[A-Z0-9]{10}$')
class PredemandesView(View):
def get_autres_demandes(self, identifiant_predemandes):
auth_token = Config.get(Config.REQUEST_TO_ANTS_AUTH_TOKEN)
url = Config.get(Config.REQUEST_TO_ANTS_BASE_URL, 'https://rendez-vous-api.france-identite.fr/api/')
if not auth_token:
logger.error(
'predemandes(%s) server error REQUEST_TO_ANTS_AUTH_TOKEN is not configured',
self.request.raccordement,
)
return None, JsonResponse({'err': 'auth-token-not-configured'}, status=500)
params = [
('application_ids', identifiant_predemande) for identifiant_predemande in identifiant_predemandes
]
identifiant_predemandes = list(filter(IDENTIFIANT_PREDEMANDE_RE.match, identifiant_predemandes))
try:
response = requests.get(
f'{url}searchApplicationIds',
headers={'x-hub-rdv-auth-token': auth_token},
params=params,
timeout=10,
)
response.raise_for_status()
except requests.RequestException as e:
logger.error('predemandes(%s) %s is down, %s', self.request.raccordement, url, e)
accept_rdv, message, dummy, appointments = get_status_of_predemandes(identifiant_predemandes)
except ANTSError as e:
return None, JsonResponse(
{'err': 1, 'err_class': 'rendez-vous-api-is-unavailable', 'err_desc': str(e)}, status=200
)
try:
data = response.json()
if not isinstance(data, dict):
raise ValueError('data is not a dict')
except (ValueError, AttributeError) as e:
logger.error(
'predemandes(%s) %s is down, result is not a JSON object: %s',
self.request.raccordement,
url,
response.content[:1024],
{
'err': 1,
'err_desc': str(e),
}
)
for appointment in appointments:
appointment['datetime'] = appointment.get('appointment_date')
appointment['cancel_url'] = appointment.get('management_url')
if not accept_rdv and not appointments:
return None, JsonResponse(
{'err': 1, 'err_class': 'rendez-vous-api-is-unavailable', 'err_desc': str(e)}, status=200
{
'err': 1,
'err_desc': message,
}
)
except KeyError:
data = {}
response_data = []
for identifiant_predemande in identifiant_predemandes:
meetings = data.get(identifiant_predemande, [])
for rendez_vous in meetings:
# fix stupid formatting of dates from ANTS, Paris time with 'Z' suffix :/
date = rendez_vous.get('datetime', None)
date = datetime.datetime.fromisoformat(rendez_vous['datetime'].rstrip('Z'))
date = date.replace(tzinfo=ANTS_TIMEZONE)
date = localtime(date)
rendez_vous = rendez_vous.copy()
rendez_vous['identifiant_predemande'] = identifiant_predemande
rendez_vous['datetime'] = make_naive(date).isoformat()
response_data.append(rendez_vous)
logger.info(
'predemandes(%s) returned %s demandes from %s for predemande "%s"',
self.request.raccordement,
len(response_data),
','.join(map(str, (rendez_vous['meeting_point'] for rendez_vous in response_data))),
','.join(identifiant_predemandes),
)
return response_data, None
return appointments, None
def get(self, request):
identifiant_predemandes = [
part.strip() for part in request.GET.getlist('identifiant_predemande') if part.strip()
part.strip().upper() for part in request.GET.getlist('identifiant_predemande') if part.strip()
]
if not identifiant_predemandes:
return JsonResponse({'err': 'missing=identifiant-predemande'}, status=400)
return JsonResponse({'err': 0, 'data': []})
autres_demandes, error_response = self.get_autres_demandes(identifiant_predemandes)
if error_response is not None:
return error_response
@ -526,7 +483,7 @@ class RdvStatus(View):
msg = []
for identifiant_predemande in identifiant_predemandes:
if not re.match(r'^([A-Z0-9]{10}[,;:\-/.\s])*[A-Z0-9]{10}$', identifiant_predemande):
if not IDENTIFIANT_PREDEMANDE_RE.match(identifiant_predemande):
msg.append(
f'Identifiant de pré-demande "{identifiant_predemande}" invalide, il doit faire 10 caractères.'
)

View File

@ -4,6 +4,7 @@ import datetime
import zoneinfo
import pytest
import requests
import responses
import responses.matchers
from django.db import OperationalError
@ -355,83 +356,105 @@ def test_rendez_vous_disponibles(django_app, db):
@responses.activate
def test_predemandes(db, django_app):
Config.set(Config.REQUEST_TO_ANTS_AUTH_TOKEN, 'xyz')
Config.set(Config.REQUEST_TO_ANTS_V2_AUTH_TOKEN, 'abcd')
Raccordement.objects.create(name='plateforme', apikey='abcd')
django_app.set_authorization(('Basic', ('abcd', '')))
document = {
'xyz': [
{
'meeting_point': 'MAIRIE DE SAINT-DIDIER',
'datetime': '2023-04-03T10:00:00Z',
'management_url': 'https://saint-didier/rdv/1/',
'cancel_url': 'https://saint-didier/rdv/1/cancel/',
}
]
'ABCDE12345': {
'status': 'validated',
'appointments': [],
},
'1234567890': {
'status': 'validated',
'appointments': [],
},
}
rdv_api_url = 'https://rendez-vous-api.france-identite.fr/api/searchApplicationIds'
rdv_api_url = 'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status'
responses.add(
responses.GET,
rdv_api_url,
json=document,
status=200,
match=[responses.matchers.header_matcher({'x-hub-rdv-auth-token': 'xyz'})],
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
)
response = django_app.get('/api/chrono/predemandes/', params={'identifiant_predemande': 'xyz'})
response = django_app.get('/api/chrono/predemandes/')
assert response.json['err'] == 0
assert response.json['data'] == [
{
'meeting_point': 'MAIRIE DE SAINT-DIDIER',
'datetime': '2023-04-03T10:00:00',
'management_url': 'https://saint-didier/rdv/1/',
'cancel_url': 'https://saint-didier/rdv/1/cancel/',
'identifiant_predemande': 'xyz',
}
]
assert (
responses._default_mock.calls[0].request.url
== 'https://rendez-vous-api.france-identite.fr/api/searchApplicationIds?application_ids=xyz'
assert response.json['data'] == []
response = django_app.get(
'/api/chrono/predemandes/',
params=[('identifiant_predemande', 'ABCDE12345'), ('identifiant_predemande', '1234567890')],
)
assert response.json['err'] == 0
assert response.json['data'] == []
# with multiple identifiant_predemande
document['1234'] = [
document['ABCDE12345']['appointments'] = [
{
'meeting_point': 'MAIRIE DE POUETPOUET',
'datetime': '2023-04-03T10:00:00Z',
'management_url': 'https://rdvenmairie.fr/gestion/login?ants=83AHERZY8F&appointment_id=64594c435d7bfc0012fa8c87&canceled=true',
'meeting_point': 'Mairie de Luisant',
'appointment_date': '2023-09-20T09:00:11',
}
]
responses.add(
responses.replace(
responses.GET,
rdv_api_url,
json=document,
status=200,
match=[responses.matchers.header_matcher({'x-hub-rdv-auth-token': 'xyz'})],
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
)
response = django_app.get(
'/api/chrono/predemandes/',
params=[('identifiant_predemande', 'xyz'), ('identifiant_predemande', '1234')],
params=[('identifiant_predemande', 'ABCDE12345'), ('identifiant_predemande', '1234567890')],
)
assert response.json['err'] == 0
assert response.json['data'] == [
{
'meeting_point': 'MAIRIE DE SAINT-DIDIER',
'datetime': '2023-04-03T10:00:00',
'management_url': 'https://saint-didier/rdv/1/',
'cancel_url': 'https://saint-didier/rdv/1/cancel/',
'identifiant_predemande': 'xyz',
},
{
'datetime': '2023-04-03T10:00:00',
'identifiant_predemande': '1234',
'meeting_point': 'MAIRIE DE POUETPOUET',
},
]
assert (
responses._default_mock.calls[1].request.url
== 'https://rendez-vous-api.france-identite.fr/api/searchApplicationIds?application_ids=xyz&application_ids=1234'
assert response.json == {
'err': 0,
'data': [
{
'appointment_date': '2023-09-20T09:00:11',
'datetime': '2023-09-20T09:00:11',
'identifiant_predemande': 'ABCDE12345',
'management_url': 'https://rdvenmairie.fr/gestion/login?ants=83AHERZY8F&appointment_id=64594c435d7bfc0012fa8c87&canceled=true',
'cancel_url': 'https://rdvenmairie.fr/gestion/login?ants=83AHERZY8F&appointment_id=64594c435d7bfc0012fa8c87&canceled=true',
'meeting_point': 'Mairie de Luisant',
}
],
}
responses.replace(
responses.GET,
rdv_api_url,
json={},
status=200,
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
)
response = django_app.get(
'/api/chrono/predemandes/',
params=[('identifiant_predemande', 'ABcDE12345'), ('identifiant_predemande', '1234567890')],
)
assert response.json == {
'err': 1,
'err_desc': 'Prédemande "ABCDE12345" inconnue. Prédemande "1234567890" inconnue.',
}
responses.replace(
responses.GET,
rdv_api_url,
body=requests.RequestException('connection error'),
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
)
response = django_app.get(
'/api/chrono/predemandes/',
params=[('identifiant_predemande', 'ABCDE12345'), ('identifiant_predemande', '1234567890')],
)
assert response.json == {
'err': 1,
'err_desc': "RequestException('connection error')",
}
def test_rendez_vous_disponibles_full(django_app, db, freezer):