Projet

Général

Profil

0001-start-atal-connector-33348.patch

Emmanuel Cazenave, 27 mai 2019 20:10

Télécharger (14,6 ko)

Voir les différences:

Subject: [PATCH] start atal connector (#33348)

 passerelle/apps/atal/__init__.py              |   0
 .../apps/atal/migrations/0001_initial.py      |  31 ++++
 passerelle/apps/atal/migrations/__init__.py   |   0
 passerelle/apps/atal/models.py                | 168 ++++++++++++++++++
 passerelle/settings.py                        |   1 +
 tests/test_atal.py                            | 141 +++++++++++++++
 6 files changed, 341 insertions(+)
 create mode 100644 passerelle/apps/atal/__init__.py
 create mode 100644 passerelle/apps/atal/migrations/0001_initial.py
 create mode 100644 passerelle/apps/atal/migrations/__init__.py
 create mode 100644 passerelle/apps/atal/models.py
 create mode 100644 tests/test_atal.py
passerelle/apps/atal/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2019-05-24 10:25
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    initial = True
11

  
12
    dependencies = [
13
        ('base', '0012_job'),
14
    ]
15

  
16
    operations = [
17
        migrations.CreateModel(
18
            name='ATALConnector',
19
            fields=[
20
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
                ('title', models.CharField(max_length=50, verbose_name='Title')),
22
                ('description', models.TextField(verbose_name='Description')),
23
                ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
24
                ('base_soap_url', models.URLField(help_text='URL of the base SOAP endpoint', max_length=400, verbose_name='Base SOAP endpoint')),
25
                ('users', models.ManyToManyField(blank=True, related_name='_atalconnector_users_+', related_query_name='+', to='base.ApiUser')),
26
            ],
27
            options={
28
                'verbose_name': 'ATAL connector',
29
            },
30
        ),
31
    ]
passerelle/apps/atal/models.py
1
# -*- coding: utf-8 -*-
2

  
3
# Copyright (C) 2019  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

  
19
from django.db import models
20
from django.utils.six.moves import urllib
21
from django.utils.translation import ugettext_lazy as _
22
import lxml.etree
23
from zeep import helpers
24

  
25
from passerelle.base.models import BaseResource
26
from passerelle.utils.api import endpoint
27
from passerelle.utils.jsonresponse import APIError
28

  
29

  
30
INSERT_DEMANDE_BY_TYPE_SCHEMA = {
31
    "$schema": "http://json-schema.org/draft-03/schema#",
32
    "title": "",
33
    "description": "",
34
    "type": "object",
35
    "properties": {
36
        "contact_nom": {
37
            "description": "Nom du contact",
38
            "required": True
39
        },
40
        "contact_tel": {
41
            "description": "Téléphone du contact",
42
            "type": "string",
43
        },
44
        "contact_email": {
45
            "description": "Email du contact",
46
            "type": "string",
47
        },
48
        "contact_adresse": {
49
            "description": "Adresse du contact",
50
            "type": "string",
51
        },
52
        "demande_objet": {
53
            "description": "Object de la demande",
54
            "type": "string",
55
        },
56
        "demande_lieu": {
57
            "description": "Lieu de la demande",
58
            "type": "string",
59
        },
60
        "demande_description": {
61
            "description": "Description de la demande",
62
            "type": "string",
63
        },
64
        "remote_adresse": {
65
            "description": "",
66
            "type": "string"
67
        },
68
        "code_equipement": {
69
            "description": "Code de l'équipement",
70
            "type": "string"
71
        },
72
        "code_service_demandeur": {
73
            "description": "Code du service demandeur",
74
            "type": "string"
75
        },
76
        "date_souhaite": {
77
            "description": "Date souhaitée",
78
            "type": "string"
79
        },
80
        "type_demande": {
81
            "description": "Type demande",
82
            "type": "string"
83
        }
84
    }
85
}
86

  
87

  
88
class ATALConnector(BaseResource):
89
    base_soap_url = models.URLField(
90
        max_length=400, verbose_name=_('Base SOAP endpoint'),
91
        help_text=_('URL of the base SOAP endpoint'))
92
    category = _('Business Process Connectors')
93

  
94
    class Meta:
95
        verbose_name = _('ATAL connector')
96

  
97
    def _soap_call(self, wsdl, method, **kwargs):
98
        wsdl_url = urllib.parse.urljoin(self.base_soap_url, '%s?wsdl' % wsdl)
99
        client = self.soap_client(wsdl_url=wsdl_url)
100
        return getattr(client.service, method)(**kwargs)
101

  
102
    def _basic_ref(self, wsdl, method):
103
        soap_res = self._soap_call(wsdl=wsdl, method=method)
104
        res = []
105
        for elem in soap_res:
106
            res.append({'id': elem.code, 'text': elem.libelle})
107
        return {'data': res}
108

  
109
    @endpoint(methods=['get'], perm='can_access')
110
    def get_type_activite(self, request):
111
        return self._basic_ref('VilleAgileService', 'getTypeActivite')
112

  
113
    @endpoint(methods=['get'], perm='can_access')
114
    def get_type_de_voie(self, request):
115
        return self._basic_ref('VilleAgileService', 'getTypeDeVoie')
116

  
117
    @endpoint(methods=['get'], perm='can_access')
118
    def get_types_equipement(self, request):
119
        soap_res = self._soap_call(wsdl='VilleAgileService', method='getTypesEquipement')
120
        tree = lxml.etree.fromstring(soap_res.encode('utf-8')).getroottree()
121
        types = tree.xpath('//types')[0]
122
        res = []
123
        for type_elem in types.getchildren():
124
            res.append({'id': type_elem.get('id'), 'text': type_elem.get('label')})
125
        return {'data': res}
126

  
127
    @endpoint(
128
        perm='can_access',
129
        post={
130
            'description': _('Insert Demande By Type'),
131
            'request_body': {
132
                'schema': {
133
                    'application/json': INSERT_DEMANDE_BY_TYPE_SCHEMA
134
                }
135
            }
136
        }
137
    )
138
    def insert_demande_by_type(self, request, post_data):
139
        demande_number = self._soap_call(
140
            wsdl='DemandeService', method='insertDemandeByType',
141
            contactNom=post_data['contact_nom'],
142
            contactTelephone=post_data['contact_telephone'],
143
            contactCourriel=post_data['contact_email'],
144
            contactAdresse=post_data['contact_adresse'], demandeObjet=post_data['demande_objet'],
145
            demandeLieu=post_data['demande_lieu'],
146
            demandeDescription=post_data['demande_description'],
147
            remoteAddress=post_data['remote_adresse'], codeEquipement=post_data['code_equipement'],
148
            codeServiceDemandeur=post_data['code_service_demandeur'],
149
            dateSouhaitee=post_data['date_souhaite'], typeDemande=post_data['type_demande']
150
        )
151
        return {'data': {'demande_number': demande_number}}
152

  
153
    @endpoint(
154
        methods=['get'], perm='can_access', example_pattern='{demande_number}/',
155
        pattern='^(?P<demande_number>\w+)/$',
156
        parameters={
157
            'demande_number': {
158
                'description': _('Demande number'), 'example_value': 'DIT18050001'
159
            }
160
        }
161
    )
162
    def retrieve_details_demande(self, request, demande_number, **kwargs):
163
        if not demande_number:
164
            raise APIError('A demande_number parameter must be specified')
165
        soap_res = self._soap_call(
166
            wsdl='DemandeService', method='retrieveDetailsDemande',
167
            demandeNumberParam=demande_number)
168
        return {'data': helpers.serialize_object(soap_res)}
passerelle/settings.py
123 123
    'passerelle.apps.api_particulier',
124 124
    'passerelle.apps.arcgis',
125 125
    'passerelle.apps.arpege_ecp',
126
    'passerelle.apps.atal',
126 127
    'passerelle.apps.atos_genesys',
127 128
    'passerelle.apps.base_adresse',
128 129
    'passerelle.apps.bdp',
tests/test_atal.py
1
from django.contrib.contenttypes.models import ContentType
2
import mock
3
import pytest
4

  
5
from passerelle.apps.atal.models import ATALConnector
6
from passerelle.base.models import ApiUser, AccessRight
7

  
8

  
9
@pytest.fixture()
10
def connector(db):
11
    api = ApiUser.objects.create(username='all', keytype='', key='')
12
    connector = ATALConnector.objects.create(
13
        base_soap_url='http://example.atal.com/', slug='slug-atal')
14
    obj_type = ContentType.objects.get_for_model(connector)
15
    AccessRight.objects.create(
16
        codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=connector.pk)
17
    return connector
18

  
19

  
20
def mock_atal_soap_call(monkeypatch, return_value=None, side_effect=None):
21
    kwargs = {}
22
    if return_value is not None:
23
        kwargs['return_value'] = return_value
24
    if side_effect is not None:
25
        kwargs['side_effect'] = side_effect
26
    mock_soap_call = mock.Mock(**kwargs)
27
    monkeypatch.setattr(ATALConnector, '_soap_call', mock_soap_call)
28
    return mock_soap_call
29

  
30

  
31
class SoapElem(object):
32

  
33
    def __init__(self, **kwargs):
34
        for attr, value in kwargs.items():
35
            setattr(self, attr, value)
36

  
37

  
38
REFS = [
39
    SoapElem(code='code1', libelle='elem1'), SoapElem(code='code2', libelle='elem2')
40
]
41

  
42

  
43
def test_get_type_activite(app, connector, monkeypatch):
44
    mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=REFS)
45
    response = app.get('/atal/slug-atal/get_type_activite')
46
    assert response.json == {
47
        'err': 0,
48
        'data': [
49
            {'text': 'elem1', 'id': 'code1'},
50
            {'text': 'elem2', 'id': 'code2'}]
51
    }
52
    call_params = mock_soap_call.call_args.kwargs
53
    assert call_params['wsdl'] == 'VilleAgileService'
54
    assert call_params['method'] == 'getTypeActivite'
55

  
56

  
57
def test_get_type_de_voie(app, connector, monkeypatch):
58
    mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=REFS)
59
    response = app.get('/atal/slug-atal/get_type_de_voie')
60
    assert response.json == {
61
        'err': 0,
62
        'data': [
63
            {'text': 'elem1', 'id': 'code1'},
64
            {'text': 'elem2', 'id': 'code2'}]
65
    }
66
    call_params = mock_soap_call.call_args.kwargs
67
    assert call_params['wsdl'] == 'VilleAgileService'
68
    assert call_params['method'] == 'getTypeDeVoie'
69

  
70

  
71
def test_get_types_equipement(app, connector, monkeypatch):
72
    return_value = u"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
73
    <types>
74
        <type id="2" label="Espaces Verts"></type>
75
        <type id="4" label="Voirie">
76
    </type>
77
</types>
78
"""
79
    mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=return_value)
80
    response = app.get('/atal/slug-atal/get_types_equipement')
81
    assert response.json == {
82
        'err': 0,
83
        'data': [
84
            {'text': 'Espaces Verts', 'id': '2'},
85
            {'text': 'Voirie', 'id': '4'}]
86
    }
87
    call_params = mock_soap_call.call_args.kwargs
88
    assert call_params['wsdl'] == 'VilleAgileService'
89
    assert call_params['method'] == 'getTypesEquipement'
90

  
91

  
92
def test_insert_demande_by_type(app, connector, monkeypatch):
93
    mock_soap_call = mock_atal_soap_call(monkeypatch, return_value='DIT19050001')
94
    params = {
95
        'contact_nom': 'John Doe',
96
        'contact_telephone': '0101010101',
97
        'contact_email': 'john@doe.com',
98
        'contact_adresse': '1 doe street',
99
        'demande_objet': 'sarah connor',
100
        'demande_lieu': 'LA',
101
        'demande_description': 'poker face',
102
        'remote_adresse': 'hollywood bd',
103
        'code_equipement': 'MAC10',
104
        'code_service_demandeur': 'skynet',
105
        'date_souhaite': 'now',
106
        'type_demande': 'scary'
107
    }
108
    response = app.post_json('/atal/slug-atal/insert_demande_by_type', params=params)
109
    assert response.json == {
110
        'err': 0,
111
        'data': {'demande_number': 'DIT19050001'}
112
    }
113
    call_params = mock_soap_call.call_args.kwargs
114
    assert call_params['wsdl'] == 'DemandeService'
115
    assert call_params['method'] == 'insertDemandeByType'
116
    assert call_params['contactNom'] == 'John Doe'
117
    assert call_params['contactTelephone'] == '0101010101'
118
    assert call_params['contactCourriel'] == 'john@doe.com'
119
    assert call_params['contactAdresse'] == '1 doe street'
120
    assert call_params['demandeObjet'] == 'sarah connor'
121
    assert call_params['demandeLieu'] == 'LA'
122
    assert call_params['demandeDescription'] == 'poker face'
123
    assert call_params['remoteAddress'] == 'hollywood bd'
124
    assert call_params['codeEquipement'] == 'MAC10'
125
    assert call_params['codeServiceDemandeur'] == 'skynet'
126
    assert call_params['dateSouhaitee'] == 'now'
127
    assert call_params['typeDemande'] == 'scary'
128

  
129

  
130
def test_retrieve_details_demande(app, connector, monkeypatch):
131
    mock_soap_call = mock_atal_soap_call(
132
        monkeypatch, return_value=dict(code='code1', libelle='elem1'))
133
    response = app.get('/atal/slug-atal/retrieve_details_demande/DIT19050001/')
134
    assert response.json == {
135
        'err': 0,
136
        'data': {'code': 'code1', 'libelle': 'elem1'}
137
    }
138
    call_params = mock_soap_call.call_args.kwargs
139
    assert call_params['wsdl'] == 'DemandeService'
140
    assert call_params['method'] == 'retrieveDetailsDemande'
141
    assert call_params['demandeNumberParam'] == 'DIT19050001'
0
-