1
1
# from __future__ import annotations
2
2
3
3
import json
4
- import os
5
4
import time
6
5
import threading
7
6
10
9
from urllib3 import Retry
11
10
12
11
from google .oauth2 import service_account
12
+ from google .oauth2 .credentials import Credentials
13
13
import google .auth .transport .requests
14
14
15
15
from pyfcm .errors import (
16
16
AuthenticationError ,
17
17
InvalidDataError ,
18
- FCMError ,
19
18
FCMSenderIdMismatchError ,
20
19
FCMServerError ,
21
20
FCMNotRegisteredError ,
25
24
26
25
27
26
class BaseAPI (object ):
28
- FCM_END_POINT = "https://fcm.googleapis.com/v1/projects"
27
+ FCM_END_POINT_BASE = "https://fcm.googleapis.com/v1/projects"
29
28
30
29
def __init__ (
31
30
self ,
32
- service_account_file : str ,
33
- project_id : str ,
34
- credentials = None ,
31
+ service_account_file : str = None ,
32
+ project_id : str = None ,
33
+ credentials : Credentials = None ,
35
34
proxy_dict = None ,
36
35
env = None ,
37
36
json_encoder = None ,
@@ -48,25 +47,38 @@ def __init__(
48
47
json_encoder (BaseJSONEncoder): JSON encoder
49
48
adapter (BaseAdapter): adapter instance
50
49
"""
51
- self .service_account_file = service_account_file
52
- self .project_id = project_id
53
- self .FCM_END_POINT = self .FCM_END_POINT + f"/{ self .project_id } /messages:send"
54
- self .FCM_REQ_PROXIES = None
55
- self .custom_adapter = adapter
56
- self .thread_local = threading .local ()
57
- self .credentials = credentials
58
-
59
- if not service_account_file and not credentials :
50
+ if not (service_account_file or credentials ):
60
51
raise AuthenticationError (
61
52
"Please provide a service account file path or credentials in the constructor"
62
53
)
63
54
55
+ if credentials is not None :
56
+ self .credentials = credentials
57
+ else :
58
+ self .credentials = service_account .Credentials .from_service_account_file (
59
+ service_account_file ,
60
+ scopes = ["https://www.googleapis.com/auth/firebase.messaging" ],
61
+ )
62
+
63
+ # prefer the project ID scoped to the supplied credentials.
64
+ # If, for some reason, the credentials do not specify a project id,
65
+ # we'll check for an explicitly supplied one, and raise an error otherwise
66
+ project_id = getattr (self .credentials , "project_id" , None ) or project_id
67
+
68
+ if not project_id :
69
+ raise AuthenticationError (
70
+ "Please provide a project_id either explicitly or through Google credentials."
71
+ )
72
+
73
+ self .fcm_end_point = self .FCM_END_POINT_BASE + f"/{ project_id } /messages:send"
74
+ self .custom_adapter = adapter
75
+ self .thread_local = threading .local ()
76
+
64
77
if (
65
78
proxy_dict
66
79
and isinstance (proxy_dict , dict )
67
80
and (("http" in proxy_dict ) or ("https" in proxy_dict ))
68
81
):
69
- self .FCM_REQ_PROXIES = proxy_dict
70
82
self .requests_session .proxies .update (proxy_dict )
71
83
72
84
if env == "app_engine" :
@@ -101,7 +113,7 @@ def requests_session(self):
101
113
102
114
def send_request (self , payload = None , timeout = None ):
103
115
response = self .requests_session .post (
104
- self .FCM_END_POINT , data = payload , timeout = timeout
116
+ self .fcm_end_point , data = payload , timeout = timeout
105
117
)
106
118
if (
107
119
"Retry-After" in response .headers
@@ -113,14 +125,13 @@ def send_request(self, payload=None, timeout=None):
113
125
return response
114
126
115
127
def send_async_request (self , params_list , timeout ):
116
-
117
128
import asyncio
118
129
from .async_fcm import fetch_tasks
119
130
120
131
payloads = [self .parse_payload (** params ) for params in params_list ]
121
132
responses = asyncio .new_event_loop ().run_until_complete (
122
133
fetch_tasks (
123
- end_point = self .FCM_END_POINT ,
134
+ end_point = self .fcm_end_point ,
124
135
headers = self .request_headers (),
125
136
payloads = payloads ,
126
137
timeout = timeout ,
@@ -138,16 +149,9 @@ def _get_access_token(self):
138
149
"""
139
150
# get OAuth 2.0 access token
140
151
try :
141
- if self .service_account_file :
142
- credentials = service_account .Credentials .from_service_account_file (
143
- self .service_account_file ,
144
- scopes = ["https://www.googleapis.com/auth/firebase.messaging" ],
145
- )
146
- else :
147
- credentials = self .credentials
148
152
request = google .auth .transport .requests .Request ()
149
- credentials .refresh (request )
150
- return credentials .token
153
+ self . credentials .refresh (request )
154
+ return self . credentials .token
151
155
except Exception as e :
152
156
raise InvalidDataError (e )
153
157
@@ -195,7 +199,6 @@ def parse_response(self, response):
195
199
FCMSenderIdMismatchError: the authenticated sender is different from the sender registered to the token
196
200
FCMNotRegisteredError: device token is missing, not registered, or invalid
197
201
"""
198
-
199
202
if response .status_code == 200 :
200
203
if (
201
204
"content-length" in response .headers
@@ -283,7 +286,9 @@ def parse_payload(
283
286
else :
284
287
raise InvalidDataError ("Provided fcm_options is in the wrong format" )
285
288
286
- fcm_payload ["notification" ] = (
289
+ fcm_payload [
290
+ "notification"
291
+ ] = (
287
292
{}
288
293
) # - https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#notification
289
294
# If title is present, use it
0 commit comments