diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py index fd2a2cce..49ca78c7 100644 --- a/passerelle/contrib/toulouse_maelis/models.py +++ b/passerelle/contrib/toulouse_maelis/models.py @@ -113,10 +113,21 @@ class ToulouseMaelis(BaseResource, HTTPResource): except Link.DoesNotExist: raise APIError('User not linked to family', err_code='not-linked') - def get_family(self, family_id): + def get_family_raw(self, family_id): response = self.call('Family', 'readFamily', dossierNumber=family_id) data = serialize_object(response) + # make emergencyPersonList keys egal to authorizedPersonList + for person in data['emergencyPersonList']: + person['numPerson'] = person['num'] + person['dateBirth'] = '1970-01-01' # I need a date + del person['num'] + del person['id'] + return data + + def get_family(self, family_id): + data = self.get_family_raw(family_id) + def add_text_value(referential_name, data, keys): last_key = keys.pop() for key in keys: @@ -136,6 +147,10 @@ class ToulouseMaelis(BaseResource, HTTPResource): add_text_value('CSP', data, [rlg, 'profession', 'codeCSP']) for child in data['childList']: add_text_value('Sex', child, ['sexe']) + for kind in ('authorized', 'emergency'): + for person in data[kind + 'PersonList']: + add_text_value('Civility', person, ['civility']) + add_text_value('Quality', person, ['quality']) return data def replace_null_values(self, dico): @@ -285,6 +300,31 @@ class ToulouseMaelis(BaseResource, HTTPResource): raise APIError("no '%s' RL on '%s' family" % (rl_id, family_id), err_code='not-found') return {'data': data} + @endpoint( + display_category=_('Family'), + description="Informations sur une personne autorisée à récupérer les enfants ou à prévenir en cas d'urgence", + perm='can_access', + name='read-person', + parameters={ + 'NameID': {'description': _('Publik ID')}, + 'person_id': {'description': 'Numéro de la personne'}, + 'kind': {'description': "'authorized' (par defaut) ou 'emergency'"}, + }, + ) + def read_person(self, request, NameID, person_id, kind='authorized'): + if kind not in ('authorized', 'emergency'): + raise APIError("wrong '%s' value for kind parameter" % kind) + family_id = self.get_link(NameID).family_id + data = self.get_family(family_id) + for person in data[kind + 'PersonList']: + if str(person['numPerson']) == person_id: + break + else: + raise APIError( + "no '%s' %s person on '%s' family" % (person_id, kind, family_id), err_code='not-found' + ) + return {'data': person} + @endpoint( display_category=_('Family'), description="Vérifier qu'un responsable légal existe en base", @@ -358,6 +398,109 @@ class ToulouseMaelis(BaseResource, HTTPResource): self.call('Family', 'updateCoordinate', numDossier=family_id, numPerson=rl_id, **post_data) return {'data': 'ok'} + @endpoint( + display_category=_('Family'), + description="Création d'une personne autorisée à récupérer les enfants ou à prévenir en cas d'urgence", + name='create-person', + perm='can_access', + parameters={ + 'NameID': {'description': _('Publik ID')}, + 'kind': {'description': "'authorized' (par defaut) ou 'emergency'"}, + }, + post={'request_body': {'schema': {'application/json': schemas.FAMILYPERSON_SCHEMA}}}, + ) + def create_person(self, request, NameID, post_data, kind='authorized'): + if kind not in ('authorized', 'emergency'): + raise APIError("wrong '%s' value for kind parameter" % kind) + family_id = self.get_link(NameID).family_id + family = self.get_family_raw(family_id) + self.replace_null_values(post_data) + + personList = family[kind + 'PersonList'] + personList.append(post_data) + payload = { + 'dossierNumber': family_id, + 'categorie': family['category'], + 'situation': family['situation'], + kind + 'PersonList': [{'personList': personList}], + } + self.call('Family', 'updateFamily', **payload) + return {'data': 'ok'} + + @endpoint( + display_category=_('Family'), + description="Mise à jour d'une personne autorisée à récupérer les enfants ou à prévenir en cas d'urgence", + name='update-person', + perm='can_access', + parameters={ + 'NameID': {'description': _('Publik ID')}, + 'person_id': {'description': 'Numéro de la personne'}, + 'kind': {'description': "'authorized' (par defaut) ou 'emergency'"}, + }, + post={'request_body': {'schema': {'application/json': schemas.FAMILYPERSON_SCHEMA}}}, + ) + def update_person(self, request, NameID, person_id, post_data, kind='authorized'): + if kind not in ('authorized', 'emergency'): + raise APIError("wrong '%s' value for kind parameter" % kind) + family_id = self.get_link(NameID).family_id + family = self.get_family_raw(family_id) + self.replace_null_values(post_data) + + personList = family[kind + 'PersonList'] + for i, person in enumerate(personList): + if str(person['numPerson']) == person_id: + personList[i] = post_data + personList[i]['numPerson'] = person_id + break + else: + raise APIError( + "no '%s' authorized person on '%s' family" % (person_id, family_id), err_code='not-found' + ) + payload = { + 'dossierNumber': family_id, + 'categorie': family['category'], + 'situation': family['situation'], + kind + 'PersonList': [{'personList': personList}], + } + self.call('Family', 'updateFamily', **payload) + return {'data': 'ok'} + + @endpoint( + display_category=_('Family'), + description="Suppression d'une personne autorisée à récupérer les enfants ou à prévenir en cas d'urgence", + name='delete-person', + perm='can_access', + parameters={ + 'NameID': {'description': _('Publik ID')}, + 'person_id': {'description': 'Numéro de la personne'}, + 'kind': {'description': "'authorized' (par defaut) ou 'emergency'"}, + }, + methods=['post'], + ) + def delete_person(self, request, NameID, person_id, kind='authorized'): + if kind not in ('authorized', 'emergency'): + raise APIError("wrong '%s' value for kind parameter" % kind) + family_id = self.get_link(NameID).family_id + family = self.get_family_raw(family_id) + + personList = family[kind + 'PersonList'] + for i, person in enumerate(personList): + if str(person['numPerson']) == person_id: + del personList[i] + break + else: + raise APIError( + "no '%s' authorized person on '%s' family" % (person_id, family_id), err_code='not-found' + ) + payload = { + 'dossierNumber': family_id, + 'categorie': family['category'], + 'situation': family['situation'], + kind + 'PersonList': [{'personList': personList}], + } + self.call('Family', 'updateFamily', **payload) + return {'data': 'ok'} + class Link(models.Model): resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE) diff --git a/passerelle/contrib/toulouse_maelis/schemas.py b/passerelle/contrib/toulouse_maelis/schemas.py index 19cc394d..257c8227 100644 --- a/passerelle/contrib/toulouse_maelis/schemas.py +++ b/passerelle/contrib/toulouse_maelis/schemas.py @@ -265,6 +265,82 @@ RLINFO_SCHEMA = { }, } +CONTACTLIGHT_SCHEMA = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Contact', + 'description': "Informations de contact pour les personnes autorisées à récupérer les enfants ou à prévenir en cas d'urgence", + 'oneOf': [ + {'type': 'null'}, + { + 'type': 'object', + 'properties': { + 'phone': { + 'description': 'Téléphone', + 'oneOf': [{'type': 'null'}, {'type': 'string'}], + }, + 'mobile': { + 'description': 'Portable', + 'oneOf': [{'type': 'null'}, {'type': 'string'}], + }, + 'mail': { + 'description': 'Mail', + 'oneOf': [{'type': 'null'}, {'type': 'string'}], + }, + }, + }, + ], +} + +FAMILYPERSON_SCHEMA = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Family', + 'description': "Informations sur les personnes autorisées à venir chercher les enfants ou à prévenir en cas d'urgence", + 'type': 'object', + 'required': ['firstname', 'lastname', 'dateBirth'], + 'properties': { + 'civility': { + 'description': 'civilité (depuis référenciel)', + 'oneOf': [{'type': 'null'}, {'type': 'string'}], + }, + 'firstname': { + 'description': 'Prénom', + 'type': 'string', + }, + 'lastname': { + 'description': 'Nom', + 'type': 'string', + }, + 'quality': { + 'description': 'Qualité', + 'oneOf': [{'type': 'null'}, {'type': 'string'}], + }, + 'dateBirth': { + 'description': 'Date de naissance', + 'type': 'string', + 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$', + }, + 'contact': CONTACTLIGHT_SCHEMA, + }, + 'unflatten': True, +} + +AUTHORIZEDPERSON_SCHEMA = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Family', + 'description': "Informations sur les personnes autorisées à venir chercher les enfants ou à prévenir en cas d'urgence", + 'type': 'object', + 'properties': { + 'personList': { + 'oneOf': [ + {'type': 'null'}, + { + 'type': 'array', + 'items': FAMILYPERSON_SCHEMA, + }, + ], + }, + }, +} CREATE_FAMILY_SCHEMA = { '$schema': 'http://json-schema.org/draft-04/schema#', @@ -299,6 +375,24 @@ CREATE_FAMILY_SCHEMA = { }, 'rl1': RLINFO_SCHEMA, 'rl2': RLINFO_SCHEMA, + 'authorizedPersonList': { + 'oneOf': [ + {'type': 'null'}, + { + 'type': 'array', + 'items': AUTHORIZEDPERSON_SCHEMA, + }, + ], + }, + 'emergencyPersonList': { + 'oneOf': [ + {'type': 'null'}, + { + 'type': 'array', + 'items': AUTHORIZEDPERSON_SCHEMA, + }, + ], + }, }, 'unflatten': True, } diff --git a/tests/data/toulouse_maelis/Q_create_person.xml b/tests/data/toulouse_maelis/Q_create_person.xml new file mode 100644 index 00000000..bf96915a --- /dev/null +++ b/tests/data/toulouse_maelis/Q_create_person.xml @@ -0,0 +1,42 @@ + + + + + + maelis-webservice + maelis-password + + + + + + 1312 + BI + M + + + 614059 + KENY + ARKANA + 1970-01-01 + MERE + + 0123456789 + + + + + Mathias + Cassel + 1972-01-01 + PERE + + + 0623456789 + + + + + + + diff --git a/tests/data/toulouse_maelis/Q_delete_person.xml b/tests/data/toulouse_maelis/Q_delete_person.xml new file mode 100644 index 00000000..c9bf9ec0 --- /dev/null +++ b/tests/data/toulouse_maelis/Q_delete_person.xml @@ -0,0 +1,19 @@ + + + + + + maelis-webservice + maelis-password + + + + + + 1312 + BI + M + + + + diff --git a/tests/data/toulouse_maelis/Q_update_person.xml b/tests/data/toulouse_maelis/Q_update_person.xml new file mode 100644 index 00000000..a8e9c8e1 --- /dev/null +++ b/tests/data/toulouse_maelis/Q_update_person.xml @@ -0,0 +1,33 @@ + + + + + + maelis-webservice + maelis-password + + + + + + 1312 + BI + M + + + 614059 + + Mathias + Cassel + 1972-01-01 + PERE + + + 0623456789 + + + + + + + diff --git a/tests/data/toulouse_maelis/R_read_family.xml b/tests/data/toulouse_maelis/R_read_family.xml index faba8d64..84cbdc6e 100644 --- a/tests/data/toulouse_maelis/R_read_family.xml +++ b/tests/data/toulouse_maelis/R_read_family.xml @@ -59,6 +59,26 @@ 51 + + 614059 + S10032140599 + KENY + ARKANA + MERE + + 0123456789 + + + + 614058 + CATHY + PALENNE + 1975-06-28T00:00:00+01:00 + PERE + + 0123456789 + + 613880 COSTANZE diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py index 42bb8bd9..87dc7ed4 100644 --- a/tests/test_toulouse_maelis.py +++ b/tests/test_toulouse_maelis.py @@ -450,6 +450,16 @@ def test_read_family(mocked_post, mocked_get, con, app): 'subscribeActivityList': [], 'paiInfoBean': None, } + assert resp.json['data']['emergencyPersonList'][0] == { + 'firstname': 'KENY', + 'lastname': 'ARKANA', + 'quality': 'MERE', + 'civility': None, + 'contact': {'phone': '0123456789', 'mobile': None, 'mail': None}, + 'numPerson': 614059, + 'dateBirth': '1970-01-01', + 'quality_text': 'MERE', + } def test_read_family_not_linked_error(con, app): @@ -535,6 +545,48 @@ def test_read_rl_not_found(mocked_post, mocked_get, con, app): assert resp.json['err_desc'] == "no '000000' RL on '1312' family" +@mock.patch('passerelle.utils.Request.get') +@mock.patch('passerelle.utils.Request.post') +def test_read_person(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES] + url = get_endpoint('read-person') + Link.objects.create(resource=con, family_id='1312', name_id='local') + + resp = app.get(url + '?NameID=local&person_id=614059&kind=emergency') + assert resp.json['err'] == 0 + assert resp.json['data']['firstname'] == 'KENY' + + +def test_read_person_wrong_parameter(con, app): + url = get_endpoint('read-person') + + resp = app.get(url + '?NameID=local&person_id=000000&kind=baby-sitter') + assert resp.json['err'] == 1 + assert resp.json['err_desc'] == "wrong 'baby-sitter' value for kind parameter" + + +def test_read_person_not_linked_error(con, app): + url = get_endpoint('read-person') + + resp = app.get(url + '?NameID=local&person_id=614059') + 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_read_person_not_found(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES] + url = get_endpoint('read-person') + Link.objects.create(resource=con, family_id='1312', name_id='local') + + resp = app.get(url + '?NameID=local&person_id=000000&kind=emergency') + assert resp.json['err'] == 'not-found' + assert resp.json['err_desc'] == "no '000000' emergency person on '1312' family" + + @pytest.mark.parametrize( 'post_response, result', [ @@ -771,3 +823,226 @@ def test_update_coordinate_schema_error(con, app): resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params, status=400) assert resp.json['err'] == 1 assert resp.json['err_desc'] == "'true more text' is not of type 'boolean'" + + +@mock.patch('passerelle.utils.Request.get') +@mock.patch('passerelle.utils.Request.post') +def test_create_person(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, UPDATE_FAMILY] + url = get_endpoint('create-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + Link.objects.create(resource=con, family_id='1312', name_id='local') + resp = app.post_json(url + '?NameID=local&kind=emergency', params=params) + assert_sent_payload(mocked_post, 'Q_create_person.xml') + assert resp.json['err'] == 0 + + +def test_create_person_wrong_parameter(con, app): + url = get_endpoint('create-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&kind=baby-sitter', params=params) + assert resp.json['err'] == 1 + assert resp.json['err_desc'] == "wrong 'baby-sitter' value for kind parameter" + + +def test_create_person_not_linked_error(con, app): + url = get_endpoint('create-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&kind=emergency', params=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_person(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, UPDATE_FAMILY] + url = get_endpoint('update-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + Link.objects.create(resource=con, family_id='1312', name_id='local') + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=emergency', params=params) + assert_sent_payload(mocked_post, 'Q_update_person.xml') + assert resp.json['err'] == 0 + + +def test_update_person_wrong_parameter(con, app): + url = get_endpoint('update-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=baby-sitter', params=params) + assert resp.json['err'] == 1 + assert resp.json['err_desc'] == "wrong 'baby-sitter' value for kind parameter" + + +def test_update_person_not_linked_error(con, app): + url = get_endpoint('update-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=emergency', params=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_person_not_found(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, UPDATE_FAMILY] + url = get_endpoint('update-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + Link.objects.create(resource=con, family_id='1312', name_id='local') + resp = app.post_json(url + '?NameID=local&person_id=000000&kind=emergency', params=params) + assert resp.json['err'] == 'not-found' + assert resp.json['err_desc'] == "no '000000' authorized person on '1312' family" + + +@mock.patch('passerelle.utils.Request.get') +@mock.patch('passerelle.utils.Request.post') +def test_delete_person(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, UPDATE_FAMILY] + url = get_endpoint('delete-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + Link.objects.create(resource=con, family_id='1312', name_id='local') + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=emergency', params=params) + assert_sent_payload(mocked_post, 'Q_delete_person.xml') + assert resp.json['err'] == 0 + + +def test_delete_person_wrong_parameter(con, app): + url = get_endpoint('delete-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=baby-sitter', params=params) + assert resp.json['err'] == 1 + assert resp.json['err_desc'] == "wrong 'baby-sitter' value for kind parameter" + + +def test_delete_person_not_linked_error(con, app): + url = get_endpoint('delete-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + resp = app.post_json(url + '?NameID=local&person_id=614059&kind=emergency', params=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_delete_person_not_found(mocked_post, mocked_get, con, app): + mocked_get.return_value = FAMILY_SERVICE_WSDL + mocked_post.side_effect = [READ_FAMILY, UPDATE_FAMILY] + url = get_endpoint('delete-person') + params = { + 'civility': None, + 'firstname': 'Mathias', + 'lastname': 'Cassel', + 'quality': 'PERE', + 'dateBirth': '1972-01-01', + 'contact/phone': None, + 'contact/mobile': '0623456789', + 'contact/mail': None, + } + + Link.objects.create(resource=con, family_id='1312', name_id='local') + resp = app.post_json(url + '?NameID=local&person_id=000000&kind=emergency', params=params) + assert resp.json['err'] == 'not-found' + assert resp.json['err_desc'] == "no '000000' authorized person on '1312' family"