Reimplement predemandes API over the new rdv-status API (#76412)
gitea/ants-hub/pipeline/head This commit looks good
Details
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:
parent
2f9cd366b3
commit
0fa00339c6
|
@ -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.'
|
||||
)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue