14
14
15
15
"""Firebase Dynamic Links module.
16
16
17
- This module lets admins get the stats for a dynamic link.
17
+ This module lets admins get statistics for a Firebase dynamic link.
18
18
"""
19
19
20
- from collections import namedtuple
21
-
22
- from six .moves import urllib_parse
20
+ from six .moves import urllib
23
21
import six
24
22
25
23
from firebase_admin import _http_client
26
24
from firebase_admin import _utils
27
25
28
26
29
-
30
-
31
- _LINKS_ATTRIBUTE = '_links'
27
+ _LINKS_ATTRIBUTE = '_dynamic_links'
32
28
_LINKS_BASE_URL = 'https://firebasedynamiclinks.googleapis.com/v1/
8000
'
33
29
34
30
PLATFORM_DESKTOP = 'desktop'
41
37
EVENT_TYPE_APP_FIRST_OPEN = 'app_first_open'
42
38
EVENT_TYPE_APP_RE_OPEN = 'app_re_open'
43
39
44
- StatOptions = namedtuple ('StatOptions' , ['duration_days' ])
45
-
46
40
def get_link_stats (short_link , stat_options , app = None ):
47
- """ Returns a LinkStats object with the event stats for the given short link
41
+ """ Returns a ` LinkStats` object with the event statistics for the given short link
48
42
49
43
Args:
50
44
short_link: The string of the designated short link. e.g. https://abc12.app.goo.gl/link
51
45
The link must belong to the project associated with the service account
52
46
used to call this API.
53
- stat_options: an object containing a single field "duration_days" for which the
54
- app: a firebase_app instance or None, for default.
47
+ stat_options: An object containing a single field "duration_days" for which the statistics
48
+ are retrieved.
49
+ app: (optional) `firebase_app` instance. (If missing uses default app.)
55
50
56
51
Returns:
57
- LinkStats: an LinkStats object. (containing an array of EventStats)
52
+ LinkStats: An ` LinkStats` object. (containing an array of ` EventStats` )
58
53
59
54
Raises:
60
55
ValueError: If any of the arguments are invalid.
61
- url must be encoded and start with "http"
56
+ url must start with the protocol "http"
62
57
stat_options should have a field with duration_days > 0
63
58
"""
64
- return _get_link_service (app ).get_stats (short_link , stat_options )
59
+ return _get_link_service (app )._get_stats (short_link , stat_options )
65
60
66
61
def _get_link_service (app ):
67
62
"""Returns an _LinksService instance for an App.
68
63
69
- If the App already has an _LinksService associated with it, simply returns
64
+ If the App already has a _LinksService associated with it, simply returns
70
65
it. Otherwise creates a new _LinksService, and adds it to the App before
71
66
returning it.
72
67
73
68
Args:
74
69
app: A Firebase App instance (or None to use the default App).
75
70
76
71
Returns:
77
- _LinksService: An _LinksService for the specified App instance.
72
+ _LinksService: An ` _LinksService` for the specified App instance.
78
73
79
74
Raises:
80
75
ValueError: If the app argument is invalid.
@@ -83,7 +78,7 @@ def _get_link_service(app):
83
78
84
79
85
80
class LinkStats (object ):
86
- """The LinkStats object is returned by get_link_stats, it contains a list of EventStats"""
81
+ """The ` LinkStats` object is returned by get_link_stats, it contains a list of ` EventStats` """
87
82
def __init__ (self , event_stats ):
88
83
if not isinstance (event_stats , (list , tuple )):
89
84
raise ValueError ('Invalid data argument: {0}. Must be a list or tuple'
@@ -95,46 +90,16 @@ def __init__(self, event_stats):
95
90
96
91
@property
97
92
def event_stats (self ):
98
- """Returns the event_stats for this link.
93
+ """Returns the event statistics for this link, for the requested period .
99
94
100
95
Returns:
101
- event_stats: A list of EventStats.
96
+ event_stats: A list of ` EventStats` .
102
97
"""
103
98
return self ._stats
104
99
105
-
106
- class _LinksService (object ):
107
- """Provides methods for the Firebase Dynamic Links interaction"""
108
- def __init__ (self , app ):
109
- self ._client = _http_client .JsonHttpClient (
110
- credential = app .credential .get_credential (),
111
- base_url = _LINKS_BASE_URL )
112
- self ._timeout = app .options .get ('httpTimeout' )
113
- self ._links_request = '{0}/linkStats?durationDays={1}'
114
-
115
- def _populated_request (self , url , options ):
116
- days = options .duration_days
117
- # This is due to six.urllib_parse mistaken error for python 2
118
- #pylint: disable=too-many-function-args
119
- # Complaints about the named second argument needed to replace "/"
120
- #pylint: disable=redundant-keyword-arg
121
- url_quoted = urllib_parse .quote (url , safe = "" )
122
- #pylint: enable=redundant-keyword-arg
123
- #pylint: enable=too-many-function-args
124
- return self ._links_request .format (url_quoted , days )
125
-
126
- def get_stats (self , url , options ):
127
- _validate_url (url )
128
- _validate_stat_options (options )
129
- url_p = self ._populated_request (url , options )
130
- resp = self ._client .request ('get' , url_p )
131
- link_event_stats = resp .json ().get ('linkEventStats' , [])
132
- event_stats = [EventStats .from_json (** es ) for es in link_event_stats ]
133
-
134
- return LinkStats (event_stats )
135
-
136
100
class EventStats (object ):
137
- """EventStats is a single stat item containing (platform, event, count)"""
101
+ """`EventStats` is a single stat item containing (platform, event, count)"""
102
+
138
103
_platforms = {
139
104
'DESKTOP' : PLATFORM_DESKTOP ,
140
105
'IOS' : PLATFORM_IOS ,
@@ -160,7 +125,9 @@ def __repr__(self):
160
125
self .platform , self .event , self .count )
161
126
162
127
@classmethod
163
- def from_json (cls , platform , event , count ):
128
+ def make_event_stat (cls , platform , event , count ):
129
+ """make_event_stat creates an EventStat object given the appropriate constants. e.g:
130
+ make_event_stat(platform=PLATFORM_DESKTOP, event=EVENT_TYPE_REDIRECT, count=4)"""
164
131
return EventStats (cls ._platforms [platform ],
165
132
cls ._event_types [event ],
166
133
int (count ))
@@ -171,10 +138,10 @@ def platform(self):
171
138
172
139
@platform .setter
173
140
def platform (self , platform ):
174
- if isinstance (platform , str ) and platform in self ._platforms .keys ():
141
+ if isinstance (platform , six . string_types ) and platform in self ._platforms .keys ():
175
142
raise ValueError (('Raw string {} detected. Use one of the dynamic_links.PLATFORM_...' +
176
- ' constants, or the from_json () method.' ).format (platform ))
177
- if not isinstance (platform , str ) or platform not in self ._platforms .values ():
143
+ ' constants, or the make_event_stat () method.' ).format (platform ))
144
+ if not isinstance (platform , six . string_types ) or platform not in self ._platforms .values ():
178
145
raise ValueError ('platform {}, not recognized' .format (platform ))
179
146
self ._platform = platform
180
147
@@ -184,10 +151,10 @@ def event(self):
184
151
185
152
@event .setter
186
153
def event (self , event ):
187
- if isinstance (event , str ) and event in self ._event_types .keys ():
154
+ if isinstance (event , six . string_types ) and event in self ._event_types .keys ():
188
155
raise ValueError (('Raw string {} detected. Use one of the dynamic_links.EVENT_TYPES_' +
189
- ' constants, or the from_json () method.' ).format (event ))
190
- if not isinstance (event , str ) or event not in self ._event_types .values ():
156
+ ' constants, or the make_event_stat () method.' ).format (event ))
157
+ if not isinstance (event , six . string_types ) or event not in self ._event_types .values ():
191
158
raise ValueError ('event_type {}, not recognized' .format (event ))
192
159
self ._event = event
193
160
@@ -202,14 +169,49 @@ def count(self, count):
202
169
self ._count = count
203
170
204
171
205
- def _validate_url (url ):
206
- if not isinstance (url , six .string_types ) or not url .startswith ('https://' ):
207
- raise ValueError ('Url must be a string and begin with "https://".' )
172
+ class StatOptions (object ):
173
+ def __init__ (self , duration_days ):
174
+ self .duration_days = duration_days
175
+
176
+ @property
177
+ def duration_days (self ):
178
+ return self ._duration_days
179
+
180
+ @duration_days .setter
181
+ def duration_days (self , duration_days ):
182
+ if (isinstance (duration_days , bool )
183
+ or not isinstance (duration_days , int )
184
+ or duration_days < 1 ):
185
+ raise ValueError ('duration_days must be positive integer (got {})'
186
+ .format (duration_days ))
187
+ self ._duration_days = duration_days
208
188
209
- def _validate_stat_options (options ):
210
- if not isinstance (options , StatOptions ):
211
- raise ValueError ('Options must be of type StatOptions.' )
212
- if (isinstance (options .duration_days , bool )
213
- or not isinstance (options .duration_days , int )
214
- or options .duration_days < 1 ):
215
- raise ValueError ('duration_days: {} must be positive int' .format (options .duration_days ))
189
+ class _LinksService (object ):
190
+ """Provides methods for the Firebase dynamic links interaction"""
191
+ def __init__ (self , app ):
192
+ self ._client = _http_client .JsonHttpClient (
193
+ credential = app .credential .get_credential (),
194
+ base_url = _LINKS_BASE_URL )
195
+ self ._timeout = app .options .get ('httpTimeout' )
196
+ self ._request_string = '{0}/linkStats?durationDays={1}'
197
+
198
+ def _format_request_string (self , short_link , options ):
199
+ days = options .duration_days
200
+ # Complaints about the named second argument needed to replace "/"
201
+ #pylint: disable=redundant-keyword-arg
202
+ url_quoted = urllib .parse .quote (short_link , safe = '' )
203
+ #pylint: enable=redundant-keyword-arg
204
+ return self ._request_string .format (url_quoted , days )
205
+
206
+ def _get_stats (self , short_link , stat_options ):
207
+ if (not isinstance (short_link , six .string_types )
208
+ or not short_link .startswith ('https://' )):
209
+ raise ValueError ('short_link must be a string and begin with "https://".' )
210
+ if not isinstance (stat_options , StatOptions ):
211
+ raise ValueError ('stat_options must be of type StatOptions.' )
212
+
213
+ request_string = self ._format_request_string (short_link , stat_options )
214
+ resp = self ._client .body ('get' , request_string )
215
+ link_event_stats_dict = resp .get ('linkEventStats' , [])
216
+ event_stats = [EventStats .make_event_stat (** es ) for es in link_event_stats_dict ]
217
+ return LinkStats (event_stats )
0 commit comments