From 09c49e9ae5183ba028b66a323a45424f19e934c2 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Sun, 11 Sep 2022 17:07:32 +0200
Subject: [PATCH 5/5] toulouse_maelis: add medical record webservice (#69045)

---
 passerelle/contrib/toulouse_maelis/models.py  | 22 ++++++++
 passerelle/contrib/toulouse_maelis/schemas.py |  7 +--
 .../Q_update_child_medical_record.xml         | 36 +++++++++++++
 .../R_update_child_medical_record.xml         |  5 ++
 ...update_child_medical_record_soap_error.xml |  8 +++
 tests/test_toulouse_maelis.py                 | 54 +++++++++++++++++++
 6 files changed, 129 insertions(+), 3 deletions(-)
 create mode 100644 tests/data/toulouse_maelis/Q_update_child_medical_record.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_child_medical_record.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_child_medical_record_soap_error.xml

diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py
index 5c8cf059..34aae318 100644
--- a/passerelle/contrib/toulouse_maelis/models.py
+++ b/passerelle/contrib/toulouse_maelis/models.py
@@ -666,16 +666,38 @@ class ToulouseMaelis(BaseResource, HTTPResource):
 
         # use None to empty optional date passed as an empty string by date filter
         if post_data.get('dtcPrap1') == '':
             post_data['dtcPrap1'] = None
 
         self.call('Family', 'createOrUpdateFSL', arg0=child_id, arg1=post_data)
         return {'data': 'ok'}
 
+    @endpoint(
+        display_category='Famille',
+        description="Créer ou mettre à jour les données médicales d'un enfant",
+        name='update-child-medical-record',
+        perm='can_access',
+        parameters={
+            'NameID': {'description': 'Publik NameID'},
+            'child_id': {'description': "Numéro de l'enfant"},
+        },
+        post={'request_body': {'schema': {'application/json': schemas.MEDICALRECORD_SCHEMA}}},
+    )
+    def update_child_medical_record(self, request, NameID, child_id, post_data):
+        self.get_link(NameID)
+        self.replace_null_values(post_data)
+
+        payload = {
+            'numPerson': child_id,
+            'medicalRecord': post_data,
+        }
+        self.call('Family', 'updateChildMedicalRecord', updateChildMedicalRecordRequest=payload)
+        return {'data': 'ok'}
+
 
 class Link(models.Model):
     resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE)
     name_id = models.CharField(blank=False, max_length=256)
     family_id = models.CharField(blank=False, max_length=128)
     created = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(auto_now=True)
 
diff --git a/passerelle/contrib/toulouse_maelis/schemas.py b/passerelle/contrib/toulouse_maelis/schemas.py
index 02c0b965..ecdf56f7 100644
--- a/passerelle/contrib/toulouse_maelis/schemas.py
+++ b/passerelle/contrib/toulouse_maelis/schemas.py
@@ -369,25 +369,25 @@ FSL_SCHEMA = {
 DOCTORADDRESS_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
     'title': 'Doctor address',
     'description': "Informations sur l'adresse du docteur",
     'type': 'object',
     'properties': {
         'street1': {
             'description': 'Libellé de la voie',
-            'type': 'string',
+            'oneOf': [{'type': 'null'}, {'type': 'string'}],
         },
         'town': {
             'description': 'Ville',
-            'type': 'string',
+            'oneOf': [{'type': 'null'}, {'type': 'string'}],
         },
         'zipcode': {
             'description': 'Code postal',
-            'type': 'string',
+            'oneOf': [{'type': 'null'}, {'type': 'string'}],
         },
     },
 }
 
 FAMILYDOCTOR_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
     'title': 'Family doctor',
     'description': "Informations sur le docteur",
@@ -444,16 +444,17 @@ MEDICALRECORD_SCHEMA = {
                             'type': 'array',
                             'items': VACCIN_SCHEMA,
                         },
                     ],
                 },
             },
         },
     ],
+    'unflatten': True,
 }
 
 PAIINFO_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
     'title': 'PAI Info',
     'description': "Informations médicales",
     'oneOf': [
         {'type': 'null'},
diff --git a/tests/data/toulouse_maelis/Q_update_child_medical_record.xml b/tests/data/toulouse_maelis/Q_update_child_medical_record.xml
new file mode 100644
index 00000000..9cd3b0f4
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_child_medical_record.xml
@@ -0,0 +1,36 @@
+<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap-env:Header>
+    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+      <wsse:UsernameToken>
+        <wsse:Username>maelis-webservice</wsse:Username>
+        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
+      </wsse:UsernameToken>
+    </wsse:Security>
+  </soap-env:Header>
+  <soap-env:Body>
+    <ns0:updateChildMedicalRecord xmlns:ns0="family.ws.maelis.sigec.com">
+      <updateChildMedicalRecordRequest>
+        <numPerson>613878</numPerson>
+        <medicalRecord>
+          <familyDoctor>
+            <name>Dre</name>
+            <phone>0612341234</phone>
+            <address>
+              <street1>Alameda</street1>
+              <zipcode>90220</zipcode>
+              <town>Compton</town>
+            </address>
+          </familyDoctor>
+          <vaccinList>
+            <code>DTC</code>
+            <vaccinationDate>2011-01-11</vaccinationDate>
+          </vaccinList>
+          <vaccinList>
+            <code>ROR</code>
+            <vaccinationDate>2022-02-22</vaccinationDate>
+          </vaccinList>
+        </medicalRecord>
+      </updateChildMedicalRecordRequest>
+    </ns0:updateChildMedicalRecord>
+  </soap-env:Body>
+</soap-env:Envelope>
diff --git a/tests/data/toulouse_maelis/R_update_child_medical_record.xml b/tests/data/toulouse_maelis/R_update_child_medical_record.xml
new file mode 100644
index 00000000..f8c57bfa
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_child_medical_record.xml
@@ -0,0 +1,5 @@
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap:Body>
+    <ns2:updateChildMedicalRecordResponse xmlns:ns2="family.ws.maelis.sigec.com"/>
+  </soap:Body>
+</soap:Envelope>
diff --git a/tests/data/toulouse_maelis/R_update_child_medical_record_soap_error.xml b/tests/data/toulouse_maelis/R_update_child_medical_record_soap_error.xml
new file mode 100644
index 00000000..785b16f9
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_child_medical_record_soap_error.xml
@@ -0,0 +1,8 @@
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap:Body>
+    <soap:Fault>
+      <faultcode>soap:Client</faultcode>
+      <faultstring>Unmarshalling Error: "2012-02-31" ne représente pas une valeur de calendrier grégorien XML valide. </faultstring>
+    </soap:Fault>
+  </soap:Body>
+</soap:Envelope>
diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py
index fa422ccc..dc6dd040 100644
--- a/tests/test_toulouse_maelis.py
+++ b/tests/test_toulouse_maelis.py
@@ -64,16 +64,20 @@ CREATE_FAMILY_ERR = FakedResponse(content=get_xml_file('R_create_family_error.xm
 UPDATE_FAMILY = FakedResponse(content=get_xml_file('R_update_family.xml'), status_code=200)
 UPDATE_FAMILY_ERR = FakedResponse(content=get_xml_file('R_update_family_error.xml'), status_code=200)
 UPDATE_FAMILY_500 = FakedResponse(content=get_xml_file('R_update_family_soap_error.xml'), status_code=500)
 UPDATE_DIETCODE = FakedResponse(content=get_xml_file('R_update_child_dietcode.xml'), status_code=200)
 UPDATE_PAI = FakedResponse(content=get_xml_file('R_update_child_pai.xml'), status_code=200)
 UPDATE_PAI_500 = FakedResponse(content=get_xml_file('R_update_child_pai_soap_error.xml'), status_code=500)
 UPDATE_FSL = FakedResponse(content=get_xml_file('R_update_child_fsl.xml'), status_code=200)
 UPDATE_FSL_500 = FakedResponse(content=get_xml_file('R_update_child_fsl_soap_error.xml'), status_code=500)
+UPDATE_MEDICAL = FakedResponse(content=get_xml_file('R_update_child_fsl.xml'), status_code=200)
+UPDATE_MEDICAL_500 = FakedResponse(
+    content=get_xml_file('R_update_child_medical_record_soap_error.xml'), status_code=500
+)
 
 
 def assert_sent_payload(mocked_post, query_file):
     soap_sent = etree.tostring(etree.fromstring(mocked_post.call_args.kwargs['data']), pretty_print=True)
     expected = etree.tostring(etree.fromstring(get_xml_file(query_file)), pretty_print=True)
     assert soap_sent.decode() == expected.decode()
 
 
@@ -1527,8 +1531,58 @@ def test_update_child_fsl_soap_error(mocked_post, mocked_get, con, app):
     }
 
     Link.objects.create(resource=con, family_id='1312', name_id='local')
     resp = app.post_json(url + '?NameID=local&child_id=613878', params=params)
     assert resp.json['err'] == 'Family-createOrUpdateFSL-soap:Server'
     assert 'Une erreur est survenue' in resp.json['err_desc']
     assert 'valeur trop grande' in resp.json['err_desc']
     assert 'maximum : 70' in resp.json['err_desc']
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_child_medical_record(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.return_value = UPDATE_MEDICAL
+    url = get_endpoint('update-child-medical-record')
+    params = {
+        'familyDoctor/name': 'Dre',
+        'familyDoctor/phone': '0612341234',
+        'familyDoctor/address/street1': 'Alameda',
+        'familyDoctor/address/zipcode': '90220',
+        'familyDoctor/address/town': 'Compton',
+        'vaccinList/0/code': 'DTC',
+        'vaccinList/0/vaccinationDate': '2011-01-11',
+        'vaccinList/1/code': 'ROR',
+        'vaccinList/1/vaccinationDate': '2022-02-22',
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613878', params=params)
+    assert_sent_payload(mocked_post, 'Q_update_child_medical_record.xml')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == 'ok'
+
+
+def test_update_child_medical_record_not_linked_error(con, app):
+    url = get_endpoint('update-child-medical-record')
+
+    resp = app.post_json(url + '?NameID=local&child_id=613878', params={})
+    assert resp.json['err'] == 'not-linked'
+    assert resp.json['err_desc'] == 'User not linked to family'
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_child_medical_record_soap_error(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.return_value = UPDATE_MEDICAL_500
+    url = get_endpoint('update-child-medical-record')
+    params = {
+        'vaccinList/0/vaccinationDate': '2022-02-31',
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613878', params=params)
+    assert resp.json['err'] == 'Family-updateChildMedicalRecord-soap:Client'
+    assert 'Unmarshalling Error' in resp.json['err_desc']
+    assert 'pas une valeur de calendrier grégorien' in resp.json['err_desc']
-- 
2.30.2

