Projet

Général

Profil

0001-api_entreprise-add-initial-connector-30010.patch

Voir les différences:

Subject: [PATCH] api_entreprise: add initial connector (#30010)

 passerelle/apps/api_entreprise/__init__.py    |   0
 .../api_entreprise/migrations/0001_initial.py |  35 ++
 .../api_entreprise/migrations/__init__.py     |   0
 passerelle/apps/api_entreprise/models.py      | 247 +++++++++++
 passerelle/settings.py                        |   1 +
 passerelle/static/css/style.css               |   4 +
 tests/test_api_entreprise.py                  | 415 ++++++++++++++++++
 7 files changed, 702 insertions(+)
 create mode 100644 passerelle/apps/api_entreprise/__init__.py
 create mode 100644 passerelle/apps/api_entreprise/migrations/0001_initial.py
 create mode 100644 passerelle/apps/api_entreprise/migrations/__init__.py
 create mode 100644 passerelle/apps/api_entreprise/models.py
 create mode 100644 tests/test_api_entreprise.py
passerelle/apps/api_entreprise/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.20 on 2019-03-15 09:38
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='APIEntreprise',
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
                ('url', models.URLField(default=b'https://entreprise.api.gouv.fr/v2/', max_length=256, verbose_name='API URL')),
25
                ('token', models.CharField(max_length=1024, verbose_name='API token')),
26
                ('recipient', models.CharField(help_text='ex: siret, siren, waldec, etc...', max_length=256, verbose_name='Call recipient')),
27
                ('context', models.CharField(help_text='ex: APS, MPS, etc...', max_length=256, verbose_name='Call context')),
28
                ('obj', models.CharField(max_length=49, verbose_name='Calls object')),
29
                ('users', models.ManyToManyField(blank=True, to='base.ApiUser')),
30
            ],
31
            options={
32
                'verbose_name': 'API Entreprise',
33
            },
34
        ),
35
    ]
passerelle/apps/api_entreprise/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2019  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
'''Gateway to API-Entreprise web-service from SGMAP:
18
   https://entreprise.api.gouv.fr
19
'''
20

  
21
from six.moves.urllib_parse import urljoin
22
import requests
23

  
24

  
25
from django.db import models
26
from django.utils.translation import ugettext_lazy as _
27
from django.utils.timezone import datetime, make_aware, timedelta
28
from django.http import HttpResponse, Http404, HttpResponseBadRequest
29
from django.core import signing
30
from django.core.urlresolvers import reverse
31

  
32
from passerelle.base.models import BaseResource
33
from passerelle.utils.api import endpoint
34
from passerelle.utils.jsonresponse import APIError
35

  
36
DOCUMENT_SIGNATURE_MAX_AGE = timedelta(days=7)
37

  
38
def normalize_dates(data):
39
    for key in data:
40
        if isinstance(data[key], dict):
41
            normalize_dates(data[key])
42
        if isinstance(data[key], list):
43
            for item in data[key]:
44
                normalize_dates(item)
45
        if key.startswith('date_'):
46
            # try to parse isoformatted dates
47
            try:
48
                data[key] = datetime.fromtimestamp(int(data[key])).date()
49
            except (ValueError, TypeError):
50
                pass
51
        if key.endswith('timestamp'):
52
            # timestamps can be integers or strings
53
            try:
54
                data[key] = make_aware(datetime.fromtimestamp(int(data[key])))
55
            except (ValueError, TypeError):
56
                pass
57

  
58

  
59
class APIEntreprise(BaseResource):
60

  
61
    url = models.URLField(_('API URL'), max_length=256, default='https://entreprise.api.gouv.fr/v2/')
62
    token = models.CharField(max_length=1024, verbose_name=_('API token'))
63
    recipient = models.CharField(max_length=256, verbose_name=_('Call recipient'),
64
                                 help_text=_('ex: siret, institution_id, waldec, etc...'))
65
    context = models.CharField(max_length=256, verbose_name=_('Call context'),
66
                                 help_text=_('ex: APS, MPS, etc...'))
67
    obj = models.CharField(max_length=49, verbose_name=_('Calls object'))
68

  
69
    category = _('Business Process Connectors')
70

  
71
    class Meta:
72
        verbose_name = _('API Entreprise')
73

  
74
    def get_context_and_object(self, **kwargs):
75
        return {'context': kwargs.get('context', self.context),
76
                'object': kwargs.get('object', self.obj)}
77

  
78
    def get(self, path, **kwargs):
79
        params = {'token': self.token,
80
                  'recipient': kwargs.get('recipient', self.recipient)
81
        }
82
        params.update(self.get_context_and_object(**kwargs))
83
        url = urljoin(self.url, path)
84
        try:
85
            response = self.requests.get(url, data=params)
86
        except requests.RequestException as e:
87
            raise APIError(u'API-entreprise connection error: %s' %
88
                           response.status_code,
89
                           data={'error': unicode(e)})
90
        try:
91
            data = response.json()
92
        except ValueError as e:
93
            content = repr(response.content[:1000])
94
            raise APIError(
95
                u'API-entreprise returned non-JSON content with status %s: %s' %
96
                (response.status_code, content),
97
                data={'status_code': response.status_code,
98
                      'exception': unicode(e),
99
                      'content': content,
100
                })
101
        if response.status_code != 200:
102
            if data.get('error') == 'not_found':
103
                return {
104
                    'err': 1,
105
                    'err_desc': data.get('message', 'not-found'),
106
                }
107
            raise APIError(
108
                u'API-entreprise returned a non 200 status %s: %s' %
109
                (response.status_code, data),
110
                data={
111
                    'status_code': response.status_code,
112
                    'content': data,
113
                })
114
        normalize_dates(data)
115
        return {
116
            'err': 0,
117
            'data': data,
118
        }
119

  
120

  
121
    @endpoint(perm='can_access',
122
              pattern='(?P<institution_id>\w+)/$',
123
              example_pattern='{institution_id}/',
124
              description=_('Get association\'s documents'),
125
              parameters={
126
                  'institution_id': {
127
                      'description': _('association id'),
128
                      'example_value': '44317013900036',
129
                }
130
              }
131
    )
132
    def documents_associations(self, request, institution_id, **kwargs):
133
        data = []
134
        resp = self.get('documents_associations/%s/' % institution_id, **kwargs)
135
        for item in resp['data'].get('documents', []):
136
            # ignore documents with no type
137
            if not item.get('type'):
138
                continue
139
            signature_elements = {'url': item['url']}
140
            signature_elements.update(self.get_context_and_object(**kwargs))
141
            signature = signing.dumps(signature_elements)
142
            document_url = request.build_absolute_uri(reverse('generic-endpoint', kwargs={'connector': self.get_connector_slug(),
143
                                        'slug': self.slug,
144
                                        'endpoint': 'document',
145
                                        'rest': '%s/%s/' % (institution_id, signature)}))
146
            data.append({'id': item['timestamp'],
147
                         'text': item['type'],
148
                         'type': item['type'],
149
                         'url': document_url})
150
        # sort data by date
151
        data.sort(key=lambda i: i['id'])
152
        return {'err': 0, 'data': data}
153

  
154

  
155
    @endpoint(pattern='(?P<institution_id>\w+)/(?P<document_id>[\:\w-]+)/$',
156
              example_pattern='{institution_id}/{document_id}/',
157
              description=_('Get institution\'s document'),
158
              parameters={
159
                  'institution_id': {
160
                      'description': _('institution id'),
161
                      'example_value': '44317013900036',
162
                  },
163
                  'document_id': {
164
                      'description': _('document id'),
165
                      'example_value': 'A1500660325',
166
                  }
167
              }
168
    )
169
    def document(self, request, institution_id, document_id, **kwargs):
170
        resp = self.get('documents_associations/%s/' % institution_id, **kwargs)
171
        request_context = self.get_context_and_object(**kwargs)
172
        context = kwargs.get('context', self.context)
173
        obj = kwargs.get('object', self.obj)
174
        for item in resp['data'].get('documents', []):
175
            # ignore documents with no type
176
            if not item.get('type'):
177
                continue
178
            try:
179
                r = signing.loads(document_id, max_age=DOCUMENT_SIGNATURE_MAX_AGE)
180
            except signing.BadSignature:
181
                continue
182
            print r
183
            if r['object'] != request_context['object']:
184
                return HttpResponseBadRequest('wrong object')
185
            if r['context'] != request_context['context']:
186
                return HttpResponseBadRequest('wrong context')
187
            if r['url'] == item['url']:
188
                return HttpResponse(self.requests.get(item['url']),
189
                                    content_type='application/pdf')
190
        raise Http404('document not found')
191

  
192

  
193
    @endpoint(perm='can_access',
194
              pattern='(?P<institution_id>\w+)/$',
195
              example_pattern='{institution_id}/',
196
              description=_('Get institution\'s data from Infogreffe'),
197
              parameters={
198
                  'institution_id': {
199
                      'description': _('institution id'),
200
                      'example_value': '44317013900036',
201
                }
202
              }
203
    )
204
    def extraits_rcs(self, request, institution_id, **kwargs):
205
        return self.get('extraits_rcs_infogreffe/%s/' % institution_id, **kwargs)
206

  
207
    @endpoint(perm='can_access',
208
              pattern='(?P<institution_id>\w+)/$',
209
              example_pattern='{institution_id}/',
210
              description=_('Get institution\'s related informations'),
211
              parameters={
212
                  'institution_id': {
213
                      'description': _('institution id'),
214
                      'example_value': '44317013900036',
215
                }
216
              }
217
    )
218
    def associations(self, request, institution_id, **kwargs):
219
        return self.get('associations/%s/' % institution_id, **kwargs)
220

  
221
    @endpoint(perm='can_access',
222
              pattern='(?P<institution_id>\w+)/$',
223
              example_pattern='{institution_id}/',
224
              description=_('Get institution\'s related informations'),
225
              parameters={
226
                  'institution_id': {
227
                      'description': _('association id'),
228
                      'example_value': '44317013900036',
229
                }
230
              }
231
    )
232
    def entreprises(self, request, institution_id, **kwargs):
233
        return self.get('entreprises/%s/' % institution_id, **kwargs)
234

  
235
    @endpoint(perm='can_access',
236
              pattern='(?P<institution_id>\w+)/$',
237
              example_pattern='{institution_id}/',
238
              description=_('Get institution\'s related informations'),
239
              parameters={
240
                  'institution_id': {
241
                      'description': _('institution id'),
242
                      'example_value': '44317013900036',
243
                }
244
              }
245
    )
246
    def etablissements(self, request, institution_id, **kwargs):
247
        return self.get('etablissements/%s/' % institution_id, **kwargs)
passerelle/settings.py
145 145
    'passerelle.apps.solis',
146 146
    'passerelle.apps.arpege_ecp',
147 147
    'passerelle.apps.vivaticket',
148
    'passerelle.apps.api_entreprise',
148 149
    # backoffice templates and static
149 150
    'gadjo',
150 151
)
passerelle/static/css/style.css
168 168
	content: "\f2c3";  /* id-card-o */
169 169
}
170 170

  
171
li.connector.apientreprise a::before {
172
	content: "\f1ad";  /* fa-building-o */
173
}
174

  
171 175
li.connector.dpark a::before {
172 176
	content: "\f1b9";  /* car */
173 177
}
tests/test_api_entreprise.py
1
# -*- coding: utf-8 -*-
2

  
3
# tests/test_api_entreprise.py
4
# Copyright (C) 2019  Entr'ouvert
5
#
6
# This program is free software: you can redistribute it and/or modify it
7
# under the terms of the GNU Affero General Public License as published
8
# by the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU Affero General Public License for more details.
15
#
16
# You should have received a copy of the GNU Affero General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18

  
19
import pytest
20
import mock
21

  
22
from httmock import urlmatch, HTTMock, response
23

  
24
from django.core.urlresolvers import reverse
25
from django.utils import timezone
26

  
27
from passerelle.apps.api_entreprise.models import APIEntreprise
28

  
29
from utils import make_resource, endpoint_get, FakedResponse
30

  
31
ETABLISSEMENTS_RESPONSE = {
32
  "etablissement": {
33
    "siege_social": True,
34
    "siret": "41816609600051",
35
    "naf": "6202A",
36
    "libelle_naf": "Conseil en systèmes et logiciels informatiques",
37
    "date_mise_a_jour": 1449183600,
38
    "tranche_effectif_salarie_etablissement": {
39
      "de": 200,
40
      "a": 249,
41
      "code": "31",
42
      "date_reference": "2014",
43
      "intitule": "200 à 249 salariés"
44
    },
45
    "date_creation_etablissement": 1108594800,
46
    "region_implantation": {
47
      "code": "11",
48
      "value": "Île-de-France"
49
    },
50
    "commune_implantation": {
51
      "code": "75108",
52
      "value": "PARIS 8"
53
    },
54
    "adresse": {
55
      "l1": "OCTO TECHNOLOGY",
56
      "l4": "50 AVENUE DES CHAMPS ELYSEES",
57
      "l6": "75008 PARIS",
58
      "l7": "FRANCE",
59
      "numero_voie": "50",
60
      "type_voie": "AV",
61
      "nom_voie": "DES CHAMPS ELYSEES",
62
      "code_postal": "75008",
63
      "localite": "PARIS 8",
64
      "code_insee_localite": "75108",
65
    },
66
    "etat_administratif": {
67
      "value": "F",
68
      "date_fermeture": 1315173600
69
    }
70
  },
71
  "gateway_error": False
72
}
73

  
74
ENTREPRISES_RESPONSE = {
75
  "entreprise": {
76
    "siren": "418166096",
77
    "capital_social": 459356,
78
    "numero_tva_intracommunautaire": "FR16418166096",
79
    "forme_juridique": "SA à directoire (s.a.i.)",
80
    "forme_juridique_code": "5699",
81
    "nom_commercial": "OCTO-TECHNOLOGY",
82
    "procedure_collective": False,
83
    "naf_entreprise": "6202A",
84
    "libelle_naf_entreprise": "Conseil en systèmes et logiciels informatiques",
85
    "raison_sociale": "OCTO-TECHNOLOGY",
86
    "siret_siege_social": "41816609600051",
87
    "code_effectif_entreprise": "31",
88
    "date_creation": 891381600,
89
    "categorie_entreprise": "PME",
90
    "tranche_effectif_salarie_entreprise": {
91
      "de": 200,
92
      "a": 249,
93
      "code": "31",
94
      "date_reference": "2014",
95
      "intitule": "200 à 249 salariés"
96
    },
97
    "mandataires_sociaux": [{
98
      "nom": "HISQUIN",
99
      "prenom": "FRANCOIS",
100
      "fonction": "PRESIDENT DU DIRECTOIRE",
101
      "dirigeant": True,
102
      "date_naissance": "1965-01-27",
103
      "raison_sociale": "",
104
      "identifiant": "",
105
      "type": "PP"
106
    }, {
107
        "nom": "",
108
        "prenom": "",
109
        "fonction": "COMMISSAIRE AUX COMPTES SUPPLEANT",
110
        "dirigeant": True,
111
        "date_naissance": "",
112
        "date_naissance_timestamp": 0,
113
        "raison_sociale": "BCRH & ASSOCIES - SOCIETE A RESPONSABILITE LIMITEE A ASSOCIE UNIQUE",
114
        "identifiant": "490092574",
115
        "type": "PM"
116
      }
117
    ],
118
    "etat_administratif": {
119
      "value": "C", # A (actif) ou C (cessé)
120
      "date_cessation": 1315173600 # null quand actif (A), un timestamp (un entier) quand cessé (C )
121
    }
122
  },
123
  "etablissement_siege": {
124
    "siege_social": True,
125
    "siret": "41816609600051",
126
    "naf": "6202A",
127
    "libelle_naf": "Conseil en systèmes et logiciels informatiques",
128
    "date_mise_a_jour": 1449183600,
129
    "tranche_effectif_salarie_etablissement": {
130
      "de": 200,
131
      "a": 249,
132
      "code": "31",
133
      "date_reference": "2014",
134
      "intitule": "200 à 249 salariés"
135
    },
136
    "date_creation_etablissement": 1108594800,
137
    "region_implantation": {
138
      "code": "11",
139
      "value": "Île-de-France"
140
    },
141
    "commune_implantation": {
142
      "code": "75108",
143
      "value": "PARIS 8"
144
    },
145
    "adresse": {
146
      "l1": "OCTO TECHNOLOGY",
147
      "l4": "50 AVENUE DES CHAMPS ELYSEES",
148
      "l6": "75008 PARIS",
149
      "l7": "FRANCE",
150
      "numero_voie": "50",
151
      "type_voie": "AV",
152
      "nom_voie": "DES CHAMPS ELYSEES",
153
      "code_postal": "75008",
154
      "localite": "PARIS 8",
155
      "code_insee_localite": "75108",
156
    },
157
    "etat_administratif": {
158
        "value": "F",
159
        "date_fermeture": 1315173600
160
    }
161
  },
162
  "gateway_error": False
163
}
164

  
165
EXTRAITS_RCS_RESPONSE = {
166
  "siren": "418166096",
167
  "date_immatriculation": "1998-03-27",
168
  "date_immatriculation_timestamp": 890953200,
169
  "date_extrait": "21 AVRIL 2017",
170
  "observations": [
171
    {
172
      "date": "2000-02-23",
173
      "date_timestamp": 951260400,
174
      "numero": "12197",
175
      "libelle": " LA SOCIETE NE CONSERVE AUCUNE ACTIVITE A SON ANCIEN SIEGE "
176
    }
177
  ]
178
}
179

  
180
ASSOCIATIONS_RESPONSE = {
181
  "association" : {
182
    "id": "W751135389",
183
    "titre": "ALLIANCE DU COEUR: UNION NATIONALE DES FEDERATIONS ET ASSOCIATIONS DE MALADES CARDIOVASCULAIRES",
184
    "objet": "information, soutien, solidarité et accompagnement psycho médico social des personnes malades cardiovasculaires et de leurs proches...",
185
    "siret": "42135938100025",
186
    "siret_siege_social": "42135938100033",
187
    "date_creation": "1993-02-11",
188
    "date_declaration": "2013-06-28",
189
    "date_publication": "1993-03-03",
190
    "adresse_siege": {
191
      "numero_voie": "10",
192
      "type_voie": "RUE",
193
      "libelle_voie": "Lebouis",
194
      "code_insee": "75120",
195
      "code_postal": "75014",
196
      "commune": "Paris"
197
    },
198
    "groupement": "Simple",
199
    "mise_a_jour": "2013-06-28"
200
  }
201
}
202

  
203
DOCUMENTS_ASSOCIATION_RESPONSE = {
204
  "nombre_documents": 2,
205
  "documents": [
206
    {
207
      "type": "Statuts",
208
      "url": "https://apientreprise.fr/attestations/40ab0b07d434d0417e8997ce7c5afbef/attestation_document_association.pdf",
209
      "timestamp": "1500660325"
210
    },
211
    {
212
      "type": "Récépissé",
213
      "url": "https://apientreprise.fr/attestations/40ab0b07d434d0417e8997ce7c5afbef/recepisse_association.pdf",
214
      "timestamp": "1500667325"
215
    },
216
  ]
217
}
218

  
219
DOCUMENT_ASSOCIATION_RESPONSE = "binary content"
220

  
221
@urlmatch(netloc='^entreprise.api.gouv.fr$',
222
          path='^/v2/etablissements/')
223
def api_entreprise_etablissements(url, request):
224
    return response(200, ETABLISSEMENTS_RESPONSE, request=request)
225

  
226

  
227
@urlmatch(netloc='^entreprise.api.gouv.fr$',
228
          path='^/v2/entreprises/')
229
def api_entreprise_entreprises(url, request):
230
    return response(200, ENTREPRISES_RESPONSE, request=request)
231

  
232

  
233
@urlmatch(netloc='^entreprise.api.gouv.fr$',
234
          path='^/v2/associations/')
235
def api_entreprise_associations(url, request):
236
    return response(200, ASSOCIATIONS_RESPONSE, request=request)
237

  
238

  
239
@urlmatch(netloc='^entreprise.api.gouv.fr$',
240
          path='^/v2/extraits_rcs_infogreffe/')
241
def api_entreprise_extraits_rcs(url, request):
242
    return response(200, EXTRAITS_RCS_RESPONSE, request=request)
243

  
244
@urlmatch(netloc='^entreprise.api.gouv.fr$',
245
          path='^/v2/documents_associations/')
246
def api_entreprise_documents_associations(url, request):
247
    return response(200, DOCUMENTS_ASSOCIATION_RESPONSE, request=request)
248

  
249
@urlmatch(netloc='^apientreprise.fr$',
250
          path='^/attestations/')
251
def api_entreprise_document_association(url, request):
252
    return response(200, DOCUMENT_ASSOCIATION_RESPONSE, request=request)
253

  
254

  
255
@urlmatch(netloc='^entreprise.api.gouv.fr$')
256
def api_entreprise_error_500(url, request):
257
    return response(500, 'bad error happened', request=request)
258

  
259

  
260
@urlmatch(netloc='^entreprise.api.gouv.fr$')
261
def api_entreprise_error_not_json(url, request):
262
    return response(200, 'simple text', request=request)
263

  
264

  
265
@urlmatch(netloc='^entreprise.api.gouv.fr$')
266
def api_entreprise_error_not_found(url, request):
267
    return response(404, {
268
        'error': 'not_found',
269
        'message': u'Page not found'
270
    }, request=request)
271

  
272

  
273
@pytest.yield_fixture
274
def mock_api_entreprise():
275
    with HTTMock(api_entreprise_etablissements, api_entreprise_entreprises, api_entreprise_associations, api_entreprise_extraits_rcs,
276
                 api_entreprise_associations, api_entreprise_documents_associations, api_entreprise_document_association):
277
        yield None
278

  
279

  
280
@pytest.fixture
281
def resource(db):
282
    return make_resource(
283
        APIEntreprise,
284
        slug='test',
285
        title='API Entreprise',
286
        description='API Entreprise',
287
        token='83c68bf0b6013c4daf3f8213f7212aa5',
288
        recipient='Test', context='Test',
289
        obj='Test')
290

  
291

  
292
def test_entreprises_endpoint(app, resource, mock_api_entreprise):
293
    response = app.get('/api-entreprise/test/entreprises/443170139/')
294
    data = response.json['data']
295
    assert data['entreprise']['categorie_entreprise'] == 'PME'
296
    assert data['entreprise']['numero_tva_intracommunautaire'] == 'FR16418166096'
297
    assert data['entreprise']['siret_siege_social'] == '41816609600051'
298
    assert data['entreprise']['forme_juridique_code'] == '5699'
299
    assert data['entreprise']['siren'] == '418166096'
300
    assert data['entreprise']['date_creation'] == '1998-03-31'
301
    assert data['entreprise']['mandataires_sociaux'][0]['date_naissance'] == '1965-01-27'
302

  
303
    assert 'etablissement_siege' in data
304
    assert data['etablissement_siege']['siret'] == '41816609600051'
305
    assert data['etablissement_siege']['adresse']['numero_voie'] == '50'
306
    assert data['etablissement_siege']['adresse']['type_voie'] == 'AV'
307
    assert data['etablissement_siege']['adresse']['nom_voie'] == 'DES CHAMPS ELYSEES'
308
    assert data['etablissement_siege']['adresse']['code_postal'] == '75008'
309
    assert data['etablissement_siege']['adresse']['localite'] == 'PARIS 8'
310
    assert data['etablissement_siege']['adresse']['code_insee_localite'] == '75108'
311
    assert data['etablissement_siege']['date_mise_a_jour'] == '2015-12-03'
312

  
313

  
314
def test_etablissements_endpoint(app, resource, mock_api_entreprise):
315
    response = app.get('/api-entreprise/test/etablissements/44317013900036/')
316
    assert 'data' in response.json
317
    data = response.json['data']
318

  
319
    assert 'etablissement' in data
320
    assert data['etablissement']['siret'] == '41816609600051'
321
    assert data['etablissement']['naf'] == '6202A'
322
    assert data['etablissement']['date_mise_a_jour'] == '2015-12-03'
323
    assert data['etablissement']['date_creation_etablissement'] == '2005-02-16'
324

  
325
    assert 'adresse' in data['etablissement']
326
    assert data['etablissement']['adresse']['code_postal'] == '75008'
327
    assert data['etablissement']['adresse']['localite'] == 'PARIS 8'
328
    assert data['etablissement']['adresse']['code_insee_localite'] == '75108'
329

  
330

  
331
def test_associations_endpoint(app, resource, mock_api_entreprise):
332
    response = app.get('/api-entreprise/test/associations/443170139/')
333
    assert 'data' in response.json
334
    data = response.json['data']
335

  
336
    assert 'association' in data
337
    assert data['association']['id'] == 'W751135389'
338
    assert data['association']['siret'] == '42135938100025'
339
    assert data['association']['date_creation'] == '1993-02-11'
340
    assert data['association']['date_declaration'] == '2013-06-28'
341
    assert data['association']['date_publication'] == '1993-03-03'
342

  
343
    assert 'adresse_siege' in data['association']
344
    assert data['association']['adresse_siege']['code_postal'] == '75014'
345
    assert data['association']['adresse_siege']['code_insee'] == '75120'
346

  
347

  
348

  
349
def test_documents_associations_endpoint(app, resource, mock_api_entreprise):
350
    response = app.get('/api-entreprise/test/documents_associations/443170139/')
351
    assert 'data' in response.json
352
    data = response.json['data']
353
    assert len(data) == 2
354
    for document in data:
355
        assert 'id' in document
356
        assert 'text' in document
357
        assert 'url' in document
358
        assert 'type' in document
359

  
360

  
361
def test_document_association(app, resource, mock_api_entreprise, freezer):
362
    response = app.get('/api-entreprise/test/documents_associations/443170139/')
363
    assert 'data' in response.json
364
    data = response.json['data']
365
    assert len(data) == 2
366
    document = data[0]
367
    assert 'url' in document
368
    resp = app.get(document['url'], status=200)
369
    # try to get document with wrong signature
370
    url = document['url']
371
    wrong_url = document['url'] + "wrong/"
372
    resp = app.get(wrong_url, status=404)
373
    # try to get document with different object
374
    resp = app.get(document['url'], params={'object': 'new-object'}, status=400)
375
    # try to get document with different context
376
    resp = app.get(document['url'], params={'context': 'new-context'}, status=400)
377

  
378
    # try expired url
379
    freezer.move_to(timezone.now() + timezone.timedelta(days=8))
380
    resp = app.get(document['url'], status=404)
381

  
382

  
383
@mock.patch('passerelle.utils.Request.get')
384
def test_endpoint_with_params(mocked_get, app, resource):
385
    mocked_get.return_value = FakedResponse(content='{}', status_code=200)
386
    params = {'context': 'Custom context', 'object': 'Custom object', 'recipient': 'Custom recipient'}
387
    response = app.get('/api-entreprise/test/documents_associations/443170139/', params=params)
388
    assert mocked_get.call_args[1]['data']['context'] == 'Custom context'
389
    assert mocked_get.call_args[1]['data']['object'] == 'Custom object'
390
    assert mocked_get.call_args[1]['data']['recipient'] == 'Custom recipient'
391

  
392

  
393
def test_error_500(app, resource, mock_api_entreprise):
394
    with HTTMock(api_entreprise_error_500):
395
        response = app.get('/api-entreprise/test/entreprises/443170139/')
396
        assert response.status_code == 200
397
        assert response.json['err'] == 1
398
        assert response.json['data']['status_code'] == 500
399
        assert 'error happened' in response.json['err_desc']
400

  
401

  
402
def test_no_json_error(app, resource, mock_api_entreprise):
403
    with HTTMock(api_entreprise_error_not_json):
404
        response = app.get('/api-entreprise/test/entreprises/443170139/')
405
        assert response.status_code == 200
406
        assert response.json['err'] == 1
407
        assert response.json['err_desc'] == "API-entreprise returned non-JSON content with status 200: 'simple text'"
408

  
409

  
410
def test_error_404(app, resource, mock_api_entreprise):
411
    with HTTMock(api_entreprise_error_not_found):
412
        response = app.get('/api-entreprise/test/entreprises/443170139/')
413
        assert response.status_code == 200
414
        assert response.json['err'] == 1
415
        assert response.json['err_desc'] == 'Page not found'
0
-