daren_project/venv/Lib/site-packages/autobahn/xbr/templates/py-autobahn/service.py.jinja2

254 lines
9.7 KiB
Django/Jinja

{% if is_first_by_category %}
##
## service types (aka "APIs")
##
from uuid import UUID
from typing import List
import txaio
from txaio.interfaces import ILogger
from autobahn.wamp.types import PublishOptions, SubscribeOptions, EventDetails, CallOptions, CallDetails, RegisterOptions
from autobahn.wamp.request import Publication, Subscription, Registration
from autobahn.wamp.interfaces import ISession
from autobahn.xbr import IDelegate
Oid = UUID
Oids = List[UUID]
Void = type(None)
{% endif %}
class {{ metadata.classname }}(object):
"""
{{ metadata.docs }}
Interface UUID: ``{{ metadata.attrs.uuid }}``
"""
__slots__ = [
'log',
'_x_api_id',
'_x_prefix',
'_x_session',
'_x_delegate',
'_x_regs',
'_x_subs',
]
def __init__(self, prefix: str, log: Optional[ILogger]=None):
"""
:param prefix: The URI prefix under which this API will be instantiated under on the realm joined.
:param log: If provided, log to this logger, else create a new one internally.
"""
if log:
self.log = log
else:
import txaio
self.log = txaio.make_logger()
self._x_api_id = uuid.UUID('{{ metadata.attrs.uuid }}')
self._x_prefix = prefix
self._x_session = None
self._x_delegate = None
self._x_regs = None
self._x_subs = None
@property
def api(self) -> uuid.UUID:
"""
Interface UUID of this API (``{{ metadata.attrs.uuid }}``).
"""
return self._x_api_id
@property
def prefix(self) -> str:
"""
WAMP URI prefix under which this API is instantiated.
"""
return self._x_prefix
# WAMP PubSub part of the API:
{% for call_name in metadata.calls_by_id %}
{% if metadata.calls[call_name].attrs['type'] == 'topic' %}
async def publish_{{ call_name }}(self, evt: {{ repo.objs[metadata.calls[call_name].request.name].map('python', required=False, objtype_as_string=True) }}, options: Optional[PublishOptions] = None) -> Optional[Publication]:
"""
As an **interface provider**, publish event:
{{ metadata.calls[call_name].docs }}
:param evt: {{ repo.objs[metadata.calls[call_name].request.name].docs }}
:returns: When doing an acknowledged publish, the WAMP publication is returned.
"""
assert self._x_session and self._x_session.is_attached()
topic = '{}.{{ call_name }}'.format(self._x_prefix)
payload = evt.marshal()
if self._x_delegate:
key_id, enc_ser, ciphertext = await self._x_delegate.wrap(self._x_api_id, topic, payload)
if options.acknowledge:
pub = await self._x_session.publish(topic, key_id, enc_ser, ciphertext, options=options)
else:
self._x_session.publish(topic, key_id, enc_ser, ciphertext, options=options)
pub = None
else:
if options.acknowledge:
pub = await self._x_session.publish(topic, payload, options=options)
else:
self._x_session.publish(topic, payload, options=options)
pub = None
return pub
def receive_{{ call_name }}(self, evt: {{ repo.objs[metadata.calls[call_name].request.name].map('python', required=False, objtype_as_string=True) }}, details: Optional[EventDetails] = None):
"""
As an **interface consumer**, receive event:
{{ metadata.calls[call_name].docs }}
:param evt: {{ repo.objs[metadata.calls[call_name].request.name].docs }}
"""
raise NotImplementedError('event handler for "{{ call_name }}" not implemented')
{% endif %}
{% endfor %}
# WAMP RPC part of the API:
{% for call_name in metadata.calls_by_id %}
{% if metadata.calls[call_name].attrs['type'] == 'procedure' %}
async def call_{{ call_name }}(self, req: {{ repo.objs[metadata.calls[call_name].request.name].map('python', required=False, objtype_as_string=True) }}, options: Optional[CallOptions] = None) -> {{ repo.objs[metadata.calls[call_name].response.name].map('python', required=False, objtype_as_string=True) }}:
"""
As an **interface consumer**, call procedure:
{{ metadata.calls[call_name].docs }}
:param req: {{ repo.objs[metadata.calls[call_name].request.name].docs }}
:returns: {{ repo.objs[metadata.calls[call_name].response.name].docs }}
"""
assert self._x_session and self._x_session.is_attached()
procedure = '{}.{{ call_name }}'.format(self._x_prefix)
payload = req.marshal()
if self._x_delegate:
key_id, enc_ser, ciphertext = await self._x_delegate.wrap(self._x_api_id, procedure, payload)
result = await self._x_session.call(procedure, key_id, enc_ser, ciphertext, options=options)
else:
result = await self._x_session.call(procedure, payload, options=options)
return result
def invoke_{{ call_name }}(self, req: {{ repo.objs[metadata.calls[call_name].request.name].map('python', required=False, objtype_as_string=True) }}, details: Optional[CallDetails] = None) -> {{ repo.objs[metadata.calls[call_name].response.name].map('python', required=False, objtype_as_string=True) }}:
"""
As an **interface provider**, process call invocation:
{{ metadata.calls[call_name].docs }}
:param req: {{ repo.objs[metadata.calls[call_name].request.name].docs }}
:returns: {{ repo.objs[metadata.calls[call_name].response.name].docs }}
"""
raise NotImplementedError('call invocation handler for "{{ call_name }}" not implemented')
{% endif %}
{% endfor %}
@property
def session(self) -> Optional[ISession]:
"""
WAMP session this API is attached to.
"""
return self._x_session
@property
def delegate(self) -> Optional[IDelegate]:
"""
XBR (buyer/seller) delegate this API is attached to.
"""
return self._x_delegate
async def attach(self, session: ISession, delegate: Optional[IDelegate]):
"""
Attach this API instance with the given session and delegate, and under the given WAMP URI prefix.
:param session: WAMP session this API instance is attached to.
:param delegate: If using end-to-end data encryption, XBR ("buyer/seller") delegate used by this API instance.
"""
assert self._x_session is None and session.is_attached()
self._x_session = session
self._x_delegate = delegate
# WAMP PubSub part of the API:
subscriptions = []
{% for call_name in metadata.calls_by_id %}
{% if metadata.calls[call_name].attrs['type'] == 'topic' %}
if self._x_delegate:
async def do_receive_{{ call_name }}(key_id, enc_ser, ciphertext, details=None):
try:
payload = await self._x_delegate.unwrap(key_id, enc_ser, ciphertext)
obj = {{ repo.objs[metadata.calls[call_name].request.name].map('python') }}.parse(payload)
except:
self.log.failure()
else:
self.receive_{{ call_name }}(obj, details=details)
else:
def do_receive_{{ call_name }}(evt, details=None):
obj = {{ repo.objs[metadata.calls[call_name].request.name].map('python') }}.parse(evt)
self.receive_{{ call_name }}(obj, details=details)
topic = '{}.{{ call_name }}'.format(self._x_prefix)
sub = await self._x_session.subscribe(do_receive_{{ call_name }}, topic, options=SubscribeOptions(details=True))
subscriptions.append(sub)
{% endif %}
{% endfor %}
for sub in subscriptions:
self.log.info('Subscription {} created for "{}"'.format(sub.id, sub.topic))
self._x_subs = subscriptions
# WAMP RPC part of the API:
registrations = []
{% for call_name in metadata.calls_by_id %}
{% if metadata.calls[call_name].attrs['type'] == 'procedure' %}
if self._x_delegate:
async def do_invoke_{{ call_name }}(key_id, enc_ser, ciphertext, details=None):
try:
payload = await self._x_delegate.unwrap(key_id, enc_ser, ciphertext)
obj = {{ repo.objs[metadata.calls[call_name].request.name].map('python') }}.parse(payload)
except:
self.log.failure()
else:
self.invoke_{{ call_name }}(obj, details=details)
else:
def do_invoke_{{ call_name }}(req, details=None):
obj = {{ repo.objs[metadata.calls[call_name].request.name].map('python') }}.parse(req)
self.invoke_{{ call_name }}(obj, details=details)
procedure = '{}.{{ call_name }}'.format(self._x_prefix)
reg = await self._x_session.register(do_invoke_{{ call_name }}, procedure, options=RegisterOptions(details=True))
registrations.append(reg)
{% endif %}
{% endfor %}
for reg in registrations:
self.log.info('Registration {} created for "{}"'.format(reg.id, reg.procedure))
self._x_regs = registrations
def detach(self):
"""
Detach this API instance from the session and delegate.
"""
assert self._x_session is not None
dl = []
if self._x_session and self._x_session.is_attached():
for reg in self._x_regs:
dl.append(reg.unregister())
for sub in self._x_subs:
dl.append(sub.unsubscribe())
self._x_session = None
self._x_delegate = None
return txaio.gather(dl, consume_exceptions=True)