Projet

Général

Profil

0002-caluire-axel-link-endpoint-53704.patch

Lauréline Guérin, 04 mai 2021 16:52

Télécharger (44,2 ko)

Voir les différences:

Subject: [PATCH 2/2] caluire-axel: link endpoint (#53704)

 functests/caluire_axel/conftest.py            |  24 ++
 functests/caluire_axel/test_caluire_axel.py   |  21 ++
 passerelle/contrib/caluire_axel/__init__.py   |   0
 .../caluire_axel/migrations/0001_initial.py   |  65 ++++
 .../caluire_axel/migrations/__init__.py       |   0
 passerelle/contrib/caluire_axel/models.py     | 119 ++++++
 passerelle/contrib/caluire_axel/schemas.py    |  39 ++
 .../contrib/caluire_axel/xsd/Adresse.xsd      |  23 ++
 .../contrib/caluire_axel/xsd/AllAxelTypes.xsd | 301 +++++++++++++++
 .../contrib/caluire_axel/xsd/Individu.xsd     |  43 +++
 .../caluire_axel/xsd/Q_FindIndividus.xsd      |  37 ++
 .../caluire_axel/xsd/R_FindIndividus.xsd      |  68 ++++
 .../caluire_axel/xsd/R_ShemaResultat.xsd      |  54 +++
 tests/settings.py                             |   1 +
 tests/test_caluire_axel.py                    | 351 ++++++++++++++++++
 15 files changed, 1146 insertions(+)
 create mode 100644 functests/caluire_axel/conftest.py
 create mode 100644 functests/caluire_axel/test_caluire_axel.py
 create mode 100644 passerelle/contrib/caluire_axel/__init__.py
 create mode 100644 passerelle/contrib/caluire_axel/migrations/0001_initial.py
 create mode 100644 passerelle/contrib/caluire_axel/migrations/__init__.py
 create mode 100644 passerelle/contrib/caluire_axel/models.py
 create mode 100644 passerelle/contrib/caluire_axel/schemas.py
 create mode 100644 passerelle/contrib/caluire_axel/xsd/Adresse.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/AllAxelTypes.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/Individu.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/Q_FindIndividus.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/R_FindIndividus.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/R_ShemaResultat.xsd
 create mode 100644 tests/test_caluire_axel.py
functests/caluire_axel/conftest.py
1
import pytest
2

  
3

  
4
def pytest_addoption(parser):
5
    parser.addoption("--url", help="Url of a passerelle Caluire Axel connector instance")
6
    parser.addoption("--nameid", help="Publik Name ID")
7
    parser.addoption("--firstname", help="first name of a user")
8
    parser.addoption("--lastname", help="Last name of a user")
9
    parser.addoption("--family", help="Family ID")
10

  
11

  
12
@pytest.fixture(scope='session')
13
def conn(request):
14
    return request.config.getoption("--url")
15

  
16

  
17
@pytest.fixture(scope='session')
18
def user(request):
19
    return {
20
        'name_id': request.config.getoption("--nameid"),
21
        'first_name': request.config.getoption("--firstname"),
22
        'last_name': request.config.getoption("--lastname"),
23
        'family': request.config.getoption("--family"),
24
    }
functests/caluire_axel/test_caluire_axel.py
1
import pprint
2

  
3
import requests
4

  
5

  
6
def test_link(conn, user):
7
    name_id = user['name_id']
8
    url = conn + '/link?NameID=%s' % name_id
9
    payload = {
10
        'IDENTFAMILLE': user['family'],
11
        'NOM': user['last_name'],
12
        'PRENOM': user['first_name'],
13
    }
14
    print("Creating link with the following payload:")
15
    pprint.pprint(payload)
16
    resp = requests.post(url, json=payload)
17
    resp.raise_for_status()
18
    res = resp.json()
19
    pprint.pprint(res)
20
    assert res['err'] == 0
21
    print('\n')
passerelle/contrib/caluire_axel/migrations/0001_initial.py
1
import django.db.models.deletion
2
from django.db import migrations, models
3

  
4

  
5
class Migration(migrations.Migration):
6

  
7
    initial = True
8

  
9
    dependencies = [
10
        ('base', '0029_auto_20210202_1627'),
11
    ]
12

  
13
    operations = [
14
        migrations.CreateModel(
15
            name='CaluireAxel',
16
            fields=[
17
                (
18
                    'id',
19
                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
20
                ),
21
                ('title', models.CharField(max_length=50, verbose_name='Title')),
22
                ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
23
                ('description', models.TextField(verbose_name='Description')),
24
                (
25
                    'wsdl_url',
26
                    models.CharField(
27
                        help_text='Caluire Axel WSDL URL', max_length=128, verbose_name='WSDL URL'
28
                    ),
29
                ),
30
                (
31
                    'users',
32
                    models.ManyToManyField(
33
                        blank=True,
34
                        related_name='_caluireaxel_users_+',
35
                        related_query_name='+',
36
                        to='base.ApiUser',
37
                    ),
38
                ),
39
            ],
40
            options={
41
                'verbose_name': 'Caluire Axel',
42
            },
43
        ),
44
        migrations.CreateModel(
45
            name='Link',
46
            fields=[
47
                (
48
                    'id',
49
                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
50
                ),
51
                ('name_id', models.CharField(max_length=256)),
52
                ('family_id', models.CharField(max_length=128)),
53
                ('person_id', models.CharField(max_length=128)),
54
                (
55
                    'resource',
56
                    models.ForeignKey(
57
                        on_delete=django.db.models.deletion.CASCADE, to='caluire_axel.CaluireAxel'
58
                    ),
59
                ),
60
            ],
61
            options={
62
                'unique_together': {('resource', 'name_id')},
63
            },
64
        ),
65
    ]
passerelle/contrib/caluire_axel/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2021  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.db import models
18
from django.utils.translation import ugettext_lazy as _
19

  
20
from passerelle.base.models import BaseResource
21
from passerelle.contrib.utils import axel
22
from passerelle.utils.api import endpoint
23
from passerelle.utils.jsonresponse import APIError
24

  
25
from . import schemas
26

  
27

  
28
class CaluireAxel(BaseResource):
29

  
30
    wsdl_url = models.CharField(
31
        max_length=128, blank=False, verbose_name=_('WSDL URL'), help_text=_('Caluire Axel WSDL URL')
32
    )
33

  
34
    category = _('Business Process Connectors')
35
    _category_ordering = [_('Family')]
36

  
37
    class Meta:
38
        verbose_name = _('Caluire Axel')
39

  
40
    def check_status(self):
41
        response = self.requests.get(self.wsdl_url)
42
        response.raise_for_status()
43

  
44
    def check_individu(self, post_data):
45
        family_id = post_data.pop('IDENTFAMILLE')
46
        for key in ['NAISSANCE', 'CODEPOSTAL', 'VILLE', 'TEL', 'MAIL']:
47
            post_data[key] = None
48
        try:
49
            result = schemas.find_individus(self, {'PORTAIL': {'FINDINDIVIDU': post_data}})
50
        except axel.AxelError as e:
51
            raise APIError(
52
                'Axel error: %s' % e,
53
                err_code='error',
54
                data={'xml_request': e.xml_request, 'xml_response': e.xml_response},
55
            )
56
        data = result.json_response['DATA']['PORTAIL']['FINDINDIVIDUS']
57
        for individu in data.get('INDIVIDU') or []:
58
            for famille in individu['FAMILLE']:
59
                if famille['IDENTFAMILLE'] == family_id:
60
                    place = famille['PLACE']
61
                    if place not in ['1', '2']:
62
                        # not RL1 or RL2
63
                        raise APIError('Wrong place in family', err_code='family-place-error-%s' % place)
64
                    return individu, result
65

  
66
        raise APIError('Person not found', err_code='not-found')
67

  
68
    @endpoint(
69
        display_category=_('Family'),
70
        display_order=1,
71
        description=_('Create link between user and Caluire Axel'),
72
        perm='can_access',
73
        parameters={
74
            'NameID': {'description': _('Publik ID')},
75
        },
76
        post={
77
            'request_body': {
78
                'schema': {
79
                    'application/json': schemas.LINK_SCHEMA,
80
                }
81
            }
82
        },
83
    )
84
    def link(self, request, NameID, post_data):
85
        if not NameID:
86
            raise APIError('NameID is empty', err_code='bad-request', http_status=400)
87

  
88
        family_id = post_data['IDENTFAMILLE']
89
        try:
90
            data, result = self.check_individu(post_data)
91
        except APIError as e:
92
            if not hasattr(e, 'err_code') or e.err_code == 'error':
93
                raise
94
            raise APIError('Person not found', err_code='not-found')
95

  
96
        link, created = self.link_set.get_or_create(
97
            name_id=NameID, defaults={'family_id': family_id, 'person_id': data['IDENT']}
98
        )
99
        if not created and (link.family_id != family_id or link.person_id != data['IDENT']):
100
            raise APIError('Data conflict', err_code='conflict')
101
        return {
102
            'link': link.pk,
103
            'created': created,
104
            'family_id': link.family_id,
105
            'data': {
106
                'xml_request': result.xml_request,
107
                'xml_response': result.xml_response,
108
            },
109
        }
110

  
111

  
112
class Link(models.Model):
113
    resource = models.ForeignKey(CaluireAxel, on_delete=models.CASCADE)
114
    name_id = models.CharField(blank=False, max_length=256)
115
    family_id = models.CharField(blank=False, max_length=128)
116
    person_id = models.CharField(blank=False, max_length=128)
117

  
118
    class Meta:
119
        unique_together = ('resource', 'name_id')
passerelle/contrib/caluire_axel/schemas.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2021  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import copy
18
import os
19

  
20
from passerelle.contrib.utils import axel
21

  
22
BASE_XSD_PATH = os.path.join(os.path.dirname(__file__), 'xsd')
23

  
24

  
25
class Operation(axel.Operation):
26
    base_xsd_path = BASE_XSD_PATH
27

  
28

  
29
find_individus = Operation('FindIndividus')
30

  
31

  
32
LINK_SCHEMA = copy.deepcopy(
33
    find_individus.request_schema['properties']['PORTAIL']['properties']['FINDINDIVIDU']
34
)
35
for key in ['NAISSANCE', 'CODEPOSTAL', 'VILLE', 'TEL', 'MAIL']:
36
    LINK_SCHEMA['properties'].pop(key)
37
    LINK_SCHEMA['required'].remove(key)
38
LINK_SCHEMA['properties']['IDENTFAMILLE'] = {'type': 'string', 'maxLength': 8}
39
LINK_SCHEMA['required'].append('IDENTFAMILLE')
passerelle/contrib/caluire_axel/xsd/Adresse.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:all="urn:AllAxelTypes" targetNamespace="urn:Adresse" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:adr="urn:Adresse">
3
	
4
	<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes"/>
5
	
6
	<xsd:simpleType name="NPAIType">
7
		<xsd:union memberTypes="all:ONType all:empty-string" />
8
	</xsd:simpleType>
9
	
10
	<xsd:complexType name="ADRESSEType">
11
		<xsd:sequence>
12
			<xsd:element name="ADRESSE3" type="all:COMPLEMENTType"/>
13
			<xsd:element name="ADRESSE4" type="all:COMPLEMENTType"/>
14
			<xsd:element name="NORUE" type="all:NUMVOIEType"/>
15
			<xsd:element name="ADRESSE1" type="all:COMPLEMENTType"/>
16
			<xsd:element name="ADRESSE2" type="all:COMPLEMENTType"/>
17
			<xsd:element name="CODEPOSTAL" type="all:CODEPOSTALType"/>
18
			<xsd:element name="VILLE" type="all:VILLEType"/>
19
			<xsd:element name="PAYS" type="all:PAYSType"/>
20
			<xsd:element name="NPAI" type="adr:NPAIType"/>
21
		</xsd:sequence> 
22
	</xsd:complexType>
23
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/AllAxelTypes.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema targetNamespace="urn:AllAxelTypes" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
3

  
4
	<xsd:simpleType name="empty-string">
5
		<xsd:restriction base="xsd:string">
6
			<xsd:enumeration value="" />
7
		</xsd:restriction>
8
	</xsd:simpleType>
9
	
10
	<xsd:simpleType name="decimal-or-empty">
11
		<xsd:union memberTypes="xsd:decimal all:empty-string" />
12
	</xsd:simpleType>
13
	
14
	<xsd:simpleType name="unsignedInt-or-empty">
15
		<xsd:union memberTypes="xsd:unsignedInt all:empty-string" />
16
	</xsd:simpleType>
17
		
18
	<xsd:simpleType name="TELREQUIREDType">
19
		<xsd:restriction base="xsd:string">
20
			<xsd:minLength value="1" />
21
			<xsd:maxLength value="10" />
22
		</xsd:restriction>
23
	</xsd:simpleType>
24
	
25
	<xsd:simpleType name="TEL2REQUIREDType">
26
		<xsd:restriction base="xsd:string">
27
			<xsd:minLength value="1" />
28
			<xsd:maxLength value="16" />
29
		</xsd:restriction>
30
	</xsd:simpleType>
31
	
32
	<xsd:simpleType name="COURRIELREQUIREDType">
33
		<xsd:restriction base="xsd:string" >
34
			<xsd:pattern value="[^@]+@[^\.]+\..+" /> 
35
		</xsd:restriction>
36
	</xsd:simpleType>
37
	
38
	<xsd:simpleType name="NOMREQUIREDType">
39
		<xsd:restriction base="xsd:string">
40
			<xsd:minLength value="1" />
41
			<xsd:maxLength value="50" />
42
		</xsd:restriction>
43
	</xsd:simpleType>
44
	
45
	<xsd:simpleType name="PRENOMREQUIREDType">
46
		<xsd:restriction base="xsd:string">
47
			<xsd:minLength value="1" />
48
			<xsd:maxLength value="30" />
49
		</xsd:restriction>
50
	</xsd:simpleType>
51
	
52
	<xsd:simpleType name="NOMType">
53
		<xsd:restriction base="xsd:string">
54
			<xsd:minLength value="0" />
55
			<xsd:maxLength value="50" />
56
		</xsd:restriction>
57
	</xsd:simpleType>
58
	
59
	<xsd:simpleType name="PRENOMType">
60
		<xsd:restriction base="xsd:string">
61
			<xsd:minLength value="0" />
62
			<xsd:maxLength value="30" />
63
		</xsd:restriction>
64
	</xsd:simpleType>
65
	
66
	<xsd:simpleType name="DATEREQUIREDType">
67
		<xsd:restriction base="xsd:string">
68
			<xsd:pattern value="((0[1-9])|([12][0-9])|(3[01]))/((0[1-9])|(1[012]))/((000[1-9])|(00[1-9][0-9])|(0[1-9][0-9]{2})|([1-9][0-9]{3}))" /> 
69
		</xsd:restriction>
70
	</xsd:simpleType>
71
	
72
	<xsd:simpleType name="DATEType">
73
		<xsd:union memberTypes="all:DATEREQUIREDType all:empty-string" />
74
	</xsd:simpleType>
75
	
76
	<xsd:simpleType name="DATETIMEType">
77
		<xsd:restriction base="xsd:string">
78
			<xsd:pattern value="((0[1-9])|([12][0-9])|(3[01]))/((0[1-9])|(1[012]))/((000[1-9])|(00[1-9][0-9])|(0[1-9][0-9]{2})|([1-9][0-9]{3})) (([01][0-9])|(2[0-3]))(:[0-5][0-9]){2}(\.[0-9]+)?" /> 
79
		</xsd:restriction>
80
	</xsd:simpleType>
81
	
82
	<xsd:simpleType name="TIMEType">
83
		<xsd:restriction base="xsd:string">
84
			<xsd:pattern value="(([01][0-9])|(2[0-3]))(:[0-5][0-9])" /> 
85
		</xsd:restriction>
86
	</xsd:simpleType>
87
	
88
	<xsd:simpleType name="IDENTType">
89
		<xsd:restriction base="xsd:string">
90
			<xsd:maxLength value="8" />
91
		</xsd:restriction>
92
	</xsd:simpleType>
93
	
94
	<xsd:simpleType name="IDENTREQUIREDType">
95
		<xsd:restriction base="xsd:string">
96
			<xsd:minLength value="1"/>
97
			<xsd:maxLength value="8"/>
98
		</xsd:restriction>
99
	</xsd:simpleType>
100
	
101
	<xsd:simpleType name="IDType">
102
		<xsd:restriction base="xsd:string">
103
			<xsd:maxLength value="10" />
104
		</xsd:restriction>
105
	</xsd:simpleType>
106
	
107
	<xsd:simpleType name="IDREQUIREDType">
108
		<xsd:restriction base="xsd:string">
109
			<xsd:minLength value="1"/>
110
			<xsd:maxLength value="10"/>
111
		</xsd:restriction>
112
	</xsd:simpleType>
113
		
114
	<xsd:simpleType name="TELType">
115
		<xsd:union memberTypes="all:TELREQUIREDType all:empty-string" />
116
	</xsd:simpleType>
117

  
118
	<xsd:simpleType name="TEL2Type">
119
		<xsd:union memberTypes="all:TEL2REQUIREDType all:empty-string" />
120
	</xsd:simpleType>
121
	
122
	<xsd:simpleType name="COURRIELType" > 
123
		<xsd:union memberTypes="all:COURRIELREQUIREDType all:empty-string" />
124
	</xsd:simpleType>
125
	
126
	<xsd:simpleType name="ANNEEType">
127
		<xsd:restriction base="xsd:string">
128
			<xsd:pattern value="[0-9]{4}"/>
129
		</xsd:restriction>
130
	</xsd:simpleType>
131
	
132
	<xsd:simpleType name="MOISType">
133
		<xsd:restriction base="xsd:string">
134
			<xsd:pattern value="[0-1][0-9]"/>		
135
		</xsd:restriction>
136
	</xsd:simpleType>
137
	
138
	<xsd:simpleType name="OUINONREQUIREDType">
139
		<xsd:restriction base="xsd:string">
140
			<xsd:pattern value="[Oo][Uu][Ii]" />
141
			<xsd:pattern value="[Nn][Oo][Nn]" />
142
		</xsd:restriction>
143
	</xsd:simpleType>
144
	
145
	<xsd:simpleType name="BOOLEANREQUIREDType">
146
		<xsd:restriction base="xsd:string">
147
			<xsd:pattern value="0" />
148
			<xsd:pattern value="1" />
149
		</xsd:restriction>
150
	</xsd:simpleType>
151
	
152
	<xsd:simpleType name="OUINONType">
153
		<xsd:restriction base="xsd:string">
154
			<xsd:pattern value="|" />
155
			<xsd:pattern value="[Oo][Uu][Ii]" />
156
			<xsd:pattern value="[Nn][Oo][Nn]" />
157
		</xsd:restriction>
158
	</xsd:simpleType>
159
	
160
	<xsd:simpleType name="NUMVOIEREQUIREDType">
161
		<xsd:restriction base="xsd:string">
162
			<xsd:minLength value="1" />
163
			<xsd:maxLength value="5" />
164
		</xsd:restriction>
165
	</xsd:simpleType>
166
	
167
	<xsd:simpleType name="NUMVOIEType">
168
		<xsd:union memberTypes="all:NUMVOIEREQUIREDType all:empty-string" />
169
	</xsd:simpleType>
170
	
171
	<xsd:simpleType name="COMPLEMENTNUMType">
172
		<xsd:restriction base="xsd:string">
173
			<xsd:pattern value="|" />
174
			<xsd:pattern value="[Bb][Ii][Ss]" />
175
			<xsd:pattern value="[Tt][Ee][Rr]" />
176
			<xsd:pattern value="[Qq][Uu][Aa][Tt][Ee][Rr]" />
177
			<xsd:pattern value="[Qq][Uu][Ii][Nn][Qq][Uu][Ii][Ee][Ss]" />
178
		</xsd:restriction>
179
	</xsd:simpleType>
180

  
181
	<xsd:simpleType name="TYPEVOIEType">
182
		<xsd:restriction base="xsd:string">
183
			<xsd:pattern value="[Aa][Vv][Ee][Nn][Uu][Ee]" />
184
			<xsd:pattern value="[Rr][Uu][Ee]" />
185
			<xsd:pattern value="[Ff][Aa][Uu][Bb][Oo][Uu][Rr][Gg]" />
186
			<xsd:pattern value="[Cc][Hh][Ee][Mm][Ii][Nn]" />
187
			<xsd:pattern value="[Cc][Oo][Uu][Rr][Ss]" />
188
			<xsd:pattern value="[Bb][Oo][Uu][Ll][Ee][Vv][Aa][Rr][Dd]" />
189
			<xsd:pattern value="[Ii][Mm][Pp][Aa][Ss][Ss][Ee]" />
190
			<xsd:pattern value="[Ll][Ii][Ee][Uu] [Dd][Ii][Tt]" />
191
		</xsd:restriction>
192
	</xsd:simpleType>
193
	
194
	<xsd:simpleType name="COMPLEMENTREQUIREDType">
195
		<xsd:restriction base="xsd:string">
196
			<xsd:minLength value="1" />
197
			<xsd:maxLength value="40" />
198
		</xsd:restriction>
199
	</xsd:simpleType>
200
	
201
	<xsd:simpleType name="COMPLEMENTType">
202
		<xsd:union memberTypes="all:COMPLEMENTREQUIREDType all:empty-string" />
203
	</xsd:simpleType>
204

  
205
	<xsd:simpleType name="CODEPOSTALREQUIREDType">
206
		<xsd:restriction base="xsd:string">
207
			<xsd:pattern value="[0-9]{5}"/>
208
		</xsd:restriction>
209
	</xsd:simpleType>
210
	
211
	<xsd:simpleType name="VILLEREQUIREDType">
212
		<xsd:restriction base="xsd:string">
213
			<xsd:minLength value="1" />
214
			<xsd:maxLength value="35" />
215
		</xsd:restriction>
216
	</xsd:simpleType>
217
	
218
	<xsd:simpleType name="PAYSREQUIREDType">
219
		<xsd:restriction base="xsd:string">
220
			<xsd:minLength value="1" />
221
			<xsd:maxLength value="30" />
222
		</xsd:restriction>
223
	</xsd:simpleType>
224
	
225
	<xsd:simpleType name="CODEPOSTALType">
226
		<xsd:union memberTypes="all:CODEPOSTALREQUIREDType all:empty-string" />
227
	</xsd:simpleType>
228
	
229
	<xsd:simpleType name="VILLEType">
230
		<xsd:union memberTypes="all:VILLEREQUIREDType all:empty-string" />
231
	</xsd:simpleType>
232
	
233
	<xsd:simpleType name="PAYSType">
234
		<xsd:union memberTypes="all:PAYSREQUIREDType all:empty-string" />
235
	</xsd:simpleType>
236
	
237
	<xsd:simpleType name="CODEINSEEVILLEREQUIREDType">
238
		<xsd:restriction base="xsd:string">
239
			<xsd:pattern value="[0-9]{5}"/>
240
		</xsd:restriction>
241
	</xsd:simpleType>
242
	
243
	<xsd:simpleType name="CODEINSEEVILLEType">
244
		<xsd:union memberTypes="all:CODEINSEEVILLEREQUIREDType all:empty-string" />
245
	</xsd:simpleType>
246
	
247
	<xsd:simpleType name="ONEmptyType">
248
		<xsd:union memberTypes="all:ONType all:empty-string" />
249
	</xsd:simpleType>
250
	
251
	<xsd:simpleType name="ONType">
252
		<xsd:restriction base="xsd:string">
253
			<xsd:pattern value="[Oo]" />
254
			<xsd:pattern value="[Nn]" />
255
		</xsd:restriction>
256
	</xsd:simpleType>
257

  
258
	<xsd:simpleType name="MONTANTREQUIREDType">
259
		<xsd:restriction base="xsd:decimal">
260
			<xsd:totalDigits value="10"/>
261
			<xsd:fractionDigits value="2"/>
262
		</xsd:restriction>
263
	</xsd:simpleType>
264
	
265
	<xsd:simpleType name="MONTANTType">
266
		<xsd:union memberTypes="all:MONTANTREQUIREDType all:empty-string" />
267
	</xsd:simpleType>
268
	
269
	<xsd:simpleType name="PRIXREQUIREDType">
270
		<xsd:restriction base="xsd:decimal">
271
			<xsd:totalDigits value="10"/>
272
			<xsd:fractionDigits value="5"/>
273
		</xsd:restriction>
274
	</xsd:simpleType>
275
	
276
	<xsd:simpleType name="PRIXType">
277
		<xsd:union memberTypes="all:PRIXREQUIREDType all:empty-string" />
278
	</xsd:simpleType>
279
	
280
	<xsd:simpleType name="LIBELLEType">
281
		<xsd:restriction base="xsd:string">
282
			<xsd:minLength value="0" />
283
			<xsd:maxLength value="40" />
284
		</xsd:restriction>
285
	</xsd:simpleType>
286
	
287
	<xsd:simpleType name="LIBELLE100Type">
288
		<xsd:restriction base="xsd:string">
289
			<xsd:minLength value="0" />
290
			<xsd:maxLength value="100" />
291
		</xsd:restriction>
292
	</xsd:simpleType>
293
	
294
	<xsd:simpleType name="FOOBARType"><!-- CUSTOM -->
295
		<xsd:restriction base="xsd:string">
296
			<xsd:minLength value="0" />
297
			<xsd:maxLength value="256" />
298
		</xsd:restriction>
299
	</xsd:simpleType>
300
	
301
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/Individu.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:adr="urn:Adresse" xmlns:all="urn:AllAxelTypes" targetNamespace="urn:Individu" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ind="urn:Individu" >
3
	
4
	<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes" />
5
	<xsd:import schemaLocation="./Adresse.xsd" namespace="urn:Adresse" />
6
	
7
	<xsd:simpleType name="SEXEType">
8
		<xsd:restriction base="xsd:string">
9
			<xsd:enumeration value="" />
10
			<xsd:enumeration value="M" />
11
			<xsd:enumeration value="F" />
12
		</xsd:restriction>
13
	</xsd:simpleType>
14
	
15
	<xsd:simpleType name="CIVILITEType">
16
		<xsd:restriction base="xsd:string">
17
			<xsd:minLength value="0" />
18
			<xsd:maxLength value="4" />
19
		</xsd:restriction>
20
	</xsd:simpleType>
21
	
22
	<xsd:complexType name="INDIVIDUType">
23
		<xsd:sequence>
24
			<xsd:element name="IDENT" type="all:IDENTType"/>
25
			<xsd:element name="CIVILITE" type="ind:CIVILITEType"/>
26
			<xsd:element name="NOM" type="all:NOMREQUIREDType"/>
27
			<xsd:element name="PRENOM" type="all:PRENOMType"/>
28
			<xsd:element name="NAISSANCE" type="all:DATEType"/>
29
			<xsd:element name="SEXE" type="ind:SEXEType"/>
30
			<xsd:element name="NOMJF" type="all:NOMType"/>
31
			<xsd:element name="TELFIXE" type="all:TEL2Type"/>
32
			<xsd:element name="TELPORTABLE" type="all:TEL2Type"/>
33
			<xsd:element name="MAIL" type="all:COURRIELType"/>
34
			<xsd:element name="CSP" type="all:FOOBARType"/><!-- CUSTOM -->
35
			<xsd:element name="EMPLOYEUR" type="all:FOOBARType"/><!-- CUSTOM -->
36
			<xsd:element name="VILLEEMP" type="all:FOOBARType"/><!-- CUSTOM -->
37
			<xsd:element name="PAI" type="all:FOOBARType"/><!-- CUSTOM -->
38
			<xsd:element name="GARDEALTERNEE" type="all:FOOBARType"/><!-- CUSTOM -->
39
			<xsd:element name="ADRESSE" type="adr:ADRESSEType" minOccurs="0" maxOccurs="1"/>
40
		</xsd:sequence> 
41
	</xsd:complexType>
42
		
43
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/Q_FindIndividus.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
3
	
4
	<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes"  />
5
	
6
	
7
	<xsd:complexType name="FINDINDIVIDUType">
8
		<xsd:sequence>
9
			<xsd:element ref="NOM" />
10
			<xsd:element ref="PRENOM" />
11
			<xsd:element ref="NAISSANCE" />
12
			<xsd:element ref="CODEPOSTAL" />
13
			<xsd:element ref="VILLE" />
14
			<xsd:element ref="TEL" />
15
			<xsd:element ref="MAIL" />
16
		</xsd:sequence> 
17
	</xsd:complexType>
18
	
19
	<xsd:complexType name="PORTAILType">
20
		<xsd:sequence>
21
			<xsd:element ref="FINDINDIVIDU" minOccurs="0" maxOccurs="1"/>
22
		</xsd:sequence> 
23
	</xsd:complexType>
24
	
25
	<xsd:element name="NOM" type="all:NOMREQUIREDType"/>
26
	<xsd:element name="PRENOM" type="all:PRENOMREQUIREDType"/> <!-- CUSTOM, it should be PRENOMType -->
27
	<xsd:element name="NAISSANCE" type="all:DATEType"/>
28
	<xsd:element name="CODEPOSTAL" type="all:CODEPOSTALType"/>
29
	<xsd:element name="VILLE" type="all:VILLEType"/>
30
	<xsd:element name="TEL" type="all:TEL2Type"/>
31
	<xsd:element name="MAIL" type="all:COURRIELType"/>
32
		
33
	<xsd:element name="FINDINDIVIDU" type="FINDINDIVIDUType"/>
34
	
35
	<xsd:element name="PORTAIL" type="PORTAILType"/>	
36
		
37
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/R_FindIndividus.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema	xmlns:all="urn:AllAxelTypes" xmlns:ind="urn:Individu"  xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
3
	
4
	<xsd:import schemaLocation="./Individu.xsd" namespace="urn:Individu" />
5
	<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes" />
6
	
7
	<xsd:redefine schemaLocation="./R_ShemaResultat.xsd">
8
	    <xsd:simpleType name="TYPEType">
9
			<xsd:restriction base="TYPEType">
10
				<xsd:enumeration value="FindIndividus" />
11
			</xsd:restriction>
12
	    </xsd:simpleType>
13
		
14
		<xsd:complexType name="PORTAILType">
15
			<xsd:complexContent>
16
				<xsd:extension base="PORTAILType">
17
					<xsd:sequence>
18
							<xsd:element ref="FINDINDIVIDUS" minOccurs="0" maxOccurs="1"/>
19
					</xsd:sequence>
20
				</xsd:extension>
21
			 </xsd:complexContent>
22
		</xsd:complexType>
23
	</xsd:redefine>	
24
			
25
	<xsd:complexType name="INDIType">
26
		<xsd:complexContent>
27
			<xsd:extension base="ind:INDIVIDUType">
28
				<xsd:sequence>
29
					<xsd:element ref="FAMILLE"  minOccurs="0" maxOccurs="unbounded"/>
30
				</xsd:sequence>
31
			</xsd:extension>
32
		</xsd:complexContent>
33
	</xsd:complexType>
34
	
35
	<xsd:complexType name="FAMILLEType">
36
		<xsd:sequence>
37
			<xsd:element ref="IDENTFAMILLE" />
38
			<xsd:element ref="PLACE"/>
39
			<xsd:element ref="SITUATION" minOccurs="0" maxOccurs="1"/><!-- CUSTOM -->
40
		</xsd:sequence> 
41
	</xsd:complexType>
42
	
43
	<xsd:simpleType name="PLACEType">
44
		<xsd:restriction base="xsd:string">
45
			<xsd:enumeration value="" />
46
			<xsd:enumeration value="1" />
47
			<xsd:enumeration value="2" />
48
			<xsd:enumeration value="3" />
49
		</xsd:restriction>
50
	</xsd:simpleType>
51
	
52
	<xsd:complexType name="FINDINDIVIDUSType">
53
		<xsd:sequence>
54
			<xsd:element ref="CODE" />
55
			<xsd:element ref="INDIVIDU" minOccurs="0" maxOccurs="unbounded" />
56
		</xsd:sequence> 
57
	</xsd:complexType>
58

  
59
	<xsd:element name="CODE" type="xsd:integer"/>
60
	<xsd:element name="INDIVIDU" type="INDIType"/>
61
	<xsd:element name="FAMILLE" type="FAMILLEType"/>
62
	<xsd:element name="IDENTFAMILLE" type="all:IDENTType"/>
63
	<xsd:element name="PLACE" type="PLACEType"/>
64
	<xsd:element name="SITUATION" type="all:FOOBARType"/><!-- CUSTOM -->
65
	
66
	<xsd:element name="FINDINDIVIDUS" type="FINDINDIVIDUSType"/>
67
		
68
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/R_ShemaResultat.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
3
	
4
	<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes"  />
5
	
6
	<xsd:simpleType name="TYPEType">
7
		<xsd:restriction base="xsd:string">
8
		</xsd:restriction>
9
	</xsd:simpleType>
10
	
11
	<xsd:simpleType name="STATUSType">
12
		<xsd:restriction base="xsd:string">
13
			<xsd:enumeration value="OK" />
14
			<xsd:enumeration value="NOK" />
15
		</xsd:restriction>
16
	</xsd:simpleType>
17
		
18
	<xsd:complexType name="PORTAILType">
19
	</xsd:complexType>
20
	
21
	<xsd:complexType name="RESULTATType">
22
		<xsd:sequence>
23
			<xsd:element ref="TYPE" />
24
			<xsd:element ref="STATUS" />
25
			<xsd:element ref="DATE" />
26
			<xsd:element ref="COMMENTAIRES" />
27
		</xsd:sequence> 
28
	</xsd:complexType>
29
	
30
	<xsd:complexType name="DATAType">
31
		<xsd:sequence>
32
			<xsd:element ref="PORTAIL"/>
33
		</xsd:sequence> 
34
	</xsd:complexType>
35
		
36
	<xsd:complexType name="PORTAILSERVICEType">
37
		<xsd:sequence>
38
			<xsd:element ref="RESULTAT" />
39
			<xsd:element ref="DATA" />
40
		</xsd:sequence>
41
	</xsd:complexType>
42
	
43
	<xsd:element name="TYPE" type="TYPEType"/>
44
	<xsd:element name="STATUS" type="STATUSType"/>
45
	<xsd:element name="DATE" type="all:DATETIMEType"/>
46
	<xsd:element name="COMMENTAIRES" type="xsd:string"/>
47
		
48
	<xsd:element name="PORTAIL" type="PORTAILType"/>
49
	
50
	<xsd:element name="RESULTAT" type="RESULTATType"/>
51
	<xsd:element name="DATA" type="DATAType"/>
52
	
53
	<xsd:element name="PORTAILSERVICE" type="PORTAILSERVICEType"/>
54
</xsd:schema>
tests/settings.py
16 16
# include all contrib apps
17 17
INSTALLED_APPS += (
18 18
    'passerelle.contrib.adict',
19
    'passerelle.contrib.caluire_axel',
19 20
    'passerelle.contrib.dpark',
20 21
    'passerelle.contrib.fake_family',
21 22
    'passerelle.contrib.gdema',
tests/test_caluire_axel.py
1
# -*- coding: utf-8 -*-
2
# passerelle - uniform access to multiple data sources and services
3
# Copyright (C) 2021 Entr'ouvert
4
#
5
# This program is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU Affero General Public License as published
7
# by the Free Software Foundation, either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU Affero General Public License for more details.
14
#
15
# You should have received a copy of the GNU Affero General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17

  
18
from contextlib import contextmanager
19

  
20
import mock
21
import pytest
22
import utils
23
import xmlschema
24

  
25
from passerelle.contrib.caluire_axel import schemas
26
from passerelle.contrib.caluire_axel.models import CaluireAxel, Link
27
from passerelle.contrib.utils.axel import AxelError
28
from passerelle.utils.soap import SOAPError
29

  
30

  
31
@pytest.fixture
32
def resource(db):
33
    return utils.make_resource(
34
        CaluireAxel, slug='test', wsdl_url='http://example.net/AXEL_WS/AxelWS.php?wsdl'
35
    )
36

  
37

  
38
@pytest.fixture
39
def link_params():
40
    return {
41
        'IDENTFAMILLE': '12345',
42
        'NOM': 'Doe',
43
        'PRENOM': 'John',
44
    }
45

  
46

  
47
@contextmanager
48
def mock_getdata(content, operation):
49
    with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
50
        resp = '''
51
        <?xml version="1.0"?>
52
        <PORTAILSERVICE>
53
          <RESULTAT>
54
            <TYPE>%s</TYPE>
55
            <STATUS>OK</STATUS>
56
            <DATE>10/10/2010 10:10:01</DATE>
57
            <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
58
          </RESULTAT>
59
          <DATA>
60
            %s
61
          </DATA>
62
        </PORTAILSERVICE>
63
        '''.strip() % (
64
            operation,
65
            content,
66
        )
67
        client.return_value.service.getData.return_value = resp
68
        yield
69

  
70

  
71
def test_operation_status_error(resource):
72
    resp = '''
73
    <?xml version="1.0"?>
74
    <PORTAILSERVICE>
75
      <RESULTAT>
76
        <TYPE>FindIndividus</TYPE>
77
        <STATUS>NOK</STATUS>
78
        <COMMENTAIRES><![CDATA[Foo reason]]></COMMENTAIRES>
79
      </RESULTAT>
80
      <DATA>
81
        <PORTAIL/>
82
      </DATA>
83
    </PORTAILSERVICE>
84
    '''.strip()
85
    with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
86
        client.return_value.service.getData.return_value = resp
87
        with pytest.raises(AxelError, match='Foo reason'):
88
            schemas.find_individus(
89
                resource,
90
                {
91
                    'PORTAIL': {
92
                        'FINDINDIVIDU': {
93
                            'NOM': 'Doe',
94
                            'PRENOM': 'John',
95
                            'NAISSANCE': None,
96
                            'CODEPOSTAL': None,
97
                            'VILLE': None,
98
                            'TEL': None,
99
                            'MAIL': None,
100
                        }
101
                    }
102
                },
103
            )
104

  
105

  
106
@pytest.mark.parametrize(
107
    'content',
108
    [
109
        '<PORTAIL><FINDINDIVIDUS/></PORTAIL>',
110
    ],
111
)
112
def test_operation_find_individus(resource, content):
113
    with mock_getdata(content, 'FindIndividus'):
114
        with pytest.raises(AxelError):
115
            schemas.find_individus(
116
                resource,
117
                {
118
                    'PORTAIL': {
119
                        'FINDINDIVIDU': {
120
                            'NOM': 'Doe',
121
                            'PRENOM': 'John',
122
                            'NAISSANCE': None,
123
                            'CODEPOSTAL': None,
124
                            'VILLE': None,
125
                            'TEL': None,
126
                            'MAIL': None,
127
                        }
128
                    }
129
                },
130
            )
131

  
132

  
133
def test_link_endpoint_nameid_empty(app, resource, link_params):
134
    resp = app.post_json('/caluire-axel/test/link?NameID=', params=link_params, status=400)
135
    assert resp.json['err_desc'] == "NameID is empty"
136
    assert resp.json['err'] == 'bad-request'
137

  
138

  
139
def test_link_endpoint_axel_error(app, resource, link_params):
140
    with mock.patch('passerelle.contrib.caluire_axel.schemas.find_individus') as operation:
141
        operation.side_effect = AxelError('FooBar')
142
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
143
    assert resp.json['err_desc'] == "Axel error: FooBar"
144
    assert resp.json['err'] == 'error'
145
    assert resp.json['data'] == {'xml_request': None, 'xml_response': None}
146

  
147
    # test xml_request and xml_response only for this endpoint
148
    xml_request = """<PORTAIL>
149
  <FINDINDIVIDU>
150
    <NOM>Doe</NOM>
151
    <PRENOM>John</PRENOM>
152
    <NAISSANCE />
153
    <CODEPOSTAL />
154
    <VILLE />
155
    <TEL />
156
    <MAIL />
157
  </FINDINDIVIDU>
158
</PORTAIL>
159
"""
160

  
161
    with mock_getdata('', 'FindIndividus'):
162
        with mock.patch('xmlschema.XMLSchema.validate') as xml_validate:
163
            xml_validate.side_effect = xmlschema.XMLSchemaValidationError(None, None)
164
            resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
165
    assert resp.json['err_desc'].startswith("Axel error: invalid request")
166
    assert resp.json['err'] == 'error'
167
    assert resp.json['data']['xml_request'] == xml_request
168
    assert resp.json['data']['xml_response'] is None
169

  
170
    xml_response = """<PORTAILSERVICE>
171
  <RESULTAT>
172
    <TYPE>FINDINDIVIDUS</TYPE>
173
    <STATUS>NOK</STATUS>
174
    <COMMENTAIRES>Foo reason</COMMENTAIRES>
175
  </RESULTAT>
176
  <DATA>
177
    <PORTAIL />
178
  </DATA>
179
</PORTAILSERVICE>"""
180
    response = """
181
    <?xml version="1.0"?>
182
    <PORTAILSERVICE>
183
      <RESULTAT>
184
        <TYPE>FINDINDIVIDUS</TYPE>
185
        <STATUS>NOK</STATUS>
186
        <COMMENTAIRES><![CDATA[Foo reason]]></COMMENTAIRES>
187
      </RESULTAT>
188
      <DATA>
189
        <PORTAIL/>
190
      </DATA>
191
    </PORTAILSERVICE>
192
    """.strip()
193
    with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
194
        client.return_value.service.getData.return_value = response
195
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
196
    assert resp.json['err_desc'] == 'Axel error: Foo reason'
197
    assert resp.json['err'] == 'error'
198
    assert resp.json['data']['xml_request'] == xml_request
199
    assert resp.json['data']['xml_response'] == xml_response
200

  
201
    content = """<PORTAIL>
202
      <FINDINDIVIDUS>
203
        <INDIVIDU>
204
          <IDENT>123</IDENT>
205
          <NOM>Doe</NOM>
206
          <PRENOM>John</PRENOM>
207
          <FAMILLE>
208
            <IDENTFAMILLE>12345</IDENTFAMILLE>
209
            <PLACE>2</PLACE>
210
          </FAMILLE>
211
        </INDIVIDU>
212
      </FINDINDIVIDUS>
213
    </PORTAIL>"""
214
    xml_response = (
215
        """<PORTAILSERVICE>
216
  <RESULTAT>
217
    <TYPE>FindIndividus</TYPE>
218
    <STATUS>OK</STATUS>
219
    <DATE>10/10/2010 10:10:01</DATE>
220
    <COMMENTAIRES />
221
  </RESULTAT>
222
  <DATA>
223
    %s
224
  </DATA>
225
</PORTAILSERVICE>"""
226
        % content
227
    )
228
    with mock_getdata(content, 'FindIndividus'):
229
        with mock.patch('passerelle.contrib.utils.axel.AxelSchema.decode') as decode:
230
            decode.side_effect = xmlschema.XMLSchemaValidationError(None, None)
231
            resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
232
    assert resp.json['err_desc'].startswith("Axel error: invalid response")
233
    assert resp.json['err'] == 'error'
234
    assert resp.json['data']['xml_request'] == xml_request
235
    assert resp.json['data']['xml_response'] == xml_response
236

  
237
    with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
238
        client.side_effect = SOAPError('SOAP service is down')
239
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
240
    assert resp.json['err_desc'] == "SOAP service is down"
241

  
242

  
243
@pytest.mark.parametrize(
244
    'xml_response',
245
    [
246
        '',
247
        """<INDIVIDU>
248
  <IDENT>123</IDENT>
249
  <CIVILITE/><NOM>A</NOM><PRENOM/><NAISSANCE/><SEXE/><NOMJF/><TELFIXE/><TELPORTABLE/><MAIL/><CSP/><EMPLOYEUR/><VILLEEMP/><PAI/><GARDEALTERNEE/>
250
  <FAMILLE>
251
    <IDENTFAMILLE>12346</IDENTFAMILLE>
252
    <PLACE>2</PLACE>
253
    <SITUATION/>
254
  </FAMILLE>
255
  <FAMILLE>
256
    <IDENTFAMILLE>12347</IDENTFAMILLE>
257
    <PLACE>1</PLACE>
258
  </FAMILLE>
259
  </INDIVIDU>""",
260
        """<INDIVIDU>
261
  <IDENT>123</IDENT>
262
  <CIVILITE/><NOM>A</NOM><PRENOM/><NAISSANCE/><SEXE/><NOMJF/><TELFIXE/><TELPORTABLE/><MAIL/><CSP/><EMPLOYEUR/><VILLEEMP/><PAI/><GARDEALTERNEE/>
263
  <FAMILLE>
264
    <IDENTFAMILLE>12345</IDENTFAMILLE>
265
    <PLACE>3</PLACE>
266
    <SITUATION/>
267
  </FAMILLE>
268
  </INDIVIDU>""",
269
    ],
270
)
271
def test_link_endpoint_no_result(app, resource, link_params, xml_response):
272
    content = (
273
        '''<PORTAIL>
274
    <FINDINDIVIDUS>
275
        <CODE>42</CODE>
276
        %s
277
    </FINDINDIVIDUS>
278
</PORTAIL>'''
279
        % xml_response
280
    )
281
    with mock_getdata(content, 'FindIndividus'):
282
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
283
    assert resp.json['err_desc'] == "Person not found"
284
    assert resp.json['err'] == 'not-found'
285

  
286

  
287
def test_link_endpoint_conflict(app, resource, link_params):
288
    content = """<PORTAIL><FINDINDIVIDUS>
289
  <CODE>42</CODE>
290
  <INDIVIDU>
291
    <IDENT>123</IDENT>
292
    <CIVILITE/><NOM>A</NOM><PRENOM/><NAISSANCE/><SEXE/><NOMJF/><TELFIXE/><TELPORTABLE/><MAIL/><CSP/><EMPLOYEUR/><VILLEEMP/><PAI/><GARDEALTERNEE/>
293
    <FAMILLE>
294
      <IDENTFAMILLE>12345</IDENTFAMILLE>
295
      <PLACE>2</PLACE>
296
      <SITUATION/>
297
    </FAMILLE>
298
  </INDIVIDU>
299
</FINDINDIVIDUS></PORTAIL>"""
300
    # existing link but family_id is wrong
301
    link = Link.objects.create(resource=resource, name_id='yyy', family_id='YYY', person_id='42')
302
    with mock_getdata(content, 'FindIndividus'):
303
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
304
    assert resp.json['err_desc'] == "Data conflict"
305
    assert resp.json['err'] == 'conflict'
306

  
307
    # existing link but person_id is wrong
308
    link.family_id = '12345'
309
    link.person_id = '35'
310
    link.save()
311
    with mock_getdata(content, 'FindIndividus'):
312
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
313
    assert resp.json['err_desc'] == "Data conflict"
314
    assert resp.json['err'] == 'conflict'
315

  
316

  
317
@pytest.mark.parametrize('place', [1, 2])
318
def test_link_endpoint(app, resource, link_params, place):
319
    content = (
320
        """<PORTAIL><FINDINDIVIDUS>
321
  <CODE>42</CODE>
322
  <INDIVIDU>
323
    <IDENT>123</IDENT>
324
    <CIVILITE/><NOM>A</NOM><PRENOM/><NAISSANCE/><SEXE/><NOMJF/><TELFIXE/><TELPORTABLE/><MAIL/><CSP/><EMPLOYEUR/><VILLEEMP/><PAI/><GARDEALTERNEE/>
325
    <FAMILLE>
326
      <IDENTFAMILLE>12345</IDENTFAMILLE>
327
      <PLACE>%s</PLACE>
328
      <SITUATION/>
329
    </FAMILLE>
330
  </INDIVIDU>
331
</FINDINDIVIDUS></PORTAIL>"""
332
        % place
333
    )
334
    with mock_getdata(content, 'FindIndividus'):
335
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
336
    assert set(resp.json.keys()) == set(['err', 'link', 'created', 'family_id', 'data'])
337
    assert resp.json['err'] == 0
338
    assert resp.json['family_id'] == '12345'
339
    assert resp.json['created'] is True
340
    assert 'xml_request' in resp.json['data']
341
    assert 'xml_response' in resp.json['data']
342

  
343
    # again
344
    with mock_getdata(content, 'FindIndividus'):
345
        resp = app.post_json('/caluire-axel/test/link?NameID=yyy', params=link_params)
346
    assert set(resp.json.keys()) == set(['err', 'link', 'created', 'family_id', 'data'])
347
    assert resp.json['err'] == 0
348
    assert resp.json['family_id'] == '12345'
349
    assert resp.json['created'] is False  # link already exists
350
    assert 'xml_request' in resp.json['data']
351
    assert 'xml_response' in resp.json['data']
0
-