From be595d8caafa3beef3019d7ba9282fff9ab6f3b1 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 26 Nov 2020 13:42:51 +0100
Subject: [PATCH 2/2] maelis: add cache on family data (#48477)

---
 passerelle/apps/maelis/models.py |  5 ++++
 tests/test_maelis.py             | 41 ++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/passerelle/apps/maelis/models.py b/passerelle/apps/maelis/models.py
index 617f69ed..fb963bea 100644
--- a/passerelle/apps/maelis/models.py
+++ b/passerelle/apps/maelis/models.py
@@ -133,22 +133,26 @@ class Maelis(BaseResource):
             return self.link_set.get(name_id=name_id)
         except Link.DoesNotExist:
             raise APIError('User not linked to family', err_code='not-found')
 
     def get_family_data(self, family_id, school_year=None):
         if not school_year:
             # fallback to current year if not provided
             school_year = utils.get_default_school_year()
+        family_data = self.get_from_2keys_cache('family', family_id, school_year)
+        if family_data:
+            return family_data
         family_data = serialize_object(self.call('FamilyService?wsdl',
                                                  'readFamily',
                                                  dossierNumber=family_id,
                                                  schoolYear=school_year))
         for child in family_data['childInfoList']:
             utils.normalize_person(child)
+        self.set_to_2keys_cache('family', family_id, school_year, family_data)
         return family_data
 
     def get_invoices(self, regie_id, name_id):
         family_id = self.get_link(name_id).family_id
         return [utils.normalize_invoice(i) for i in self.call(
             'InvoiceService?wsdl', 'readInvoices',
             numDossier=family_id, codeRegie=regie_id)]
 
@@ -293,16 +297,17 @@ class Maelis(BaseResource):
         for contact_param in ('phone', 'mobile', 'mail'):
             if contact_param in post_data:
                 params['contact'][contact_param] = post_data[contact_param]
 
         r = self.call('FamilyService?wsdl', 'updateCoordinate',
                       numDossier=link.family_id,
                       numPerson=personID,
                       **params)
+        self.invalidate_2keys_cache('family', link.family_id)
         return serialize_object(r)
 
     @endpoint(
         display_category=_('Invoices'),
         display_order=1,
         name='regie',
         perm='can_access',
         pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$',
diff --git a/tests/test_maelis.py b/tests/test_maelis.py
index 5790046b..03d7dd7e 100644
--- a/tests/test_maelis.py
+++ b/tests/test_maelis.py
@@ -193,8 +193,49 @@ def test_get_invoice_details(mocked_post, mocked_get, invoice_service_wsdl,
                                          status_code=200,
                                          headers={'Content-Type': 'text/xml'}
                                          )
     Link.objects.create(resource=connector, family_id='3264', name_id='local')
     resp = app.get('/maelis/test/regie/1/invoice/3264-53186?NameID=local')
     assert resp.json['data']
     assert resp.json['data']['label'] == 'FACTURATION SEPTEMBRE 2014'
     assert resp.json['data']['display_id'] == '53186'
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_family_cache(mocked_post, mocked_get, family_service_wsdl,
+                      connector, app):
+    mocked_get.return_value = mock.Mock(content=family_service_wsdl)
+    mocked_post.side_effect = (
+        utils.FakedResponse(content=get_xml_file('readFamily.xml'),
+                            status_code=200,
+                            headers={'Content-Type': 'text/xml'}),
+        utils.FakedResponse(content=get_xml_file('updateCoordinatesResponse.xml'),
+                            status_code=200,
+                            headers={'Content-Type': 'text/xml'}),
+        utils.FakedResponse(content=get_xml_file('readFamily.xml'),
+                            status_code=200,
+                            headers={'Content-Type': 'text/xml'}),
+    )
+
+    assert Link.objects.count() == 0
+    Link.objects.create(resource=connector, family_id='3264', name_id='local')
+    resp = app.get('/maelis/test/family-info?NameID=local')
+    assert not resp.json['err']
+    family_data = resp.json['data']
+    assert mocked_post.call_count == 1
+
+    # cached data
+    resp = app.get('/maelis/test/family-info?NameID=local')
+    assert family_data == resp.json['data']
+    assert mocked_post.call_count == 1
+
+    # invalidate cache
+    resp = app.post_json('/maelis/test/update-coordinates?NameID=local&personID=21293',
+                         params={'mail': 'foo@example.com'}
+                         )
+    assert resp.content is not None
+    assert mocked_post.call_count == 2
+
+    # no more cached data
+    resp = app.get('/maelis/test/family-info?NameID=local')
+    assert mocked_post.call_count == 3
-- 
2.29.2

