Fix publication exception handling for notification. (#105)
This commit is contained in:
parent
1efa191771
commit
f8bbd1df41
|
@ -114,7 +114,7 @@ class AbstractPlatform(ABC, LoggerMixin, ConfLoaderMixin):
|
||||||
|
|
||||||
class AbstractEventFormatter(LoggerMixin, ConfLoaderMixin):
|
class AbstractEventFormatter(LoggerMixin, ConfLoaderMixin):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def validate_event(self, message) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
"""
|
"""
|
||||||
Validates publisher's event.
|
Validates publisher's event.
|
||||||
Should raise ``PublisherError`` (or one of its subclasses) if event
|
Should raise ``PublisherError`` (or one of its subclasses) if event
|
||||||
|
@ -122,6 +122,19 @@ class AbstractEventFormatter(LoggerMixin, ConfLoaderMixin):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: no cover
|
raise NotImplementedError # pragma: no cover
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _validate_message(self, message: str) -> None:
|
||||||
|
"""
|
||||||
|
Validates notifier's message.
|
||||||
|
Should raise ``PublisherError`` (or one of its subclasses) if message
|
||||||
|
is not valid.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError # pragma: no cover
|
||||||
|
|
||||||
|
def validate_event(self, event: MobilizonEvent) -> None:
|
||||||
|
self._validate_event(event)
|
||||||
|
self._validate_message(self.get_message_from_event(event))
|
||||||
|
|
||||||
def _preprocess_event(self, event):
|
def _preprocess_event(self, event):
|
||||||
"""
|
"""
|
||||||
Allows publishers to preprocess events before feeding them to the template
|
Allows publishers to preprocess events before feeding them to the template
|
||||||
|
@ -142,15 +155,6 @@ class AbstractEventFormatter(LoggerMixin, ConfLoaderMixin):
|
||||||
template_path = self.conf.msg_template_path or self.default_template_path
|
template_path = self.conf.msg_template_path or self.default_template_path
|
||||||
return JINJA_ENV.get_template(template_path)
|
return JINJA_ENV.get_template(template_path)
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def validate_message(self, message: str) -> None:
|
|
||||||
"""
|
|
||||||
Validates notifier's message.
|
|
||||||
Should raise ``PublisherError`` (or one of its subclasses) if message
|
|
||||||
is not valid.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError # pragma: no cover
|
|
||||||
|
|
||||||
def get_recap_header(self):
|
def get_recap_header(self):
|
||||||
template_path = (
|
template_path = (
|
||||||
self.conf.recap_header_template_path
|
self.conf.recap_header_template_path
|
||||||
|
|
|
@ -113,7 +113,6 @@ class PublisherCoordinator:
|
||||||
f(*args, **kwargs)
|
f(*args, **kwargs)
|
||||||
return reasons
|
return reasons
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(str(e))
|
|
||||||
return reasons + [str(e)]
|
return reasons + [str(e)]
|
||||||
|
|
||||||
def _validate(self):
|
def _validate(self):
|
||||||
|
@ -122,16 +121,12 @@ class PublisherCoordinator:
|
||||||
for publication in self.publications:
|
for publication in self.publications:
|
||||||
reasons = []
|
reasons = []
|
||||||
reasons = self._safe_run(
|
reasons = self._safe_run(
|
||||||
reasons, publication.publisher.validate_credentials,
|
reasons,
|
||||||
|
publication.publisher.validate_credentials,
|
||||||
)
|
)
|
||||||
reasons = self._safe_run(
|
reasons = self._safe_run(
|
||||||
reasons, publication.formatter.validate_event, publication.event
|
reasons, publication.formatter.validate_event, publication.event
|
||||||
)
|
)
|
||||||
reasons = self._safe_run(
|
|
||||||
reasons,
|
|
||||||
publication.formatter.validate_message,
|
|
||||||
publication.formatter.get_message_from_event(publication.event),
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(reasons) > 0:
|
if len(reasons) > 0:
|
||||||
errors.append(
|
errors.append(
|
||||||
|
|
|
@ -29,12 +29,12 @@ class FacebookFormatter(AbstractEventFormatter):
|
||||||
"mobilizon_reshare.publishers.templates", "facebook_recap_header.tmpl.j2"
|
"mobilizon_reshare.publishers.templates", "facebook_recap_header.tmpl.j2"
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_event(self, event: MobilizonEvent) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
text = event.description
|
text = event.description
|
||||||
if not (text and text.strip()):
|
if not (text and text.strip()):
|
||||||
self._log_error("No text was found", raise_error=InvalidEvent)
|
self._log_error("No text was found", raise_error=InvalidEvent)
|
||||||
|
|
||||||
def validate_message(self, message) -> None:
|
def _validate_message(self, message) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,14 +34,14 @@ class MastodonFormatter(AbstractEventFormatter):
|
||||||
"mobilizon_reshare.publishers.templates", "mastodon_recap_header.tmpl.j2"
|
"mobilizon_reshare.publishers.templates", "mastodon_recap_header.tmpl.j2"
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_event(self, event: MobilizonEvent) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
text = event.description
|
text = event.description
|
||||||
if not (text and text.strip()):
|
if not (text and text.strip()):
|
||||||
self._log_error("No text was found", raise_error=InvalidEvent)
|
self._log_error("No text was found", raise_error=InvalidEvent)
|
||||||
|
|
||||||
def validate_message(self, message) -> None:
|
def _validate_message(self, message) -> None:
|
||||||
if len(message.encode("utf-8")) >= self.conf.toot_length:
|
if len(message.encode("utf-8")) >= self.conf.toot_length:
|
||||||
raise InvalidMessage("Message is too long")
|
self._log_error("Message is too long", raise_error=InvalidMessage)
|
||||||
|
|
||||||
|
|
||||||
class MastodonPlatform(AbstractPlatform):
|
class MastodonPlatform(AbstractPlatform):
|
||||||
|
|
|
@ -65,14 +65,14 @@ class TelegramFormatter(AbstractEventFormatter):
|
||||||
|
|
||||||
return TelegramFormatter.restore_links(message)
|
return TelegramFormatter.restore_links(message)
|
||||||
|
|
||||||
def validate_event(self, event: MobilizonEvent) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
description = event.description
|
description = event.description
|
||||||
if not (description and description.strip()):
|
if not (description and description.strip()):
|
||||||
self._log_error("No description was found", raise_error=InvalidEvent)
|
self._log_error("No description was found", raise_error=InvalidEvent)
|
||||||
|
|
||||||
def validate_message(self, message: str) -> None:
|
def _validate_message(self, message: str) -> None:
|
||||||
if len(message) >= 4096:
|
if len(message) >= 4096:
|
||||||
raise InvalidMessage("Message is too long")
|
self._log_error("Message is too long", raise_error=InvalidMessage)
|
||||||
|
|
||||||
def _preprocess_event(self, event: MobilizonEvent):
|
def _preprocess_event(self, event: MobilizonEvent):
|
||||||
event.description = html_to_markdown(event.description)
|
event.description = html_to_markdown(event.description)
|
||||||
|
|
|
@ -13,6 +13,7 @@ from mobilizon_reshare.publishers.exceptions import (
|
||||||
InvalidCredentials,
|
InvalidCredentials,
|
||||||
InvalidEvent,
|
InvalidEvent,
|
||||||
PublisherError,
|
PublisherError,
|
||||||
|
InvalidMessage,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,16 +32,16 @@ class TwitterFormatter(AbstractEventFormatter):
|
||||||
"mobilizon_reshare.publishers.templates", "twitter_recap_header.tmpl.j2"
|
"mobilizon_reshare.publishers.templates", "twitter_recap_header.tmpl.j2"
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_event(self, event: MobilizonEvent) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
text = event.description
|
text = event.description
|
||||||
if not (text and text.strip()):
|
if not (text and text.strip()):
|
||||||
self._log_error("No text was found", raise_error=InvalidEvent)
|
self._log_error("No text was found", raise_error=InvalidEvent)
|
||||||
|
|
||||||
def validate_message(self, message) -> None:
|
def _validate_message(self, message) -> None:
|
||||||
# TODO this is not precise. It should count the characters according to Twitter's logic but
|
# TODO this is not precise. It should count the characters according to Twitter's logic but
|
||||||
# Tweepy doesn't seem to support the validation client side
|
# Tweepy doesn't seem to support the validation client side
|
||||||
if len(message.encode("utf-8")) > 280:
|
if len(message.encode("utf-8")) > 280:
|
||||||
raise PublisherError("Message is too long")
|
self._log_error("Message is too long", raise_error=InvalidMessage)
|
||||||
|
|
||||||
|
|
||||||
class TwitterPlatform(AbstractPlatform):
|
class TwitterPlatform(AbstractPlatform):
|
||||||
|
@ -65,7 +66,7 @@ class TwitterPlatform(AbstractPlatform):
|
||||||
try:
|
try:
|
||||||
return self._get_api().update_status(message)
|
return self._get_api().update_status(message)
|
||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
raise PublisherError(e.args[0])
|
self._log_error(e.args[0], raise_error=PublisherError)
|
||||||
|
|
||||||
def validate_credentials(self):
|
def validate_credentials(self):
|
||||||
if not self._get_api().verify_credentials():
|
if not self._get_api().verify_credentials():
|
||||||
|
|
|
@ -36,14 +36,14 @@ class ZulipFormatter(AbstractEventFormatter):
|
||||||
"mobilizon_reshare.publishers.templates", "zulip_recap_header.tmpl.j2"
|
"mobilizon_reshare.publishers.templates", "zulip_recap_header.tmpl.j2"
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_event(self, event: MobilizonEvent) -> None:
|
def _validate_event(self, event: MobilizonEvent) -> None:
|
||||||
text = event.description
|
text = event.description
|
||||||
if not (text and text.strip()):
|
if not (text and text.strip()):
|
||||||
self._log_error("No text was found", raise_error=InvalidEvent)
|
self._log_error("No text was found", raise_error=InvalidEvent)
|
||||||
|
|
||||||
def validate_message(self, message) -> None:
|
def _validate_message(self, message: str) -> None:
|
||||||
if len(message.encode("utf-8")) >= 10000:
|
if len(message.encode("utf-8")) >= 10000:
|
||||||
raise InvalidMessage("Message is too long")
|
self._log_error("Message is too long", raise_error=InvalidMessage)
|
||||||
|
|
||||||
def _preprocess_event(self, event: MobilizonEvent):
|
def _preprocess_event(self, event: MobilizonEvent):
|
||||||
event.description = html_to_markdown(event.description)
|
event.description = html_to_markdown(event.description)
|
||||||
|
|
|
@ -103,8 +103,12 @@ async def mock_publications(
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_publications", [2])
|
@pytest.mark.parametrize("num_publications", [2])
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_publication_coordinator_run_success(mock_publications,):
|
async def test_publication_coordinator_run_success(
|
||||||
coordinator = PublisherCoordinator(publications=mock_publications,)
|
mock_publications,
|
||||||
|
):
|
||||||
|
coordinator = PublisherCoordinator(
|
||||||
|
publications=mock_publications,
|
||||||
|
)
|
||||||
report = coordinator.run()
|
report = coordinator.run()
|
||||||
assert len(report.reports) == 2
|
assert len(report.reports) == 2
|
||||||
assert report.successful, "\n".join(map(lambda rep: rep.reason, report.reports))
|
assert report.successful, "\n".join(map(lambda rep: rep.reason, report.reports))
|
||||||
|
@ -123,10 +127,7 @@ async def test_publication_coordinator_run_failure(
|
||||||
report = coordinator.run()
|
report = coordinator.run()
|
||||||
assert len(report.reports) == 1
|
assert len(report.reports) == 1
|
||||||
assert not report.successful
|
assert not report.successful
|
||||||
assert (
|
assert list(report.reports)[0].reason == "credentials error, Invalid event error"
|
||||||
list(report.reports)[0].reason
|
|
||||||
== "credentials error, Invalid event error, Invalid message error"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_publications", [1])
|
@pytest.mark.parametrize("num_publications", [1])
|
||||||
|
|
|
@ -16,16 +16,14 @@ from mobilizon_reshare.publishers.platforms.mastodon import (
|
||||||
def test_message_length_success(event):
|
def test_message_length_success(event):
|
||||||
message = "a" * 200
|
message = "a" * 200
|
||||||
event.name = message
|
event.name = message
|
||||||
message = MastodonFormatter().get_message_from_event(event)
|
MastodonFormatter().validate_event(event)
|
||||||
MastodonFormatter().validate_message(message)
|
|
||||||
|
|
||||||
|
|
||||||
def test_message_length_failure(event):
|
def test_message_length_failure(event):
|
||||||
message = "a" * 500
|
message = "a" * 500
|
||||||
event.name = message
|
event.name = message
|
||||||
message = MastodonFormatter().get_message_from_event(event)
|
|
||||||
with pytest.raises(InvalidMessage):
|
with pytest.raises(InvalidMessage):
|
||||||
MastodonFormatter().validate_message(message)
|
MastodonFormatter().validate_event(event)
|
||||||
|
|
||||||
|
|
||||||
def test_event_validation(event):
|
def test_event_validation(event):
|
||||||
|
|
|
@ -15,12 +15,7 @@ from mobilizon_reshare.publishers.platforms.telegram import (
|
||||||
def test_message_length_success(event):
|
def test_message_length_success(event):
|
||||||
message = "a" * 500
|
message = "a" * 500
|
||||||
event.description = message
|
event.description = message
|
||||||
assert (
|
assert TelegramFormatter().validate_event(event) is None
|
||||||
TelegramFormatter().validate_message(
|
|
||||||
TelegramFormatter().get_message_from_event(event)
|
|
||||||
)
|
|
||||||
is None
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_message_length_failure(event):
|
def test_message_length_failure(event):
|
||||||
|
@ -28,9 +23,7 @@ def test_message_length_failure(event):
|
||||||
event.description = message
|
event.description = message
|
||||||
|
|
||||||
with pytest.raises(InvalidMessage):
|
with pytest.raises(InvalidMessage):
|
||||||
TelegramFormatter().validate_message(
|
TelegramFormatter().validate_event(event)
|
||||||
TelegramFormatter().get_message_from_event(event)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -141,21 +141,14 @@ def test_event_validation(event):
|
||||||
def test_message_length_success(event):
|
def test_message_length_success(event):
|
||||||
message = "a" * 500
|
message = "a" * 500
|
||||||
event.description = message
|
event.description = message
|
||||||
assert (
|
assert ZulipFormatter().validate_event(event) is None
|
||||||
ZulipFormatter().validate_message(
|
|
||||||
ZulipFormatter().get_message_from_event(event)
|
|
||||||
)
|
|
||||||
is None
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_message_length_failure(event):
|
def test_message_length_failure(event):
|
||||||
message = "a" * 10000
|
message = "a" * 10000
|
||||||
event.description = message
|
event.description = message
|
||||||
with pytest.raises(InvalidMessage):
|
with pytest.raises(InvalidMessage):
|
||||||
ZulipFormatter().validate_message(
|
ZulipFormatter().validate_event(event)
|
||||||
ZulipFormatter().get_message_from_event(event)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_response():
|
def test_validate_response():
|
||||||
|
|
Loading…
Reference in New Issue