From 3334a7b8ca5b14d62a12203358b93ecb573ced48 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:41:21 +0000 Subject: [PATCH 1/5] Add counters for Topic and Subscriber --- CHANGELOG.rst | 6 ++++++ brewtils/models.py | 11 ++++++++--- brewtils/schemas.py | 3 +++ brewtils/test/fixtures.py | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 261d77d5..b849ae48 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Brewtils Changelog ================== +3.27.0 +------ +TBD + +- Expanded Topic/Subscriber models to include counters + 3.26.4 ------ 7/12/24 diff --git a/brewtils/models.py b/brewtils/models.py index fa594a49..0a9e152e 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1670,6 +1670,7 @@ def __init__( instance=None, command=None, subscriber_type=None, + consumer_count=None, ): self.garden = garden self.namespace = namespace @@ -1678,6 +1679,7 @@ def __init__( self.instance = instance self.command = command self.subscriber_type = subscriber_type or "DYNAMIC" + self.consumer_count = consumer_count or 0 def __str__(self): return "%s" % self.__dict__ @@ -1685,7 +1687,7 @@ def __str__(self): def __repr__(self): return ( "" + "command=%s, subscriber_type=%s, consumer_cont=%s>" % ( self.garden, self.namespace, @@ -1694,6 +1696,7 @@ def __repr__(self): self.instance, self.command, self.subscriber_type, + self.consumer_count ) ) @@ -1716,16 +1719,18 @@ def __eq__(self, other): class Topic(BaseModel): schema = "TopicSchema" - def __init__(self, id=None, name=None, subscribers=None): # noqa # shadows built-in + def __init__(self, id=None, name=None, subscribers=None, publisher_count=None): # noqa # shadows built-in self.id = id self.name = name self.subscribers = subscribers or [] + self.publisher_count = publisher_count or 0 def __str__(self): return "%s: %s" % (self.name, [str(s) for s in self.subscribers]) def __repr__(self): - return "" % ( + return "" % ( self.name, self.subscribers, + self.publisher_count ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 92427abd..ee348a7c 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -634,12 +634,15 @@ class SubscriberSchema(BaseSchema): instance = fields.Str(allow_none=True) command = fields.Str(allow_none=True) subscriber_type = fields.Str(allow_none=True) + subscriber_type = fields.Str(allow_none=True) + consumer_count = fields.Int(allow_none=True) class TopicSchema(BaseSchema): id = fields.Str(allow_none=True) name = fields.Str(allow_none=True) subscribers = fields.List(fields.Nested(SubscriberSchema, allow_none=True)) + publisher_count = fields.Int(allow_none=True) class UserSchema(BaseSchema): diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 3996cc8b..1d81d0a0 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -918,6 +918,7 @@ def subscriber_dict(): "instance": "inst", "command": "run", "subscriber_type": "DYNAMIC", + "consumer_count": 10 } @@ -929,7 +930,7 @@ def bg_subscriber(subscriber_dict): @pytest.fixture def topic_dict(subscriber_dict): """Topic as dict""" - return {"id": "5d174df1", "name": "foo", "subscribers": [subscriber_dict]} + return {"id": "5d174df1", "name": "foo", "subscribers": [subscriber_dict], "publisher_count": 10} @pytest.fixture From ab64bbe75bf1239ac24f0e4088fdb0bf454836b8 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:59:29 +0000 Subject: [PATCH 2/5] updating testing --- brewtils/models.py | 16 +++++++++------- brewtils/test/fixtures.py | 9 +++++++-- test/models_test.py | 7 +++++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 0a9e152e..1db6de28 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1670,7 +1670,7 @@ def __init__( instance=None, command=None, subscriber_type=None, - consumer_count=None, + consumer_count=0, ): self.garden = garden self.namespace = namespace @@ -1679,7 +1679,7 @@ def __init__( self.instance = instance self.command = command self.subscriber_type = subscriber_type or "DYNAMIC" - self.consumer_count = consumer_count or 0 + self.consumer_count = consumer_count def __str__(self): return "%s" % self.__dict__ @@ -1687,7 +1687,7 @@ def __str__(self): def __repr__(self): return ( "" + "command=%s, subscriber_type=%s, consumer_count=%s>" % ( self.garden, self.namespace, @@ -1696,7 +1696,7 @@ def __repr__(self): self.instance, self.command, self.subscriber_type, - self.consumer_count + self.consumer_count, ) ) @@ -1719,11 +1719,13 @@ def __eq__(self, other): class Topic(BaseModel): schema = "TopicSchema" - def __init__(self, id=None, name=None, subscribers=None, publisher_count=None): # noqa # shadows built-in + def __init__( + self, id=None, name=None, subscribers=None, publisher_count=0 + ): # noqa # shadows built-in self.id = id self.name = name self.subscribers = subscribers or [] - self.publisher_count = publisher_count or 0 + self.publisher_count = publisher_count def __str__(self): return "%s: %s" % (self.name, [str(s) for s in self.subscribers]) @@ -1732,5 +1734,5 @@ def __repr__(self): return "" % ( self.name, self.subscribers, - self.publisher_count + self.publisher_count, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 1d81d0a0..d1ff28ea 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -918,7 +918,7 @@ def subscriber_dict(): "instance": "inst", "command": "run", "subscriber_type": "DYNAMIC", - "consumer_count": 10 + "consumer_count": 10, } @@ -930,7 +930,12 @@ def bg_subscriber(subscriber_dict): @pytest.fixture def topic_dict(subscriber_dict): """Topic as dict""" - return {"id": "5d174df1", "name": "foo", "subscribers": [subscriber_dict], "publisher_count": 10} + return { + "id": "5d174df1", + "name": "foo", + "subscribers": [subscriber_dict], + "publisher_count": 10, + } @pytest.fixture diff --git a/test/models_test.py b/test/models_test.py index ec3f40dc..9cfd6e12 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -688,7 +688,7 @@ def subscriber1(): @pytest.fixture def topic1(subscriber1): - return Topic(name="foo.*", subscribers=[subscriber1]) + return Topic(name="foo.*", subscribers=[subscriber1], publisher_count=10) class TestSubscriber(object): @@ -709,7 +709,10 @@ def test_str(self, topic1, subscriber1): assert str(topic1) == "%s: %s" % (topic1.name, [str(subscriber1)]) def test_repr(self, topic1, subscriber1): - assert repr(topic1) == "" % ( + assert repr( + topic1 + ) == "" % ( topic1.name, [subscriber1], + topic1.publisher_count, ) From 741f50c70c7d127289f3101e1902ad7c77fd8b76 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:41:19 -0400 Subject: [PATCH 3/5] Expanding Topics API to support name --- CHANGELOG.rst | 6 +++++ brewtils/rest/client.py | 51 +++++++++++++++++++++++++----------- brewtils/rest/easy_client.py | 21 ++++++++------- test/rest/client_test.py | 28 +++++++++++++++----- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 261d77d5..73f3cfe9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Brewtils Changelog ================== +3.27.0 +------ +TBD + +- Expanded Topics Rest API to support Topic Name as a key + 3.26.4 ------ 7/12/24 diff --git a/brewtils/rest/client.py b/brewtils/rest/client.py index 39b530bc..44fbdf8e 100644 --- a/brewtils/rest/client.py +++ b/brewtils/rest/client.py @@ -154,6 +154,7 @@ def __init__(self, *args, **kwargs): self.admin_url = self.base_url + "api/v1/admin/" self.forward_url = self.base_url + "api/v1/forward" self.topic_url = self.base_url + "api/v1/topics/" + self.topic_name_url = self.base_url + "api/v1/topics/name/" # Deprecated self.logging_config_url = self.base_url + "api/v1/config/logging/" @@ -947,17 +948,23 @@ def get_tokens(self, username=None, password=None): return response @enable_auth - def get_topic(self, topic_id): - # type: (str, **Any) -> Response + def get_topic(self, topic_id=None, topic_name=None): + # type: (str, str, **Any) -> Response """Performs a GET on the Topic URL Args: - topic_id: Topic id + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name Returns: Requests Response object """ - return self.session.get(self.topic_url + topic_id) + if topic_id: + return self.session.get(self.topic_url + topic_id) + if topic_name: + return self.session.get(self.topic_name_url + topic_name) + + raise RuntimeError("Unable to find Topic ID or Topic Name to Get") @enable_auth def get_topics(self): @@ -985,30 +992,44 @@ def post_topics(self, payload): ) @enable_auth - def patch_topic(self, topic_id, payload): - # type: (str, str) -> Response + def patch_topic(self, topic_id=None, topic_name=None, operations=None): + # type: (str, str, str) -> Response """Performs a PATCH on a Topic URL Args: - topic_id: Topic id - payload: Serialized PatchOperation + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name + operations: Serialized PatchOperation Returns: Requests Response object """ - return self.session.patch( - self.topic_url + topic_id, data=payload, headers=self.JSON_HEADERS - ) + if topic_id: + return self.session.patch( + self.topic_url + topic_id, data=operations, headers=self.JSON_HEADERS + ) + if topic_name: + return self.session.patch( + self.topic_name_url + topic_name, data=operations, headers=self.JSON_HEADERS + ) + + raise RuntimeError("Unable to find Topic ID or Topic Name to Patch") @enable_auth - def delete_topic(self, topic_id): - # type: (str) -> Response + def delete_topic(self, topic_id=None, topic_name=None): + # type: (str, str) -> Response """Performs a DELETE on a Topic URL Args: - topic_id: Topic id + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name Returns: Requests Response object """ - return self.session.delete(self.topic_url + topic_id) + if topic_id: + return self.session.delete(self.topic_url + topic_id) + if topic_name: + return self.session.delete(self.topic_name_url + topic_name) + + raise RuntimeError("Unable to find Topic ID or Topic Name to Delete") diff --git a/brewtils/rest/easy_client.py b/brewtils/rest/easy_client.py index be5234d9..0a79fd9d 100644 --- a/brewtils/rest/easy_client.py +++ b/brewtils/rest/easy_client.py @@ -1159,17 +1159,18 @@ def _check_chunked_file_validity(self, file_id): return False, metadata_json @wrap_response(parse_method="parse_topic", parse_many=False, default_exc=FetchError) - def get_topic(self, topic_id): + def get_topic(self, topic_id=None, topic_name=None): """Get a topic Args: - topic_id: Topic id + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name Returns: The Topic """ - return self.client.get_topic(topic_id) + return self.client.get_topic(topic_id=topic_id, topic_name=topic_name) @wrap_response(parse_method="parse_topic", parse_many=True, default_exc=FetchError) def get_topics(self): @@ -1195,11 +1196,12 @@ def create_topic(self, topic): return self.client.post_topics(SchemaParser.serialize_topic(topic)) @wrap_response(return_boolean=True, raise_404=True) - def remove_topic(self, topic_id): + def remove_topic(self, topic_id=None, topic_name=None): """Remove a unique Topic Args: - topic_id: Topic id + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name Returns: bool: True if removal was successful @@ -1208,14 +1210,15 @@ def remove_topic(self, topic_id): NotFoundError: Couldn't find a Topic matching given parameters """ - return self.client.delete_topic(topic_id) + return self.client.delete_topic(topic_id=topic_id, topic_name=topic_name) @wrap_response(parse_method="parse_topic", parse_many=False, default_exc=SaveError) - def update_topic(self, topic_id, add=None, remove=None): + def update_topic(self, topic_id=None, topic_name=None, add=None, remove=None): """Update a Topic Args: - topic_id (str): The Topic ID + topic_id (Optional[str]): Topic id + topic_name (Optional[str]): Topic name add (Optional[str]): Add subscriber remove (Optional[str]): Remove subscriber @@ -1231,5 +1234,5 @@ def update_topic(self, topic_id, add=None, remove=None): operations.append(PatchOperation("remove", value=remove)) return self.client.patch_topic( - topic_id, SchemaParser.serialize_patch(operations, many=True) + topic_id=topic_id, topic_name=topic_name, operations=SchemaParser.serialize_patch(operations, many=True) ) diff --git a/test/rest/client_test.py b/test/rest/client_test.py index 57e267dc..25befa44 100644 --- a/test/rest/client_test.py +++ b/test/rest/client_test.py @@ -473,10 +473,14 @@ def test_client_cert_without_username_password(self, monkeypatch): assert get_tokens_mock.called is True - def test_get_topic(self, client, session_mock): - client.get_topic("id") + def test_get_topic_id(self, client, session_mock): + client.get_topic(topic_id="id") session_mock.get.assert_called_with(client.topic_url + "id") + def test_get_topic_name(self, client, session_mock): + client.get_topic(topic_name="topic_name") + session_mock.get.assert_called_with(client.topic_name_url + "topic_name") + def test_get_topics(self, client, session_mock): client.get_topics() session_mock.get_assert_called_with(client.topic_url, params={}) @@ -487,14 +491,26 @@ def test_post_topics(self, client, session_mock): client.topic_url, data="payload", headers=client.JSON_HEADERS ) - def test_patch_topic(self, client, session_mock): - client.patch_topic("id", "payload") + def test_patch_topic_id(self, client, session_mock): + client.patch_topic(topic_id="id", operations="payload") session_mock.patch.assert_called_with( client.topic_url + "id", data="payload", headers=client.JSON_HEADERS, ) - def test_delete_topic(self, client, session_mock): - client.delete_topic("id") + def test_patch_topic_name(self, client, session_mock): + client.patch_topic(topic_name="topic_name", operations="payload") + session_mock.patch.assert_called_with( + client.topic_name_url + "topic_name", + data="payload", + headers=client.JSON_HEADERS, + ) + + def test_delete_topic_id(self, client, session_mock): + client.delete_topic(topic_id="id") session_mock.delete.assert_called_with(client.topic_url + "id") + + def test_delete_topic_name(self, client, session_mock): + client.delete_topic(topic_name="topic_name") + session_mock.delete.assert_called_with(client.topic_name_url + "topic_name") From 396187d65cf2cbad02db3785868a746930493b7d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:46:07 -0400 Subject: [PATCH 4/5] formatting --- brewtils/rest/client.py | 10 ++++++---- brewtils/rest/easy_client.py | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/brewtils/rest/client.py b/brewtils/rest/client.py index 6de4f709..8f45eac4 100644 --- a/brewtils/rest/client.py +++ b/brewtils/rest/client.py @@ -970,7 +970,7 @@ def get_topic(self, topic_id=None, topic_name=None): return self.session.get(self.topic_url + topic_id) if topic_name: return self.session.get(self.topic_name_url + topic_name) - + raise RuntimeError("Unable to find Topic ID or Topic Name to Get") @enable_auth @@ -1017,9 +1017,11 @@ def patch_topic(self, topic_id=None, topic_name=None, operations=None): ) if topic_name: return self.session.patch( - self.topic_name_url + topic_name, data=operations, headers=self.JSON_HEADERS + self.topic_name_url + topic_name, + data=operations, + headers=self.JSON_HEADERS, ) - + raise RuntimeError("Unable to find Topic ID or Topic Name to Patch") @enable_auth @@ -1038,5 +1040,5 @@ def delete_topic(self, topic_id=None, topic_name=None): return self.session.delete(self.topic_url + topic_id) if topic_name: return self.session.delete(self.topic_name_url + topic_name) - + raise RuntimeError("Unable to find Topic ID or Topic Name to Delete") diff --git a/brewtils/rest/easy_client.py b/brewtils/rest/easy_client.py index 5392d2c2..9c2c5f86 100644 --- a/brewtils/rest/easy_client.py +++ b/brewtils/rest/easy_client.py @@ -1232,5 +1232,7 @@ def update_topic(self, topic_id=None, topic_name=None, add=None, remove=None): operations.append(PatchOperation("remove", value=remove)) return self.client.patch_topic( - topic_id=topic_id, topic_name=topic_name, operations=SchemaParser.serialize_patch(operations, many=True) + topic_id=topic_id, + topic_name=topic_name, + operations=SchemaParser.serialize_patch(operations, many=True), ) From faed7e92520c8348ae4a27105da6b69116a7f11c Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 9 Aug 2024 06:27:35 -0400 Subject: [PATCH 5/5] Remove Duplicates --- brewtils/schemas.py | 1 - 1 file changed, 1 deletion(-) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 0b59cf20..913435f8 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -601,7 +601,6 @@ class SubscriberSchema(BaseSchema): instance = fields.Str(allow_none=True) command = fields.Str(allow_none=True) subscriber_type = fields.Str(allow_none=True) - subscriber_type = fields.Str(allow_none=True) consumer_count = fields.Int(allow_none=True)