Projet

Général

Profil

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

Serghei Mihai (congés, retour 15/05), 10 avril 2019 00:49

Télécharger (31,4 ko)

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 |  32 ++
 .../api_entreprise/migrations/__init__.py     |   0
 passerelle/apps/api_entreprise/models.py      | 305 +++++++++++++
 passerelle/settings.py                        |   1 +
 passerelle/static/css/style.css               |   4 +
 tests/test_api_entreprise.py                  | 423 ++++++++++++++++++
 7 files changed, 765 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
                ('users', models.ManyToManyField(blank=True, to='base.ApiUser')),
27
            ],
28
            options={
29
                'verbose_name': 'API Entreprise',
30
            },
31
        ),
32
    ]
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
from passerelle.views import WrongParameter
36

  
37
DOCUMENT_SIGNATURE_MAX_AGE = timedelta(days=7)
38

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

  
59

  
60
class APIEntreprise(BaseResource):
61

  
62
    url = models.URLField(_('API URL'), max_length=256, default='https://entreprise.api.gouv.fr/v2/')
63
    token = models.CharField(max_length=1024, verbose_name=_('API token'))
64

  
65
    category = _('Business Process Connectors')
66

  
67
    class Meta:
68
        verbose_name = _('API Entreprise')
69

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

  
113

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

  
161

  
162
    @endpoint(pattern='(?P<institution_id>\w+)/(?P<document_id>[\:\w-]+)/$',
163
              example_pattern='{institution_id}/{document_id}/',
164
              description=_('Get institution\'s document'),
165
              parameters={
166
                  'institution_id': {
167
                      'description': _('institution id'),
168
                      'example_value': '44317013900036',
169
                  },
170
                  'document_id': {
171
                      'description': _('document id'),
172
                      'example_value': 'A1500660325',
173
                  },
174
                  'object': {
175
                      'Description': _('request object'),
176
                      'example_value': 'MSP'
177
                  },
178
                  'context': {
179
                      'description': _('request context'),
180
                      'example_value': '42'
181
                  },
182
                  'recipient': {
183
                      'description': _('request recipient: usually customer number'),
184
                      'example_value': '44317013900036'
185
                  }
186
              }
187
    )
188
    def document(self, request, institution_id, document_id, **kwargs):
189
        try:
190
            params = signing.loads(document_id, max_age=DOCUMENT_SIGNATURE_MAX_AGE)
191
        except signing.BadSignature:
192
            raise Http404('document not found')
193
        response = self.requests.get(params['url'])
194
        if response.ok:
195
            return HttpResponse(response, content_type='application/pdf')
196
        raise Http404('document not found')
197

  
198

  
199
    @endpoint(perm='can_access',
200
              pattern='(?P<institution_id>\w+)/$',
201
              example_pattern='{institution_id}/',
202
              description=_('Get institution\'s data from Infogreffe'),
203
              parameters={
204
                  'institution_id': {
205
                      'description': _('institution id'),
206
                      'example_value': '44317013900036',
207
                  },
208
                  'object': {
209
                      'Description': _('request object'),
210
                      'example_value': 'MSP'
211
                  },
212
                  'context': {
213
                      'description': _('request context'),
214
                      'example_value': '42'
215
                  },
216
                  'recipient': {
217
                      'description': _('request recipient: usually customer number'),
218
                      'example_value': '44317013900036'
219
                  }
220
              }
221
    )
222
    def extraits_rcs(self, request, institution_id, **kwargs):
223
        return self.get('extraits_rcs_infogreffe/%s/' % institution_id, **kwargs)
224

  
225

  
226
    @endpoint(perm='can_access',
227
              pattern='(?P<institution_id>\w+)/$',
228
              example_pattern='{institution_id}/',
229
              description=_('Get institution\'s related informations'),
230
              parameters={
231
                  'institution_id': {
232
                      'description': _('institution id'),
233
                      'example_value': '44317013900036',
234
                  },
235
                  'object': {
236
                      'Description': _('request object'),
237
                      'example_value': 'MSP'
238
                  },
239
                  'context': {
240
                      'description': _('request context'),
241
                      'example_value': '42'
242
                  },
243
                  'recipient': {
244
                      'description': _('request recipient: usually customer number'),
245
                      'example_value': '44317013900036'
246
                  }
247
              }
248
    )
249
    def associations(self, request, institution_id, **kwargs):
250
        return self.get('associations/%s/' % institution_id, **kwargs)
251

  
252

  
253
    @endpoint(perm='can_access',
254
              pattern='(?P<institution_id>\w+)/$',
255
              example_pattern='{institution_id}/',
256
              description=_('Get institution\'s related informations'),
257
              parameters={
258
                  'institution_id': {
259
                      'description': _('association id'),
260
                      'example_value': '44317013900036',
261
                  },
262
                  'object': {
263
                      'description': _('request object'),
264
                      'example_value': 'MSP'
265
                  },
266
                  'context': {
267
                      'description': _('request context'),
268
                      'example_value': '42'
269
                  },
270
                  'recipient': {
271
                      'description': _('request recipient: usually customer number'),
272
                      'example_value': '44317013900036'
273
                  }
274
              }
275
    )
276
    def entreprises(self, request, institution_id, **kwargs):
277
        return self.get('entreprises/%s/' % institution_id, **kwargs)
278

  
279

  
280
    @endpoint(perm='can_access',
281
              methods=['get'],
282
              pattern='(?P<institution_id>\w+)/$',
283
              example_pattern='{institution_id}/', #&
284
              description_get=_('Get institution\'s related informations'),
285
              parameters={
286
                  'institution_id': {
287
                      'description': _('institution id'),
288
                      'example_value': '44317013900036',
289
                  },
290
                  'object': {
291
                      'description': _('request object'),
292
                      'example_value': 'MSP'
293
                  },
294
                  'context': {
295
                      'description': _('request context'),
296
                      'example_value': '42'
297
                  },
298
                  'recipient': {
299
                      'description': _('request recipient: usually customer number'),
300
                      'example_value': '44317013900036'
301
                  }
302
              }
303
    )
304
    def etablissements(self, request, institution_id, **kwargs):
305
        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
REQUEST_PARAMS = {'context': 'MSP', 'object': 'demand', 'recipient': 'siret'}
222

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

  
228

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

  
234

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

  
240

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

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

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

  
256

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

  
261

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

  
266

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

  
274

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

  
281

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

  
291

  
292
@mock.patch('passerelle.utils.Request.get')
293
def test_endpoint_with_no_params(mocked_get, app, resource):
294
    mocked_get.return_value = FakedResponse(content='{}', status_code=200)
295
    response = app.get('/api-entreprise/test/documents_associations/443170139/', status=400)
296
    assert response.json['err_class'] == 'passerelle.views.WrongParameter'
297
    assert response.json['err_desc'] == "missing parameters: 'context'."
298
    params = {'context': 'Custom context', 'object': 'Custom object', 'recipient': 'Custom recipient'}
299
    response = app.get('/api-entreprise/test/documents_associations/443170139/', params=params)
300
    assert mocked_get.call_args[1]['data']['context'] == 'Custom context'
301
    assert mocked_get.call_args[1]['data']['object'] == 'Custom object'
302
    assert mocked_get.call_args[1]['data']['recipient'] == 'Custom recipient'
303

  
304

  
305
def test_entreprises_endpoint(app, resource, mock_api_entreprise):
306
    response = app.get('/api-entreprise/test/entreprises/443170139/',
307
                       params=REQUEST_PARAMS)
308
    data = response.json['data']
309
    assert data['entreprise']['categorie_entreprise'] == 'PME'
310
    assert data['entreprise']['numero_tva_intracommunautaire'] == 'FR16418166096'
311
    assert data['entreprise']['siret_siege_social'] == '41816609600051'
312
    assert data['entreprise']['forme_juridique_code'] == '5699'
313
    assert data['entreprise']['siren'] == '418166096'
314
    assert data['entreprise']['date_creation'] == '1998-03-31'
315
    assert data['entreprise']['mandataires_sociaux'][0]['date_naissance'] == '1965-01-27'
316

  
317
    assert 'etablissement_siege' in data
318
    assert data['etablissement_siege']['siret'] == '41816609600051'
319
    assert data['etablissement_siege']['adresse']['numero_voie'] == '50'
320
    assert data['etablissement_siege']['adresse']['type_voie'] == 'AV'
321
    assert data['etablissement_siege']['adresse']['nom_voie'] == 'DES CHAMPS ELYSEES'
322
    assert data['etablissement_siege']['adresse']['code_postal'] == '75008'
323
    assert data['etablissement_siege']['adresse']['localite'] == 'PARIS 8'
324
    assert data['etablissement_siege']['adresse']['code_insee_localite'] == '75108'
325
    assert data['etablissement_siege']['date_mise_a_jour'] == '2015-12-03'
326

  
327

  
328
def test_etablissements_endpoint(app, resource, mock_api_entreprise):
329
    response = app.get('/api-entreprise/test/etablissements/44317013900036/',
330
                       params=REQUEST_PARAMS)
331
    assert 'data' in response.json
332
    data = response.json['data']
333

  
334
    assert 'etablissement' in data
335
    assert data['etablissement']['siret'] == '41816609600051'
336
    assert data['etablissement']['naf'] == '6202A'
337
    assert data['etablissement']['date_mise_a_jour'] == '2015-12-03'
338
    assert data['etablissement']['date_creation_etablissement'] == '2005-02-16'
339

  
340
    assert 'adresse' in data['etablissement']
341
    assert data['etablissement']['adresse']['code_postal'] == '75008'
342
    assert data['etablissement']['adresse']['localite'] == 'PARIS 8'
343
    assert data['etablissement']['adresse']['code_insee_localite'] == '75108'
344

  
345

  
346
def test_associations_endpoint(app, resource, mock_api_entreprise):
347
    response = app.get('/api-entreprise/test/associations/443170139/',
348
                       params=REQUEST_PARAMS)
349
    assert 'data' in response.json
350
    data = response.json['data']
351

  
352
    assert 'association' in data
353
    assert data['association']['id'] == 'W751135389'
354
    assert data['association']['siret'] == '42135938100025'
355
    assert data['association']['date_creation'] == '1993-02-11'
356
    assert data['association']['date_declaration'] == '2013-06-28'
357
    assert data['association']['date_publication'] == '1993-03-03'
358

  
359
    assert 'adresse_siege' in data['association']
360
    assert data['association']['adresse_siege']['code_postal'] == '75014'
361
    assert data['association']['adresse_siege']['code_insee'] == '75120'
362

  
363

  
364
def test_documents_associations_endpoint(app, resource, mock_api_entreprise):
365
    response = app.get('/api-entreprise/test/documents_associations/443170139/',
366
                       params=REQUEST_PARAMS)
367
    assert 'data' in response.json
368
    data = response.json['data']
369
    assert len(data) == 2
370
    for document in data:
371
        assert 'id' in document
372
        assert 'text' in document
373
        assert 'url' in document
374
        assert 'type' in document
375

  
376

  
377
def test_document_association(app, resource, mock_api_entreprise, freezer):
378
    response = app.get('/api-entreprise/test/documents_associations/443170139/',
379
                       params=REQUEST_PARAMS)
380
    assert 'data' in response.json
381
    data = response.json['data']
382
    assert len(data) == 2
383
    document = data[0]
384
    assert 'url' in document
385
    resp = app.get(document['url'],
386
                   params={'context': 'MSP', 'object': 'demand', 'recipient': 'siret'},
387
                   status=200)
388
    # try to get document with wrong signature
389
    url = document['url']
390
    wrong_url = document['url'] + "wrong/"
391
    resp = app.get(wrong_url, status=404)
392

  
393
    # try expired url
394
    freezer.move_to(timezone.now() + timezone.timedelta(days=8))
395
    resp = app.get(document['url'], status=404)
396

  
397

  
398
def test_error_500(app, resource, mock_api_entreprise):
399
    with HTTMock(api_entreprise_error_500):
400
        response = app.get('/api-entreprise/test/entreprises/443170139/',
401
                           params=REQUEST_PARAMS)
402
        assert response.status_code == 200
403
        assert response.json['err'] == 1
404
        assert response.json['data']['status_code'] == 500
405
        assert 'error happened' in response.json['err_desc']
406

  
407

  
408
def test_no_json_error(app, resource, mock_api_entreprise):
409
    with HTTMock(api_entreprise_error_not_json):
410
        response = app.get('/api-entreprise/test/entreprises/443170139/',
411
                           params=REQUEST_PARAMS)
412
        assert response.status_code == 200
413
        assert response.json['err'] == 1
414
        assert response.json['err_desc'] == "API-entreprise returned non-JSON content with status 200: 'simple text'"
415

  
416

  
417
def test_error_404(app, resource, mock_api_entreprise):
418
    with HTTMock(api_entreprise_error_not_found):
419
        response = app.get('/api-entreprise/test/entreprises/443170139/',
420
                           params=REQUEST_PARAMS)
421
        assert response.status_code == 200
422
        assert response.json['err'] == 1
423
        assert response.json['err_desc'] == 'Page not found'
0
-