Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topic subscriber models #451

Merged
merged 22 commits into from
Mar 21, 2024
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Brewtils Changelog
==================

3.25.0
------
TBD

- Added Topic and Subscriber models and related access methods to easy client

3.24.4
------
3/11/2024
Expand Down
76 changes: 75 additions & 1 deletion brewtils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"Garden",
"Operation",
"Resolvable",
"Subscriber",
"Topic",
]


Expand Down Expand Up @@ -94,8 +96,11 @@ class Events(Enum):
COMMAND_PUBLISHING_BLOCKLIST_SYNC = 48
COMMAND_PUBLISHING_BLOCKLIST_REMOVE = 49
COMMAND_PUBLISHING_BLOCKLIST_UPDATE = 50
TOPIC_CREATED = 54
TOPIC_UPDATED = 55
TOPIC_REMOVED = 56

# Next: 54
# Next: 57


class BaseModel(object):
Expand Down Expand Up @@ -1649,3 +1654,72 @@ def __repr__(self):
self.storage,
self.details,
)


class Subscriber(BaseModel):
schema = "SubscriberSchema"

def __init__(
self,
garden=None,
namespace=None,
system=None,
version=None,
instance=None,
command=None,
):
self.garden = garden
self.namespace = namespace
self.system = system
self.version = version
self.instance = instance
self.command = command

def __str__(self):
return "%s" % self.__dict__

def __repr__(self):
return (
"<Subscriber: garden=%s, namespace=%s, system=%s, version=%s, instance=%s, "
"command=%s>"
% (
self.garden,
self.namespace,
self.system,
self.version,
self.instance,
self.command,
)
)

def __eq__(self, other):
if not isinstance(other, Subscriber):
# don't attempt to compare against unrelated types
return NotImplemented

return (
self.garden == other.garden
and self.namespace == other.namespace
and self.system == other.system
and self.version == other.version
and self.instance == other.instance
and self.command == other.command
)


class Topic(BaseModel):
schema = "TopicSchema"

def __init__(self, id=None, name=None, subscribers=None): # noqa # shadows built-in
self.id = id
self.name = name
self.subscribers = subscribers or []

def __str__(self):
return "%s: %s" % (self.name, [str(s) for s in self.subscribers])

def __repr__(self):
return "<Topic: name=%s, subscribers=%s>" % (
self.name,
self.subscribers,
)
68 changes: 68 additions & 0 deletions brewtils/rest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def __init__(self, *args, **kwargs):
self.user_url = self.base_url + "api/v1/users/"
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/"

# Deprecated
self.logging_config_url = self.base_url + "api/v1/config/logging/"
Expand Down Expand Up @@ -943,3 +944,70 @@ def get_tokens(self, username=None, password=None):
self.session.headers["Authorization"] = "Bearer " + self.access_token

return response

@enable_auth
def get_topic(self, topic_id):
# type: (str, **Any) -> Response
"""Performs a GET on the Topic URL

Args:
topic_id: Topic id

Returns:
Requests Response object
"""
return self.session.get(self.topic_url + topic_id)

@enable_auth
def get_topics(self):
# type: () -> Response
"""Perform a GET on the Topic URL

Returns:
Requests Response object
"""
return self.session.get(self.topic_url)

@enable_auth
def post_topics(self, payload):
# type: (str) -> Response
"""Performs a POST on the Topic URL

Args:
payload: New Topic definition

Returns:
Requests Response object
"""
return self.session.post(
self.topic_url, data=payload, headers=self.JSON_HEADERS
)

@enable_auth
def patch_topic(self, topic_id, payload):
# type: (str, str) -> Response
"""Performs a PATCH on a Topic URL

Args:
topic_id: Topic id
payload: Serialized PatchOperation

Returns:
Requests Response object
"""
return self.session.patch(
self.topic_url + topic_id, data=payload, headers=self.JSON_HEADERS
)

@enable_auth
def delete_topic(self, topic_id):
# type: (str) -> Response
"""Performs a DELETE on a Topic URL

Args:
topic_id: Topic id

Returns:
Requests Response object
"""
return self.session.delete(self.topic_url + topic_id)
76 changes: 76 additions & 0 deletions brewtils/rest/easy_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,3 +1156,79 @@ def _check_chunked_file_validity(self, file_id):
return True, metadata_json
else:
return False, metadata_json

@wrap_response(parse_method="parse_topic", parse_many=False, default_exc=FetchError)
def get_topic(self, topic_id):
"""Get a topic

Args:
topic_id: Topic id

Returns:
The Topic

"""
return self.client.get_topic(topic_id)

@wrap_response(parse_method="parse_topic", parse_many=True, default_exc=FetchError)
def get_topics(self):
"""Get all Topics

Returns:
List[Topics]: List of Topics

"""
return self.client.get_topics()

@wrap_response(parse_method="parse_topic", parse_many=False, default_exc=SaveError)
def create_topic(self, topic):
Comment on lines +1183 to +1184
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a patch_topic in the client.py and no here. Take a look at update_request in this file for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added update_topic to easy client and related tests.

"""Create a new Topic

Args:
system (Topic): The Topic to create

Returns:
Topic: The newly-created topic

"""
return self.client.post_topics(SchemaParser.serialize_topic(topic))

@wrap_response(return_boolean=True, raise_404=True)
def remove_topic(self, topic_id):
"""Remove a unique Topic

Args:
topic_id: Topic id

Returns:
bool: True if removal was successful

Raises:
NotFoundError: Couldn't find a Topic matching given parameters

"""
return self.client.delete_topic(topic_id)

@wrap_response(parse_method="parse_topic", parse_many=False, default_exc=SaveError)
def update_topic(self, topic_id, add=None, remove=None):
"""Update a Topic

Args:
topic_id (str): The Topic ID
add (Optional[str]): Add subscriber
remove (Optional[str]): Remove subscriber

Returns:
Topic: The updated topic

"""
operations = []

if add:
operations.append(PatchOperation("add", value=add))
if remove:
operations.append(PatchOperation("remove", value=remove))

return self.client.patch_topic(
topic_id, SchemaParser.serialize_patch(operations, many=True)
)
74 changes: 74 additions & 0 deletions brewtils/schema_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class SchemaParser(object):
"OperationSchema": brewtils.models.Operation,
"RunnerSchema": brewtils.models.Runner,
"ResolvableSchema": brewtils.models.Resolvable,
"SubscriberSchema": brewtils.models.Subscriber,
"TopicSchema": brewtils.models.Topic,
}

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -403,6 +405,38 @@ def parse_resolvable(cls, resolvable, from_string=False, **kwargs):
resolvable, brewtils.models.Resolvable, from_string=from_string, **kwargs
)

@classmethod
def parse_subscriber(cls, subscriber, from_string=False, **kwargs):
"""Convert raw JSON string or dictionary to a subscriber model object

Args:
subscriber: The raw input
from_string: True if input is a JSON string, False if a dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)

Returns:
A Subscriber object
"""
return cls.parse(
subscriber, brewtils.models.Subscriber, from_string=from_string, **kwargs
)

@classmethod
def parse_topic(cls, topic, from_string=False, **kwargs):
"""Convert raw JSON string or dictionary to a subscriber model object

Args:
topic: The raw input
from_string: True if input is a JSON string, False if a dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)

Returns:
A Topic object
"""
return cls.parse(
topic, brewtils.models.Topic, from_string=from_string, **kwargs
)

@classmethod
def parse(
cls,
Expand Down Expand Up @@ -869,6 +903,46 @@ def serialize_resolvable(cls, resolvable, to_string=True, **kwargs):
**kwargs
)

@classmethod
def serialize_subscriber(cls, subscriber, to_string=True, **kwargs):
"""Convert a subscriber model into serialized form

Args:
subscriber: The subscriber object(s) to be serialized
to_string: True to generate a JSON-formatted string, False to generate a
dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)

Returns:
Serialized representation of subscriber
"""
return cls.serialize(
subscriber,
to_string=to_string,
schema_name=brewtils.models.Subscriber.schema,
**kwargs
)

@classmethod
def serialize_topic(cls, topic, to_string=True, **kwargs):
"""Convert a topic model into serialized form

Args:
topic: The topic object(s) to be serialized
to_string: True to generate a JSON-formatted string, False to generate a
dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)

Returns:
Serialized representation of topic
"""
return cls.serialize(
topic,
to_string=to_string,
schema_name=brewtils.models.Topic.schema,
**kwargs
)

@classmethod
def serialize(
cls,
Expand Down
Loading
Loading