passerelle/passerelle/apps/astre_rest/models.py

471 lines
16 KiB
Python

# Copyright (C) 2022 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
import json
import urllib.parse
import requests
from django.db import models
from django.utils.translation import gettext_lazy as _
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
BASE_ENPOINT_PARAMS = {
'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
'budget': {'description': _('Budget'), 'example_value': '01'},
'exercice': {'description': _('Exercice'), 'example_value': '2022'},
}
ENTITY_TYPE_PARAM = {'optionnal': False, 'description': _('Entity type'), 'example_value': 'FACTURE'}
ENTITY_REF_PARAM = {'optionnal': False, 'description': _('Entity ref'), 'example_value': ''}
GF_DOCUMENTS_DOCUMENT_READ_PARAMS = dict(BASE_ENPOINT_PARAMS)
GF_DOCUMENTS_DOCUMENT_READ_PARAMS.update(
{
'entity_type': ENTITY_TYPE_PARAM,
'entity_ref': ENTITY_REF_PARAM,
'document_ref': {'optionnal': False, 'description': _('Document ref'), 'example_value': ''},
}
)
GF_DOCUMENTS_ENTITIES_GETFREF_PARAMS = dict(BASE_ENPOINT_PARAMS)
GF_DOCUMENTS_ENTITIES_GETFREF_PARAMS.update(
{
'entity_type': ENTITY_TYPE_PARAM,
'entity_code': {'optionnal': False, 'description': _('Entity type'), 'example_value': 'RMA01'},
}
)
GF_DOCUMENTS_ENTITIES_READ_PARAMS = dict(BASE_ENPOINT_PARAMS)
GF_DOCUMENTS_ENTITIES_READ_PARAMS.update(
{
'entity_type': ENTITY_TYPE_PARAM,
'entity_ref': ENTITY_REF_PARAM,
}
)
GF_DOCUMENTS_ENTITIES_SEARCH_PARAMS = dict(BASE_ENPOINT_PARAMS)
GF_DOCUMENTS_ENTITIES_SEARCH_PARAMS.update(
{
'entity_type': ENTITY_TYPE_PARAM,
's': {'optionnal': False, 'description': _('Search criteria'), 'example_value': ''},
}
)
GF_DOCUMENTS_ENTITIES_LIST_PARAMS = dict(BASE_ENPOINT_PARAMS)
GF_DOCUMENTS_ENTITIES_LIST_PARAMS.update(
{
'entity_type': ENTITY_TYPE_PARAM,
'entity_ref': ENTITY_REF_PARAM,
'code_list': {'optionnal': False, 'description': _('Code list'), 'example_value': ''},
}
)
DOCUMENT_CREATE_OR_UPDATE_SCHEMA = {
'type': 'object',
'title': _('Document creation or update'),
'properties': {
'file': {
'title': _('File object'),
'type': 'object',
'properties': {
'filename': {
'type': 'string',
'description': _('Filename'),
},
'content': {
'type': 'string',
'description': _('Content'),
},
'content_type': {
'type': 'string',
'description': _('Content type'),
},
},
'required': ['content'],
},
'entity_type': {
'type': 'string',
},
'entity_ref': {
'type': 'string',
},
'astre_ref': {
'type': 'string',
},
'ged_ref': {
'type': 'string',
},
'doc_type': {
'type': 'string',
},
'description': {
'type': 'string',
},
'filename': {
'type': 'string',
'description': _('Filename (takes precendence over filename in "file" object)'),
},
'indpj': {
'type': 'string',
},
'domain_code': {
'type': 'string',
},
},
'required': ['entity_type', 'entity_ref', 'doc_type', 'file'],
'unflatten': False,
}
DOCUMENT_DELETE_SCHEMA = {
'type': 'object',
'title': _('Document delete'),
'properties': {
'entity_type': {
'type': 'string',
},
'entity_ref': {
'type': 'string',
},
'document_ref': {
'type': 'string',
},
},
'required': ['entity_type', 'entity_ref', 'document_ref'],
'unflatten': False,
}
ASTRE_ERRORS = (
'UndefinedError',
'VersionError',
'ConfigError',
'AuthError',
'ConfError',
'DataError',
'NotFoundError',
'AstreError',
'AstreException',
)
AUTH_CHOICES = [
('AD', 'AD'),
('EXTERNE', 'EXTERNE'),
('LDAP', 'LDAP'),
('NORMAL', 'NORMAL'),
]
class AstreREST(BaseResource):
base_url = models.URLField(_('API URL'))
username = models.CharField(_('Username'), max_length=32)
password = models.CharField(_('Password'), max_length=32)
database = models.CharField('Database', max_length=32)
auth = models.CharField(_('Authentication mode'), max_length=32, choices=AUTH_CHOICES, blank=True)
organism = models.CharField('Organisme', max_length=32)
budget = models.CharField('Budget', max_length=32)
exercice = models.CharField('Exercice', max_length=32)
verify_cert = models.BooleanField(default=True, verbose_name=_('Check HTTPS Certificate validity'))
category = _('Business Process Connectors')
class Meta:
verbose_name = 'Astre REST'
def check_status(self):
self._astre_call('astre/webservices/gf/documents/referentiel/typedocument/list/json')
@property
def encrypted_password(self):
key = 48
password = ''
for char in self.password:
password += chr(ord(char) ^ key)
return urllib.parse.quote(password, 'iso-8859-1')
def _astre_call(self, path, method='get', params=None, data=None, files=None, expect_json=True):
params = params or {}
url = urllib.parse.urljoin(self.base_url, path)
authentication_params = {
'login': self.username,
'password': self.encrypted_password,
'database': self.database,
}
if self.auth:
authentication_params['auth'] = self.auth
kwargs = {}
if method == 'get':
kwargs['headers'] = authentication_params
elif method == 'post':
if not data:
data = {}
data.update(authentication_params)
kwargs['data'] = data
if files:
kwargs['files'] = files
resp = getattr(self.requests, method)(url, params=params, **kwargs)
try:
resp.raise_for_status()
except requests.RequestException as e:
content = getattr(getattr(e, 'response', None), 'content', None)
raise APIError('request failed', url, e, content)
try:
json_resp = resp.json()
if hasattr(json_resp, 'get') and json_resp.get('code') in ASTRE_ERRORS:
msg = json_resp.get('code')
if json_resp.get('message'):
msg += ': %s' % json_resp.get('message')
raise APIError(msg)
return json_resp
except json.JSONDecodeError:
if expect_json:
raise
return resp.text
def _get_context_param(self, params, concat=True):
res = []
for param_name in ('organism', 'budget', 'exercice'):
res.append(params[param_name] if param_name in params else getattr(self, param_name))
if concat:
return '%s-%s-%s' % tuple(res)
return res
def _get_document_create_update_data(self, post_data):
organism, budget, exercice = self._get_context_param(post_data, concat=False)
return {
'codeOrganisme': organism,
'codeBudget': budget,
'codeExercice': exercice,
'typeEntite': post_data['entity_type'],
'refEntite': post_data['entity_ref'],
'referenceAstre': post_data.get('astre_ref', ''),
'referenceGed': post_data.get('ged_ref', ''),
'typeDoc': post_data['doc_type'],
'description': post_data.get('description', ''),
'nomFichier': post_data.get('filename', '') or post_data['file'].get('filename', ''),
'typeMime': post_data['file'].get('mime_type', ''),
'indPJ': post_data.get('indpj', ''),
'codeDomaine': post_data.get('domain_code', ''),
}
def _get_data_source(self, data):
res = []
for record in data:
res.append(
{
'id': record['code'],
'text': record['libelle'],
'code': record['code'],
'libelle': record['libelle'],
}
)
return res
@endpoint(
methods=['get'],
perm='can_access',
name='gf-documents-entites-getref',
parameters=GF_DOCUMENTS_ENTITIES_GETFREF_PARAMS,
)
def gf_documents_entites_getref(self, request, **kwargs):
contexte = self._get_context_param(kwargs)
fonctionnal_key = '%s-%s' % (kwargs['entity_type'], kwargs['entity_code'])
path = 'astre/webservices/gf/documents/entites/getRefEntite/%s/%s' % (contexte, fonctionnal_key)
return {'data': self._astre_call(path, expect_json=False)}
@endpoint(
methods=['get'],
perm='can_access',
name='gf-documents-entites-list',
parameters=GF_DOCUMENTS_ENTITIES_LIST_PARAMS,
)
def gf_documents_entites_list(self, request, **kwargs):
contexte = self._get_context_param(kwargs)
entity_key = '%s-%s' % (kwargs['entity_type'], kwargs['entity_ref'])
path = 'astre/webservices/gf/documents/entites/%s/%s/list/%s/json' % (
contexte,
entity_key,
kwargs['code_list'],
)
data = []
for record in self._astre_call(path):
data.append(
{
'id': record['codeEntite'],
'text': record['libelleEntite'],
'codeEntite': record['codeEntite'],
'libelleEntite': record['libelleEntite'],
'typeEntite': record['typeEntite'],
'refEntite': record['refEntite'],
'codeOrganisme': record['codeOrganisme'],
}
)
return {'data': data}
@endpoint(
methods=['get'],
perm='can_access',
name='gf-documents-entites-read',
parameters=GF_DOCUMENTS_ENTITIES_READ_PARAMS,
)
def gf_documents_entites_read(self, request, **kwargs):
contexte = self._get_context_param(kwargs)
entity_key = '%s-%s' % (kwargs['entity_type'], kwargs['entity_ref'])
path = 'astre/webservices/gf/documents/entites/read/%s/%s/json' % (contexte, entity_key)
return {'data': self._astre_call(path)}
@endpoint(
methods=['get'],
perm='can_access',
name='gf-documents-entites-search',
parameters=GF_DOCUMENTS_ENTITIES_SEARCH_PARAMS,
)
def gf_documents_entites_search(self, request, **kwargs):
contexte = self._get_context_param(kwargs)
path = 'astre/webservices/gf/documents/entites/search/%s/%s/json' % (
contexte,
kwargs['entity_type'],
)
data = []
for record in self._astre_call(path, params={'s': kwargs['s']}):
data.append(
{
'id': record['codeEntite'],
'text': record['libEntite'],
'refEntite': record['refEntite'],
'libEntite': record['libEntite'],
'typeEntite': record['typeEntite'],
'codeEntite': record['codeEntite'],
'codeOrganisme': record['codeOrganisme'],
}
)
return {'data': data}
@endpoint(
name='gf-documents-gedmanager-document-create',
description=_('Create document'),
perm='can_access',
post={
'request_body': {
'schema': {
'application/json': DOCUMENT_CREATE_OR_UPDATE_SCHEMA,
}
}
},
)
def gf_documents_gedmanager_document_create(self, request, post_data):
send_data = self._get_document_create_update_data(post_data)
return {
'data': self._astre_call(
'astre/webservices/gf/documents/gedmanager/document/create/json',
method='post',
data=send_data,
files={'fichier': base64.b64decode(post_data['file']['content'])},
)
}
@endpoint(
name='gf-documents-gedmanager-document-delete',
description=_('Delete document'),
perm='can_access',
post={
'request_body': {
'schema': {
'application/json': DOCUMENT_DELETE_SCHEMA,
}
}
},
)
def gf_documents_gedmanager_document_delete(self, request, post_data):
organism, budget, exercice = self._get_context_param(post_data, concat=False)
send_data = {
'codeOrganisme': organism,
'codeBudget': budget,
'codeExercice': exercice,
'typeEntite': post_data['entity_type'],
'refEntite': post_data['entity_ref'],
'referenceGed': post_data['document_ref'],
}
return {
'data': self._astre_call(
'astre/webservices/gf/documents/gedmanager/document/delete/json',
method='post',
data=send_data,
)
}
@endpoint(
methods=['get'],
perm='can_access',
name='gf-documents-gedmanager-document-read',
parameters=GF_DOCUMENTS_DOCUMENT_READ_PARAMS,
)
def gf_documents_gedmanager_document_read(self, request, **kwargs):
context = self._get_context_param(kwargs)
entity_key = '%s-%s' % (kwargs['entity_type'], kwargs['entity_ref'])
path = '/astre/webservices/gf/documents/gedmanager/document/read/%s/%s/%s/json?metadonnees=all' % (
context,
entity_key,
kwargs['document_ref'],
)
return {'data': self._astre_call(path)}
@endpoint(
name='gf-documents-gedmanager-document-update',
description=_('Update document'),
perm='can_access',
post={
'request_body': {
'schema': {
'application/json': DOCUMENT_CREATE_OR_UPDATE_SCHEMA,
}
}
},
)
def gf_documents_gedmanager_document_update(self, request, post_data):
send_data = self._get_document_create_update_data(post_data)
return {
'data': self._astre_call(
'astre/webservices/gf/documents/gedmanager/document/update/json',
method='post',
data=send_data,
files={'fichier': base64.b64decode(post_data['file']['content'])},
)
}
@endpoint(methods=['get'], perm='can_access', name='gf-documents-referentiel-domainepj')
def gf_documents_referentiel_domainepj(self, request):
return {
'data': self._get_data_source(
self._astre_call('astre/webservices/gf/documents/referentiel/domainepj/list/json')
)
}
@endpoint(methods=['get'], perm='can_access', name='gf-documents-referentiel-typedocument')
def gf_documents_referentiel_typedocument(self, request):
return {
'data': self._get_data_source(
self._astre_call('astre/webservices/gf/documents/referentiel/typedocument/list/json')
)
}