Skip to content

Commit

Permalink
Merge pull request #61 from juntossomosmais/feature/enable_disable_ex…
Browse files Browse the repository at this point in the history
…clusive_queue

feat: add possibility to enable/disable exclusive queue feature
  • Loading branch information
lucasssouza1 authored Aug 18, 2023
2 parents c486cda + 8487edd commit 7362f19
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [5.3.0] - 2023-08-07

### Changed

- Adds a new setting option (STOMP_DEFAULT_EXCLUSIVE_QUEUE) that allows the app to create a queue in RabbitMQ as exclusive or not.

## [5.2.0] - 2023-01-16

### Changed
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ defined, this parameter **must** be an integer!

  Optional parameter that controls how many seconds django-stomp will wait for a message to be processed. Defaults to 30 seconds. If defined, this parameter **must** be an integer!

***STOMP_DEFAULT_EXCLUSIVE_QUEUE***

Optional parameter that defines if the default value of `exclusive` queue parameter will be True or False when creating a queue in RabbitMQ. The default value is `False`.

## Tests

In order to execute tests for ActiveMQ, execute the following:
Expand Down
3 changes: 3 additions & 0 deletions django_stomp/services/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from django_stomp.helpers import only_destination_name
from django_stomp.helpers import set_ssl_connection
from django_stomp.settings import DEFAULT_STOMP_SSL_VERSION
from django_stomp.settings import STOMP_DEFAULT_EXCLUSIVE_QUEUE
from django_stomp.settings import STOMP_PROCESS_MSG_WORKERS
from django_stomp.settings import STOMP_USE_SSL

Expand Down Expand Up @@ -207,6 +208,8 @@ def build_listener(
# These two parameters must be set on producer side as well, otherwise you'll get precondition_failed
"x-dead-letter-routing-key": create_dlq_destination_from_another_destination(destination_name),
"x-dead-letter-exchange": "",
# This parameter below defines if the queue to be created will be exclusive or not.
"exclusive": STOMP_DEFAULT_EXCLUSIVE_QUEUE,
}

if durable_topic_subscription is True:
Expand Down
1 change: 1 addition & 0 deletions django_stomp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ def eval_settings_otherwise_raise_exception(

STOMP_USE_SSL = eval_str_as_boolean(getattr(django_settings, "STOMP_USE_SSL", "False"))
DEFAULT_STOMP_SSL_VERSION = getattr(django_settings, "DEFAULT_STOMP_SSL_VERSION", ssl.PROTOCOL_TLS_CLIENT)
STOMP_DEFAULT_EXCLUSIVE_QUEUE = eval_str_as_boolean(getattr(django_settings, "STOMP_DEFAULT_EXCLUSIVE_QUEUE", "False"))
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ env =
D:STOMP_CORRELATION_ID_REQUIRED=True
D:STOMP_PROCESS_MSG_ON_BACKGROUND=False
D:STOMP_OUTGOING_HEARTBEAT=0
D:STOMP_INCOMING_HEARTBEAT=0
D:STOMP_INCOMING_HEARTBEAT=0
D:STOMP_DEFAULT_EXCLUSIVE_QUEUE=False
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name="django-stomp",
version="5.2.0",
version="5.3.0",
description="A simple implementation of STOMP with Django",
long_description=README,
long_description_content_type="text/markdown",
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def pytest_configure():
STOMP_OUTGOING_HEARTBEAT=os.getenv("STOMP_OUTGOING_HEARTBEAT"),
STOMP_INCOMING_HEARTBEAT=os.getenv("STOMP_INCOMING_HEARTBEAT"),
STOMP_SERVER_VHOST=os.getenv("STOMP_SERVER_VHOST"),
STOMP_DEFAULT_EXCLUSIVE_QUEUE=os.getenv("STOMP_DEFAULT_EXCLUSIVE_QUEUE"),
DATABASES={
"default": {
"ENGINE": os.getenv("DB_ENGINE", "django.db.backends.sqlite3"),
Expand Down
26 changes: 26 additions & 0 deletions tests/integration/test_execution/test_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,3 +959,29 @@ def test_shouldnt_open_a_new_db_connection_when_there_is_one_still_usable(settin
assert all(t.db.connection is not None for t in threads_with_db_connections)

listener.close()


@pytest.mark.parametrize("setting_value,expected", [(True, True), (False, False)])
def test_should_create_a_exclusive_queue_by_publisher_at_rabbitmq(mocker, setting_value, expected):
# Arrange - creates a publisher with the exclusive setting header that should create a new queue exclusive or not
mocker.patch("django_stomp.services.consumer.STOMP_DEFAULT_EXCLUSIVE_QUEUE", setting_value)
queue_name = f"my-test-exclusive-destination-{uuid.uuid4()}"
destination_three = f"/queue/{queue_name}"
start_processing(
destination_three,
callback_move_and_ack_path,
is_testing=True,
return_listener=True,
)
try:
queue_status = current_queue_configuration(queue_name)
assert queue_status.number_of_pending_messages == 0
assert queue_status.number_of_consumers == 1
assert queue_status.messages_enqueued == 0
assert queue_status.messages_dequeued == 0
assert (
queue_status.is_exclusive_destination_queue == None
), "Header parameter 'exclusive' cannot change queue behavior in ActiveMQ"
except Exception:
queue_status = rabbitmq.current_queue_configuration(queue_name)
assert queue_status.is_exclusive_destination_queue == expected, "The queue must be exclusive or not"
1 change: 1 addition & 0 deletions tests/support/dtos.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class CurrentDestinationStatus:
number_of_consumers: int
messages_enqueued: int
messages_dequeued: int
is_exclusive_destination_queue: Optional[bool] = None


@dataclass(frozen=True)
Expand Down
7 changes: 6 additions & 1 deletion tests/support/rabbitmq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@ def current_queue_configuration(queue_name, host="localhost", port=15672) -> Opt

number_of_pending_messages = result["messages"]
number_of_consumers = result["consumers"]
is_exclusive_destination_queue = result.get("exclusive", False)

return CurrentDestinationStatus(
number_of_pending_messages, number_of_consumers, messages_enqueued, messages_dequeued
number_of_pending_messages,
number_of_consumers,
messages_enqueued,
messages_dequeued,
is_exclusive_destination_queue,
)


Expand Down
40 changes: 40 additions & 0 deletions tests/unit/services/test_consumer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
from uuid import uuid4

import pytest

from django_stomp import builder
from django_stomp.services.consumer import StompFrame
from django_stomp.services.consumer import build_listener
Expand Down Expand Up @@ -79,3 +81,41 @@ def test_should_have_only_one_django_stomp_listener_even_if_set_listener_is_call

django_stomp_listener_id = django_stomp_listener._listener_id
assert django_stomp_listener._connection.get_listener(django_stomp_listener_id) is not None


def test_listener_connection_configuration_must_have_headers_properly_configured():
# Arrange - defines the expected headers setup
headers_setup_mapping = {
"client-id": str,
"activemq.prefetchSize": str,
"prefetch-count": str,
"x-dead-letter-routing-key": str,
"x-dead-letter-exchange": str,
"exclusive": bool,
}

# Arrange - build listener to some arbirtrary queue and get its connection configuration headers
django_stomp_listener = builder.build_listener(f"some-destination-{uuid4()}")
created_header_setup = django_stomp_listener._connection_configuration["headers"]

assert headers_setup_mapping.keys() == created_header_setup.keys(), "headers do not have all required keys"

for key, value in headers_setup_mapping.items():
assert isinstance(created_header_setup[key], value), "header has wrong type"

assert int(created_header_setup["prefetch-count"]), "prefetch-count must be a number"


@pytest.mark.parametrize("setting_value,expected", [(True, True), (False, False)])
def test_should_have_a_listener_with_param_exclusive_in_connection_configuration_headers(
mocker, setting_value, expected
):
# Arrange - mock the stomp default exclusive queue setting
mocker.patch("django_stomp.services.consumer.STOMP_DEFAULT_EXCLUSIVE_QUEUE", setting_value)

# Arrange - build listener to some arbirtrary queue
django_stomp_listener = builder.build_listener(f"some-destination-{uuid4()}")

assert (
django_stomp_listener._connection_configuration["headers"]["exclusive"] is expected
), "The stomp default exclusive queue parameter was not configured correctly."

0 comments on commit 7362f19

Please sign in to comment.