Repository: getmoto/moto
Pull request #8993 (2025-06-17 06:38:37)

ServiceDiscovery: create hosted zone when created public or private dns namespaces


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 6b6029df3..13e26d002 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -51,23 +51,20 @@
 
 ## amp
 <details>
-<summary>51% implemented</summary>
+<summary>58% implemented</summary>
 
 - [ ] create_alert_manager_definition
 - [X] create_logging_configuration
-- [ ] create_query_logging_configuration
 - [X] create_rule_groups_namespace
 - [ ] create_scraper
 - [X] create_workspace
 - [ ] delete_alert_manager_definition
 - [X] delete_logging_configuration
-- [ ] delete_query_logging_configuration
 - [X] delete_rule_groups_namespace
 - [ ] delete_scraper
 - [X] delete_workspace
 - [ ] describe_alert_manager_definition
 - [X] describe_logging_configuration
-- [ ] describe_query_logging_configuration
 - [X] describe_rule_groups_namespace
 - [ ] describe_scraper
 - [X] describe_workspace
@@ -82,7 +79,6 @@
 - [X] tag_resource
 - [X] untag_resource
 - [X] update_logging_configuration
-- [ ] update_query_logging_configuration
 - [ ] update_scraper
 - [X] update_workspace_alias
 - [ ] update_workspace_configuration
@@ -229,7 +225,7 @@
 
 ## apigatewayv2
 <details>
-<summary>70% implemented</summary>
+<summary>75% implemented</summary>
 
 - [X] create_api
 - [X] create_api_mapping
@@ -241,7 +237,6 @@
 - [X] create_model
 - [X] create_route
 - [X] create_route_response
-- [ ] create_routing_rule
 - [X] create_stage
 - [X] create_vpc_link
 - [ ] delete_access_log_settings
@@ -258,7 +253,6 @@
 - [X] delete_route_request_parameter
 - [X] delete_route_response
 - [ ] delete_route_settings
-- [ ] delete_routing_rule
 - [X] delete_stage
 - [X] delete_vpc_link
 - [ ] export_api
@@ -283,15 +277,12 @@
 - [X] get_route_response
 - [ ] get_route_responses
 - [X] get_routes
-- [ ] get_routing_rule
 - [X] get_stage
 - [X] get_stages
 - [X] get_tags
 - [X] get_vpc_link
 - [X] get_vpc_links
 - [ ] import_api
-- [ ] list_routing_rules
-- [ ] put_routing_rule
 - [X] reimport_api
 - [ ] reset_authorizers_cache
 - [X] tag_resource
@@ -964,7 +955,7 @@
 
 ## ce
 <details>
-<summary>17% implemented</summary>
+<summary>18% implemented</summary>
 
 - [ ] create_anomaly_monitor
 - [ ] create_anomaly_subscription
@@ -979,10 +970,8 @@
 - [ ] get_approximate_usage_records
 - [ ] get_commitment_purchase_analysis
 - [X] get_cost_and_usage
-- [ ] get_cost_and_usage_comparisons
 - [ ] get_cost_and_usage_with_resources
 - [ ] get_cost_categories
-- [ ] get_cost_comparison_drivers
 - [ ] get_cost_forecast
 - [ ] get_dimension_values
 - [ ] get_reservation_coverage
@@ -1359,7 +1348,7 @@
 
 ## cloudtrail
 <details>
-<summary>27% implemented</summary>
+<summary>28% implemented</summary>
 
 - [X] add_tags
 - [ ] cancel_query
@@ -1380,7 +1369,6 @@
 - [ ] generate_query
 - [ ] get_channel
 - [ ] get_dashboard
-- [ ] get_event_configuration
 - [ ] get_event_data_store
 - [X] get_event_selectors
 - [ ] get_import
@@ -1400,7 +1388,6 @@
 - [X] list_tags
 - [X] list_trails
 - [ ] lookup_events
-- [ ] put_event_configuration
 - [X] put_event_selectors
 - [X] put_insight_selectors
 - [ ] put_resource_policy
@@ -2064,27 +2051,27 @@
 
 ## connectcampaigns
 <details>
-<summary>22% implemented</summary>
+<summary>63% implemented</summary>
 
 - [X] create_campaign
 - [X] delete_campaign
 - [ ] delete_connect_instance_config
 - [ ] delete_instance_onboarding_job
 - [X] describe_campaign
-- [ ] get_campaign_state
+- [X] get_campaign_state
 - [ ] get_campaign_state_batch
 - [X] get_connect_instance_config
 - [ ] get_instance_onboarding_job_status
-- [ ] list_campaigns
-- [ ] list_tags_for_resource
-- [ ] pause_campaign
+- [X] list_campaigns
+- [X] list_tags_for_resource
+- [X] pause_campaign
 - [ ] put_dial_request_batch
-- [ ] resume_campaign
-- [ ] start_campaign
+- [X] resume_campaign
+- [X] start_campaign
 - [X] start_instance_onboarding_job
-- [ ] stop_campaign
-- [ ] tag_resource
-- [ ] untag_resource
+- [X] stop_campaign
+- [X] tag_resource
+- [X] untag_resource
 - [ ] update_campaign_dialer_config
 - [ ] update_campaign_name
 - [ ] update_campaign_outbound_call_config
@@ -3093,7 +3080,6 @@
 - [ ] export_image
 - [ ] export_transit_gateway_routes
 - [ ] export_verified_access_instance_client_configuration
-- [ ] get_active_vpn_tunnel_status
 - [ ] get_allowed_images_settings
 - [ ] get_associated_enclave_certificate_iam_roles
 - [ ] get_associated_ipv6_pool_cidrs
@@ -3192,7 +3178,6 @@
 - [X] modify_managed_prefix_list
 - [X] modify_network_interface_attribute
 - [ ] modify_private_dns_name_options
-- [ ] modify_public_ip_dns_name_options
 - [ ] modify_reserved_instances
 - [ ] modify_route_server
 - [X] modify_security_group_rules
@@ -6262,7 +6247,7 @@
 
 ## network-firewall
 <details>
-<summary>10% implemented</summary>
+<summary>11% implemented</summary>
 
 - [ ] associate_firewall_policy
 - [ ] associate_subnets
@@ -6270,15 +6255,12 @@
 - [ ] create_firewall_policy
 - [ ] create_rule_group
 - [ ] create_tls_inspection_configuration
-- [ ] create_vpc_endpoint_association
 - [ ] delete_firewall
 - [ ] delete_firewall_policy
 - [ ] delete_resource_policy
 - [ ] delete_rule_group
 - [ ] delete_tls_inspection_configuration
-- [ ] delete_vpc_endpoint_association
 - [X] describe_firewall
-- [ ] describe_firewall_metadata
 - [ ] describe_firewall_policy
 - [ ] describe_flow_operation
 - [X] describe_logging_configuration
@@ -6286,7 +6268,6 @@
 - [ ] describe_rule_group
 - [ ] describe_rule_group_metadata
 - [ ] describe_tls_inspection_configuration
-- [ ] describe_vpc_endpoint_association
 - [ ] disassociate_subnets
 - [ ] get_analysis_report_results
 - [ ] list_analysis_reports
@@ -6297,7 +6278,6 @@
 - [ ] list_rule_groups
 - [ ] list_tags_for_resource
 - [ ] list_tls_inspection_configurations
-- [ ] list_vpc_endpoint_associations
 - [ ] put_resource_policy
 - [ ] start_analysis_report
 - [ ] start_flow_capture
@@ -10214,7 +10194,6 @@
 - eks-auth
 - entityresolution
 - evidently
-- evs
 - finspace
 - finspace-data
 - fis
diff --git a/docs/docs/services/amp.rst b/docs/docs/services/amp.rst
index af7c00e82..471e3ec1a 100644
--- a/docs/docs/services/amp.rst
+++ b/docs/docs/services/amp.rst
@@ -18,7 +18,6 @@ amp
 
 - [ ] create_alert_manager_definition
 - [X] create_logging_configuration
-- [ ] create_query_logging_configuration
 - [X] create_rule_groups_namespace
   
         The ClientToken-parameter is not yet implemented
@@ -32,7 +31,6 @@ amp
 
 - [ ] delete_alert_manager_definition
 - [X] delete_logging_configuration
-- [ ] delete_query_logging_configuration
 - [X] delete_rule_groups_namespace
   
         The ClientToken-parameter is not yet implemented
@@ -46,7 +44,6 @@ amp
 
 - [ ] describe_alert_manager_definition
 - [X] describe_logging_configuration
-- [ ] describe_query_logging_configuration
 - [X] describe_rule_groups_namespace
 - [ ] describe_scraper
 - [X] describe_workspace
@@ -65,7 +62,6 @@ amp
 - [X] tag_resource
 - [X] untag_resource
 - [X] update_logging_configuration
-- [ ] update_query_logging_configuration
 - [ ] update_scraper
 - [X] update_workspace_alias
   
diff --git a/docs/docs/services/apigatewayv2.rst b/docs/docs/services/apigatewayv2.rst
index dc1eb4488..17e5bd765 100644
--- a/docs/docs/services/apigatewayv2.rst
+++ b/docs/docs/services/apigatewayv2.rst
@@ -35,7 +35,6 @@ apigatewayv2
         The following parameters are not yet implemented: ResponseModels, ResponseParameters
         
 
-- [ ] create_routing_rule
 - [X] create_stage
 - [X] create_vpc_link
 - [ ] delete_access_log_settings
@@ -52,7 +51,6 @@ apigatewayv2
 - [X] delete_route_request_parameter
 - [X] delete_route_response
 - [ ] delete_route_settings
-- [ ] delete_routing_rule
 - [X] delete_stage
 - [X] delete_vpc_link
 - [ ] export_api
@@ -93,15 +91,12 @@ apigatewayv2
         Pagination is not yet implemented
         
 
-- [ ] get_routing_rule
 - [X] get_stage
 - [X] get_stages
 - [X] get_tags
 - [X] get_vpc_link
 - [X] get_vpc_links
 - [ ] import_api
-- [ ] list_routing_rules
-- [ ] put_routing_rule
 - [X] reimport_api
   
         Only YAML is supported at the moment. Full OpenAPI-support is not guaranteed. Only limited validation is implemented
diff --git a/docs/docs/services/ce.rst b/docs/docs/services/ce.rst
index 7ee305e76..34a56e40b 100644
--- a/docs/docs/services/ce.rst
+++ b/docs/docs/services/ce.rst
@@ -84,10 +84,8 @@ ce
             resp = ce.get_cost_and_usage(...)
         
 
-- [ ] get_cost_and_usage_comparisons
 - [ ] get_cost_and_usage_with_resources
 - [ ] get_cost_categories
-- [ ] get_cost_comparison_drivers
 - [ ] get_cost_forecast
 - [ ] get_dimension_values
 - [ ] get_reservation_coverage
diff --git a/docs/docs/services/cloudtrail.rst b/docs/docs/services/cloudtrail.rst
index 298a434fa..28fc2b37f 100644
--- a/docs/docs/services/cloudtrail.rst
+++ b/docs/docs/services/cloudtrail.rst
@@ -35,7 +35,6 @@ cloudtrail
 - [ ] generate_query
 - [ ] get_channel
 - [ ] get_dashboard
-- [ ] get_event_configuration
 - [ ] get_event_data_store
 - [X] get_event_selectors
 - [ ] get_import
@@ -59,7 +58,6 @@ cloudtrail
 
 - [X] list_trails
 - [ ] lookup_events
-- [ ] put_event_configuration
 - [X] put_event_selectors
 - [X] put_insight_selectors
 - [ ] put_resource_policy
diff --git a/docs/docs/services/connectcampaigns.rst b/docs/docs/services/connectcampaigns.rst
index 3b10d80e7..3af635d7c 100644
--- a/docs/docs/services/connectcampaigns.rst
+++ b/docs/docs/services/connectcampaigns.rst
@@ -19,20 +19,20 @@ connectcampaigns
 - [ ] delete_connect_instance_config
 - [ ] delete_instance_onboarding_job
 - [X] describe_campaign
-- [ ] get_campaign_state
+- [X] get_campaign_state
 - [ ] get_campaign_state_batch
 - [X] get_connect_instance_config
 - [ ] get_instance_onboarding_job_status
-- [ ] list_campaigns
-- [ ] list_tags_for_resource
-- [ ] pause_campaign
+- [X] list_campaigns
+- [X] list_tags_for_resource
+- [X] pause_campaign
 - [ ] put_dial_request_batch
-- [ ] resume_campaign
-- [ ] start_campaign
+- [X] resume_campaign
+- [X] start_campaign
 - [X] start_instance_onboarding_job
-- [ ] stop_campaign
-- [ ] tag_resource
-- [ ] untag_resource
+- [X] stop_campaign
+- [X] tag_resource
+- [X] untag_resource
 - [ ] update_campaign_dialer_config
 - [ ] update_campaign_name
 - [ ] update_campaign_outbound_call_config
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 3b9ecce82..88f6c6249 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -527,7 +527,6 @@ ec2
 - [ ] export_image
 - [ ] export_transit_gateway_routes
 - [ ] export_verified_access_instance_client_configuration
-- [ ] get_active_vpn_tunnel_status
 - [ ] get_allowed_images_settings
 - [ ] get_associated_enclave_certificate_iam_roles
 - [ ] get_associated_ipv6_pool_cidrs
@@ -626,7 +625,6 @@ ec2
 - [X] modify_managed_prefix_list
 - [X] modify_network_interface_attribute
 - [ ] modify_private_dns_name_options
-- [ ] modify_public_ip_dns_name_options
 - [ ] modify_reserved_instances
 - [ ] modify_route_server
 - [X] modify_security_group_rules
diff --git a/docs/docs/services/network-firewall.rst b/docs/docs/services/network-firewall.rst
index 34467b558..0895bedcd 100644
--- a/docs/docs/services/network-firewall.rst
+++ b/docs/docs/services/network-firewall.rst
@@ -22,15 +22,12 @@ network-firewall
 - [ ] create_firewall_policy
 - [ ] create_rule_group
 - [ ] create_tls_inspection_configuration
-- [ ] create_vpc_endpoint_association
 - [ ] delete_firewall
 - [ ] delete_firewall_policy
 - [ ] delete_resource_policy
 - [ ] delete_rule_group
 - [ ] delete_tls_inspection_configuration
-- [ ] delete_vpc_endpoint_association
 - [X] describe_firewall
-- [ ] describe_firewall_metadata
 - [ ] describe_firewall_policy
 - [ ] describe_flow_operation
 - [X] describe_logging_configuration
@@ -38,7 +35,6 @@ network-firewall
 - [ ] describe_rule_group
 - [ ] describe_rule_group_metadata
 - [ ] describe_tls_inspection_configuration
-- [ ] describe_vpc_endpoint_association
 - [ ] disassociate_subnets
 - [ ] get_analysis_report_results
 - [ ] list_analysis_reports
@@ -49,7 +45,6 @@ network-firewall
 - [ ] list_rule_groups
 - [ ] list_tags_for_resource
 - [ ] list_tls_inspection_configurations
-- [ ] list_vpc_endpoint_associations
 - [ ] put_resource_policy
 - [ ] start_analysis_report
 - [ ] start_flow_capture
diff --git a/docs/docs/services/ram.rst b/docs/docs/services/ram.rst
index 546f9704d..1a3b86074 100644
--- a/docs/docs/services/ram.rst
+++ b/docs/docs/services/ram.rst
@@ -38,7 +38,7 @@ ram
 - [ ] list_principals
 - [ ] list_replace_permission_associations_work
 - [ ] list_resource_share_permissions
-- [ ] list_resource_types
+- [X] list_resource_types
 - [ ] list_resources
 - [ ] promote_permission_created_from_policy
 - [ ] promote_resource_share_created_from_policy
diff --git a/moto/athena/models.py b/moto/athena/models.py
index a8fcdbcdc..152e692e4 100644
--- a/moto/athena/models.py
+++ b/moto/athena/models.py
@@ -117,6 +117,7 @@ class Execution(BaseModel):
         self.workgroup = workgroup
         self.execution_parameters = execution_parameters
         self.start_time = time.time()
+        self.end_time = time.time()
         self.status = "SUCCEEDED"
 
         if self.config is not None and "OutputLocation" in self.config:
diff --git a/moto/athena/responses.py b/moto/athena/responses.py
index 60cd55526..0b58b21b2 100644
--- a/moto/athena/responses.py
+++ b/moto/athena/responses.py
@@ -76,18 +76,24 @@ class AthenaResponse(BaseResponse):
                 "Query": execution.query,
                 "StatementType": statement_type,
                 "ResultConfiguration": execution.config,
+                "ResultReuseConfiguration": {
+                    "ResultReuseByAgeConfiguration": {"Enabled": False}
+                },
                 "QueryExecutionContext": execution.context,
                 "Status": {
                     "State": execution.status,
                     "SubmissionDateTime": execution.start_time,
+                    "CompletionDateTime": execution.end_time,
                 },
                 "Statistics": {
                     "EngineExecutionTimeInMillis": 0,
                     "DataScannedInBytes": 0,
                     "TotalExecutionTimeInMillis": 0,
                     "QueryQueueTimeInMillis": 0,
+                    "ServicePreProcessingTimeInMillis": 0,
                     "QueryPlanningTimeInMillis": 0,
                     "ServiceProcessingTimeInMillis": 0,
+                    "ResultReuseInformation": {"ReusedPreviousResult": False},
                 },
                 "WorkGroup": execution.workgroup.name if execution.workgroup else None,
             }
diff --git a/moto/backends.py b/moto/backends.py
index 6023d9b0e..bb92611bb 100644
--- a/moto/backends.py
+++ b/moto/backends.py
@@ -38,6 +38,7 @@ if TYPE_CHECKING:
     from moto.cognitoidp.models import CognitoIdpBackend
     from moto.comprehend.models import ComprehendBackend
     from moto.config.models import ConfigBackend
+    from moto.connectcampaigns.models import ConnectCampaignServiceBackend
     from moto.core.base_backend import SERVICE_BACKEND, BackendDict
     from moto.databrew.models import DataBrewBackend
     from moto.datapipeline.models import DataPipelineBackend
@@ -228,6 +229,7 @@ SERVICE_NAMES = Union[
     "Literal['cognito-identity']",
     "Literal['cognito-idp']",
     "Literal['comprehend']",
+    "Literal['connectcampaigns']",
     "Literal['config']",
     "Literal['databrew']",
     "Literal['datapipeline']",
@@ -442,6 +444,10 @@ def get_backend(name: "Literal['cognito-idp']") -> "BackendDict[CognitoIdpBacken
 @overload
 def get_backend(name: "Literal['comprehend']") -> "BackendDict[ComprehendBackend]": ...
 @overload
+def get_backend(
+    name: "Literal['connectcampaigns']",
+) -> "BackendDict[ConnectCampaignServiceBackend]": ...
+@overload
 def get_backend(name: "Literal['config']") -> "BackendDict[ConfigBackend]": ...
 @overload
 def get_backend(name: "Literal['databrew']") -> "BackendDict[DataBrewBackend]": ...
diff --git a/moto/connectcampaigns/models.py b/moto/connectcampaigns/models.py
index 3dc5c29c5..24068bb38 100644
--- a/moto/connectcampaigns/models.py
+++ b/moto/connectcampaigns/models.py
@@ -1,14 +1,27 @@
 """ConnectCampaignServiceBackend class with methods for supported APIs."""
 
 import uuid
-from typing import Any, Dict, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
+from urllib.parse import unquote
 
 from moto.core import DEFAULT_ACCOUNT_ID
 from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.common_models import BaseModel
+from moto.utilities.paginator import paginate
+from moto.utilities.tagging_service import TaggingService
 
 from .exceptions import ResourceNotFoundException, ValidationException
 
+PAGINATION_MODEL = {
+    "list_campaigns": {
+        "input_token": "next_token",
+        "limit_key": "max_results",
+        "limit_default": 100,
+        "output_token": "nextToken",
+        "unique_attribute": "id",
+    }
+}
+
 
 class ConnectCampaign(BaseModel):
     def __init__(
@@ -23,6 +36,7 @@ class ConnectCampaign(BaseModel):
         self.id = str(uuid.uuid4())
         self.name = name
         self.connect_instance_id = connect_instance_id
+        self.state = "Initialized"
         self.dialer_config = dialer_config
         self.outbound_call_config = outbound_call_config
         self.region = region
@@ -93,6 +107,7 @@ class ConnectCampaignServiceBackend(BaseBackend):
         self.campaigns: Dict[str, ConnectCampaign] = {}
         self.instance_configs: Dict[str, ConnectInstanceConfig] = {}
         self.onboarding_jobs: Dict[str, ConnectInstanceOnboardingJobStatus] = {}
+        self.tagger = TaggingService()
 
     def create_campaign(
         self,
@@ -183,6 +198,126 @@ class ConnectCampaignServiceBackend(BaseBackend):
 
         return job_status.to_dict()
 
+    def start_campaign(self, id: str) -> None:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+        self.campaigns[id].state = "Running"
+        return
+
+    def stop_campaign(self, id: str) -> None:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+        self.campaigns[id].state = "Stopped"
+        return
+
+    def pause_campaign(self, id: str) -> None:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+        self.campaigns[id].state = "Paused"
+        return
+
+    def resume_campaign(self, id: str) -> None:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+        self.campaigns[id].state = "Running"
+        return
+
+    def get_campaign_state(self, id: str) -> str:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+        return self.campaigns[id].state
+
+    @paginate(pagination_model=PAGINATION_MODEL)
+    def list_campaigns(
+        self,
+        filters: Optional[Dict[str, Dict[str, str]]],
+        max_results: Optional[int],
+        next_token: Optional[str],
+    ) -> List[Dict[str, str]]:
+        filtered_campaigns = list(self.campaigns.values())
+
+        if filters and "instanceIdFilter" in filters:
+            filter_value = filters["instanceIdFilter"]["value"]
+            filter_operator = filters["instanceIdFilter"]["operator"]
+
+            operator_logic = {
+                "Eq": lambda campaign: campaign.connect_instance_id == filter_value,
+                "Ne": lambda campaign: campaign.connect_instance_id != filter_value,
+                "Contains": lambda campaign: filter_value
+                in campaign.connect_instance_id,
+                "StartsWith": lambda campaign: campaign.connect_instance_id.startswith(
+                    filter_value
+                ),
+            }
+
+            if filter_operator not in operator_logic:
+                raise ValidationException(f"Unsupported operator: {filter_operator}")
+
+            filtered_campaigns = list(
+                filter(operator_logic[filter_operator], filtered_campaigns)
+            )
+
+        campaign_summary_list = [
+            {
+                "id": campaign.id,
+                "arn": campaign.arn,
+                "name": campaign.name,
+                "connectInstanceId": campaign.connect_instance_id,
+            }
+            for campaign in filtered_campaigns
+        ]
+        return campaign_summary_list
+
+    def tag_resource(self, arn: str, tags: Dict[str, str]) -> None:
+        arn = unquote(arn)
+        campaign = None
+        for c in self.campaigns.values():
+            if c.arn == arn:
+                campaign = c
+                break
+
+        if campaign is None:
+            raise ResourceNotFoundException(f"Resource {arn} not found")
+
+        tag_list = [{"Key": k, "Value": v} for k, v in tags.items()]
+        campaign.tags.update(tags)
+        self.tagger.tag_resource(arn, tag_list)
+        return
+
+    def untag_resource(self, arn: str, tag_keys: List[str]) -> None:
+        arn = unquote(arn)
+        campaign = None
+        for c in self.campaigns.values():
+            if c.arn == arn:
+                campaign = c
+                break
+
+        if campaign is None:
+            raise ResourceNotFoundException(f"Resource {arn} not found")
+
+        if not isinstance(tag_keys, list):
+            tag_keys = [tag_keys]
+        if not tag_keys:
+            raise ValidationException("tagKeys is a required parameter")
+
+        for tag in tag_keys:
+            campaign.tags.pop(tag, None)
+        self.tagger.untag_resource_using_names(arn, tag_keys)
+        return
+
+    def list_tags_for_resource(self, arn: str) -> Dict[str, str]:
+        arn = unquote(arn)
+        campaign = None
+        for c in self.campaigns.values():
+            if c.arn == arn:
+                campaign = c
+                break
+
+        if campaign is None:
+            raise ResourceNotFoundException(f"Resource {arn} not found")
+
+        return self.tagger.get_tag_dict_for_resource(arn)
+
 
 connectcampaigns_backends = BackendDict(
     ConnectCampaignServiceBackend, "connectcampaigns"
diff --git a/moto/connectcampaigns/responses.py b/moto/connectcampaigns/responses.py
index 91bdc0336..88109727d 100644
--- a/moto/connectcampaigns/responses.py
+++ b/moto/connectcampaigns/responses.py
@@ -1,6 +1,7 @@
 """Handles incoming connectcampaigns requests, invokes methods, returns responses."""
 
 import json
+from typing import Dict, List, Optional, Tuple
 
 from moto.core.responses import BaseResponse
 
@@ -85,3 +86,94 @@ class ConnectCampaignServiceResponse(BaseResponse):
         response = {"connectInstanceOnboardingJobStatus": job_status}
 
         return json.dumps(response)
+
+    def start_campaign(self) -> str:
+        id = self.path.split("/")[2]
+
+        self.connectcampaigns_backend.start_campaign(
+            id=id,
+        )
+
+        return "{}"
+
+    def stop_campaign(self) -> str:
+        id = self.path.split("/")[2]
+
+        self.connectcampaigns_backend.stop_campaign(
+            id=id,
+        )
+
+        return "{}"
+
+    def pause_campaign(self) -> str:
+        id = self.path.split("/")[2]
+
+        self.connectcampaigns_backend.pause_campaign(
+            id=id,
+        )
+
+        return "{}"
+
+    def resume_campaign(self) -> str:
+        id = self.path.split("/")[2]
+
+        self.connectcampaigns_backend.resume_campaign(
+            id=id,
+        )
+
+        return "{}"
+
+    def get_campaign_state(self) -> str:
+        id = self.path.split("/")[2]
+
+        campaign_state = self.connectcampaigns_backend.get_campaign_state(
+            id=id,
+        )
+
+        response = {"state": campaign_state}
+
+        return json.dumps(response)
+
+    def list_campaigns(self) -> str:
+        max_results = self._get_param("maxResults")
+        next_token = self._get_param("nextToken")
+        filters = self._get_param("filters", {})
+
+        campaigns_result: Tuple[List[Dict[str, str]], Optional[str]] = (
+            self.connectcampaigns_backend.list_campaigns(
+                filters=filters,
+                max_results=max_results,
+                next_token=next_token,
+            )
+        )
+
+        response = {
+            "campaignSummaryList": campaigns_result[0],
+            "nextToken": campaigns_result[1],
+        }
+        return json.dumps(response)
+
+    def tag_resource(self) -> str:
+        arn = self._get_param("arn")
+        tags = self._get_param("tags")
+        self.connectcampaigns_backend.tag_resource(
+            arn=arn,
+            tags=tags,
+        )
+        return json.dumps(dict())
+
+    def untag_resource(self) -> str:
+        arn = self._get_param("arn")
+        tag_keys = self._get_param("tagKeys")
+        self.connectcampaigns_backend.untag_resource(
+            arn=arn,
+            tag_keys=tag_keys,
+        )
+        return json.dumps(dict())
+
+    def list_tags_for_resource(self) -> str:
+        arn = self._get_param("arn")
+        tags = self.connectcampaigns_backend.list_tags_for_resource(
+            arn=arn,
+        )
+        return json.dumps(dict(tags=tags))
diff --git a/moto/connectcampaigns/urls.py b/moto/connectcampaigns/urls.py
index fd20423f7..2d3759ed1 100644
--- a/moto/connectcampaigns/urls.py
+++ b/moto/connectcampaigns/urls.py
@@ -7,8 +7,16 @@ url_bases = [
 ]
 
 url_paths = {
+    "{0}/tags/(?P<arn>.+)$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/tags/(?P<arn>[^/]+)?tagKeys=(?P<tagKeys>[^/]+)$": ConnectCampaignServiceResponse.dispatch,
     "{0}/campaigns$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns-summary$": ConnectCampaignServiceResponse.dispatch,
     "{0}/campaigns/(?P<id>[^/]+)$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)/start$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)/stop$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)/pause$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)/resume$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)/state$": ConnectCampaignServiceResponse.dispatch,
     "{0}/connect-instance/(?P<connectInstanceId>[^/]+)/config$": ConnectCampaignServiceResponse.dispatch,
     "{0}/connect-instance/(?P<connectInstanceId>[^/]+)/onboarding$": ConnectCampaignServiceResponse.dispatch,
 }
diff --git a/moto/ram/models.py b/moto/ram/models.py
index 69fb55375..6f19c4c2c 100644
--- a/moto/ram/models.py
+++ b/moto/ram/models.py
@@ -37,7 +37,10 @@ class ResourceShare(BaseModel):
         "report-group",  # AWS CodeBuild report group
         "resolver-rule",  # Amazon Route 53 forwarding rule
         "subnet",  # Amazon EC2 subnet
-        "transit-gateway",  # Amazon EC2 transit gateway
+        "transit-gateway",  # Amazon EC2 transit gateway,
+        "database",  # Amazon Glue database
+        "table",  # Amazon Glue table
+        "catalog",  # Amazon Glue catalog
     ]
 
     def __init__(self, account_id: str, region: str, **kwargs: Any):
@@ -153,6 +156,94 @@ class ResourceShare(BaseModel):
 
 
 class ResourceAccessManagerBackend(BaseBackend):
+    RESOURCE_TYPES = [  # List of resource types based on SHAREABLE_RESOURCES
+        {
+            "resourceType": "rds:Cluster",
+            "serviceName": "rds",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "imagebuilder:Component",
+            "serviceName": "imagebuilder",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "networkmanager:CoreNetwork",
+            "serviceName": "networkmanager",
+            "resourceRegionScope": "GLOBAL",
+        },
+        {
+            "resourceType": "resource-groups:Group",
+            "serviceName": "resource-groups",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "imagebuilder:Image",
+            "serviceName": "imagebuilder",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "imagebuilder:ImageRecipe",
+            "serviceName": "imagebuilder",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "license-manager:LicenseConfiguration",
+            "serviceName": "license-manager",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "appmesh:Mesh",
+            "serviceName": "appmesh",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "ec2:PrefixList",
+            "serviceName": "ec2",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "codebuild:Project",
+            "serviceName": "codebuild",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "codebuild:ReportGroup",
+            "serviceName": "codebuild",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "route53resolver:ResolverRule",
+            "serviceName": "route53resolver",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "ec2:Subnet",
+            "serviceName": "ec2",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "ec2:TransitGatewayMulticastDomain",
+            "serviceName": "ec2",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "glue:Database",
+            "serviceName": "glue",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "glue:Table",
+            "serviceName": "glue",
+            "resourceRegionScope": "REGIONAL",
+        },
+        {
+            "resourceType": "glue:Catalog",
+            "serviceName": "glue",
+            "resourceRegionScope": "REGIONAL",
+        },
+    ]
+
     def __init__(self, region_name: str, account_id: str):
         super().__init__(region_name, account_id)
         self.resource_shares: List[ResourceShare] = []
@@ -302,5 +393,25 @@ class ResourceAccessManagerBackend(BaseBackend):
 
         return dict(resourceShareAssociations=associations)
 
+    def list_resource_types(self, **kwargs: Any) -> Dict[str, Any]:
+        resource_region_scope = kwargs.get("resourceRegionScope", "ALL")
+
+        if resource_region_scope not in ["ALL", "REGIONAL", "GLOBAL"]:
+            raise InvalidParameterException(
+                f"{resource_region_scope} is not a valid resource region "
+                "scope value. Specify a valid value and try again."
+            )
+
+        if resource_region_scope == "ALL":
+            resource_types = self.RESOURCE_TYPES
+        else:
+            resource_types = [
+                resource_type
+                for resource_type in self.RESOURCE_TYPES
+                if resource_type["resourceRegionScope"] == resource_region_scope
+            ]
+
+        return dict(resourceTypes=resource_types)
+
 
 ram_backends = BackendDict(ResourceAccessManagerBackend, "ram")
diff --git a/moto/ram/responses.py b/moto/ram/responses.py
index 93ea41153..7b36f71f1 100644
--- a/moto/ram/responses.py
+++ b/moto/ram/responses.py
@@ -41,3 +41,6 @@ class ResourceAccessManagerResponse(BaseResponse):
         return json.dumps(
             self.ram_backend.get_resource_share_associations(**self.request_params)
         )
+
+    def list_resource_types(self) -> str:
+        return json.dumps(self.ram_backend.list_resource_types(**self.request_params))
diff --git a/moto/ram/urls.py b/moto/ram/urls.py
index 1b5775bb0..e973085d1 100644
--- a/moto/ram/urls.py
+++ b/moto/ram/urls.py
@@ -9,4 +9,5 @@ url_paths = {
     "{0}/getresourceshares$": ResourceAccessManagerResponse.dispatch,
     "{0}/getresourceshareassociations$": ResourceAccessManagerResponse.dispatch,
     "{0}/updateresourceshare$": ResourceAccessManagerResponse.dispatch,
+    "{0}/listresourcetypes$": ResourceAccessManagerResponse.dispatch,
 }
diff --git a/moto/rds/models.py b/moto/rds/models.py
index 49304d78e..cb4f97375 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -492,6 +492,7 @@ class DBCluster(RDSBaseModel):
         port: Optional[int] = None,
         preferred_backup_window: str = "01:37-02:07",
         preferred_maintenance_window: str = "wed:02:40-wed:03:10",
+        publicly_accessible: bool = False,
         storage_encrypted: bool = False,
         tags: Optional[List[Dict[str, str]]] = None,
         vpc_security_group_ids: Optional[List[str]] = None,
@@ -582,6 +583,7 @@ class DBCluster(RDSBaseModel):
         self.port = port or DBCluster.default_port(self.engine)
         self.preferred_backup_window = preferred_backup_window
         self.preferred_maintenance_window = preferred_maintenance_window
+        self.publicly_accessible = publicly_accessible
         # This should default to the default security group
         self._vpc_security_group_ids = vpc_security_group_ids or []
         self.hosted_zone_id = "".join(
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 136fb0dea..0de8b6697 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -7,6 +7,10 @@ from moto.backup.models import BackupBackend, backup_backends
 from moto.clouddirectory import CloudDirectoryBackend, clouddirectory_backends
 from moto.cloudfront.models import CloudFrontBackend, cloudfront_backends
 from moto.cloudwatch.models import CloudWatchBackend, cloudwatch_backends
+from moto.connectcampaigns.models import (
+    ConnectCampaignServiceBackend,
+    connectcampaigns_backends,
+)
 from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.exceptions import RESTError
 from moto.dms.models import DatabaseMigrationServiceBackend, dms_backends
@@ -210,6 +214,13 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
     def cloudwatch_backend(self) -> CloudWatchBackend:
         return cloudwatch_backends[self.account_id][self.region_name]
 
+    @property
+    def connectcampaigns_backend(self) -> Optional[ConnectCampaignServiceBackend]:
+        # Connect Campaigns service has limited region availability
+        if self.region_name in connectcampaigns_backends[self.account_id].regions:
+            return connectcampaigns_backends[self.account_id][self.region_name]
+        return None
+
     def _get_resources_generator(
         self,
         tag_filters: Optional[List[Dict[str, Any]]] = None,
@@ -388,6 +399,20 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
                             "Tags": tags,
                         }
 
+        # Connect Campaigns v1
+        if self.connectcampaigns_backend:
+            if not resource_type_filters or "connectcampaigns" in resource_type_filters:
+                connectcampaigns_backend: ConnectCampaignServiceBackend = (
+                    connectcampaigns_backends[self.account_id][self.region_name]
+                )
+                for campaign in connectcampaigns_backend.campaigns.values():
+                    tags = connectcampaigns_backend.tagger.list_tags_for_resource(
+                        campaign.arn
+                    )["Tags"]
+                    if not tags or not tag_filter(tags):
+                        continue
+                    yield {"ResourceARN": f"{campaign.arn}", "Tags": tags}
+
         # DMS
         if not resource_type_filters or "dms:endpoint" in resource_type_filters:
             for endpoint in self.dms_backend.endpoints.values():
diff --git a/moto/servicediscovery/models.py b/moto/servicediscovery/models.py
index fbf5e7557..a77e0088b 100644
--- a/moto/servicediscovery/models.py
+++ b/moto/servicediscovery/models.py
@@ -5,6 +5,7 @@ from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.common_models import BaseModel
 from moto.core.utils import unix_time
 from moto.moto_api._internal import mock_random as random
+from moto.route53 import route53_backends
 from moto.utilities.tagging_service import TaggingService
 from moto.utilities.utils import get_partition
 
@@ -275,7 +276,17 @@ class ServiceDiscoveryBackend(BaseBackend):
             if namespace.vpc == vpc:
                 raise ConflictingDomainExists(vpc)
         dns_properties = (properties or {}).get("DnsProperties", {})
-        dns_properties["HostedZoneId"] = "hzi"
+
+        # create the hosted zone
+        hosted_zone = route53_backends[self.account_id][
+            get_partition(self.region_name)
+        ].create_hosted_zone(
+            f"{name}-hz",
+            private_zone=True,
+            comment=f"hosted zone for private dns namespace {name}",
+        )
+
+        dns_properties["HostedZoneId"] = hosted_zone.id
         namespace = Namespace(
             account_id=self.account_id,
             region=self.region_name,
@@ -304,7 +315,17 @@ class ServiceDiscoveryBackend(BaseBackend):
         properties: Dict[str, Any],
     ) -> str:
         dns_properties = (properties or {}).get("DnsProperties", {})
-        dns_properties["HostedZoneId"] = "hzi"
+
+        # create the hosted zone
+        hosted_zone = route53_backends[self.account_id][
+            get_partition(self.region_name)
+        ].create_hosted_zone(
+            f"{name}-hz",
+            private_zone=False,
+            comment=f"hosted zone for public dns namespace {name}",
+        )
+
+        dns_properties["HostedZoneId"] = hosted_zone.id
         namespace = Namespace(
             account_id=self.account_id,
             region=self.region_name,


Test patch:
diff --git a/tests/test_athena/test_athena.py b/tests/test_athena/test_athena.py
index 6229dec8a..0121a7a87 100644
--- a/tests/test_athena/test_athena.py
+++ b/tests/test_athena/test_athena.py
@@ -1,3 +1,5 @@
+from datetime import datetime
+
 import boto3
 import pytest
 from botocore.exceptions import ClientError
@@ -172,7 +174,7 @@ def test_get_query_execution(location):
     # Start Query
     exex_id = client.start_query_execution(
         QueryString=query,
-        QueryExecutionContext={"Database": database},
+        QueryExecutionContext={"Database": database, "Catalog": "awsdatacatalog"},
         ResultConfiguration={"OutputLocation": location},
     )["QueryExecutionId"]
     #
@@ -186,15 +188,24 @@ def test_get_query_execution(location):
         assert result_config["OutputLocation"] == f"{location}{exex_id}.csv"
     else:
         assert result_config["OutputLocation"] == f"{location}/{exex_id}.csv"
+    assert (
+        details["ResultReuseConfiguration"]["ResultReuseByAgeConfiguration"]["Enabled"]
+        is False
+    )
     assert details["QueryExecutionContext"]["Database"] == database
+    assert details["QueryExecutionContext"]["Catalog"] == "awsdatacatalog"
     assert details["Status"]["State"] == "SUCCEEDED"
+    assert isinstance(details["Status"]["SubmissionDateTime"], datetime)
+    assert isinstance(details["Status"]["CompletionDateTime"], datetime)
     assert details["Statistics"] == {
         "EngineExecutionTimeInMillis": 0,
         "DataScannedInBytes": 0,
         "TotalExecutionTimeInMillis": 0,
         "QueryQueueTimeInMillis": 0,
+        "ServicePreProcessingTimeInMillis": 0,
         "QueryPlanningTimeInMillis": 0,
         "ServiceProcessingTimeInMillis": 0,
+        "ResultReuseInformation": {"ReusedPreviousResult": False},
     }
     assert "WorkGroup" not in details
 
diff --git a/tests/test_connectcampaigns/test_connectcampaigns.py b/tests/test_connectcampaigns/test_connectcampaigns.py
index 21da781db..3f195e978 100644
--- a/tests/test_connectcampaigns/test_connectcampaigns.py
+++ b/tests/test_connectcampaigns/test_connectcampaigns.py
@@ -175,3 +175,395 @@ def test_start_instance_onboarding_job():
             connectInstanceId=connect_instance_id,
             encryptionConfig={"enabled": True, "encryptionType": "KMS"},
         )
+
+
+@mock_aws
+def test_start_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToStart",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+            "connectSourcePhoneNumber": "+12065550100",
+            "connectQueueId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    client.start_campaign(id=campaign_id)
+    describe_response = client.get_campaign_state(id=campaign_id)
+
+    assert describe_response["state"] == "Running"
+
+
+@mock_aws
+def test_stop_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToStop",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+            "connectSourcePhoneNumber": "+12065550100",
+            "connectQueueId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    client.stop_campaign(id=campaign_id)
+    describe_response = client.get_campaign_state(id=campaign_id)
+
+    assert describe_response["state"] == "Stopped"
+
+
+@mock_aws
+def test_list_campaigns():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    # Create a couple of campaigns
+    client.create_campaign(
+        name="Campaign1",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    client.create_campaign(
+        name="Campaign2",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    response = client.list_campaigns()
+
+    assert len(response["campaignSummaryList"]) >= 2
+    assert any(c["name"] == "Campaign1" for c in response["campaignSummaryList"])
+    assert any(c["name"] == "Campaign2" for c in response["campaignSummaryList"])
+
+
+@mock_aws
+def test_list_campaigns_with_filters():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    # Create a couple of campaigns
+    client.create_campaign(
+        name="Campaign1",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    client.create_campaign(
+        name="Campaign2",
+        connectInstanceId="12345678-1234-1234-1234-000000000000",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    # Filter by name
+    response = client.list_campaigns(
+        filters={
+            "instanceIdFilter": {
+                "value": "12345678-1234-1234-1234-123456789012",
+                "operator": "Eq",
+            }
+        }
+    )
+
+    assert len(response["campaignSummaryList"]) == 1
+    assert response["campaignSummaryList"][0]["name"] == "Campaign1"
+
+    # Filter by connectInstanceId
+    response = client.list_campaigns(
+        filters={
+            "instanceIdFilter": {
+                "value": "12345678-1234-1234-1234-12340000012",
+                "operator": "Eq",
+            }
+        }
+    )
+    assert len(response["campaignSummaryList"]) == 0
+
+
+@mock_aws
+def test_start_campaign_invalid_id():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.start_campaign(id="non-existent-id")
+
+
+@mock_aws
+def test_stop_campaign_invalid_id():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.stop_campaign(id="non-existent-id")
+
+
+@mock_aws
+def test_tag_resource():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToTag",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+        tags={"Environment": "TestLater", "Owner": "DevTeamLater"},
+    )
+
+    client.tag_resource(
+        arn=create_response["arn"],
+        tags={"Environment": "Test", "Owner": "DevTeam"},
+    )
+
+    # assert tag_response is None
+    # Verify tags
+    describe_tags = client.list_tags_for_resource(arn=create_response["arn"])
+    assert "tags" in describe_tags
+
+    tags = describe_tags["tags"]
+    assert tags["Environment"] == "Test"
+    assert tags["Owner"] == "DevTeam"
+
+
+@mock_aws
+def test_untag_resource():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToUntag",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+    client.tag_resource(
+        arn=create_response["arn"],
+        tags={"Environment": "Test", "Owner": "DevTeam"},
+    )
+
+    client.untag_resource(
+        arn=create_response["arn"],
+        tagKeys=["Environment"],
+    )
+    # Verify tags after untagging
+    describe_response = client.describe_campaign(id=campaign_id)
+    assert "tags" in describe_response["campaign"]
+    tags = describe_response["campaign"]["tags"]
+    assert "Environment" not in tags
+    assert tags["Owner"] == "DevTeam"
+
+
+@mock_aws
+def test_list_tags_for_resource():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToListTags",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    client.tag_resource(
+        arn=create_response["arn"],
+        tags={"Environment": "Test", "Owner": "DevTeam"},
+    )
+
+    tags_response = client.list_tags_for_resource(arn=create_response["arn"])
+
+    assert len(tags_response) == 2
+    assert tags_response["tags"]["Environment"] == "Test"
+    assert tags_response["tags"]["Owner"] == "DevTeam"
+
+
+@mock_aws
+def test_get_campaign_state():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignStateTest",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    # Initially, the campaign state should be 'Initialized'
+    state_response = client.get_campaign_state(id=campaign_id)
+    assert state_response["state"] == "Initialized"
+
+    # Start the campaign
+    client.start_campaign(id=campaign_id)
+    state_response = client.get_campaign_state(id=campaign_id)
+    assert state_response["state"] == "Running"
+
+    # Stop the campaign
+    client.stop_campaign(id=campaign_id)
+    state_response = client.get_campaign_state(id=campaign_id)
+    assert state_response["state"] == "Stopped"
+
+
+@mock_aws
+def test_get_campaign_state_invalid_id():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.get_campaign_state(id="non-existent-id")
+
+
+@mock_aws
+def test_pause_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToPause",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    # Start the campaign
+    client.start_campaign(id=campaign_id)
+
+    # Pause the campaign
+    client.pause_campaign(id=campaign_id)
+    state_response = client.get_campaign_state(id=campaign_id)
+
+    assert state_response["state"] == "Paused"
+
+
+@mock_aws
+def test_resume_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToResume",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    # Start the campaign
+    client.start_campaign(id=campaign_id)
+
+    # Pause the campaign
+    client.pause_campaign(id=campaign_id)
+
+    # Resume the campaign
+    client.resume_campaign(id=campaign_id)
+    state_response = client.get_campaign_state(id=campaign_id)
+
+    assert state_response["state"] == "Running"
+
+
+@mock_aws
+def test_pause_campaign_invalid_id():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.pause_campaign(id="non-existent-id")
+
+
+@mock_aws
+def test_resume_campaign_invalid_id():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.resume_campaign(id="non-existent-id")
diff --git a/tests/test_ram/test_ram.py b/tests/test_ram/test_ram.py
index da782ad8d..5a3b96c9d 100644
--- a/tests/test_ram/test_ram.py
+++ b/tests/test_ram/test_ram.py
@@ -8,6 +8,7 @@ from botocore.exceptions import ClientError
 
 from moto import mock_aws
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from moto.ram.models import ResourceAccessManagerBackend
 
 
 @mock_aws
@@ -530,3 +531,52 @@ def test_get_resource_share_associations_errors():
         "You cannot specify a principal when the association type is RESOURCE"
         in ex.response["Error"]["Message"]
     )
+
+
+@mock_aws
+@pytest.mark.parametrize(
+    "resource_region_scope, expect_error, error_message",
+    [
+        ({}, False, None),  # default value is "ALL"
+        ({"resourceRegionScope": "ALL"}, False, None),
+        ({"resourceRegionScope": "GLOBAL"}, False, None),
+        ({"resourceRegionScope": "REGIONAL"}, False, None),
+        (
+            {"resourceRegionScope": "INVALID"},
+            True,
+            "INVALID is not a valid resource region scope value. Specify a valid value and try again.",
+        ),
+    ],
+    ids=[
+        "default_region_scope",
+        "all_region_scope",
+        "global_region_scope",
+        "regional_region_scope",
+        "invalid_region_scope",
+    ],
+)
+def test_list_resource_types(resource_region_scope, expect_error, error_message):
+    client = boto3.client("ram", region_name="us-east-1")
+    region_scope = resource_region_scope.get("resourceRegionScope")
+
+    if expect_error:
+        with pytest.raises(ClientError) as e:
+            client.list_resource_types(**resource_region_scope)
+        ex = e.value
+        assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+        assert "InvalidParameterException" in ex.response["Error"]["Code"]
+        assert ex.response["Error"]["Message"] == error_message
+    else:
+        response = client.list_resource_types(**resource_region_scope)
+        expected_types = ResourceAccessManagerBackend.RESOURCE_TYPES
+        if region_scope == "GLOBAL":
+            expected_types = [
+                rt for rt in expected_types if rt["resourceRegionScope"] == "GLOBAL"
+            ]
+        elif region_scope == "REGIONAL":
+            expected_types = [
+                rt for rt in expected_types if rt["resourceRegionScope"] == "REGIONAL"
+            ]
+
+        assert "resourceTypes" in response
+        assert response["resourceTypes"] == expected_types
diff --git a/tests/test_rds/test_rds_clusters.py b/tests/test_rds/test_rds_clusters.py
index ec857c7c1..7fc5b9a57 100644
--- a/tests/test_rds/test_rds_clusters.py
+++ b/tests/test_rds/test_rds_clusters.py
@@ -2006,3 +2006,17 @@ def test_db_cluster_identifier_is_case_insensitive(client):
 
     response = client.delete_db_cluster(DBClusterIdentifier="xXyY")
     assert response["DBCluster"]["DBClusterIdentifier"] == "xxyy"
+
+
+@mock_aws
+def test_db_cluster_attributes(client):
+    cluster = client.create_db_cluster(
+        DBClusterIdentifier="test-cluster",
+        Engine="aurora-postgresql",
+        MasterUsername="root",
+        MasterUserPassword="password",
+        PubliclyAccessible=True,
+    )["DBCluster"]
+    assert cluster["DBClusterIdentifier"] == "test-cluster"
+    assert cluster["Engine"] == "aurora-postgresql"
+    assert cluster["PubliclyAccessible"] is True
diff --git a/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py b/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py
index 8e00d2aba..9ff9a883f 100644
--- a/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py
+++ b/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py
@@ -262,7 +262,18 @@ def test_create_public_dns_namespace():
 
     assert "DnsProperties" in namespace["Properties"]
     dns_props = namespace["Properties"]["DnsProperties"]
-    assert dns_props == {"HostedZoneId": "hzi", "SOA": {"TTL": 124}}
+    hosted_zone_id = dns_props["HostedZoneId"]
+
+    hosted_zone_resp = boto3.client("route53", region_name="us-east-2").get_hosted_zone(
+        Id=hosted_zone_id
+    )
+    assert "HostedZone" in hosted_zone_resp
+    assert "Config" in hosted_zone_resp["HostedZone"]
+    private_zone = hosted_zone_resp["HostedZone"]["Config"]["PrivateZone"]
+    assert not private_zone
+
+    soa = dns_props["SOA"]
+    assert soa == {"TTL": 124}
 
 
 @mock_aws