From 1aae05ff5d117c34ea78b0764ae7100c73853c38 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:30:56 -0500 Subject: [PATCH 01/79] New User Model --- brewtils/models.py | 141 +++++++++++++++++++++------------- brewtils/rest/easy_client.py | 8 +- brewtils/schema_parser.py | 37 ++++----- brewtils/schemas.py | 69 ++++++----------- brewtils/test/comparable.py | 15 ++-- brewtils/test/fixtures.py | 48 +++++++----- test/models_test.py | 35 +++++---- test/rest/easy_client_test.py | 6 +- test/schema_parser_test.py | 74 +++++++++--------- test/schema_test.py | 91 ---------------------- 10 files changed, 220 insertions(+), 304 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index c60c794a..9084218b 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -21,8 +21,6 @@ "Event", "Events", "Queue", - "Principal", - "LegacyRole", "RefreshToken", "Job", "RequestFile", @@ -37,6 +35,8 @@ "Garden", "Operation", "Resolvable", + "Role", + "User", ] @@ -1103,58 +1103,6 @@ def __repr__(self): return "" % (self.name, self.size) -class Principal(BaseModel): - schema = "PrincipalSchema" - - def __init__( - self, - id=None, # noqa # shadows built-in - username=None, - roles=None, - permissions=None, - preferences=None, - metadata=None, - ): - self.id = id - self.username = username - self.roles = roles - self.permissions = permissions - self.preferences = preferences - self.metadata = metadata - - def __str__(self): - return "%s" % self.username - - def __repr__(self): - return "" % ( - self.username, - self.roles, - self.permissions, - ) - - -class LegacyRole(BaseModel): - schema = "LegacyRoleSchema" - - def __init__( - self, - id=None, # noqa # shadows built-in - name=None, - description=None, - permissions=None, - ): - self.id = id - self.name = name - self.description = description - self.permissions = permissions - - def __str__(self): - return "%s" % self.name - - def __repr__(self): - return "" % (self.name, self.permissions) - - class RefreshToken(BaseModel): schema = "RefreshTokenSchema" @@ -1649,3 +1597,88 @@ def __repr__(self): self.storage, self.details, ) + + +class User(BaseModel): + schema = "UserSchema" + + def __init__( + self, + username, + password=None, + roles=None, + assigned_roles=None, + is_remote=False, + metadata=None, + ): + self.username = username + self.password = password + self.roles = roles or [] + self.assigned_roles = assigned_roles or [] + self.is_remote = is_remote + self.metadata = metadata + + def __str__(self): + return "%s: %s" % (self.username, self.roles) + + def __repr__(self): + return "" % ( + self.username, + self.roles, + ) + + +class Role(BaseModel): + schema = "RoleSchema" + + PERMISSION_TYPES = { + "ADMIN", + "OPERATOR", + "READ_ONLY", # Default value if not role is provided + } + + def __init__( + self, + name, + permission=None, + description=None, + id=None, + is_remote=False, + scope_gardens=None, + scope_namespaces=None, + scope_systems=None, + scope_instances=None, + scope_verisons=None, + scope_commands=None, + ): + self.permission = permission or "READ_ONLY" + self.description = description + self.id = id + self.name = name + self.is_remote = is_remote + self.scope_gardens = scope_gardens or [] + self.scope_namespaces = scope_namespaces or [] + self.scope_systems = scope_systems or [] + self.scope_instances = scope_instances or [] + self.scope_verisons = scope_verisons or [] + self.scope_commands = scope_commands or [] + + def __str__(self): + return "%s" % (self.name) + + def __repr__(self): + return ( + "" + ) % ( + self.id, + self.name, + self.permission, + self.is_remote, + self.scope_gardens, + self.scope_namespaces, + self.scope_systems, + self.scope_instances, + self.scope_verisons, + self.scope_commands, + ) diff --git a/brewtils/rest/easy_client.py b/brewtils/rest/easy_client.py index 575d728a..ba515916 100644 --- a/brewtils/rest/easy_client.py +++ b/brewtils/rest/easy_client.py @@ -1088,9 +1088,7 @@ def forward(self, operation, **kwargs): SchemaParser.serialize_operation(operation), **kwargs ) - @wrap_response( - parse_method="parse_principal", parse_many=False, default_exc=FetchError - ) + @wrap_response(parse_method="parse_user", parse_many=False, default_exc=FetchError) def get_user(self, user_identifier): """Find a user @@ -1098,7 +1096,7 @@ def get_user(self, user_identifier): user_identifier (str): User ID or username Returns: - Principal: The User + User: The User """ return self.client.get_user(user_identifier) @@ -1107,7 +1105,7 @@ def who_am_i(self): """Find user using the current set of credentials Returns: - Principal: The User + User: The User """ return self.get_user(self.client.username or "anonymous") diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 98598ada..514ae696 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -37,7 +37,7 @@ class SchemaParser(object): "QueueSchema": brewtils.models.Queue, "ParameterSchema": brewtils.models.Parameter, "PatchSchema": brewtils.models.PatchOperation, - "PrincipalSchema": brewtils.models.Principal, + "UserSchema": brewtils.models.User, "RefreshTokenSchema": brewtils.models.RefreshToken, "RequestSchema": brewtils.models.Request, "RequestFileSchema": brewtils.models.RequestFile, @@ -45,11 +45,12 @@ class SchemaParser(object): "FileChunkSchema": brewtils.models.FileChunk, "FileStatusSchema": brewtils.models.FileStatus, "RequestTemplateSchema": brewtils.models.RequestTemplate, - "LegacyRoleSchema": brewtils.models.LegacyRole, "SystemSchema": brewtils.models.System, "OperationSchema": brewtils.models.Operation, "RunnerSchema": brewtils.models.Runner, "ResolvableSchema": brewtils.models.Resolvable, + "RoleSchema": brewtils.models.Role, + "UserSchema": brewtils.models.User, } logger = logging.getLogger(__name__) @@ -253,20 +254,18 @@ def parse_queue(cls, queue, from_string=False, **kwargs): ) @classmethod - def parse_principal(cls, principal, from_string=False, **kwargs): - """Convert raw JSON string or dictionary to a principal model object + def parse_user(cls, user, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a user model object Args: - principal: The raw input + user: 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 Principal object + A User object """ - return cls.parse( - principal, brewtils.models.Principal, from_string=from_string, **kwargs - ) + return cls.parse(user, brewtils.models.User, from_string=from_string, **kwargs) @classmethod def parse_role(cls, role, from_string=False, **kwargs): @@ -280,9 +279,7 @@ def parse_role(cls, role, from_string=False, **kwargs): Returns: A Role object """ - return cls.parse( - role, brewtils.models.LegacyRole, from_string=from_string, **kwargs - ) + return cls.parse(role, brewtils.models.Role, from_string=from_string, **kwargs) @classmethod def parse_refresh_token(cls, refresh_token, from_string=False, **kwargs): @@ -674,11 +671,11 @@ def serialize_queue(cls, queue, to_string=True, **kwargs): ) @classmethod - def serialize_principal(cls, principal, to_string=True, **kwargs): - """Convert a principal model into serialized form + def serialize_user(cls, user, to_string=True, **kwargs): + """Convert a user model into serialized form Args: - principal: The principal object(s) to be serialized + user: The user 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) @@ -687,10 +684,7 @@ def serialize_principal(cls, principal, to_string=True, **kwargs): Serialized representation """ return cls.serialize( - principal, - to_string=to_string, - schema_name=brewtils.models.Principal.schema, - **kwargs + user, to_string=to_string, schema_name=brewtils.models.User.schema, **kwargs ) @classmethod @@ -707,10 +701,7 @@ def serialize_role(cls, role, to_string=True, **kwargs): Serialized representation """ return cls.serialize( - role, - to_string=to_string, - schema_name=brewtils.models.LegacyRole.schema, - **kwargs + role, to_string=to_string, schema_name=brewtils.models.Role.schema, **kwargs ) @classmethod diff --git a/brewtils/schemas.py b/brewtils/schemas.py index e734f13a..f038ec82 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -24,8 +24,6 @@ "LoggingConfigSchema", "EventSchema", "QueueSchema", - "PrincipalSchema", - "LegacyRoleSchema", "RefreshTokenSchema", "JobSchema", "JobExportSchema", @@ -42,8 +40,6 @@ "UserCreateSchema", "UserListSchema", "RoleSchema", - "RoleAssignmentSchema", - "RoleAssignmentDomainSchema", "GardenDomainIdentifierSchema", "SystemDomainIdentifierSchema", ] @@ -425,23 +421,6 @@ class QueueSchema(BaseSchema): size = fields.Integer(allow_none=True) -class PrincipalSchema(BaseSchema): - id = fields.Str(allow_none=True) - username = fields.Str(allow_none=True) - roles = fields.Nested("LegacyRoleSchema", many=True, allow_none=True) - permissions = fields.List(fields.Str(), allow_none=True) - preferences = fields.Dict(allow_none=True) - metadata = fields.Dict(allow_none=True) - - -class LegacyRoleSchema(BaseSchema): - id = fields.Str(allow_none=True) - name = fields.Str(allow_none=True) - description = fields.Str(allow_none=True) - roles = fields.Nested("self", many=True, allow_none=True) - permissions = fields.List(fields.Str(), allow_none=True) - - class RefreshTokenSchema(BaseSchema): id = fields.Str(allow_none=True) issued = DateTime(allow_none=True, format="epoch", example="1500065932000") @@ -604,40 +583,36 @@ class ResolvableSchema(BaseSchema): class RoleSchema(BaseSchema): - id = fields.Str() + id = fields.Str(allow_none=True) name = fields.Str() description = fields.Str(allow_none=True) - permissions = fields.List(fields.Str()) - - -class RoleAssignmentDomainSchema(BaseSchema): - scope = fields.Str() - identifiers = PolyField( - serialization_schema_selector=_domain_identifier_schema_selector, - deserialization_schema_selector=_domain_identifier_schema_selector, - required=False, - ) - - -class RoleAssignmentSchema(BaseSchema): - domain = fields.Nested(RoleAssignmentDomainSchema, required=True) - role = fields.Nested(RoleSchema()) + permission = fields.Str() + is_remote = fields.Boolean(allow_none=True) + scope_gardens = fields.List(fields.Str(), allow_none=True) + scope_namespaces = fields.List(fields.Str(), allow_none=True) + scope_systems = fields.List(fields.Str(), allow_none=True) + scope_instances = fields.List(fields.Str(), allow_none=True) + scope_verisons = fields.List(fields.Str(), allow_none=True) + scope_commands = fields.List(fields.Str(), allow_none=True) class UserSchema(BaseSchema): - id = fields.Str() + id = fields.Str(allow_none=True) username = fields.Str() - role_assignments = fields.List(fields.Nested(RoleAssignmentSchema())) - permissions = fields.Dict() + password = fields.Str() + roles = fields.List(fields.Str(), allow_none=True) + assigned_roles = fields.List(fields.Nested(RoleSchema())) + is_remote = fields.Boolean(allow_none=True) + metadata = fields.Dict(allow_none=True) -class UserCreateSchema(BaseSchema): - username = fields.Str(required=True) - password = fields.Str(required=True, load_only=True) +# class UserCreateSchema(BaseSchema): +# username = fields.Str(required=True) +# password = fields.Str(required=True, load_only=True) -class UserListSchema(BaseSchema): - users = fields.List(fields.Nested(UserSchema())) +# class UserListSchema(BaseSchema): +# users = fields.List(fields.Nested(UserSchema())) model_schema_map.update( @@ -658,7 +633,6 @@ class UserListSchema(BaseSchema): "Queue": QueueSchema, "Parameter": ParameterSchema, "PatchOperation": PatchSchema, - "Principal": PrincipalSchema, "RefreshToken": RefreshTokenSchema, "Request": RequestSchema, "RequestFile": RequestFileSchema, @@ -666,11 +640,12 @@ class UserListSchema(BaseSchema): "FileChunk": FileChunkSchema, "FileStatus": FileStatusSchema, "RequestTemplate": RequestTemplateSchema, - "LegacyRole": LegacyRoleSchema, "System": SystemSchema, "Operation": OperationSchema, "Runner": RunnerSchema, "Resolvable": ResolvableSchema, + "Role": RoleSchema, + "User": UserSchema, # Compatibility for the Job trigger types "interval": IntervalTriggerSchema, "date": DateTriggerSchema, diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 2003a7c8..8b320c30 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -24,12 +24,12 @@ Instance, IntervalTrigger, Job, - LegacyRole, + Role, LoggingConfig, Operation, Parameter, PatchOperation, - Principal, + User, Queue, Request, RequestFile, @@ -50,7 +50,7 @@ "assert_trigger_equal", "assert_command_equal", "assert_parameter_equal", - "assert_principal_equal", + "assert_user_equal", "assert_request_equal", "assert_role_equal", "assert_system_equal", @@ -236,12 +236,12 @@ def assert_event_equal(obj1, obj2, do_raise=False): ) -def assert_principal_equal(obj1, obj2, do_raise=False): +def assert_user_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, obj2, - expected_type=Principal, - deep_fields={"roles": partial(assert_role_equal, do_raise=True)}, + expected_type=User, + deep_fields={"assigned_roles": partial(assert_role_equal, do_raise=True)}, do_raise=do_raise, ) @@ -298,8 +298,7 @@ def assert_role_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, obj2, - expected_type=LegacyRole, - deep_fields={"roles": partial(assert_role_equal, do_raise=True)}, + expected_type=Role, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index cef700d8..d0c9c28f 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -17,12 +17,10 @@ Instance, IntervalTrigger, Job, - LegacyRole, LoggingConfig, Operation, Parameter, PatchOperation, - Principal, Queue, Request, RequestFile, @@ -30,6 +28,8 @@ Resolvable, Runner, System, + User, + Role, ) @@ -528,38 +528,44 @@ def bg_queue(queue_dict): @pytest.fixture -def principal_dict(legacy_role_dict): +def role_dict(): return { - "id": "58542eb571afd47ead90d24f", - "username": "admin", - "roles": [legacy_role_dict], - "permissions": ["bg-all"], - "preferences": {"theme": "dark"}, - "metadata": {"foo": "bar"}, + "permission": "ADMIN", + "description": "ADMIN ROLE", + "id": "1", + "name": "ADMIN_ROLE", + "is_remote": False, + "scope_gardens": ["FOO"], + "scope_namespaces": [], + "scope_systems": [], + "scope_instances": [], + "scope_verisons": [], + "scope_commands": [], } @pytest.fixture -def bg_principal(principal_dict, bg_role): - dict_copy = copy.deepcopy(principal_dict) - dict_copy["roles"] = [bg_role] - return Principal(**dict_copy) +def bg_role(role_dict): + return Role(**role_dict) @pytest.fixture -def legacy_role_dict(): +def user_dict(role_dict): return { - "id": "58542eb571afd47ead90d26f", - "name": "bg-admin", - "description": "The admin role", - "permissions": ["bg-all"], + "username": "USERNAME", + "password": "HASH", + "roles": ["ADMIN_ROLE"], + "assigned_roles": [role_dict], + "is_remote": False, + "metadata": {}, } @pytest.fixture -def bg_role(legacy_role_dict): - dict_copy = copy.deepcopy(legacy_role_dict) - return LegacyRole(**dict_copy) +def bg_user(user_dict, bg_role): + dict_copy = copy.deepcopy(user_dict) + dict_copy["assigned_roles"] = [bg_role] + return User(**dict_copy) @pytest.fixture diff --git a/test/models_test.py b/test/models_test.py index b64bafb1..64c22cb3 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -13,12 +13,12 @@ LoggingConfig, Parameter, PatchOperation, - Principal, + User, Queue, Request, RequestFile, RequestTemplate, - LegacyRole, + Role, ) from pytest_lazyfixture import lazy_fixture @@ -529,31 +529,36 @@ def test_repr(self, queue): assert repr(queue) == "" -class TestPrincipal(object): +class TestUser(object): @pytest.fixture - def principal(self): - return Principal(username="admin", roles=["bg-admin"], permissions=["bg-all"]) + def user(self): + return User( + username="admin", + roles=["bg-admin"], + assigned_roles=[Role(name="foo", permission="ADMIN")], + ) - def test_str(self, principal): - assert str(principal) == "admin" + def test_str(self, user): + assert str(user) == "admin: ['bg-admin']" - def test_repr(self, principal): - assert ( - repr(principal) - == "" - ) + def test_repr(self, user): + assert repr(user) == "" -class TestLegacyRole(object): +class TestRole(object): @pytest.fixture def role(self): - return LegacyRole(name="bg-admin", permissions=["bg-all"]) + return Role(name="bg-admin", permission="ADMIN") def test_str(self, role): assert str(role) == "bg-admin" def test_repr(self, role): - assert repr(role) == "" + assert repr(role) == ( + "" + ) class TestDateTrigger(object): diff --git a/test/rest/easy_client_test.py b/test/rest/easy_client_test.py index 488bb27c..9ccf8533 100644 --- a/test/rest/easy_client_test.py +++ b/test/rest/easy_client_test.py @@ -515,11 +515,11 @@ def test_forward(client, rest_client, success, bg_operation): assert rest_client.post_forward.called is True -def test_get_user(client, rest_client, success, bg_principal): +def test_get_user(client, rest_client, success, bg_user): rest_client.get_user.return_value = success - client.get_user(bg_principal.username) - rest_client.get_user.assert_called_once_with(bg_principal.username) + client.get_user(bg_user.username) + rest_client.get_user.assert_called_once_with(bg_user.username) class TestWhoAmI(object): diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index cbeee083..aea91843 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -21,7 +21,7 @@ assert_operation_equal, assert_parameter_equal, assert_patch_equal, - assert_principal_equal, + assert_user_equal, assert_queue_equal, assert_request_equal, assert_request_file_equal, @@ -121,14 +121,14 @@ def test_no_modify(self, system_dict): lazy_fixture("bg_queue"), ), ( - brewtils.models.Principal, - lazy_fixture("principal_dict"), - assert_principal_equal, - lazy_fixture("bg_principal"), + brewtils.models.User, + lazy_fixture("user_dict"), + assert_user_equal, + lazy_fixture("bg_user"), ), ( - brewtils.models.LegacyRole, - lazy_fixture("legacy_role_dict"), + brewtils.models.Role, + lazy_fixture("role_dict"), assert_role_equal, lazy_fixture("bg_role"), ), @@ -249,14 +249,14 @@ def test_single_from_string(self): lazy_fixture("bg_queue"), ), ( - "parse_principal", - lazy_fixture("principal_dict"), - assert_principal_equal, - lazy_fixture("bg_principal"), + "parse_user", + lazy_fixture("user_dict"), + assert_user_equal, + lazy_fixture("bg_user"), ), ( "parse_role", - lazy_fixture("legacy_role_dict"), + lazy_fixture("role_dict"), assert_role_equal, lazy_fixture("bg_role"), ), @@ -382,14 +382,14 @@ def test_single_specific_from_string(self): lazy_fixture("bg_queue"), ), ( - brewtils.models.Principal, - lazy_fixture("principal_dict"), - assert_principal_equal, - lazy_fixture("bg_principal"), + brewtils.models.User, + lazy_fixture("user_dict"), + assert_user_equal, + lazy_fixture("bg_user"), ), ( - brewtils.models.LegacyRole, - lazy_fixture("legacy_role_dict"), + brewtils.models.Role, + lazy_fixture("role_dict"), assert_role_equal, lazy_fixture("bg_role"), ), @@ -508,14 +508,14 @@ def test_many(self, model, data, assertion, expected): lazy_fixture("bg_queue"), ), ( - "parse_principal", - lazy_fixture("principal_dict"), - assert_principal_equal, - lazy_fixture("bg_principal"), + "parse_user", + lazy_fixture("user_dict"), + assert_user_equal, + lazy_fixture("bg_user"), ), ( "parse_role", - lazy_fixture("legacy_role_dict"), + lazy_fixture("role_dict"), assert_role_equal, lazy_fixture("bg_role"), ), @@ -605,8 +605,8 @@ class TestSerialize(object): (lazy_fixture("bg_logging_config"), lazy_fixture("logging_config_dict")), (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), - (lazy_fixture("bg_principal"), lazy_fixture("principal_dict")), - (lazy_fixture("bg_role"), lazy_fixture("legacy_role_dict")), + (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -665,14 +665,14 @@ def test_single(self, model, expected): ("serialize_event", lazy_fixture("bg_event"), lazy_fixture("event_dict")), ("serialize_queue", lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), ( - "serialize_principal", - lazy_fixture("bg_principal"), - lazy_fixture("principal_dict"), + "serialize_user", + lazy_fixture("bg_user"), + lazy_fixture("user_dict"), ), ( "serialize_role", lazy_fixture("bg_role"), - lazy_fixture("legacy_role_dict"), + lazy_fixture("role_dict"), ), ("serialize_job", lazy_fixture("bg_job"), lazy_fixture("job_dict")), ( @@ -734,8 +734,8 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_logging_config"), lazy_fixture("logging_config_dict")), (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), - (lazy_fixture("bg_principal"), lazy_fixture("principal_dict")), - (lazy_fixture("bg_role"), lazy_fixture("legacy_role_dict")), + (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -811,11 +811,11 @@ class TestRoundTrip(object): (brewtils.models.Event, assert_event_equal, lazy_fixture("bg_event")), (brewtils.models.Queue, assert_queue_equal, lazy_fixture("bg_queue")), ( - brewtils.models.Principal, - assert_principal_equal, - lazy_fixture("bg_principal"), + brewtils.models.User, + assert_user_equal, + lazy_fixture("bg_user"), ), - (brewtils.models.LegacyRole, assert_role_equal, lazy_fixture("bg_role")), + (brewtils.models.Role, assert_role_equal, lazy_fixture("bg_role")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_cron_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_interval_job")), @@ -853,8 +853,8 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.LoggingConfig, lazy_fixture("logging_config_dict")), (brewtils.models.Event, lazy_fixture("event_dict")), (brewtils.models.Queue, lazy_fixture("queue_dict")), - (brewtils.models.Principal, lazy_fixture("principal_dict")), - (brewtils.models.LegacyRole, lazy_fixture("legacy_role_dict")), + (brewtils.models.User, lazy_fixture("user_dict")), + (brewtils.models.Role, lazy_fixture("role_dict")), (brewtils.models.Job, lazy_fixture("job_dict")), (brewtils.models.Job, lazy_fixture("cron_job_dict")), (brewtils.models.Job, lazy_fixture("interval_job_dict")), diff --git a/test/schema_test.py b/test/schema_test.py index f032780b..da25bd61 100644 --- a/test/schema_test.py +++ b/test/schema_test.py @@ -9,7 +9,6 @@ from brewtils.schemas import ( BaseSchema, DateTime, - RoleAssignmentSchema, SystemSchema, _deserialize_model, _serialize_model, @@ -96,93 +95,3 @@ def test_deserialize_mapping(self): assert len(models) == len( SchemaParser._models ), "Missing mapped schema for deserialization" - - -class TestRoleAssignmentSchema(object): - @pytest.fixture - def schema(self): - yield RoleAssignmentSchema() - - @pytest.fixture - def role_assignment_garden_scope(self): - role = {"name": "myrole", "permissions": ["perm1"]} - domain = {"scope": "Garden", "identifiers": {"name": "mygarden"}} - role_assignment = {"role": role, "domain": domain} - - yield role_assignment - - @pytest.fixture - def role_assignment_system_scope(self): - role = {"name": "myrole", "permissions": ["perm1"]} - domain = { - "scope": "System", - "identifiers": {"name": "mysystem", "namespace": "mygarden"}, - } - role_assignment = {"role": role, "domain": domain} - - yield role_assignment - - @pytest.fixture - def role_assignment_global_scope(self): - role = {"name": "myrole", "permissions": ["perm1"]} - domain = {"scope": "Global"} - role_assignment = {"role": role, "domain": domain} - - yield role_assignment - - def test_role_assignment_domain_schema_can_deserialize_garden_scope( - self, schema, role_assignment_garden_scope - ): - assert ( - schema.load(role_assignment_garden_scope).data - == role_assignment_garden_scope - ) - - def test_role_assignment_domain_schema_can_deserialize_system_scope( - self, schema, role_assignment_system_scope - ): - assert ( - schema.load(role_assignment_system_scope).data - == role_assignment_system_scope - ) - - def test_role_assignment_domain_schema_can_deserialize_global_scope( - self, schema, role_assignment_global_scope - ): - assert ( - schema.load(role_assignment_global_scope).data - == role_assignment_global_scope - ) - - def test_role_assignment_domain_schema_can_serialize( - self, schema, role_assignment_garden_scope - ): - assert ( - schema.dump(role_assignment_garden_scope).data - == role_assignment_garden_scope - ) - - def test_role_assignment_domain_schema_can_serialize_global_scope( - self, schema, role_assignment_global_scope - ): - assert ( - schema.dump(role_assignment_global_scope).data - == role_assignment_global_scope - ) - - def test_role_assignment_domain_schema_validates_identifiers( - self, schema, role_assignment_system_scope - ): - # Remove one of the required identifier fields - del role_assignment_system_scope["domain"]["identifiers"]["namespace"] - - with pytest.raises(ValidationError): - schema.load(role_assignment_system_scope) - - def test_role_assignment_domain_schema_raises_error_on_invalid_scope( - self, schema, role_assignment_system_scope - ): - role_assignment_system_scope["domain"]["scope"] = "Unsupported" - - with pytest.raises(ValidationError): - schema.load(role_assignment_system_scope) From 504d4232c1e0c6113591d2018484a4615c827d52 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:19:53 -0500 Subject: [PATCH 02/79] code cleanup --- brewtils/models.py | 5 +++-- brewtils/schema_parser.py | 1 - brewtils/schemas.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9084218b..a2853f61 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1668,8 +1668,9 @@ def __str__(self): def __repr__(self): return ( - "" + "" ) % ( self.id, self.name, diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 514ae696..2a6b3783 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -37,7 +37,6 @@ class SchemaParser(object): "QueueSchema": brewtils.models.Queue, "ParameterSchema": brewtils.models.Parameter, "PatchSchema": brewtils.models.PatchOperation, - "UserSchema": brewtils.models.User, "RefreshTokenSchema": brewtils.models.RefreshToken, "RequestSchema": brewtils.models.Request, "RequestFileSchema": brewtils.models.RequestFile, diff --git a/brewtils/schemas.py b/brewtils/schemas.py index f038ec82..743950a8 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -37,8 +37,6 @@ "GardenSchema", "OperationSchema", "UserSchema", - "UserCreateSchema", - "UserListSchema", "RoleSchema", "GardenDomainIdentifierSchema", "SystemDomainIdentifierSchema", From 7705412b7400e09224506e03f6d7e39d01f26c5a Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:44:42 -0500 Subject: [PATCH 03/79] Expanding to support remote user mappings --- brewtils/models.py | 19 ++++++++++----- brewtils/schema_parser.py | 40 ++++++++++++++++++++++++++++++++ brewtils/schemas.py | 46 ++++++++----------------------------- brewtils/test/comparable.py | 8 ++++++- brewtils/test/fixtures.py | 25 ++++++++++++++++---- test/models_test.py | 6 ++--- test/schema_parser_test.py | 37 +++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 52 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index a2853f61..628ce829 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1607,15 +1607,17 @@ def __init__( username, password=None, roles=None, - assigned_roles=None, + remote_roles=None, is_remote=False, + remote_user_mapping=None, metadata=None, ): self.username = username self.password = password self.roles = roles or [] - self.assigned_roles = assigned_roles or [] + self.remote_roles = remote_roles or [] self.is_remote = is_remote + self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata def __str__(self): @@ -1643,7 +1645,6 @@ def __init__( permission=None, description=None, id=None, - is_remote=False, scope_gardens=None, scope_namespaces=None, scope_systems=None, @@ -1655,7 +1656,6 @@ def __init__( self.description = description self.id = id self.name = name - self.is_remote = is_remote self.scope_gardens = scope_gardens or [] self.scope_namespaces = scope_namespaces or [] self.scope_systems = scope_systems or [] @@ -1668,14 +1668,13 @@ def __str__(self): def __repr__(self): return ( - "" ) % ( self.id, self.name, self.permission, - self.is_remote, self.scope_gardens, self.scope_namespaces, self.scope_systems, @@ -1683,3 +1682,11 @@ def __repr__(self): self.scope_verisons, self.scope_commands, ) + + +class RemoteUserMap(BaseModel): + schema = "RemoteUserMapSchema" + + def __init__(self, target_garden, username): + self.target_garden = target_garden + self.username = username diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 2a6b3783..7462c243 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -50,6 +50,7 @@ class SchemaParser(object): "ResolvableSchema": brewtils.models.Resolvable, "RoleSchema": brewtils.models.Role, "UserSchema": brewtils.models.User, + "RemoteUserMapSchema": brewtils.models.RemoteUserMap, } logger = logging.getLogger(__name__) @@ -280,6 +281,25 @@ def parse_role(cls, role, from_string=False, **kwargs): """ return cls.parse(role, brewtils.models.Role, from_string=from_string, **kwargs) + @classmethod + def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a RemoteUserMap model object + + Args: + role: 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 RemoteUserMap object + """ + return cls.parse( + remote_user_map, + brewtils.models.RemoteUserMap, + from_string=from_string, + **kwargs + ) + @classmethod def parse_refresh_token(cls, refresh_token, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a refresh token object @@ -703,6 +723,26 @@ def serialize_role(cls, role, to_string=True, **kwargs): role, to_string=to_string, schema_name=brewtils.models.Role.schema, **kwargs ) + @classmethod + def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): + """Convert a RemoteUserMap model into serialized form + + Args: + RemoteUserMap: The RemoteUserMap 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 + """ + return cls.serialize( + remote_user_map, + to_string=to_string, + schema_name=brewtils.models.RemoteUserMap.schema, + **kwargs + ) + @classmethod def serialize_refresh_token(cls, refresh_token, to_string=True, **kwargs): """Convert a role model into serialized form diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 743950a8..9ea8ca42 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -38,8 +38,7 @@ "OperationSchema", "UserSchema", "RoleSchema", - "GardenDomainIdentifierSchema", - "SystemDomainIdentifierSchema", + "RemoteUserMapSchema", ] # This will be updated after all the schema classes are defined @@ -68,26 +67,6 @@ def _deserialize_model(_, data, type_field=None, allowed_types=None): return model_schema_map.get(data[type_field])() -def _domain_identifier_schema_selector(_, role_assignment_domain): - scope_schema_map = { - "Garden": GardenDomainIdentifierSchema, - "System": SystemDomainIdentifierSchema, - "Global": Schema, - } - - if isinstance(role_assignment_domain, dict): - scope = role_assignment_domain.get("scope") - else: - scope = role_assignment_domain.scope - - schema = scope_schema_map.get(scope) - - if schema is None: - raise TypeError("Invalid scope: %s" % scope) - - return schema() - - class ModelField(PolyField): """Field representing a Brewtils model @@ -495,10 +474,6 @@ class GardenSchema(BaseSchema): metadata = fields.Dict(allow_none=True) -class GardenDomainIdentifierSchema(BaseSchema): - name = fields.Str(required=True) - - class JobSchema(BaseSchema): id = fields.Str(allow_none=True) name = fields.Str(allow_none=True) @@ -585,7 +560,6 @@ class RoleSchema(BaseSchema): name = fields.Str() description = fields.Str(allow_none=True) permission = fields.Str() - is_remote = fields.Boolean(allow_none=True) scope_gardens = fields.List(fields.Str(), allow_none=True) scope_namespaces = fields.List(fields.Str(), allow_none=True) scope_systems = fields.List(fields.Str(), allow_none=True) @@ -594,25 +568,22 @@ class RoleSchema(BaseSchema): scope_commands = fields.List(fields.Str(), allow_none=True) +class RemoteUserMapSchema(BaseSchema): + target_garden = fields.Str() + username = fields.Str() + + class UserSchema(BaseSchema): id = fields.Str(allow_none=True) username = fields.Str() password = fields.Str() roles = fields.List(fields.Str(), allow_none=True) - assigned_roles = fields.List(fields.Nested(RoleSchema())) + remote_roles = fields.List(fields.Nested(RoleSchema())) + remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) -# class UserCreateSchema(BaseSchema): -# username = fields.Str(required=True) -# password = fields.Str(required=True, load_only=True) - - -# class UserListSchema(BaseSchema): -# users = fields.List(fields.Nested(UserSchema())) - - model_schema_map.update( { "Choices": ChoicesSchema, @@ -644,6 +615,7 @@ class UserSchema(BaseSchema): "Resolvable": ResolvableSchema, "Role": RoleSchema, "User": UserSchema, + "RemoteUserMap": RemoteUserMapSchema, # Compatibility for the Job trigger types "interval": IntervalTriggerSchema, "date": DateTriggerSchema, diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 8b320c30..f1197ed6 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -31,6 +31,7 @@ PatchOperation, User, Queue, + RemoteUserMap, Request, RequestFile, RequestTemplate, @@ -51,6 +52,7 @@ "assert_command_equal", "assert_parameter_equal", "assert_user_equal", + "assert_remote_user_map_equal", "assert_request_equal", "assert_role_equal", "assert_system_equal", @@ -193,6 +195,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_runner_equal = partial(_assert_wrapper, expected_type=Runner) assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable) assert_connection_equal = partial(_assert_wrapper, expected_type=Connection) +assert_remote_user_map_equal = partial(_assert_wrapper, expected_type=RemoteUserMap) def assert_command_equal(obj1, obj2, do_raise=False): @@ -241,7 +244,10 @@ def assert_user_equal(obj1, obj2, do_raise=False): obj1, obj2, expected_type=User, - deep_fields={"assigned_roles": partial(assert_role_equal, do_raise=True)}, + deep_fields={ + "remote_roles": partial(assert_role_equal, do_raise=True), + "remote_user_mapping": partial(assert_remote_user_map_equal, do_raise=True), + }, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index d0c9c28f..5fe6c2c4 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -22,6 +22,7 @@ Parameter, PatchOperation, Queue, + RemoteUserMap, Request, RequestFile, RequestTemplate, @@ -527,6 +528,19 @@ def bg_queue(queue_dict): return Queue(**queue_dict) +@pytest.fixture +def remote_user_map_dict(): + return { + "target_garden": "test", + "username": "user", + } + + +@pytest.fixture +def bg_remote_user_map(remote_user_map_dict): + return RemoteUserMap(**remote_user_map_dict) + + @pytest.fixture def role_dict(): return { @@ -534,7 +548,6 @@ def role_dict(): "description": "ADMIN ROLE", "id": "1", "name": "ADMIN_ROLE", - "is_remote": False, "scope_gardens": ["FOO"], "scope_namespaces": [], "scope_systems": [], @@ -550,21 +563,23 @@ def bg_role(role_dict): @pytest.fixture -def user_dict(role_dict): +def user_dict(role_dict, remote_user_map_dict): return { "username": "USERNAME", "password": "HASH", "roles": ["ADMIN_ROLE"], - "assigned_roles": [role_dict], + "remote_roles": [role_dict], + "remote_user_mapping": [remote_user_map_dict], "is_remote": False, "metadata": {}, } @pytest.fixture -def bg_user(user_dict, bg_role): +def bg_user(user_dict, bg_role, bg_remote_user_map): dict_copy = copy.deepcopy(user_dict) - dict_copy["assigned_roles"] = [bg_role] + dict_copy["remote_roles"] = [bg_role] + dict_copy["remote_user_mapping"] = [bg_remote_user_map] return User(**dict_copy) diff --git a/test/models_test.py b/test/models_test.py index 64c22cb3..6b136600 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -535,7 +535,7 @@ def user(self): return User( username="admin", roles=["bg-admin"], - assigned_roles=[Role(name="foo", permission="ADMIN")], + remote_roles=[Role(name="foo", permission="ADMIN")], ) def test_str(self, user): @@ -556,8 +556,8 @@ def test_str(self, role): def test_repr(self, role): assert repr(role) == ( "" + "scope_garden=[], scope_namespaces=[], scope_systems=[], " + "scope_instances=[], scope_versions=[], scope_commands=[]>" ) diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index aea91843..ab9dab8d 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -23,6 +23,7 @@ assert_patch_equal, assert_user_equal, assert_queue_equal, + assert_remote_user_map_equal, assert_request_equal, assert_request_file_equal, assert_resolvable_equal, @@ -126,6 +127,12 @@ def test_no_modify(self, system_dict): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( brewtils.models.Role, lazy_fixture("role_dict"), @@ -254,6 +261,12 @@ def test_single_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_remote_user_map", + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( "parse_role", lazy_fixture("role_dict"), @@ -387,6 +400,12 @@ def test_single_specific_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( brewtils.models.Role, lazy_fixture("role_dict"), @@ -513,6 +532,12 @@ def test_many(self, model, data, assertion, expected): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_remote_user_map", + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( "parse_role", lazy_fixture("role_dict"), @@ -606,6 +631,7 @@ class TestSerialize(object): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), @@ -669,6 +695,11 @@ def test_single(self, model, expected): lazy_fixture("bg_user"), lazy_fixture("user_dict"), ), + ( + "serialize_remote_user_map", + lazy_fixture("bg_remote_user_map"), + lazy_fixture("remote_user_map_dict"), + ), ( "serialize_role", lazy_fixture("bg_role"), @@ -735,6 +766,7 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), @@ -815,6 +847,11 @@ class TestRoundTrip(object): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), (brewtils.models.Role, assert_role_equal, lazy_fixture("bg_role")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_cron_job")), From 13114cc8609bff62be0d1ea566fa0c3a505c98f5 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:29:26 +0000 Subject: [PATCH 04/79] Replace Refresh with User Token --- brewtils/models.py | 8 ++++---- brewtils/schema_parser.py | 20 ++++++++++---------- brewtils/schemas.py | 10 +++++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 628ce829..60e6f2f3 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -21,7 +21,7 @@ "Event", "Events", "Queue", - "RefreshToken", + "UserToken", "Job", "RequestFile", "File", @@ -1103,8 +1103,8 @@ def __repr__(self): return "" % (self.name, self.size) -class RefreshToken(BaseModel): - schema = "RefreshTokenSchema" +class UserToken(BaseModel): + schema = "UserTokenSchema" def __init__( self, @@ -1122,7 +1122,7 @@ def __str__(self): return "%s" % self.payload def __repr__(self): - return "" % ( + return "" % ( self.issued, self.expires, self.payload, diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 7462c243..74830a57 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -37,7 +37,7 @@ class SchemaParser(object): "QueueSchema": brewtils.models.Queue, "ParameterSchema": brewtils.models.Parameter, "PatchSchema": brewtils.models.PatchOperation, - "RefreshTokenSchema": brewtils.models.RefreshToken, + "UserTokenSchema": brewtils.models.UserToken, "RequestSchema": brewtils.models.Request, "RequestFileSchema": brewtils.models.RequestFile, "FileSchema": brewtils.models.File, @@ -301,20 +301,20 @@ def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): ) @classmethod - def parse_refresh_token(cls, refresh_token, from_string=False, **kwargs): - """Convert raw JSON string or dictionary to a refresh token object + def parse_user_token(cls, user_token, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a user token object Args: - refresh_token: The raw input + user_token: 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 RefreshToken object + A UserToken object """ return cls.parse( refresh_token, - brewtils.models.RefreshToken, + brewtils.models.UserToken, from_string=from_string, **kwargs ) @@ -744,11 +744,11 @@ def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): ) @classmethod - def serialize_refresh_token(cls, refresh_token, to_string=True, **kwargs): + def serialize_user_token(cls, user_token, to_string=True, **kwargs): """Convert a role model into serialized form Args: - refresh_token: The token object(s) to be serialized + user_token: The token 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) @@ -757,9 +757,9 @@ def serialize_refresh_token(cls, refresh_token, to_string=True, **kwargs): Serialized representation """ return cls.serialize( - refresh_token, + user_token, to_string=to_string, - schema_name=brewtils.models.RefreshToken.schema, + schema_name=brewtils.models.UserToken.schema, **kwargs ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 9ea8ca42..0657eb87 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -24,7 +24,7 @@ "LoggingConfigSchema", "EventSchema", "QueueSchema", - "RefreshTokenSchema", + "UserTokenSchema", "JobSchema", "JobExportSchema", "JobExportInputSchema", @@ -398,11 +398,11 @@ class QueueSchema(BaseSchema): size = fields.Integer(allow_none=True) -class RefreshTokenSchema(BaseSchema): - id = fields.Str(allow_none=True) +class UserTokenSchema(BaseSchema): + uuid = fields.Str(allow_none=True) issued = DateTime(allow_none=True, format="epoch", example="1500065932000") expires = DateTime(allow_none=True, format="epoch", example="1500065932000") - payload = fields.Dict(allow_none=True) + user = fields.Nested("UserSchema") class DateTriggerSchema(BaseSchema): @@ -602,7 +602,7 @@ class UserSchema(BaseSchema): "Queue": QueueSchema, "Parameter": ParameterSchema, "PatchOperation": PatchSchema, - "RefreshToken": RefreshTokenSchema, + "UserToken": UserTokenSchema, "Request": RequestSchema, "RequestFile": RequestFileSchema, "File": FileSchema, From c3942df74b1d1789a991acd8a5784cafef34fd8b Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:04:42 +0000 Subject: [PATCH 05/79] fixed typo --- brewtils/models.py | 6 +++--- brewtils/schemas.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 60e6f2f3..9d73fecf 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1649,7 +1649,7 @@ def __init__( scope_namespaces=None, scope_systems=None, scope_instances=None, - scope_verisons=None, + scope_versions=None, scope_commands=None, ): self.permission = permission or "READ_ONLY" @@ -1660,7 +1660,7 @@ def __init__( self.scope_namespaces = scope_namespaces or [] self.scope_systems = scope_systems or [] self.scope_instances = scope_instances or [] - self.scope_verisons = scope_verisons or [] + self.scope_versions = scope_versions or [] self.scope_commands = scope_commands or [] def __str__(self): @@ -1679,7 +1679,7 @@ def __repr__(self): self.scope_namespaces, self.scope_systems, self.scope_instances, - self.scope_verisons, + self.scope_versions, self.scope_commands, ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 0657eb87..84c54e40 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -564,7 +564,7 @@ class RoleSchema(BaseSchema): scope_namespaces = fields.List(fields.Str(), allow_none=True) scope_systems = fields.List(fields.Str(), allow_none=True) scope_instances = fields.List(fields.Str(), allow_none=True) - scope_verisons = fields.List(fields.Str(), allow_none=True) + scope_versions = fields.List(fields.Str(), allow_none=True) scope_commands = fields.List(fields.Str(), allow_none=True) From bb6e025d7c152123ef425a934f7fabbe84fa192d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:06:31 +0000 Subject: [PATCH 06/79] model updates --- brewtils/models.py | 5 +++++ brewtils/schemas.py | 2 ++ brewtils/test/fixtures.py | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9d73fecf..032b924d 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1412,6 +1412,8 @@ def __init__( parent=None, children=None, metadata=None, + default_user=None, + shared_users=None ): self.id = id self.name = name @@ -1429,6 +1431,9 @@ def __init__( self.children = children self.metadata = metadata or {} + self.default_user = default_user + self.shared_users = shared_users + def __str__(self): return "%s" % self.name diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 84c54e40..8b45a24f 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -472,6 +472,8 @@ class GardenSchema(BaseSchema): "self", exclude=("parent"), many=True, default=None, allow_none=True ) metadata = fields.Dict(allow_none=True) + default_user = fields.Bool(allow_none=True) + shared_users = fields.Bool(allow_none=True) class JobSchema(BaseSchema): diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 5fe6c2c4..09b1beb3 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -552,7 +552,7 @@ def role_dict(): "scope_namespaces": [], "scope_systems": [], "scope_instances": [], - "scope_verisons": [], + "scope_versions": [], "scope_commands": [], } @@ -837,6 +837,8 @@ def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_di "has_parent": False, "children": [], "metadata": {}, + "default_user": None, + "shared_users":True, } From c660498e35df6084569800a17595f91235118dbc Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:18:42 +0000 Subject: [PATCH 07/79] Add Local Roles to users --- brewtils/models.py | 2 ++ brewtils/schemas.py | 1 + brewtils/test/fixtures.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 032b924d..1ab0944a 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1612,6 +1612,7 @@ def __init__( username, password=None, roles=None, + local_roles=None, remote_roles=None, is_remote=False, remote_user_mapping=None, @@ -1620,6 +1621,7 @@ def __init__( self.username = username self.password = password self.roles = roles or [] + self.local_roles = local_roles or [] self.remote_roles = remote_roles or [] self.is_remote = is_remote self.remote_user_mapping = remote_user_mapping or [] diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 8b45a24f..6d282e73 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -580,6 +580,7 @@ class UserSchema(BaseSchema): username = fields.Str() password = fields.Str() roles = fields.List(fields.Str(), allow_none=True) + local_roles = fields.List(fields.Nested(RoleSchema())) remote_roles = fields.List(fields.Nested(RoleSchema())) remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 09b1beb3..8be66441 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -568,6 +568,7 @@ def user_dict(role_dict, remote_user_map_dict): "username": "USERNAME", "password": "HASH", "roles": ["ADMIN_ROLE"], + "local_roles": [role_dict], "remote_roles": [role_dict], "remote_user_mapping": [remote_user_map_dict], "is_remote": False, @@ -579,6 +580,7 @@ def user_dict(role_dict, remote_user_map_dict): def bg_user(user_dict, bg_role, bg_remote_user_map): dict_copy = copy.deepcopy(user_dict) dict_copy["remote_roles"] = [bg_role] + dict_copy["local_roles"] = [bg_role] dict_copy["remote_user_mapping"] = [bg_remote_user_map] return User(**dict_copy) From eb5ddc1b356e4e3034c4c9f6220fd573d58d8c9f Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:43:58 +0000 Subject: [PATCH 08/79] Cleaning up models --- brewtils/models.py | 2 -- brewtils/schemas.py | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 1ab0944a..648433a2 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1413,7 +1413,6 @@ def __init__( children=None, metadata=None, default_user=None, - shared_users=None ): self.id = id self.name = name @@ -1432,7 +1431,6 @@ def __init__( self.metadata = metadata or {} self.default_user = default_user - self.shared_users = shared_users def __str__(self): return "%s" % self.name diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 6d282e73..b062851b 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -472,8 +472,7 @@ class GardenSchema(BaseSchema): "self", exclude=("parent"), many=True, default=None, allow_none=True ) metadata = fields.Dict(allow_none=True) - default_user = fields.Bool(allow_none=True) - shared_users = fields.Bool(allow_none=True) + default_user = fields.Str(allow_none=True) class JobSchema(BaseSchema): From fbbedb62a95c08b6ce3bf879470199779ea4f0d1 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 8 Mar 2024 10:58:09 -0500 Subject: [PATCH 09/79] Update fixtures.py --- brewtils/test/fixtures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 8be66441..22c60089 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -840,7 +840,6 @@ def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_di "children": [], "metadata": {}, "default_user": None, - "shared_users":True, } From 9aeb929368c77260b57010e031b8918a58e16cbb Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:29:30 -0500 Subject: [PATCH 10/79] added back shared users --- brewtils/models.py | 2 ++ brewtils/test/fixtures.py | 1 + 2 files changed, 3 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 648433a2..71b19d6c 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1413,6 +1413,7 @@ def __init__( children=None, metadata=None, default_user=None, + shared_users=None, ): self.id = id self.name = name @@ -1431,6 +1432,7 @@ def __init__( self.metadata = metadata or {} self.default_user = default_user + self.shared_users = shared_users def __str__(self): return "%s" % self.name diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 22c60089..38908834 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -840,6 +840,7 @@ def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_di "children": [], "metadata": {}, "default_user": None, + "shared_users": True, } From ddd06fdb49a9b9fc03f617f3b7b3366769858440 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:31:57 -0500 Subject: [PATCH 11/79] updated schema --- brewtils/schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index b062851b..b43a1e2a 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -473,6 +473,7 @@ class GardenSchema(BaseSchema): ) metadata = fields.Dict(allow_none=True) default_user = fields.Str(allow_none=True) + shared_users = fields.Bool(allow_none=True) class JobSchema(BaseSchema): From 69c6eeabf0c5414ad833159e2a5358b4977dd07f Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:39:46 -0400 Subject: [PATCH 12/79] Updating Admin roles --- brewtils/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 71b19d6c..8e85d440 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1641,7 +1641,8 @@ class Role(BaseModel): schema = "RoleSchema" PERMISSION_TYPES = { - "ADMIN", + "GARDEN_ADMIN", + "PLUGIN_ADMIN", "OPERATOR", "READ_ONLY", # Default value if not role is provided } From f7bd06fd3fdb1105b9ae99acf6e5140b92cd92a2 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:40:45 -0400 Subject: [PATCH 13/79] updating fixtures --- brewtils/test/fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 38908834..8ce5a37e 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -544,10 +544,10 @@ def bg_remote_user_map(remote_user_map_dict): @pytest.fixture def role_dict(): return { - "permission": "ADMIN", - "description": "ADMIN ROLE", + "permission": "PLUGIN_ADMIN", + "description": "PLUGIN ADMIN ROLE", "id": "1", - "name": "ADMIN_ROLE", + "name": "PLUGIN_ADMIN_ROLE", "scope_gardens": ["FOO"], "scope_namespaces": [], "scope_systems": [], From af9e8a673e4875bbe94535725e76c4c98dc097a8 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:43:28 -0400 Subject: [PATCH 14/79] updating fixtures --- brewtils/test/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 8ce5a37e..8083adfb 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -567,7 +567,7 @@ def user_dict(role_dict, remote_user_map_dict): return { "username": "USERNAME", "password": "HASH", - "roles": ["ADMIN_ROLE"], + "roles": ["PLUGIN_ADMIN_ROLE"], "local_roles": [role_dict], "remote_roles": [role_dict], "remote_user_mapping": [remote_user_map_dict], From da29301f6b8d90fda8327ba31e7ec5d405a80196 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 12 Mar 2024 06:35:34 -0400 Subject: [PATCH 15/79] fixed testing --- brewtils/models.py | 3 ++- brewtils/schemas.py | 4 ++-- brewtils/test/comparable.py | 1 + test/models_test.py | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 8e85d440..3bf80e9c 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1676,12 +1676,13 @@ def __str__(self): def __repr__(self): return ( - "" ) % ( self.id, self.name, + self.description, self.permission, self.scope_gardens, self.scope_namespaces, diff --git a/brewtils/schemas.py b/brewtils/schemas.py index b43a1e2a..991f7391 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -558,10 +558,10 @@ class ResolvableSchema(BaseSchema): class RoleSchema(BaseSchema): + permission = fields.Str() + description = fields.Str(allow_none=True) id = fields.Str(allow_none=True) name = fields.Str() - description = fields.Str(allow_none=True) - permission = fields.Str() scope_gardens = fields.List(fields.Str(), allow_none=True) scope_namespaces = fields.List(fields.Str(), allow_none=True) scope_systems = fields.List(fields.Str(), allow_none=True) diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index f1197ed6..377720ec 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -245,6 +245,7 @@ def assert_user_equal(obj1, obj2, do_raise=False): obj2, expected_type=User, deep_fields={ + "local_roles": partial(assert_role_equal, do_raise=True), "remote_roles": partial(assert_role_equal, do_raise=True), "remote_user_mapping": partial(assert_remote_user_map_equal, do_raise=True), }, diff --git a/test/models_test.py b/test/models_test.py index 6b136600..76744edf 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -548,14 +548,14 @@ def test_repr(self, user): class TestRole(object): @pytest.fixture def role(self): - return Role(name="bg-admin", permission="ADMIN") + return Role(name="bg-admin", permission="PLUGIN_ADMIN") def test_str(self, role): assert str(role) == "bg-admin" def test_repr(self, role): assert repr(role) == ( - "" ) From 6296b726b73480c34f17cec376b8fa7f391905c7 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:50:10 +0000 Subject: [PATCH 16/79] UserToken update --- brewtils/models.py | 19 +++++++++++-------- brewtils/schema_parser.py | 2 +- brewtils/schemas.py | 3 ++- brewtils/test/comparable.py | 14 +++++++++++++- brewtils/test/fixtures.py | 20 +++++++++++++++++++ test/schema_parser_test.py | 38 +++++++++++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 3bf80e9c..096db57a 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1109,23 +1109,26 @@ class UserToken(BaseModel): def __init__( self, id=None, # noqa # shadows built-in + uuid=None, issued=None, - expires=None, - payload=None, + expires_at=None, + user=None, ): self.id = id + self.uuid = uuid self.issued = issued - self.expires = expires - self.payload = payload or {} + self.expires_at = expires_at + self.user = user or {} def __str__(self): - return "%s" % self.payload + return "%s" % self.user def __repr__(self): - return "" % ( + return "" % ( + self.uuid, self.issued, - self.expires, - self.payload, + self.expires_at, + self.user, ) diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 74830a57..03268295 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -313,7 +313,7 @@ def parse_user_token(cls, user_token, from_string=False, **kwargs): A UserToken object """ return cls.parse( - refresh_token, + user_token, brewtils.models.UserToken, from_string=from_string, **kwargs diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 991f7391..2f1739da 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -399,9 +399,10 @@ class QueueSchema(BaseSchema): class UserTokenSchema(BaseSchema): + id = fields.Str(allow_none=True) uuid = fields.Str(allow_none=True) issued = DateTime(allow_none=True, format="epoch", example="1500065932000") - expires = DateTime(allow_none=True, format="epoch", example="1500065932000") + expires_at = DateTime(allow_none=True, format="epoch", example="1500065932000") user = fields.Nested("UserSchema") diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 377720ec..911622fc 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -30,6 +30,7 @@ Parameter, PatchOperation, User, + UserToken, Queue, RemoteUserMap, Request, @@ -51,6 +52,7 @@ "assert_trigger_equal", "assert_command_equal", "assert_parameter_equal", + "assert_user_token_equal", "assert_user_equal", "assert_remote_user_map_equal", "assert_request_equal", @@ -238,7 +240,6 @@ def assert_event_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) - def assert_user_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -252,6 +253,17 @@ def assert_user_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) +def assert_user_token_equal(obj1, obj2, do_raise=False): + return _assert_wrapper( + obj1, + obj2, + expected_type=UserToken, + deep_fields={ + "user": partial(assert_user_equal, do_raise=True), + }, + do_raise=do_raise, + ) + def assert_request_equal(obj1, obj2, do_raise=False): """Assert that two requests are 'equal'. diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 8083adfb..359db929 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -30,6 +30,7 @@ Runner, System, User, + UserToken, Role, ) @@ -540,6 +541,25 @@ def remote_user_map_dict(): def bg_remote_user_map(remote_user_map_dict): return RemoteUserMap(**remote_user_map_dict) +@pytest.fixture +def user_token_dict(user_dict, ts_epoch): + return { + "id": "1", + "uuid": "11111111-2222-4444-5555-66666666666", + "issued": ts_epoch, + "expires_at": ts_epoch, + "user": user_dict, + } + + +@pytest.fixture +def bg_user_token(user_token_dict, bg_user, ts_dt): + dict_copy = copy.deepcopy(user_token_dict) + dict_copy["user"] = bg_user + dict_copy["issued"] = ts_dt + dict_copy["expires_at"] = ts_dt + return UserToken(**dict_copy) + @pytest.fixture def role_dict(): diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index ab9dab8d..2552b814 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -22,6 +22,7 @@ assert_parameter_equal, assert_patch_equal, assert_user_equal, + assert_user_token_equal, assert_queue_equal, assert_remote_user_map_equal, assert_request_equal, @@ -127,6 +128,12 @@ def test_no_modify(self, system_dict): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.UserToken, + lazy_fixture("user_token_dict"), + assert_user_token_equal, + lazy_fixture("bg_user_token"), + ), ( brewtils.models.RemoteUserMap, lazy_fixture("remote_user_map_dict"), @@ -261,6 +268,12 @@ def test_single_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_user_token", + lazy_fixture("user_token_dict"), + assert_user_token_equal, + lazy_fixture("bg_user_token"), + ), ( "parse_remote_user_map", lazy_fixture("remote_user_map_dict"), @@ -400,6 +413,12 @@ def test_single_specific_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.UserToken, + lazy_fixture("user_token_dict"), + assert_user_token_equal, + lazy_fixture("bg_user_token"), + ), ( brewtils.models.RemoteUserMap, lazy_fixture("remote_user_map_dict"), @@ -532,6 +551,12 @@ def test_many(self, model, data, assertion, expected): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_user_token", + lazy_fixture("user_token_dict"), + assert_user_token_equal, + lazy_fixture("bg_user_token"), + ), ( "parse_remote_user_map", lazy_fixture("remote_user_map_dict"), @@ -631,6 +656,7 @@ class TestSerialize(object): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), @@ -695,6 +721,11 @@ def test_single(self, model, expected): lazy_fixture("bg_user"), lazy_fixture("user_dict"), ), + ( + "serialize_user_token", + lazy_fixture("bg_user_token"), + lazy_fixture("user_token_dict"), + ), ( "serialize_remote_user_map", lazy_fixture("bg_remote_user_map"), @@ -766,6 +797,7 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), @@ -847,6 +879,11 @@ class TestRoundTrip(object): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.UserToken, + assert_user_token_equal, + lazy_fixture("bg_user_token"), + ), ( brewtils.models.RemoteUserMap, assert_remote_user_map_equal, @@ -891,6 +928,7 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.Event, lazy_fixture("event_dict")), (brewtils.models.Queue, lazy_fixture("queue_dict")), (brewtils.models.User, lazy_fixture("user_dict")), + (brewtils.models.UserToken, lazy_fixture("user_token_dict")), (brewtils.models.Role, lazy_fixture("role_dict")), (brewtils.models.Job, lazy_fixture("job_dict")), (brewtils.models.Job, lazy_fixture("cron_job_dict")), From 7c49af0b74306d1f89f43f1bbd085ed7a1d64a68 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:51:59 -0400 Subject: [PATCH 17/79] Updated UserToken --- brewtils/models.py | 2 +- brewtils/schemas.py | 2 +- brewtils/test/fixtures.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 096db57a..e9ea318b 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1116,7 +1116,7 @@ def __init__( ): self.id = id self.uuid = uuid - self.issued = issued + self.issued_at = issued self.expires_at = expires_at self.user = user or {} diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 2f1739da..009955b9 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -401,7 +401,7 @@ class QueueSchema(BaseSchema): class UserTokenSchema(BaseSchema): id = fields.Str(allow_none=True) uuid = fields.Str(allow_none=True) - issued = DateTime(allow_none=True, format="epoch", example="1500065932000") + issued_at = DateTime(allow_none=True, format="epoch", example="1500065932000") expires_at = DateTime(allow_none=True, format="epoch", example="1500065932000") user = fields.Nested("UserSchema") diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 359db929..8a0c4e68 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -546,7 +546,7 @@ def user_token_dict(user_dict, ts_epoch): return { "id": "1", "uuid": "11111111-2222-4444-5555-66666666666", - "issued": ts_epoch, + "issued_at": ts_epoch, "expires_at": ts_epoch, "user": user_dict, } @@ -556,7 +556,7 @@ def user_token_dict(user_dict, ts_epoch): def bg_user_token(user_token_dict, bg_user, ts_dt): dict_copy = copy.deepcopy(user_token_dict) dict_copy["user"] = bg_user - dict_copy["issued"] = ts_dt + dict_copy["issued_at"] = ts_dt dict_copy["expires_at"] = ts_dt return UserToken(**dict_copy) From f84eead0ef6b7ad83799cc00c88ab428ab805aed Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:53:37 -0400 Subject: [PATCH 18/79] Fixed model --- brewtils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index e9ea318b..9d999d19 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1110,7 +1110,7 @@ def __init__( self, id=None, # noqa # shadows built-in uuid=None, - issued=None, + issued_at=None, expires_at=None, user=None, ): From 6f0cbeed3b345be129713aaa1f54c8e194d93411 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:55:02 -0400 Subject: [PATCH 19/79] Fixed Models --- brewtils/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9d999d19..3f23e81f 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1124,9 +1124,9 @@ def __str__(self): return "%s" % self.user def __repr__(self): - return "" % ( + return "" % ( self.uuid, - self.issued, + self.issued_at, self.expires_at, self.user, ) From 14d02765782c42dce10c55ddfd22d02efe348a21 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:58:39 +0000 Subject: [PATCH 20/79] Fixed model --- brewtils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 3f23e81f..0968adba 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1116,7 +1116,7 @@ def __init__( ): self.id = id self.uuid = uuid - self.issued_at = issued + self.issued_at = issued_at self.expires_at = expires_at self.user = user or {} From 11a4772de2484bbcab122d3a9573e995226b8747 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:28:14 -0400 Subject: [PATCH 21/79] User ID field --- brewtils/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 0968adba..194453b0 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1612,6 +1612,7 @@ class User(BaseModel): def __init__( self, + id, username, password=None, roles=None, @@ -1621,6 +1622,7 @@ def __init__( remote_user_mapping=None, metadata=None, ): + self.id = id self.username = username self.password = password self.roles = roles or [] From 562f8b7e75f6aca47bc24dde37d12a3da3f6e463 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:32:31 -0400 Subject: [PATCH 22/79] fixed user id --- brewtils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 194453b0..9cc1f6aa 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1612,8 +1612,8 @@ class User(BaseModel): def __init__( self, - id, username, + id = None, password=None, roles=None, local_roles=None, From d26a394876225ee892c38a928c275e1281ec6d31 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:41:53 -0400 Subject: [PATCH 23/79] All none on Users --- brewtils/models.py | 2 +- brewtils/schemas.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9cc1f6aa..9f9f0417 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1612,7 +1612,7 @@ class User(BaseModel): def __init__( self, - username, + username = None, id = None, password=None, roles=None, diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 009955b9..3f8cfe05 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -578,8 +578,8 @@ class RemoteUserMapSchema(BaseSchema): class UserSchema(BaseSchema): id = fields.Str(allow_none=True) - username = fields.Str() - password = fields.Str() + username = fields.Str(allow_none=True) + password = fields.Str(allow_none=True) roles = fields.List(fields.Str(), allow_none=True) local_roles = fields.List(fields.Nested(RoleSchema())) remote_roles = fields.List(fields.Nested(RoleSchema())) From 9e7be7006db01c08ea647a6b2ba32456ce48ffc9 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:31:36 -0400 Subject: [PATCH 24/79] Usertoken stores username --- brewtils/models.py | 10 +++++----- brewtils/schemas.py | 2 +- brewtils/test/fixtures.py | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9f9f0417..88bf76d1 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1112,23 +1112,23 @@ def __init__( uuid=None, issued_at=None, expires_at=None, - user=None, + username=None, ): self.id = id self.uuid = uuid self.issued_at = issued_at self.expires_at = expires_at - self.user = user or {} + self.username = username def __str__(self): - return "%s" % self.user + return "%s" % self.username def __repr__(self): - return "" % ( + return "" % ( self.uuid, self.issued_at, self.expires_at, - self.user, + self.username, ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 3f8cfe05..351d2bdb 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -403,7 +403,7 @@ class UserTokenSchema(BaseSchema): uuid = fields.Str(allow_none=True) issued_at = DateTime(allow_none=True, format="epoch", example="1500065932000") expires_at = DateTime(allow_none=True, format="epoch", example="1500065932000") - user = fields.Nested("UserSchema") + username = fields.Str(allow_none=True) class DateTriggerSchema(BaseSchema): diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 8a0c4e68..79c7a047 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -548,14 +548,13 @@ def user_token_dict(user_dict, ts_epoch): "uuid": "11111111-2222-4444-5555-66666666666", "issued_at": ts_epoch, "expires_at": ts_epoch, - "user": user_dict, + "username": "USERNAME", } @pytest.fixture -def bg_user_token(user_token_dict, bg_user, ts_dt): +def bg_user_token(user_token_dict, ts_dt): dict_copy = copy.deepcopy(user_token_dict) - dict_copy["user"] = bg_user dict_copy["issued_at"] = ts_dt dict_copy["expires_at"] = ts_dt return UserToken(**dict_copy) From 02d0d128d35f23ab340ce68fb48e1a02204d3a1b Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:25:42 -0400 Subject: [PATCH 25/79] Allow none on roles --- brewtils/schemas.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 351d2bdb..015e9d0e 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -581,8 +581,8 @@ class UserSchema(BaseSchema): username = fields.Str(allow_none=True) password = fields.Str(allow_none=True) roles = fields.List(fields.Str(), allow_none=True) - local_roles = fields.List(fields.Nested(RoleSchema())) - remote_roles = fields.List(fields.Nested(RoleSchema())) + local_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) + remote_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) From e1f44d774561ff7366734b0114a861dbf0294e86 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:46:47 -0400 Subject: [PATCH 26/79] Adding Remote Roles --- brewtils/models.py | 2 ++ brewtils/schemas.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 88bf76d1..ba2bcf10 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1697,6 +1697,8 @@ def __repr__(self): self.scope_commands, ) +class RemoteRole(Role): + pass class RemoteUserMap(BaseModel): schema = "RemoteUserMapSchema" diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 015e9d0e..8736a20a 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -587,6 +587,9 @@ class UserSchema(BaseSchema): is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) +class RemoteUserSchema(UserSchema): + pass + model_schema_map.update( { @@ -618,6 +621,7 @@ class UserSchema(BaseSchema): "Runner": RunnerSchema, "Resolvable": ResolvableSchema, "Role": RoleSchema, + "RemoteRole": RemoteRoleSchema, "User": UserSchema, "RemoteUserMap": RemoteUserMapSchema, # Compatibility for the Job trigger types From 282669751a60855c0d07c09158fb3f461dcd2527 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 21 Mar 2024 09:58:39 -0400 Subject: [PATCH 27/79] Fixed schemas --- brewtils/schemas.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 8736a20a..530343a4 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -570,6 +570,8 @@ class RoleSchema(BaseSchema): scope_versions = fields.List(fields.Str(), allow_none=True) scope_commands = fields.List(fields.Str(), allow_none=True) +class RemoteRoleSchema(RoleSchema): + pass class RemoteUserMapSchema(BaseSchema): target_garden = fields.Str() @@ -582,13 +584,12 @@ class UserSchema(BaseSchema): password = fields.Str(allow_none=True) roles = fields.List(fields.Str(), allow_none=True) local_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) - remote_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) + remote_roles = fields.List(fields.Nested(RemoteRoleSchema()), allow_none=True) remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) -class RemoteUserSchema(UserSchema): - pass + model_schema_map.update( From 181557bb63ddcdf0236309d73221838a5ed3e87f Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 21 Mar 2024 10:49:13 -0400 Subject: [PATCH 28/79] Add Remote Role Schema parsing --- brewtils/schema_parser.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 03268295..3de95c74 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -49,6 +49,7 @@ class SchemaParser(object): "RunnerSchema": brewtils.models.Runner, "ResolvableSchema": brewtils.models.Resolvable, "RoleSchema": brewtils.models.Role, + "RemoteRoleSchema": brewtils.models.RemoteRole, "UserSchema": brewtils.models.User, "RemoteUserMapSchema": brewtils.models.RemoteUserMap, } @@ -280,6 +281,20 @@ def parse_role(cls, role, from_string=False, **kwargs): A Role object """ return cls.parse(role, brewtils.models.Role, from_string=from_string, **kwargs) + + @classmethod + def parse_remote_role(cls, role, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a role model object + + Args: + role: 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 Role object + """ + return cls.parse(role, brewtils.models.RemoteRole, from_string=from_string, **kwargs) @classmethod def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): @@ -722,6 +737,23 @@ def serialize_role(cls, role, to_string=True, **kwargs): return cls.serialize( role, to_string=to_string, schema_name=brewtils.models.Role.schema, **kwargs ) + + @classmethod + def serialize_remote_role(cls, role, to_string=True, **kwargs): + """Convert a role model into serialized form + + Args: + role: The role 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 + """ + return cls.serialize( + role, to_string=to_string, schema_name=brewtils.models.RemoteRole.schema, **kwargs + ) @classmethod def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): From e591cd646ac98e2e3c207a76be15a6187b0ade04 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:00:47 -0400 Subject: [PATCH 29/79] Updating testing --- brewtils/schemas.py | 12 +++++++++++- brewtils/test/comparable.py | 11 ++++++++++- brewtils/test/fixtures.py | 30 ++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index e724f959..0c769d01 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -573,7 +573,17 @@ class RoleSchema(BaseSchema): scope_commands = fields.List(fields.Str(), allow_none=True) class RemoteRoleSchema(RoleSchema): - pass + permission = fields.Str() + description = fields.Str(allow_none=True) + id = fields.Str(allow_none=True) + name = fields.Str() + scope_gardens = fields.List(fields.Str(), allow_none=True) + scope_namespaces = fields.List(fields.Str(), allow_none=True) + scope_systems = fields.List(fields.Str(), allow_none=True) + scope_instances = fields.List(fields.Str(), allow_none=True) + scope_versions = fields.List(fields.Str(), allow_none=True) + scope_commands = fields.List(fields.Str(), allow_none=True) + class RemoteUserMapSchema(BaseSchema): target_garden = fields.Str() diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 2123b2f2..43a4add7 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -33,6 +33,7 @@ UserToken, Queue, RemoteUserMap, + RemoteRole, Request, RequestFile, RequestTemplate, @@ -252,7 +253,7 @@ def assert_user_equal(obj1, obj2, do_raise=False): expected_type=User, deep_fields={ "local_roles": partial(assert_role_equal, do_raise=True), - "remote_roles": partial(assert_role_equal, do_raise=True), + "remote_roles": partial(assert_remote_role_equal, do_raise=True), "remote_user_mapping": partial(assert_remote_user_map_equal, do_raise=True), }, do_raise=do_raise, @@ -326,6 +327,14 @@ def assert_role_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) +def assert_remote_role_equal(obj1, obj2, do_raise=False): + return _assert_wrapper( + obj1, + obj2, + expected_type=RemoteRole, + do_raise=do_raise, + ) + def assert_system_equal(obj1, obj2, do_raise=False): return _assert_wrapper( diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 6b4b2a9a..6eb113b3 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -23,6 +23,7 @@ PatchOperation, Queue, RemoteUserMap, + RemoteRole, Request, RequestFile, RequestTemplate, @@ -578,19 +579,40 @@ def role_dict(): } +@pytest.fixture +def bg_remote_role(role_dict): + return RemoteRole(**role_dict) + +@pytest.fixture +def remote_role_dict(): + return { + "permission": "PLUGIN_ADMIN", + "description": "PLUGIN ADMIN ROLE", + "id": "1", + "name": "PLUGIN_ADMIN_ROLE", + "scope_gardens": ["FOO"], + "scope_namespaces": [], + "scope_systems": [], + "scope_instances": [], + "scope_versions": [], + "scope_commands": [], + } + + @pytest.fixture def bg_role(role_dict): return Role(**role_dict) @pytest.fixture -def user_dict(role_dict, remote_user_map_dict): +def user_dict(role_dict, remote_role_dict, remote_user_map_dict): return { + "id": "1", "username": "USERNAME", "password": "HASH", "roles": ["PLUGIN_ADMIN_ROLE"], "local_roles": [role_dict], - "remote_roles": [role_dict], + "remote_roles": [remote_role_dict], "remote_user_mapping": [remote_user_map_dict], "is_remote": False, "metadata": {}, @@ -598,9 +620,9 @@ def user_dict(role_dict, remote_user_map_dict): @pytest.fixture -def bg_user(user_dict, bg_role, bg_remote_user_map): +def bg_user(user_dict, bg_role, bg_remote_role, bg_remote_user_map): dict_copy = copy.deepcopy(user_dict) - dict_copy["remote_roles"] = [bg_role] + dict_copy["remote_roles"] = [bg_remote_role] dict_copy["local_roles"] = [bg_role] dict_copy["remote_user_mapping"] = [bg_remote_user_map] return User(**dict_copy) From 121d7c487336f5617da83ce576e3fff5db342ce2 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:14:05 -0400 Subject: [PATCH 30/79] Updating more testing --- brewtils/models.py | 2 +- test/schema_parser_test.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index f74ea152..8a5b70d7 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1703,7 +1703,7 @@ def __repr__(self): ) class RemoteRole(Role): - pass + schema = "RemoteRoleSchema" class RemoteUserMap(BaseModel): schema = "RemoteUserMapSchema" diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index 59b899f7..d575980b 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -25,6 +25,7 @@ assert_user_token_equal, assert_queue_equal, assert_remote_user_map_equal, + assert_remote_role_equal, assert_request_equal, assert_request_file_equal, assert_resolvable_equal, @@ -148,6 +149,12 @@ def test_no_modify(self, system_dict): assert_role_equal, lazy_fixture("bg_role"), ), + ( + brewtils.models.RemoteRole, + lazy_fixture("remote_role_dict"), + assert_remote_role_equal, + lazy_fixture("bg_remote_role"), + ), ( brewtils.models.Job, lazy_fixture("job_dict"), @@ -300,6 +307,12 @@ def test_single_from_string(self): assert_role_equal, lazy_fixture("bg_role"), ), + ( + "parse_remote_role", + lazy_fixture("remote_role_dict"), + assert_remote_role_equal, + lazy_fixture("bg_remote_role"), + ), ( "parse_job", lazy_fixture("job_dict"), @@ -457,6 +470,12 @@ def test_single_specific_from_string(self): assert_role_equal, lazy_fixture("bg_role"), ), + ( + brewtils.models.RemoteRole, + lazy_fixture("remote_role_dict"), + assert_remote_role_equal, + lazy_fixture("bg_remote_role"), + ), ( brewtils.models.Job, lazy_fixture("job_dict"), @@ -607,6 +626,12 @@ def test_many(self, model, data, assertion, expected): assert_role_equal, lazy_fixture("bg_role"), ), + ( + "parse_remote_role", + lazy_fixture("remote_role_dict"), + assert_remote_role_equal, + lazy_fixture("bg_remote_role"), + ), ( "parse_job", lazy_fixture("job_dict"), @@ -709,6 +734,7 @@ class TestSerialize(object): (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), + (lazy_fixture("bg_remote_role"), lazy_fixture("remote_role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -788,6 +814,11 @@ def test_single(self, model, expected): lazy_fixture("bg_role"), lazy_fixture("role_dict"), ), + ( + "serialize_remote_role", + lazy_fixture("bg_remote_role"), + lazy_fixture("remote_role_dict"), + ), ("serialize_job", lazy_fixture("bg_job"), lazy_fixture("job_dict")), ( "serialize_job", @@ -862,6 +893,7 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), + (lazy_fixture("bg_remote_role"), lazy_fixture("remote_role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -954,6 +986,7 @@ class TestRoundTrip(object): lazy_fixture("bg_remote_user_map"), ), (brewtils.models.Role, assert_role_equal, lazy_fixture("bg_role")), + (brewtils.models.RemoteRole, assert_remote_role_equal, lazy_fixture("bg_remote_role")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_cron_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_interval_job")), @@ -1000,6 +1033,7 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.User, lazy_fixture("user_dict")), (brewtils.models.UserToken, lazy_fixture("user_token_dict")), (brewtils.models.Role, lazy_fixture("role_dict")), + (brewtils.models.RemoteRole, lazy_fixture("remote_role_dict")), (brewtils.models.Job, lazy_fixture("job_dict")), (brewtils.models.Job, lazy_fixture("cron_job_dict")), (brewtils.models.Job, lazy_fixture("interval_job_dict")), From 3503afdedd1f4fbdb42399e9b2a54d639a45339d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:51:41 -0400 Subject: [PATCH 31/79] combining roles schemas --- brewtils/schemas.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 0c769d01..b2262559 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -573,16 +573,7 @@ class RoleSchema(BaseSchema): scope_commands = fields.List(fields.Str(), allow_none=True) class RemoteRoleSchema(RoleSchema): - permission = fields.Str() - description = fields.Str(allow_none=True) - id = fields.Str(allow_none=True) - name = fields.Str() - scope_gardens = fields.List(fields.Str(), allow_none=True) - scope_namespaces = fields.List(fields.Str(), allow_none=True) - scope_systems = fields.List(fields.Str(), allow_none=True) - scope_instances = fields.List(fields.Str(), allow_none=True) - scope_versions = fields.List(fields.Str(), allow_none=True) - scope_commands = fields.List(fields.Str(), allow_none=True) + pass class RemoteUserMapSchema(BaseSchema): From ed9319900731323135afe2fed6744cfa3f63fb14 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:46:03 -0400 Subject: [PATCH 32/79] Add protected to roles --- brewtils/models.py | 2 ++ brewtils/schemas.py | 1 + brewtils/test/fixtures.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 8a5b70d7..2eb861d6 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1669,6 +1669,7 @@ def __init__( scope_instances=None, scope_versions=None, scope_commands=None, + protected=False, ): self.permission = permission or "READ_ONLY" self.description = description @@ -1680,6 +1681,7 @@ def __init__( self.scope_instances = scope_instances or [] self.scope_versions = scope_versions or [] self.scope_commands = scope_commands or [] + self.protected = protected def __str__(self): return "%s" % (self.name) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index b2262559..0aaafedc 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -571,6 +571,7 @@ class RoleSchema(BaseSchema): scope_instances = fields.List(fields.Str(), allow_none=True) scope_versions = fields.List(fields.Str(), allow_none=True) scope_commands = fields.List(fields.Str(), allow_none=True) + protected = fields.Boolean(allow_none=True) class RemoteRoleSchema(RoleSchema): pass diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 6eb113b3..3cc919d8 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -576,6 +576,7 @@ def role_dict(): "scope_instances": [], "scope_versions": [], "scope_commands": [], + "protected": False, } @@ -596,6 +597,7 @@ def remote_role_dict(): "scope_instances": [], "scope_versions": [], "scope_commands": [], + "protected": False, } From 014cba49de83bec3716cf87d5242063cc7c08bbd Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:39:39 -0400 Subject: [PATCH 33/79] Adding is protected to user accounts --- brewtils/models.py | 2 ++ brewtils/schemas.py | 1 + brewtils/test/fixtures.py | 1 + 3 files changed, 4 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 2eb861d6..ebd673c7 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1626,6 +1626,7 @@ def __init__( is_remote=False, remote_user_mapping=None, metadata=None, + is_protected=False, ): self.id = id self.username = username @@ -1636,6 +1637,7 @@ def __init__( self.is_remote = is_remote self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata + self.is_protected = is_protected def __str__(self): return "%s: %s" % (self.username, self.roles) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 0aaafedc..d93a066e 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -606,6 +606,7 @@ class UserSchema(BaseSchema): remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) + is_protected = fields.Boolean(allow_none=True) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 3cc919d8..afd920f8 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -618,6 +618,7 @@ def user_dict(role_dict, remote_role_dict, remote_user_map_dict): "remote_user_mapping": [remote_user_map_dict], "is_remote": False, "metadata": {}, + "is_protected": False, } From 3865bd7341716aeed21eec0aaea564046daa6bbd Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:42:18 -0400 Subject: [PATCH 34/79] Changed is_protected to protected --- brewtils/models.py | 4 ++-- brewtils/schemas.py | 2 +- brewtils/test/fixtures.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index ebd673c7..66c55190 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1626,7 +1626,7 @@ def __init__( is_remote=False, remote_user_mapping=None, metadata=None, - is_protected=False, + protected=False, ): self.id = id self.username = username @@ -1637,7 +1637,7 @@ def __init__( self.is_remote = is_remote self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata - self.is_protected = is_protected + self.protected = is_protected def __str__(self): return "%s: %s" % (self.username, self.roles) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index d93a066e..f4d3269f 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -606,7 +606,7 @@ class UserSchema(BaseSchema): remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) - is_protected = fields.Boolean(allow_none=True) + protected = fields.Boolean(allow_none=True) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index afd920f8..09a845b1 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -618,7 +618,7 @@ def user_dict(role_dict, remote_role_dict, remote_user_map_dict): "remote_user_mapping": [remote_user_map_dict], "is_remote": False, "metadata": {}, - "is_protected": False, + "protected": False, } From 4aae1dc1e8b8578715334782120c9a8b4cd06ec7 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:03:03 -0400 Subject: [PATCH 35/79] fixed model --- brewtils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 66c55190..b7ac64fb 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1637,7 +1637,7 @@ def __init__( self.is_remote = is_remote self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata - self.protected = is_protected + self.protected = protected def __str__(self): return "%s: %s" % (self.username, self.roles) From 198380b83cbfd6c0257dbcb5e465bd5e07db5f5e Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:01:13 -0400 Subject: [PATCH 36/79] Update Requester on child requests --- brewtils/request_handling.py | 1 + brewtils/rest/system_client.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/brewtils/request_handling.py b/brewtils/request_handling.py index 259f2c0b..7ec877d4 100644 --- a/brewtils/request_handling.py +++ b/brewtils/request_handling.py @@ -61,6 +61,7 @@ def process_command(self, request): if parent_request: request.parent = Request(id=str(parent_request.id)) + request.requester = parent_request.requester request.has_parent = True # check for kwargs on the target command diff --git a/brewtils/rest/system_client.py b/brewtils/rest/system_client.py index 7b7a0064..75569a6d 100644 --- a/brewtils/rest/system_client.py +++ b/brewtils/rest/system_client.py @@ -535,6 +535,10 @@ def _wait_for_request(self, request, raise_on_error, timeout): brewtils.plugin.request_context, "current_request", None ) request.has_parent = request.parent is not None + if request.has_parent: + request.requester = getattr( + brewtils.plugin.request_context.current_request, "requester", None + ) ec = EasyClient( bg_host=brewtils.plugin.CONFIG.bg_host, bg_port=brewtils.plugin.CONFIG.bg_port, From f31dc469f0f49acf95bdd0cfc67b8898f37e3751 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:22:01 -0400 Subject: [PATCH 37/79] Set requester from parent --- brewtils/rest/system_client.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/brewtils/rest/system_client.py b/brewtils/rest/system_client.py index 75569a6d..d48b99bb 100644 --- a/brewtils/rest/system_client.py +++ b/brewtils/rest/system_client.py @@ -535,10 +535,7 @@ def _wait_for_request(self, request, raise_on_error, timeout): brewtils.plugin.request_context, "current_request", None ) request.has_parent = request.parent is not None - if request.has_parent: - request.requester = getattr( - brewtils.plugin.request_context.current_request, "requester", None - ) + ec = EasyClient( bg_host=brewtils.plugin.CONFIG.bg_host, bg_port=brewtils.plugin.CONFIG.bg_port, @@ -586,6 +583,11 @@ def _construct_bg_request(self, **kwargs): topic = kwargs.pop("_topic", None) propagate = kwargs.pop("_propagate", None) + if parent: + requester = getattr(brewtils.plugin.request_context.current_request, "requester", None) + else: + requester = None + if system_display: metadata["system_display_name"] = system_display if publish: @@ -616,6 +618,7 @@ def _construct_bg_request(self, **kwargs): parent=parent, metadata=metadata, parameters=kwargs, + requester=requester, ) request.parameters = self._resolve_parameters(command, request) From 5fb7344f9716f8b22504850ec8d152f15e7f8e85 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:33:00 -0400 Subject: [PATCH 38/79] add file generated flag --- brewtils/models.py | 8 ++++++-- brewtils/schemas.py | 2 ++ brewtils/test/fixtures.py | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index b7ac64fb..afafa7b1 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1622,11 +1622,12 @@ def __init__( password=None, roles=None, local_roles=None, - remote_roles=None, - is_remote=False, + remote_roles=None, remote_user_mapping=None, metadata=None, + is_remote=False, protected=False, + file_generated=False, ): self.id = id self.username = username @@ -1638,6 +1639,7 @@ def __init__( self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata self.protected = protected + self.file_generated = file_generated def __str__(self): return "%s: %s" % (self.username, self.roles) @@ -1672,6 +1674,7 @@ def __init__( scope_versions=None, scope_commands=None, protected=False, + file_generated=False, ): self.permission = permission or "READ_ONLY" self.description = description @@ -1684,6 +1687,7 @@ def __init__( self.scope_versions = scope_versions or [] self.scope_commands = scope_commands or [] self.protected = protected + self.file_generated = file_generated def __str__(self): return "%s" % (self.name) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index f4d3269f..470f07b3 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -572,6 +572,7 @@ class RoleSchema(BaseSchema): scope_versions = fields.List(fields.Str(), allow_none=True) scope_commands = fields.List(fields.Str(), allow_none=True) protected = fields.Boolean(allow_none=True) + file_generated = fields.Boolean(allow_none=True) class RemoteRoleSchema(RoleSchema): pass @@ -607,6 +608,7 @@ class UserSchema(BaseSchema): is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) protected = fields.Boolean(allow_none=True) + file_generated = fields.Boolean(allow_none=True) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 09a845b1..85b89937 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -577,6 +577,7 @@ def role_dict(): "scope_versions": [], "scope_commands": [], "protected": False, + "file_generated": False, } @@ -598,6 +599,7 @@ def remote_role_dict(): "scope_versions": [], "scope_commands": [], "protected": False, + "file_generated": False, } @@ -619,6 +621,7 @@ def user_dict(role_dict, remote_role_dict, remote_user_map_dict): "is_remote": False, "metadata": {}, "protected": False, + "file_generated": False, } From fdb17e39f147bd2df8e7261dad9d2bde6486b31a Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:51:45 -0400 Subject: [PATCH 39/79] Load Tokens first if configured --- brewtils/rest/client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/brewtils/rest/client.py b/brewtils/rest/client.py index 39b530bc..b9e348ee 100644 --- a/brewtils/rest/client.py +++ b/brewtils/rest/client.py @@ -23,12 +23,17 @@ def enable_auth(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): + + # Load Token initially if authentication settings are provided + if not self.session.headers.get("Authorization") and ((self.username and self.password) or self.client_cert): + self.get_tokens() + original_response = method(self, *args, **kwargs) if original_response.status_code != 401: return original_response - # Try to use credentials + # Refresh Token if expired and caused 401 if (self.username and self.password) or self.client_cert: credential_response = self.get_tokens() From 12e6c4070872bea2c6dd155fed48317cb89e1031 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:20:24 +0000 Subject: [PATCH 40/79] Add Replication Model --- brewtils/models.py | 15 +++++++++++++++ brewtils/schema_parser.py | 37 +++++++++++++++++++++++++++++++++++++ brewtils/schemas.py | 7 +++++++ brewtils/test/comparable.py | 5 ++++- brewtils/test/fixtures.py | 16 +++++++++++++++- test/schema_parser_test.py | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 7bfbcbef..6e3ffe6d 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -39,6 +39,7 @@ "Resolvable", "Subscriber", "Topic", + "Replication" ] @@ -1723,3 +1724,17 @@ def __repr__(self): self.name, self.subscribers, ) + +class Replication(BaseModel): + schema = "ReplicationSchema" + + def __init__(self, id=None, replication_id=None, heartbeat=None): + self.id = id + self.replication_id = replication_id + self.heartbeat = heartbeat + + def __str__(self): + return "%s:%s" % (self.replication_id, self.heartbeat) + + def __repr__(self): + return "" % (self.replication_id, self.heartbeat) \ No newline at end of file diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index cdbc21f4..b8861935 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -52,6 +52,7 @@ class SchemaParser(object): "ResolvableSchema": brewtils.models.Resolvable, "SubscriberSchema": brewtils.models.Subscriber, "TopicSchema": brewtils.models.Topic, + "ReplicationSchema": brewtils.models.Replication, } logger = logging.getLogger(__name__) @@ -436,6 +437,22 @@ def parse_topic(cls, topic, from_string=False, **kwargs): return cls.parse( topic, brewtils.models.Topic, from_string=from_string, **kwargs ) + + @classmethod + def parse_replication(cls, replication, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a replication model object + + Args: + replication: 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 Replication object + """ + return cls.parse( + replication, brewtils.models.Replication, from_string=from_string, **kwargs + ) @classmethod def parse( @@ -942,6 +959,26 @@ def serialize_topic(cls, topic, to_string=True, **kwargs): schema_name=brewtils.models.Topic.schema, **kwargs ) + + @classmethod + def serialize_replication(cls, replication, to_string=True, **kwargs): + """Convert a replication model into serialized form + + Args: + replication: The replication 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 replication + """ + return cls.serialize( + replication, + to_string=to_string, + schema_name=brewtils.models.Replication.schema, + **kwargs + ) @classmethod def serialize( diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 6bfe08c1..85825a0d 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -48,6 +48,7 @@ "SystemDomainIdentifierSchema", "SubscriberSchema", "TopicSchema", + "ReplicationSchema", ] # This will be updated after all the schema classes are defined @@ -640,6 +641,11 @@ class TopicSchema(BaseSchema): name = fields.Str(allow_none=True) subscribers = fields.List(fields.Nested(SubscriberSchema, allow_none=True)) +class ReplicationSchema(BaseSchema): + id = fields.Str(allow_none=True) + replication_id = fields.Str(allow_none=True) + heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000") + class UserSchema(BaseSchema): id = fields.Str() @@ -690,6 +696,7 @@ class UserListSchema(BaseSchema): "Resolvable": ResolvableSchema, "Subscriber": SubscriberSchema, "Topic": TopicSchema, + "Replication": ReplicationSchema, # Compatibility for the Job trigger types "interval": IntervalTriggerSchema, "date": DateTriggerSchema, diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 1dda4a73..4cacda06 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -31,6 +31,7 @@ PatchOperation, Principal, Queue, + Replication, Request, RequestFile, RequestTemplate, @@ -62,6 +63,7 @@ "assert_runner_equal", "assert_subscriber_equal", "assert_topic_equal", + "assert_replication_equal", ] @@ -198,7 +200,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable) assert_connection_equal = partial(_assert_wrapper, expected_type=Connection) assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber) - +assert_replication_equal = partial(_assert_wrapper, expected_type=Replication) def assert_command_equal(obj1, obj2, do_raise=False): return _assert_wrapper( @@ -405,3 +407,4 @@ def assert_topic_equal(obj1, obj2, do_raise=False): }, do_raise=do_raise, ) + diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 6cb362fc..e54e3ac9 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -24,6 +24,7 @@ PatchOperation, Principal, Queue, + Replication, Request, RequestFile, RequestTemplate, @@ -31,7 +32,7 @@ Runner, System, Subscriber, - Topic, + Topic, ) @@ -936,3 +937,16 @@ def bg_topic(topic_dict, bg_subscriber): dict_copy = copy.deepcopy(topic_dict) dict_copy["subscribers"] = [bg_subscriber] return Topic(**dict_copy) + +@pytest.fixture +def replication_dict(ts_epoch): + """Replication as a dictionary.""" + return { + "id": "1234", + "replication_id": "89cd6a3a-e0e2-486b-b8e8-535d1893faf3", + "heartbeat": ts_epoch, + } + +@pytest.fixture +def bg_replication(replication_dict): + return Replication(**replication_dict) \ No newline at end of file diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index 9246dba2..7d41aff1 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -23,6 +23,7 @@ assert_patch_equal, assert_principal_equal, assert_queue_equal, + assert_replication_equal, assert_request_equal, assert_request_file_equal, assert_resolvable_equal, @@ -194,6 +195,12 @@ def test_no_modify(self, system_dict): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + brewtils.models.Replication, + lazy_fixture("replication_dict"), + assert_replication_equal, + lazy_fixture("bg_replication"), + ), ], ) def test_single(self, model, data, assertion, expected): @@ -340,6 +347,12 @@ def test_single_from_string(self): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + "parse_replication", + lazy_fixture("replication_dict"), + assert_replication_equal, + lazy_fixture("bg_replication"), + ), ], ) def test_single_specific(self, method, data, assertion, expected): @@ -479,6 +492,12 @@ def test_single_specific_from_string(self): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + brewtils.models.Replication, + lazy_fixture("replication_dict"), + assert_replication_equal, + lazy_fixture("bg_replication"), + ), ], ) def test_many(self, model, data, assertion, expected): @@ -611,6 +630,12 @@ def test_many(self, model, data, assertion, expected): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + "parse_replication", + lazy_fixture("replication_dict"), + assert_replication_equal, + lazy_fixture("bg_replication"), + ), ], ) def test_many_specific(self, method, data, assertion, expected): @@ -666,6 +691,7 @@ class TestSerialize(object): (lazy_fixture("bg_resolvable"), lazy_fixture("resolvable_dict")), (lazy_fixture("bg_subscriber"), lazy_fixture("subscriber_dict")), (lazy_fixture("bg_topic"), lazy_fixture("topic_dict")), + (lazy_fixture("bg_replication"), lazy_fixture("replication_dict")), ], ) def test_single(self, model, expected): @@ -777,6 +803,11 @@ def test_single(self, model, expected): lazy_fixture("bg_topic"), lazy_fixture("topic_dict"), ), + ( + "serialize_replication", + lazy_fixture("bg_replication"), + lazy_fixture("replication_dict"), + ), ], ) def test_single_specific(self, method, data, expected): @@ -807,6 +838,7 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_resolvable"), lazy_fixture("resolvable_dict")), (lazy_fixture("bg_subscriber"), lazy_fixture("subscriber_dict")), (lazy_fixture("bg_topic"), lazy_fixture("topic_dict")), + (lazy_fixture("bg_replication"), lazy_fixture("replication_dict")), ], ) def test_many(self, model, expected): @@ -901,6 +933,7 @@ class TestRoundTrip(object): lazy_fixture("bg_subscriber"), ), (brewtils.models.Topic, assert_topic_equal, lazy_fixture("bg_topic")), + (brewtils.models.Replication, assert_replication_equal, lazy_fixture("bg_replication")), ], ) def test_parsed_start(self, model, assertion, data): @@ -932,6 +965,7 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.Operation, lazy_fixture("operation_dict")), (brewtils.models.Runner, lazy_fixture("runner_dict")), (brewtils.models.Resolvable, lazy_fixture("resolvable_dict")), + (brewtils.models.Replication, lazy_fixture("replication_dict")), ], ) def test_serialized_start(self, model, data): From 3a532d572c519201d391a53b189483227331553d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:22:12 +0000 Subject: [PATCH 41/79] Formatting --- brewtils/models.py | 10 +++++++--- brewtils/schema_parser.py | 4 ++-- brewtils/schemas.py | 1 + brewtils/test/comparable.py | 2 +- brewtils/test/fixtures.py | 6 ++++-- test/schema_parser_test.py | 6 +++++- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 6e3ffe6d..1b3f9618 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -39,7 +39,7 @@ "Resolvable", "Subscriber", "Topic", - "Replication" + "Replication", ] @@ -1724,7 +1724,8 @@ def __repr__(self): self.name, self.subscribers, ) - + + class Replication(BaseModel): schema = "ReplicationSchema" @@ -1737,4 +1738,7 @@ def __str__(self): return "%s:%s" % (self.replication_id, self.heartbeat) def __repr__(self): - return "" % (self.replication_id, self.heartbeat) \ No newline at end of file + return "" % ( + self.replication_id, + self.heartbeat, + ) diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index b8861935..3b0c1a5d 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -437,7 +437,7 @@ def parse_topic(cls, topic, from_string=False, **kwargs): return cls.parse( topic, brewtils.models.Topic, from_string=from_string, **kwargs ) - + @classmethod def parse_replication(cls, replication, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a replication model object @@ -959,7 +959,7 @@ def serialize_topic(cls, topic, to_string=True, **kwargs): schema_name=brewtils.models.Topic.schema, **kwargs ) - + @classmethod def serialize_replication(cls, replication, to_string=True, **kwargs): """Convert a replication model into serialized form diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 85825a0d..1345139b 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -641,6 +641,7 @@ class TopicSchema(BaseSchema): name = fields.Str(allow_none=True) subscribers = fields.List(fields.Nested(SubscriberSchema, allow_none=True)) + class ReplicationSchema(BaseSchema): id = fields.Str(allow_none=True) replication_id = fields.Str(allow_none=True) diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 4cacda06..ba733765 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -202,6 +202,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber) assert_replication_equal = partial(_assert_wrapper, expected_type=Replication) + def assert_command_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -407,4 +408,3 @@ def assert_topic_equal(obj1, obj2, do_raise=False): }, do_raise=do_raise, ) - diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index e54e3ac9..bcde15eb 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -32,7 +32,7 @@ Runner, System, Subscriber, - Topic, + Topic, ) @@ -938,6 +938,7 @@ def bg_topic(topic_dict, bg_subscriber): dict_copy["subscribers"] = [bg_subscriber] return Topic(**dict_copy) + @pytest.fixture def replication_dict(ts_epoch): """Replication as a dictionary.""" @@ -947,6 +948,7 @@ def replication_dict(ts_epoch): "heartbeat": ts_epoch, } + @pytest.fixture def bg_replication(replication_dict): - return Replication(**replication_dict) \ No newline at end of file + return Replication(**replication_dict) diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index 7d41aff1..52f2ad33 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -933,7 +933,11 @@ class TestRoundTrip(object): lazy_fixture("bg_subscriber"), ), (brewtils.models.Topic, assert_topic_equal, lazy_fixture("bg_topic")), - (brewtils.models.Replication, assert_replication_equal, lazy_fixture("bg_replication")), + ( + brewtils.models.Replication, + assert_replication_equal, + lazy_fixture("bg_replication"), + ), ], ) def test_parsed_start(self, model, assertion, data): From 89cf57bbc652fb957ecf0fcf31a54344ce00b38d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:30:54 +0000 Subject: [PATCH 42/79] fixing testing --- brewtils/test/fixtures.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index bcde15eb..6f678dda 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -951,4 +951,6 @@ def replication_dict(ts_epoch): @pytest.fixture def bg_replication(replication_dict): - return Replication(**replication_dict) + dict_copy = copy.deepcopy(replication_dict) + dict_copy["heartbeat"] = ts_dt + return Replication(**dict_copy) From 3012d93d77d1df4be6c77e289364fa93aff3250e Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:32:03 +0000 Subject: [PATCH 43/79] testing --- brewtils/test/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 6f678dda..2bc43ede 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -950,7 +950,7 @@ def replication_dict(ts_epoch): @pytest.fixture -def bg_replication(replication_dict): +def bg_replication(replication_dict, ts_dt): dict_copy = copy.deepcopy(replication_dict) dict_copy["heartbeat"] = ts_dt return Replication(**dict_copy) From 23cc356cd06ad00900518347589afabae7af9ce4 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:29:16 +0000 Subject: [PATCH 44/79] Updating Job Model --- brewtils/models.py | 12 +++++++----- brewtils/schemas.py | 3 ++- brewtils/test/fixtures.py | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 1b3f9618..ed482c72 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1209,6 +1209,7 @@ def __init__( status=None, max_instances=None, timeout=None, + replication_id=None, ): self.id = id self.name = name @@ -1225,6 +1226,7 @@ def __init__( self.status = status self.max_instances = max_instances self.timeout = timeout + self.replication_id = replication_id def __str__(self): return "%s: %s" % (self.name, self.id) @@ -1729,16 +1731,16 @@ def __repr__(self): class Replication(BaseModel): schema = "ReplicationSchema" - def __init__(self, id=None, replication_id=None, heartbeat=None): + def __init__(self, id=None, replication_id=None, expires_at=None): self.id = id self.replication_id = replication_id - self.heartbeat = heartbeat + self.expires_at = expires_at def __str__(self): - return "%s:%s" % (self.replication_id, self.heartbeat) + return "%s:%s" % (self.replication_id, self.expires_at) def __repr__(self): - return "" % ( + return "" % ( self.replication_id, - self.heartbeat, + self.expires_at, ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 1345139b..0fcc99bd 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -545,6 +545,7 @@ class JobSchema(BaseSchema): status = fields.Str(allow_none=True) max_instances = fields.Int(allow_none=True) timeout = fields.Int(allow_none=True) + replication_id = fields.Str(allow_none=True) class JobExportInputSchema(BaseSchema): @@ -645,7 +646,7 @@ class TopicSchema(BaseSchema): class ReplicationSchema(BaseSchema): id = fields.Str(allow_none=True) replication_id = fields.Str(allow_none=True) - heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000") + expires_at = DateTime(allow_none=True, format="epoch", example="1500065932000") class UserSchema(BaseSchema): diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 2bc43ede..195d8798 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -584,6 +584,7 @@ def job_dict(ts_epoch, request_template_dict, date_trigger_dict): "status": "RUNNING", "max_instances": 3, "timeout": 30, + "replication_id": "123" } @@ -945,12 +946,12 @@ def replication_dict(ts_epoch): return { "id": "1234", "replication_id": "89cd6a3a-e0e2-486b-b8e8-535d1893faf3", - "heartbeat": ts_epoch, + "expires_at": ts_epoch, } @pytest.fixture def bg_replication(replication_dict, ts_dt): dict_copy = copy.deepcopy(replication_dict) - dict_copy["heartbeat"] = ts_dt + dict_copy["expires_at"] = ts_dt return Replication(**dict_copy) From 3056dbdcd633ffd319ea2b513559622afc28bad4 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:30:10 +0000 Subject: [PATCH 45/79] formatting --- brewtils/test/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 195d8798..a7db6e51 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -584,7 +584,7 @@ def job_dict(ts_epoch, request_template_dict, date_trigger_dict): "status": "RUNNING", "max_instances": 3, "timeout": 30, - "replication_id": "123" + "replication_id": "123", } From 0bfd6bc9d47dbefcb485fba882b59a4a3f38ca92 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:54:54 +0000 Subject: [PATCH 46/79] Jobs direct ref to replications --- brewtils/models.py | 4 ++-- brewtils/schemas.py | 2 +- brewtils/test/comparable.py | 1 + brewtils/test/fixtures.py | 9 ++++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index ed482c72..8af1baf0 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1209,7 +1209,7 @@ def __init__( status=None, max_instances=None, timeout=None, - replication_id=None, + replication=None, ): self.id = id self.name = name @@ -1226,7 +1226,7 @@ def __init__( self.status = status self.max_instances = max_instances self.timeout = timeout - self.replication_id = replication_id + self.replication = replication def __str__(self): return "%s: %s" % (self.name, self.id) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 0fcc99bd..96a633c2 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -545,7 +545,7 @@ class JobSchema(BaseSchema): status = fields.Str(allow_none=True) max_instances = fields.Int(allow_none=True) timeout = fields.Int(allow_none=True) - replication_id = fields.Str(allow_none=True) + replication = fields.Nested("ReplicationSchema", allow_none=True) class JobExportInputSchema(BaseSchema): diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index ba733765..c03f92c0 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -333,6 +333,7 @@ def assert_job_equal(obj1, obj2, do_raise=False): deep_fields={ "trigger": partial(assert_trigger_equal, do_raise=True), "request_template": partial(assert_request_template_equal, do_raise=True), + "replication": partial(assert_replication_equal, do_raise=True), }, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index a7db6e51..11183db5 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -566,7 +566,7 @@ def bg_role(legacy_role_dict): @pytest.fixture -def job_dict(ts_epoch, request_template_dict, date_trigger_dict): +def job_dict(ts_epoch, request_template_dict, date_trigger_dict, replication_dict): """A date job represented as a dictionary.""" return { "name": "job_name", @@ -584,7 +584,7 @@ def job_dict(ts_epoch, request_template_dict, date_trigger_dict): "status": "RUNNING", "max_instances": 3, "timeout": 30, - "replication_id": "123", + "replication": replication_dict, } @@ -642,12 +642,13 @@ def job_dict_for_import(job_dict): @pytest.fixture -def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger): +def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger, bg_replication): """A job as a model.""" dict_copy = copy.deepcopy(job_dict) dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_date_trigger dict_copy["request_template"] = bg_request_template + dict_copy["replication"] = bg_replication return Job(**dict_copy) @@ -658,6 +659,7 @@ def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt): dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_cron_trigger dict_copy["request_template"] = bg_request_template + dict_copy["replication"] = bg_replication return Job(**dict_copy) @@ -668,6 +670,7 @@ def bg_interval_job(interval_job_dict, bg_request_template, bg_interval_trigger, dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_interval_trigger dict_copy["request_template"] = bg_request_template + dict_copy["replication"] = bg_replication return Job(**dict_copy) From 80d3adab982134d4acf7646d113cbe03436757f9 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:56:59 +0000 Subject: [PATCH 47/79] order fixtures --- brewtils/test/fixtures.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 11183db5..f7bb5d22 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -565,6 +565,23 @@ def bg_role(legacy_role_dict): return LegacyRole(**dict_copy) +@pytest.fixture +def replication_dict(ts_epoch): + """Replication as a dictionary.""" + return { + "id": "1234", + "replication_id": "89cd6a3a-e0e2-486b-b8e8-535d1893faf3", + "expires_at": ts_epoch, + } + + +@pytest.fixture +def bg_replication(replication_dict, ts_dt): + dict_copy = copy.deepcopy(replication_dict) + dict_copy["expires_at"] = ts_dt + return Replication(**dict_copy) + + @pytest.fixture def job_dict(ts_epoch, request_template_dict, date_trigger_dict, replication_dict): """A date job represented as a dictionary.""" @@ -941,20 +958,3 @@ def bg_topic(topic_dict, bg_subscriber): dict_copy = copy.deepcopy(topic_dict) dict_copy["subscribers"] = [bg_subscriber] return Topic(**dict_copy) - - -@pytest.fixture -def replication_dict(ts_epoch): - """Replication as a dictionary.""" - return { - "id": "1234", - "replication_id": "89cd6a3a-e0e2-486b-b8e8-535d1893faf3", - "expires_at": ts_epoch, - } - - -@pytest.fixture -def bg_replication(replication_dict, ts_dt): - dict_copy = copy.deepcopy(replication_dict) - dict_copy["expires_at"] = ts_dt - return Replication(**dict_copy) From b8b5d8493d1daef7884aeb2d1c743f5e5e36f29d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:58:54 +0000 Subject: [PATCH 48/79] Fix fixtures --- brewtils/test/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index f7bb5d22..d5ab3abf 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -670,7 +670,7 @@ def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger, bg_replication @pytest.fixture -def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt): +def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt, bg_replication): """A beer garden cron job""" dict_copy = copy.deepcopy(cron_job_dict) dict_copy["next_run_time"] = ts_dt @@ -681,7 +681,7 @@ def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt): @pytest.fixture -def bg_interval_job(interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt): +def bg_interval_job(interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt, bg_replication): """A beer garden interval job""" dict_copy = copy.deepcopy(interval_job_dict) dict_copy["next_run_time"] = ts_dt From 242af1364f98ec16373cc2615833c22e1d02fc94 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:59:52 +0000 Subject: [PATCH 49/79] formatting --- brewtils/test/fixtures.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index d5ab3abf..da3f1342 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -670,7 +670,9 @@ def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger, bg_replication @pytest.fixture -def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt, bg_replication): +def bg_cron_job( + cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt, bg_replication +): """A beer garden cron job""" dict_copy = copy.deepcopy(cron_job_dict) dict_copy["next_run_time"] = ts_dt @@ -681,7 +683,9 @@ def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt, bg_r @pytest.fixture -def bg_interval_job(interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt, bg_replication): +def bg_interval_job( + interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt, bg_replication +): """A beer garden interval job""" dict_copy = copy.deepcopy(interval_job_dict) dict_copy["next_run_time"] = ts_dt From 3949c61194732eebe14f1b2943900454c66e4444 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 11 Jun 2024 09:49:17 -0400 Subject: [PATCH 50/79] User metadata defaults --- brewtils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index afafa7b1..d22bf768 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1637,7 +1637,7 @@ def __init__( self.remote_roles = remote_roles or [] self.is_remote = is_remote self.remote_user_mapping = remote_user_mapping or [] - self.metadata = metadata + self.metadata = metadata or {} self.protected = protected self.file_generated = file_generated From 64f81e37c0ab5bf5db182593a4b8d94264b36456 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:07:38 +0000 Subject: [PATCH 51/79] changing to upstream and alias --- brewtils/models.py | 23 ++++---- brewtils/schema_parser.py | 46 ++++++++------- brewtils/schemas.py | 16 ++--- brewtils/test/comparable.py | 27 +++++---- brewtils/test/fixtures.py | 40 +++++++------ test/schema_parser_test.py | 113 +++++++++++++++++++----------------- 6 files changed, 140 insertions(+), 125 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index d22bf768..ad42ccc7 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1617,13 +1617,13 @@ class User(BaseModel): def __init__( self, - username = None, - id = None, + username=None, + id=None, password=None, roles=None, local_roles=None, - remote_roles=None, - remote_user_mapping=None, + upstream_roles=None, + alias_user_mapping=None, metadata=None, is_remote=False, protected=False, @@ -1634,9 +1634,9 @@ def __init__( self.password = password self.roles = roles or [] self.local_roles = local_roles or [] - self.remote_roles = remote_roles or [] + self.upstream_roles = upstream_roles or [] self.is_remote = is_remote - self.remote_user_mapping = remote_user_mapping or [] + self.alias_user_mapping = alias_user_mapping or [] self.metadata = metadata or {} self.protected = protected self.file_generated = file_generated @@ -1710,16 +1710,19 @@ def __repr__(self): self.scope_commands, ) -class RemoteRole(Role): - schema = "RemoteRoleSchema" -class RemoteUserMap(BaseModel): - schema = "RemoteUserMapSchema" +class UpstreamRole(Role): + schema = "UpstreamRoleSchema" + + +class AliasUserMap(BaseModel): + schema = "AliasUserMapSchema" def __init__(self, target_garden, username): self.target_garden = target_garden self.username = username + class Subscriber(BaseModel): schema = "SubscriberSchema" diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 4f302e2d..abe523aa 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -49,9 +49,9 @@ class SchemaParser(object): "RunnerSchema": brewtils.models.Runner, "ResolvableSchema": brewtils.models.Resolvable, "RoleSchema": brewtils.models.Role, - "RemoteRoleSchema": brewtils.models.RemoteRole, + "UpstreamRoleSchema": brewtils.models.UpstreamRole, "UserSchema": brewtils.models.User, - "RemoteUserMapSchema": brewtils.models.RemoteUserMap, + "AliasUserMapSchema": brewtils.models.AliasUserMap, "SubscriberSchema": brewtils.models.Subscriber, "TopicSchema": brewtils.models.Topic, } @@ -283,9 +283,9 @@ def parse_role(cls, role, from_string=False, **kwargs): A Role object """ return cls.parse(role, brewtils.models.Role, from_string=from_string, **kwargs) - + @classmethod - def parse_remote_role(cls, role, from_string=False, **kwargs): + def parse_upstream_role(cls, role, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a role model object Args: @@ -296,11 +296,13 @@ def parse_remote_role(cls, role, from_string=False, **kwargs): Returns: A Role object """ - return cls.parse(role, brewtils.models.RemoteRole, from_string=from_string, **kwargs) + return cls.parse( + role, brewtils.models.UpstreamRole, from_string=from_string, **kwargs + ) @classmethod - def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): - """Convert raw JSON string or dictionary to a RemoteUserMap model object + def parse_alias_user_map(cls, alias_user_map, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a AliasUserMap model object Args: role: The raw input @@ -308,11 +310,11 @@ def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): **kwargs: Additional parameters to be passed to the Schema (e.g. many=True) Returns: - A RemoteUserMap object + A AliasUserMap object """ return cls.parse( - remote_user_map, - brewtils.models.RemoteUserMap, + alias_user_map, + brewtils.models.AliasUserMap, from_string=from_string, **kwargs ) @@ -330,10 +332,7 @@ def parse_user_token(cls, user_token, from_string=False, **kwargs): A UserToken object """ return cls.parse( - user_token, - brewtils.models.UserToken, - from_string=from_string, - **kwargs + user_token, brewtils.models.UserToken, from_string=from_string, **kwargs ) @classmethod @@ -771,9 +770,9 @@ def serialize_role(cls, role, to_string=True, **kwargs): return cls.serialize( role, to_string=to_string, schema_name=brewtils.models.Role.schema, **kwargs ) - + @classmethod - def serialize_remote_role(cls, role, to_string=True, **kwargs): + def serialize_upstream_role(cls, role, to_string=True, **kwargs): """Convert a role model into serialized form Args: @@ -786,15 +785,18 @@ def serialize_remote_role(cls, role, to_string=True, **kwargs): Serialized representation """ return cls.serialize( - role, to_string=to_string, schema_name=brewtils.models.RemoteRole.schema, **kwargs + role, + to_string=to_string, + schema_name=brewtils.models.UpstreamRole.schema, + **kwargs ) @classmethod - def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): - """Convert a RemoteUserMap model into serialized form + def serialize_alias_user_map(cls, alias_user_map, to_string=True, **kwargs): + """Convert a AliasUserMap model into serialized form Args: - RemoteUserMap: The RemoteUserMap object(s) to be serialized + AliasUserMap: The AliasUserMap 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) @@ -803,9 +805,9 @@ def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): Serialized representation """ return cls.serialize( - remote_user_map, + alias_user_map, to_string=to_string, - schema_name=brewtils.models.RemoteUserMap.schema, + schema_name=brewtils.models.AliasUserMap.schema, **kwargs ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 470f07b3..885a290f 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -574,14 +574,16 @@ class RoleSchema(BaseSchema): protected = fields.Boolean(allow_none=True) file_generated = fields.Boolean(allow_none=True) -class RemoteRoleSchema(RoleSchema): + +class UpstreamRoleSchema(RoleSchema): pass -class RemoteUserMapSchema(BaseSchema): +class AliasUserMapSchema(BaseSchema): target_garden = fields.Str() username = fields.Str() + class SubscriberSchema(BaseSchema): garden = fields.Str(allow_none=True) namespace = fields.Str(allow_none=True) @@ -603,16 +605,14 @@ class UserSchema(BaseSchema): password = fields.Str(allow_none=True) roles = fields.List(fields.Str(), allow_none=True) local_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) - remote_roles = fields.List(fields.Nested(RemoteRoleSchema()), allow_none=True) - remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) + upstream_roles = fields.List(fields.Nested(UpstreamRoleSchema()), allow_none=True) + alias_user_mapping = fields.List(fields.Nested(AliasUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) protected = fields.Boolean(allow_none=True) file_generated = fields.Boolean(allow_none=True) - - model_schema_map.update( { "Choices": ChoicesSchema, @@ -643,9 +643,9 @@ class UserSchema(BaseSchema): "Runner": RunnerSchema, "Resolvable": ResolvableSchema, "Role": RoleSchema, - "RemoteRole": RemoteRoleSchema, + "UpstreamRole": UpstreamRoleSchema, "User": UserSchema, - "RemoteUserMap": RemoteUserMapSchema, + "AliasUserMap": AliasUserMapSchema, "Subscriber": SubscriberSchema, "Topic": TopicSchema, # Compatibility for the Job trigger types diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 43a4add7..1874906b 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -14,6 +14,7 @@ import brewtils.test from brewtils.models import ( + AliasUserMap, Choices, Command, Connection, @@ -24,24 +25,23 @@ Instance, IntervalTrigger, Job, - Role, LoggingConfig, Operation, Parameter, PatchOperation, - User, - UserToken, Queue, - RemoteUserMap, - RemoteRole, Request, RequestFile, RequestTemplate, Resolvable, + Role, Runner, - System, Subscriber, + System, Topic, + UpstreamRole, + User, + UserToken, ) __all__ = [ @@ -57,7 +57,7 @@ "assert_parameter_equal", "assert_user_token_equal", "assert_user_equal", - "assert_remote_user_map_equal", + "assert_alias_user_map_equal", "assert_request_equal", "assert_role_equal", "assert_system_equal", @@ -202,7 +202,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_runner_equal = partial(_assert_wrapper, expected_type=Runner) assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable) assert_connection_equal = partial(_assert_wrapper, expected_type=Connection) -assert_remote_user_map_equal = partial(_assert_wrapper, expected_type=RemoteUserMap) +assert_alias_user_map_equal = partial(_assert_wrapper, expected_type=AliasUserMap) assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber) @@ -246,6 +246,7 @@ def assert_event_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) + def assert_user_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -253,12 +254,13 @@ def assert_user_equal(obj1, obj2, do_raise=False): expected_type=User, deep_fields={ "local_roles": partial(assert_role_equal, do_raise=True), - "remote_roles": partial(assert_remote_role_equal, do_raise=True), - "remote_user_mapping": partial(assert_remote_user_map_equal, do_raise=True), + "upstream_roles": partial(assert_upstream_role_equal, do_raise=True), + "alias_user_mapping": partial(assert_alias_user_map_equal, do_raise=True), }, do_raise=do_raise, ) + def assert_user_token_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -327,11 +329,12 @@ def assert_role_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) -def assert_remote_role_equal(obj1, obj2, do_raise=False): + +def assert_upstream_role_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, obj2, - expected_type=RemoteRole, + expected_type=UpstreamRole, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 85b89937..11cfdf0f 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -7,6 +7,7 @@ import pytz from brewtils.models import ( + AliasUserMap, Choices, Command, Connection, @@ -22,19 +23,18 @@ Parameter, PatchOperation, Queue, - RemoteUserMap, - RemoteRole, Request, RequestFile, RequestTemplate, Resolvable, + Role, Runner, + Subscriber, System, + Topic, + UpstreamRole, User, UserToken, - Role, - Subscriber, - Topic, ) @@ -533,7 +533,7 @@ def bg_queue(queue_dict): @pytest.fixture -def remote_user_map_dict(): +def alias_user_map_dict(): return { "target_garden": "test", "username": "user", @@ -541,8 +541,9 @@ def remote_user_map_dict(): @pytest.fixture -def bg_remote_user_map(remote_user_map_dict): - return RemoteUserMap(**remote_user_map_dict) +def bg_alias_user_map(alias_user_map_dict): + return AliasUserMap(**alias_user_map_dict) + @pytest.fixture def user_token_dict(user_dict, ts_epoch): @@ -582,11 +583,12 @@ def role_dict(): @pytest.fixture -def bg_remote_role(role_dict): - return RemoteRole(**role_dict) +def bg_role(role_dict): + return Role(**role_dict) + @pytest.fixture -def remote_role_dict(): +def upstream_role_dict(): return { "permission": "PLUGIN_ADMIN", "description": "PLUGIN ADMIN ROLE", @@ -604,20 +606,20 @@ def remote_role_dict(): @pytest.fixture -def bg_role(role_dict): - return Role(**role_dict) +def bg_upstream_role(upstream_role_dict): + return UpstreamRole(**upstream_role_dict) @pytest.fixture -def user_dict(role_dict, remote_role_dict, remote_user_map_dict): +def user_dict(role_dict, upstream_role_dict, alias_user_map_dict): return { "id": "1", "username": "USERNAME", "password": "HASH", "roles": ["PLUGIN_ADMIN_ROLE"], "local_roles": [role_dict], - "remote_roles": [remote_role_dict], - "remote_user_mapping": [remote_user_map_dict], + "upstream_roles": [upstream_role_dict], + "alias_user_mapping": [alias_user_map_dict], "is_remote": False, "metadata": {}, "protected": False, @@ -626,11 +628,11 @@ def user_dict(role_dict, remote_role_dict, remote_user_map_dict): @pytest.fixture -def bg_user(user_dict, bg_role, bg_remote_role, bg_remote_user_map): +def bg_user(user_dict, bg_role, bg_upstream_role, bg_alias_user_map): dict_copy = copy.deepcopy(user_dict) - dict_copy["remote_roles"] = [bg_remote_role] + dict_copy["upstream_roles"] = [bg_upstream_role] dict_copy["local_roles"] = [bg_role] - dict_copy["remote_user_mapping"] = [bg_remote_user_map] + dict_copy["alias_user_mapping"] = [bg_alias_user_map] return User(**dict_copy) diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index d575980b..94fa1cc2 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -5,11 +5,15 @@ import copy -import brewtils.models import pytest +from marshmallow.exceptions import MarshmallowError +from pytest_lazyfixture import lazy_fixture + +import brewtils.models from brewtils.models import System from brewtils.schema_parser import SchemaParser from brewtils.test.comparable import ( + assert_alias_user_map_equal, assert_command_equal, assert_connection_equal, assert_event_equal, @@ -21,11 +25,7 @@ assert_operation_equal, assert_parameter_equal, assert_patch_equal, - assert_user_equal, - assert_user_token_equal, assert_queue_equal, - assert_remote_user_map_equal, - assert_remote_role_equal, assert_request_equal, assert_request_file_equal, assert_resolvable_equal, @@ -34,9 +34,10 @@ assert_subscriber_equal, assert_system_equal, assert_topic_equal, + assert_upstream_role_equal, + assert_user_equal, + assert_user_token_equal, ) -from marshmallow.exceptions import MarshmallowError -from pytest_lazyfixture import lazy_fixture class TestParse(object): @@ -138,10 +139,10 @@ def test_no_modify(self, system_dict): lazy_fixture("bg_user_token"), ), ( - brewtils.models.RemoteUserMap, - lazy_fixture("remote_user_map_dict"), - assert_remote_user_map_equal, - lazy_fixture("bg_remote_user_map"), + brewtils.models.AliasUserMap, + lazy_fixture("alias_user_map_dict"), + assert_alias_user_map_equal, + lazy_fixture("bg_alias_user_map"), ), ( brewtils.models.Role, @@ -150,10 +151,10 @@ def test_no_modify(self, system_dict): lazy_fixture("bg_role"), ), ( - brewtils.models.RemoteRole, - lazy_fixture("remote_role_dict"), - assert_remote_role_equal, - lazy_fixture("bg_remote_role"), + brewtils.models.UpstreamRole, + lazy_fixture("upstream_role_dict"), + assert_upstream_role_equal, + lazy_fixture("bg_upstream_role"), ), ( brewtils.models.Job, @@ -296,10 +297,10 @@ def test_single_from_string(self): lazy_fixture("bg_user_token"), ), ( - "parse_remote_user_map", - lazy_fixture("remote_user_map_dict"), - assert_remote_user_map_equal, - lazy_fixture("bg_remote_user_map"), + "parse_alias_user_map", + lazy_fixture("alias_user_map_dict"), + assert_alias_user_map_equal, + lazy_fixture("bg_alias_user_map"), ), ( "parse_role", @@ -308,10 +309,10 @@ def test_single_from_string(self): lazy_fixture("bg_role"), ), ( - "parse_remote_role", - lazy_fixture("remote_role_dict"), - assert_remote_role_equal, - lazy_fixture("bg_remote_role"), + "parse_upstream_role", + lazy_fixture("upstream_role_dict"), + assert_upstream_role_equal, + lazy_fixture("bg_upstream_role"), ), ( "parse_job", @@ -459,10 +460,10 @@ def test_single_specific_from_string(self): lazy_fixture("bg_user_token"), ), ( - brewtils.models.RemoteUserMap, - lazy_fixture("remote_user_map_dict"), - assert_remote_user_map_equal, - lazy_fixture("bg_remote_user_map"), + brewtils.models.AliasUserMap, + lazy_fixture("alias_user_map_dict"), + assert_alias_user_map_equal, + lazy_fixture("bg_alias_user_map"), ), ( brewtils.models.Role, @@ -471,10 +472,10 @@ def test_single_specific_from_string(self): lazy_fixture("bg_role"), ), ( - brewtils.models.RemoteRole, - lazy_fixture("remote_role_dict"), - assert_remote_role_equal, - lazy_fixture("bg_remote_role"), + brewtils.models.UpstreamRole, + lazy_fixture("upstream_role_dict"), + assert_upstream_role_equal, + lazy_fixture("bg_upstream_role"), ), ( brewtils.models.Job, @@ -615,10 +616,10 @@ def test_many(self, model, data, assertion, expected): lazy_fixture("bg_user_token"), ), ( - "parse_remote_user_map", - lazy_fixture("remote_user_map_dict"), - assert_remote_user_map_equal, - lazy_fixture("bg_remote_user_map"), + "parse_alias_user_map", + lazy_fixture("alias_user_map_dict"), + assert_alias_user_map_equal, + lazy_fixture("bg_alias_user_map"), ), ( "parse_role", @@ -627,10 +628,10 @@ def test_many(self, model, data, assertion, expected): lazy_fixture("bg_role"), ), ( - "parse_remote_role", - lazy_fixture("remote_role_dict"), - assert_remote_role_equal, - lazy_fixture("bg_remote_role"), + "parse_upstream_role", + lazy_fixture("upstream_role_dict"), + assert_upstream_role_equal, + lazy_fixture("bg_upstream_role"), ), ( "parse_job", @@ -732,9 +733,9 @@ class TestSerialize(object): (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), - (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), + (lazy_fixture("bg_alias_user_map"), lazy_fixture("alias_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), - (lazy_fixture("bg_remote_role"), lazy_fixture("remote_role_dict")), + (lazy_fixture("bg_upstream_role"), lazy_fixture("upstream_role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -805,9 +806,9 @@ def test_single(self, model, expected): lazy_fixture("user_token_dict"), ), ( - "serialize_remote_user_map", - lazy_fixture("bg_remote_user_map"), - lazy_fixture("remote_user_map_dict"), + "serialize_alias_user_map", + lazy_fixture("bg_alias_user_map"), + lazy_fixture("alias_user_map_dict"), ), ( "serialize_role", @@ -815,9 +816,9 @@ def test_single(self, model, expected): lazy_fixture("role_dict"), ), ( - "serialize_remote_role", - lazy_fixture("bg_remote_role"), - lazy_fixture("remote_role_dict"), + "serialize_upstream_role", + lazy_fixture("bg_upstream_role"), + lazy_fixture("upstream_role_dict"), ), ("serialize_job", lazy_fixture("bg_job"), lazy_fixture("job_dict")), ( @@ -891,9 +892,9 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), (lazy_fixture("bg_user_token"), lazy_fixture("user_token_dict")), - (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), + (lazy_fixture("bg_alias_user_map"), lazy_fixture("alias_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), - (lazy_fixture("bg_remote_role"), lazy_fixture("remote_role_dict")), + (lazy_fixture("bg_upstream_role"), lazy_fixture("upstream_role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), (lazy_fixture("bg_interval_job"), lazy_fixture("interval_job_dict")), @@ -981,12 +982,16 @@ class TestRoundTrip(object): lazy_fixture("bg_user_token"), ), ( - brewtils.models.RemoteUserMap, - assert_remote_user_map_equal, - lazy_fixture("bg_remote_user_map"), + brewtils.models.AliasUserMap, + assert_alias_user_map_equal, + lazy_fixture("bg_alias_user_map"), ), (brewtils.models.Role, assert_role_equal, lazy_fixture("bg_role")), - (brewtils.models.RemoteRole, assert_remote_role_equal, lazy_fixture("bg_remote_role")), + ( + brewtils.models.UpstreamRole, + assert_upstream_role_equal, + lazy_fixture("bg_upstream_role"), + ), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_cron_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_interval_job")), @@ -1033,7 +1038,7 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.User, lazy_fixture("user_dict")), (brewtils.models.UserToken, lazy_fixture("user_token_dict")), (brewtils.models.Role, lazy_fixture("role_dict")), - (brewtils.models.RemoteRole, lazy_fixture("remote_role_dict")), + (brewtils.models.UpstreamRole, lazy_fixture("upstream_role_dict")), (brewtils.models.Job, lazy_fixture("job_dict")), (brewtils.models.Job, lazy_fixture("cron_job_dict")), (brewtils.models.Job, lazy_fixture("interval_job_dict")), From 8c961d2d6377a2e866658edf244b0f20f75bde0a Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:09:09 +0000 Subject: [PATCH 52/79] fixing testing --- test/models_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models_test.py b/test/models_test.py index 2e2c1e47..cb1d8b39 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -537,7 +537,7 @@ def user(self): return User( username="admin", roles=["bg-admin"], - remote_roles=[Role(name="foo", permission="ADMIN")], + upstream_roles=[Role(name="foo", permission="ADMIN")], ) def test_str(self, user): From ce072b2f0c0345d7c918effe7e8ee246f49ba3fd Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:12:04 +0000 Subject: [PATCH 53/79] formatting --- brewtils/rest/client.py | 4 +++- brewtils/schemas.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/brewtils/rest/client.py b/brewtils/rest/client.py index b9e348ee..ad7cfacc 100644 --- a/brewtils/rest/client.py +++ b/brewtils/rest/client.py @@ -25,7 +25,9 @@ def enable_auth(method): def wrapper(self, *args, **kwargs): # Load Token initially if authentication settings are provided - if not self.session.headers.get("Authorization") and ((self.username and self.password) or self.client_cert): + if not self.session.headers.get("Authorization") and ( + (self.username and self.password) or self.client_cert + ): self.get_tokens() original_response = method(self, *args, **kwargs) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 885a290f..65b98fef 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -38,7 +38,7 @@ "OperationSchema", "UserSchema", "RoleSchema", - "RemoteUserMapSchema", + "AliasUserMapSchema", "SubscriberSchema", "TopicSchema", ] From 4daaf21b9ce2c06236ef907ddc64805ac138289d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:13:24 +0000 Subject: [PATCH 54/79] formatting --- brewtils/rest/system_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brewtils/rest/system_client.py b/brewtils/rest/system_client.py index 6d78fc1b..bfcd216b 100644 --- a/brewtils/rest/system_client.py +++ b/brewtils/rest/system_client.py @@ -597,7 +597,9 @@ def _construct_bg_request(self, **kwargs): propagate = kwargs.pop("_propagate", None) if parent: - requester = getattr(brewtils.plugin.request_context.current_request, "requester", None) + requester = getattr( + brewtils.plugin.request_context.current_request, "requester", None + ) else: requester = None From c97d4ce02dfa1bdf2b7d52915641c88080dcd4c2 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 13 Jun 2024 06:44:40 -0400 Subject: [PATCH 55/79] Roles Equals func --- brewtils/models.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index ad42ccc7..be5216b3 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1710,6 +1710,22 @@ def __repr__(self): self.scope_commands, ) + def __eq__(self, other): + if not isinstance(other, Role): + # don't attempt to compare against unrelated types + return NotImplemented + + return ( + self.name == other.name + and self.description == other.description + and self.scope_gardens == other.scope_gardens + and self.scope_namespaces == other.scope_namespaces + and self.scope_systems == other.scope_systems + and self.scope_instances == other.scope_instances + and self.scope_versions == other.scope_versions + and self.scope_commands == other.scope_commands + ) + class UpstreamRole(Role): schema = "UpstreamRoleSchema" From 92aa21032f4fa90b91c2be1dcd7b038bd599ac3d Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 13 Jun 2024 08:51:27 -0400 Subject: [PATCH 56/79] Update Events Roles --- brewtils/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index be5216b3..35e463bf 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -91,8 +91,8 @@ class Events(Enum): JOB_EXECUTED = 43 USER_UPDATED = 44 USERS_IMPORTED = 45 - ROLE_UPDATED = 46 - ROLES_IMPORTED = 47 + ROLE_DELETED = 46 + ROLE_UPDATED = 47 COMMAND_PUBLISHING_BLOCKLIST_SYNC = 48 COMMAND_PUBLISHING_BLOCKLIST_REMOVE = 49 COMMAND_PUBLISHING_BLOCKLIST_UPDATE = 50 From d1142175906550ee6bb32d0119ce2a61eda4b2bf Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 14 Jun 2024 14:25:14 +0000 Subject: [PATCH 57/79] User Model Equals --- brewtils/models.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 35e463bf..81eedb7e 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1649,6 +1649,21 @@ def __repr__(self): self.username, self.roles, ) + + def __eq__(self, other): + if not isinstance(other, User): + # don't attempt to compare against unrelated types + return NotImplemented + + return ( + self.username == other.username + and self.roles == other.roles + and self.upstream_roles == other.upstream_roles + and self.is_remote == other.is_remote + and self.alias_user_mapping == other.alias_user_mapping + and self.protected == other.protected + and self.file_generated == other.file_generated + ) class Role(BaseModel): From cd47aadf57adc1774e4470652d485e3c8c71e2e1 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 19 Jun 2024 06:26:47 -0400 Subject: [PATCH 58/79] Formatting --- brewtils/auto_decorator.py | 1 - brewtils/models.py | 2 +- brewtils/request_handling.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/brewtils/auto_decorator.py b/brewtils/auto_decorator.py index 0ddd3049..ef3742b0 100644 --- a/brewtils/auto_decorator.py +++ b/brewtils/auto_decorator.py @@ -1,7 +1,6 @@ from inspect import Parameter as InspectParameter # noqa from inspect import signature - from brewtils.models import Command, Parameter diff --git a/brewtils/models.py b/brewtils/models.py index 81eedb7e..53c53796 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1649,7 +1649,7 @@ def __repr__(self): self.username, self.roles, ) - + def __eq__(self, other): if not isinstance(other, User): # don't attempt to compare against unrelated types diff --git a/brewtils/request_handling.py b/brewtils/request_handling.py index 7ec877d4..fad8e647 100644 --- a/brewtils/request_handling.py +++ b/brewtils/request_handling.py @@ -10,8 +10,8 @@ import six from requests import ConnectionError as RequestsConnectionError -from brewtils.decorators import _parse_method import brewtils.plugin +from brewtils.decorators import _parse_method from brewtils.errors import ( BGGivesUpError, DiscardMessageException, From 078681288883ebaf30d5b766b1bc2e176130b78a Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 19 Jun 2024 08:50:05 -0400 Subject: [PATCH 59/79] Change Log Updates --- CHANGELOG.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 16ea25b6..31d665ed 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,16 @@ Brewtils Changelog ================== +3.27.0 +------ +TBD + +- New Models for User, UserToken, Role, and AliasUserMap +- Must upgrade to a minimum version of Beer Garden 3.27.0 to support new authentication models. If authentication is not enabled, upgrade + is not required. +- Removed 2.0 Legacy support for Principle and LegacyRole models +- Fixed bug in SystemClient to properly assign requester field from parent request + 3.26.2 ------ 6/6/20 From 6304ad6f351709513f2fca664bcaa72a6c21750b Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 20 Jun 2024 07:48:13 -0400 Subject: [PATCH 60/79] Use Replication Id --- brewtils/models.py | 2 ++ brewtils/schemas.py | 1 + 2 files changed, 3 insertions(+) diff --git a/brewtils/models.py b/brewtils/models.py index 8af1baf0..7455bacd 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1210,6 +1210,7 @@ def __init__( max_instances=None, timeout=None, replication=None, + replication_id=None, ): self.id = id self.name = name @@ -1227,6 +1228,7 @@ def __init__( self.max_instances = max_instances self.timeout = timeout self.replication = replication + self.replication_id = replication_id def __str__(self): return "%s: %s" % (self.name, self.id) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 96a633c2..77e59f47 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -546,6 +546,7 @@ class JobSchema(BaseSchema): max_instances = fields.Int(allow_none=True) timeout = fields.Int(allow_none=True) replication = fields.Nested("ReplicationSchema", allow_none=True) + replication_id = fields.Str(allow_none=True) class JobExportInputSchema(BaseSchema): From c5c8521e7f0e9d0823b919b80082df402db4e202 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:36:16 +0000 Subject: [PATCH 61/79] update to user_alias_mapping --- brewtils/models.py | 6 +++--- brewtils/schemas.py | 2 +- brewtils/test/comparable.py | 2 +- brewtils/test/fixtures.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 53c53796..a044c939 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1623,7 +1623,7 @@ def __init__( roles=None, local_roles=None, upstream_roles=None, - alias_user_mapping=None, + user_alias_mapping=None, metadata=None, is_remote=False, protected=False, @@ -1636,7 +1636,7 @@ def __init__( self.local_roles = local_roles or [] self.upstream_roles = upstream_roles or [] self.is_remote = is_remote - self.alias_user_mapping = alias_user_mapping or [] + self.user_alias_mapping = user_alias_mapping or [] self.metadata = metadata or {} self.protected = protected self.file_generated = file_generated @@ -1660,7 +1660,7 @@ def __eq__(self, other): and self.roles == other.roles and self.upstream_roles == other.upstream_roles and self.is_remote == other.is_remote - and self.alias_user_mapping == other.alias_user_mapping + and self.user_alias_mapping == other.user_alias_mapping and self.protected == other.protected and self.file_generated == other.file_generated ) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 65b98fef..77496931 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -606,7 +606,7 @@ class UserSchema(BaseSchema): roles = fields.List(fields.Str(), allow_none=True) local_roles = fields.List(fields.Nested(RoleSchema()), allow_none=True) upstream_roles = fields.List(fields.Nested(UpstreamRoleSchema()), allow_none=True) - alias_user_mapping = fields.List(fields.Nested(AliasUserMapSchema())) + user_alias_mapping = fields.List(fields.Nested(AliasUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) protected = fields.Boolean(allow_none=True) diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 1874906b..c2fc68de 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -255,7 +255,7 @@ def assert_user_equal(obj1, obj2, do_raise=False): deep_fields={ "local_roles": partial(assert_role_equal, do_raise=True), "upstream_roles": partial(assert_upstream_role_equal, do_raise=True), - "alias_user_mapping": partial(assert_alias_user_map_equal, do_raise=True), + "user_alias_mapping": partial(assert_alias_user_map_equal, do_raise=True), }, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 11cfdf0f..98afa338 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -619,7 +619,7 @@ def user_dict(role_dict, upstream_role_dict, alias_user_map_dict): "roles": ["PLUGIN_ADMIN_ROLE"], "local_roles": [role_dict], "upstream_roles": [upstream_role_dict], - "alias_user_mapping": [alias_user_map_dict], + "user_alias_mapping": [alias_user_map_dict], "is_remote": False, "metadata": {}, "protected": False, @@ -632,7 +632,7 @@ def bg_user(user_dict, bg_role, bg_upstream_role, bg_alias_user_map): dict_copy = copy.deepcopy(user_dict) dict_copy["upstream_roles"] = [bg_upstream_role] dict_copy["local_roles"] = [bg_role] - dict_copy["alias_user_mapping"] = [bg_alias_user_map] + dict_copy["user_alias_mapping"] = [bg_alias_user_map] return User(**dict_copy) From adb189be66cd50244c83d9dcde4697fc18577ecb Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:08:57 -0400 Subject: [PATCH 62/79] PR Requested Changes --- brewtils/models.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index a044c939..9f327de1 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -91,8 +91,8 @@ class Events(Enum): JOB_EXECUTED = 43 USER_UPDATED = 44 USERS_IMPORTED = 45 - ROLE_DELETED = 46 - ROLE_UPDATED = 47 + ROLE_UPDATED = 46 + ROLE_DELETED = 47 COMMAND_PUBLISHING_BLOCKLIST_SYNC = 48 COMMAND_PUBLISHING_BLOCKLIST_REMOVE = 49 COMMAND_PUBLISHING_BLOCKLIST_UPDATE = 50 @@ -1629,8 +1629,8 @@ def __init__( protected=False, file_generated=False, ): - self.id = id self.username = username + self.id = id self.password = password self.roles = roles or [] self.local_roles = local_roles or [] @@ -1691,10 +1691,10 @@ def __init__( protected=False, file_generated=False, ): + self.name = name self.permission = permission or "READ_ONLY" self.description = description - self.id = id - self.name = name + self.id = id self.scope_gardens = scope_gardens or [] self.scope_namespaces = scope_namespaces or [] self.scope_systems = scope_systems or [] @@ -1733,6 +1733,7 @@ def __eq__(self, other): return ( self.name == other.name and self.description == other.description + and self.permission == other.permission and self.scope_gardens == other.scope_gardens and self.scope_namespaces == other.scope_namespaces and self.scope_systems == other.scope_systems From 350474ed95027c3491a84c5aca197cd35247cee7 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:11:45 -0400 Subject: [PATCH 63/79] Add Permissions Enum --- brewtils/models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 9f327de1..71a421fc 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -102,6 +102,11 @@ class Events(Enum): # Next: 57 +class Permissions(Enum): + READ_ONLY = 1 + OPERATOR = 2 + PLUGIN_ADMIN = 3 + GARDEN_ADMIN = 4 class BaseModel(object): schema = None @@ -1669,6 +1674,7 @@ def __eq__(self, other): class Role(BaseModel): schema = "RoleSchema" + # TODO: REMOVE after DB model Updated with Permissions enum PERMISSION_TYPES = { "GARDEN_ADMIN", "PLUGIN_ADMIN", @@ -1692,7 +1698,7 @@ def __init__( file_generated=False, ): self.name = name - self.permission = permission or "READ_ONLY" + self.permission = permission or Permissions.READ_ONLY.name self.description = description self.id = id self.scope_gardens = scope_gardens or [] From 47ba89d92252df4243043e15709e9310697314f5 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:41:04 -0400 Subject: [PATCH 64/79] Job Cleanup --- brewtils/models.py | 4 ---- brewtils/schemas.py | 2 -- brewtils/test/fixtures.py | 16 ++++------------ 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 7455bacd..72052e75 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1209,8 +1209,6 @@ def __init__( status=None, max_instances=None, timeout=None, - replication=None, - replication_id=None, ): self.id = id self.name = name @@ -1227,8 +1225,6 @@ def __init__( self.status = status self.max_instances = max_instances self.timeout = timeout - self.replication = replication - self.replication_id = replication_id def __str__(self): return "%s: %s" % (self.name, self.id) diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 77e59f47..db21e73b 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -545,8 +545,6 @@ class JobSchema(BaseSchema): status = fields.Str(allow_none=True) max_instances = fields.Int(allow_none=True) timeout = fields.Int(allow_none=True) - replication = fields.Nested("ReplicationSchema", allow_none=True) - replication_id = fields.Str(allow_none=True) class JobExportInputSchema(BaseSchema): diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index da3f1342..412a7f1f 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -583,7 +583,7 @@ def bg_replication(replication_dict, ts_dt): @pytest.fixture -def job_dict(ts_epoch, request_template_dict, date_trigger_dict, replication_dict): +def job_dict(ts_epoch, request_template_dict, date_trigger_dict): """A date job represented as a dictionary.""" return { "name": "job_name", @@ -601,7 +601,6 @@ def job_dict(ts_epoch, request_template_dict, date_trigger_dict, replication_dic "status": "RUNNING", "max_instances": 3, "timeout": 30, - "replication": replication_dict, } @@ -659,39 +658,32 @@ def job_dict_for_import(job_dict): @pytest.fixture -def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger, bg_replication): +def bg_job(job_dict, ts_dt, bg_request_template, bg_date_trigger): """A job as a model.""" dict_copy = copy.deepcopy(job_dict) dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_date_trigger dict_copy["request_template"] = bg_request_template - dict_copy["replication"] = bg_replication return Job(**dict_copy) @pytest.fixture -def bg_cron_job( - cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt, bg_replication -): +def bg_cron_job(cron_job_dict, bg_request_template, bg_cron_trigger, ts_dt): """A beer garden cron job""" dict_copy = copy.deepcopy(cron_job_dict) dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_cron_trigger dict_copy["request_template"] = bg_request_template - dict_copy["replication"] = bg_replication return Job(**dict_copy) @pytest.fixture -def bg_interval_job( - interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt, bg_replication -): +def bg_interval_job(interval_job_dict, bg_request_template, bg_interval_trigger, ts_dt): """A beer garden interval job""" dict_copy = copy.deepcopy(interval_job_dict) dict_copy["next_run_time"] = ts_dt dict_copy["trigger"] = bg_interval_trigger dict_copy["request_template"] = bg_request_template - dict_copy["replication"] = bg_replication return Job(**dict_copy) From ebc8c17dc8fdf9619c59fd6b6d5d16b7183f9345 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:00:49 -0400 Subject: [PATCH 65/79] Replication Events --- brewtils/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 72052e75..5dff4436 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -100,8 +100,10 @@ class Events(Enum): TOPIC_CREATED = 54 TOPIC_UPDATED = 55 TOPIC_REMOVED = 56 + REPLICATION_CREATE = 57 + REPLICATION_UPDATE = 58 - # Next: 57 + # Next: 59 class BaseModel(object): From b78ee1f76195473d0d169a1a243cb91666df73ca Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:34:14 -0400 Subject: [PATCH 66/79] Linting --- brewtils/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 71a421fc..a651fb12 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -102,12 +102,14 @@ class Events(Enum): # Next: 57 + class Permissions(Enum): READ_ONLY = 1 OPERATOR = 2 PLUGIN_ADMIN = 3 GARDEN_ADMIN = 4 + class BaseModel(object): schema = None @@ -1700,7 +1702,7 @@ def __init__( self.name = name self.permission = permission or Permissions.READ_ONLY.name self.description = description - self.id = id + self.id = id self.scope_gardens = scope_gardens or [] self.scope_namespaces = scope_namespaces or [] self.scope_systems = scope_systems or [] From 7a440a626f6d172824d559079540ec5e54f07a82 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 28 Jun 2024 12:00:22 -0400 Subject: [PATCH 67/79] Updated Event --- brewtils/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 5dff4436..d3f7f75c 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -100,8 +100,8 @@ class Events(Enum): TOPIC_CREATED = 54 TOPIC_UPDATED = 55 TOPIC_REMOVED = 56 - REPLICATION_CREATE = 57 - REPLICATION_UPDATE = 58 + REPLICATION_CREATED = 57 + REPLICATION_UPDATED = 58 # Next: 59 From 14786494c62fef81e28f18d9c3dd856ffcc0b3bc Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:47:04 -0400 Subject: [PATCH 68/79] Change log update --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 16ea25b6..e7b03208 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Brewtils Changelog ================== +3.27.0 +------ +TBD + +- Added support models for tracking primary replication + 3.26.2 ------ 6/6/20 From 3887fbc2abb42f323a08c1cd00926c7d8f2b4732 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:30:22 -0400 Subject: [PATCH 69/79] Base Model --- brewtils/models.py | 22 +++++++++++ brewtils/schema_parser.py | 74 +++++++++++++++++++++++++++++++++++++ brewtils/schemas.py | 10 +++++ brewtils/test/comparable.py | 40 +++++++++++++++++++- brewtils/test/fixtures.py | 62 ++++++++++++++++++++++++------- 5 files changed, 193 insertions(+), 15 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 7bfbcbef..ac9db44e 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -4,6 +4,7 @@ import pytz # noqa # not in requirements file import six # noqa # not in requirements file +from datetime import datetime from brewtils.errors import ModelError, _deprecate @@ -423,6 +424,27 @@ def is_different(self, other): return False +class StatusHistory(BaseModel): + schema = "StatusHistorySchema" + + def __init__(self, status=None, heartbeat=None): + self.status = status + self.heartbeat = heartbeat + + +class StatusInfo(BaseModel): + schema = "StatusInfoSchema" + + def __init__(self, heartbeat=None, history=None): + self.heartbeat = heartbeat + self.history = history or [] + + def set_status_heartbeat(self, status): + + self.heartbeat = datetime.utcnow() + self.history.append(StatusHistory(status=status, heartbeat=self.heartbeat)) + + class RequestFile(BaseModel): schema = "RequestFileSchema" diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index cdbc21f4..3041e94e 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -52,6 +52,8 @@ class SchemaParser(object): "ResolvableSchema": brewtils.models.Resolvable, "SubscriberSchema": brewtils.models.Subscriber, "TopicSchema": brewtils.models.Topic, + "StatusInfoSchema": brewtils.models.StatusInfo, + "StatusHistorySchema": brewtils.models.StatusHistory, } logger = logging.getLogger(__name__) @@ -436,6 +438,38 @@ def parse_topic(cls, topic, from_string=False, **kwargs): return cls.parse( topic, brewtils.models.Topic, from_string=from_string, **kwargs ) + + @classmethod + def parse_status_info(cls, status_info, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a status info model object + + Args: + status_info: 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 StatusInfo object + """ + return cls.parse( + status_info, brewtils.models.StatusInfo, from_string=from_string, **kwargs + ) + + @classmethod + def parse_status_history(cls, status_history, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a status history model object + + Args: + status_history: 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 StatusHistory object + """ + return cls.parse( + status_history, brewtils.models.StatusHistory, from_string=from_string, **kwargs + ) @classmethod def parse( @@ -942,6 +976,46 @@ def serialize_topic(cls, topic, to_string=True, **kwargs): schema_name=brewtils.models.Topic.schema, **kwargs ) + + @classmethod + def serialize_status_info(cls, status_info, to_string=True, **kwargs): + """Convert a status info model into serialized form + + Args: + status_info: The status info 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 status_info + """ + return cls.serialize( + status_info, + to_string=to_string, + schema_name=brewtils.models.StatusInfo.schema, + **kwargs + ) + + @classmethod + def serialize_status_history(cls, status_history, to_string=True, **kwargs): + """Convert a status history model into serialized form + + Args: + status_history: The status history 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 status_history + """ + return cls.serialize( + status_history, + to_string=to_string, + schema_name=brewtils.models.StatusHistory.schema, + **kwargs + ) @classmethod def serialize( diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 6bfe08c1..f3f0800b 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -48,6 +48,8 @@ "SystemDomainIdentifierSchema", "SubscriberSchema", "TopicSchema", + "StatusInfoSchema", + "StatusHistorySchema", ] # This will be updated after all the schema classes are defined @@ -355,8 +357,14 @@ class RequestSchema(RequestTemplateSchema): target_garden = fields.String(allow_none=True) +class StatusHistorySchema(BaseSchema): + heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000") + status = fields.Str(allow_none=True) + + class StatusInfoSchema(BaseSchema): heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000") + history = fields.Nested("StatusHistorySchema", many=True, allow_none=True) class PatchSchema(BaseSchema): @@ -690,6 +698,8 @@ class UserListSchema(BaseSchema): "Resolvable": ResolvableSchema, "Subscriber": SubscriberSchema, "Topic": TopicSchema, + "StatusInfo": StatusInfoSchema, + "StatusHistory": StatusHistorySchema, # Compatibility for the Job trigger types "interval": IntervalTriggerSchema, "date": DateTriggerSchema, diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 1dda4a73..d26bfdc7 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -39,6 +39,8 @@ System, Subscriber, Topic, + StatusHistory, + StatusInfo ) __all__ = [ @@ -62,6 +64,8 @@ "assert_runner_equal", "assert_subscriber_equal", "assert_topic_equal", + "assert_status_info_equal", + "assert_status_history_equal", ] @@ -184,7 +188,6 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): # These are the 'simple' models - they don't have any nested models as fields -assert_instance_equal = partial(_assert_wrapper, expected_type=Instance) assert_choices_equal = partial(_assert_wrapper, expected_type=Choices) assert_patch_equal = partial(_assert_wrapper, expected_type=PatchOperation) assert_logging_config_equal = partial(_assert_wrapper, expected_type=LoggingConfig) @@ -196,8 +199,37 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_request_file_equal = partial(_assert_wrapper, expected_type=RequestFile) assert_runner_equal = partial(_assert_wrapper, expected_type=Runner) assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable) -assert_connection_equal = partial(_assert_wrapper, expected_type=Connection) assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber) +assert_status_history_equal = partial(_assert_wrapper, expected_type=StatusHistory) + +def assert_status_info_equal(obj1, obj2, do_raise=False): + return _assert_wrapper( + obj1, + obj2, + expected_type=StatusInfo, + deep_fields={ + "status_history": partial(assert_status_history_equal, do_raise=True), + }, + do_raise=do_raise, + ) + +def assert_instance_equal(obj1, obj2, do_raise=False): + return _assert_wrapper( + obj1, + obj2, + expected_type=Instance, + deep_fields={"status_info": partial(assert_status_info_equal, do_raise=True)}, + do_raise=do_raise, + ) + +def assert_connection_equal(obj1, obj2, do_raise=False): + return _assert_wrapper( + obj1, + obj2, + expected_type=Connection, + deep_fields={"status_info": partial(assert_status_info_equal, do_raise=True)}, + do_raise=do_raise, + ) def assert_command_equal(obj1, obj2, do_raise=False): @@ -390,6 +422,7 @@ def assert_garden_equal(obj1, obj2, do_raise=False): "systems": partial(assert_system_equal, do_raise=True), "receiving_connections": partial(assert_connection_equal, do_raise=True), "publishing_connections": partial(assert_connection_equal, do_raise=True), + "status_info": partial(assert_status_info_equal, do_raise=True), }, do_raise=do_raise, ) @@ -405,3 +438,6 @@ def assert_topic_equal(obj1, obj2, do_raise=False): }, do_raise=do_raise, ) + + + diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 6cb362fc..609d6b95 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -32,6 +32,8 @@ System, Subscriber, Topic, + StatusHistory, + StatusInfo, ) @@ -204,7 +206,7 @@ def bg_command_2(command_dict_2, bg_parameter, system_id): @pytest.fixture -def instance_dict(ts_epoch): +def instance_dict(status_info_dict): """An instance represented as a dictionary.""" return { "id": "584f11af55a38e64799fd1d4", @@ -231,16 +233,16 @@ def instance_dict(ts_epoch): }, "url": "amqp://guest:guest@localhost:5672", }, - "status_info": {"heartbeat": ts_epoch}, + "status_info": status_info_dict, "metadata": {"meta": "data"}, } @pytest.fixture -def bg_instance(instance_dict, ts_dt): +def bg_instance(instance_dict, bg_status_info): """An instance as a model.""" dict_copy = copy.deepcopy(instance_dict) - dict_copy["status_info"]["heartbeat"] = ts_dt + dict_copy["status_info"] = bg_status_info return Instance(**dict_copy) @@ -763,52 +765,85 @@ def bg_request_file(request_file_dict): @pytest.fixture -def connection_dict(): +def connection_dict(status_info_dict): """A connection as a dictionary.""" return { "api": "HTTP", "config": {}, "status": "RECEIVING", - "status_info": {}, + "status_info": status_info_dict, } @pytest.fixture -def connection_publishing_dict(): +def connection_publishing_dict(status_info_dict): """A connection as a dictionary.""" return { "api": "HTTP", "config": {}, "status": "PUBLISHING", - "status_info": {}, + "status_info": status_info_dict, } @pytest.fixture -def bg_connection(connection_dict): +def bg_connection(connection_dict, bg_status_info): """An connection as a model.""" dict_copy = copy.deepcopy(connection_dict) + dict_copy["status_info"] = bg_status_info return Connection(**dict_copy) @pytest.fixture def bg_connection_publishing(connection_publishing_dict): """An connection as a model.""" - dict_copy = copy.deepcopy(connection_publishing_dict) + dict_copy = copy.deepcopy(connection_publishing_dict, bg_status_info) + dict_copy["status_info"] = bg_status_info return Connection(**dict_copy) +@pytest.fixture +def status_history_dict(ts_epoch): + """A status history as a dictionary""" + + return { + "status": "RUNNING", + "heartbeat": ts_epoch, + } + +@pytest.fixture +def bg_status_history(status_history_dict, ts_dt): + dict_copy = copy.deepcopy(status_history_dict) + dict_copy["heartbeat"] = ts_dt + return StatusHistory(**dict_copy) + + +@pytest.fixture +def status_info_dict(ts_epoch, status_history_dict): + """A status info as a dictionary""" + + return { + "heartbeat": ts_epoch, + "history": [status_history_dict] + } + +@pytest.fixture +def bg_status_info(status_info_dict, ts_dt, bg_status_history): + dict_copy = copy.deepcopy(status_info_dict) + dict_copy["history"] = [bg_status_history] + dict_copy["heartbeat"] = ts_dt + return StatusInfo(**dict_copy) @pytest.fixture -def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_dict): +def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_dict, status_info_dict): """A garden as a dictionary.""" return { "id": "123f11af55a38e64799fa1c1", "name": "garden", "status": "RUNNING", - "status_info": {}, + "status_info": status_info_dict, "namespaces": [system_dict["namespace"]], "systems": [system_dict], "connection_type": "http", @@ -822,12 +857,13 @@ def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_di @pytest.fixture -def bg_garden(garden_dict, bg_system, bg_connection, bg_connection_publishing): +def bg_garden(garden_dict, bg_system, bg_connection, bg_connection_publishing, bg_status_info): """An operation as a model.""" dict_copy = copy.deepcopy(garden_dict) dict_copy["systems"] = [bg_system] dict_copy["receiving_connections"] = [bg_connection] dict_copy["publishing_connections"] = [bg_connection_publishing] + dict_copy["status_info"] = [bg_status_info] return Garden(**dict_copy) From d24476c0996fef951f4344b7f1290fcc34108de7 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:32:36 +0000 Subject: [PATCH 70/79] model cleanup --- brewtils/models.py | 26 +++++++++++++++++++++++--- brewtils/test/comparable.py | 2 +- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 7f774e24..2107847c 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -247,7 +247,7 @@ def __init__( self.description = description self.id = id self.status = status.upper() if status else None - self.status_info = status_info or {} + self.status_info = status_info self.queue_type = queue_type self.queue_info = queue_info or {} self.icon_name = icon_name @@ -431,6 +431,18 @@ def __init__(self, status=None, heartbeat=None): self.status = status self.heartbeat = heartbeat + def __str__(self): + return "%s:%s" % ( + self.status, + self.heartbeat, + ) + + def __repr__(self): + return "" % ( + self.status, + self.heartbeat, + ) + class StatusInfo(BaseModel): schema = "StatusInfoSchema" @@ -444,6 +456,14 @@ def set_status_heartbeat(self, status): self.heartbeat = datetime.utcnow() self.history.append(StatusHistory(status=status, heartbeat=self.heartbeat)) + def __str__(self): + return self.heartbeat + + def __repr__(self): + return "" % ( + self.heartbeat, + self.history, + ) class RequestFile(BaseModel): schema = "RequestFileSchema" @@ -1497,7 +1517,7 @@ def __init__( self.id = id self.name = name self.status = status.upper() if status else None - self.status_info = status_info or {} + self.status_info = status_info self.namespaces = namespaces or [] self.systems = systems or [] @@ -1554,7 +1574,7 @@ def __init__( ): self.api = api self.status = status - self.status_info = status_info or {} + self.status_info = status_info self.config = config or {} def __str__(self): diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index d26bfdc7..07dbd19c 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -208,7 +208,7 @@ def assert_status_info_equal(obj1, obj2, do_raise=False): obj2, expected_type=StatusInfo, deep_fields={ - "status_history": partial(assert_status_history_equal, do_raise=True), + "history": partial(assert_status_history_equal, do_raise=True), }, do_raise=do_raise, ) From 0ea6750891a1e4e59f64f2ce28d85b09f9b69816 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:19:06 +0000 Subject: [PATCH 71/79] code cleanup --- brewtils/models.py | 3 +- brewtils/schema_parser.py | 13 +++--- brewtils/test/comparable.py | 12 +++--- brewtils/test/fixtures.py | 27 +++++++----- test/schema_parser_test.py | 83 +++++++++++++++++++++++++++++++++++-- 5 files changed, 112 insertions(+), 26 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 2107847c..7a74afd4 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- +from datetime import datetime from enum import Enum import pytz # noqa # not in requirements file import six # noqa # not in requirements file -from datetime import datetime from brewtils.errors import ModelError, _deprecate @@ -465,6 +465,7 @@ def __repr__(self): self.history, ) + class RequestFile(BaseModel): schema = "RequestFileSchema" diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 3041e94e..f023e97c 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -438,7 +438,7 @@ def parse_topic(cls, topic, from_string=False, **kwargs): return cls.parse( topic, brewtils.models.Topic, from_string=from_string, **kwargs ) - + @classmethod def parse_status_info(cls, status_info, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a status info model object @@ -454,7 +454,7 @@ def parse_status_info(cls, status_info, from_string=False, **kwargs): return cls.parse( status_info, brewtils.models.StatusInfo, from_string=from_string, **kwargs ) - + @classmethod def parse_status_history(cls, status_history, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a status history model object @@ -468,7 +468,10 @@ def parse_status_history(cls, status_history, from_string=False, **kwargs): A StatusHistory object """ return cls.parse( - status_history, brewtils.models.StatusHistory, from_string=from_string, **kwargs + status_history, + brewtils.models.StatusHistory, + from_string=from_string, + **kwargs ) @classmethod @@ -976,7 +979,7 @@ def serialize_topic(cls, topic, to_string=True, **kwargs): schema_name=brewtils.models.Topic.schema, **kwargs ) - + @classmethod def serialize_status_info(cls, status_info, to_string=True, **kwargs): """Convert a status info model into serialized form @@ -996,7 +999,7 @@ def serialize_status_info(cls, status_info, to_string=True, **kwargs): schema_name=brewtils.models.StatusInfo.schema, **kwargs ) - + @classmethod def serialize_status_history(cls, status_history, to_string=True, **kwargs): """Convert a status history model into serialized form diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 07dbd19c..dd22e7b3 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -36,11 +36,11 @@ RequestTemplate, Resolvable, Runner, - System, + StatusHistory, + StatusInfo, Subscriber, + System, Topic, - StatusHistory, - StatusInfo ) __all__ = [ @@ -202,6 +202,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber) assert_status_history_equal = partial(_assert_wrapper, expected_type=StatusHistory) + def assert_status_info_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -213,6 +214,7 @@ def assert_status_info_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) + def assert_instance_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -222,6 +224,7 @@ def assert_instance_equal(obj1, obj2, do_raise=False): do_raise=do_raise, ) + def assert_connection_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, @@ -438,6 +441,3 @@ def assert_topic_equal(obj1, obj2, do_raise=False): }, do_raise=do_raise, ) - - - diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index ac0cc861..7946b193 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -29,11 +29,11 @@ RequestTemplate, Resolvable, Runner, + StatusHistory, + StatusInfo, Subscriber, System, Topic, - StatusHistory, - StatusInfo, ) @@ -798,12 +798,13 @@ def bg_connection(connection_dict, bg_status_info): @pytest.fixture -def bg_connection_publishing(connection_publishing_dict): +def bg_connection_publishing(connection_publishing_dict, bg_status_info): """An connection as a model.""" - dict_copy = copy.deepcopy(connection_publishing_dict, bg_status_info) + dict_copy = copy.deepcopy(connection_publishing_dict) dict_copy["status_info"] = bg_status_info return Connection(**dict_copy) + @pytest.fixture def status_history_dict(ts_epoch): """A status history as a dictionary""" @@ -813,6 +814,7 @@ def status_history_dict(ts_epoch): "heartbeat": ts_epoch, } + @pytest.fixture def bg_status_history(status_history_dict, ts_dt): dict_copy = copy.deepcopy(status_history_dict) @@ -824,10 +826,8 @@ def bg_status_history(status_history_dict, ts_dt): def status_info_dict(ts_epoch, status_history_dict): """A status info as a dictionary""" - return { - "heartbeat": ts_epoch, - "history": [status_history_dict] - } + return {"heartbeat": ts_epoch, "history": [status_history_dict]} + @pytest.fixture def bg_status_info(status_info_dict, ts_dt, bg_status_history): @@ -836,8 +836,11 @@ def bg_status_info(status_info_dict, ts_dt, bg_status_history): dict_copy["heartbeat"] = ts_dt return StatusInfo(**dict_copy) + @pytest.fixture -def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_dict, status_info_dict): +def garden_dict( + ts_epoch, system_dict, connection_dict, connection_publishing_dict, status_info_dict +): """A garden as a dictionary.""" return { @@ -858,13 +861,15 @@ def garden_dict(ts_epoch, system_dict, connection_dict, connection_publishing_di @pytest.fixture -def bg_garden(garden_dict, bg_system, bg_connection, bg_connection_publishing, bg_status_info): +def bg_garden( + garden_dict, bg_system, bg_connection, bg_connection_publishing, bg_status_info +): """An operation as a model.""" dict_copy = copy.deepcopy(garden_dict) dict_copy["systems"] = [bg_system] dict_copy["receiving_connections"] = [bg_connection] dict_copy["publishing_connections"] = [bg_connection_publishing] - dict_copy["status_info"] = [bg_status_info] + dict_copy["status_info"] = bg_status_info return Garden(**dict_copy) diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index 9246dba2..da2205d5 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -5,8 +5,11 @@ import copy -import brewtils.models import pytest +from marshmallow.exceptions import MarshmallowError +from pytest_lazyfixture import lazy_fixture + +import brewtils.models from brewtils.models import System from brewtils.schema_parser import SchemaParser from brewtils.test.comparable import ( @@ -28,12 +31,12 @@ assert_resolvable_equal, assert_role_equal, assert_runner_equal, + assert_status_history_equal, + assert_status_info_equal, assert_subscriber_equal, assert_system_equal, assert_topic_equal, ) -from marshmallow.exceptions import MarshmallowError -from pytest_lazyfixture import lazy_fixture class TestParse(object): @@ -194,6 +197,18 @@ def test_no_modify(self, system_dict): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + brewtils.models.StatusInfo, + lazy_fixture("status_info_dict"), + assert_status_info_equal, + lazy_fixture("bg_status_info"), + ), + ( + brewtils.models.StatusHistory, + lazy_fixture("status_history_dict"), + assert_status_history_equal, + lazy_fixture("bg_status_history"), + ), ], ) def test_single(self, model, data, assertion, expected): @@ -340,6 +355,18 @@ def test_single_from_string(self): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + "parse_status_info", + lazy_fixture("status_info_dict"), + assert_status_info_equal, + lazy_fixture("bg_status_info"), + ), + ( + "parse_status_history", + lazy_fixture("status_history_dict"), + assert_status_history_equal, + lazy_fixture("bg_status_history"), + ), ], ) def test_single_specific(self, method, data, assertion, expected): @@ -479,6 +506,18 @@ def test_single_specific_from_string(self): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + brewtils.models.StatusInfo, + lazy_fixture("status_info_dict"), + assert_status_info_equal, + lazy_fixture("bg_status_info"), + ), + ( + brewtils.models.StatusHistory, + lazy_fixture("status_history_dict"), + assert_status_history_equal, + lazy_fixture("bg_status_history"), + ), ], ) def test_many(self, model, data, assertion, expected): @@ -611,6 +650,18 @@ def test_many(self, model, data, assertion, expected): assert_topic_equal, lazy_fixture("bg_topic"), ), + ( + "parse_status_info", + lazy_fixture("status_info_dict"), + assert_status_info_equal, + lazy_fixture("bg_status_info"), + ), + ( + "parse_status_history", + lazy_fixture("status_history_dict"), + assert_status_history_equal, + lazy_fixture("bg_status_history"), + ), ], ) def test_many_specific(self, method, data, assertion, expected): @@ -666,6 +717,8 @@ class TestSerialize(object): (lazy_fixture("bg_resolvable"), lazy_fixture("resolvable_dict")), (lazy_fixture("bg_subscriber"), lazy_fixture("subscriber_dict")), (lazy_fixture("bg_topic"), lazy_fixture("topic_dict")), + (lazy_fixture("bg_status_info"), lazy_fixture("status_info_dict")), + (lazy_fixture("bg_status_history"), lazy_fixture("status_history_dict")), ], ) def test_single(self, model, expected): @@ -777,6 +830,16 @@ def test_single(self, model, expected): lazy_fixture("bg_topic"), lazy_fixture("topic_dict"), ), + ( + "serialize_status_info", + lazy_fixture("bg_status_info"), + lazy_fixture("status_info_dict"), + ), + ( + "serialize_status_history", + lazy_fixture("bg_status_history"), + lazy_fixture("status_history_dict"), + ), ], ) def test_single_specific(self, method, data, expected): @@ -807,6 +870,8 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_resolvable"), lazy_fixture("resolvable_dict")), (lazy_fixture("bg_subscriber"), lazy_fixture("subscriber_dict")), (lazy_fixture("bg_topic"), lazy_fixture("topic_dict")), + (lazy_fixture("bg_status_info"), lazy_fixture("status_info_dict")), + (lazy_fixture("bg_status_history"), lazy_fixture("status_history_dict")), ], ) def test_many(self, model, expected): @@ -901,6 +966,16 @@ class TestRoundTrip(object): lazy_fixture("bg_subscriber"), ), (brewtils.models.Topic, assert_topic_equal, lazy_fixture("bg_topic")), + ( + brewtils.models.StatusInfo, + assert_status_info_equal, + lazy_fixture("bg_status_info"), + ), + ( + brewtils.models.StatusHistory, + assert_status_history_equal, + lazy_fixture("bg_status_history"), + ), ], ) def test_parsed_start(self, model, assertion, data): @@ -932,6 +1007,8 @@ def test_parsed_start(self, model, assertion, data): (brewtils.models.Operation, lazy_fixture("operation_dict")), (brewtils.models.Runner, lazy_fixture("runner_dict")), (brewtils.models.Resolvable, lazy_fixture("resolvable_dict")), + (brewtils.models.StatusInfo, lazy_fixture("status_info_dict")), + (brewtils.models.StatusHistory, lazy_fixture("status_history_dict")), ], ) def test_serialized_start(self, model, data): From 3e285983b6b1002af2900c2bc83ee4742b08cf30 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:36:02 -0400 Subject: [PATCH 72/79] Update models.py --- brewtils/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 7a74afd4..f9c737f9 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -247,7 +247,7 @@ def __init__( self.description = description self.id = id self.status = status.upper() if status else None - self.status_info = status_info + self.status_info = status_info if status_info else StatusInfo() self.queue_type = queue_type self.queue_info = queue_info or {} self.icon_name = icon_name @@ -1518,7 +1518,7 @@ def __init__( self.id = id self.name = name self.status = status.upper() if status else None - self.status_info = status_info + self.status_info = status_info if status_info else StatusInfo() self.namespaces = namespaces or [] self.systems = systems or [] @@ -1575,7 +1575,7 @@ def __init__( ): self.api = api self.status = status - self.status_info = status_info + self.status_info = status_info if status_info else StatusInfo() self.config = config or {} def __str__(self): From 3771ee4df461cc2af29450def97fbcc0bc7ad344 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:38:01 -0400 Subject: [PATCH 73/79] Update models.py --- brewtils/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index f9c737f9..f9a1ce6f 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import copy from datetime import datetime from enum import Enum @@ -454,7 +455,7 @@ def __init__(self, heartbeat=None, history=None): def set_status_heartbeat(self, status): self.heartbeat = datetime.utcnow() - self.history.append(StatusHistory(status=status, heartbeat=self.heartbeat)) + self.history.append(StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat)) def __str__(self): return self.heartbeat From 066dc47bd11a95b9acd8c88e7bf776af3ffe14f3 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:01:07 -0400 Subject: [PATCH 74/79] deep copy status info in fixtures --- brewtils/test/fixtures.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index 7946b193..132b33c5 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -242,7 +242,7 @@ def instance_dict(status_info_dict): def bg_instance(instance_dict, bg_status_info): """An instance as a model.""" dict_copy = copy.deepcopy(instance_dict) - dict_copy["status_info"] = bg_status_info + dict_copy["status_info"] = copy.deepcopy(bg_status_info) return Instance(**dict_copy) @@ -793,7 +793,7 @@ def connection_publishing_dict(status_info_dict): def bg_connection(connection_dict, bg_status_info): """An connection as a model.""" dict_copy = copy.deepcopy(connection_dict) - dict_copy["status_info"] = bg_status_info + dict_copy["status_info"] = copy.deepcopy(bg_status_info) return Connection(**dict_copy) @@ -801,7 +801,7 @@ def bg_connection(connection_dict, bg_status_info): def bg_connection_publishing(connection_publishing_dict, bg_status_info): """An connection as a model.""" dict_copy = copy.deepcopy(connection_publishing_dict) - dict_copy["status_info"] = bg_status_info + dict_copy["status_info"] = copy.deepcopy(bg_status_info) return Connection(**dict_copy) @@ -832,7 +832,7 @@ def status_info_dict(ts_epoch, status_history_dict): @pytest.fixture def bg_status_info(status_info_dict, ts_dt, bg_status_history): dict_copy = copy.deepcopy(status_info_dict) - dict_copy["history"] = [bg_status_history] + dict_copy["history"] = [copy.deepcopy(bg_status_history)] dict_copy["heartbeat"] = ts_dt return StatusInfo(**dict_copy) @@ -869,7 +869,7 @@ def bg_garden( dict_copy["systems"] = [bg_system] dict_copy["receiving_connections"] = [bg_connection] dict_copy["publishing_connections"] = [bg_connection_publishing] - dict_copy["status_info"] = bg_status_info + dict_copy["status_info"] = copy.deepcopy(bg_status_info) return Garden(**dict_copy) From 1102e428568d1f8ef57a775f4d1c8a2bbe78a7e5 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:25:31 -0400 Subject: [PATCH 75/79] Add max history length --- brewtils/models.py | 5 ++++- test/models_test.py | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index f9a1ce6f..5c2aa1f6 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -452,11 +452,14 @@ def __init__(self, heartbeat=None, history=None): self.heartbeat = heartbeat self.history = history or [] - def set_status_heartbeat(self, status): + def set_status_heartbeat(self, status, max_history=None): self.heartbeat = datetime.utcnow() self.history.append(StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat)) + if max_history and len(self.history) > max_history: + self.history = self.history[(max_history * -1):] + def __str__(self): return self.heartbeat diff --git a/test/models_test.py b/test/models_test.py index ec3f40dc..0946f559 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -20,6 +20,7 @@ RequestTemplate, LegacyRole, Subscriber, + StatusInfo, Topic, ) from pytest_lazyfixture import lazy_fixture @@ -713,3 +714,24 @@ def test_repr(self, topic1, subscriber1): topic1.name, [subscriber1], ) + + +class TestStatusInfo: + + def test_max_history(self): + status_info = StatusInfo() + + max_length = 5 + + for _ in range(10): + status_info.set_status_heartbeat("RUNNING", max_history=max_length) + + assert len(status_info.history) == max_length + + def test_history(self): + status_info = StatusInfo() + + for _ in range(10): + status_info.set_status_heartbeat("RUNNING") + + assert len(status_info.history) == 10 From 010a3c5ec4e749a48c0af312be0bf3bea7e9e412 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:38:40 -0400 Subject: [PATCH 76/79] allow negative checks --- brewtils/models.py | 2 +- test/models_test.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/brewtils/models.py b/brewtils/models.py index 5c2aa1f6..302e5adb 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -457,7 +457,7 @@ def set_status_heartbeat(self, status, max_history=None): self.heartbeat = datetime.utcnow() self.history.append(StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat)) - if max_history and len(self.history) > max_history: + if max_history and max_history > 0 and len(self.history) > max_history: self.history = self.history[(max_history * -1):] def __str__(self): diff --git a/test/models_test.py b/test/models_test.py index 0946f559..6d3e84f3 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -735,3 +735,11 @@ def test_history(self): status_info.set_status_heartbeat("RUNNING") assert len(status_info.history) == 10 + + def test_negative_history(self): + status_info = StatusInfo() + + for _ in range(10): + status_info.set_status_heartbeat("RUNNING", max_history=-1) + + assert len(status_info.history) == 10 From 36f423c4930668702d667e68bf933f02128cc567 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 15 Jul 2024 06:35:17 -0400 Subject: [PATCH 77/79] formatting --- brewtils/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index 302e5adb..c48f33b2 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -455,10 +455,12 @@ def __init__(self, heartbeat=None, history=None): def set_status_heartbeat(self, status, max_history=None): self.heartbeat = datetime.utcnow() - self.history.append(StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat)) + self.history.append( + StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat) + ) if max_history and max_history > 0 and len(self.history) > max_history: - self.history = self.history[(max_history * -1):] + self.history = self.history[(max_history * -1) :] def __str__(self): return self.heartbeat From 243ff22ea4afc31f8aa0bd102fe157644f98def1 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Mon, 15 Jul 2024 08:22:35 -0400 Subject: [PATCH 78/79] change log updates --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 261d77d5..d5377416 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Brewtils Changelog ================== +3.27.0 +------ +TBD + +- Formalized Status Info model and added helper features to track the history of the status changes. + + 3.26.4 ------ 7/12/24 From 554d7258f7994add5cffd978a86572c8cdcf5756 Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:31:16 -0400 Subject: [PATCH 79/79] formatting --- brewtils/schema_parser.py | 3 ++- brewtils/test/comparable.py | 1 - test/schema_parser_test.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index ded758a6..86e7a22c 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -504,6 +504,7 @@ def parse_status_history(cls, status_history, from_string=False, **kwargs): from_string=from_string, **kwargs ) + @classmethod def parse_replication(cls, replication, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a replication model object @@ -1099,7 +1100,7 @@ def serialize_status_history(cls, status_history, to_string=True, **kwargs): schema_name=brewtils.models.StatusHistory.schema, **kwargs ) - + @classmethod def serialize_replication(cls, replication, to_string=True, **kwargs): """Convert a replication model into serialized form diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index fb56d399..bcf4f5de 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -247,7 +247,6 @@ def assert_connection_equal(obj1, obj2, do_raise=False): assert_replication_equal = partial(_assert_wrapper, expected_type=Replication) - def assert_command_equal(obj1, obj2, do_raise=False): return _assert_wrapper( obj1, diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index ebe5920f..d49652ee 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -587,7 +587,7 @@ def test_single_specific_from_string(self): lazy_fixture("status_history_dict"), assert_status_history_equal, lazy_fixture("bg_status_history"), - ), + ), ( brewtils.models.Replication, lazy_fixture("replication_dict"), @@ -958,7 +958,7 @@ def test_single(self, model, expected): "serialize_status_history", lazy_fixture("bg_status_history"), lazy_fixture("status_history_dict"), - ), + ), ( "serialize_replication", lazy_fixture("bg_replication"),