Repository: getmoto/moto
Pull request #7903 (2024-07-29 22:05:33)

Bump ruby/setup-ruby from 1.188.0 to 1.190.0
Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.188.0 to 1.190.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ruby/setup-ruby/releases">ruby/setup-ruby's releases</a>.</em></p>
<blockquote>
<h2>v1.190.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update CRuby releases on Windows by <a href="https://github.com/ruby-builder-bot"><code>@​ruby-builder-bot</code></a> in <a href="https://redirect.github.com/ruby/setup-ruby/pull/628">ruby/setup-ruby#628</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ruby/setup-ruby/compare/v1.189.0...v1.190.0">https://github.com/ruby/setup-ruby/compare/v1.189.0...v1.190.0</a></p>
<h2>v1.189.0</h2>
<h2>What's Changed</h2>
<ul>
<li>docs: update ruby-version comment by <a href="https://github.com/chenrui333"><code>@​chenrui333</code></a> in <a href="https://redirect.github.com/ruby/setup-ruby/pull/626">ruby/setup-ruby#626</a></li>
<li>Add ruby-3.2.5 by <a href="https://github.com/ruby-builder-bot"><code>@​ruby-builder-bot</code></a> in <a href="https://redirect.github.com/ruby/setup-ruby/pull/627">ruby/setup-ruby#627</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/chenrui333"><code>@​chenrui333</code></a> made their first contribution in <a href="https://redirect.github.com/ruby/setup-ruby/pull/626">ruby/setup-ruby#626</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ruby/setup-ruby/compare/v1.188.0...v1.189.0">https://github.com/ruby/setup-ruby/compare/v1.188.0...v1.189.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/ruby/setup-ruby/commit/a6e6f86333f0a2523ece813039b8b4be04560854"><code>a6e6f86</code></a> Update CRuby releases on Windows</li>
<li><a href="https://github.com/ruby/setup-ruby/commit/bc2ba926aade0711935b50bcb1b9ccdb5fde2dda"><code>bc2ba92</code></a> Add ruby-3.2.5</li>
<li><a href="https://github.com/ruby/setup-ruby/commit/371544706a2f9a089bd1c41cfdfccf8f8aed25e4"><code>3715447</code></a> docs: update ruby-version comment</li>
<li>See full diff in <a href="https://github.com/ruby/setup-ruby/compare/50ba3386b050ad5b97a41fcb81240cbee1d1821f...a6e6f86333f0a2523ece813039b8b4be04560854">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ruby/setup-ruby&package-manager=github_actions&previous-version=1.188.0&new-version=1.190.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/moto/rds/models.py b/moto/rds/models.py
index f880b958f..3e14fa973 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -69,7 +69,31 @@ def find_cluster(cluster_arn: str) -> "DBCluster":
     return rds_backends[account][region].describe_db_clusters(cluster_arn)[0]
 
 
-class RDSBaseModel(BaseModel):
+class TaggingMixin:
+    _tags: List[Dict[str, str]] = []
+
+    @property
+    def tags(self) -> List[Dict[str, str]]:
+        return self._tags
+
+    @tags.setter
+    def tags(self, value: List[Dict[str, str]]) -> None:
+        self._tags = value
+
+    def get_tags(self) -> List[Dict[str, str]]:
+        return self.tags
+
+    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
+        new_keys = [tag_set["Key"] for tag_set in tags]
+        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
+        self.tags.extend(tags)
+        return self.tags
+
+    def remove_tags(self, tag_keys: List[str]) -> None:
+        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
+
+
+class RDSBaseModel(TaggingMixin, BaseModel):
     resource_type: str
 
     def __init__(self, backend: "RDSBackend"):
@@ -545,18 +569,6 @@ class DBCluster(RDSBaseModel):
             "postgres": {"gp2": 20, "io1": 100, "standard": 5},
         }[engine][storage_type]
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
 
 class DBClusterSnapshot(RDSBaseModel):
     resource_type = "cluster-snapshot"
@@ -633,18 +645,6 @@ class DBClusterSnapshot(RDSBaseModel):
         )
         return template.render(snapshot=self, cluster=self.cluster)
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
 
 class DBInstance(CloudFormationModel, RDSBaseModel):
     SUPPORTED_FILTERS = {
@@ -1115,82 +1115,6 @@ class DBInstance(CloudFormationModel, RDSBaseModel):
             database = rds_backend.create_db_instance(db_kwargs)
         return database
 
-    def to_json(self) -> str:
-        template = Template(
-            """{
-        "AllocatedStorage": 10,
-        "AutoMinorVersionUpgrade": "{{ database.auto_minor_version_upgrade }}",
-        "AvailabilityZone": "{{ database.availability_zone }}",
-        "BackupRetentionPeriod": "{{ database.backup_retention_period }}",
-        "CharacterSetName": {%- if database.character_set_name -%}{{ database.character_set_name }}{%- else %} null{%- endif -%},
-        "DBInstanceClass": "{{ database.db_instance_class }}",
-        {%- if database.db_cluster_identifier -%}"DBClusterIdentifier": "{{ database.db_cluster_identifier }}",{%- endif -%}
-        "DBInstanceIdentifier": "{{ database.db_instance_identifier }}",
-        "DBInstanceStatus": "{{ database.status }}",
-        "DBName": {%- if database.db_name -%}"{{ database.db_name }}"{%- else %} null{%- endif -%},
-        {% if database.db_parameter_group_name -%}"DBParameterGroups": {
-            "DBParameterGroup": {
-            "ParameterApplyStatus": "in-sync",
-            "DBParameterGroupName": "{{ database.db_parameter_group_name }}"
-          }
-        },{%- endif %}
-        "DBSecurityGroups": [
-          {% for security_group in database.security_groups -%}{%- if loop.index != 1 -%},{%- endif -%}
-          {"DBSecurityGroup": {
-            "Status": "active",
-            "DBSecurityGroupName": "{{ security_group }}"
-          }}{% endfor %}
-        ],
-        {%- if database.db_subnet_group -%}{{ database.db_subnet_group.to_json() }},{%- endif %}
-        "Engine": "{{ database.engine }}",
-        "EngineVersion": "{{ database.engine_version }}",
-        "LatestRestorableTime": null,
-        "LicenseModel": "{{ database.license_model }}",
-        "MasterUsername": "{{ database.master_username }}",
-        "MultiAZ": "{{ database.multi_az }}",{% if database.option_group_name %}
-        "OptionGroupMemberships": [{
-          "OptionGroupMembership": {
-            "OptionGroupName": "{{ database.option_group_name }}",
-            "Status": "in-sync"
-          }
-        }],{%- endif %}
-        "PendingModifiedValues": { "MasterUserPassword": "****" },
-        "PreferredBackupWindow": "{{ database.preferred_backup_window }}",
-        "PreferredMaintenanceWindow": "{{ database.preferred_maintenance_window }}",
-        "PubliclyAccessible": "{{ database.publicly_accessible }}",
-        "CopyTagsToSnapshot": "{{ database.copy_tags_to_snapshot }}",
-        "AllocatedStorage": "{{ database.allocated_storage }}",
-        "Endpoint": {
-            "Address": "{{ database.address }}",
-            "Port": "{{ database.port }}"
-        },
-        "InstanceCreateTime": "{{ database.instance_create_time }}",
-        "Iops": null,
-        "ReadReplicaDBInstanceIdentifiers": [{%- for replica in database.replicas -%}
-            {%- if not loop.first -%},{%- endif -%}
-            "{{ replica }}"
-        {%- endfor -%}
-        ],
-        {%- if database.source_db_identifier -%}
-        "ReadReplicaSourceDBInstanceIdentifier": "{{ database.source_db_identifier }}",
-        {%- else -%}
-        "ReadReplicaSourceDBInstanceIdentifier": null,
-        {%- endif -%}
-        "SecondaryAvailabilityZone": null,
-        "StatusInfos": null,
-        "VpcSecurityGroups": [
-            {% for vpc_security_group_id in database.vpc_security_group_ids %}
-            {
-                "Status": "active",
-                "VpcSecurityGroupId": "{{ vpc_security_group_id }}"
-            }
-            {% endfor %}
-        ],
-        "DBInstanceArn": "{{ database.db_instance_arn }}"
-      }"""
-        )
-        return template.render(database=self)
-
     def get_tags(self) -> List[Dict[str, str]]:
         return self.tags
 
@@ -1208,7 +1132,7 @@ class DBInstance(CloudFormationModel, RDSBaseModel):
         backend.delete_db_instance(self.db_instance_identifier)  # type: ignore[arg-type]
 
 
-class DBInstanceSnapshot(RDSBaseModel):
+class DBSnapshot(RDSBaseModel):
     resource_type = "snapshot"
     SUPPORTED_FILTERS = {
         "db-instance-id": FilterDef(
@@ -1297,23 +1221,11 @@ class DBInstanceSnapshot(RDSBaseModel):
         )
         return template.render(snapshot=self, database=self.database)
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
 
 class ExportTask(BaseModel):
     def __init__(
         self,
-        snapshot: Union[DBInstanceSnapshot, DBClusterSnapshot],
+        snapshot: Union[DBSnapshot, DBClusterSnapshot],
         kwargs: Dict[str, Any],
     ):
         self.snapshot = snapshot
@@ -1328,9 +1240,7 @@ class ExportTask(BaseModel):
 
         self.status = "complete"
         self.created_at = iso_8601_datetime_with_milliseconds()
-        self.source_type = (
-            "SNAPSHOT" if type(snapshot) is DBInstanceSnapshot else "CLUSTER"
-        )
+        self.source_type = "SNAPSHOT" if type(snapshot) is DBSnapshot else "CLUSTER"
 
     def to_xml(self) -> str:
         template = Template(
@@ -1415,18 +1325,6 @@ class EventSubscription(RDSBaseModel):
         )
         return template.render(subscription=self)
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
 
 class DBSecurityGroup(CloudFormationModel, RDSBaseModel):
     resource_type = "secgrp"
@@ -1481,23 +1379,6 @@ class DBSecurityGroup(CloudFormationModel, RDSBaseModel):
         )
         return template.render(security_group=self)
 
-    def to_json(self) -> str:
-        template = Template(
-            """{
-            "DBSecurityGroupDescription": "{{ security_group.description }}",
-            "DBSecurityGroupName": "{{ security_group.group_name }}",
-            "EC2SecurityGroups": {{ security_group.ec2_security_groups }},
-            "IPRanges": [{%- for ip in security_group.ip_ranges -%}
-                         {%- if loop.index != 1 -%},{%- endif -%}
-                         "{{ ip }}"
-                         {%- endfor -%}
-                        ],
-            "OwnerId": "{{ security_group.owner_id }}",
-            "VpcId": "{{ security_group.vpc_id }}"
-        }"""
-        )
-        return template.render(security_group=self)
-
     def authorize_cidr(self, cidr_ip: str) -> None:
         self.ip_ranges.append(cidr_ip)
 
@@ -1545,18 +1426,6 @@ class DBSecurityGroup(CloudFormationModel, RDSBaseModel):
                     security_group.authorize_security_group(subnet)  # type: ignore[arg-type]
         return security_group
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
     def delete(self, account_id: str, region_name: str) -> None:
         backend = rds_backends[account_id][region_name]
         backend.delete_security_group(self.group_name)
@@ -1609,29 +1478,6 @@ class DBSubnetGroup(CloudFormationModel, RDSBaseModel):
         )
         return template.render(subnet_group=self)
 
-    def to_json(self) -> str:
-        template = Template(
-            """"DBSubnetGroup": {
-                "VpcId": "{{ subnet_group.vpc_id }}",
-                "SubnetGroupStatus": "{{ subnet_group.status }}",
-                "DBSubnetGroupDescription": "{{ subnet_group.description }}",
-                "DBSubnetGroupName": "{{ subnet_group.subnet_name }}",
-                "Subnets": {
-                  "Subnet": [
-                    {% for subnet in subnet_group.subnets %}{
-                      "SubnetStatus": "Active",
-                      "SubnetIdentifier": "{{ subnet.id }}",
-                      "SubnetAvailabilityZone": {
-                        "Name": "{{ subnet.availability_zone }}",
-                        "ProvisionedIopsCapable": "false"
-                      }
-                    }{%- if not loop.last -%},{%- endif -%}{% endfor %}
-                  ]
-                }
-            }"""
-        )
-        return template.render(subnet_group=self)
-
     @staticmethod
     def cloudformation_name_type() -> str:
         return "DBSubnetGroupName"
@@ -1667,18 +1513,6 @@ class DBSubnetGroup(CloudFormationModel, RDSBaseModel):
         )
         return subnet_group
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
     def delete(self, account_id: str, region_name: str) -> None:
         backend = rds_backends[account_id][region_name]
         backend.delete_subnet_group(self.subnet_name)
@@ -1750,18 +1584,6 @@ class DBProxy(RDSBaseModel):
     def name(self) -> str:
         return self.db_proxy_name
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
     def to_xml(self) -> str:
         template = Template(
             """
@@ -1816,7 +1638,7 @@ class RDSBackend(BaseBackend):
         self.clusters: Dict[str, DBCluster] = OrderedDict()
         self.global_clusters: Dict[str, GlobalCluster] = OrderedDict()
         self.databases: Dict[str, DBInstance] = OrderedDict()
-        self.database_snapshots: Dict[str, DBInstanceSnapshot] = OrderedDict()
+        self.database_snapshots: Dict[str, DBSnapshot] = OrderedDict()
         self.cluster_snapshots: Dict[str, DBClusterSnapshot] = OrderedDict()
         self.export_tasks: Dict[str, ExportTask] = OrderedDict()
         self.event_subscriptions: Dict[str, EventSubscription] = OrderedDict()
@@ -1877,7 +1699,7 @@ class RDSBackend(BaseBackend):
         self,
         db_instance_identifier: str,
         db_snapshot_identifier: str,
-    ) -> DBInstanceSnapshot:
+    ) -> DBSnapshot:
         return self.create_db_snapshot(
             db_instance_identifier, db_snapshot_identifier, snapshot_type="automated"
         )
@@ -1888,7 +1710,7 @@ class RDSBackend(BaseBackend):
         db_snapshot_identifier: str,
         snapshot_type: str = "manual",
         tags: Optional[List[Dict[str, str]]] = None,
-    ) -> DBInstanceSnapshot:
+    ) -> DBSnapshot:
         if isinstance(db_instance, str):
             database = self.databases.get(db_instance)
             if not database:
@@ -1906,7 +1728,7 @@ class RDSBackend(BaseBackend):
             tags = list()
         if database.copy_tags_to_snapshot and not tags:
             tags = database.get_tags()
-        snapshot = DBInstanceSnapshot(
+        snapshot = DBSnapshot(
             self, database, db_snapshot_identifier, snapshot_type, tags
         )
         self.database_snapshots[db_snapshot_identifier] = snapshot
@@ -1917,7 +1739,7 @@ class RDSBackend(BaseBackend):
         source_snapshot_identifier: str,
         target_snapshot_identifier: str,
         tags: Optional[List[Dict[str, str]]] = None,
-    ) -> DBInstanceSnapshot:
+    ) -> DBSnapshot:
         if source_snapshot_identifier not in self.database_snapshots:
             raise DBSnapshotNotFoundError(source_snapshot_identifier)
 
@@ -1932,7 +1754,7 @@ class RDSBackend(BaseBackend):
             tags=tags,
         )
 
-    def delete_db_snapshot(self, db_snapshot_identifier: str) -> DBInstanceSnapshot:
+    def delete_db_snapshot(self, db_snapshot_identifier: str) -> DBSnapshot:
         if db_snapshot_identifier not in self.database_snapshots:
             raise DBSnapshotNotFoundError(db_snapshot_identifier)
 
@@ -1981,7 +1803,7 @@ class RDSBackend(BaseBackend):
         db_instance_identifier: Optional[str],
         db_snapshot_identifier: str,
         filters: Optional[Dict[str, Any]] = None,
-    ) -> List[DBInstanceSnapshot]:
+    ) -> List[DBSnapshot]:
         snapshots = self.database_snapshots
         if db_instance_identifier:
             filters = merge_filters(
@@ -1992,7 +1814,7 @@ class RDSBackend(BaseBackend):
                 filters, {"db-snapshot-id": [db_snapshot_identifier]}
             )
         if filters:
-            snapshots = self._filter_resources(snapshots, filters, DBInstanceSnapshot)
+            snapshots = self._filter_resources(snapshots, filters, DBSnapshot)
         if db_snapshot_identifier and not snapshots and not db_instance_identifier:
             raise DBSnapshotNotFoundError(db_snapshot_identifier)
         return list(snapshots.values())
@@ -2714,9 +2536,9 @@ class RDSBackend(BaseBackend):
             raise DBClusterSnapshotNotFoundError(snapshot_id)
 
         if snapshot_type == "snapshot":
-            snapshot: Union[DBInstanceSnapshot, DBClusterSnapshot] = (
-                self.database_snapshots[snapshot_id]
-            )
+            snapshot: Union[DBSnapshot, DBClusterSnapshot] = self.database_snapshots[
+                snapshot_id
+            ]
         else:
             snapshot = self.cluster_snapshots[snapshot_id]
 
@@ -3083,10 +2905,9 @@ class RDSBackend(BaseBackend):
             if attribute["AttributeName"] == attribute_name:
                 attribute_present = True
                 if values_to_add:
-                    attribute["AttributeValues"] = (
-                        values_to_add["AttributeValue"].values()
-                        + attribute["AttributeValues"]
-                    )
+                    attribute["AttributeValues"] = list(
+                        attribute["AttributeValues"]
+                    ) + list(values_to_add["AttributeValue"].values())
                 if values_to_remove:
                     attribute["AttributeValues"] = [
                         i
@@ -3127,10 +2948,9 @@ class RDSBackend(BaseBackend):
             if attribute["AttributeName"] == attribute_name:
                 attribute_present = True
                 if values_to_add:
-                    attribute["AttributeValues"] = (
-                        values_to_add["AttributeValue"].values()
-                        + attribute["AttributeValues"]
-                    )
+                    attribute["AttributeValues"] = list(
+                        attribute["AttributeValues"]
+                    ) + list(values_to_add["AttributeValue"].values())
                 if values_to_remove:
                     attribute["AttributeValues"] = [
                         i
@@ -3221,21 +3041,6 @@ class OptionGroup(RDSBaseModel):
     def name(self) -> str:
         return self._name
 
-    def to_json(self) -> str:
-        template = Template(
-            """{
-    "VpcId": null,
-    "MajorEngineVersion": "{{ option_group.major_engine_version }}",
-    "OptionGroupDescription": "{{ option_group.description }}",
-    "AllowsVpcAndNonVpcInstanceMemberships": "{{ option_group.vpc_and_non_vpc_instance_memberships }}",
-    "EngineName": "{{ option_group.engine_name }}",
-    "Options": [],
-    "OptionGroupName": "{{ option_group.name }},
-    "OptionGroupArn": "{{ option_group.arn }}
-}"""
-        )
-        return template.render(option_group=self)
-
     def to_xml(self) -> str:
         template = Template(
             """<OptionGroup>
@@ -3266,18 +3071,6 @@ class OptionGroup(RDSBaseModel):
         # error
         return
 
-    def get_tags(self) -> List[Dict[str, str]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
 
 class DBParameterGroup(CloudFormationModel, RDSBaseModel):
     resource_type = "pg"
@@ -3312,18 +3105,6 @@ class DBParameterGroup(CloudFormationModel, RDSBaseModel):
         )
         return template.render(param_group=self)
 
-    def get_tags(self) -> List[Dict[str, Any]]:
-        return self.tags
-
-    def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, Any]]:
-        new_keys = [tag_set["Key"] for tag_set in tags]
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
-        self.tags.extend(tags)
-        return self.tags
-
-    def remove_tags(self, tag_keys: List[str]) -> None:
-        self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
-
     def update_parameters(self, new_parameters: Iterable[Dict[str, Any]]) -> None:
         for new_parameter in new_parameters:
             parameter = self.parameters[new_parameter["ParameterName"]]


Test patch:
diff --git a/.github/workflows/tests_sdk_ruby.yml b/.github/workflows/tests_sdk_ruby.yml
index 411ab3d32..87f08bcb7 100644
--- a/.github/workflows/tests_sdk_ruby.yml
+++ b/.github/workflows/tests_sdk_ruby.yml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Set up Ruby ${{ matrix.ruby-version }}
-        uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f
+        uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854
         with:
           ruby-version: ${{ matrix.ruby-version }}
       - name: Set up Python 3.8
diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py
index bf06b5645..ac70f9c57 100644
--- a/tests/test_rds/test_rds.py
+++ b/tests/test_rds/test_rds.py
@@ -2780,6 +2780,11 @@ def test_modify_db_snapshot_attribute():
         AttributeName="restore",
         ValuesToRemove=["Test"],
     )
+    resp = client.modify_db_snapshot_attribute(
+        DBSnapshotIdentifier="snapshot-1",
+        AttributeName="restore",
+        ValuesToAdd=["Test3"],
+    )
 
     assert (
         resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0]["AttributeName"]
@@ -2787,7 +2792,7 @@ def test_modify_db_snapshot_attribute():
     )
     assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0][
         "AttributeValues"
-    ] == ["Test2"]
+    ] == ["Test2", "Test3"]
 
 
 def validation_helper(exc):
diff --git a/tests/test_rds/test_rds_clusters.py b/tests/test_rds/test_rds_clusters.py
index d7bd70aa9..f4fabf822 100644
--- a/tests/test_rds/test_rds_clusters.py
+++ b/tests/test_rds/test_rds_clusters.py
@@ -1090,6 +1090,11 @@ def test_modify_db_cluster_snapshot_attribute():
         AttributeName="restore",
         ValuesToRemove=["test"],
     )
+    resp = conn.modify_db_cluster_snapshot_attribute(
+        DBClusterSnapshotIdentifier="g-1",
+        AttributeName="restore",
+        ValuesToAdd=["test3"],
+    )
     assert (
         resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotIdentifier"]
         == "g-1"
@@ -1102,7 +1107,7 @@ def test_modify_db_cluster_snapshot_attribute():
     )
     assert resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"][0][
         "AttributeValues"
-    ] == ["test2"]
+    ] == ["test2", "test3"]
 
 
 @mock_aws