Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Release History
===============

dev -
------------------

* Updated requirements files to have current dependencies
* Added global option to switch off error on duplicate
* Limited duplicate detection to successful previous transmissions only
* Giving random name to duplicate messages


1.2.0 - 2020-04-12
------------------

Expand Down
5 changes: 5 additions & 0 deletions docs/detailed-guide/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ The available settings along with their usage is described below:
| MAX_ARCH_DAYS | 30 | Number of days files and messages are kept in |
| | | storage. |
+------------------------+----------------------------+------------------------------------------------+
| ERROR_ON_DUPLICATE | True | When set to true, duplicate messages are |
| | | handled as error with negative MDN. |
| | | When set to false, duplicates are handled as |
| | | successful transmissions. |
+------------------------+----------------------------+------------------------------------------------+


The Data Directory
Expand Down
3 changes: 3 additions & 0 deletions pyas2/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@

# Max number of days worth of messages to be saved in archive
MAX_ARCH_DAYS = APP_SETTINGS.get("MAX_ARCH_DAYS", 30)

# Send positive MDN when duplicate message is received
ERROR_ON_DUPLICATE = APP_SETTINGS.get("ERROR_ON_DUPLICATE", True)
81 changes: 80 additions & 1 deletion pyas2/tests/test_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,89 @@ def test_duplicate_error(self, mock_request):
# Make sure out message was created
self.assertEqual(in_message.status, "E")
out_message = Message.objects.get(
message_id=in_message.message_id + "_duplicate", direction="IN"
message_id__startswith=in_message.message_id + "_duplicate_", direction="IN"
)
self.assertEqual(out_message.status, "E")

# send it a third time to cause another duplicate error
in_message.send_message(as2message.headers, as2message.content)

# Make sure out message was created
self.assertEqual(in_message.status, "E")
out_messages = Message.objects.filter(
message_id__startswith=in_message.message_id + "_duplicate_", direction="IN"
)
for out_message in out_messages:
self.assertEqual(out_message.status, "E")

@mock.patch("requests.post")
def test_duplicate_success(self, mock_request):
with override_settings(PYAS2={"ERROR_ON_DUPLICATE": False}):
importlib.reload(settings)
partner = Partner.objects.create(
name="AS2 Server",
as2_name="as2server",
target_url="http://localhost:8080/pyas2/as2receive",
signature="sha1",
signature_cert=self.server_crt,
encryption="tripledes_192_cbc",
encryption_cert=self.server_crt,
mdn=True,
mdn_mode="SYNC",
mdn_sign="sha1",
)

# Send the message once
as2message = As2Message(
sender=self.organization.as2org, receiver=partner.as2partner
)
as2message.build(
self.payload,
filename="testmessage.edi",
subject=partner.subject,
content_type=partner.content_type,
)
in_message, _ = Message.objects.create_from_as2message(
as2message=as2message, payload=self.payload, direction="OUT", status="P"
)

mock_request.side_effect = SendMessageMock(self.client)
in_message.send_message(as2message.headers, as2message.content)

# Check the status of the message
self.assertEqual(in_message.status, "S")
out_message = Message.objects.get(
message_id=in_message.message_id, direction="IN"
)
self.assertEqual(out_message.status, "S")

# send it again to, should not cause duplicate error
in_message.send_message(as2message.headers, as2message.content)

# Make sure out message was created
self.assertEqual(in_message.status, "S")
out_message = Message.objects.get(
message_id__startswith=in_message.message_id + "_duplicate_",
direction="IN",
)
self.assertEqual(out_message.status, "S")

# send it again to, should not cause duplicate error, and no create error
in_message.send_message(as2message.headers, as2message.content)

# Make sure out message was created
self.assertEqual(in_message.status, "S")
out_messages = Message.objects.filter(
message_id__startswith=in_message.message_id + "_duplicate_",
direction="IN",
)
for out_message in out_messages:
self.assertEqual(out_message.status, "S")

with override_settings(PYAS2={"ERROR_ON_DUPLICATE": True}):
importlib.reload(settings)
self.assertEqual(settings.ERROR_ON_DUPLICATE, True)

def test_org_missing_error(self):
# Create the client partner and send the command
partner = Partner.objects.create(
Expand Down
28 changes: 24 additions & 4 deletions pyas2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import FormView
from django.utils.crypto import get_random_string
from pyas2lib import Message as As2Message
from pyas2lib import Mdn as As2Mdn
from pyas2lib.exceptions import DuplicateDocument
Expand All @@ -25,6 +26,7 @@
from pyas2.utils import run_post_receive
from pyas2.utils import run_post_send
from pyas2.forms import SendAs2MessageForm
from pyas2 import settings

logger = logging.getLogger("pyas2")

Expand All @@ -47,7 +49,19 @@ def find_message(message_id, partner_id):
return message.as2message

@staticmethod
def check_message_exists(message_id, partner_id):
def check_success_message_exists(message_id, partner_id):
""" Check if the message already exists in the system """
if settings.ERROR_ON_DUPLICATE:
return Message.objects.filter(
message_id=message_id,
partner_id=partner_id.strip(),
status__in=("S", "P"),
).exists()
else:
return False

@staticmethod
def check_same_message_exists(message_id, partner_id):
""" Check if the message already exists in the system """
return Message.objects.filter(
message_id=message_id, partner_id=partner_id.strip()
Expand Down Expand Up @@ -125,7 +139,7 @@ def post(self, request, *args, **kwargs):
request_body,
self.find_organization,
self.find_partner,
self.check_message_exists,
self.check_success_message_exists,
)

logger.info(
Expand All @@ -135,8 +149,14 @@ def post(self, request, *args, **kwargs):
)

# In case of duplicates update message id
if isinstance(exception[0], DuplicateDocument):
as2message.message_id += "_duplicate"
if isinstance(exception[0], DuplicateDocument) or (
not settings.ERROR_ON_DUPLICATE
and self.check_same_message_exists(
message_id=as2message.message_id,
partner_id=as2message.sender.as2_name,
)
):
as2message.message_id += "_duplicate_" + get_random_string(5)

# Create the Message and MDN objects
message, full_fn = Message.objects.create_from_as2message(
Expand Down