Projet

Général

Profil

0001-toulouse_maelis-parsifal-add-webservices-to-manage-R.patch

Nicolas Roche (absent jusqu'au 3 avril), 13 juillet 2022 00:14

Télécharger (39,5 ko)

Voir les différences:

Subject: [PATCH] toulouse_maelis: parsifal: add webservices to manage RL
 (#67326)

 passerelle/contrib/toulouse_maelis/models.py  | 102 ++++++
 passerelle/contrib/toulouse_maelis/schemas.py | 278 +++++++++++++++
 .../data/toulouse_maelis/Q_create_family.xml  |  28 ++
 .../toulouse_maelis/Q_update_coordinate.xml   |  25 ++
 .../data/toulouse_maelis/Q_update_family.xml  |  29 ++
 .../data/toulouse_maelis/R_create_family.xml  |  11 +
 .../toulouse_maelis/R_create_family_error.xml |  11 +
 tests/data/toulouse_maelis/R_is_rl_exists.xml |   8 +
 .../toulouse_maelis/R_update_coordinate.xml   |   6 +
 .../data/toulouse_maelis/R_update_family.xml  |  32 ++
 .../toulouse_maelis/R_update_family_error.xml |  16 +
 tests/test_toulouse_maelis.py                 | 325 ++++++++++++++++++
 12 files changed, 871 insertions(+)
 create mode 100644 tests/data/toulouse_maelis/Q_create_family.xml
 create mode 100644 tests/data/toulouse_maelis/Q_update_coordinate.xml
 create mode 100644 tests/data/toulouse_maelis/Q_update_family.xml
 create mode 100644 tests/data/toulouse_maelis/R_create_family.xml
 create mode 100644 tests/data/toulouse_maelis/R_create_family_error.xml
 create mode 100644 tests/data/toulouse_maelis/R_is_rl_exists.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_coordinate.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_family.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_family_error.xml
passerelle/contrib/toulouse_maelis/models.py
125 125
        add_text_value('Situation', data, ['situation'])
126 126
        for rlg in 'RL1', 'RL2':
127 127
            add_text_value('Civility', data, [rlg, 'civility'])
128 128
            add_text_value('Quality', data, [rlg, 'quality'])
129 129
            add_text_value('Complement', data, [rlg, 'adresse', 'numComp'])
130 130
            add_text_value('CSP', data, [rlg, 'profession', 'codeCSP'])
131 131
        return data
132 132

  
133
    def replace_null_values(self, dico):
134
        '''send null fields as empty SOAP tag to tell maelis to empty the value'''
135
        for key, value in dico.items():
136
            if isinstance(value, dict):
137
                self.replace_null_values(value)
138
            if value is None:
139
                dico[key] = ''
140

  
133 141
    @endpoint(
134 142
        display_category=_('Family'),
135 143
        description='Liste des catégories',
136 144
        name='read-category-list',
137 145
        perm='can_access',
138 146
    )
139 147
    def read_category_list(self, request):
140 148
        return {'data': self.get_referential('Category')['list']}
......
234 242
        name='read-family',
235 243
        parameters={'NameID': {'description': _('Publik ID')}},
236 244
    )
237 245
    def read_family(self, request, NameID):
238 246
        family_id = self.get_link(NameID).family_id
239 247
        data = self.get_family(family_id)
240 248
        return {'data': data}
241 249

  
250
    @endpoint(
251
        display_category=_('Family'),
252
        description="Informations sur un responsable légal",
253
        perm='can_access',
254
        name='read-rl',
255
        parameters={
256
            'NameID': {'description': _('Publik ID')},
257
            'rl_id': {'description': 'Numéro du représentant légal'},
258
        },
259
    )
260
    def read_rl(self, request, NameID, rl_id):
261
        family_id = self.get_link(NameID).family_id
262
        data = self.get_family(family_id)
263
        if data['RL1']['num'] == rl_id:
264
            data = data['RL1']
265
        elif data['RL2'] and data['RL2']['num'] == rl_id:
266
            data = data['RL2']
267
        else:
268
            raise APIError("no '%s' RL on '%s' family" % (rl_id, family_id), err_code='not-found')
269
        return {'data': data}
270

  
271
    @endpoint(
272
        display_category=_('Family'),
273
        description="Vérifier qu'un responsable légal existe en base",
274
        perm='can_access',
275
        name='is-rl-exists',
276
        post={'request_body': {'schema': {'application/json': schemas.ISRLEXISTS_SCHEMA}}},
277
    )
278
    def is_rl_exists(self, request, post_data):
279
        response = self.call('Family', 'isRLExists', **post_data)
280
        return {'data': response}
281

  
282
    @endpoint(
283
        display_category=_('Family'),
284
        description='Création de la famille',
285
        name='create-family',
286
        perm='can_access',
287
        parameters={'NameID': {'description': _('Publik ID')}},
288
        post={'request_body': {'schema': {'application/json': schemas.FAMILY_SCHEMA}}},
289
    )
290
    def create_family(self, request, NameID, post_data):
291
        if self.link_set.filter(name_id=NameID).exists():
292
            raise APIError('User already linked to family', err_code='already-linked')
293

  
294
        response = self.call('Family', 'createFamily', **post_data)
295
        data = serialize_object(response)
296
        family_id = data.get('number')
297
        if not family_id:
298
            errors = data.get('rl1ErrorList') + data.get('childErrorList')
299
            err_codes = [x.split(':')[0][:4] for x in errors]
300
            raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
301

  
302
        Link.objects.create(resource=self, name_id=NameID, family_id=family_id)
303
        return {'data': data}
304

  
305
    @endpoint(
306
        display_category=_('Family'),
307
        description='Modification de la famille',
308
        name='update-family',
309
        perm='can_access',
310
        parameters={'NameID': {'description': _('Publik ID')}},
311
        post={'request_body': {'schema': {'application/json': schemas.FAMILY_SCHEMA}}},
312
    )
313
    def update_family(self, request, NameID, post_data):
314
        family_id = self.get_link(NameID).family_id
315
        self.replace_null_values(post_data)
316

  
317
        response = self.call('Family', 'updateFamily', dossierNumber=family_id, **post_data)
318
        data = serialize_object(response)
319
        family_id = data.get('number')
320
        if not family_id:
321
            errors = data.get('rl1ErrorList') + data.get('childErrorList')
322
            err_codes = [x.split(':')[0][:4] for x in errors]
323
            raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
324
        return {'data': data}
325

  
326
    @endpoint(
327
        display_category=_('Family'),
328
        description="Mise à jour des coordonnées d'une personne",
329
        name='update-coordinate',
330
        perm='can_access',
331
        parameters={
332
            'NameID': {'description': _('Publik ID')},
333
            'rl_id': {'description': 'Numéro du représentant légal'},
334
        },
335
        post={'request_body': {'schema': {'application/json': schemas.UPDATE_COORDINATE_SCHEMA}}},
336
    )
337
    def update_coordinate(self, request, NameID, rl_id, post_data):
338
        family_id = self.get_link(NameID).family_id
339
        self.replace_null_values(post_data)
340

  
341
        self.call('Family', 'updateCoordinate', numDossier=family_id, numPerson=rl_id, **post_data)
342
        return {'data': 'ok'}
343

  
242 344

  
243 345
class Link(models.Model):
244 346
    resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE)
245 347
    name_id = models.CharField(blank=False, max_length=256)
246 348
    family_id = models.CharField(blank=False, max_length=128)
247 349
    created = models.DateTimeField(auto_now_add=True)
248 350
    updated = models.DateTimeField(auto_now=True)
249 351

  
passerelle/contrib/toulouse_maelis/schemas.py
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU Affero General Public License for more details.
13 13
#
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
BOOLEAN_TYPES = [
18
    {'type': 'boolean'},
19
    {
20
        'type': 'string',
21
        'pattern': '[Oo][Uu][Ii]|[Nn][Oo][Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0',
22
        'pattern_description': 'Les valeurs "0", "1", "true", "false", "oui" ou "non" sont autorisées (insensibles à la casse).',
23
    },
24
]
25

  
17 26
LINK_SCHEMA = {
18 27
    '$schema': 'http://json-schema.org/draft-04/schema#',
19 28
    'title': 'Link',
20 29
    'description': "Appairage d'un usager Publik à une famille dans Maelis",
21 30
    'type': 'object',
22 31
    'required': ['family_id', 'firstname', 'lastname', 'dateBirth'],
23 32
    'properties': {
24 33
        'family_id': {
......
35 44
        },
36 45
        'dateBirth': {
37 46
            'description': 'date de naissance du RL1',
38 47
            'type': 'string',
39 48
            'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}',
40 49
        },
41 50
    },
42 51
}
52

  
53
ISRLEXISTS_SCHEMA = {
54
    '$schema': 'http://json-schema.org/draft-04/schema#',
55
    'title': 'Link',
56
    'description': "Appairage d'un usager Publik à une famille dans Maelis",
57
    'type': 'object',
58
    'required': ['firstname', 'lastname', 'datebirth'],
59
    'properties': {
60
        'firstname': {
61
            'description': 'Prénom',
62
            'type': 'string',
63
        },
64
        'lastname': {
65
            'description': 'Nom',
66
            'type': 'string',
67
        },
68
        'datebirth': {
69
            'description': 'Date de naissance',
70
            'type': 'string',
71
            'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}',
72
        },
73
    },
74
}
75

  
76
ADDRESS_SCHEMA = {
77
    '$schema': 'http://json-schema.org/draft-04/schema#',
78
    'title': 'Address',
79
    'description': 'Informations sur une adresse',
80
    'type': 'object',
81
    'required': ['street1', 'town', 'zipcode'],
82
    'properties': {
83
        'num': {
84
            'description': 'numéro',
85
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
86
        },
87
        'numComp': {
88
            'description': 'Complément du numéro (B, T ou Q)',
89
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
90
        },
91
        'street1': {
92
            'description': 'Libellé de la voie',
93
            'type': 'string',
94
        },
95
        'street2': {
96
            'description': 'Complément de la voie',
97
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
98
        },
99
        'town': {
100
            'description': 'Ville',
101
            'type': 'string',
102
        },
103
        'zipcode': {
104
            'description': 'Code postal',
105
            'type': 'string',
106
        },
107
    },
108
}
109

  
110
CONTACT_SCHEMA = {
111
    '$schema': 'http://json-schema.org/draft-04/schema#',
112
    'title': 'Contact',
113
    'description': 'Informations sur le contact',
114
    'oneOf': [
115
        {'type': 'null'},
116
        {
117
            'type': 'object',
118
            'properties': {
119
                'phone': {
120
                    'description': 'Téléphone',
121
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
122
                },
123
                'mobile': {
124
                    'description': 'Portable',
125
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
126
                },
127
                'mail': {
128
                    'description': 'Mail',
129
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
130
                },
131
                'isContactMail': {
132
                    'description': 'Accepte de recevoir des mails',
133
                    'oneOf': BOOLEAN_TYPES,
134
                },
135
                'isContactSms': {
136
                    'description': 'Accepte de recevoir des sms',
137
                    'oneOf': BOOLEAN_TYPES,
138
                },
139
                'isInvoicePdf': {
140
                    'description': 'Accepte de ne plus recevoir de facture papier',
141
                    'oneOf': BOOLEAN_TYPES,
142
                },
143
            },
144
        },
145
    ],
146
}
147

  
148
ADDRESSPROF_SCHEMA = {
149
    '$schema': 'http://json-schema.org/draft-04/schema#',
150
    'title': 'Contact',
151
    'description': "Informations sur l'adresse professionnelle",
152
    'oneOf': [
153
        {'type': 'null'},
154
        {
155
            'type': 'object',
156
            'properties': {
157
                'num': {
158
                    'description': "Numéro de l'adresse",
159
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
160
                },
161
                'street': {
162
                    'description': 'Nom de la voie',
163
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
164
                },
165
                'town': {
166
                    'description': 'Ville',
167
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
168
                },
169
                'zipcode': {
170
                    'description': 'Code postal',
171
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
172
                },
173
            },
174
        },
175
    ],
176
}
177

  
178
PROFESSION_SCHEMA = {
179
    '$schema': 'http://json-schema.org/draft-04/schema#',
180
    'title': 'Profession',
181
    'description': 'Informations sur la profession',
182
    'oneOf': [
183
        {'type': 'null'},
184
        {
185
            'type': 'object',
186
            'properties': {
187
                'codeCSP': {
188
                    'description': 'Catégorie socio-professionnelle',
189
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
190
                },
191
                'profession': {
192
                    'description': 'Profession',
193
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
194
                },
195
                'employerName': {
196
                    'description': "Nom de l'employeur",
197
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
198
                },
199
                'phone': {
200
                    'description': 'Téléphone',
201
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
202
                },
203
                'addressPro': ADDRESSPROF_SCHEMA,
204
            },
205
        },
206
    ],
207
}
208

  
209
CAFINFO_SCHEMA = {
210
    '$schema': 'http://json-schema.org/draft-04/schema#',
211
    'title': 'Contact',
212
    'description': 'Informations sur la CAF',
213
    'oneOf': [
214
        {'type': 'null'},
215
        {
216
            'type': 'object',
217
            'properties': {
218
                'number': {
219
                    'description': "Numéro d'allocataire",
220
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
221
                },
222
                'organ': {
223
                    'description': "Nom de l'organisme",
224
                    'oneOf': [{'type': 'null'}, {'type': 'string'}],
225
                },
226
            },
227
        },
228
    ],
229
}
230

  
231
RLINFO_SCHEMA = {
232
    '$schema': 'http://json-schema.org/draft-04/schema#',
233
    'title': 'RLInfo',
234
    'description': "Informations sur le responsable légal",
235
    'type': 'object',
236
    'required': ['firstname', 'lastname', 'quality', 'dateBirth', 'adresse'],
237
    'properties': {
238
        'civility': {
239
            'description': 'civilité (depuis référenciel)',
240
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
241
        },
242
        'firstname': {
243
            'description': 'Prénom',
244
            'type': 'string',
245
        },
246
        'lastname': {
247
            'description': 'Nom',
248
            'type': 'string',
249
        },
250
        'quality': {
251
            'description': 'Qualité',
252
            'type': 'string',
253
        },
254
        'dateBirth': {
255
            'description': 'Date de naissance',
256
            'type': 'string',
257
            'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}',
258
        },
259
        'adresse': ADDRESS_SCHEMA,
260
        'contact': CONTACT_SCHEMA,
261
        'profession': PROFESSION_SCHEMA,
262
        'CAFInfo': CAFINFO_SCHEMA,
263
    },
264
}
265

  
266

  
267
FAMILY_SCHEMA = {
268
    '$schema': 'http://json-schema.org/draft-04/schema#',
269
    'title': 'Family',
270
    'description': 'Informations pour créer ou mettre à jour une famille',
271
    'type': 'object',
272
    'required': ['categorie', 'situation'],
273
    "oneOf": [
274
        {"required": ['rl1']},
275
        {"required": ['rl2']},
276
    ],
277
    'properties': {
278
        'categorie': {
279
            'description': 'Categorie (depuis référenciel)',
280
            'type': 'string',
281
        },
282
        'situation': {
283
            'description': 'Situation familiale (depuis référenciel)',
284
            'type': 'string',
285
        },
286
        'flagCom': {
287
            'description': 'Hors commune',
288
            'oneOf': BOOLEAN_TYPES,
289
        },
290
        'nbChild': {
291
            'description': "Nombre d'enfants à charge",
292
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
293
        },
294
        'nbTotalChild': {
295
            'description': "Nombre total d'enfants",
296
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
297
        },
298
        'nbAES': {
299
            'description': "Nombre d'AES",
300
            'oneOf': [{'type': 'null'}, {'type': 'string'}],
301
        },
302
        'rl1': RLINFO_SCHEMA,
303
        'rl2': RLINFO_SCHEMA,
304
    },
305
    'unflatten': True,
306
}
307

  
308
UPDATE_COORDINATE_SCHEMA = {
309
    '$schema': 'http://json-schema.org/draft-04/schema#',
310
    'title': 'Update coordinate',
311
    'description': "Mise à jour des coordonnées d'une personne",
312
    'type': 'object',
313
    'properties': {
314
        'adresse': ADDRESS_SCHEMA,
315
        'contact': CONTACT_SCHEMA,
316
        'profession': PROFESSION_SCHEMA,
317
        'CAFInfo': CAFINFO_SCHEMA,
318
    },
319
    'unflatten': True,
320
}
tests/data/toulouse_maelis/Q_create_family.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap-env:Header>
4
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
5
      <wsse:UsernameToken>
6
        <wsse:Username>maelis-webservice</wsse:Username>
7
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
8
      </wsse:UsernameToken>
9
    </wsse:Security>
10
  </soap-env:Header>
11
  <soap-env:Body>
12
    <ns0:createFamily xmlns:ns0="family.ws.maelis.sigec.com">
13
      <categorie>ACCEUI</categorie>
14
      <situation>C</situation>
15
      <rl1>
16
        <lastname>Doe</lastname>
17
        <firstname>Jhon</firstname>
18
        <quality>AU</quality>
19
        <dateBirth>1938-07-26</dateBirth>
20
        <adresse>
21
          <street1>Chateau</street1>
22
          <town>Paris</town>
23
          <zipcode>75014</zipcode>
24
        </adresse>
25
      </rl1>
26
    </ns0:createFamily>
27
  </soap-env:Body>
28
</soap-env:Envelope>
tests/data/toulouse_maelis/Q_update_coordinate.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap-env:Header>
4
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
5
      <wsse:UsernameToken>
6
        <wsse:Username>maelis-webservice</wsse:Username>
7
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
8
      </wsse:UsernameToken>
9
    </wsse:Security>
10
  </soap-env:Header>
11
  <soap-env:Body>
12
    <ns0:updateCoordinate xmlns:ns0="family.ws.maelis.sigec.com">
13
      <numDossier>1312</numDossier>
14
      <numPerson>613878</numPerson>
15
      <adresse>
16
        <num>169</num>
17
        <numComp/>
18
        <street1>Château</street1>
19
        <street2/>
20
        <town>Paris</town>
21
        <zipcode>75014</zipcode>
22
      </adresse>
23
    </ns0:updateCoordinate>
24
  </soap-env:Body>
25
</soap-env:Envelope>
tests/data/toulouse_maelis/Q_update_family.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap-env:Header>
4
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
5
      <wsse:UsernameToken>
6
        <wsse:Username>maelis-webservice</wsse:Username>
7
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
8
      </wsse:UsernameToken>
9
    </wsse:Security>
10
  </soap-env:Header>
11
  <soap-env:Body>
12
    <ns0:updateFamily xmlns:ns0="family.ws.maelis.sigec.com">
13
      <dossierNumber>1312</dossierNumber>
14
      <categorie>BI</categorie>
15
      <situation>C</situation>
16
      <rl1>
17
        <lastname>Doe</lastname>
18
        <firstname>Jhon</firstname>
19
        <quality>AU</quality>
20
        <dateBirth>1938-07-26</dateBirth>
21
        <adresse>
22
          <street1>Chateau</street1>
23
          <town>Paris</town>
24
          <zipcode>75014</zipcode>
25
        </adresse>
26
      </rl1>
27
    </ns0:updateFamily>
28
  </soap-env:Body>
29
</soap-env:Envelope>
tests/data/toulouse_maelis/R_create_family.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <ns2:createFamilyResponse xmlns:ns2="family.ws.maelis.sigec.com">
5
      <familyResult>
6
        <number>196545</number>
7
        <password>394634V2</password>
8
      </familyResult>
9
    </ns2:createFamilyResponse>
10
  </soap:Body>
11
</soap:Envelope>
tests/data/toulouse_maelis/R_create_family_error.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <ns2:createFamilyResponse xmlns:ns2="family.ws.maelis.sigec.com">
5
      <familyResult>
6
        <number>0</number>
7
        <rl1ErrorList>E54a : Il existe déjà un Responsable Légal correspondant au nom [DOE], prénom [JHON], date de naissance [26/07/1938] - Personne n°[613955] - Famille n°[196544]</rl1ErrorList>
8
      </familyResult>
9
    </ns2:createFamilyResponse>
10
  </soap:Body>
11
</soap:Envelope>
tests/data/toulouse_maelis/R_is_rl_exists.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <ns2:isRLExistsResponse xmlns:ns2="family.ws.maelis.sigec.com">
5
      <result>%s</result>
6
    </ns2:isRLExistsResponse>
7
  </soap:Body>
8
</soap:Envelope>
tests/data/toulouse_maelis/R_update_coordinate.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <ns2:updateCoordinateResponse xmlns:ns2="family.ws.maelis.sigec.com"/>
5
  </soap:Body>
6
</soap:Envelope>
tests/data/toulouse_maelis/R_update_family.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <ns2:updateFamilyResponse xmlns:ns2="family.ws.maelis.sigec.com">
5
      <familyResult>
6
        <number>196544</number>
7
        <category>BI</category>
8
        <situation>C</situation>
9
        <RL1>
10
          <num>613955</num>
11
          <lastname>DOE</lastname>
12
          <firstname>JHON</firstname>
13
          <quality>AU</quality>
14
          <civility>MR</civility>
15
          <dateBirth>1938-07-26T00:00:00+01:00</dateBirth>
16
          <adresse>
17
            <idStreet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
18
            <num>0</num>
19
            <street1>Chateau</street1>
20
            <town>Paris</town>
21
            <zipcode>75014</zipcode>
22
          </adresse>
23
          <contact>
24
            <isContactMail>false</isContactMail>
25
            <isContactSms>false</isContactSms>
26
            <isInvoicePdf>false</isInvoicePdf>
27
          </contact>
28
        </RL1>
29
      </familyResult>
30
    </ns2:updateFamilyResponse>
31
  </soap:Body>
32
</soap:Envelope>
tests/data/toulouse_maelis/R_update_family_error.xml
1
<?xml version="1.0" encoding="utf-8"?>
2
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
  <soap:Body>
4
    <soap:Fault>
5
      <faultcode>soap:Server</faultcode>
6
      <faultstring>Une erreur est survenue : java.sql.SQLDataException: ORA-01438: valeur incohérente avec la précision indiquée pour cette colonne
7
</faultstring>
8
      <detail>
9
        <ns1:MaelisFamilyException xmlns:ns1="family.ws.maelis.sigec.com">
10
          <message xmlns:ns2="family.ws.maelis.sigec.com">Une erreur est survenue : java.sql.SQLDataException: ORA-01438: valeur incohérente avec la précision indiquée pour cette colonne
11
</message>
12
        </ns1:MaelisFamilyException>
13
      </detail>
14
    </soap:Fault>
15
  </soap:Body>
16
</soap:Envelope>
tests/test_toulouse_maelis.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import logging
18 18
import os
19 19

  
20 20
import mock
21 21
import pytest
22
from lxml import etree
22 23
from requests.exceptions import ConnectionError
23 24

  
24 25
from passerelle.contrib.toulouse_maelis.models import Link, ToulouseMaelis
25 26
from passerelle.utils.jsonresponse import APIError
26 27
from passerelle.utils.soap import SOAPError
27 28
from tests.utils import FakedResponse, generic_endpoint_url, setup_access_rights
28 29

  
29 30
TEST_BASE_DIR = os.path.join(os.path.dirname(__file__), 'data', 'toulouse_maelis')
......
41 42
ISWSRUNNING_TRUE = FakedResponse(content=get_xml_file('R_is_ws_running.xml') % b'true', status_code=200)
42 43
ISWSRUNNING_FALSE = FakedResponse(content=get_xml_file('R_is_ws_running.xml') % b'false', status_code=200)
43 44
READ_FAMILY = FakedResponse(content=get_xml_file('R_read_family.xml'), status_code=200)
44 45
READ_CATEGORIES = FakedResponse(content=get_xml_file('R_read_category_list.xml'), status_code=200)
45 46
READ_CIVILITIES = FakedResponse(content=get_xml_file('R_read_civility_list.xml'), status_code=200)
46 47
READ_CSP = FakedResponse(content=get_xml_file('R_read_csp_list.xml'), status_code=200)
47 48
READ_QUALITIES = FakedResponse(content=get_xml_file('R_read_quality_list.xml'), status_code=200)
48 49
READ_SITUATIONS = FakedResponse(content=get_xml_file('R_read_situation_list.xml'), status_code=200)
50
IS_RL_EXISTS_TRUE = FakedResponse(content=get_xml_file('R_is_rl_exists.xml') % b'true', status_code=200)
51
IS_RL_EXISTS_FALSE = FakedResponse(content=get_xml_file('R_is_rl_exists.xml') % b'false', status_code=200)
52
CREATE_FAMILY = FakedResponse(content=get_xml_file('R_create_family.xml'), status_code=200)
53
CREATE_FAMILY_ERR = FakedResponse(content=get_xml_file('R_create_family_error.xml'), status_code=200)
54
UPDATE_FAMILY = FakedResponse(content=get_xml_file('R_update_family.xml'), status_code=200)
55
UPDATE_FAMILY_500 = FakedResponse(content=get_xml_file('R_update_family_error.xml'), status_code=500)
56

  
57

  
58
def assert_sent_payload(mocked_post, query_file):
59
    soap_sent = etree.tostring(etree.fromstring(mocked_post.call_args[1]['data']), pretty_print=True)
60
    expected = etree.tostring(etree.fromstring(get_xml_file(query_file)), pretty_print=True)
61
    assert soap_sent.decode() == expected.decode()
49 62

  
50 63

  
51 64
def get_endpoint(name):
52 65
    url = generic_endpoint_url('toulouse-maelis', name)
53 66
    assert url == '/toulouse-maelis/test/%s' % name
54 67
    return url
55 68

  
56 69

  
......
121 134
    mocked_post.side_effect = post_responses
122 135
    if exception:
123 136
        with pytest.raises(Exception):
124 137
            con.check_status()
125 138
    else:
126 139
        con.check_status()
127 140

  
128 141

  
142
def test_replace_null_values(con):
143
    payload = {
144
        'adresse': {
145
            'num': '169',
146
            'numComp': None,
147
            'street1': 'Chateau',
148
            'street2': None,
149
            'town': 'Paris',
150
            'zipcode': '75014',
151
        }
152
    }
153
    con.replace_null_values(payload)
154
    assert payload == {
155
        'adresse': {
156
            'num': '169',
157
            'numComp': '',
158
            'street1': 'Chateau',
159
            'street2': '',
160
            'town': 'Paris',
161
            'zipcode': '75014',
162
        }
163
    }
164

  
165

  
129 166
@mock.patch('passerelle.utils.Request.get')
130 167
@mock.patch('passerelle.utils.Request.post')
131 168
def test_link(mocked_post, mocked_get, con, app):
132 169
    mocked_get.return_value = FAMILY_SERVICE_WSDL
133 170
    mocked_post.return_value = READ_FAMILY
134 171
    url = get_endpoint('link')
135 172
    assert Link.objects.count() == 0
136 173

  
......
383 420

  
384 421

  
385 422
def test_read_family_not_linked_error(con, app):
386 423
    url = get_endpoint('read-family')
387 424

  
388 425
    resp = app.get(url + '?NameID=')
389 426
    assert resp.json['err'] == 'not-linked'
390 427
    assert resp.json['err_desc'] == 'User not linked to family'
428

  
429

  
430
@mock.patch('passerelle.utils.Request.get')
431
@mock.patch('passerelle.utils.Request.post')
432
def test_read_rl1(mocked_post, mocked_get, con, app):
433
    mocked_get.return_value = FAMILY_SERVICE_WSDL
434
    mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
435
    url = get_endpoint('read-rl')
436
    Link.objects.create(resource=con, family_id='1312', name_id='local')
437

  
438
    resp = app.get(url + '?NameID=local&rl_id=613878')
439
    assert resp.json['err'] == 0
440
    assert resp.json['data']['firstname'] == 'DAMIEN'
441

  
442

  
443
@mock.patch('passerelle.utils.Request.get')
444
@mock.patch('passerelle.utils.Request.post')
445
def test_read_rl2(mocked_post, mocked_get, con, app):
446
    mocked_get.return_value = FAMILY_SERVICE_WSDL
447
    mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
448
    url = get_endpoint('read-rl')
449
    Link.objects.create(resource=con, family_id='1312', name_id='local')
450

  
451
    resp = app.get(url + '?NameID=local&rl_id=613879')
452
    assert resp.json['err'] == 0
453
    assert resp.json['data'] == {
454
        'num': '613879',
455
        'lastname': 'COSTANZE',
456
        'firstname': 'JENNIFER',
457
        'quality': 'MERE',
458
        'civility': 'MME',
459
        'dateBirth': '1987-05-21T00:00:00+02:00',
460
        'adresse': {
461
            'idStreet': 'AV0044',
462
            'num': 9,
463
            'numComp': None,
464
            'street1': 'AVENUE VALDILETTA',
465
            'street2': 'LES MANDARINIERS',
466
            'town': 'NICE',
467
            'zipcode': '06100',
468
        },
469
        'contact': {
470
            'phone': None,
471
            'mobile': None,
472
            'mail': None,
473
            'isContactMail': False,
474
            'isContactSms': False,
475
            'isInvoicePdf': False,
476
        },
477
        'profession': None,
478
        'CAFInfo': {'number': '51', 'organ': None},
479
        'civility_text': 'Madame',
480
        'quality_text': 'MERE',
481
    }
482

  
483

  
484
def test_read_rl_not_linked_error(con, app):
485
    url = get_endpoint('read-rl')
486

  
487
    resp = app.get(url + '?NameID=local&rl_id=613879')
488
    assert resp.json['err'] == 'not-linked'
489
    assert resp.json['err_desc'] == 'User not linked to family'
490

  
491

  
492
@mock.patch('passerelle.utils.Request.get')
493
@mock.patch('passerelle.utils.Request.post')
494
def test_read_rl_not_found(mocked_post, mocked_get, con, app):
495
    mocked_get.return_value = FAMILY_SERVICE_WSDL
496
    mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
497
    url = get_endpoint('read-rl')
498
    Link.objects.create(resource=con, family_id='1312', name_id='local')
499

  
500
    resp = app.get(url + '?NameID=local&rl_id=000000')
501
    assert resp.json['err'] == 'not-found'
502
    assert resp.json['err_desc'] == "no '000000' RL on '1312' family"
503

  
504

  
505
@pytest.mark.parametrize(
506
    'post_response, result',
507
    [
508
        (IS_RL_EXISTS_TRUE, True),
509
        (IS_RL_EXISTS_FALSE, False),
510
    ],
511
)
512
@mock.patch('passerelle.utils.Request.get')
513
@mock.patch('passerelle.utils.Request.post')
514
def test_is_rl_exists(mocked_post, mocked_get, post_response, result, con, app):
515
    mocked_get.return_value = FAMILY_SERVICE_WSDL
516
    mocked_post.return_value = post_response
517
    url = get_endpoint('is-rl-exists')
518

  
519
    params = {
520
        'firstname': 'Damien',
521
        'lastname': 'Costanze',
522
        'datebirth': '1980-10-07',
523
    }
524
    resp = app.post_json(url, params=params)
525
    assert resp.json['err'] == 0
526
    assert resp.json['data'] == result
527

  
528

  
529
@mock.patch('passerelle.utils.Request.get')
530
@mock.patch('passerelle.utils.Request.post')
531
def test_create_family(mocked_post, mocked_get, con, app):
532
    mocked_get.return_value = FAMILY_SERVICE_WSDL
533
    mocked_post.return_value = CREATE_FAMILY
534
    url = get_endpoint('create-family')
535
    params = {
536
        'rl1/adresse': {
537
            'street1': 'Chateau',
538
            'town': 'Paris',
539
            'zipcode': '75014',
540
        },
541
        'rl1': {
542
            'firstname': 'Jhon',
543
            'lastname': 'Doe',
544
            'quality': 'AU',
545
            'dateBirth': '1938-07-26',
546
        },
547
        'categorie': 'ACCEUI',
548
        'situation': 'C',
549
    }
550

  
551
    resp = app.post_json(url + '?NameID=local', params=params)
552
    assert_sent_payload(mocked_post, 'Q_create_family.xml')
553
    assert resp.json['err'] == 0
554
    assert resp.json['data'] == {
555
        'number': 196545,
556
        'password': '394634V2',
557
        'rl1ErrorList': [],
558
        'childErrorList': [],
559
    }
560
    assert Link.objects.get(resource=con, family_id='196545', name_id='local')
561

  
562

  
563
def test_create_family_already_linked_error(con, app):
564
    url = get_endpoint('create-family')
565
    params = {
566
        'rl1/adresse': {
567
            'street1': 'Chateau',
568
            'town': 'Paris',
569
            'zipcode': '75014',
570
        },
571
        'rl1': {
572
            'firstname': 'Jhon',
573
            'lastname': 'Doe',
574
            'quality': 'AU',
575
            'dateBirth': '1938-07-26',
576
        },
577
        'categorie': 'ACCEUI',
578
        'situation': 'C',
579
    }
580

  
581
    Link.objects.create(resource=con, family_id='1312', name_id='local')
582
    resp = app.post_json(url + '?NameID=local', params=params)
583
    assert resp.json['err'] == 'already-linked'
584
    assert resp.json['err_desc'] == 'User already linked to family'
585

  
586

  
587
@mock.patch('passerelle.utils.Request.get')
588
@mock.patch('passerelle.utils.Request.post')
589
def test_create_family_maelis_error(mocked_post, mocked_get, con, app):
590
    mocked_get.return_value = FAMILY_SERVICE_WSDL
591
    mocked_post.return_value = CREATE_FAMILY_ERR
592
    url = get_endpoint('create-family')
593
    params = {
594
        'rl1/adresse': {
595
            'street1': 'Chateau',
596
            'town': 'Paris',
597
            'zipcode': '75014',
598
        },
599
        'rl1': {
600
            'firstname': 'Jhon',
601
            'lastname': 'Doe',
602
            'quality': 'AU',
603
            'dateBirth': '1938-07-26',
604
        },
605
        'categorie': 'ACCEUI',
606
        'situation': 'C',
607
    }
608

  
609
    resp = app.post_json(url + '?NameID=local', params=params)
610
    assert resp.json['err'] == 'E54a'
611
    assert 'Il existe déjà' in resp.json['err_desc']
612

  
613

  
614
@mock.patch('passerelle.utils.Request.get')
615
@mock.patch('passerelle.utils.Request.post')
616
def test_update_family(mocked_post, mocked_get, con, app):
617
    mocked_get.return_value = FAMILY_SERVICE_WSDL
618
    mocked_post.return_value = UPDATE_FAMILY
619
    url = get_endpoint('update-family')
620
    params = {
621
        'rl1/adresse': {
622
            'street1': 'Chateau',
623
            'town': 'Paris',
624
            'zipcode': '75014',
625
        },
626
        'rl1': {
627
            'firstname': 'Jhon',
628
            'lastname': 'Doe',
629
            'quality': 'AU',
630
            'dateBirth': '1938-07-26',
631
        },
632
        'categorie': 'BI',
633
        'situation': 'C',
634
    }
635

  
636
    Link.objects.create(resource=con, family_id='1312', name_id='local')
637
    resp = app.post_json(url + '?NameID=local', params=params)
638
    assert_sent_payload(mocked_post, 'Q_update_family.xml')
639
    assert resp.json['err'] == 0
640

  
641

  
642
def test_update_family_already_not_linked_error(con, app):
643
    url = get_endpoint('update-family')
644
    params = {
645
        'rl1/adresse': {
646
            'street1': 'Chateau',
647
            'town': 'Paris',
648
            'zipcode': '75014',
649
        },
650
        'rl1': {
651
            'firstname': 'Jhon',
652
            'lastname': 'Doe',
653
            'quality': 'AU',
654
            'dateBirth': '1938-07-26',
655
        },
656
        'categorie': 'ACCEUI',
657
        'situation': 'C',
658
    }
659

  
660
    resp = app.post_json(url + '?NameID=local', params=params)
661
    assert resp.json['err'] == 'not-linked'
662
    assert resp.json['err_desc'] == 'User not linked to family'
663

  
664

  
665
@mock.patch('passerelle.utils.Request.get')
666
@mock.patch('passerelle.utils.Request.post')
667
def test_update_family_soap_error(mocked_post, mocked_get, con, app):
668
    mocked_get.return_value = FAMILY_SERVICE_WSDL
669
    mocked_post.return_value = UPDATE_FAMILY_500
670
    url = get_endpoint('update-family')
671
    params = {
672
        'rl1/adresse': {
673
            'street1': 'Chateau',
674
            'town': 'Paris',
675
            'zipcode': '75014',
676
        },
677
        'rl1': {
678
            'firstname': 'Jhon',
679
            'lastname': 'Doe',
680
            'quality': 'AU',
681
            'dateBirth': '1938-07-26',
682
        },
683
        'categorie': 'BI',
684
        'situation': 'C',
685
        'nbChild': '100',
686
    }
687

  
688
    Link.objects.create(resource=con, family_id='1312', name_id='local')
689
    resp = app.post_json(url + '?NameID=local', params=params)
690
    assert resp.json['err'] == 'Family-updateFamily-soap:Server'
691
    assert 'Une erreur est survenue' in resp.json['err_desc']
692

  
693

  
694
@mock.patch('passerelle.utils.Request.get')
695
@mock.patch('passerelle.utils.Request.post')
696
def test_update_coordinate(mocked_post, mocked_get, con, app):
697
    mocked_get.return_value = FAMILY_SERVICE_WSDL
698
    mocked_post.return_value = UPDATE_FAMILY
699
    url = get_endpoint('update-coordinate')
700
    params = {
701
        'adresse': {
702
            'num': '169',
703
            'numComp': None,
704
            'street1': 'Château',
705
            'street2': None,
706
            'town': 'Paris',
707
            'zipcode': '75014',
708
        }
709
    }
710

  
711
    Link.objects.create(resource=con, family_id='1312', name_id='local')
712
    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params)
713
    assert_sent_payload(mocked_post, 'Q_update_coordinate.xml')
714
    assert resp.json['err'] == 0
715
    assert resp.json['data'] == 'ok'
391
-