passerelle/passerelle/apps/gdc/models.py

132 lines
4.5 KiB
Python

# Passerelle - uniform access to data and services
# Copyright (C) 2015-2020 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 xml.etree.ElementTree as ET
try:
import phpserialize
except ImportError:
phpserialize = None
from django.db import models
from django.utils.encoding import force_str
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
def deep_bytes2str(obj):
if obj is None or isinstance(obj, (int, str)):
return obj
if isinstance(obj, bytes):
try:
return obj.decode('utf-8')
except UnicodeDecodeError:
return obj
if isinstance(obj, list):
return [deep_bytes2str(x) for x in obj]
if isinstance(obj, dict):
new_d = {}
for k, v in obj.items():
new_d[force_str(k)] = deep_bytes2str(v)
return new_d
return obj
def phpserialize_loads(s):
try:
return deep_bytes2str(phpserialize.loads(s.encode('utf-8')))
except ValueError:
truncated = s[:128] if isinstance(s, str) else s
raise APIError(f'Could not deserialize GDC response {truncated!r}', data={'content': s})
class Gdc(BaseResource):
service_url = models.CharField(
max_length=128, blank=False, verbose_name=_('Service URL'), help_text=_('GDC Web Service URL')
)
category = _('Business Process Connectors')
class Meta:
verbose_name = _('GDC Web Service')
def call_soap(self, action, *args, **kwargs):
def escape(s):
return force_str(s).replace('&', '&amp;').replace('>', '&gt;').replace('<', '&lt;')
params = []
for i, arg in enumerate(args):
params.append(
'<v%(i)s xsi:type="xsd:string">%(value)s</v%(i)s>' % {'i': i + 1, 'value': escape(arg)}
)
for key, value in kwargs.items():
if value is None:
params.append('<%s xsi:null="1"/>' % key)
continue
type_ = 'int' if isinstance(value, int) else 'string'
params.append(
'<%(key)s xsi:type="xsd:%(type)s">%(value)s</%(key)s>'
% {'key': key, 'type': type_, 'value': escape(value)}
)
data = """<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
>
<SOAP-ENV:Body>
<%(action)s SOAP-ENC:root="1">
%(params)s
</%(action)s>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>""" % {
'action': action,
'params': '\n'.join(params),
}
resp = self.requests.post(
self.service_url,
data=data.encode('utf-8'),
headers={'SOAPAction': '"%s"' % action, 'Content-type': 'text/xml; charset=UTF-8'},
)
return ET.ElementTree(ET.fromstring(resp.content))
@endpoint(perm='OPEN')
def communes(self, request, *args, **kwargs):
resp = self.call_soap('getListeCommune')
soap_result = phpserialize_loads(resp.findall('.//listeCommune')[0].text)
result = []
for k, v in soap_result.items():
result.append({'id': k, 'text': force_str(v, 'utf-8')})
result.sort(key=lambda x: x['id'])
return result
@endpoint(perm='OPEN')
def objets(self, request, *args, **kwargs):
resp = self.call_soap('getListeObjet')
soap_result = phpserialize_loads(resp.findall('.//listeObjet')[0].text)
result = []
for k, v in soap_result.items():
result.append({'id': k, 'text': force_str(v, 'utf-8')})
result.sort(key=lambda x: x['id'])
return result