41
41
_TRANSACTION_MAX_RETRIES = 25
42
42
43
43
44
- def reference (path = '/' , app = None ):
44
+ def reference (path = '/' , app = None , url = None ):
45
45
"""Returns a database Reference representing the node at the specified path.
46
46
47
47
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.
48
51
49
52
Args:
50
53
path: Path to a node in the Firebase realtime database (optional).
51
54
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.
52
57
53
58
Returns:
54
59
Reference: A newly initialized Reference.
55
60
56
61
Raises:
57
62
ValueError: If the specified path or app is invalid.
58
63
"""
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 )
60
66
return Reference (client = client , path = path )
61
67
62
68
def _parse_path (path ):
@@ -662,65 +668,50 @@ def __eq__(self, other):
662
668
return self ._compare (other ) is 0
663
669
664
670
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."""
671
673
672
674
_DEFAULT_AUTH_OVERRIDE = '_admin_'
673
675
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 )
692
684
if auth_override != self ._DEFAULT_AUTH_OVERRIDE and auth_override != {}:
693
685
encoded = json .dumps (auth_override , separators = (',' , ':' ))
694
686
self ._auth_override = 'auth_variable_override={0}' .format (encoded )
695
687
else :
696
688
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 ]
698
700
699
701
@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."""
712
704
if not url or not isinstance (url , six .string_types ):
713
705
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 ))
717
708
parsed = urllib .parse .urlparse (url )
718
709
if parsed .scheme != 'https' :
719
710
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 ))
721
712
elif not parsed .netloc .endswith ('.firebaseio.com' ):
722
713
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 '
724
715
'Firebase Realtime Database instance.' .format (url ))
725
716
return 'https://{0}' .format (parsed .netloc )
726
717
@@ -735,6 +726,39 @@ def _get_auth_override(cls, app):
735
726
else :
736
727
return auth_override
737
728
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
+
738
762
@property
739
763
def auth_override (self ):
740
764
return self ._auth_override
0 commit comments