# Path: autopush/exceptions.py
# class RouterException(AutopushException):
#     """Exception if routing has failed, may include a custom status_code and
#     body to write to the response.
# 
#     """
#     def __init__(self, message, status_code=500, response_body="",
#                  router_data=None, headers=None, log_exception=True,
#                  errno=None, logged_status=None, **kwargs):
#         """Create a new RouterException"""
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.headers = {} if headers is None else headers
#         self.log_exception = log_exception
#         self.response_body = response_body or message
#         self.errno = errno
#         self.logged_status = logged_status
#         self.extra = kwargs
# 
# Path: autopush/metrics.py
# def make_tags(base=None, **kwargs):
#     # type: (Sequence[str], **Any) -> Sequence[str]
#     """Generate a list of tag values"""
#     tags = list(base or [])
#     tags.extend('{}:{}'.format(key, val) for key, val in kwargs.iteritems())
#     return tags
# 
# Path: autopush/router/interface.py
# class RouterResponse(object):
#     """Router response if routing has succeeded.
# 
#     If the router data needs to change as a result of this message, either the
#     router got invalidated, or needs updating, then the router_data should be
#     set.
# 
#     """
#     def __init__(self, status_code=200, response_body="", router_data=None,
#                  headers=None, errno=None, logged_status=None):
#         """Create a new RouterResponse"""
#         self.status_code = status_code
#         self.response_body = response_body
#         self.router_data = router_data
#         self.headers = {} if headers is None else headers
#         self.errno = errno
#         self.logged_status = logged_status
# 
# Path: autopush/types.py



from typing import Any  # noqa
from requests.exceptions import ConnectionError
from twisted.internet.threads import deferToThread
from twisted.logger import Logger
from autopush.exceptions import RouterException
from autopush.metrics import make_tags
from autopush.router.interface import RouterResponse
from autopush.types import JSONDict  # noqa
import pyfcm
            if 'encryption' in notification.headers:
                data['enc'] = notification.headers['encryption']
            if 'crypto_key' in notification.headers:
                data['cryptokey'] = notification.headers['crypto_key']
            elif 'encryption_key' in notification.headers:
                data['enckey'] = notification.headers['encryption_key']
        try:
            client = self.clients[router_data["app_id"]]
        except KeyError:
            self.log.critical("Missing FCM bridge credentials for {id}",
                              id=router_data['app_id'])
            raise RouterException("Server error", status_code=500, error=901)

        # registration_ids are the FCM instance tokens (specified during
        # registration.
        router_ttl = min(self.MAX_TTL,
                         max(self.min_ttl, notification.ttl or 0))
        try:
            result = client.notify_single_device(
                collapse_key=self.collapseKey,
                data_message=data,
                dry_run=self.dryRun or ('dryrun' in router_data),
                registration_id=regid,
                time_to_live=router_ttl,
            )
        except pyfcm.errors.AuthenticationError as e:
            self.log.error("Authentication Error: %s" % e)
            raise RouterException("Server error", status_code=500)
        except ConnectionError as e:
            self.metrics.increment("notification.bridge.error",

# Path: autopush/exceptions.py
# class RouterException(AutopushException):
#     """Exception if routing has failed, may include a custom status_code and
#     body to write to the response.
# 
#     """
#     def __init__(self, message, status_code=500, response_body="",
#                  router_data=None, headers=None, log_exception=True,
#                  errno=None, logged_status=None, **kwargs):
#         """Create a new RouterException"""
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.headers = {} if headers is None else headers
#         self.log_exception = log_exception
#         self.response_body = response_body or message
#         self.errno = errno
#         self.logged_status = logged_status
#         self.extra = kwargs
# 
# Path: autopush/metrics.py
# def make_tags(base=None, **kwargs):
#     # type: (Sequence[str], **Any) -> Sequence[str]
#     """Generate a list of tag values"""
#     tags = list(base or [])
#     tags.extend('{}:{}'.format(key, val) for key, val in kwargs.iteritems())
#     return tags
# 
# Path: autopush/router/interface.py
# class RouterResponse(object):
#     """Router response if routing has succeeded.
# 
#     If the router data needs to change as a result of this message, either the
#     router got invalidated, or needs updating, then the router_data should be
#     set.
# 
#     """
#     def __init__(self, status_code=200, response_body="", router_data=None,
#                  headers=None, errno=None, logged_status=None):
#         """Create a new RouterResponse"""
#         self.status_code = status_code
#         self.response_body = response_body
#         self.router_data = router_data
#         self.headers = {} if headers is None else headers
#         self.errno = errno
#         self.logged_status = logged_status
# 
# Path: autopush/types.py



# Path: autopush/router/fcm.py
from typing import Any  # noqa
from requests.exceptions import ConnectionError
from twisted.internet.threads import deferToThread
from twisted.logger import Logger
from autopush.exceptions import RouterException
from autopush.metrics import make_tags
from autopush.router.interface import RouterResponse
from autopush.types import JSONDict  # noqa
import pyfcm

            if 'encryption' in notification.headers:
                data['enc'] = notification.headers['encryption']
            if 'crypto_key' in notification.headers:
                data['cryptokey'] = notification.headers['crypto_key']
            elif 'encryption_key' in notification.headers:
                data['enckey'] = notification.headers['encryption_key']
        try:
            client = self.clients[router_data["app_id"]]
        except KeyError:
            self.log.critical("Missing FCM bridge credentials for {id}",
                              id=router_data['app_id'])
            raise RouterException("Server error", status_code=500, error=901)

        # registration_ids are the FCM instance tokens (specified during
        # registration.
        router_ttl = min(self.MAX_TTL,
                         max(self.min_ttl, notification.ttl or 0))
        try:
            result = client.notify_single_device(
                collapse_key=self.collapseKey,
                data_message=data,
                dry_run=self.dryRun or ('dryrun' in router_data),
                registration_id=regid,
                time_to_live=router_ttl,
            )
        except pyfcm.errors.AuthenticationError as e:
            self.log.error("Authentication Error: %s" % e)
            raise RouterException("Server error", status_code=500)
        except ConnectionError as e:
            self.metrics.increment("notification.bridge.error",

                                   tags=make_tags(