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'): DEFAULT_NOT_IMPLEMENTED_ERROR_MESSAGE = 'This action is not supported, you may contact your support or administrator.'
DEFAULT_BAD_REQUEST_ERROR_MESSAGE = 'The request data is invalid.'
DEFAULT_EXCEPTION_ERROR_MESSAGE = 'Some error occurred, please contact your support or administrator.' def to_json_response(obj):
    """ Following https://stackoverflow.com/a/1118038 """
    from flask import jsonify
    return jsonify(_to_dict(obj)) def ensure_list(x: Union[Iterable, Any]):
    import collections
    if isinstance(x, collections.abc.Iterable):
        return x
    else:
        return [x] def print_request():
    from flask import request
    logger.debug("--- Request Start -------------------")
    logger.debug("--- Header")
    for k, v in request.headers:
        logger.debug("%s:%s", k, v)
    logger.debug("--- Body")
    logger.debug(request.data)
    logger.debug("--- Request End ---------------------") def check_originating_identity():
    """
    Check and decode the "X-Broker-API-Originating-Identity" header
    https://github.com/openservicebrokerapi/servicebroker/blob/v2.13/spec.md#originating-identity
    """
    from flask import request, json
    if "X-Broker-API-Originating-Identity" in request.headers:
        try:
            platform, value = request.headers["X-Broker-API-Originating-Identity"].split(None, 1)
            request.originating_identity = {
                'platform': platform,
                'value': json.loads(base64.standard_b64decode(value))
            }
        except ValueError as e:
            return to_json_response(ErrorResponse(
                description='Improper "X-Broker-API-Originating-Identity" header. ' + str(e))
            ), HTTPStatus.BAD_REQUEST
    else:
        request.originating_identity = None def get_auth_filter(broker_credentials):
    def requires_auth():
        """Check authentication over all provided usernames else sends a 401 response that enables basic auth"""
        from flask import request
        auth = request.authorization
        if auth:
            for credentials in broker_credentials:
                if auth.username == credentials.username and auth.password == credentials.password:
                    return
        return to_json_response(ErrorResponse(
            description='Could not verify your access level for that URL.\nYou have to login with proper credentials'
        )), HTTPStatus.UNAUTHORIZED, {'WWW-Authenticate': 'Basic realm="Login Required"'}

    return requires_auth def check_version():
    from flask import request
    version = request.headers.get("X-Broker-Api-Version", None)
    if not version:
        return to_json_response(ErrorResponse(description="No X-Broker-Api-Version found.")), HTTPStatus.BAD_REQUEST
    if MIN_VERSION > version_tuple(version):
        return to_json_response(ErrorResponse(
            description="Service broker requires version %d.%d+." % MIN_VERSION)
        ), HTTPStatus.PRECONDITION_FAILED def requires_application_json(f):
    """Decorator for enforcing application/json Content-Type"""

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        from flask import request
        if request.get_json(silent=True) is None:
            er = ErrorResponse(description='Improper Content-Type header. Expecting "application/json"')
            return to_json_response(er), HTTPStatus.BAD_REQUEST
        else:
            return f(*args, **kwargs)

    return wrapped class BindResponse:
    def __init__(self,
                 credentials: dict = None,
                 syslog_drain_url: str = None,
                 route_service_url: str = None,
                 volume_mounts: List[VolumeMount] = None,
                 operation: Optional[str] = None
                 ):
        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 CatalogResponse:
    def __init__(self, services: List[Service]):
        self.services = services class DeprovisionResponse(AsyncResponse):
    def __init__(self, operation: str):
        self.operation = operation class EmptyResponse:
    pass class ErrorResponse:
    def __init__(self,
                 error: str = None,
                 description: str = None):
        self.error = error
        self.description = description class LastOperationResponse:
    def __init__(self,
                 state: OperationState,
                 description: str
                 ):
        self.state = state.value
        self.description = description class ProvisioningResponse(AsyncResponse):
    def __init__(self,
                 dashboard_url: str,
                 operation: str):
        self.dashboard_url = dashboard_url
        self.operation = operation class UpdateResponse(AsyncResponse):
    def __init__(self, operation: Optional[str], dashboard_url: Optional[str]):
        self.operation = operation
        self.dashboard_url = dashboard_url class UnbindResponse(AsyncResponse):
    def __init__(self, operation: str):
        self.operation = operation class GetInstanceResponse:
    def __init__(self,
                 service_id: str,
                 plan_id: str,
                 dashboard_url: Optional[str] = None,
                 parameters: Optional[dict] = None
                 ):
        self.service_id = service_id
        self.plan_id = plan_id
        self.dashboard_url = dashboard_url
        self.parameters = parameters class GetBindingResponse:
    def __init__(self,
                 credentials: dict = None,
                 syslog_drain_url: str = None,
                 route_service_url: str = None,
                 volume_mounts: List[VolumeMount] = None,
                 parameters: Optional[dict] = None
                 ):
        self.credentials = credentials
        self.syslog_drain_url = syslog_drain_url
        self.route_service_url = route_service_url
        self.volume_mounts = volume_mounts
        self.parameters = parameters class Router(ServiceBroker):
    def __init__(self, *service_brokers: ServiceBroker):
        self._service_brokers = service_brokers

    @staticmethod
    def _service_ids(broker: ServiceBroker) -> Set[str]:
        return {service.id for service in ensure_list(broker.catalog())}

    def _get_provider_by_id(self, service_id: str) -> ServiceBroker:
        for broker in self._service_brokers:
            if service_id in self._service_ids(broker):
                return broker
        raise KeyError('Service {} not found'.format(service_id))

    @staticmethod
    def add_service_id_to_async_response(response, service_id: str):
        if response.is_async:
            if response.operation is None:
                response.operation = service_id
            else:
                response.operation = ' '.join((service_id, response.operation))

    def catalog(self) -> List[Service]:
        services = []
        for broker in self._service_brokers:
            services.extend(ensure_list(broker.catalog()))
        return services

    def provision(self,
                  instance_id: str,
                  details: ProvisionDetails,
                  async_allowed: bool,
                  **kwargs) -> ProvisionedServiceSpec:
        provider = self._get_provider_by_id(details.service_id)

        result = provider.provision(instance_id, details, async_allowed, **kwargs)
        self.add_service_id_to_async_response(result, details.service_id)

        return result

    def update(self, instance_id: str,
               details: UpdateDetails,
               async_allowed: bool,
               **kwargs) -> UpdateServiceSpec:
        provider = self._get_provider_by_id(details.service_id)
        result = provider.update(instance_id, details, async_allowed, **kwargs)
        self.add_service_id_to_async_response(result, details.service_id)

        return result

    def deprovision(self, instance_id: str,
                    details: DeprovisionDetails,
                    async_allowed: bool,
                    **kwargs) -> DeprovisionServiceSpec:
        provider = self._get_provider_by_id(details.service_id)
        result = provider.deprovision(instance_id, details, async_allowed, **kwargs)
        self.add_service_id_to_async_response(result, details.service_id)

        return result

    def bind(self,
             instance_id: str,
             binding_id: str,
             details: BindDetails,
             async_allowed: bool,
             **kwargs) -> Binding:
        provider = self._get_provider_by_id(details.service_id)
        result = provider.bind(instance_id, binding_id, details, False, **kwargs)

        return result

    def unbind(self,
               instance_id: str,
               binding_id: str,
               details: UnbindDetails,
               async_allowed: bool,
               **kwargs):
        provider = self._get_provider_by_id(details.service_id)
        result = provider.unbind(instance_id, binding_id, details, False, **kwargs)

        return result

    def last_operation(self,
                       instance_id: str,
                       operation_data: Optional[str],
                       **kwargs) -> LastOperation:
        data = operation_data.split(' ', maxsplit=1)
        service_id = data[0]
        if len(data) == 2:
            operation_data = data[1]
        else:
            operation_data = None

        try:
            provider = self._get_provider_by_id(service_id)
        except KeyError as e:
            logger.exception(e)
            raise errors.ErrInvalidParameters('Invalid operation string')

        result = provider.last_operation(instance_id, operation_data, **kwargs)
        return result 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 BindState(Enum):
    IS_ASYNC = "is_async"
    SUCCESSFUL_BOUND = "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 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 ProvisionState(Enum):
    IS_ASYNC = "is_async"
    SUCCESSFUL_CREATED = "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 UpdateDetails:
    def __init__(self,
                 service_id: str,
                 plan_id: str = None,
                 parameters: dict = None,
                 previous_values: dict = None,
                 context: dict = None,
                 **kwargs
                 ):
        self.service_id = service_id
        self.plan_id = plan_id
        self.parameters = parameters
        self.previous_values = PreviousValues(**previous_values) if previous_values 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 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 OperationState(Enum):
    IN_PROGRESS = "in progress"
    SUCCEEDED = "succeeded"
    FAILED = "failed" MIN_VERSION = version_tuple(MIN_VERSION_STR) DISABLE_VERSION_CHECK = getenv('DISABLE_VERSION_CHECK', '').lower() == 'true'
import logging
from http import HTTPStatus
from json.decoder import JSONDecodeError
from typing import List, Union
from flask import Blueprint, Request
from flask import json, request
from openbrokerapi import errors, constants
from openbrokerapi.helper import to_json_response, ensure_list
from openbrokerapi.request_filter import (
    print_request,
    check_originating_identity,
    get_auth_filter,
    check_version,
    requires_application_json
)
from openbrokerapi.response import (
    BindResponse,
    CatalogResponse,
    DeprovisionResponse,
    EmptyResponse,
    ErrorResponse,
    LastOperationResponse,
    ProvisioningResponse,
    UpdateResponse,
    UnbindResponse,
    GetInstanceResponse,
    GetBindingResponse)
from openbrokerapi.router import Router
from openbrokerapi.service_broker import (
    BindDetails,
    BindState,
    DeprovisionDetails,
    ProvisionDetails,
    ProvisionState,
    UnbindDetails,
    UpdateDetails,
    ServiceBroker, OperationState)
from openbrokerapi.settings import (
    MIN_VERSION,
    DISABLE_VERSION_CHECK
)
    from flask import Flask
        from gevent.pywsgi import WSGIServer

        if result.is_async:
            return to_json_response(UpdateResponse(result.operation, result.dashboard_url)), HTTPStatus.ACCEPTED
        else:
            return to_json_response(UpdateResponse(None, result.dashboard_url)), HTTPStatus.OK

    @openbroker.route("/v2/service_instances/<instance_id>/service_bindings/<binding_id>", methods=['PUT'])
    @requires_application_json
    def bind(instance_id, binding_id):
        try:
            accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false')

            binding_details = BindDetails(**json.loads(request.data))
            binding_details.originating_identity = request.originating_identity
            binding_details.authorization_username = extract_authorization_username(request)
            if not _check_plan_id(service_broker, binding_details.plan_id):
                raise TypeError('plan_id not found in this service.')
        except (TypeError, KeyError, JSONDecodeError) as e:
            logger.exception(e)
            return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST

        try:
            result = service_broker.bind(instance_id, binding_id, binding_details, accepts_incomplete)
        except errors.ErrBindingAlreadyExists as e:
            logger.exception(e)
            return to_json_response(EmptyResponse()), HTTPStatus.CONFLICT
        except errors.ErrAppGuidNotProvided as e:
            logger.exception(e)
            return to_json_response(ErrorResponse(
                error="RequiresApp",
                description="This service supports generation of credentials through binding an application only."
            )), HTTPStatus.UNPROCESSABLE_ENTITY
        except errors.ErrConcurrentInstanceAccess as e:
            logger.exception(e)
            error_response = ErrorResponse(error='ConcurrencyError',
                                           description='The Service Broker does not support concurrent requests that mutate the same resource.')
            return to_json_response(error_response), HTTPStatus.UNPROCESSABLE_ENTITY

        response = BindResponse(
            credentials=result.credentials,
            syslog_drain_url=result.syslog_drain_url,
            route_service_url=result.route_service_url,
            volume_mounts=result.volume_mounts
        )
        if result.state == BindState.SUCCESSFUL_BOUND:
            return to_json_response(response), HTTPStatus.CREATED
        elif result.state == BindState.IDENTICAL_ALREADY_EXISTS:
            return to_json_response(response), HTTPStatus.OK
        elif result.state == BindState.IS_ASYNC:
            return to_json_response(BindResponse(operation=result.operation)), HTTPStatus.ACCEPTED
        else:
            raise errors.ServiceException('IllegalState, BindState unknown.')

    @openbroker.route("/v2/service_instances/<instance_id>/service_bindings/<binding_id>", methods=['DELETE'])
    def unbind(instance_id, binding_id):
        try:
            accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false')

            plan_id = request.args["plan_id"]
            service_id = request.args["service_id"]

            unbind_details = UnbindDetails(service_id=service_id, plan_id=plan_id)
            unbind_details.originating_identity = request.originating_identity
            unbind_details.authorization_username = extract_authorization_username(request)
            if not _check_plan_id(service_broker, unbind_details.plan_id):
                raise TypeError('plan_id not found in this service.')
        except (TypeError, KeyError) as e:
            logger.exception(e)
            return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST

        try:
            result = service_broker.unbind(instance_id, binding_id, unbind_details, accepts_incomplete)
        except errors.ErrBindingDoesNotExist as e:
            logger.exception(e)
            return to_json_response(EmptyResponse()), HTTPStatus.GONE
        except errors.ErrConcurrentInstanceAccess as e:
            logger.exception(e)
            error_response = ErrorResponse(error='ConcurrencyError',
                                           description='The Service Broker does not support concurrent requests that mutate the same resource.')
            return to_json_response(error_response), HTTPStatus.UNPROCESSABLE_ENTITY

        if result.is_async:
            return to_json_response(UnbindResponse(result.operation)), HTTPStatus.ACCEPTED
        else:
            return to_json_response(EmptyResponse()), HTTPStatus.OK

    @openbroker.route("/v2/service_instances/<instance_id>", methods=['DELETE'])
    def deprovision(instance_id):
        try:
            plan_id = request.args["plan_id"]
            service_id = request.args["service_id"]
            accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false')

            deprovision_details = DeprovisionDetails(service_id=service_id, plan_id=plan_id)
            deprovision_details.originating_identity = request.originating_identity
            deprovision_details.authorization_username = extract_authorization_username(request)
            if not _check_plan_id(service_broker, deprovision_details.plan_id):
                raise TypeError('plan_id not found in this service.')
        except (TypeError, KeyError) as e:
            logger.exception(e)
            return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST

        try:
            result = service_broker.deprovision(instance_id, deprovision_details, accepts_incomplete)
        except errors.ErrInstanceDoesNotExist as e:
            logger.exception(e)
            return to_json_response(EmptyResponse()), HTTPStatus.GONE
        except errors.ErrAsyncRequired as e:
            logger.exception(e)
            return to_json_response(ErrorResponse(
                error="AsyncRequired",
                description="This service plan requires client support for asynchronous service operations."
            )), HTTPStatus.UNPROCESSABLE_ENTITY
        except errors.ErrConcurrentInstanceAccess as e:
            logger.exception(e)
            error_response = ErrorResponse(error='ConcurrencyError',
                                           description='The Service Broker does not support concurrent requests that mutate the same resource.')
            return to_json_response(error_response), HTTPStatus.UNPROCESSABLE_ENTITY

        if result.is_async:

            return to_json_response(DeprovisionResponse(result.operation)), HTTPStatus.ACCEPTED