content: #
  #  Licensed under the Apache License, Version 2.0 (the "License");
  #  you may not use this file except in compliance with the License.
  #  You may obtain a copy of the License at:
  #
  #       https://www.apache.org/licenses/LICENSE-2.0
  #
  #  Unless required by applicable law or agreed to in writing, software
  #  distributed under the License is distributed on an "AS IS" BASIS,
  #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  #  or implied. See the License for the specific language governing
  #  permissions and limitations under the License.
  import logging
  import uuid
  from copy import deepcopy
  from datetime import datetime
  from typing import (
      Any,
      Callable,
      Dict,
      List,
      Optional,
      Tuple,
      Type,
      TypeVar,
      Union,
  )
  
  from pydantic import BaseModel, Field
  from typing_extensions import Annotated
  
  from tests.integration.functional.utils import sample_name
  from zenml.client import Client
  from zenml.config.global_config import GlobalConfiguration
  from zenml.config.pipeline_configurations import PipelineConfiguration
  from zenml.config.pipeline_spec import PipelineSpec
  from zenml.config.store_config import StoreConfiguration
  from zenml.enums import (
      ArtifactSaveType,
      ArtifactType,
      PluginSubType,
      StackComponentType,
  )
  from zenml.exceptions import IllegalOperationError
  from zenml.login.credentials_store import CredentialsStore
  from zenml.models import (
      ActionFilter,
      ActionRequest,
      ActionUpdate,
      APIKeyRequest,
      ArtifactFilter,
      ArtifactRequest,
      ArtifactUpdate,
      ArtifactVersionFilter,
      ArtifactVersionRequest,
      ArtifactVersionUpdate,
      AuthenticationMethodModel,
      BaseFilter,
      BaseIdentifiedResponse,
      BaseRequest,
      CodeRepositoryFilter,
      CodeRepositoryRequest,
      CodeRepositoryUpdate,
      ComponentFilter,
      ComponentRequest,
      ComponentUpdate,
      EventSourceFilter,
      EventSourceRequest,
      EventSourceUpdate,
      FlavorFilter,
      FlavorRequest,
      ModelFilter,
      ModelRequest,
      ModelUpdate,
      ModelVersionRequest,
      Page,
      PipelineBuildFilter,
      PipelineBuildRequest,
      PipelineFilter,
      PipelineRequest,
      PipelineRunFilter,
      PipelineRunRequest,
      PipelineSnapshotFilter,
      PipelineSnapshotRequest,
      PipelineUpdate,
      ProjectFilter,
      ProjectRequest,
      ProjectUpdate,
      ResourceTypeModel,
      RunTemplateFilter,
      RunTemplateRequest,
      RunTemplateUpdate,
      SecretFilter,
      SecretRequest,
      ServiceAccountFilter,
      ServiceAccountRequest,
      ServiceAccountUpdate,
      ServiceConnectorConfiguration,
      ServiceConnectorFilter,
      ServiceConnectorRequest,
      ServiceConnectorTypeModel,
      ServiceConnectorUpdate,
      StackFilter,
      StackRequest,
      StepRunFilter,
      StepRunResponse,
      TriggerFilter,
      TriggerRequest,
      TriggerUpdate,
      UserFilter,
      UserRequest,
      UserUpdate,
  )
  from zenml.pipelines import pipeline
  from zenml.service_connectors.service_connector import AuthenticationConfig
  from zenml.service_connectors.service_connector_registry import (
      service_connector_registry,
  )
  from zenml.steps import step
  from zenml.utils.secret_utils import PlainSerializedSecretStr
  from zenml.utils.string_utils import random_str
  from zenml.zen_stores.base_zen_store import BaseZenStore
  from zenml.zen_stores.rest_zen_store import RestZenStore
  from zenml.zen_stores.sql_zen_store import SqlZenStore
  
  
  @step
  def constant_int_output_test_step() -> Annotated[int, "test_step_output"]:
      logging.info("log")
      return 7
  
  
  @step
  def int_plus_one_test_step(
      input: int,
  ) -> Annotated[int, "test_step_output_plus_one"]:
      return input + 1
  
  
  @pipeline(name="connected_two_step_pipeline")
  def pipeline_instance():
      int_plus_one_test_step(constant_int_output_test_step())
  
  
  class PipelineRunContext:
      """Context manager that creates pipeline runs and cleans them up afterwards."""
  
      def __init__(self, num_runs: int, enable_step_logs: bool = True):
          self.num_runs = num_runs
          self.client = Client()
          self.store = self.client.zen_store
          self.enable_step_logs = enable_step_logs
  
      def __enter__(self):
          self.pipeline_name = sample_name("sample_pipeline_run_")
          for i in range(self.num_runs):
              pipeline_instance.with_options(
                  run_name=f"{self.pipeline_name}_{i}",
                  unlisted=True,
                  enable_step_logs=self.enable_step_logs,
              )()
  
          # persist which runs, steps and artifact versions were produced.
          # In case the test ends up deleting some or all of these, this allows
          # for a thorough cleanup nonetheless
          self.runs = self.store.list_runs(
              PipelineRunFilter(name=f"startswith:{self.pipeline_name}")
          ).items
          self.steps: List[StepRunResponse] = []
          self.artifact_versions = []
          for run in self.runs:
              self.steps += self.store.list_run_steps(
                  StepRunFilter(pipeline_run_id=run.id)
              ).items
              for s in self.steps:
                  self.artifact_versions += [
                      av for avs in s.outputs.values() for av in avs
                  ]
          return self.runs
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          for run in self.runs:
              try:
                  self.client.delete_snapshot(run.snapshot.id)
              except KeyError:
                  pass
              try:
                  self.client.delete_pipeline_run(run.id)
              except KeyError:
                  pass
          for artifact_version in self.artifact_versions:
              try:
                  self.client.delete_artifact_version(artifact_version.id)
              except KeyError:
                  pass
              try:
                  artifact = self.client.get_artifact(
                      artifact_version.artifact.id
                  )
                  if not artifact.versions:
                      self.client.delete_artifact(artifact.id)
              except KeyError:
                  pass
  
  
  class UserContext:
      def __init__(
          self,
          user_name: Optional[str] = None,
          password: Optional[str] = None,
          inactive: bool = False,
          login: bool = False,
          existing_user: bool = False,
          delete: bool = True,
          is_admin: bool = True,
      ):
          if existing_user:
              self.user_name = user_name
          elif user_name:
              self.user_name = user_name
          else:
              self.user_name = sample_name("aria")
          self.client = Client()
          self.store = self.client.zen_store
          self.login = login
          if inactive and password is None:
              self.password = None
          else:
              self.password = password or random_str(32)
          self.existing_user = existing_user
          self.delete = delete
          self.is_admin = is_admin
  
      def __enter__(self):
          if not self.existing_user:
              new_user = UserRequest(
                  name=self.user_name,
                  password=self.password,
                  active=True,
                  is_admin=self.is_admin,
              )
              self.created_user = self.store.create_user(new_user)
          else:
              self.created_user = self.store.get_user(self.user_name)
  
          if self.login or self.existing_user:
              self.original_config = GlobalConfiguration.get_instance()
              self.original_client = Client.get_instance()
              self.original_credentials = CredentialsStore.get_instance()
  
              CredentialsStore.reset_instance()
              GlobalConfiguration._reset_instance()
              Client._reset_instance()
  
              try:
                  self.client = Client()
                  store_config = StoreConfiguration(
                      url=self.original_config.store.url,
                      type=self.original_config.store.type,
                      username=self.user_name,
                      password=self.password,
                      secrets_store=self.original_config.store.secrets_store,
                  )
                  GlobalConfiguration().set_store(config=store_config)
              except Exception:
                  self.cleanup()
                  raise
          return self.created_user
  
      def cleanup(self):
          if self.login or self.existing_user:
              GlobalConfiguration._reset_instance(self.original_config)
              Client._reset_instance(self.original_client)
              CredentialsStore.reset_instance(self.original_credentials)
              _ = Client().zen_store
          if not self.existing_user and self.delete:
              try:
                  self.store.delete_user(self.created_user.id)
              except (KeyError, IllegalOperationError):
                  pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          self.cleanup()
  
  
  class ServiceAccountContext:
      def __init__(
          self,
          name: str = "aria",
          description: str = "Aria's service account",
          login: bool = False,
          existing_account: bool = False,
          delete: bool = True,
      ):
          if existing_account:
              self.name = name
          else:
              self.name = sample_name(name)
          self.description = description
          self.client = Client()
          self.store = self.client.zen_store
          self.login = login
          self.existing_account = existing_account
          self.delete = delete
  
      def __enter__(self):
          if not self.existing_account:
              new_account = ServiceAccountRequest(
                  name=self.name,
                  description=self.description,
                  active=True,
              )
              self.created_service_account = self.store.create_service_account(
                  new_account
              )
          else:
              self.created_service_account = self.store.get_service_account(
                  self.name
              )
  
          if self.login or self.existing_account:
              # Create a temporary API key for the service account
              api_key_name = sample_name("temp_api_key")
              self.api_key = self.store.create_api_key(
                  self.created_service_account.id,
                  APIKeyRequest(
                      name=api_key_name,
                  ),
              )
              self.original_config = GlobalConfiguration.get_instance()
              self.original_client = Client.get_instance()
              self.original_credentials = CredentialsStore.get_instance()
  
              CredentialsStore.reset_instance()
              GlobalConfiguration._reset_instance()
              Client._reset_instance()
              try:
                  self.client = Client()
                  store_config = StoreConfiguration(
                      url=self.original_config.store.url,
                      type=self.original_config.store.type,
                      api_key=self.api_key.key,
                      secrets_store=self.original_config.store.secrets_store,
                  )
                  GlobalConfiguration().set_store(config=store_config)
              except Exception:
                  self.cleanup()
                  raise
          return self.created_service_account
  
      def cleanup(self):
          if self.login or self.existing_account:
              GlobalConfiguration._reset_instance(self.original_config)
              Client._reset_instance(self.original_client)
              CredentialsStore.reset_instance(self.original_credentials)
              _ = Client().zen_store
          if self.existing_account or self.login and self.delete:
              self.store.delete_api_key(
                  self.created_service_account.id,
                  self.api_key.id,
              )
          if not self.existing_account and self.delete:
              try:
                  self.store.delete_service_account(
                      self.created_service_account.id
                  )
              except (KeyError, IllegalOperationError):
                  pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          self.cleanup()
  
  
  class LoginContext:
      def __init__(
          self,
          user_name: Optional[str] = None,
          password: Optional[str] = None,
          api_key: Optional[str] = None,
      ):
          self.user_name = user_name
          self.password = password
          self.api_key = api_key
  
      def __enter__(self):
          self.original_config = GlobalConfiguration.get_instance()
          self.original_client = Client.get_instance()
          self.original_credentials = CredentialsStore.get_instance()
          CredentialsStore.reset_instance()
          GlobalConfiguration._reset_instance()
          Client._reset_instance()
          try:
              store_config = StoreConfiguration(
                  url=self.original_config.store.url,
                  type=self.original_config.store.type,
                  api_key=self.api_key,
                  username=self.user_name,
                  password=self.password,
                  secrets_store=self.original_config.store.secrets_store,
              )
              GlobalConfiguration().set_store(config=store_config)
          except Exception:
              self.cleanup()
              raise
  
      def cleanup(self):
          GlobalConfiguration._reset_instance(self.original_config)
          Client._reset_instance(self.original_client)
          CredentialsStore.reset_instance(self.original_credentials)
  
          _ = Client().zen_store
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          self.cleanup()
  
  
  class StackContext:
      def __init__(
          self,
          components: Dict[StackComponentType, List[uuid.UUID]],
          stack_name: str = "aria",
          delete: bool = True,
      ):
          self.stack_name = sample_name(stack_name)
          self.components = components
          self.client = Client()
          self.store = self.client.zen_store
          self.delete = delete
  
      def __enter__(self):
          new_stack = StackRequest(
              name=self.stack_name,
              components=self.components,
          )
          self.created_stack = self.store.create_stack(new_stack)
          return self.created_stack
  
      def cleanup(self):
          try:
              self.store.delete_stack(self.created_stack.id)
          except KeyError:
              pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class ComponentContext:
      def __init__(
          self,
          c_type: StackComponentType,
          config: Dict[str, Any],
          flavor: str,
          component_name: str = "aria",
          delete: bool = True,
      ):
          self.component_name = sample_name(component_name)
          self.flavor = flavor
          self.component_type = c_type
          self.config = config
          self.client = Client()
          self.store = self.client.zen_store
          self.delete = delete
  
      def __enter__(self):
          new_component = ComponentRequest(
              name=self.component_name,
              type=self.component_type,
              flavor=self.flavor,
              configuration=self.config,
          )
          self.created_component = self.store.create_stack_component(
              new_component
          )
          return self.created_component
  
      def cleanup(self):
          try:
              self.store.delete_stack_component(self.created_component.id)
          except KeyError:
              pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class ProjectContext:
      def __init__(
          self,
          project_name: str = "super_axl",
          create: bool = True,
          activate: bool = False,
      ):
          self.project_name = (
              sample_name(project_name) if create else project_name
          )
          self.client = Client()
          self.store = self.client.zen_store
          self.create = create
          self.activate = activate
  
      def __enter__(self):
          if self.create:
              new_project = ProjectRequest(name=self.project_name)
              self.project = self.store.create_project(new_project)
          else:
              self.project = self.store.get_project(self.project_name)
  
          if self.activate:
              self.original_project = self.client.active_project
              self.client.set_active_project(self.project.id)
          return self.project
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.activate:
              self.client.set_active_project(self.original_project.id)
          if self.create:
              try:
                  self.store.delete_project(self.project.id)
              except KeyError:
                  pass
  
  
  class SecretContext:
      def __init__(
          self,
          secret_name: Optional[str] = None,
          private: bool = False,
          values: Dict[str, str] = {
              "sleep": "yes",
              "food": "hell yeah",
              "bath": "NO!",
          },
          delete: bool = True,
      ):
          self.secret_name = (
              sample_name("axls-secrets") if not secret_name else secret_name
          )
          self.private = private
          self.values = values
          self.client = Client()
          self.store = self.client.zen_store
          self.delete = delete
  
      def __enter__(self):
          new_secret = SecretRequest(
              name=self.secret_name,
              private=self.private,
              values=self.values,
          )
          self.created_secret = self.store.create_secret(new_secret)
          return self.created_secret
  
      def cleanup(self):
          try:
              self.store.delete_secret(self.created_secret.id)
          except KeyError:
              pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class CodeRepositoryContext:
      def __init__(
          self,
          project_id: Optional[uuid.UUID] = None,
          delete: bool = True,
      ):
          self.code_repo_name = sample_name("code_repo")
          self.project_id = project_id
          self.client = Client()
          self.store = self.client.zen_store
          self.delete = delete
  
      def __enter__(self):
          request = CodeRepositoryRequest(
              name=self.code_repo_name,
              config={},
              source={
                  "module": "tests.unit.pipelines.test_build_utils",
                  "attribute": "StubCodeRepository",
                  "type": "user",
              },
              project=self.project_id or self.client.active_project.id,
          )
  
          self.repo = self.store.create_code_repository(request)
          return self.repo
  
      def cleanup(self):
          try:
              self.store.delete_code_repository(self.repo.id)
          except KeyError:
              pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class ServiceConnectorContext:
      def __init__(
          self,
          connector_type: str,
          auth_method: str,
          resource_types: List[str],
          name: Optional[str] = None,
          resource_id: Optional[str] = None,
          configuration: Optional[Dict[str, str]] = None,
          secrets: Optional[Dict[str, str]] = None,
          expires_at: Optional[datetime] = None,
          expires_skew_tolerance: Optional[int] = None,
          expiration_seconds: Optional[int] = None,
          labels: Optional[Dict[str, str]] = None,
          client: Optional[Client] = None,
          delete: bool = True,
      ):
          self.name = name or sample_name("connect-or")
          self.connector_type = connector_type
          self.auth_method = auth_method
          self.resource_types = resource_types
          self.resource_id = resource_id
          self.configuration = configuration
          self.secrets = secrets
          self.expires_at = expires_at
          self.expires_skew_tolerance = expires_skew_tolerance
          self.expiration_seconds = expiration_seconds
          self.labels = labels
          self.client = client or Client()
          self.store = self.client.zen_store
          self.delete = delete
  
      def __enter__(self):
          config = ServiceConnectorConfiguration(**self.configuration or {})
          if self.secrets:
              config.add_secrets(self.secrets)
          request = ServiceConnectorRequest(
              name=self.name,
              connector_type=self.connector_type,
              auth_method=self.auth_method,
              resource_types=self.resource_types,
              resource_id=self.resource_id,
              configuration=config,
              expires_at=self.expires_at,
              expires_skew_tolerance=self.expires_skew_tolerance,
              expiration_seconds=self.expiration_seconds,
              labels=self.labels or {},
          )
  
          self.connector = self.store.create_service_connector(request)
          return self.connector
  
      def cleanup(self):
          try:
              self.store.delete_service_connector(self.connector.id)
          except KeyError:
              pass
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class ModelContext:
      def __init__(
          self,
          create_version: bool = False,
          create_artifacts: int = 0,
          create_prs: int = 0,
          artifact_types: Optional[List[ArtifactType]] = None,
          delete: bool = True,
      ):
          client = Client()
          self.project = client.active_project.id
          self.model = sample_name("su_model")
          self.model_version = "2.0.0"
  
          self.create_version = create_version
          self.create_artifacts = create_artifacts
          self.artifacts = []
          self.artifact_versions = []
          self.create_prs = create_prs
          self.prs = []
          self.snapshots = []
          self.delete = delete
  
          if create_artifacts > 0:
              artifact_types = artifact_types or [
                  ArtifactType.DATA for _ in range(create_artifacts)
              ]
              assert len(artifact_types) == create_artifacts
              self.artifact_types = artifact_types
  
      def __enter__(self):
          client = Client()
          ws = client.get_project(self.project)
          stack = client.active_stack
          try:
              model = client.get_model(self.model)
          except KeyError:
              model = client.create_model(name=self.model, tags=["foo", "bar"])
          if self.create_version:
              try:
                  mv = client.get_model_version(self.model, self.model_version)
              except KeyError:
                  mv = client.zen_store.create_model_version(
                      ModelVersionRequest(
                          project=ws.id,
                          model=model.id,
                          name=self.model_version,
                      )
                  )
  
          self.pipeline = client.zen_store.create_pipeline(
              PipelineRequest(
                  name=sample_name("pipeline"),
                  project=ws.id,
              )
          )
  
          for i in range(self.create_artifacts):
              artifact = client.zen_store.create_artifact(
                  ArtifactRequest(
                      name=sample_name("sample_artifact"),
                      has_custom_name=True,
                      project=ws.id,
                  )
              )
              client.get_artifact(artifact.id)
              self.artifacts.append(artifact)
              artifact_version = client.zen_store.create_artifact_version(
                  ArtifactVersionRequest(
                      artifact_id=artifact.id,
                      version=1,
                      data_type="module.class",
                      materializer="module.class",
                      type=self.artifact_types[i],
                      uri="",
                      project=ws.id,
                      save_type=ArtifactSaveType.STEP_OUTPUT,
                  )
              )
              self.artifact_versions.append(artifact_version)
          for _ in range(self.create_prs):
              snapshot = client.zen_store.create_snapshot(
                  PipelineSnapshotRequest(
                      project=ws.id,
                      stack=stack.id,
                      pipeline=self.pipeline.id,
                      run_name_template="",
                      pipeline_configuration={"name": "pipeline_name"},
                      client_version="0.12.3",
                      server_version="0.12.3",
                      is_dynamic=False,
                  ),
              )
              self.snapshots.append(snapshot)
              self.prs.append(
                  client.zen_store.get_or_create_run(
                      PipelineRunRequest(
                          name=sample_name("sample_pipeline_run"),
                          status="running",
                          config=PipelineConfiguration(name="aria_pipeline"),
                          project=ws.id,
                          snapshot=snapshot.id,
                      )
                  )[0]
              )
          if self.create_version:
              if self.create_artifacts:
                  return mv, self.artifact_versions
              if self.create_prs:
                  return mv, self.prs
              else:
                  return mv
          else:
              if self.create_artifacts:
                  return model, self.artifact_versions
              if self.create_prs:
                  return model, self.prs
              else:
                  return model
  
      def cleanup(self):
          client = Client()
          try:
              client.delete_model(self.model)
          except KeyError:
              pass
          for artifact_version in self.artifact_versions:
              client.delete_artifact_version(artifact_version.id)
          for artifact in self.artifacts:
              client.delete_artifact(artifact.id)
          for run in self.prs:
              client.zen_store.delete_run(run.id)
          for snapshot in self.snapshots:
              client.delete_snapshot(snapshot.id)
          client.zen_store.delete_pipeline(self.pipeline.id)
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              self.cleanup()
  
  
  class CatClawMarks(AuthenticationConfig):
      """Cat claw marks authentication credentials."""
  
      paw: PlainSerializedSecretStr = Field(
          title="Paw",
      )
      hiding_spot: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Hiding spot",
      )
      color: Optional[str] = Field(
          default=None,
          title="Cat color.",
      )
      name: str = Field(
          title="Cat name.",
      )
  
  
  class CatVoicePrint(AuthenticationConfig):
      """Cat voice-print authentication credentials."""
  
      secret_word: PlainSerializedSecretStr = Field(
          title="Secret word",
      )
      hiding_spot: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Hiding spot",
      )
      color: Optional[str] = Field(
          default=None,
          title="Cat color.",
      )
      name: str = Field(
          title="Cat name.",
      )
  
  
  class CatPawPrint(AuthenticationConfig):
      """Cat paw print authentication credentials."""
  
      color: Optional[str] = Field(
          default=None,
          title="Cat color.",
      )
      dreams: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Cat dreams.",
      )
      hiding_place: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Hiding place",
      )
      language: Optional[str] = Field(
          default=None,
          title="Cat language.",
      )
      foods: Optional[str] = Field(
          default=None,
          title="Cat foods.",
      )
      chase: Optional[str] = Field(
          default=None,
          title="Cat chase.",
      )
  
  
  class CatTailPrint(AuthenticationConfig):
      """Cat tail print authentication credentials."""
  
      color: Optional[str] = Field(
          default=None,
          title="Cat color.",
      )
      dreams: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Cat dreams.",
      )
      hiding_place: Optional[PlainSerializedSecretStr] = Field(
          default=None,
          title="Hiding place",
      )
      language: Optional[str] = Field(
          default=None,
          title="Cat language.",
      )
      foods: Optional[str] = Field(
          default=None,
          title="Cat foods.",
      )
  
  
  class ServiceConnectorTypeContext:
      def __init__(
          self,
          connector_type: Optional[str] = None,
          resource_type_one: Optional[str] = None,
          resource_type_two: Optional[str] = None,
          delete: bool = True,
      ):
          self.connector_type = connector_type
          self.resource_type_one = resource_type_one
          self.resource_type_two = resource_type_two
          self.delete = delete
  
      def __enter__(self):
          self.connector_type_spec = ServiceConnectorTypeModel(
              name="Cat service connector",
              connector_type=self.connector_type or sample_name("cat'o'matic"),
              auth_methods=[
                  AuthenticationMethodModel(
                      name="Claw marks authentication",
                      auth_method="claw-marks",
                      config_class=CatClawMarks,
                  ),
                  AuthenticationMethodModel(
                      name="Voice print authentication",
                      auth_method="voice-print",
                      config_class=CatVoicePrint,
                  ),
                  AuthenticationMethodModel(
                      name="Paw print authentication",
                      auth_method="paw-print",
                      config_class=CatPawPrint,
                  ),
                  AuthenticationMethodModel(
                      name="Tail print authentication",
                      auth_method="tail-print",
                      config_class=CatTailPrint,
                  ),
              ],
              resource_types=[
                  ResourceTypeModel(
                      name="Cat scratches",
                      resource_type=self.resource_type_one
                      or sample_name("scratch"),
                      auth_methods=[
                          "claw-marks",
                          "voice-print",
                          "paw-print",
                          "tail-print",
                      ],
                      supports_instances=True,
                  ),
                  ResourceTypeModel(
                      name="Cat purrs",
                      resource_type=self.resource_type_two
                      or sample_name("purr"),
                      auth_methods=[
                          "claw-marks",
                          "voice-print",
                          "paw-print",
                          "tail-print",
                      ],
                      supports_instances=False,
                  ),
              ],
          )
  
          service_connector_registry.register_service_connector_type(
              self.connector_type_spec
          )
  
          return self.connector_type_spec
  
      def __exit__(self, exc_type, exc_value, exc_traceback):
          if self.delete:
              try:
                  del service_connector_registry.service_connector_types[
                      self.connector_type
                  ]
              except KeyError:
                  pass
  
  
  AnyRequest = TypeVar("AnyRequest", bound=BaseRequest)
  AnyResponse = TypeVar("AnyResponse", bound=BaseIdentifiedResponse)
  
  
  class CrudTestConfig:
      """Model to collect all methods pertaining to a given entity."""
  
      def __init__(
          self,
          create_model: "BaseModel",
          filter_model: Type[BaseFilter],
          entity_name: str,
          update_model: Optional["BaseModel"] = None,
          supported_zen_stores: Tuple[Type["BaseZenStore"]] = None,
          conditional_entities: Optional[
              Dict[str, Union["CrudTestConfig", List["CrudTestConfig"]]]
          ] = None,
      ):
          """Initializes a CrudTestConfig.
  
          Args:
              create_model: Model to use for creating the entity.
              update_model: Model to use for updating the entity.
              filter_model: Model to use for filtering entities.
              entity_name: Name of the entity.
              supported_zen_stores: Set of supported Zen Stores. Defaults to all.
              conditional_entities: Other entities that need to exist before the
                  entity under test can be created. Expected to be a mapping from
                  field in the `create_model` to corresponding `CrudTestConfig`
                  (or list of them if target value is a list of ids).
          """
          self.create_model = create_model
          self.update_model = update_model
          self.filter_model = filter_model
          self.entity_name = entity_name
          self.conditional_entities = conditional_entities or {}
          self.id: Optional[uuid.UUID] = None
          if not supported_zen_stores:
              self.supported_zen_stores = (RestZenStore, SqlZenStore)
          else:
  