Projet

Général

Profil

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

Serghei Mihai (congés, retour 15/05), 12 avril 2019 14:24

Télécharger (32,3 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      | 313 +++++++++++++
 passerelle/settings.py                        |   1 +
 passerelle/static/css/style.css               |   4 +
 tests/test_api_entreprise.py                  | 437 ++++++++++++++++++
 7 files changed, 787 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
    object_datetime = None
41
    for key in data:
42
        if isinstance(data[key], dict):
43
            normalize_dates(data[key])
44
        if isinstance(data[key], list):
45
            for item in data[key]:
46
                normalize_dates(item)
47

  
48
        if key.startswith('date') and not key.endswith('timestamp'):
49
            if isinstance(data[key], int) or data[key].isdigit():
50
                try:
51
                    data[key] = datetime.fromtimestamp(int(data[key])).date()
52
                except (ValueError, TypeError):
53
                    pass
54

  
55
        if key.endswith('timestamp'):
56
            # timestamps can be integers or strings
57
            # convert only if positive values
58
            if int(data[key]) > 0:
59
                try:
60
                    object_datetime = make_aware(datetime.fromtimestamp(int(data[key])))
61
                except (ValueError, TypeError):
62
                    pass
63
    if object_datetime is not None:
64
        data['datetime'] = object_datetime
65

  
66

  
67
class APIEntreprise(BaseResource):
68

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

  
72
    category = _('Business Process Connectors')
73

  
74
    class Meta:
75
        verbose_name = _('API Entreprise')
76

  
77
    def get(self, path, **kwargs):
78
        params = {'token': self.token}
79
        for param in ('context', 'object', 'recipient'):
80
            if not kwargs.get(param):
81
                raise WrongParameter([param], [])
82
            params[param] = kwargs[param]
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
                  'object': {
131
                      'Description': _('request object'),
132
                      'example_value': 'MSP'
133
                  },
134
                  'context': {
135
                      'description': _('request context'),
136
                      'example_value': '42'
137
                  },
138
                  'recipient': {
139
                      'description': _('request recipient: usually customer number'),
140
                      'example_value': '44317013900036'
141
                  }
142
              }
143
    )
144
    def documents_associations(self, request, institution_id, **kwargs):
145
        data = []
146
        resp = self.get('documents_associations/%s/' % institution_id, **kwargs)
147
        for item in resp['data'].get('documents', []):
148
            # ignore documents with no type
149
            if not item.get('type'):
150
                continue
151
            signature_elements = {'url': item['url'],
152
                                  'context': kwargs['context'],
153
                                  'object': kwargs['object'],
154
                                  'recipient': kwargs['recipient']}
155
            signature = signing.dumps(signature_elements)
156
            document_url = request.build_absolute_uri(reverse('generic-endpoint', kwargs={'connector': self.get_connector_slug(),
157
                                        'slug': self.slug,
158
                                        'endpoint': 'document',
159
                                        'rest': '%s/%s/' % (institution_id, signature)}))
160
            data.append({'id': item['timestamp'],
161
                         'text': item['type'],
162
                         'type': item['type'],
163
                         'datetime': item['datetime'],
164
                         'url': document_url})
165
        # sort data by date
166
        data.sort(key=lambda i: i['id'])
167
        return {'err': 0, 'data': data}
168

  
169

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

  
206

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

  
233

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

  
260

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

  
287

  
288
    @endpoint(perm='can_access',
289
              methods=['get'],
290
              pattern='(?P<institution_id>\w+)/$',
291
              example_pattern='{institution_id}/', #&
292
              description_get=_('Get institution\'s related informations'),
293
              parameters={
294
                  'institution_id': {
295
                      'description': _('institution id'),
296
                      'example_value': '44317013900036',
297
                  },
298
                  'object': {
299
                      'description': _('request object'),
300
                      'example_value': 'MSP'
301
                  },
302
                  'context': {
303
                      'description': _('request context'),
304
                      'example_value': '42'
305
                  },
306
                  'recipient': {
307
                      'description': _('request recipient: usually customer number'),
308
                      'example_value': '44317013900036'
309
                  }
310
              }
311
    )
312
    def etablissements(self, request, institution_id, **kwargs):
313
        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
        assert 'datetime' in document
376

  
377

  
378
def test_extraits_rcs(app, resource, mock_api_entreprise, freezer):
379
    response = app.get('/api-entreprise/test/extraits_rcs/443170139/',
380
                       params=REQUEST_PARAMS)
381
    assert 'data' in response.json
382
    data = response.json['data']
383

  
384
    assert data['siren'] == '418166096'
385
    assert data['date_extrait'] == '21 AVRIL 2017'
386
    assert data['date_immatriculation_timestamp'] == 890953200
387
    assert data['date_immatriculation'] == '1998-03-27'
388
    assert data['datetime'] == '1998-03-26T23:00:00Z'
389

  
390

  
391
def test_document_association(app, resource, mock_api_entreprise, freezer):
392
    response = app.get('/api-entreprise/test/documents_associations/443170139/',
393
                       params=REQUEST_PARAMS)
394
    assert 'data' in response.json
395
    data = response.json['data']
396
    assert len(data) == 2
397
    document = data[0]
398
    assert 'url' in document
399
    resp = app.get(document['url'],
400
                   params={'context': 'MSP', 'object': 'demand', 'recipient': 'siret'},
401
                   status=200)
402
    # try to get document with wrong signature
403
    url = document['url']
404
    wrong_url = document['url'] + "wrong/"
405
    resp = app.get(wrong_url, status=404)
406

  
407
    # try expired url
408
    freezer.move_to(timezone.now() + timezone.timedelta(days=8))
409
    resp = app.get(document['url'], status=404)
410

  
411

  
412
def test_error_500(app, resource, mock_api_entreprise):
413
    with HTTMock(api_entreprise_error_500):
414
        response = app.get('/api-entreprise/test/entreprises/443170139/',
415
                           params=REQUEST_PARAMS)
416
        assert response.status_code == 200
417
        assert response.json['err'] == 1
418
        assert response.json['data']['status_code'] == 500
419
        assert 'error happened' in response.json['err_desc']
420

  
421

  
422
def test_no_json_error(app, resource, mock_api_entreprise):
423
    with HTTMock(api_entreprise_error_not_json):
424
        response = app.get('/api-entreprise/test/entreprises/443170139/',
425
                           params=REQUEST_PARAMS)
426
        assert response.status_code == 200
427
        assert response.json['err'] == 1
428
        assert response.json['err_desc'] == "API-entreprise returned non-JSON content with status 200: 'simple text'"
429

  
430

  
431
def test_error_404(app, resource, mock_api_entreprise):
432
    with HTTMock(api_entreprise_error_not_found):
433
        response = app.get('/api-entreprise/test/entreprises/443170139/',
434
                           params=REQUEST_PARAMS)
435
        assert response.status_code == 200
436
        assert response.json['err'] == 1
437
        assert response.json['err_desc'] == 'Page not found'
0
-