25
25
import six
26
26
27
27
import firebase_admin
28
+ from firebase_admin import exceptions
28
29
from firebase_admin import _http_client
29
30
from firebase_admin import _utils
30
31
@@ -139,21 +140,6 @@ def _check_not_none(obj, field_name):
139
140
return obj
140
141
141
142
142
- class ApiCallError (Exception ):
143
- """An error encountered while interacting with the Firebase Project Management Service."""
144
-
145
- def __init__ (self , message , error ):
146
- Exception .__init__ (self , message )
147
- self .detail = error
148
-
149
-
150
- class _PollingError (Exception ):
151
- """An error encountered during the polling of an app's creation status."""
152
-
153
- def __init__ (self , message ):
154
- Exception .__init__ (self , message )
155
-
156
-
157
143
class AndroidApp (object ):
158
144
"""A reference to an Android app within a Firebase project.
159
145
@@ -185,7 +171,7 @@ def get_metadata(self):
185
171
AndroidAppMetadata: An ``AndroidAppMetadata`` instance.
186
172
187
173
Raises:
188
- ApiCallError : If an error occurs while communicating with the Firebase Project
174
+ FirebaseError : If an error occurs while communicating with the Firebase Project
189
175
Management Service.
190
176
"""
191
177
return self ._service .get_android_app_metadata (self ._app_id )
@@ -200,7 +186,7 @@ def set_display_name(self, new_display_name):
200
186
NoneType: None.
201
187
202
188
Raises:
203
- ApiCallError : If an error occurs while communicating with the Firebase Project
189
+ FirebaseError : If an error occurs while communicating with the Firebase Project
204
190
Management Service.
205
191
"""
206
192
return self ._service .set_android_app_display_name (self ._app_id , new_display_name )
@@ -216,7 +202,7 @@ def get_sha_certificates(self):
216
202
list: A list of ``ShaCertificate`` instances.
217
203
218
204
Raises:
219
- ApiCallError : If an error occurs while communicating with the Firebase Project
205
+ FirebaseError : If an error occurs while communicating with the Firebase Project
220
206
Management Service.
221
207
"""
222
208
return self ._service .get_sha_certificates (self ._app_id )
@@ -231,7 +217,7 @@ def add_sha_certificate(self, certificate_to_add):
231
217
NoneType: None.
232
218
233
219
Raises:
234
- ApiCallError : If an error occurs while communicating with the Firebase Project
220
+ FirebaseError : If an error occurs while communicating with the Firebase Project
235
221
Management Service. (For example, if the certificate_to_add already exists.)
236
222
"""
237
223
return self ._service .add_sha_certificate (self ._app_id , certificate_to_add )
@@ -246,7 +232,7 @@ def delete_sha_certificate(self, certificate_to_delete):
246
232
NoneType: None.
247
233
248
234
Raises:
249
- ApiCallError : If an error occurs while communicating with the Firebase Project
235
+ FirebaseError : If an error occurs while communicating with the Firebase Project
250
236
Management Service. (For example, if the certificate_to_delete is not found.)
251
237
"""
252
238
return self ._service .delete_sha_certificate (certificate_to_delete )
@@ -283,7 +269,7 @@ def get_metadata(self):
283
269
IosAppMetadata: An ``IosAppMetadata`` instance.
284
270
285
271
Raises:
286
- ApiCallError : If an error occurs while communicating with the Firebase Project
272
+ FirebaseError : If an error occurs while communicating with the Firebase Project
287
273
Management Service.
288
274
"""
289
275
return self ._service .get_ios_app_metadata (self ._app_id )
@@ -298,7 +284,7 @@ def set_display_name(self, new_display_name):
298
284
NoneType: None.
299
285
300
286
Raises:
301
- ApiCallError : If an error occurs while communicating with the Firebase Project
287
+ FirebaseError : If an error occurs while communicating with the Firebase Project
302
288
Management Service.
303
289
"""
304
290
return self ._service .set_ios_app_display_name (self ._app_id , new_display_name )
@@ -478,22 +464,11 @@ class _ProjectManagementService(object):
478
464
MAXIMUM_POLLING_ATTEMPTS = 8
479
465
POLL_BASE_WAIT_TIME_SECONDS = 0.5
480
466
POLL_EXPONENTIAL_BACKOFF_FACTOR = 1.5
481
- ERROR_CODES = {
482
- 401 : 'Request not authorized.' ,
483
- 403 : 'Client does not have sufficient privileges.' ,
484
- 404 : 'Failed to find the resource.' ,
485
- 409 : 'The resource already exists.' ,
486
- 429 : 'Request throttled out by the backend server.' ,
487
- 500 : 'Internal server error.' ,
488
- 503 : 'Backend servers are over capacity. Try again later.'
489
- }
490
467
491
468
ANDROID_APPS_RESOURCE_NAME = 'androidApps'
492
469
ANDROID_APP_IDENTIFIER_NAME = 'packageName'
493
- ANDROID_APP_IDENTIFIER_LABEL = 'Package name'
494
470
IOS_APPS_RESOURCE_NAME = 'iosApps'
495
471
IOS_APP_IDENTIFIER_NAME = 'bundleId'
496
- IOS_APP_IDENTIFIER_LABEL = 'Bundle ID'
497
472
498
473
def __init__ (self , app ):
499
474
project_id = app .project_id
@@ -528,7 +503,7 @@ def _get_app_metadata(self, platform_resource_name, identifier_name, metadata_cl
528
503
"""Retrieves detailed information about an Android or iOS app."""
529
504
_check_is_nonempty_string (app_id , 'app_id' )
530
505
path = '/v1beta1/projects/-/{0}/{1}' .format (platform_resource_name , app_id )
531
- response = self ._make_request ('get' , path , app_id , 'App ID' )
506
+ response = self ._make_request ('get' , path )
532
507
return metadata_class (
533
508
response [identifier_name ],
534
509
name = response ['name' ],
@@ -553,7 +528,7 @@ def _set_display_name(self, app_id, new_display_name, platform_resource_name):
553
528
path = '/v1beta1/projects/-/{0}/{1}?updateMask=displayName' .format (
554
529
platform_resource_name , app_id )
555
530
request_body = {'displayName' : new_display_name }
556
- self ._make_request ('patch' , path , app_id , 'App ID' , json = request_body )
531
+ self ._make_request ('patch' , path , json = request_body )
557
532
558
533
def list_android_apps (self ):
559
534
return self ._list_apps (
@@ -571,7 +546,7 @@ def _list_apps(self, platform_resource_name, app_class):
571
546
self ._project_id ,
572
547
platform_resource_name ,
573
548
_ProjectManagementService .MAXIMUM_LIST_APPS_PAGE_SIZE )
574
- response = self ._make_request ('get' , path , self . _project_id , 'Project ID' )
549
+ response = self ._make_request ('get' , path )
575
550
apps_list = []
576
551
while True :
577
552
apps = response .get ('apps' )
@@ -587,14 +562,13 @@ def _list_apps(self, platform_resource_name, app_class):
587
562
platform_resource_name ,
588
563
next_page_token ,
589
564
_ProjectManagementService .MAXIMUM_LIST_APPS_PAGE_SIZE )
590
- response = self ._make_request ('get' , path , self . _project_id , 'Project ID' )
565
+ response = self ._make_request ('get' , path )
591
566
return apps_list
592
567
593
568
def create_android_app (self , package_name , display_name = None ):
594
569
return self ._create_app (
595
570
platform_resource_name = _ProjectManagementService .ANDROID_APPS_RESOURCE_NAME ,
596
571
identifier_name = _ProjectManagementService .ANDROID_APP_IDENTIFIER_NAME ,
597
- identifier_label = _ProjectManagementService .ANDROID_APP_IDENTIFIER_LABEL ,
598
572
identifier = package_name ,
599
573
display_name = display_name ,
600
574
app_class = AndroidApp )
@@ -603,7 +577,6 @@ def create_ios_app(self, bundle_id, display_name=None):
603
577
return self ._create_app (
604
578
platform_resource_name = _ProjectManagementService .IOS_APPS_RESOURCE_NAME ,
605
579
identifier_name = _ProjectManagementService .IOS_APP_IDENTIFIER_NAME ,
606
- identifier_label = _ProjectManagementService .IOS_APP_IDENTIFIER_LABEL ,
607
580
identifier = bundle_id ,
608
581
display_name = display_name ,
609
582
app_class = IosApp )
@@ -612,7 +585,6 @@ def _create_app(
612
585
self ,
613
586
platform_resource_name ,
614
587
identifier_name ,
615
- identifier_label ,
616
588
identifier ,
617
589
display_name ,
618
590
app_class ):
@@ -622,15 +594,10 @@ def _create_app(
622
594
request_body = {identifier_name : identifier }
623
595
if display_name :
624
596
request_body ['displayName' ] = display_name
625
- response = self ._make_request ('post' , path , identifier , identifier_label , json = request_body )
597
+ response = self ._make_request ('post' , path , json = request_body )
626
598
operation_name = response ['name' ]
627
- try :
628
- poll_response = self ._poll_app_creation (operation_name )
629
- return app_class (app_id = poll_response ['appId' ], service = self )
630
- except _PollingError as error :
631
- raise ApiCallError (
632
- _ProjectManagementService ._extract_message (operation_name , 'Operation name' , error ),
633
- error )
599
+ poll_response = self ._poll_app_creation (operation_name )
600
+ return app_class (app_id = poll_response ['appId' ], service = self )
634
601
635
602
def _poll_app_creation (self , operation_name ):
636
603
"""Polls the Long-Running Operation repeatedly until it is done with exponential backoff."""
@@ -640,16 +607,17 @@ def _poll_app_creation(self, operation_name):
640
607
wait_time_seconds = delay_factor * _ProjectManagementService .POLL_BASE_WAIT_TIME_SECONDS
641
608
time .sleep (wait_time_seconds )
642
609
path = '/v1/{0}' .format (operation_name )
643
- poll_response = self ._make_request ('get' , path , operation_name , 'Operation name' )
610
+ poll_response , http_response = self ._body_and_response ('get' , path )
644
611
done = poll_response .get ('done' )
645
612
if done :
646
613
response = poll_response .get ('response' )
647
614
if response :
648
615
return response
649
616
else :
650
- raise _PollingError (
651
- 'Polling finished, but the operation terminated in an error.' )
652
- raise _PollingError ('Polling deadline exceeded.' )
617
+ raise exceptions .UnknownError (
618
+ 'Polling finished, but the operation terminated in an error.' ,
619
+ http_response = http_response )
620
+ raise exceptions .DeadlineExceededError ('Polling deadline exceeded.' )
653
621
654
622
def get_android_app_config (self , app_id ):
655
623
return self ._get_app_config (
@@ -662,14 +630,14 @@ def get_ios_app_config(self, app_id):
662
630
663
631
def _get_app_config (self , platform_resource_name , app_id ):
664
632
path = '/v1beta1/projects/-/{0}/{1}/config' .format (platform_resource_name , app_id )
665
- response = self ._make_request ('get' , path , app_id , 'App ID' )
633
+ response = self ._make_request ('get' , path )
666
634
# In Python 2.7, the base64 module works with strings, while in Python 3, it works with
667
635
# bytes objects. This line works in both versions.
668
636
return base64 .standard_b64decode (response ['configFileContents' ]).decode (encoding = 'utf-8' )
669
637
670
638
def get_sha_certificates (self , app_id ):
671
639
path = '/v1beta1/projects/-/androidApps/{0}/sha' .format (app_id )
672
- response = self ._make_request ('get' , path , app_id , 'App ID' )
640
+ response = self ._make_request ('get' , path )
673
641
cert_list = response .get ('certificates' ) or []
674
642
return [ShaCertificate (sha_hash = cert ['shaHash' ], name = cert ['name' ]) for cert in cert_list ]
675
643
@@ -678,28 +646,20 @@ def add_sha_certificate(self, app_id, certificate_to_add):
678
646
sha_hash = _check_not_none (certificate_to_add , 'certificate_to_add' ).sha_hash
679
647
cert_type = certificate_to_add .cert_type
680
648
request_body = {'shaHash' : sha_hash , 'certType' : cert_type }
681
- self ._make_request ('post' , path , app_id , 'App ID' , json = request_body )
649
+ self ._make_request ('post' , path , json = request_body )
682
650
683
651
def delete_sha_certificate (self , certificate_to_delete ):
684
652
name = _check_not_none (certificate_to_delete , 'certificate_to_delete' ).name
685
653
path = '/v1beta1/{0}' .format (name )
686
- self ._make_request ('delete' , path , name , 'SHA ID' )
654
+ self ._make_request ('delete' , path )
655
+
656
+ def _make_request (self , method , url , json = None ):
657
+ body , _ = self ._body_and_response (method , url , json )
658
+ return body
687
659
688
- def _make_request (self , method , url , resource_identifier , resource_identifier_label , json = None ):
660
+ def _body_and_response (self , method , url , json = None ):
689
661
try :
690
- return self ._client .body (method = method , url = url , json = json , timeout = self ._timeout )
662
+ return self ._client .body_and_response (
663
+ method = method , url = url , json = json , timeout = self ._timeout )
691
664
except requests .exceptions .RequestException as error :
692
- raise ApiCallError (
693
- _ProjectManagementService ._extract_message (
694
- resource_identifier , resource_identifier_label , error ),
695
- error )
696
-
697
- @staticmethod
698
- def _extract_message (identifier , identifier_label , error ):
699
- if not isinstance (error , requests .exceptions .RequestException ) or error .response is None :
700
- return '{0} "{1}": {2}' .format (identifier_label , identifier , str (error ))
701
- status = error .response .status_code
702
- message = _ProjectManagementService .ERROR_CODES .get (status )
703
- if message :
704
- return '{0} "{1}": {2}' .format (identifier_label , identifier , message )
705
- return '{0} "{1}": Error {2}.' .format (identifier_label , identifier , status )
665
+ raise _utils .handle_platform_error_from_requests (error )
0 commit comments