api: simplify push_rdv()/upload_rdvs() (#89609)
gitea/ants-hub/pipeline/head This commit looks good
Details
gitea/ants-hub/pipeline/head This commit looks good
Details
* all errors are transmitted through exceptions * last_upload is always updated even if no API call is necessary * report when DELETE returns rowcount=0 or POST is not success
This commit is contained in:
parent
99a25b6df9
commit
dbffe4bef0
|
@ -6,7 +6,7 @@ import re
|
|||
import requests
|
||||
from django.db import OperationalError
|
||||
from django.db.models import F, Q
|
||||
from django.db.transaction import atomic, set_rollback
|
||||
from django.db.transaction import atomic
|
||||
|
||||
from ants_hub.data.models import Config, RendezVous
|
||||
from ants_hub.timezone import localtime, now
|
||||
|
@ -75,7 +75,8 @@ class APIDoublon:
|
|||
]
|
||||
response = self.request(method='POST', endpoint='appointments', params=params)
|
||||
try:
|
||||
return response['success']
|
||||
if not response['success']:
|
||||
raise RuntimeError('creation failed, success is not true')
|
||||
except Exception as e:
|
||||
raise ANTSError(str(e), {'response': response})
|
||||
|
||||
|
@ -89,7 +90,8 @@ class APIDoublon:
|
|||
]
|
||||
response = self.request(method='DELETE', endpoint='appointments', params=params)
|
||||
try:
|
||||
return 'rowcount' in response
|
||||
if not bool(response['rowcount']):
|
||||
raise RuntimeError('deletion failed, rowcount is zero')
|
||||
except Exception as e:
|
||||
raise ANTSError(str(e), {'response': response})
|
||||
|
||||
|
@ -132,38 +134,27 @@ def push_rdv(rdv):
|
|||
|
||||
api_doublon = APIDoublon()
|
||||
|
||||
delete = False
|
||||
create = False
|
||||
|
||||
existing = api_doublon.existing_rdv(rdv)
|
||||
|
||||
if rdv.canceled:
|
||||
action = 'deleted'
|
||||
if existing:
|
||||
delete = True
|
||||
if not existing:
|
||||
# Tout est déjà bon, on ne fait rien
|
||||
return False
|
||||
api_doublon.delete_rdv(rdv)
|
||||
return 'deleted'
|
||||
else:
|
||||
action = 'created'
|
||||
if len(existing) > 1:
|
||||
delete = True
|
||||
create = True
|
||||
# Il y a des doublons pour la même référence meeting_point/date on supprime tout
|
||||
api_doublon.delete_rdv(rdv)
|
||||
elif existing:
|
||||
if existing[0]['management_url'] != rdv.get_gestion_url_for_ants():
|
||||
# L'URL de gestion a changé, on supprime le RdV existant
|
||||
delete = True
|
||||
create = True
|
||||
api_doublon.delete_rdv(rdv)
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
create = True
|
||||
|
||||
success = True
|
||||
if delete:
|
||||
success = success and api_doublon.delete_rdv(rdv)
|
||||
|
||||
if create:
|
||||
success = success and api_doublon.create_rdv(rdv)
|
||||
|
||||
return success and (delete or create) and action
|
||||
# Tout est déjà bon, on ne fait rien
|
||||
return False
|
||||
api_doublon.create_rdv(rdv)
|
||||
return 'created'
|
||||
|
||||
|
||||
def get_rdvs_to_upload():
|
||||
|
@ -184,14 +175,14 @@ def upload_rdvs():
|
|||
except RendezVous.DoesNotExist:
|
||||
continue
|
||||
try:
|
||||
RendezVous.objects.filter(pk=rdv.pk).update(last_upload=start)
|
||||
result = push_rdv(rdv)
|
||||
if result:
|
||||
# si result == False, c'est un rejeu inutile, on ne log rien, mais on met à jour last_upload
|
||||
logger.info(f'{result} rdv(%s) of lieu %s', rdv, rdv.lieu)
|
||||
continue
|
||||
rdv.last_upload = start
|
||||
rdv.save(update_fields=['last_upload'])
|
||||
except ANTSError as e:
|
||||
logger.warning('unable to push rdv(%s) of lieu %s: %r', rdv, rdv.lieu, e)
|
||||
set_rollback(True)
|
||||
except OperationalError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -663,6 +663,91 @@ class TestAPIV2Push:
|
|||
assert post_response.call_count == 1
|
||||
assert delete_response.call_count == 1
|
||||
|
||||
@responses.activate
|
||||
@pytest.mark.parametrize(
|
||||
'response_kwargs',
|
||||
[
|
||||
{
|
||||
'json': {},
|
||||
'status': 200,
|
||||
},
|
||||
{
|
||||
'json': None,
|
||||
'status': 200,
|
||||
},
|
||||
{
|
||||
'json': '',
|
||||
'status': 200,
|
||||
},
|
||||
{
|
||||
'json': {'detail': []},
|
||||
'status': 422,
|
||||
},
|
||||
{
|
||||
'status': 500,
|
||||
},
|
||||
],
|
||||
ids=[
|
||||
'JSON is empty dict',
|
||||
'JSON is null',
|
||||
'JSON is empty string',
|
||||
'HTTP 422 with details',
|
||||
'HTTP 500',
|
||||
],
|
||||
)
|
||||
def test_delete_duplicates_error(self, response_kwargs, db, freezer):
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(canceled__isnull=True).count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
},
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'success': True},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
delete_response = responses.add(
|
||||
responses.DELETE,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
**response_kwargs,
|
||||
)
|
||||
|
||||
RendezVous.objects.update(canceled=now(), last_update=now())
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert post_response.call_count == 0
|
||||
assert delete_response.call_count == 1
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
@responses.activate
|
||||
def test_delete_management_url_changed(self, db, freezer):
|
||||
assert RendezVous.objects.count() == 1
|
||||
|
@ -709,6 +794,75 @@ class TestAPIV2Push:
|
|||
assert post_response.call_count == 1
|
||||
assert delete_response.call_count == 1
|
||||
|
||||
@responses.activate
|
||||
def test_create_already_exists(self, db, freezer):
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'status': 'validated',
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
'management_url': (
|
||||
'https://ants-hub.entrouvert.org/rdv/saint-didier-1/'
|
||||
'mairie-1/2023-04-03T12:15:00+02:00/gestion/7cace277-9157-4fbc-9705-45522984805d/'
|
||||
),
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'success': True},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert post_response.call_count == 0
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=False).count() == 1
|
||||
|
||||
@responses.activate
|
||||
def test_delete_already_deleted(self, db, freezer):
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
delete_response = responses.add(
|
||||
responses.DELETE,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'rowcount': 1},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
RendezVous.objects.update(canceled=now(), last_update=now())
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert delete_response.call_count == 0
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=False).count() == 1
|
||||
|
||||
|
||||
class TestGetStatusOfPredemandes:
|
||||
@pytest.fixture(autouse=True)
|
||||
|
|
Loading…
Reference in New Issue