class BrokerCredentials:
    def __init__(self, username: str, password: str):
def _check_plan_id(broker: ServiceBroker, plan_id) -> bool:
def get_blueprint(service_broker: ServiceBroker,
                  broker_credentials: Union[None, List[BrokerCredentials], BrokerCredentials],
                  logger: logging.Logger) -> Blueprint:
    def extract_authorization_username(request: Request):
    def error_handler(e):
    def error_handler_not_implemented(e):
    def error_handler_bad_request(e):
    def catalog():
    def provision(instance_id):
    def update(instance_id):
    def bind(instance_id, binding_id):
    def unbind(instance_id, binding_id):
    def deprovision(instance_id):
    def last_operation(instance_id):
    def last_binding_operation(instance_id, binding_id):
    def get_instance(instance_id):
    def get_binding(instance_id, binding_id):
def serve_multiple(service_brokers: List[ServiceBroker],
                   credentials: Union[List[BrokerCredentials], BrokerCredentials, None],
                   logger: logging.Logger = logging.root,
                   port=5000,
                   debug=False):
def serve(service_broker: ServiceBroker,
          credentials: Union[List[BrokerCredentials], BrokerCredentials, None],
          logger: logging.Logger = logging.root,
          port=5000,
          debug=False): class ServiceException(Exception):
class ErrInstanceAlreadyExists(ServiceException):
class ErrInstanceDoesNotExist(ServiceException):
class ErrInstanceLimitMet(ServiceException):
class ErrPlanQuotaExceeded(ServiceException):
class ErrServiceQuotaExceeded(ServiceException):
class ErrBindingAlreadyExists(ServiceException):
class ErrBindingDoesNotExist(ServiceException):
class ErrInvalidParameters(ServiceException):
class ErrAsyncRequired(ServiceException):
class ErrPlanChangeNotSupported(ErrInvalidParameters):
class ErrAppGuidNotProvided(ServiceException):
class ErrConcurrentInstanceAccess(ServiceException):
class ErrBadRequest(ServiceException):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self, msg):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self, msg='Malformed or missing data'): class ServicePlan:
    def __init__(self,
                 id: str,
                 name: str,
                 description: str,
                 metadata: ServicePlanMetadata = None,
                 free: bool = None,
                 bindable: bool = None,
                 schemas: Schemas = None,
                 **kwargs):
        self.id = id
        self.name = name
        self.description = description
        self.metadata = metadata
        self.free = free
        self.bindable = bindable
        self.schemas = schemas

        self.__dict__.update(kwargs) class ServiceBroker:
    """
    Provides a service. This covers catalog, provision, update, bind, unbind, deprovision and last operation.
    """

    def catalog(self) -> Union[Service, List[Service]]:
        """
        Returns the services information which is provided by this broker.

        :return: Service or list of services
        """
        raise NotImplementedError()

    def provision(self,
                  instance_id: str,
                  details: ProvisionDetails,
                  async_allowed: bool,
                  **kwargs
                  ) -> ProvisionedServiceSpec:
        """
        Further readings `CF Broker API#Provisioning <https://docs.cloudfoundry.org/services/api.html#provisioning>`_

        :param instance_id: Instance id provided by the platform
        :param details: Details about the service to create
        :param async_allowed: Client allows async creation
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: ProvisionedServiceSpec
        :raises ErrInstanceAlreadyExists: If instance already exists
        :raises ErrAsyncRequired: If async is required but not supported
        """
        raise NotImplementedError()

    def update(self,
               instance_id: str,
               details: UpdateDetails,
               async_allowed: bool,
               **kwargs
               ) -> UpdateServiceSpec:
        """
        Further readings `CF Broker API#Update <https://docs.cloudfoundry.org/services/api.html#updating_service_instance>`_

        :param instance_id: Instance id provided by the platform
        :param details: Details about the service to update
        :param async_allowed: Client allows async creation
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: UpdateServiceSpec
        :raises ErrAsyncRequired: If async is required but not supported
        """
        raise NotImplementedError()

    def deprovision(self,
                    instance_id: str,
                    details: DeprovisionDetails,
                    async_allowed: bool,
                    **kwargs
                    ) -> DeprovisionServiceSpec:
        """
        Further readings `CF Broker API#Deprovisioning <https://docs.cloudfoundry.org/services/api.html#deprovisioning>`_

        :param instance_id: Instance id provided by the platform
        :param details: Details about the service to delete
        :param async_allowed: Client allows async creation
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: DeprovisionServiceSpec
        :raises ErrInstanceDoesNotExist: If instance does not exists
        :raises ErrAsyncRequired: If async is required but not supported
        """
        raise NotImplementedError()

    def bind(self,
             instance_id: str,
             binding_id: str,
             details: BindDetails,
             async_allowed: bool,
             **kwargs
             ) -> Binding:
        """
        Further readings `CF Broker API#Binding <https://docs.cloudfoundry.org/services/api.html#binding>`_

        :param instance_id: Instance id provided by the platform
        :param binding_id: Binding id provided by the platform
        :param details: Details about the binding to create
        :param async_allowed: Client allows async binding
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: Binding
        :raises ErrBindingAlreadyExists: If binding already exists
        :raises ErrAppGuidNotProvided: If AppGuid is required but not provided
        """
        raise NotImplementedError()

    def unbind(self,
               instance_id: str,
               binding_id: str,
               details: UnbindDetails,
               async_allowed: bool,
               **kwargs
               ) -> UnbindSpec:
        """
        Further readings `CF Broker API#Unbinding <https://docs.cloudfoundry.org/services/api.html#unbinding>`_

        :param instance_id: Instance id provided by the platform
        :param binding_id: Binding id provided by the platform
        :param details: Details about the binding to delete
        :param async_allowed: Client allows async unbind
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: UnbindDetails
        :raises ErrBindingAlreadyExists: If binding already exists
        """
        raise NotImplementedError()

    def get_instance(self,
                     instance_id: str,
                     **kwargs
                     ) -> GetInstanceDetailsSpec:
        """
        Further readings `CF Broker API#FetchServiceInstance <https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md#fetching-a-service-instance>`_
        Must be implemented if `"instances_retrievable" :true` is declared for a service in `catalog`.

        :param instance_id: Instance id provided by the platform
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: GetInstanceDetailsSpec
        :raises ErrInstanceDoesNotExist: If instance does not exists
        :raises ErrConcurrentInstanceAccess: If instance is being updated
        """
        raise NotImplementedError()

    def get_binding(self,
                    instance_id: str,
                    binding_id: str,
                    **kwargs
                    ) -> GetBindingSpec:
        """
                Further readings `CF Broker API#FetchServiceBinding <https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md#fetching-a-service-binding>`_
                Must be implemented if `"bindings_retrievable" :true` is declared for a service in `catalog`.

                :param instance_id: Instance id provided by the platform
                :param binding_id: Instance id provided by the platform
                :param kwargs: May contain additional information, improves compatibility with upstream versions
                :rtype: GetBindingSpec
                :raises ErrBindingDoesNotExist: If binding does not exist
                """
        raise NotImplementedError()

    def last_operation(self,
                       instance_id: str,
                       operation_data: Optional[str],
                       service_id: Optional[str],
                       plan_id: Optional[str],
                       **kwargs
                       ) -> LastOperation:
        """
        Further readings `CF Broker API#LastOperation <https://docs.cloudfoundry.org/services/api.html#polling>`_

        :param instance_id: Instance id provided by the platform
        :param operation_data: Operation data received from async operation
        :param service_id: service identifier
        :param plan_id: plan identifier
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: LastOperation
        """
        raise NotImplementedError()

    def last_binding_operation(self,
                               instance_id: str,
                               binding_id: str,
                               operation_data: Optional[str],
                               service_id: Optional[str],
                               plan_id: Optional[str],
                               **kwargs
                               ) -> LastOperation:
        """
        Further readings `CF Broker API#LastOperationForBindings <https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md#polling-last-operation-for-service-bindings>`_
        Must be implemented if `Provision`, `Update`, or `Deprovision` are async.

        :param instance_id: Instance id provided by the platform
        :param binding_id: Binding id provided by the platform
        :param operation_data: Operation data received from async operation
        :param service_id: service identifier
        :param plan_id: plan identifier
        :param kwargs: May contain additional information, improves compatibility with upstream versions
        :rtype: LastOperation
        """
        raise NotImplementedError() class Service:
    def __init__(self,
                 id: str,
                 name: str,
                 description: str,
                 bindable: bool,
                 plans: List[ServicePlan],
                 tags: List[str] = None,
                 requires: List[str] = None,
                 metadata: ServiceMetadata = None,
                 dashboard_client: ServiceDashboardClient = None,
                 plan_updateable: bool = False,
                 instances_retrievable: bool = False,
                 bindings_retrievable: bool = False,
                 **kwargs
                 ):
        """
        :param requires:  syslog_drain, route_forwarding or volume_mount
        """
        self.id = id
        self.name = name
        self.description = description
        self.bindable = bindable
        self.plans = plans
        self.tags = tags
        self.requires = requires
        self.metadata = metadata
        self.dashboard_client = dashboard_client
        self.plan_updateable = plan_updateable
        self.instances_retrievable = instances_retrievable
        self.bindings_retrievable = bindings_retrievable

        self.__dict__.update(kwargs) class ProvisionDetails:
    def __init__(self,
                 service_id: str,
                 plan_id: str,
                 organization_guid: str = None,
                 space_guid: str = None,
                 parameters: dict = None,
                 context: dict = None,
                 **kwargs):
        self.service_id = service_id
        self.plan_id = plan_id
        self.organization_guid = organization_guid
        self.space_guid = space_guid
        self.parameters = parameters
        self.context = context

        # Usage context information
        if isinstance(context, dict) and 'organization_guid' in context:
            if organization_guid is not None and context['organization_guid'] != organization_guid:
                raise TypeError('organization_guid does not match with context.organization_guid')
            self.organization_guid = context['organization_guid']
        if isinstance(context, dict) and 'space_guid' in context:
            if space_guid is not None and context['space_guid'] != space_guid:
                raise TypeError('space_guid does not match with context.space_guid')
            self.space_guid = context['space_guid']

        if DISABLE_SPACE_ORG_GUID_CHECK:
            pass
        elif None in (self.organization_guid, self.space_guid):
            raise TypeError('Organization and space guid are required.')

        # HTTP contextual data
        self.authorization_username = None  #: username of HTTP Basic Auth
        self.originating_identity = None  #: decoded X-Broker-Originating-Identity HTTP Header class ProvisionedServiceSpec:
    def __init__(self,
                 state: ProvisionState = ProvisionState.SUCCESSFUL_CREATED,
                 dashboard_url: str = None,
                 operation: str = None
                 ):
        self.state = state
        self.dashboard_url = dashboard_url
        self.operation = operation

    @property
    def is_async(self):
        return self.state == ProvisionState.IS_ASYNC class ProvisionState(Enum):
    IS_ASYNC = "is_async"
    SUCCESSFUL_CREATED = "successfully created"
    IDENTICAL_ALREADY_EXISTS = "exists with identical config" class DeprovisionDetails:
    def __init__(self,
                 service_id: str,
                 plan_id: str
                 ):
        self.service_id = service_id
        self.plan_id = plan_id
        # HTTP contextual data
        self.authorization_username = None  #: username of HTTP Basic Auth
        self.originating_identity = None  #: decoded X-Broker-Originating-Identity HTTP Header class DeprovisionServiceSpec:
    def __init__(self,
                 is_async: bool,
                 operation: str = None
                 ):
        self.is_async = is_async
        self.operation = operation class BindDetails:
    def __init__(self,
                 service_id: str,
                 plan_id: str,
                 app_guid: str = None,
                 bind_resource: dict = None,
                 parameters: dict = None,
                 context: dict = None,
                 **kwargs
                 ):
        self.app_guid = app_guid
        self.plan_id = plan_id
        self.service_id = service_id
        self.parameters = parameters
        self.bind_resource = BindResource(**bind_resource) if bind_resource else None
        self.context = context
        # HTTP contextual data
        self.authorization_username = None  #: username of HTTP Basic Auth
        self.originating_identity = None  #: decoded X-Broker-Originating-Identity HTTP Header class Binding:
    def __init__(self,
                 state=BindState.SUCCESSFUL_BOUND,
                 credentials: dict = None,
                 syslog_drain_url: str = None,
                 route_service_url: str = None,
                 volume_mounts: List[VolumeMount] = None,
                 operation: Optional[str] = None
                 ):
        self.state = state
        self.credentials = credentials
        self.syslog_drain_url = syslog_drain_url
        self.route_service_url = route_service_url
        self.volume_mounts = volume_mounts
        self.operation = operation class BindState(Enum):
    IS_ASYNC = "is_async"
    SUCCESSFUL_BOUND = "successfully created"
    IDENTICAL_ALREADY_EXISTS = "exists with identical config" class UnbindDetails:
    def __init__(self,
                 service_id: str,
                 plan_id: str
                 ):
        self.plan_id = plan_id
        self.service_id = service_id
        # HTTP contextual data
        self.authorization_username = None  #: username of HTTP Basic Auth
        self.originating_identity = None  #: decoded X-Broker-Originating-Identity HTTP Header class UnbindSpec:
    def __init__(self,
                 is_async: bool,
                 operation: str = None
                 ):
        self.is_async = is_async
        self.operation = operation
import json
import time
import requests
from http import HTTPStatus
from threading import Thread
from typing import Union, List
from unittest import TestCase
from uuid import uuid4
from requests.auth import HTTPBasicAuth
from openbrokerapi import api, errors
from openbrokerapi.catalog import ServicePlan
from openbrokerapi.service_broker import (
    ServiceBroker,
    Service,
    ProvisionDetails,
    ProvisionedServiceSpec,
    ProvisionState,
    DeprovisionDetails,
    DeprovisionServiceSpec,
    BindDetails,
    Binding,
    BindState,
    UnbindDetails,
    UnbindSpec)




class FullRouterTestCase(TestCase):

    def setUp(self) -> None:
        broker_username = str(uuid4())
        broker_passsword = str(uuid4())
        self.request_ads = {
            'auth': HTTPBasicAuth(broker_username, broker_passsword),
            'headers': {'X-Broker-Api-Version': '2.15', 'Content-Type': 'application/json'}
        }

        self.broker_1 = InMemoryBroker(str(uuid4()), str(uuid4()))
        self.broker_2 = InMemoryBroker(str(uuid4()), str(uuid4()))

        def run_server():
            api.serve_multiple([self.broker_1, self.broker_2],

                               api.BrokerCredentials(broker_username, broker_passsword),