Revert "pastell: add initial connector (#79105)" (#79494)

This reverts commit ccb53be16e.
This commit is contained in:
Valentin Deniaud 2023-07-06 18:05:01 +02:00
parent 5cda735517
commit 75db45cdfa
6 changed files with 0 additions and 552 deletions

View File

@ -1,42 +0,0 @@
# Generated by Django 3.2.18 on 2023-06-28 15:32
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('base', '0030_resourcelog_base_resour_appname_298cbc_idx'),
]
operations = [
migrations.CreateModel(
name='Pastell',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('title', models.CharField(max_length=50, verbose_name='Title')),
('slug', models.SlugField(unique=True, verbose_name='Identifier')),
('description', models.TextField(verbose_name='Description')),
('api_base_url', models.URLField(max_length=128, verbose_name='API base URL')),
('token', models.CharField(blank=True, max_length=128, verbose_name='API token')),
('username', models.CharField(blank=True, max_length=128, verbose_name='API username')),
('password', models.CharField(blank=True, max_length=128, verbose_name='API password')),
(
'users',
models.ManyToManyField(
blank=True,
related_name='_pastell_pastell_users_+',
related_query_name='+',
to='base.ApiUser',
),
),
],
options={
'verbose_name': 'Pastell',
},
),
]

View File

@ -1,215 +0,0 @@
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2023 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 base64
from urllib import parse as urlparse
import requests
from django.core.exceptions import ValidationError
from django.db import models
from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _
from requests.auth import HTTPBasicAuth
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
DOCUMENT_CREATION_SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'type': 'object',
'required': ['type'],
'additionalProperties': True,
'properties': {'type': {'type': 'string'}},
}
DOCUMENT_FILE_UPLOAD_SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'type': 'object',
'required': ['file', 'field_name'],
'additionalProperties': False,
'properties': {
'field_name': {
'type': 'string',
'description': _('Document file\'s field name'),
},
'file': {
'title': _('File object'),
'type': 'object',
'properties': {
'name': {
'type': 'string',
'description': _('Filename'),
},
'content': {
'type': 'string',
'description': _('Content'),
},
'content_type': {
'type': 'string',
'description': _('Content type'),
},
},
'required': ['name', 'content'],
},
},
}
class Pastell(BaseResource):
api_base_url = models.URLField(
max_length=128,
verbose_name=_('API base URL'),
)
token = models.CharField(max_length=128, blank=True, verbose_name=_('API token'))
username = models.CharField(max_length=128, blank=True, verbose_name=_('API username'))
password = models.CharField(max_length=128, blank=True, verbose_name=_('API password'))
category = _('Business Process Connectors')
class Meta:
verbose_name = _('Pastell')
def clean(self, *args, **kwargs):
if not self.token and not self.username:
raise ValidationError(_('API token or API username and API password should be defined.'))
return super().clean(*args, **kwargs)
@property
def requests_options(self):
options = {}
if self.token:
options['headers'] = {'Authorization': 'Bearer: %s' % self.token}
elif self.username:
options['auth'] = HTTPBasicAuth(self.username, self.password)
return options
def call(self, path, method='get', params=None, **kwargs):
url = urlparse.urljoin(self.api_base_url, path)
try:
response = self.requests.request(
url=url, method=method, params=params, **kwargs, **self.requests_options
)
response.raise_for_status()
except (requests.Timeout, requests.RequestException) as e:
raise APIError(str(e))
return response
def check_status(self):
try:
response = self.call('version')
except APIError as e:
raise Exception('Pastell server is down: %s' % e)
return {'data': response.json()}
@endpoint(description=_('List entities'))
def entities(self, request):
data = []
response = self.call('entite')
for item in response.json():
item['id'] = item['id_e']
item['text'] = item['denomination']
data.append(item)
return {'data': data}
@endpoint(
description=_('List entity documents'),
parameters={'entity_id': {'description': _('Entity ID'), 'example_value': '42'}},
)
def documents(self, request, entity_id):
data = []
response = self.call('entite/%s/document' % entity_id)
for item in response.json():
item['id'] = item['id_d']
item['text'] = item['titre']
data.append(item)
return {'data': data}
@endpoint(
description=_('Get document details'),
parameters={
'entity_id': {'description': _('Entity ID'), 'example_value': '42'},
'document_id': {'description': _('Document ID'), 'example_value': 'hDWtdSC'},
},
)
def document(self, request, entity_id, document_id):
response = self.call('entite/%s/document/%s' % (entity_id, document_id))
return {'data': response.json()}
@endpoint(
post={
'description': _('Create a document for an entity'),
'request_body': {'schema': {'application/json': DOCUMENT_CREATION_SCHEMA}},
},
parameters={
'entity_id': {'description': _('Entity ID'), 'example_value': '42'},
},
)
def create_document(self, request, entity_id, post_data):
# create document
response = self.call('entite/%s/document' % entity_id, 'post', params=post_data)
document_id = response.json()['id_d']
# update it with other attributes
response = self.call('entite/%s/document/%s' % (entity_id, document_id), 'patch', params=post_data)
return {'data': response.json()}
@endpoint(
post={
'description': _('Upload a file to a document'),
'request_body': {'schema': {'application/json': DOCUMENT_FILE_UPLOAD_SCHEMA}},
},
parameters={
'entity_id': {'description': _('Entity ID'), 'example_value': '42'},
'document_id': {'description': _('Document ID'), 'example_value': 'hDWtdSC'},
},
)
def upload_document_file(self, request, entity_id, document_id, post_data):
filename = post_data['file']['name']
file_data = {
'file_content': (
filename,
base64.b64decode(post_data['file']['content']),
post_data['file'].get('content_type'),
)
}
response = self.call(
'entite/%s/document/%s/file/%s' % (entity_id, document_id, post_data['field_name']),
'post',
files=file_data,
data={'file_name': filename},
)
return {'data': response.json()}
@endpoint(
description=_('Get document\'s file'),
parameters={
'entity_id': {'description': _('Entity ID'), 'example_value': '42'},
'document_id': {'description': _('Document ID'), 'example_value': 'hDWtdSC'},
'field_name': {
'description': _('Document file\'s field name'),
'example_value': 'document',
},
},
)
def get_document_file(self, request, entity_id, document_id, field_name):
document = self.call('entite/%s/document/%s/file/%s' % (entity_id, document_id, field_name))
response = HttpResponse(document.content, content_type=document.headers['Content-Type'])
response['Content-Disposition'] = document.headers['Content-disposition']
return response

View File

@ -170,7 +170,6 @@ INSTALLED_APPS = (
'passerelle.apps.orange',
'passerelle.apps.ovh',
'passerelle.apps.oxyd',
'passerelle.apps.pastell',
'passerelle.apps.pdf',
'passerelle.apps.phonecalls',
'passerelle.apps.photon',

View File

@ -1,294 +0,0 @@
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2023 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 responses
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from passerelle.apps.pastell.models import Pastell
from passerelle.base.models import AccessRight, ApiUser
@pytest.fixture()
def setup(db):
api = ApiUser.objects.create(username='all', keytype='', key='')
conn = Pastell.objects.create(
api_base_url='http://example.com/api/v2/', username='admin', password='admin', slug='test'
)
obj_type = ContentType.objects.get_for_model(conn)
AccessRight.objects.create(
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=conn.pk
)
return conn
@responses.activate
def test_auth_headers(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/version',
json={"version": "3.0.4", "revision": 54322},
status=200,
)
setup.check_status()
assert len(responses.calls) == 1
assert 'Basic ' in responses.calls[0].request.headers['Authorization']
setup.token = '12345'
setup.save()
setup.check_status()
assert len(responses.calls) == 2
assert responses.calls[1].request.headers['Authorization'] == 'Bearer: 12345'
@responses.activate
def test_service_down_status(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/version',
status=404,
)
with pytest.raises(Exception):
setup.check_status()
@responses.activate
def test_list_entities(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/entite',
json=[
{
"id_e": "7",
"denomination": "Publik",
"siren": "198307662",
"type": "collectivite",
"centre_de_gestion": "0",
"entite_mere": "1",
}
],
status=200,
)
url = reverse(
'generic-endpoint', kwargs={'connector': 'pastell', 'endpoint': 'entities', 'slug': setup.slug}
)
resp = app.get(url)
data = resp.json['data']
assert data[0]['id'] == '7'
assert data[0]['text'] == 'Publik'
@responses.activate
def test_list_documents(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/entite/7/document',
json=[
{
"id_d": "MNYDNCa",
"id_e": "7",
"type": "publik",
"titre": "TestConnecteur",
"creation": "2023-06-27 18:02:04",
"modification": "2023-06-27 18:02:28",
"denomination": "Publik",
"entite_base": "Publik",
}
],
status=200,
)
url = reverse(
'generic-endpoint', kwargs={'connector': 'pastell', 'endpoint': 'documents', 'slug': setup.slug}
)
resp = app.get(url, params={'entity_id': '7'})
data = resp.json['data']
assert data[0]['id'] == 'MNYDNCa'
assert data[0]['text'] == 'TestConnecteur'
@responses.activate
def test_get_document_details(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/entite/7/document/MNYDNCa',
json={
"info": {
"id_d": "MNYDNCa",
"type": "publik",
"titre": "TestConnecteur",
"creation": "2023-06-27 18:02:04",
"modification": "2023-06-27 18:02:28",
},
"data": {
"envoi_signature": "checked",
"envoi_iparapheur": "1",
"nom_dossier": "TestConnecteur",
"iparapheur_type": "Type Publik",
"iparapheur_sous_type": "SPublik",
},
},
status=200,
)
url = reverse(
'generic-endpoint', kwargs={'connector': 'pastell', 'endpoint': 'document', 'slug': setup.slug}
)
resp = app.get(url, params={'entity_id': '7', 'document_id': 'MNYDNCa'})
data = resp.json['data']
assert data['data']['nom_dossier'] == 'TestConnecteur'
assert data['info']['id_d'] == 'MNYDNCa'
@responses.activate
def test_create_document(app, setup):
responses.add(
responses.POST,
'http://example.com/api/v2/entite/7/document',
json={
"info": {
"id_d": "67WaYzM",
"type": "publik",
"titre": "",
"creation": "2023-06-27 17:25:54",
"modification": "2023-06-27 17:25:54",
},
"data": {"envoi_signature": "checked", "envoi_iparapheur": "1"},
"action_possible": ["modification", "supression"],
"action-possible": ["modification", "supression"],
"last_action": {
"action": "creation",
"message": "Cr\u00e9ation du document",
"date": "2023-06-27 17:25:54",
},
"id_d": "67WaYzM",
},
status=200,
)
responses.add(
responses.PATCH,
'http://example.com/api/v2/entite/7/document/67WaYzM',
json={
"content": {
"info": {
"id_d": "MNYDNCa",
"type": "publik",
"titre": "TestConnecteur",
"creation": "2023-06-27 18:02:04",
"modification": "2023-06-27 18:02:28",
},
"data": {
"envoi_signature": "checked",
"envoi_iparapheur": "1",
"nom_dossier": "TestConnecteu",
"iparapheur_type": "Type Publik",
"iparapheur_sous_type": "SPublik",
},
},
"result": "ok",
"formulaire_ok": 0,
"message": "Le formulaire est incomplet : le champ \u00abDocument\u00bb est obligatoire.",
},
status=200,
)
url = reverse(
'generic-endpoint', kwargs={'connector': 'pastell', 'endpoint': 'create_document', 'slug': setup.slug}
)
resp = app.post_json(
'%s?entity_id=7' % url,
params={
'type': 'publik',
'nom_dossier': 'TestConnecteur',
'iparapheur_type': 'Type Publik',
'iparapheur_sous_type': 'SPublik',
},
)
assert len(responses.calls) == 2
assert resp.json['data']['result'] == 'ok'
@responses.activate
def test_add_document_file(app, setup):
responses.add(
responses.POST,
'http://example.com/api/v2/entite/7/document/13fgg3E/file/document',
json={
'content': {
'info': {
'id_d': '13fgg3E',
'type': 'publik',
'titre': 'DocumentViaAPI',
'creation': '2023-07-03 17:35:16',
'modification': '2023-07-03 17:35:16',
},
'data': {
'envoi_signature': 'checked',
'envoi_iparapheur': '1',
'nom_dossier': 'DocumentViaAPI',
'iparapheur_type': 'Type Publik',
'iparapheur_sous_type': 'SPublik',
'document': ['TestSerghei.pdf'],
},
},
'result': 'ok',
'formulaire_ok': 1,
'message': '',
'err': 0,
},
status=201,
)
url = reverse(
'generic-endpoint',
kwargs={'connector': 'pastell', 'endpoint': 'upload_document_file', 'slug': setup.slug},
)
resp = app.post_json(
'%s?entity_id=7&document_id=13fgg3E' % url,
params={
'field_name': 'document',
'file': {
'name': 'test.txt',
'content': 'dGVzdA==',
'content_type': 'text/plain',
},
},
)
assert resp.json['data']['result'] == 'ok'
@responses.activate
def test_get_document_file(app, setup):
responses.add(
responses.GET,
'http://example.com/api/v2/entite/7/document/13fgg3E/file/document',
body='test',
content_type='text/plain',
headers={'Content-disposition': 'attachment; filename=test.txt'},
status=200,
)
url = reverse(
'generic-endpoint',
kwargs={'connector': 'pastell', 'endpoint': 'get_document_file', 'slug': setup.slug},
)
resp = app.get(
'%s?entity_id=7&document_id=13fgg3E&field_name=document' % url,
)
assert resp.headers.get('Content-Disposition') == 'attachment; filename=test.txt'
assert resp.headers.get('Content-Type') == 'text/plain'
assert resp.body == b'test'