api_entreprise: add initial connector (#30010)
This commit is contained in:
parent
fdf35728ad
commit
16100aa2fb
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-03-15 09:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('base', '0012_job'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='APIEntreprise',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=50, verbose_name='Title')),
|
||||
('description', models.TextField(verbose_name='Description')),
|
||||
('slug', models.SlugField(unique=True, verbose_name='Identifier')),
|
||||
('url', models.URLField(default=b'https://entreprise.api.gouv.fr/v2/', max_length=256, verbose_name='API URL')),
|
||||
('token', models.CharField(max_length=1024, verbose_name='API token')),
|
||||
('users', models.ManyToManyField(blank=True, to='base.ApiUser')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'API Entreprise',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,312 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
'''Gateway to API-Entreprise web-service from SGMAP:
|
||||
https://entreprise.api.gouv.fr
|
||||
'''
|
||||
|
||||
from six.moves.urllib_parse import urljoin
|
||||
import requests
|
||||
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.timezone import datetime, make_aware, timedelta
|
||||
from django.http import HttpResponse, Http404, HttpResponseBadRequest
|
||||
from django.core import signing
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from passerelle.base.models import BaseResource
|
||||
from passerelle.utils.api import endpoint
|
||||
from passerelle.utils.jsonresponse import APIError
|
||||
from passerelle.views import WrongParameter
|
||||
|
||||
DOCUMENT_SIGNATURE_MAX_AGE = timedelta(days=7)
|
||||
|
||||
def normalize_dates(data):
|
||||
timestamp_to_datetime = {}
|
||||
for key in data:
|
||||
if isinstance(data[key], dict):
|
||||
normalize_dates(data[key])
|
||||
if isinstance(data[key], list):
|
||||
for item in data[key]:
|
||||
normalize_dates(item)
|
||||
|
||||
if key.startswith('date') and not key.endswith('timestamp'):
|
||||
if isinstance(data[key], int):
|
||||
try:
|
||||
data[key] = datetime.fromtimestamp(int(data[key])).date()
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if key.endswith('timestamp'):
|
||||
# timestamps can be integers or strings
|
||||
# convert only if positive values
|
||||
if int(data[key]) > 0:
|
||||
try:
|
||||
timestamp_to_datetime[key[:-len('timestamp')] + 'datetime'] = make_aware(datetime.fromtimestamp(int(data[key])))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
# add converted timestamps to initial data
|
||||
data.update(timestamp_to_datetime)
|
||||
|
||||
|
||||
class APIEntreprise(BaseResource):
|
||||
|
||||
url = models.URLField(_('API URL'), max_length=256, default='https://entreprise.api.gouv.fr/v2/')
|
||||
token = models.CharField(max_length=1024, verbose_name=_('API token'))
|
||||
|
||||
category = _('Business Process Connectors')
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('API Entreprise')
|
||||
|
||||
def get(self, path, **kwargs):
|
||||
params = {'token': self.token}
|
||||
for param in ('context', 'object', 'recipient'):
|
||||
if not kwargs.get(param):
|
||||
raise WrongParameter([param], [])
|
||||
params[param] = kwargs[param]
|
||||
url = urljoin(self.url, path)
|
||||
try:
|
||||
response = self.requests.get(url, data=params)
|
||||
except requests.RequestException as e:
|
||||
raise APIError(u'API-entreprise connection error: %s' %
|
||||
response.status_code,
|
||||
data={'error': unicode(e)})
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError as e:
|
||||
content = repr(response.content[:1000])
|
||||
raise APIError(
|
||||
u'API-entreprise returned non-JSON content with status %s: %s' %
|
||||
(response.status_code, content),
|
||||
data={'status_code': response.status_code,
|
||||
'exception': unicode(e),
|
||||
'content': content,
|
||||
})
|
||||
if response.status_code != 200:
|
||||
if data.get('error') == 'not_found':
|
||||
return {
|
||||
'err': 1,
|
||||
'err_desc': data.get('message', 'not-found'),
|
||||
}
|
||||
raise APIError(
|
||||
u'API-entreprise returned a non 200 status %s: %s' %
|
||||
(response.status_code, data),
|
||||
data={
|
||||
'status_code': response.status_code,
|
||||
'content': data,
|
||||
})
|
||||
normalize_dates(data)
|
||||
return {
|
||||
'err': 0,
|
||||
'data': data,
|
||||
}
|
||||
|
||||
|
||||
@endpoint(perm='can_access',
|
||||
pattern='(?P<institution_id>\w+)/$',
|
||||
example_pattern='{institution_id}/',
|
||||
description=_('Get association\'s documents'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('association id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'object': {
|
||||
'Description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def documents_associations(self, request, institution_id, **kwargs):
|
||||
data = []
|
||||
resp = self.get('documents_associations/%s/' % institution_id, **kwargs)
|
||||
for item in resp['data'].get('documents', []):
|
||||
# ignore documents with no type
|
||||
if not item.get('type'):
|
||||
continue
|
||||
signature_elements = {'url': item['url'],
|
||||
'context': kwargs['context'],
|
||||
'object': kwargs['object'],
|
||||
'recipient': kwargs['recipient']}
|
||||
signature = signing.dumps(signature_elements)
|
||||
document_url = request.build_absolute_uri(reverse('generic-endpoint', kwargs={'connector': self.get_connector_slug(),
|
||||
'slug': self.slug,
|
||||
'endpoint': 'document',
|
||||
'rest': '%s/%s/' % (institution_id, signature)}))
|
||||
item['id'] = item['timestamp']
|
||||
item['text'] = item['type']
|
||||
item['url'] = document_url
|
||||
data.append(item)
|
||||
# sort data by date
|
||||
data.sort(key=lambda i: i['id'])
|
||||
return {'err': 0, 'data': data}
|
||||
|
||||
|
||||
@endpoint(pattern='(?P<institution_id>\w+)/(?P<document_id>[\:\w-]+)/$',
|
||||
example_pattern='{institution_id}/{document_id}/',
|
||||
description=_('Get institution\'s document'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('institution id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'document_id': {
|
||||
'description': _('document id'),
|
||||
'example_value': 'A1500660325',
|
||||
},
|
||||
'object': {
|
||||
'Description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def document(self, request, institution_id, document_id, **kwargs):
|
||||
try:
|
||||
params = signing.loads(document_id, max_age=DOCUMENT_SIGNATURE_MAX_AGE)
|
||||
except signing.BadSignature:
|
||||
raise Http404('document not found')
|
||||
response = self.requests.get(params['url'])
|
||||
if response.ok:
|
||||
return HttpResponse(response, content_type='application/pdf')
|
||||
raise Http404('document not found')
|
||||
|
||||
|
||||
@endpoint(perm='can_access',
|
||||
pattern='(?P<institution_id>\w+)/$',
|
||||
example_pattern='{institution_id}/',
|
||||
description=_('Get institution\'s data from Infogreffe'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('institution id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'object': {
|
||||
'Description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def extraits_rcs(self, request, institution_id, **kwargs):
|
||||
return self.get('extraits_rcs_infogreffe/%s/' % institution_id, **kwargs)
|
||||
|
||||
|
||||
@endpoint(perm='can_access',
|
||||
pattern='(?P<institution_id>\w+)/$',
|
||||
example_pattern='{institution_id}/',
|
||||
description=_('Get institution\'s related informations'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('institution id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'object': {
|
||||
'Description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def associations(self, request, institution_id, **kwargs):
|
||||
return self.get('associations/%s/' % institution_id, **kwargs)
|
||||
|
||||
|
||||
@endpoint(perm='can_access',
|
||||
pattern='(?P<institution_id>\w+)/$',
|
||||
example_pattern='{institution_id}/',
|
||||
description=_('Get institution\'s related informations'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('association id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'object': {
|
||||
'description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def entreprises(self, request, institution_id, **kwargs):
|
||||
return self.get('entreprises/%s/' % institution_id, **kwargs)
|
||||
|
||||
|
||||
@endpoint(perm='can_access',
|
||||
methods=['get'],
|
||||
pattern='(?P<institution_id>\w+)/$',
|
||||
example_pattern='{institution_id}/', #&
|
||||
description_get=_('Get institution\'s related informations'),
|
||||
parameters={
|
||||
'institution_id': {
|
||||
'description': _('institution id'),
|
||||
'example_value': '44317013900036',
|
||||
},
|
||||
'object': {
|
||||
'description': _('request object'),
|
||||
'example_value': 'MSP'
|
||||
},
|
||||
'context': {
|
||||
'description': _('request context'),
|
||||
'example_value': '42'
|
||||
},
|
||||
'recipient': {
|
||||
'description': _('request recipient: usually customer number'),
|
||||
'example_value': '44317013900036'
|
||||
}
|
||||
}
|
||||
)
|
||||
def etablissements(self, request, institution_id, **kwargs):
|
||||
return self.get('etablissements/%s/' % institution_id, **kwargs)
|
|
@ -145,6 +145,7 @@ INSTALLED_APPS = (
|
|||
'passerelle.apps.solis',
|
||||
'passerelle.apps.arpege_ecp',
|
||||
'passerelle.apps.vivaticket',
|
||||
'passerelle.apps.api_entreprise',
|
||||
# backoffice templates and static
|
||||
'gadjo',
|
||||
)
|
||||
|
|
|
@ -168,6 +168,10 @@ li.connector.apiparticulier a::before {
|
|||
content: "\f2c3"; /* id-card-o */
|
||||
}
|
||||
|
||||
li.connector.apientreprise a::before {
|
||||
content: "\f1ad"; /* fa-building-o */
|
||||
}
|
||||
|
||||
li.connector.dpark a::before {
|
||||
content: "\f1b9"; /* car */
|
||||
}
|
||||
|
|
|
@ -0,0 +1,437 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# tests/test_api_entreprise.py
|
||||
# Copyright (C) 2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
|
||||
from httmock import urlmatch, HTTMock, response
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from passerelle.apps.api_entreprise.models import APIEntreprise
|
||||
|
||||
from utils import make_resource, endpoint_get, FakedResponse
|
||||
|
||||
ETABLISSEMENTS_RESPONSE = {
|
||||
"etablissement": {
|
||||
"siege_social": True,
|
||||
"siret": "41816609600051",
|
||||
"naf": "6202A",
|
||||
"libelle_naf": "Conseil en systèmes et logiciels informatiques",
|
||||
"date_mise_a_jour": 1449183600,
|
||||
"tranche_effectif_salarie_etablissement": {
|
||||
"de": 200,
|
||||
"a": 249,
|
||||
"code": "31",
|
||||
"date_reference": "2014",
|
||||
"intitule": "200 à 249 salariés"
|
||||
},
|
||||
"date_creation_etablissement": 1108594800,
|
||||
"region_implantation": {
|
||||
"code": "11",
|
||||
"value": "Île-de-France"
|
||||
},
|
||||
"commune_implantation": {
|
||||
"code": "75108",
|
||||
"value": "PARIS 8"
|
||||
},
|
||||
"adresse": {
|
||||
"l1": "OCTO TECHNOLOGY",
|
||||
"l4": "50 AVENUE DES CHAMPS ELYSEES",
|
||||
"l6": "75008 PARIS",
|
||||
"l7": "FRANCE",
|
||||
"numero_voie": "50",
|
||||
"type_voie": "AV",
|
||||
"nom_voie": "DES CHAMPS ELYSEES",
|
||||
"code_postal": "75008",
|
||||
"localite": "PARIS 8",
|
||||
"code_insee_localite": "75108",
|
||||
},
|
||||
"etat_administratif": {
|
||||
"value": "F",
|
||||
"date_fermeture": 1315173600
|
||||
}
|
||||
},
|
||||
"gateway_error": False
|
||||
}
|
||||
|
||||
ENTREPRISES_RESPONSE = {
|
||||
"entreprise": {
|
||||
"siren": "418166096",
|
||||
"capital_social": 459356,
|
||||
"numero_tva_intracommunautaire": "FR16418166096",
|
||||
"forme_juridique": "SA à directoire (s.a.i.)",
|
||||
"forme_juridique_code": "5699",
|
||||
"nom_commercial": "OCTO-TECHNOLOGY",
|
||||
"procedure_collective": False,
|
||||
"naf_entreprise": "6202A",
|
||||
"libelle_naf_entreprise": "Conseil en systèmes et logiciels informatiques",
|
||||
"raison_sociale": "OCTO-TECHNOLOGY",
|
||||
"siret_siege_social": "41816609600051",
|
||||
"code_effectif_entreprise": "31",
|
||||
"date_creation": 891381600,
|
||||
"categorie_entreprise": "PME",
|
||||
"tranche_effectif_salarie_entreprise": {
|
||||
"de": 200,
|
||||
"a": 249,
|
||||
"code": "31",
|
||||
"date_reference": "2014",
|
||||
"intitule": "200 à 249 salariés"
|
||||
},
|
||||
"mandataires_sociaux": [{
|
||||
"nom": "HISQUIN",
|
||||
"prenom": "FRANCOIS",
|
||||
"fonction": "PRESIDENT DU DIRECTOIRE",
|
||||
"dirigeant": True,
|
||||
"date_naissance": "1965-01-27",
|
||||
"raison_sociale": "",
|
||||
"identifiant": "",
|
||||
"type": "PP"
|
||||
}, {
|
||||
"nom": "",
|
||||
"prenom": "",
|
||||
"fonction": "COMMISSAIRE AUX COMPTES SUPPLEANT",
|
||||
"dirigeant": True,
|
||||
"date_naissance": "",
|
||||
"date_naissance_timestamp": 0,
|
||||
"raison_sociale": "BCRH & ASSOCIES - SOCIETE A RESPONSABILITE LIMITEE A ASSOCIE UNIQUE",
|
||||
"identifiant": "490092574",
|
||||
"type": "PM"
|
||||
}
|
||||
],
|
||||
"etat_administratif": {
|
||||
"value": "C", # A (actif) ou C (cessé)
|
||||
"date_cessation": 1315173600 # null quand actif (A), un timestamp (un entier) quand cessé (C )
|
||||
}
|
||||
},
|
||||
"etablissement_siege": {
|
||||
"siege_social": True,
|
||||
"siret": "41816609600051",
|
||||
"naf": "6202A",
|
||||
"libelle_naf": "Conseil en systèmes et logiciels informatiques",
|
||||
"date_mise_a_jour": 1449183600,
|
||||
"tranche_effectif_salarie_etablissement": {
|
||||
"de": 200,
|
||||
"a": 249,
|
||||
"code": "31",
|
||||
"date_reference": "2014",
|
||||
"intitule": "200 à 249 salariés"
|
||||
},
|
||||
"date_creation_etablissement": 1108594800,
|
||||
"region_implantation": {
|
||||
"code": "11",
|
||||
"value": "Île-de-France"
|
||||
},
|
||||
"commune_implantation": {
|
||||
"code": "75108",
|
||||
"value": "PARIS 8"
|
||||
},
|
||||
"adresse": {
|
||||
"l1": "OCTO TECHNOLOGY",
|
||||
"l4": "50 AVENUE DES CHAMPS ELYSEES",
|
||||
"l6": "75008 PARIS",
|
||||
"l7": "FRANCE",
|
||||
"numero_voie": "50",
|
||||
"type_voie": "AV",
|
||||
"nom_voie": "DES CHAMPS ELYSEES",
|
||||
"code_postal": "75008",
|
||||
"localite": "PARIS 8",
|
||||
"code_insee_localite": "75108",
|
||||
},
|
||||
"etat_administratif": {
|
||||
"value": "F",
|
||||
"date_fermeture": 1315173600
|
||||
}
|
||||
},
|
||||
"gateway_error": False
|
||||
}
|
||||
|
||||
EXTRAITS_RCS_RESPONSE = {
|
||||
"siren": "418166096",
|
||||
"date_immatriculation": "1998-03-27",
|
||||
"date_immatriculation_timestamp": 890953200,
|
||||
"date_extrait": "21 AVRIL 2017",
|
||||
"observations": [
|
||||
{
|
||||
"date": "2000-02-23",
|
||||
"date_timestamp": 951260400,
|
||||
"numero": "12197",
|
||||
"libelle": " LA SOCIETE NE CONSERVE AUCUNE ACTIVITE A SON ANCIEN SIEGE "
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
ASSOCIATIONS_RESPONSE = {
|
||||
"association" : {
|
||||
"id": "W751135389",
|
||||
"titre": "ALLIANCE DU COEUR: UNION NATIONALE DES FEDERATIONS ET ASSOCIATIONS DE MALADES CARDIOVASCULAIRES",
|
||||
"objet": "information, soutien, solidarité et accompagnement psycho médico social des personnes malades cardiovasculaires et de leurs proches...",
|
||||
"siret": "42135938100025",
|
||||
"siret_siege_social": "42135938100033",
|
||||
"date_creation": "1993-02-11",
|
||||
"date_declaration": "2013-06-28",
|
||||
"date_publication": "1993-03-03",
|
||||
"adresse_siege": {
|
||||
"numero_voie": "10",
|
||||
"type_voie": "RUE",
|
||||
"libelle_voie": "Lebouis",
|
||||
"code_insee": "75120",
|
||||
"code_postal": "75014",
|
||||
"commune": "Paris"
|
||||
},
|
||||
"groupement": "Simple",
|
||||
"mise_a_jour": "2013-06-28"
|
||||
}
|
||||
}
|
||||
|
||||
DOCUMENTS_ASSOCIATION_RESPONSE = {
|
||||
"nombre_documents": 2,
|
||||
"documents": [
|
||||
{
|
||||
"type": "Statuts",
|
||||
"url": "https://apientreprise.fr/attestations/40ab0b07d434d0417e8997ce7c5afbef/attestation_document_association.pdf",
|
||||
"timestamp": "1500660325"
|
||||
},
|
||||
{
|
||||
"type": "Récépissé",
|
||||
"url": "https://apientreprise.fr/attestations/40ab0b07d434d0417e8997ce7c5afbef/recepisse_association.pdf",
|
||||
"timestamp": "1500667325"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
DOCUMENT_ASSOCIATION_RESPONSE = "binary content"
|
||||
|
||||
REQUEST_PARAMS = {'context': 'MSP', 'object': 'demand', 'recipient': 'siret'}
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$',
|
||||
path='^/v2/etablissements/')
|
||||
def api_entreprise_etablissements(url, request):
|
||||
return response(200, ETABLISSEMENTS_RESPONSE, request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$',
|
||||
path='^/v2/entreprises/')
|
||||
def api_entreprise_entreprises(url, request):
|
||||
return response(200, ENTREPRISES_RESPONSE, request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$',
|
||||
path='^/v2/associations/')
|
||||
def api_entreprise_associations(url, request):
|
||||
return response(200, ASSOCIATIONS_RESPONSE, request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$',
|
||||
path='^/v2/extraits_rcs_infogreffe/')
|
||||
def api_entreprise_extraits_rcs(url, request):
|
||||
return response(200, EXTRAITS_RCS_RESPONSE, request=request)
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$',
|
||||
path='^/v2/documents_associations/')
|
||||
def api_entreprise_documents_associations(url, request):
|
||||
return response(200, DOCUMENTS_ASSOCIATION_RESPONSE, request=request)
|
||||
|
||||
@urlmatch(netloc='^apientreprise.fr$',
|
||||
path='^/attestations/')
|
||||
def api_entreprise_document_association(url, request):
|
||||
return response(200, DOCUMENT_ASSOCIATION_RESPONSE, request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$')
|
||||
def api_entreprise_error_500(url, request):
|
||||
return response(500, 'bad error happened', request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$')
|
||||
def api_entreprise_error_not_json(url, request):
|
||||
return response(200, 'simple text', request=request)
|
||||
|
||||
|
||||
@urlmatch(netloc='^entreprise.api.gouv.fr$')
|
||||
def api_entreprise_error_not_found(url, request):
|
||||
return response(404, {
|
||||
'error': 'not_found',
|
||||
'message': u'Page not found'
|
||||
}, request=request)
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def mock_api_entreprise():
|
||||
with HTTMock(api_entreprise_etablissements, api_entreprise_entreprises, api_entreprise_associations, api_entreprise_extraits_rcs,
|
||||
api_entreprise_associations, api_entreprise_documents_associations, api_entreprise_document_association):
|
||||
yield None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def resource(db):
|
||||
return make_resource(
|
||||
APIEntreprise,
|
||||
slug='test',
|
||||
title='API Entreprise',
|
||||
description='API Entreprise',
|
||||
token='83c68bf0b6013c4daf3f8213f7212aa5')
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.get')
|
||||
def test_endpoint_with_no_params(mocked_get, app, resource):
|
||||
mocked_get.return_value = FakedResponse(content='{}', status_code=200)
|
||||
response = app.get('/api-entreprise/test/documents_associations/443170139/', status=400)
|
||||
assert response.json['err_class'] == 'passerelle.views.WrongParameter'
|
||||
assert response.json['err_desc'] == "missing parameters: 'context'."
|
||||
params = {'context': 'Custom context', 'object': 'Custom object', 'recipient': 'Custom recipient'}
|
||||
response = app.get('/api-entreprise/test/documents_associations/443170139/', params=params)
|
||||
assert mocked_get.call_args[1]['data']['context'] == 'Custom context'
|
||||
assert mocked_get.call_args[1]['data']['object'] == 'Custom object'
|
||||
assert mocked_get.call_args[1]['data']['recipient'] == 'Custom recipient'
|
||||
|
||||
|
||||
def test_entreprises_endpoint(app, resource, mock_api_entreprise):
|
||||
response = app.get('/api-entreprise/test/entreprises/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
data = response.json['data']
|
||||
assert data['entreprise']['categorie_entreprise'] == 'PME'
|
||||
assert data['entreprise']['numero_tva_intracommunautaire'] == 'FR16418166096'
|
||||
assert data['entreprise']['siret_siege_social'] == '41816609600051'
|
||||
assert data['entreprise']['forme_juridique_code'] == '5699'
|
||||
assert data['entreprise']['siren'] == '418166096'
|
||||
assert data['entreprise']['date_creation'] == '1998-03-31'
|
||||
assert data['entreprise']['mandataires_sociaux'][0]['date_naissance'] == '1965-01-27'
|
||||
|
||||
assert 'etablissement_siege' in data
|
||||
assert data['etablissement_siege']['siret'] == '41816609600051'
|
||||
assert data['etablissement_siege']['adresse']['numero_voie'] == '50'
|
||||
assert data['etablissement_siege']['adresse']['type_voie'] == 'AV'
|
||||
assert data['etablissement_siege']['adresse']['nom_voie'] == 'DES CHAMPS ELYSEES'
|
||||
assert data['etablissement_siege']['adresse']['code_postal'] == '75008'
|
||||
assert data['etablissement_siege']['adresse']['localite'] == 'PARIS 8'
|
||||
assert data['etablissement_siege']['adresse']['code_insee_localite'] == '75108'
|
||||
assert data['etablissement_siege']['date_mise_a_jour'] == '2015-12-03'
|
||||
|
||||
|
||||
def test_etablissements_endpoint(app, resource, mock_api_entreprise):
|
||||
response = app.get('/api-entreprise/test/etablissements/44317013900036/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert 'data' in response.json
|
||||
data = response.json['data']
|
||||
|
||||
assert 'etablissement' in data
|
||||
assert data['etablissement']['siret'] == '41816609600051'
|
||||
assert data['etablissement']['naf'] == '6202A'
|
||||
assert data['etablissement']['date_mise_a_jour'] == '2015-12-03'
|
||||
assert data['etablissement']['date_creation_etablissement'] == '2005-02-16'
|
||||
|
||||
assert 'adresse' in data['etablissement']
|
||||
assert data['etablissement']['adresse']['code_postal'] == '75008'
|
||||
assert data['etablissement']['adresse']['localite'] == 'PARIS 8'
|
||||
assert data['etablissement']['adresse']['code_insee_localite'] == '75108'
|
||||
|
||||
|
||||
def test_associations_endpoint(app, resource, mock_api_entreprise):
|
||||
response = app.get('/api-entreprise/test/associations/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert 'data' in response.json
|
||||
data = response.json['data']
|
||||
|
||||
assert 'association' in data
|
||||
assert data['association']['id'] == 'W751135389'
|
||||
assert data['association']['siret'] == '42135938100025'
|
||||
assert data['association']['date_creation'] == '1993-02-11'
|
||||
assert data['association']['date_declaration'] == '2013-06-28'
|
||||
assert data['association']['date_publication'] == '1993-03-03'
|
||||
|
||||
assert 'adresse_siege' in data['association']
|
||||
assert data['association']['adresse_siege']['code_postal'] == '75014'
|
||||
assert data['association']['adresse_siege']['code_insee'] == '75120'
|
||||
|
||||
|
||||
def test_documents_associations_endpoint(app, resource, mock_api_entreprise):
|
||||
response = app.get('/api-entreprise/test/documents_associations/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert 'data' in response.json
|
||||
data = response.json['data']
|
||||
assert len(data) == 2
|
||||
for document in data:
|
||||
assert 'id' in document
|
||||
assert 'text' in document
|
||||
assert 'url' in document
|
||||
assert 'type' in document
|
||||
assert 'datetime' in document
|
||||
|
||||
|
||||
def test_extraits_rcs(app, resource, mock_api_entreprise, freezer):
|
||||
response = app.get('/api-entreprise/test/extraits_rcs/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert 'data' in response.json
|
||||
data = response.json['data']
|
||||
|
||||
assert data['siren'] == '418166096'
|
||||
assert data['date_extrait'] == '21 AVRIL 2017'
|
||||
assert data['date_immatriculation_timestamp'] == 890953200
|
||||
assert data['date_immatriculation'] == '1998-03-27'
|
||||
assert data['date_immatriculation_datetime'] == '1998-03-26T23:00:00Z'
|
||||
|
||||
|
||||
def test_document_association(app, resource, mock_api_entreprise, freezer):
|
||||
response = app.get('/api-entreprise/test/documents_associations/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert 'data' in response.json
|
||||
data = response.json['data']
|
||||
assert len(data) == 2
|
||||
document = data[0]
|
||||
assert 'url' in document
|
||||
resp = app.get(document['url'],
|
||||
params={'context': 'MSP', 'object': 'demand', 'recipient': 'siret'},
|
||||
status=200)
|
||||
# try to get document with wrong signature
|
||||
url = document['url']
|
||||
wrong_url = document['url'] + "wrong/"
|
||||
resp = app.get(wrong_url, status=404)
|
||||
|
||||
# try expired url
|
||||
freezer.move_to(timezone.now() + timezone.timedelta(days=8))
|
||||
resp = app.get(document['url'], status=404)
|
||||
|
||||
|
||||
def test_error_500(app, resource, mock_api_entreprise):
|
||||
with HTTMock(api_entreprise_error_500):
|
||||
response = app.get('/api-entreprise/test/entreprises/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert response.status_code == 200
|
||||
assert response.json['err'] == 1
|
||||
assert response.json['data']['status_code'] == 500
|
||||
assert 'error happened' in response.json['err_desc']
|
||||
|
||||
|
||||
def test_no_json_error(app, resource, mock_api_entreprise):
|
||||
with HTTMock(api_entreprise_error_not_json):
|
||||
response = app.get('/api-entreprise/test/entreprises/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert response.status_code == 200
|
||||
assert response.json['err'] == 1
|
||||
assert response.json['err_desc'] == "API-entreprise returned non-JSON content with status 200: 'simple text'"
|
||||
|
||||
|
||||
def test_error_404(app, resource, mock_api_entreprise):
|
||||
with HTTMock(api_entreprise_error_not_found):
|
||||
response = app.get('/api-entreprise/test/entreprises/443170139/',
|
||||
params=REQUEST_PARAMS)
|
||||
assert response.status_code == 200
|
||||
assert response.json['err'] == 1
|
||||
assert response.json['err_desc'] == 'Page not found'
|
Loading…
Reference in New Issue