4141_TRANSACTION_MAX_RETRIES = 25
4242
4343
44- def reference (path = '/' , app = None ):
44+ def reference (path = '/' , app = None , url = None ):
4545 """Returns a database Reference representing the node at the specified path.
4646
4747 If no path is specified, this function returns a Reference that represents the database root.
48+ By default, the returned References provide access to the Firebase Database specified at
49+ app initialization. To connect to a different Database instance in the same Firebase project,
50+ specify the ``url`` parameter.
4851
4952 Args:
5053 path: Path to a node in the Firebase realtime database (optional).
5154 app: An App instance (optional).
55+ url: Base URL of the Firebase Database instance (optional). When specified, takes
56+ precedence over the the ``databaseURL`` option set at app initialization.
5257
5358 Returns:
5459 Reference: A newly initialized Reference.
5560
5661 Raises:
5762 ValueError: If the specified path or app is invalid.
5863 """
59- client = _utils .get_app_service (app , _DB_ATTRIBUTE , _Client .from_app )
64+ service = _utils .get_app_service (app , _DB_ATTRIBUTE , _DatabaseService )
65+ client = service .get_client (url )
6066 return Reference (client = client , path = path )
6167
6268def _parse_path (path ):
@@ -662,65 +668,50 @@ def __eq__(self, other):
662668 return self ._compare (other ) is 0
663669
664670
665- class _Client (_http_client .JsonHttpClient ):
666- """HTTP client used to make REST calls.
667-
668- _Client maintains an HTTP session, and handles authenticating HTTP requests along with
669- marshalling and unmarshalling of JSON data.
670- """
671+ class _DatabaseService (object ):
672+ """Service that maintains a collection of database clients."""
671673
672674 _DEFAULT_AUTH_OVERRIDE = '_admin_'
673675
674- def __init__ (self , credential , base_url , auth_override = _DEFAULT_AUTH_OVERRIDE , timeout = None ):
675- """Creates a new _Client from the given parameters.
676-
677- This exists primarily to enable testing. For regular use, obtain _Client instances by
678- calling the from_app() class method.
679-
680- Args:
681- credential: A Google credential that can be used to authenticate requests.
682- base_url: A URL prefix to be added to all outgoing requests. This is typically the
683- Firebase Realtime Database URL.
684- auth_override: A dictionary representing auth variable overrides or None (optional).
685- Default value provides admin privileges. A None value here provides un-authenticated
686- guest privileges.
687- timeout: HTTP request timeout in seconds (optional). If not set connections will never
688- timeout, which is the default behavior of the underlying requests library.
689- """
690- _http_client .JsonHttpClient .__init__ (
691- self , credential = credential , base_url = base_url , headers = {'User-Agent' : _USER_AGENT })
676+ def __init__ (self , app ):
677+ self ._credential = app .credential .get_credential ()
678+ db_url = app .options .get ('databaseURL' )
679+ if db_url :
680+ self ._db_url = _DatabaseService ._validate_url (db_url )
681+ else :
682+ self ._db_url = None
683+ auth_override = _DatabaseService ._get_auth_override (app )
692684 if auth_override != self ._DEFAULT_AUTH_OVERRIDE and auth_override != {}:
693685 encoded = json .dumps (auth_override , separators = (',' , ':' ))
694686 self ._auth_override = 'auth_variable_override={0}' .format (encoded )
695687 else :
696688 self ._auth_override = None
697- self ._timeout = timeout
689+ self ._timeout = app .options .get ('httpTimeout' )
690+ self ._clients = {}
691+
692+ def get_client (self , base_url = None ):
693+ if base_url is None :
694+ base_url = self ._db_url
695+ base_url = _DatabaseService ._validate_url (base_url )
696+ if base_url not in self ._clients :
697+ client = _Client (self ._credential , base_url , self ._auth_override , self ._timeout )
698+ self ._clients [base_url ] = client
699+ return self ._clients [base_url ]
698700
699701 @classmethod
700- def from_app (cls , app ):
701- """Creates a new _Client for a given App"""
702- credential = app .credential .get_credential ()
703- db_url = cls ._get_db_url (app )
704- auth_override = cls ._get_auth_override (app )
705- timeout = app .options .get ('httpTimeout' )
706- return _Client (credential , db_url , auth_override , timeout )
707-
708- @classmethod
709- def _get_db_url (cls , app ):
710- """Retrieves and parses the database URL option."""
711- url = app .options .get ('databaseURL' )
702+ def _validate_url (cls , url ):
703+ """Parses and validates a given database URL."""
712704 if not url or not isinstance (url , six .string_types ):
713705 raise ValueError (
714- 'Invalid databaseURL option: "{0}". databaseURL must be a non-empty URL '
715- 'string.' .format (url ))
716-
706+ 'Invalid database URL: "{0}". Database URL must be a non-empty '
707+ 'URL string.' .format (url ))
717708 parsed = urllib .parse .urlparse (url )
718709 if parsed .scheme != 'https' :
719710 raise ValueError (
720- 'Invalid databaseURL option : "{0}". databaseURL must be an HTTPS URL.' .format (url ))
711+ 'Invalid database URL : "{0}". Database URL must be an HTTPS URL.' .format (url ))
721712 elif not parsed .netloc .endswith ('.firebaseio.com' ):
722713 raise ValueError (
723- 'Invalid databaseURL option : "{0}". databaseURL must be a valid URL to a '
714+ 'Invalid database URL : "{0}". Database URL must be a valid URL to a '
724715 'Firebase Realtime Database instance.' .format (url ))
725716 return 'https://{0}' .format (parsed .netloc )
726717
@@ -735,6 +726,39 @@ def _get_auth_override(cls, app):
735726 else :
736727 return auth_override
737728
729+ def close (self ):
730+ for value in self ._clients .values ():
731+ value .close ()
732+ self ._clients = {}
733+
734+
735+ class _Client (_http_client .JsonHttpClient ):
736+ """HTTP client used to make REST calls.
737+
738+ _Client maintains an HTTP session, and handles authenticating HTTP requests along with
739+ marshalling and unmarshalling of JSON data.
740+ """
741+
742+ def __init__ (self , credential , base_url , auth_override , timeout ):
743+ """Creates a new _Client from the given parameters.
744+
745+ This exists primarily to enable testing. For regular use, obtain _Client instances by
746+ calling the from_app() class method.
747+
748+ Args:
749+ credential: A Google credential that can be used to authenticate requests.
750+ base_url: A URL prefix to be added to all outgoing requests. This is typically the
751+ Firebase Realtime Database URL.
752+ auth_override: The encoded auth_variable_override query parameter to be included in
753+ outgoing requests.
754+ timeout: HTTP request timeout in seconds. If not set connections will never
755+ timeout, which is the default behavior of the underlying requests library.
756+ """
757+ _http_client .JsonHttpClient .__init__ (
758+ self , credential = credential , base_url = base_url , headers = {'User-Agent' : _USER_AGENT })
759+ self ._auth_override = auth_override
760+ self ._timeout = timeout
761+
738762 @property
739763 def auth_override (self ):
740764 return self ._auth_override
0 commit comments